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