1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 package org.eclipse.jgit.treewalk;
46
47 import static org.eclipse.jgit.lib.Constants.DOT_GIT_ATTRIBUTES;
48 import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
49 import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
50 import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
51 import static org.eclipse.jgit.lib.Constants.TYPE_TREE;
52 import static org.eclipse.jgit.lib.Constants.encode;
53
54 import java.io.IOException;
55 import java.io.InputStream;
56 import java.util.Arrays;
57 import java.util.Collections;
58
59 import org.eclipse.jgit.attributes.AttributesNode;
60 import org.eclipse.jgit.attributes.AttributesRule;
61 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
62 import org.eclipse.jgit.errors.MissingObjectException;
63 import org.eclipse.jgit.lib.AnyObjectId;
64 import org.eclipse.jgit.lib.FileMode;
65 import org.eclipse.jgit.lib.MutableObjectId;
66 import org.eclipse.jgit.lib.ObjectId;
67 import org.eclipse.jgit.lib.ObjectReader;
68
69
70
71
72 public class CanonicalTreeParser extends AbstractTreeIterator {
73 private static final byte[] EMPTY = {};
74 private static final byte[] ATTRS = encode(DOT_GIT_ATTRIBUTES);
75
76 private byte[] raw;
77
78
79 private int prevPtr;
80
81
82 private int currPtr;
83
84
85 private int nextPtr;
86
87
88
89
90 public CanonicalTreeParser() {
91 reset(EMPTY);
92 }
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115 public CanonicalTreeParser(final byte[] prefix, final ObjectReader reader,
116 final AnyObjectId treeId) throws IncorrectObjectTypeException,
117 IOException {
118 super(prefix);
119 reset(reader, treeId);
120 }
121
122 private CanonicalTreeParser(CanonicalTreeParser p) {
123 super(p);
124 }
125
126
127
128
129
130
131
132 @Deprecated
133 public CanonicalTreeParser getParent() {
134 return (CanonicalTreeParser) parent;
135 }
136
137
138
139
140
141
142
143 public void reset(byte[] treeData) {
144 attributesNode = null;
145 raw = treeData;
146 prevPtr = -1;
147 currPtr = 0;
148 if (eof())
149 nextPtr = 0;
150 else
151 parseEntry();
152 }
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171 public CanonicalTreeParser resetRoot(final ObjectReader reader,
172 final AnyObjectId id) throws IncorrectObjectTypeException,
173 IOException {
174 CanonicalTreeParser p = this;
175 while (p.parent != null)
176 p = (CanonicalTreeParser) p.parent;
177 p.reset(reader, id);
178 return p;
179 }
180
181
182
183
184
185
186 public CanonicalTreeParser next() {
187 CanonicalTreeParser p = this;
188 for (;;) {
189 if (p.nextPtr == p.raw.length) {
190
191 if (p.parent == null) {
192 p.currPtr = p.nextPtr;
193 return p;
194 }
195 p = (CanonicalTreeParser) p.parent;
196 continue;
197 }
198
199 p.prevPtr = p.currPtr;
200 p.currPtr = p.nextPtr;
201 p.parseEntry();
202 return p;
203 }
204 }
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222 public void reset(ObjectReader reader, AnyObjectId id)
223 throws IncorrectObjectTypeException, IOException {
224 reset(reader.open(id, OBJ_TREE).getCachedBytes());
225 }
226
227
228 @Override
229 public CanonicalTreeParser createSubtreeIterator(final ObjectReader reader,
230 final MutableObjectId idBuffer)
231 throws IncorrectObjectTypeException, IOException {
232 idBuffer.fromRaw(idBuffer(), idOffset());
233 if (!FileMode.TREE.equals(mode)) {
234 final ObjectId me = idBuffer.toObjectId();
235 throw new IncorrectObjectTypeException(me, TYPE_TREE);
236 }
237 return createSubtreeIterator0(reader, idBuffer);
238 }
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255 public final CanonicalTreeParser createSubtreeIterator0(
256 final ObjectReader reader, final AnyObjectId id)
257 throws IOException {
258 final CanonicalTreeParser p = new CanonicalTreeParser(this);
259 p.reset(reader, id);
260 return p;
261 }
262
263
264 @Override
265 public CanonicalTreeParser createSubtreeIterator(ObjectReader reader)
266 throws IncorrectObjectTypeException, IOException {
267 return createSubtreeIterator(reader, new MutableObjectId());
268 }
269
270
271 @Override
272 public boolean hasId() {
273 return true;
274 }
275
276
277 @Override
278 public byte[] idBuffer() {
279 return raw;
280 }
281
282
283 @Override
284 public int idOffset() {
285 return nextPtr - OBJECT_ID_LENGTH;
286 }
287
288
289 @Override
290 public void reset() {
291 if (!first())
292 reset(raw);
293 }
294
295
296 @Override
297 public boolean first() {
298 return currPtr == 0;
299 }
300
301
302 @Override
303 public boolean eof() {
304 return currPtr == raw.length;
305 }
306
307
308 @Override
309 public void next(int delta) {
310 if (delta == 1) {
311
312
313 prevPtr = currPtr;
314 currPtr = nextPtr;
315 if (!eof())
316 parseEntry();
317 return;
318 }
319
320
321
322 final int end = raw.length;
323 int ptr = nextPtr;
324 while (--delta > 0 && ptr != end) {
325 prevPtr = ptr;
326 while (raw[ptr] != 0)
327 ptr++;
328 ptr += OBJECT_ID_LENGTH + 1;
329 }
330 if (delta != 0)
331 throw new ArrayIndexOutOfBoundsException(delta);
332 currPtr = ptr;
333 if (!eof())
334 parseEntry();
335 }
336
337
338 @Override
339 public void back(int delta) {
340 if (delta == 1 && 0 <= prevPtr) {
341
342
343
344 currPtr = prevPtr;
345 prevPtr = -1;
346 if (!eof())
347 parseEntry();
348 return;
349 } else if (delta <= 0)
350 throw new ArrayIndexOutOfBoundsException(delta);
351
352
353
354
355
356
357 final int[] trace = new int[delta + 1];
358 Arrays.fill(trace, -1);
359 int ptr = 0;
360 while (ptr != currPtr) {
361 System.arraycopy(trace, 1, trace, 0, delta);
362 trace[delta] = ptr;
363 while (raw[ptr] != 0)
364 ptr++;
365 ptr += OBJECT_ID_LENGTH + 1;
366 }
367 if (trace[1] == -1)
368 throw new ArrayIndexOutOfBoundsException(delta);
369 prevPtr = trace[0];
370 currPtr = trace[1];
371 parseEntry();
372 }
373
374 private void parseEntry() {
375 int ptr = currPtr;
376 byte c = raw[ptr++];
377 int tmp = c - '0';
378 for (;;) {
379 c = raw[ptr++];
380 if (' ' == c)
381 break;
382 tmp <<= 3;
383 tmp += c - '0';
384 }
385 mode = tmp;
386
387 tmp = pathOffset;
388 for (;; tmp++) {
389 c = raw[ptr++];
390 if (c == 0) {
391 break;
392 }
393 if (tmp >= path.length) {
394 growPath(tmp);
395 }
396 path[tmp] = c;
397 }
398 pathLen = tmp;
399 nextPtr = ptr + OBJECT_ID_LENGTH;
400 }
401
402
403
404
405
406
407
408
409
410
411
412
413
414 public AttributesNode getEntryAttributesNode(ObjectReader reader)
415 throws IOException {
416 if (attributesNode == null) {
417 attributesNode = findAttributes(reader);
418 }
419 return attributesNode.getRules().isEmpty() ? null : attributesNode;
420 }
421
422 private AttributesNode findAttributes(ObjectReader reader)
423 throws IOException {
424 CanonicalTreeParser itr = new CanonicalTreeParser();
425 itr.reset(raw);
426 if (itr.findFile(ATTRS)) {
427 return loadAttributes(reader, itr.getEntryObjectId());
428 }
429 return noAttributes();
430 }
431
432 private static AttributesNode loadAttributes(ObjectReader reader,
433 AnyObjectId id) throws IOException {
434 AttributesNode r = new AttributesNode();
435 try (InputStream in = reader.open(id, OBJ_BLOB).openStream()) {
436 r.parse(in);
437 }
438 return r.getRules().isEmpty() ? noAttributes() : r;
439 }
440
441 private static AttributesNode noAttributes() {
442 return new AttributesNode(Collections.<AttributesRule> emptyList());
443 }
444 }