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.dircache;
46
47 import static java.nio.charset.StandardCharsets.UTF_8;
48
49 import java.io.IOException;
50 import java.io.InputStream;
51 import java.util.Collections;
52
53 import org.eclipse.jgit.attributes.AttributesNode;
54 import org.eclipse.jgit.attributes.AttributesRule;
55 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
56 import org.eclipse.jgit.lib.Constants;
57 import org.eclipse.jgit.lib.FileMode;
58 import org.eclipse.jgit.lib.ObjectId;
59 import org.eclipse.jgit.lib.ObjectLoader;
60 import org.eclipse.jgit.lib.ObjectReader;
61 import org.eclipse.jgit.treewalk.AbstractTreeIterator;
62 import org.eclipse.jgit.treewalk.EmptyTreeIterator;
63 import org.eclipse.jgit.util.RawParseUtils;
64
65
66
67
68
69
70
71
72
73
74
75
76
77 public class DirCacheIterator extends AbstractTreeIterator {
78
79 private static final byte[] DOT_GIT_ATTRIBUTES_BYTES = Constants.DOT_GIT_ATTRIBUTES
80 .getBytes(UTF_8);
81
82
83 protected final DirCache cache;
84
85
86 private final DirCacheTree tree;
87
88
89 private final int treeStart;
90
91
92 private final int treeEnd;
93
94
95 private final byte[] subtreeId;
96
97
98 protected int ptr;
99
100
101 private int nextSubtreePos;
102
103
104 protected DirCacheEntry currentEntry;
105
106
107 protected DirCacheTree currentSubtree;
108
109
110
111
112
113
114
115
116
117
118
119 public DirCacheIterator(DirCache dc) {
120 cache = dc;
121 tree = dc.getCacheTree(true);
122 treeStart = 0;
123 treeEnd = tree.getEntrySpan();
124 subtreeId = new byte[Constants.OBJECT_ID_LENGTH];
125 if (!eof())
126 parseEntry();
127 }
128
129 DirCacheIterator(DirCacheIterator p, DirCacheTree dct) {
130 super(p, p.path, p.pathLen + 1);
131 cache = p.cache;
132 tree = dct;
133 treeStart = p.ptr;
134 treeEnd = treeStart + tree.getEntrySpan();
135 subtreeId = p.subtreeId;
136 ptr = p.ptr;
137 parseEntry();
138 }
139
140
141 @Override
142 public AbstractTreeIterator createSubtreeIterator(ObjectReader reader)
143 throws IncorrectObjectTypeException, IOException {
144 if (currentSubtree == null)
145 throw new IncorrectObjectTypeException(getEntryObjectId(),
146 Constants.TYPE_TREE);
147 return new DirCacheIterator(this, currentSubtree);
148 }
149
150
151 @Override
152 public EmptyTreeIterator createEmptyTreeIterator() {
153 final byte[] n = new byte[Math.max(pathLen + 1, DEFAULT_PATH_SIZE)];
154 System.arraycopy(path, 0, n, 0, pathLen);
155 n[pathLen] = '/';
156 return new EmptyTreeIterator(this, n, pathLen + 1);
157 }
158
159
160 @Override
161 public boolean hasId() {
162 if (currentSubtree != null)
163 return currentSubtree.isValid();
164 return currentEntry != null;
165 }
166
167
168 @Override
169 public byte[] idBuffer() {
170 if (currentSubtree != null)
171 return currentSubtree.isValid() ? subtreeId : zeroid;
172 if (currentEntry != null)
173 return currentEntry.idBuffer();
174 return zeroid;
175 }
176
177
178 @Override
179 public int idOffset() {
180 if (currentSubtree != null)
181 return 0;
182 if (currentEntry != null)
183 return currentEntry.idOffset();
184 return 0;
185 }
186
187
188 @Override
189 public void reset() {
190 if (!first()) {
191 ptr = treeStart;
192 nextSubtreePos = 0;
193 currentEntry = null;
194 currentSubtree = null;
195 if (!eof())
196 parseEntry();
197 }
198 }
199
200
201 @Override
202 public boolean first() {
203 return ptr == treeStart;
204 }
205
206
207 @Override
208 public boolean eof() {
209 return ptr == treeEnd;
210 }
211
212
213 @Override
214 public void next(int delta) {
215 while (--delta >= 0) {
216 if (currentSubtree != null)
217 ptr += currentSubtree.getEntrySpan();
218 else
219 ptr++;
220 if (eof())
221 break;
222 parseEntry();
223 }
224 }
225
226
227 @Override
228 public void back(int delta) {
229 while (--delta >= 0) {
230 if (currentSubtree != null)
231 nextSubtreePos--;
232 ptr--;
233 parseEntry(false);
234 if (currentSubtree != null)
235 ptr -= currentSubtree.getEntrySpan() - 1;
236 }
237 }
238
239 private void parseEntry() {
240 parseEntry(true);
241 }
242
243 private void parseEntry(boolean forward) {
244 currentEntry = cache.getEntry(ptr);
245 final byte[] cep = currentEntry.path;
246
247 if (!forward) {
248 if (nextSubtreePos > 0) {
249 final DirCacheTree p = tree.getChild(nextSubtreePos - 1);
250 if (p.contains(cep, pathOffset, cep.length)) {
251 nextSubtreePos--;
252 currentSubtree = p;
253 }
254 }
255 }
256 if (nextSubtreePos != tree.getChildCount()) {
257 final DirCacheTree s = tree.getChild(nextSubtreePos);
258 if (s.contains(cep, pathOffset, cep.length)) {
259
260
261
262 currentSubtree = s;
263 nextSubtreePos++;
264
265 if (s.isValid())
266 s.getObjectId().copyRawTo(subtreeId, 0);
267 mode = FileMode.TREE.getBits();
268 path = cep;
269 pathLen = pathOffset + s.nameLength();
270 return;
271 }
272 }
273
274
275
276
277 mode = currentEntry.getRawMode();
278 path = cep;
279 pathLen = cep.length;
280 currentSubtree = null;
281
282 if (RawParseUtils.match(path, pathOffset, DOT_GIT_ATTRIBUTES_BYTES) == path.length)
283 attributesNode = new LazyLoadingAttributesNode(
284 currentEntry.getObjectId());
285 }
286
287
288
289
290
291
292
293 public DirCacheEntry getDirCacheEntry() {
294 return currentSubtree == null ? currentEntry : null;
295 }
296
297
298
299
300
301
302
303
304
305
306
307
308
309 public AttributesNode getEntryAttributesNode(ObjectReader reader)
310 throws IOException {
311 if (attributesNode instanceof LazyLoadingAttributesNode)
312 attributesNode = ((LazyLoadingAttributesNode) attributesNode)
313 .load(reader);
314 return attributesNode;
315 }
316
317
318
319
320
321 private static class LazyLoadingAttributesNode extends AttributesNode {
322 final ObjectId objectId;
323
324 LazyLoadingAttributesNode(ObjectId objectId) {
325 super(Collections.<AttributesRule> emptyList());
326 this.objectId = objectId;
327
328 }
329
330 AttributesNode load(ObjectReader reader) throws IOException {
331 AttributesNode r = new AttributesNode();
332 ObjectLoader loader = reader.open(objectId);
333 if (loader != null) {
334 try (InputStream in = loader.openStream()) {
335 r.parse(in);
336 }
337 }
338 return r.getRules().isEmpty() ? null : r;
339 }
340 }
341
342 }