2 * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
3 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
7 * Redistribution and use in source and binary forms, with or
8 * without modification, are permitted provided that the following
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
19 * - Neither the name of the Git Development Community nor the
20 * names of its contributors may be used to endorse or promote
21 * products derived from this software without specific prior
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
25 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
26 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
29 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 package org
.spearce
.jgit
.treewalk
;
41 import java
.io
.IOException
;
42 import java
.nio
.ByteBuffer
;
43 import java
.nio
.CharBuffer
;
45 import org
.spearce
.jgit
.errors
.CorruptObjectException
;
46 import org
.spearce
.jgit
.errors
.IncorrectObjectTypeException
;
47 import org
.spearce
.jgit
.lib
.Constants
;
48 import org
.spearce
.jgit
.lib
.FileMode
;
49 import org
.spearce
.jgit
.lib
.ObjectId
;
50 import org
.spearce
.jgit
.lib
.Repository
;
51 import org
.spearce
.jgit
.treewalk
.filter
.TreeFilter
;
54 * Walks a Git tree (directory) in Git sort order.
56 * A new iterator instance should be positioned on the first entry, or at eof.
57 * Data for the first entry (if not at eof) should be available immediately.
59 * Implementors must walk a tree in the Git sort order, which has the following
67 * In the second item, <code>A</code> is the name of a subtree and
68 * <code>c</code> is a file within that subtree. The other two items are files
69 * in the root level tree.
71 * @see CanonicalTreeParser
73 public abstract class AbstractTreeIterator
{
74 private static final int DEFAULT_PATH_SIZE
= 128;
76 /** A dummy object id buffer that matches the zero ObjectId. */
77 protected static final byte[] zeroid
= new byte[Constants
.OBJECT_ID_LENGTH
];
79 /** Iterator for the parent tree; null if we are the root iterator. */
80 final AbstractTreeIterator parent
;
82 /** The iterator this current entry is path equal to. */
83 AbstractTreeIterator matches
;
86 * Number of entries we moved forward to force a D/F conflict match.
88 * @see NameConflictTreeWalk
93 * Mode bits for the current entry.
95 * A numerical value from FileMode is usually faster for an iterator to
96 * obtain from its data source so this is the preferred representation.
98 * @see org.spearce.jgit.lib.FileMode
103 * Path buffer for the current entry.
105 * This buffer is pre-allocated at the start of walking and is shared from
106 * parent iterators down into their subtree iterators. The sharing allows
107 * the current entry to always be a full path from the root, while each
108 * subtree only needs to populate the part that is under their control.
110 protected byte[] path
;
113 * Position within {@link #path} this iterator starts writing at.
115 * This is the first offset in {@link #path} that this iterator must
116 * populate during {@link #next}. At the root level (when {@link #parent}
117 * is null) this is 0. For a subtree iterator the index before this position
118 * should have the value '/'.
120 protected final int pathOffset
;
123 * Total length of the current entry's complete path from the root.
125 * This is the number of bytes within {@link #path} that pertain to the
126 * current entry. Values at this index through the end of the array are
127 * garbage and may be randomly populated from prior entries.
129 protected int pathLen
;
131 /** Create a new iterator with no parent. */
132 protected AbstractTreeIterator() {
134 path
= new byte[DEFAULT_PATH_SIZE
];
139 * Create a new iterator with no parent and a prefix.
141 * The prefix path supplied is inserted in front of all paths generated by
142 * this iterator. It is intended to be used when an iterator is being
143 * created for a subsection of an overall repository and needs to be
144 * combined with other iterators that are created to run over the entire
145 * repository namespace.
148 * position of this iterator in the repository tree. The value
149 * may be null or the empty string to indicate the prefix is the
150 * root of the repository. A trailing slash ('/') is
151 * automatically appended if the prefix does not end in '/'.
153 protected AbstractTreeIterator(final String prefix
) {
156 if (prefix
!= null && prefix
.length() > 0) {
159 b
= Constants
.CHARSET
.encode(CharBuffer
.wrap(prefix
));
161 path
= new byte[Math
.max(DEFAULT_PATH_SIZE
, pathLen
+ 1)];
162 b
.get(path
, 0, pathLen
);
163 if (path
[pathLen
- 1] != '/')
164 path
[pathLen
++] = '/';
165 pathOffset
= pathLen
;
167 path
= new byte[DEFAULT_PATH_SIZE
];
173 * Create an iterator for a subtree of an existing iterator.
176 * parent tree iterator.
178 protected AbstractTreeIterator(final AbstractTreeIterator p
) {
181 pathOffset
= p
.pathLen
+ 1;
183 path
[pathOffset
- 1] = '/';
184 } catch (ArrayIndexOutOfBoundsException e
) {
186 path
[pathOffset
- 1] = '/';
191 * Create an iterator for a subtree of an existing iterator.
193 * The caller is responsible for setting up the path of the child iterator.
196 * parent tree iterator.
198 * path array to be used by the child iterator. This path must
199 * contain the path from the top of the walk to the first child
200 * and must end with a '/'.
201 * @param childPathOffset
202 * position within <code>childPath</code> where the child can
203 * insert its data. The value at
204 * <code>childPath[childPathOffset-1]</code> must be '/'.
206 protected AbstractTreeIterator(final AbstractTreeIterator p
,
207 final byte[] childPath
, final int childPathOffset
) {
210 pathOffset
= childPathOffset
;
214 * Grow the path buffer larger.
217 * number of live bytes in the path buffer. This many bytes will
218 * be moved into the larger buffer.
220 protected void growPath(final int len
) {
221 final byte[] n
= new byte[path
.length
<< 1];
222 System
.arraycopy(path
, 0, n
, 0, len
);
223 for (AbstractTreeIterator p
= this; p
!= null; p
= p
.parent
)
228 * Compare the path of this current entry to another iterator's entry.
231 * the other iterator to compare the path against.
232 * @return -1 if this entry sorts first; 0 if the entries are equal; 1 if
233 * p's entry sorts first.
235 public int pathCompare(final AbstractTreeIterator p
) {
236 return pathCompare(p
, p
.mode
);
239 int pathCompare(final AbstractTreeIterator p
, final int pMode
) {
240 final byte[] a
= path
;
241 final byte[] b
= p
.path
;
242 final int aLen
= pathLen
;
243 final int bLen
= p
.pathLen
;
246 // Its common when we are a subtree for both parents to match;
247 // when this happens everything in path[0..cPos] is known to
248 // be equal and does not require evaluation again.
250 cPos
= alreadyMatch(this, p
);
252 for (; cPos
< aLen
&& cPos
< bLen
; cPos
++) {
253 final int cmp
= (a
[cPos
] & 0xff) - (b
[cPos
] & 0xff);
259 return (a
[cPos
] & 0xff) - lastPathChar(pMode
);
261 return lastPathChar(mode
) - (b
[cPos
] & 0xff);
262 return lastPathChar(mode
) - lastPathChar(pMode
);
265 private static int alreadyMatch(AbstractTreeIterator a
,
266 AbstractTreeIterator b
) {
268 final AbstractTreeIterator ap
= a
.parent
;
269 final AbstractTreeIterator bp
= b
.parent
;
270 if (ap
== null || bp
== null)
272 if (ap
.matches
== bp
.matches
)
279 private static int lastPathChar(final int mode
) {
280 return FileMode
.TREE
.equals(mode
) ?
'/' : '\0';
284 * Check if the current entry of both iterators has the same id.
286 * This method is faster than {@link #getEntryObjectId()} as it does not
287 * require copying the bytes out of the buffers. A direct {@link #idBuffer}
288 * compare operation is performed.
290 * @param otherIterator
291 * the other iterator to test against.
292 * @return true if both iterators have the same object id; false otherwise.
294 public boolean idEqual(final AbstractTreeIterator otherIterator
) {
295 return ObjectId
.equals(idBuffer(), idOffset(),
296 otherIterator
.idBuffer(), otherIterator
.idOffset());
300 * Get the object id of the current entry.
302 * @return an object id for the current entry.
304 public ObjectId
getEntryObjectId() {
305 return ObjectId
.fromRaw(idBuffer(), idOffset());
309 * Get the byte array buffer object IDs must be copied out of.
311 * The id buffer contains the bytes necessary to construct an ObjectId for
312 * the current entry of this iterator. The buffer can be the same buffer for
313 * all entries, or it can be a unique buffer per-entry. Implementations are
314 * encouraged to expose their private buffer whenever possible to reduce
315 * garbage generation and copying costs.
317 * @return byte array the implementation stores object IDs within.
318 * @see #getEntryObjectId()
320 public abstract byte[] idBuffer();
323 * Get the position within {@link #idBuffer()} of this entry's ObjectId.
325 * @return offset into the array returned by {@link #idBuffer()} where the
326 * ObjectId must be copied out of.
328 public abstract int idOffset();
331 * Create a new iterator for the current entry's subtree.
333 * The parent reference of the iterator must be <code>this</code>,
334 * otherwise the caller would not be able to exit out of the subtree
335 * iterator correctly and return to continue walking <code>this</code>.
338 * repository to load the tree data from.
339 * @return a new parser that walks over the current subtree.
340 * @throws IncorrectObjectTypeException
341 * the current entry is not actually a tree and cannot be parsed
342 * as though it were a tree.
343 * @throws IOException
344 * a loose object or pack file could not be read.
346 public abstract AbstractTreeIterator
createSubtreeIterator(Repository repo
)
347 throws IncorrectObjectTypeException
, IOException
;
350 * Is this tree iterator positioned on its first entry?
352 * An iterator is positioned on the first entry if <code>back(1)</code>
353 * would be an invalid request as there is no entry before the current one.
355 * An empty iterator (one with no entries) will be
356 * <code>first() && eof()</code>.
358 * @return true if the iterator is positioned on the first entry.
360 public abstract boolean first();
363 * Is this tree iterator at its EOF point (no more entries)?
365 * An iterator is at EOF if there is no current entry.
367 * @return true if we have walked all entries and have none left.
369 public abstract boolean eof();
372 * Move to next entry, populating this iterator with the entry data.
374 * The delta indicates how many moves forward should occur. The most common
375 * delta is 1 to move to the next entry.
377 * Implementations must populate the following members:
379 * <li>{@link #mode}</li>
380 * <li>{@link #path} (from {@link #pathOffset} to {@link #pathLen})</li>
381 * <li>{@link #pathLen}</li>
383 * as well as any implementation dependent information necessary to
384 * accurately return data from {@link #idBuffer()} and {@link #idOffset()}
388 * number of entries to move the iterator by. Must be a positive,
390 * @throws CorruptObjectException
391 * the tree is invalid.
393 public abstract void next(int delta
) throws CorruptObjectException
;
396 * Move to prior entry, populating this iterator with the entry data.
398 * The delta indicates how many moves backward should occur.The most common
399 * delta is 1 to move to the prior entry.
401 * Implementations must populate the following members:
403 * <li>{@link #mode}</li>
404 * <li>{@link #path} (from {@link #pathOffset} to {@link #pathLen})</li>
405 * <li>{@link #pathLen}</li>
407 * as well as any implementation dependent information necessary to
408 * accurately return data from {@link #idBuffer()} and {@link #idOffset()}
412 * number of entries to move the iterator by. Must be a positive,
414 * @throws CorruptObjectException
415 * the tree is invalid.
417 public abstract void back(int delta
) throws CorruptObjectException
;
420 * Advance to the next tree entry, populating this iterator with its data.
422 * This method behaves like <code>seek(1)</code> but is called by
423 * {@link TreeWalk} only if a {@link TreeFilter} was used and ruled out the
424 * current entry from the results. In such cases this tree iterator may
425 * perform special behavior.
427 * @throws CorruptObjectException
428 * the tree is invalid.
430 public void skip() throws CorruptObjectException
{
435 * Indicates to the iterator that no more entries will be read.
437 * This is only invoked by TreeWalk when the iteration is aborted early due
438 * to a {@link org.spearce.jgit.errors.StopWalkException} being thrown from
439 * within a TreeFilter.
441 public void stopWalk() {
442 // Do nothing by default. Most iterators do not care.