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