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 java.io.IOException;
48 import java.util.Arrays;
49
50 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
51 import org.eclipse.jgit.errors.MissingObjectException;
52 import org.eclipse.jgit.lib.AnyObjectId;
53 import org.eclipse.jgit.lib.Constants;
54 import org.eclipse.jgit.lib.FileMode;
55 import org.eclipse.jgit.lib.MutableObjectId;
56 import org.eclipse.jgit.lib.ObjectId;
57 import org.eclipse.jgit.lib.ObjectReader;
58
59
60 public class CanonicalTreeParser extends AbstractTreeIterator {
61 private static final byte[] EMPTY = {};
62
63 private byte[] raw;
64
65
66 private int prevPtr;
67
68
69 private int currPtr;
70
71
72 private int nextPtr;
73
74
75 public CanonicalTreeParser() {
76 reset(EMPTY);
77 }
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100 public CanonicalTreeParser(final byte[] prefix, final ObjectReader reader,
101 final AnyObjectId treeId) throws IncorrectObjectTypeException,
102 IOException {
103 super(prefix);
104 reset(reader, treeId);
105 }
106
107 private CanonicalTreeParser(final CanonicalTreeParser p) {
108 super(p);
109 }
110
111
112
113
114
115 @Deprecated
116 public CanonicalTreeParser getParent() {
117 return (CanonicalTreeParser) parent;
118 }
119
120
121
122
123
124
125
126 public void reset(final byte[] treeData) {
127 raw = treeData;
128 prevPtr = -1;
129 currPtr = 0;
130 if (eof())
131 nextPtr = 0;
132 else
133 parseEntry();
134 }
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153 public CanonicalTreeParser resetRoot(final ObjectReader reader,
154 final AnyObjectId id) throws IncorrectObjectTypeException,
155 IOException {
156 CanonicalTreeParser p = this;
157 while (p.parent != null)
158 p = (CanonicalTreeParser) p.parent;
159 p.reset(reader, id);
160 return p;
161 }
162
163
164 public CanonicalTreeParser next() {
165 CanonicalTreeParser p = this;
166 for (;;) {
167 if (p.nextPtr == p.raw.length) {
168
169 if (p.parent == null) {
170 p.currPtr = p.nextPtr;
171 return p;
172 }
173 p = (CanonicalTreeParser) p.parent;
174 continue;
175 }
176
177 p.prevPtr = p.currPtr;
178 p.currPtr = p.nextPtr;
179 p.parseEntry();
180 return p;
181 }
182 }
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200 public void reset(final ObjectReader reader, final AnyObjectId id)
201 throws IncorrectObjectTypeException, IOException {
202 reset(reader.open(id, Constants.OBJ_TREE).getCachedBytes());
203 }
204
205 @Override
206 public CanonicalTreeParser createSubtreeIterator(final ObjectReader reader,
207 final MutableObjectId idBuffer)
208 throws IncorrectObjectTypeException, IOException {
209 idBuffer.fromRaw(idBuffer(), idOffset());
210 if (!FileMode.TREE.equals(mode)) {
211 final ObjectId me = idBuffer.toObjectId();
212 throw new IncorrectObjectTypeException(me, Constants.TYPE_TREE);
213 }
214 return createSubtreeIterator0(reader, idBuffer);
215 }
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232 public final CanonicalTreeParser createSubtreeIterator0(
233 final ObjectReader reader, final AnyObjectId id)
234 throws IOException {
235 final CanonicalTreeParser p = new CanonicalTreeParser(this);
236 p.reset(reader, id);
237 return p;
238 }
239
240 public CanonicalTreeParser createSubtreeIterator(final ObjectReader reader)
241 throws IncorrectObjectTypeException, IOException {
242 return createSubtreeIterator(reader, new MutableObjectId());
243 }
244
245 @Override
246 public boolean hasId() {
247 return true;
248 }
249
250 @Override
251 public byte[] idBuffer() {
252 return raw;
253 }
254
255 @Override
256 public int idOffset() {
257 return nextPtr - Constants.OBJECT_ID_LENGTH;
258 }
259
260 @Override
261 public void reset() {
262 if (!first())
263 reset(raw);
264 }
265
266 @Override
267 public boolean first() {
268 return currPtr == 0;
269 }
270
271 public boolean eof() {
272 return currPtr == raw.length;
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 += Constants.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 @Override
305 public void back(int delta) {
306 if (delta == 1 && 0 <= prevPtr) {
307
308
309
310 currPtr = prevPtr;
311 prevPtr = -1;
312 if (!eof())
313 parseEntry();
314 return;
315 } else if (delta <= 0)
316 throw new ArrayIndexOutOfBoundsException(delta);
317
318
319
320
321
322
323 final int[] trace = new int[delta + 1];
324 Arrays.fill(trace, -1);
325 int ptr = 0;
326 while (ptr != currPtr) {
327 System.arraycopy(trace, 1, trace, 0, delta);
328 trace[delta] = ptr;
329 while (raw[ptr] != 0)
330 ptr++;
331 ptr += Constants.OBJECT_ID_LENGTH + 1;
332 }
333 if (trace[1] == -1)
334 throw new ArrayIndexOutOfBoundsException(delta);
335 prevPtr = trace[0];
336 currPtr = trace[1];
337 parseEntry();
338 }
339
340 private void parseEntry() {
341 int ptr = currPtr;
342 byte c = raw[ptr++];
343 int tmp = c - '0';
344 for (;;) {
345 c = raw[ptr++];
346 if (' ' == c)
347 break;
348 tmp <<= 3;
349 tmp += c - '0';
350 }
351 mode = tmp;
352
353 tmp = pathOffset;
354 for (;; tmp++) {
355 c = raw[ptr++];
356 if (c == 0)
357 break;
358 try {
359 path[tmp] = c;
360 } catch (ArrayIndexOutOfBoundsException e) {
361 growPath(tmp);
362 path[tmp] = c;
363 }
364 }
365 pathLen = tmp;
366 nextPtr = ptr + Constants.OBJECT_ID_LENGTH;
367 }
368 }