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