1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.transport;
12
13 import static java.nio.charset.StandardCharsets.UTF_8;
14 import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH;
15 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SYMREF;
16
17 import java.io.IOException;
18 import java.nio.ByteBuffer;
19 import java.nio.CharBuffer;
20 import java.nio.charset.CharacterCodingException;
21 import java.nio.charset.CharsetEncoder;
22 import java.nio.charset.CoderResult;
23 import java.util.Collection;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.LinkedHashSet;
27 import java.util.Map;
28 import java.util.Set;
29
30 import org.eclipse.jgit.lib.AnyObjectId;
31 import org.eclipse.jgit.lib.Constants;
32 import org.eclipse.jgit.lib.ObjectId;
33 import org.eclipse.jgit.lib.Ref;
34 import org.eclipse.jgit.lib.RefComparator;
35 import org.eclipse.jgit.lib.Repository;
36
37
38
39
40
41 public abstract class RefAdvertiser {
42
43 public static class PacketLineOutRefAdvertiser extends RefAdvertiser {
44 private final CharsetEncoder utf8 = UTF_8.newEncoder();
45 private final PacketLineOut pckOut;
46
47 private byte[] binArr = new byte[256];
48 private ByteBuffer binBuf = ByteBuffer.wrap(binArr);
49
50 private char[] chArr = new char[256];
51 private CharBuffer chBuf = CharBuffer.wrap(chArr);
52
53
54
55
56
57
58
59 public PacketLineOutRefAdvertiser(PacketLineOut out) {
60 pckOut = out;
61 }
62
63 @Override
64 public void advertiseId(AnyObjectId id, String refName)
65 throws IOException {
66 id.copyTo(binArr, 0);
67 binArr[OBJECT_ID_STRING_LENGTH] = ' ';
68 binBuf.position(OBJECT_ID_STRING_LENGTH + 1);
69 append(refName);
70 if (first) {
71 first = false;
72 if (!capablities.isEmpty()) {
73 append('\0');
74 for (String cap : capablities) {
75 append(' ');
76 append(cap);
77 }
78 }
79 }
80 append('\n');
81 pckOut.writePacket(binArr, 0, binBuf.position());
82 }
83
84 private void append(String str) throws CharacterCodingException {
85 int n = str.length();
86 if (n > chArr.length) {
87 chArr = new char[n + 256];
88 chBuf = CharBuffer.wrap(chArr);
89 }
90 str.getChars(0, n, chArr, 0);
91 chBuf.position(0).limit(n);
92 utf8.reset();
93 for (;;) {
94 CoderResult cr = utf8.encode(chBuf, binBuf, true);
95 if (cr.isOverflow()) {
96 grow();
97 } else if (cr.isUnderflow()) {
98 break;
99 } else {
100 cr.throwException();
101 }
102 }
103 }
104
105 private void append(int b) {
106 if (!binBuf.hasRemaining()) {
107 grow();
108 }
109 binBuf.put((byte) b);
110 }
111
112 private void grow() {
113 int cnt = binBuf.position();
114 byte[] tmp = new byte[binArr.length << 1];
115 System.arraycopy(binArr, 0, tmp, 0, cnt);
116 binArr = tmp;
117 binBuf = ByteBuffer.wrap(binArr);
118 binBuf.position(cnt);
119 }
120
121 @Override
122 protected void writeOne(CharSequence line) throws IOException {
123 pckOut.writeString(line.toString());
124 }
125
126 @Override
127 protected void end() throws IOException {
128 pckOut.end();
129 }
130 }
131
132 private final StringBuilder tmpLine = new StringBuilder(100);
133
134 private final char[] tmpId = new char[Constants.OBJECT_ID_STRING_LENGTH];
135
136 final Set<String> capablities = new LinkedHashSet<>();
137
138 private final Set<ObjectId> sent = new HashSet<>();
139
140 private Repository repository;
141
142 private boolean derefTags;
143
144 boolean first = true;
145
146 private boolean useProtocolV2;
147
148
149 private final Map<String, String> symrefs = new HashMap<>();
150
151
152
153
154
155
156
157 public void init(Repository src) {
158 repository = src;
159 }
160
161
162
163
164
165
166
167 public void setUseProtocolV2(boolean b) {
168 useProtocolV2 = b;
169 }
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185 public void setDerefTags(boolean deref) {
186 derefTags = deref;
187 }
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205 public void advertiseCapability(String name) {
206 capablities.add(name);
207 }
208
209
210
211
212
213
214
215
216
217
218 public void advertiseCapability(String name, String value) {
219 if (value != null) {
220 capablities.add(name + '=' + value);
221 }
222 }
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240 public void addSymref(String from, String to) {
241 if (useProtocolV2) {
242 symrefs.put(from, to);
243 } else {
244 advertiseCapability(OPTION_SYMREF, from + ':' + to);
245 }
246 }
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261 @Deprecated
262 public Set<ObjectId> send(Map<String, Ref> refs) throws IOException {
263 return send(refs.values());
264 }
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279 public Set<ObjectId> send(Collection<Ref> refs) throws IOException {
280 for (Ref ref : RefComparator.sort(refs)) {
281
282
283 ObjectId objectId = ref.getObjectId();
284 if (objectId == null) {
285 continue;
286 }
287
288 if (useProtocolV2) {
289 String symrefPart = symrefs.containsKey(ref.getName())
290 ? (" symref-target:" + symrefs.get(ref.getName()))
291 : "";
292 String peelPart = "";
293 if (derefTags) {
294 if (!ref.isPeeled() && repository != null) {
295 ref = repository.getRefDatabase().peel(ref);
296 }
297 ObjectId peeledObjectId = ref.getPeeledObjectId();
298 if (peeledObjectId != null) {
299 peelPart = " peeled:" + peeledObjectId.getName();
300 }
301 }
302 writeOne(objectId.getName() + " " + ref.getName() + symrefPart
303 + peelPart + "\n");
304 continue;
305 }
306
307 advertiseAny(objectId, ref.getName());
308
309 if (!derefTags)
310 continue;
311
312 if (!ref.isPeeled()) {
313 if (repository == null)
314 continue;
315 ref = repository.getRefDatabase().peel(ref);
316 }
317
318 if (ref.getPeeledObjectId() != null)
319 advertiseAny(ref.getPeeledObjectId(), ref.getName() + "^{}");
320 }
321 return sent;
322 }
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338 public void advertiseHave(AnyObjectId id) throws IOException {
339 advertiseAnyOnce(id, ".have");
340 }
341
342
343
344
345
346
347 public boolean isEmpty() {
348 return first;
349 }
350
351 private void advertiseAnyOnce(AnyObjectId obj, String refName)
352 throws IOException {
353 if (!sent.contains(obj))
354 advertiseAny(obj, refName);
355 }
356
357 private void advertiseAny(AnyObjectId obj, String refName)
358 throws IOException {
359 sent.add(obj.toObjectId());
360 advertiseId(obj, refName);
361 }
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378 public void advertiseId(AnyObjectId id, String refName)
379 throws IOException {
380 tmpLine.setLength(0);
381 id.copyTo(tmpId, tmpLine);
382 tmpLine.append(' ');
383 tmpLine.append(refName);
384 if (first) {
385 first = false;
386 if (!capablities.isEmpty()) {
387 tmpLine.append('\0');
388 for (String capName : capablities) {
389 tmpLine.append(' ');
390 tmpLine.append(capName);
391 }
392 tmpLine.append(' ');
393 }
394 }
395 tmpLine.append('\n');
396 writeOne(tmpLine);
397 }
398
399
400
401
402
403
404
405
406
407
408
409 protected abstract void writeOne(CharSequence line) throws IOException;
410
411
412
413
414
415
416
417
418 protected abstract void end() throws IOException;
419 }