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