Add builder-style API to jgit and Commit & Log cmd
[jgit.git] / org.eclipse.jgit / src / org / eclipse / jgit / treewalk / TreeWalk.java
blob25ab78ef04e81b70735aeb2baf8116582238e039
1 /*
2 * Copyright (C) 2008-2009, Google Inc.
3 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
4 * and other copyright owners as documented in the project's IP log.
6 * This program and the accompanying materials are made available
7 * under the terms of the Eclipse Distribution License v1.0 which
8 * accompanies this distribution, is reproduced below, and is
9 * available at http://www.eclipse.org/org/documents/edl-v10.php
11 * All rights reserved.
13 * Redistribution and use in source and binary forms, with or
14 * without modification, are permitted provided that the following
15 * conditions are met:
17 * - Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
20 * - Redistributions in binary form must reproduce the above
21 * copyright notice, this list of conditions and the following
22 * disclaimer in the documentation and/or other materials provided
23 * with the distribution.
25 * - Neither the name of the Eclipse Foundation, Inc. nor the
26 * names of its contributors may be used to endorse or promote
27 * products derived from this software without specific prior
28 * written permission.
30 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
31 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
32 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
33 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
34 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
35 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
36 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
37 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
38 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
39 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
40 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
41 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
42 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45 package org.eclipse.jgit.treewalk;
47 import java.io.IOException;
48 import java.util.Collections;
50 import org.eclipse.jgit.errors.CorruptObjectException;
51 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
52 import org.eclipse.jgit.errors.MissingObjectException;
53 import org.eclipse.jgit.errors.StopWalkException;
54 import org.eclipse.jgit.lib.AnyObjectId;
55 import org.eclipse.jgit.lib.Constants;
56 import org.eclipse.jgit.lib.FileMode;
57 import org.eclipse.jgit.lib.MutableObjectId;
58 import org.eclipse.jgit.lib.ObjectId;
59 import org.eclipse.jgit.lib.Repository;
60 import org.eclipse.jgit.lib.WindowCursor;
61 import org.eclipse.jgit.revwalk.RevTree;
62 import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
63 import org.eclipse.jgit.treewalk.filter.TreeFilter;
64 import org.eclipse.jgit.util.RawParseUtils;
66 /**
67 * Walks one or more {@link AbstractTreeIterator}s in parallel.
68 * <p>
69 * This class can perform n-way differences across as many trees as necessary.
70 * <p>
71 * Each tree added must have the same root as existing trees in the walk.
72 * <p>
73 * A TreeWalk instance can only be used once to generate results. Running a
74 * second time requires creating a new TreeWalk instance, or invoking
75 * {@link #reset()} and adding new trees before starting again. Resetting an
76 * existing instance may be faster for some applications as some internal
77 * buffers may be recycled.
78 * <p>
79 * TreeWalk instances are not thread-safe. Applications must either restrict
80 * usage of a TreeWalk instance to a single thread, or implement their own
81 * synchronization at a higher level.
82 * <p>
83 * Multiple simultaneous TreeWalk instances per {@link Repository} are
84 * permitted, even from concurrent threads.
86 public class TreeWalk {
87 /**
88 * Open a tree walk and filter to exactly one path.
89 * <p>
90 * The returned tree walk is already positioned on the requested path, so
91 * the caller should not need to invoke {@link #next()} unless they are
92 * looking for a possible directory/file name conflict.
94 * @param db
95 * repository to read tree object data from.
96 * @param path
97 * single path to advance the tree walk instance into.
98 * @param trees
99 * one or more trees to walk through, all with the same root.
100 * @return a new tree walk configured for exactly this one path; null if no
101 * path was found in any of the trees.
102 * @throws IOException
103 * reading a pack file or loose object failed.
104 * @throws CorruptObjectException
105 * an tree object could not be read as its data stream did not
106 * appear to be a tree, or could not be inflated.
107 * @throws IncorrectObjectTypeException
108 * an object we expected to be a tree was not a tree.
109 * @throws MissingObjectException
110 * a tree object was not found.
112 public static TreeWalk forPath(final Repository db, final String path,
113 final AnyObjectId... trees) throws MissingObjectException,
114 IncorrectObjectTypeException, CorruptObjectException, IOException {
115 final TreeWalk r = new TreeWalk(db);
116 r.setFilter(PathFilterGroup.createFromStrings(Collections
117 .singleton(path)));
118 r.setRecursive(r.getFilter().shouldBeRecursive());
119 r.reset(trees);
120 return r.next() ? r : null;
124 * Open a tree walk and filter to exactly one path.
125 * <p>
126 * The returned tree walk is already positioned on the requested path, so
127 * the caller should not need to invoke {@link #next()} unless they are
128 * looking for a possible directory/file name conflict.
130 * @param db
131 * repository to read tree object data from.
132 * @param path
133 * single path to advance the tree walk instance into.
134 * @param tree
135 * the single tree to walk through.
136 * @return a new tree walk configured for exactly this one path; null if no
137 * path was found in any of the trees.
138 * @throws IOException
139 * reading a pack file or loose object failed.
140 * @throws CorruptObjectException
141 * an tree object could not be read as its data stream did not
142 * appear to be a tree, or could not be inflated.
143 * @throws IncorrectObjectTypeException
144 * an object we expected to be a tree was not a tree.
145 * @throws MissingObjectException
146 * a tree object was not found.
148 public static TreeWalk forPath(final Repository db, final String path,
149 final RevTree tree) throws MissingObjectException,
150 IncorrectObjectTypeException, CorruptObjectException, IOException {
151 return forPath(db, path, new ObjectId[] { tree });
154 private final Repository db;
156 private final MutableObjectId idBuffer = new MutableObjectId();
158 private final WindowCursor curs = new WindowCursor();
160 private TreeFilter filter;
162 AbstractTreeIterator[] trees;
164 private boolean recursive;
166 private boolean postOrderTraversal;
168 private int depth;
170 private boolean advance;
172 private boolean postChildren;
174 AbstractTreeIterator currentHead;
177 * Create a new tree walker for a given repository.
179 * @param repo
180 * the repository the walker will obtain data from.
182 public TreeWalk(final Repository repo) {
183 db = repo;
184 filter = TreeFilter.ALL;
185 trees = new AbstractTreeIterator[] { new EmptyTreeIterator() };
189 * Get the repository this tree walker is reading from.
191 * @return the repository configured when the walker was created.
193 public Repository getRepository() {
194 return db;
198 * Get the currently configured filter.
200 * @return the current filter. Never null as a filter is always needed.
202 public TreeFilter getFilter() {
203 return filter;
207 * Set the tree entry filter for this walker.
208 * <p>
209 * Multiple filters may be combined by constructing an arbitrary tree of
210 * <code>AndTreeFilter</code> or <code>OrTreeFilter</code> instances to
211 * describe the boolean expression required by the application. Custom
212 * filter implementations may also be constructed by applications.
213 * <p>
214 * Note that filters are not thread-safe and may not be shared by concurrent
215 * TreeWalk instances. Every TreeWalk must be supplied its own unique
216 * filter, unless the filter implementation specifically states it is (and
217 * always will be) thread-safe. Callers may use {@link TreeFilter#clone()}
218 * to create a unique filter tree for this TreeWalk instance.
220 * @param newFilter
221 * the new filter. If null the special {@link TreeFilter#ALL}
222 * filter will be used instead, as it matches every entry.
223 * @see org.eclipse.jgit.treewalk.filter.AndTreeFilter
224 * @see org.eclipse.jgit.treewalk.filter.OrTreeFilter
226 public void setFilter(final TreeFilter newFilter) {
227 filter = newFilter != null ? newFilter : TreeFilter.ALL;
231 * Is this walker automatically entering into subtrees?
232 * <p>
233 * If the walker is recursive then the caller will not see a subtree node
234 * and instead will only receive file nodes in all relevant subtrees.
236 * @return true if automatically entering subtrees is enabled.
238 public boolean isRecursive() {
239 return recursive;
243 * Set the walker to enter (or not enter) subtrees automatically.
244 * <p>
245 * If recursive mode is enabled the walker will hide subtree nodes from the
246 * calling application and will produce only file level nodes. If a tree
247 * (directory) is deleted then all of the file level nodes will appear to be
248 * deleted, recursively, through as many levels as necessary to account for
249 * all entries.
251 * @param b
252 * true to skip subtree nodes and only obtain files nodes.
254 public void setRecursive(final boolean b) {
255 recursive = b;
259 * Does this walker return a tree entry after it exits the subtree?
260 * <p>
261 * If post order traversal is enabled then the walker will return a subtree
262 * after it has returned the last entry within that subtree. This may cause
263 * a subtree to be seen by the application twice if {@link #isRecursive()}
264 * is false, as the application will see it once, call
265 * {@link #enterSubtree()}, and then see it again as it leaves the subtree.
266 * <p>
267 * If an application does not enable {@link #isRecursive()} and it does not
268 * call {@link #enterSubtree()} then the tree is returned only once as none
269 * of the children were processed.
271 * @return true if subtrees are returned after entries within the subtree.
273 public boolean isPostOrderTraversal() {
274 return postOrderTraversal;
278 * Set the walker to return trees after their children.
280 * @param b
281 * true to get trees after their children.
282 * @see #isPostOrderTraversal()
284 public void setPostOrderTraversal(final boolean b) {
285 postOrderTraversal = b;
288 /** Reset this walker so new tree iterators can be added to it. */
289 public void reset() {
290 trees = new AbstractTreeIterator[0];
291 advance = false;
292 depth = 0;
296 * Reset this walker to run over a single existing tree.
298 * @param id
299 * the tree we need to parse. The walker will execute over this
300 * single tree if the reset is successful.
301 * @throws MissingObjectException
302 * the given tree object does not exist in this repository.
303 * @throws IncorrectObjectTypeException
304 * the given object id does not denote a tree, but instead names
305 * some other non-tree type of object. Note that commits are not
306 * trees, even if they are sometimes called a "tree-ish".
307 * @throws CorruptObjectException
308 * the object claimed to be a tree, but its contents did not
309 * appear to be a tree. The repository may have data corruption.
310 * @throws IOException
311 * a loose object or pack file could not be read.
313 public void reset(final AnyObjectId id) throws MissingObjectException,
314 IncorrectObjectTypeException, CorruptObjectException, IOException {
315 if (trees.length == 1) {
316 AbstractTreeIterator o = trees[0];
317 while (o.parent != null)
318 o = o.parent;
319 if (o instanceof CanonicalTreeParser) {
320 o.matches = null;
321 o.matchShift = 0;
322 ((CanonicalTreeParser) o).reset(db, id, curs);
323 trees[0] = o;
324 } else {
325 trees[0] = parserFor(id);
327 } else {
328 trees = new AbstractTreeIterator[] { parserFor(id) };
331 advance = false;
332 depth = 0;
336 * Reset this walker to run over a set of existing trees.
338 * @param ids
339 * the trees we need to parse. The walker will execute over this
340 * many parallel trees if the reset is successful.
341 * @throws MissingObjectException
342 * the given tree object does not exist in this repository.
343 * @throws IncorrectObjectTypeException
344 * the given object id does not denote a tree, but instead names
345 * some other non-tree type of object. Note that commits are not
346 * trees, even if they are sometimes called a "tree-ish".
347 * @throws CorruptObjectException
348 * the object claimed to be a tree, but its contents did not
349 * appear to be a tree. The repository may have data corruption.
350 * @throws IOException
351 * a loose object or pack file could not be read.
353 public void reset(final AnyObjectId[] ids) throws MissingObjectException,
354 IncorrectObjectTypeException, CorruptObjectException, IOException {
355 final int oldLen = trees.length;
356 final int newLen = ids.length;
357 final AbstractTreeIterator[] r = newLen == oldLen ? trees
358 : new AbstractTreeIterator[newLen];
359 for (int i = 0; i < newLen; i++) {
360 AbstractTreeIterator o;
362 if (i < oldLen) {
363 o = trees[i];
364 while (o.parent != null)
365 o = o.parent;
366 if (o instanceof CanonicalTreeParser && o.pathOffset == 0) {
367 o.matches = null;
368 o.matchShift = 0;
369 ((CanonicalTreeParser) o).reset(db, ids[i], curs);
370 r[i] = o;
371 continue;
375 o = parserFor(ids[i]);
376 r[i] = o;
379 trees = r;
380 advance = false;
381 depth = 0;
385 * Add an already existing tree object for walking.
386 * <p>
387 * The position of this tree is returned to the caller, in case the caller
388 * has lost track of the order they added the trees into the walker.
389 * <p>
390 * The tree must have the same root as existing trees in the walk.
392 * @param id
393 * identity of the tree object the caller wants walked.
394 * @return position of this tree within the walker.
395 * @throws MissingObjectException
396 * the given tree object does not exist in this repository.
397 * @throws IncorrectObjectTypeException
398 * the given object id does not denote a tree, but instead names
399 * some other non-tree type of object. Note that commits are not
400 * trees, even if they are sometimes called a "tree-ish".
401 * @throws CorruptObjectException
402 * the object claimed to be a tree, but its contents did not
403 * appear to be a tree. The repository may have data corruption.
404 * @throws IOException
405 * a loose object or pack file could not be read.
407 public int addTree(final ObjectId id) throws MissingObjectException,
408 IncorrectObjectTypeException, CorruptObjectException, IOException {
409 return addTree(parserFor(id));
413 * Add an already created tree iterator for walking.
414 * <p>
415 * The position of this tree is returned to the caller, in case the caller
416 * has lost track of the order they added the trees into the walker.
417 * <p>
418 * The tree which the iterator operates on must have the same root as
419 * existing trees in the walk.
421 * @param p
422 * an iterator to walk over. The iterator should be new, with no
423 * parent, and should still be positioned before the first entry.
424 * The tree which the iterator operates on must have the same root
425 * as other trees in the walk.
427 * @return position of this tree within the walker.
428 * @throws CorruptObjectException
429 * the iterator was unable to obtain its first entry, due to
430 * possible data corruption within the backing data store.
432 public int addTree(final AbstractTreeIterator p)
433 throws CorruptObjectException {
434 final int n = trees.length;
435 final AbstractTreeIterator[] newTrees = new AbstractTreeIterator[n + 1];
437 System.arraycopy(trees, 0, newTrees, 0, n);
438 newTrees[n] = p;
439 p.matches = null;
440 p.matchShift = 0;
442 trees = newTrees;
443 return n;
447 * Get the number of trees known to this walker.
449 * @return the total number of trees this walker is iterating over.
451 public int getTreeCount() {
452 return trees.length;
456 * Advance this walker to the next relevant entry.
458 * @return true if there is an entry available; false if all entries have
459 * been walked and the walk of this set of tree iterators is over.
460 * @throws MissingObjectException
461 * {@link #isRecursive()} was enabled, a subtree was found, but
462 * the subtree object does not exist in this repository. The
463 * repository may be missing objects.
464 * @throws IncorrectObjectTypeException
465 * {@link #isRecursive()} was enabled, a subtree was found, and
466 * the subtree id does not denote a tree, but instead names some
467 * other non-tree type of object. The repository may have data
468 * corruption.
469 * @throws CorruptObjectException
470 * the contents of a tree did not appear to be a tree. The
471 * repository may have data corruption.
472 * @throws IOException
473 * a loose object or pack file could not be read.
475 public boolean next() throws MissingObjectException,
476 IncorrectObjectTypeException, CorruptObjectException, IOException {
477 try {
478 if (advance) {
479 advance = false;
480 postChildren = false;
481 popEntriesEqual();
484 for (;;) {
485 final AbstractTreeIterator t = min();
486 if (t.eof()) {
487 if (depth > 0) {
488 exitSubtree();
489 if (postOrderTraversal) {
490 advance = true;
491 postChildren = true;
492 return true;
494 popEntriesEqual();
495 continue;
497 return false;
500 currentHead = t;
501 if (!filter.include(this)) {
502 skipEntriesEqual();
503 continue;
506 if (recursive && FileMode.TREE.equals(t.mode)) {
507 enterSubtree();
508 continue;
511 advance = true;
512 return true;
514 } catch (StopWalkException stop) {
515 for (final AbstractTreeIterator t : trees)
516 t.stopWalk();
517 return false;
522 * Obtain the tree iterator for the current entry.
523 * <p>
524 * Entering into (or exiting out of) a subtree causes the current tree
525 * iterator instance to be changed for the nth tree. This allows the tree
526 * iterators to manage only one list of items, with the diving handled by
527 * recursive trees.
529 * @param <T>
530 * type of the tree iterator expected by the caller.
531 * @param nth
532 * tree to obtain the current iterator of.
533 * @param clazz
534 * type of the tree iterator expected by the caller.
535 * @return r the current iterator of the requested type; null if the tree
536 * has no entry to match the current path.
538 public <T extends AbstractTreeIterator> T getTree(final int nth,
539 final Class<T> clazz) {
540 final AbstractTreeIterator t = trees[nth];
541 return t.matches == currentHead ? (T) t : null;
545 * Obtain the raw {@link FileMode} bits for the current entry.
546 * <p>
547 * Every added tree supplies mode bits, even if the tree does not contain
548 * the current entry. In the latter case {@link FileMode#MISSING}'s mode
549 * bits (0) are returned.
551 * @param nth
552 * tree to obtain the mode bits from.
553 * @return mode bits for the current entry of the nth tree.
554 * @see FileMode#fromBits(int)
556 public int getRawMode(final int nth) {
557 final AbstractTreeIterator t = trees[nth];
558 return t.matches == currentHead ? t.mode : 0;
562 * Obtain the {@link FileMode} for the current entry.
563 * <p>
564 * Every added tree supplies a mode, even if the tree does not contain the
565 * current entry. In the latter case {@link FileMode#MISSING} is returned.
567 * @param nth
568 * tree to obtain the mode from.
569 * @return mode for the current entry of the nth tree.
571 public FileMode getFileMode(final int nth) {
572 return FileMode.fromBits(getRawMode(nth));
576 * Obtain the ObjectId for the current entry.
577 * <p>
578 * Using this method to compare ObjectId values between trees of this walker
579 * is very inefficient. Applications should try to use
580 * {@link #idEqual(int, int)} or {@link #getObjectId(MutableObjectId, int)}
581 * whenever possible.
582 * <p>
583 * Every tree supplies an object id, even if the tree does not contain the
584 * current entry. In the latter case {@link ObjectId#zeroId()} is returned.
586 * @param nth
587 * tree to obtain the object identifier from.
588 * @return object identifier for the current tree entry.
589 * @see #getObjectId(MutableObjectId, int)
590 * @see #idEqual(int, int)
592 public ObjectId getObjectId(final int nth) {
593 final AbstractTreeIterator t = trees[nth];
594 return t.matches == currentHead ? t.getEntryObjectId() : ObjectId
595 .zeroId();
599 * Obtain the ObjectId for the current entry.
600 * <p>
601 * Every tree supplies an object id, even if the tree does not contain the
602 * current entry. In the latter case {@link ObjectId#zeroId()} is supplied.
603 * <p>
604 * Applications should try to use {@link #idEqual(int, int)} when possible
605 * as it avoids conversion overheads.
607 * @param out
608 * buffer to copy the object id into.
609 * @param nth
610 * tree to obtain the object identifier from.
611 * @see #idEqual(int, int)
613 public void getObjectId(final MutableObjectId out, final int nth) {
614 final AbstractTreeIterator t = trees[nth];
615 if (t.matches == currentHead)
616 t.getEntryObjectId(out);
617 else
618 out.clear();
622 * Compare two tree's current ObjectId values for equality.
624 * @param nthA
625 * first tree to compare the object id from.
626 * @param nthB
627 * second tree to compare the object id from.
628 * @return result of
629 * <code>getObjectId(nthA).equals(getObjectId(nthB))</code>.
630 * @see #getObjectId(int)
632 public boolean idEqual(final int nthA, final int nthB) {
633 final AbstractTreeIterator ch = currentHead;
634 final AbstractTreeIterator a = trees[nthA];
635 final AbstractTreeIterator b = trees[nthB];
636 if (a.matches == ch && b.matches == ch)
637 return a.idEqual(b);
638 if (a.matches != ch && b.matches != ch) {
639 // If neither tree matches the current path node then neither
640 // tree has this entry. In such case the ObjectId is zero(),
641 // and zero() is always equal to zero().
643 return true;
645 return false;
649 * Get the current entry's name within its parent tree.
650 * <p>
651 * This method is not very efficient and is primarily meant for debugging
652 * and final output generation. Applications should try to avoid calling it,
653 * and if invoked do so only once per interesting entry, where the name is
654 * absolutely required for correct function.
656 * @return name of the current entry within the parent tree (or directory).
657 * The name never includes a '/'.
659 public String getNameString() {
660 final AbstractTreeIterator t = currentHead;
661 final int off = t.pathOffset;
662 final int end = t.pathLen;
663 return RawParseUtils.decode(Constants.CHARSET, t.path, off, end);
667 * Get the current entry's complete path.
668 * <p>
669 * This method is not very efficient and is primarily meant for debugging
670 * and final output generation. Applications should try to avoid calling it,
671 * and if invoked do so only once per interesting entry, where the name is
672 * absolutely required for correct function.
674 * @return complete path of the current entry, from the root of the
675 * repository. If the current entry is in a subtree there will be at
676 * least one '/' in the returned string.
678 public String getPathString() {
679 return pathOf(currentHead);
683 * Get the current entry's complete path as a UTF-8 byte array.
685 * @return complete path of the current entry, from the root of the
686 * repository. If the current entry is in a subtree there will be at
687 * least one '/' in the returned string.
689 public byte[] getRawPath() {
690 final AbstractTreeIterator t = currentHead;
691 final int n = t.pathLen;
692 final byte[] r = new byte[n];
693 System.arraycopy(t.path, 0, r, 0, n);
694 return r;
698 * Test if the supplied path matches the current entry's path.
699 * <p>
700 * This method tests that the supplied path is exactly equal to the current
701 * entry, or is one of its parent directories. It is faster to use this
702 * method then to use {@link #getPathString()} to first create a String
703 * object, then test <code>startsWith</code> or some other type of string
704 * match function.
706 * @param p
707 * path buffer to test. Callers should ensure the path does not
708 * end with '/' prior to invocation.
709 * @param pLen
710 * number of bytes from <code>buf</code> to test.
711 * @return < 0 if p is before the current path; 0 if p matches the current
712 * path; 1 if the current path is past p and p will never match
713 * again on this tree walk.
715 public int isPathPrefix(final byte[] p, final int pLen) {
716 final AbstractTreeIterator t = currentHead;
717 final byte[] c = t.path;
718 final int cLen = t.pathLen;
719 int ci;
721 for (ci = 0; ci < cLen && ci < pLen; ci++) {
722 final int c_value = (c[ci] & 0xff) - (p[ci] & 0xff);
723 if (c_value != 0)
724 return c_value;
727 if (ci < cLen) {
728 // Ran out of pattern but we still had current data.
729 // If c[ci] == '/' then pattern matches the subtree.
730 // Otherwise we cannot be certain so we return -1.
732 return c[ci] == '/' ? 0 : -1;
735 if (ci < pLen) {
736 // Ran out of current, but we still have pattern data.
737 // If p[ci] == '/' then pattern matches this subtree,
738 // otherwise we cannot be certain so we return -1.
740 return p[ci] == '/' ? 0 : -1;
743 // Both strings are identical.
745 return 0;
749 * Test if the supplied path matches (being suffix of) the current entry's
750 * path.
751 * <p>
752 * This method tests that the supplied path is exactly equal to the current
753 * entry, or is relative to one of entry's parent directories. It is faster
754 * to use this method then to use {@link #getPathString()} to first create
755 * a String object, then test <code>endsWith</code> or some other type of
756 * string match function.
758 * @param p
759 * path buffer to test.
760 * @param pLen
761 * number of bytes from <code>buf</code> to test.
762 * @return true if p is suffix of the current path;
763 * false if otherwise
765 public boolean isPathSuffix(final byte[] p, final int pLen) {
766 final AbstractTreeIterator t = currentHead;
767 final byte[] c = t.path;
768 final int cLen = t.pathLen;
769 int ci;
771 for (ci = 1; ci < cLen && ci < pLen; ci++) {
772 if (c[cLen-ci] != p[pLen-ci])
773 return false;
776 return true;
780 * Get the current subtree depth of this walker.
782 * @return the current subtree depth of this walker.
784 public int getDepth() {
785 return depth;
789 * Is the current entry a subtree?
790 * <p>
791 * This method is faster then testing the raw mode bits of all trees to see
792 * if any of them are a subtree. If at least one is a subtree then this
793 * method will return true.
795 * @return true if {@link #enterSubtree()} will work on the current node.
797 public boolean isSubtree() {
798 return FileMode.TREE.equals(currentHead.mode);
802 * Is the current entry a subtree returned after its children?
804 * @return true if the current node is a tree that has been returned after
805 * its children were already processed.
806 * @see #isPostOrderTraversal()
808 public boolean isPostChildren() {
809 return postChildren && isSubtree();
813 * Enter into the current subtree.
814 * <p>
815 * If the current entry is a subtree this method arranges for its children
816 * to be returned before the next sibling following the subtree is returned.
818 * @throws MissingObjectException
819 * a subtree was found, but the subtree object does not exist in
820 * this repository. The repository may be missing objects.
821 * @throws IncorrectObjectTypeException
822 * a subtree was found, and the subtree id does not denote a
823 * tree, but instead names some other non-tree type of object.
824 * The repository may have data corruption.
825 * @throws CorruptObjectException
826 * the contents of a tree did not appear to be a tree. The
827 * repository may have data corruption.
828 * @throws IOException
829 * a loose object or pack file could not be read.
831 public void enterSubtree() throws MissingObjectException,
832 IncorrectObjectTypeException, CorruptObjectException, IOException {
833 final AbstractTreeIterator ch = currentHead;
834 final AbstractTreeIterator[] tmp = new AbstractTreeIterator[trees.length];
835 for (int i = 0; i < trees.length; i++) {
836 final AbstractTreeIterator t = trees[i];
837 final AbstractTreeIterator n;
838 if (t.matches == ch && !t.eof() && FileMode.TREE.equals(t.mode))
839 n = t.createSubtreeIterator(db, idBuffer, curs);
840 else
841 n = t.createEmptyTreeIterator();
842 tmp[i] = n;
844 depth++;
845 advance = false;
846 System.arraycopy(tmp, 0, trees, 0, trees.length);
849 AbstractTreeIterator min() throws CorruptObjectException {
850 int i = 0;
851 AbstractTreeIterator minRef = trees[i];
852 while (minRef.eof() && ++i < trees.length)
853 minRef = trees[i];
854 if (minRef.eof())
855 return minRef;
857 minRef.matches = minRef;
858 while (++i < trees.length) {
859 final AbstractTreeIterator t = trees[i];
860 if (t.eof())
861 continue;
862 final int cmp = t.pathCompare(minRef);
863 if (cmp < 0) {
864 t.matches = t;
865 minRef = t;
866 } else if (cmp == 0) {
867 t.matches = minRef;
871 return minRef;
874 void popEntriesEqual() throws CorruptObjectException {
875 final AbstractTreeIterator ch = currentHead;
876 for (int i = 0; i < trees.length; i++) {
877 final AbstractTreeIterator t = trees[i];
878 if (t.matches == ch) {
879 t.next(1);
880 t.matches = null;
885 void skipEntriesEqual() throws CorruptObjectException {
886 final AbstractTreeIterator ch = currentHead;
887 for (int i = 0; i < trees.length; i++) {
888 final AbstractTreeIterator t = trees[i];
889 if (t.matches == ch) {
890 t.skip();
891 t.matches = null;
896 private void exitSubtree() {
897 depth--;
898 for (int i = 0; i < trees.length; i++)
899 trees[i] = trees[i].parent;
901 AbstractTreeIterator minRef = null;
902 for (final AbstractTreeIterator t : trees) {
903 if (t.matches != t)
904 continue;
905 if (minRef == null || t.pathCompare(minRef) < 0)
906 minRef = t;
908 currentHead = minRef;
911 private CanonicalTreeParser parserFor(final AnyObjectId id)
912 throws IncorrectObjectTypeException, IOException {
913 final CanonicalTreeParser p = new CanonicalTreeParser();
914 p.reset(db, id, curs);
915 return p;
918 static String pathOf(final AbstractTreeIterator t) {
919 return RawParseUtils.decode(Constants.CHARSET, t.path, 0, t.pathLen);