Allow CanonicalTreeParsers to be created with a UTF-8 path prefix
[egit/qmx.git] / org.spearce.jgit / src / org / spearce / jgit / treewalk / TreeWalk.java
blobcbecb05e2e75fcc70894bbcd8ee37fb82cc6e2f5
1 /*
2 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
8 * conditions are met:
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
18 * - Neither the name of the Git Development Community nor the
19 * names of its contributors may be used to endorse or promote
20 * products derived from this software without specific prior
21 * written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
24 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 package org.spearce.jgit.treewalk;
40 import java.io.IOException;
41 import java.util.Collections;
43 import org.spearce.jgit.errors.CorruptObjectException;
44 import org.spearce.jgit.errors.IncorrectObjectTypeException;
45 import org.spearce.jgit.errors.MissingObjectException;
46 import org.spearce.jgit.errors.StopWalkException;
47 import org.spearce.jgit.lib.AnyObjectId;
48 import org.spearce.jgit.lib.Constants;
49 import org.spearce.jgit.lib.FileMode;
50 import org.spearce.jgit.lib.MutableObjectId;
51 import org.spearce.jgit.lib.ObjectId;
52 import org.spearce.jgit.lib.Repository;
53 import org.spearce.jgit.lib.WindowCursor;
54 import org.spearce.jgit.revwalk.RevTree;
55 import org.spearce.jgit.treewalk.filter.PathFilterGroup;
56 import org.spearce.jgit.treewalk.filter.TreeFilter;
57 import org.spearce.jgit.util.RawParseUtils;
59 /**
60 * Walks one or more {@link AbstractTreeIterator}s in parallel.
61 * <p>
62 * This class can perform n-way differences across as many trees as necessary.
63 * <p>
64 * A TreeWalk instance can only be used once to generate results. Running a
65 * second time requires creating a new TreeWalk instance, or invoking
66 * {@link #reset()} and adding new trees before starting again. Resetting an
67 * existing instance may be faster for some applications as some internal
68 * buffers may be recycled.
69 * <p>
70 * TreeWalk instances are not thread-safe. Applications must either restrict
71 * usage of a TreeWalk instance to a single thread, or implement their own
72 * synchronization at a higher level.
73 * <p>
74 * Multiple simultaneous TreeWalk instances per {@link Repository} are
75 * permitted, even from concurrent threads.
77 public class TreeWalk {
78 /**
79 * Open a tree walk and filter to exactly one path.
80 * <p>
81 * The returned tree walk is already positioned on the requested path, so
82 * the caller should not need to invoke {@link #next()} unless they are
83 * looking for a possible directory/file name conflict.
85 * @param db
86 * repository to read tree object data from.
87 * @param path
88 * single path to advance the tree walk instance into.
89 * @param trees
90 * one or more trees to walk through.
91 * @return a new tree walk configured for exactly this one path; null if no
92 * path was found in any of the trees.
93 * @throws IOException
94 * reading a pack file or loose object failed.
95 * @throws CorruptObjectException
96 * an tree object could not be read as its data stream did not
97 * appear to be a tree, or could not be inflated.
98 * @throws IncorrectObjectTypeException
99 * an object we expected to be a tree was not a tree.
100 * @throws MissingObjectException
101 * a tree object was not found.
103 public static TreeWalk forPath(final Repository db, final String path,
104 final AnyObjectId[] trees) throws MissingObjectException,
105 IncorrectObjectTypeException, CorruptObjectException, IOException {
106 final TreeWalk r = new TreeWalk(db);
107 r.setFilter(PathFilterGroup.createFromStrings(Collections
108 .singleton(path)));
109 r.setRecursive(r.getFilter().shouldBeRecursive());
110 r.reset(trees);
111 return r.next() ? r : null;
115 * Open a tree walk and filter to exactly one path.
116 * <p>
117 * The returned tree walk is already positioned on the requested path, so
118 * the caller should not need to invoke {@link #next()} unless they are
119 * looking for a possible directory/file name conflict.
121 * @param db
122 * repository to read tree object data from.
123 * @param path
124 * single path to advance the tree walk instance into.
125 * @param tree
126 * the single tree to walk through.
127 * @return a new tree walk configured for exactly this one path; null if no
128 * path was found in any of the trees.
129 * @throws IOException
130 * reading a pack file or loose object failed.
131 * @throws CorruptObjectException
132 * an tree object could not be read as its data stream did not
133 * appear to be a tree, or could not be inflated.
134 * @throws IncorrectObjectTypeException
135 * an object we expected to be a tree was not a tree.
136 * @throws MissingObjectException
137 * a tree object was not found.
139 public static TreeWalk forPath(final Repository db, final String path,
140 final RevTree tree) throws MissingObjectException,
141 IncorrectObjectTypeException, CorruptObjectException, IOException {
142 return forPath(db, path, new ObjectId[] { tree });
145 private final Repository db;
147 private final MutableObjectId idBuffer = new MutableObjectId();
149 private final WindowCursor curs = new WindowCursor();
151 private TreeFilter filter;
153 AbstractTreeIterator[] trees;
155 private boolean recursive;
157 private boolean postOrderTraversal;
159 private int depth;
161 private boolean advance;
163 private boolean postChildren;
165 AbstractTreeIterator currentHead;
168 * Create a new tree walker for a given repository.
170 * @param repo
171 * the repository the walker will obtain data from.
173 public TreeWalk(final Repository repo) {
174 db = repo;
175 filter = TreeFilter.ALL;
176 trees = new AbstractTreeIterator[] { new EmptyTreeIterator() };
180 * Get the repository this tree walker is reading from.
182 * @return the repository configured when the walker was created.
184 public Repository getRepository() {
185 return db;
189 * Get the currently configured filter.
191 * @return the current filter. Never null as a filter is always needed.
193 public TreeFilter getFilter() {
194 return filter;
198 * Set the tree entry filter for this walker.
199 * <p>
200 * Multiple filters may be combined by constructing an arbitrary tree of
201 * <code>AndTreeFilter</code> or <code>OrTreeFilter</code> instances to
202 * describe the boolean expression required by the application. Custom
203 * filter implementations may also be constructed by applications.
204 * <p>
205 * Note that filters are not thread-safe and may not be shared by concurrent
206 * TreeWalk instances. Every TreeWalk must be supplied its own unique
207 * filter, unless the filter implementation specifically states it is (and
208 * always will be) thread-safe. Callers may use {@link TreeFilter#clone()}
209 * to create a unique filter tree for this TreeWalk instance.
211 * @param newFilter
212 * the new filter. If null the special {@link TreeFilter#ALL}
213 * filter will be used instead, as it matches every entry.
214 * @see org.spearce.jgit.treewalk.filter.AndTreeFilter
215 * @see org.spearce.jgit.treewalk.filter.OrTreeFilter
217 public void setFilter(final TreeFilter newFilter) {
218 filter = newFilter != null ? newFilter : TreeFilter.ALL;
222 * Is this walker automatically entering into subtrees?
223 * <p>
224 * If the walker is recursive then the caller will not see a subtree node
225 * and instead will only receive file nodes in all relevant subtrees.
227 * @return true if automatically entering subtrees is enabled.
229 public boolean isRecursive() {
230 return recursive;
234 * Set the walker to enter (or not enter) subtrees automatically.
235 * <p>
236 * If recursive mode is enabled the walker will hide subtree nodes from the
237 * calling application and will produce only file level nodes. If a tree
238 * (directory) is deleted then all of the file level nodes will appear to be
239 * deleted, recursively, through as many levels as necessary to account for
240 * all entries.
242 * @param b
243 * true to skip subtree nodes and only obtain files nodes.
245 public void setRecursive(final boolean b) {
246 recursive = b;
250 * Does this walker return a tree entry after it exits the subtree?
251 * <p>
252 * If post order traversal is enabled then the walker will return a subtree
253 * after it has returned the last entry within that subtree. This may cause
254 * a subtree to be seen by the application twice if {@link #isRecursive()}
255 * is false, as the application will see it once, call
256 * {@link #enterSubtree()}, and then see it again as it leaves the subtree.
257 * <p>
258 * If an application does not enable {@link #isRecursive()} and it does not
259 * call {@link #enterSubtree()} then the tree is returned only once as none
260 * of the children were processed.
262 * @return true if subtrees are returned after entries within the subtree.
264 public boolean isPostOrderTraversal() {
265 return postOrderTraversal;
269 * Set the walker to return trees after their children.
271 * @param b
272 * true to get trees after their children.
273 * @see #isPostOrderTraversal()
275 public void setPostOrderTraversal(final boolean b) {
276 postOrderTraversal = b;
279 /** Reset this walker so new tree iterators can be added to it. */
280 public void reset() {
281 trees = new AbstractTreeIterator[0];
282 advance = false;
283 depth = 0;
287 * Reset this walker to run over a single existing tree.
289 * @param id
290 * the tree we need to parse. The walker will execute over this
291 * single tree if the reset is successful.
292 * @throws MissingObjectException
293 * the given tree object does not exist in this repository.
294 * @throws IncorrectObjectTypeException
295 * the given object id does not denote a tree, but instead names
296 * some other non-tree type of object. Note that commits are not
297 * trees, even if they are sometimes called a "tree-ish".
298 * @throws CorruptObjectException
299 * the object claimed to be a tree, but its contents did not
300 * appear to be a tree. The repository may have data corruption.
301 * @throws IOException
302 * a loose object or pack file could not be read.
304 public void reset(final AnyObjectId id) throws MissingObjectException,
305 IncorrectObjectTypeException, CorruptObjectException, IOException {
306 if (trees.length == 1) {
307 AbstractTreeIterator o = trees[0];
308 while (o.parent != null)
309 o = o.parent;
310 if (o instanceof CanonicalTreeParser) {
311 o.matches = null;
312 o.matchShift = 0;
313 ((CanonicalTreeParser) o).reset(db, id, curs);
314 trees[0] = o;
315 } else {
316 trees[0] = parserFor(id);
318 } else {
319 trees = new AbstractTreeIterator[] { parserFor(id) };
322 advance = false;
323 depth = 0;
327 * Reset this walker to run over a set of existing trees.
329 * @param ids
330 * the trees we need to parse. The walker will execute over this
331 * many parallel trees if the reset is successful.
332 * @throws MissingObjectException
333 * the given tree object does not exist in this repository.
334 * @throws IncorrectObjectTypeException
335 * the given object id does not denote a tree, but instead names
336 * some other non-tree type of object. Note that commits are not
337 * trees, even if they are sometimes called a "tree-ish".
338 * @throws CorruptObjectException
339 * the object claimed to be a tree, but its contents did not
340 * appear to be a tree. The repository may have data corruption.
341 * @throws IOException
342 * a loose object or pack file could not be read.
344 public void reset(final AnyObjectId[] ids) throws MissingObjectException,
345 IncorrectObjectTypeException, CorruptObjectException, IOException {
346 final int oldLen = trees.length;
347 final int newLen = ids.length;
348 final AbstractTreeIterator[] r = newLen == oldLen ? trees
349 : new AbstractTreeIterator[newLen];
350 for (int i = 0; i < newLen; i++) {
351 AbstractTreeIterator o;
353 if (i < oldLen) {
354 o = trees[i];
355 while (o.parent != null)
356 o = o.parent;
357 if (o instanceof CanonicalTreeParser && o.pathOffset == 0) {
358 o.matches = null;
359 o.matchShift = 0;
360 ((CanonicalTreeParser) o).reset(db, ids[i], curs);
361 r[i] = o;
362 continue;
366 o = parserFor(ids[i]);
367 r[i] = o;
370 trees = r;
371 advance = false;
372 depth = 0;
376 * Add an already existing tree object for walking.
377 * <p>
378 * The position of this tree is returned to the caller, in case the caller
379 * has lost track of the order they added the trees into the walker.
381 * @param id
382 * identity of the tree object the caller wants walked.
383 * @return position of this tree within the walker.
384 * @throws MissingObjectException
385 * the given tree object does not exist in this repository.
386 * @throws IncorrectObjectTypeException
387 * the given object id does not denote a tree, but instead names
388 * some other non-tree type of object. Note that commits are not
389 * trees, even if they are sometimes called a "tree-ish".
390 * @throws CorruptObjectException
391 * the object claimed to be a tree, but its contents did not
392 * appear to be a tree. The repository may have data corruption.
393 * @throws IOException
394 * a loose object or pack file could not be read.
396 public int addTree(final ObjectId id) throws MissingObjectException,
397 IncorrectObjectTypeException, CorruptObjectException, IOException {
398 return addTree(parserFor(id));
402 * Add an already created tree iterator for walking.
403 * <p>
404 * The position of this tree is returned to the caller, in case the caller
405 * has lost track of the order they added the trees into the walker.
407 * @param p
408 * an iterator to walk over. The iterator should be new, with no
409 * parent, and should still be positioned before the first entry.
410 * @return position of this tree within the walker.
411 * @throws CorruptObjectException
412 * the iterator was unable to obtain its first entry, due to
413 * possible data corruption within the backing data store.
415 public int addTree(final AbstractTreeIterator p)
416 throws CorruptObjectException {
417 final int n = trees.length;
418 final AbstractTreeIterator[] newTrees = new AbstractTreeIterator[n + 1];
420 System.arraycopy(trees, 0, newTrees, 0, n);
421 newTrees[n] = p;
422 p.matches = null;
423 p.matchShift = 0;
425 trees = newTrees;
426 return n;
430 * Get the number of trees known to this walker.
432 * @return the total number of trees this walker is iterating over.
434 public int getTreeCount() {
435 return trees.length;
439 * Advance this walker to the next relevant entry.
441 * @return true if there is an entry available; false if all entries have
442 * been walked and the walk of this set of tree iterators is over.
443 * @throws MissingObjectException
444 * {@link #isRecursive()} was enabled, a subtree was found, but
445 * the subtree object does not exist in this repository. The
446 * repository may be missing objects.
447 * @throws IncorrectObjectTypeException
448 * {@link #isRecursive()} was enabled, a subtree was found, and
449 * the subtree id does not denote a tree, but instead names some
450 * other non-tree type of object. The repository may have data
451 * corruption.
452 * @throws CorruptObjectException
453 * the contents of a tree did not appear to be a tree. The
454 * repository may have data corruption.
455 * @throws IOException
456 * a loose object or pack file could not be read.
458 public boolean next() throws MissingObjectException,
459 IncorrectObjectTypeException, CorruptObjectException, IOException {
460 try {
461 if (advance) {
462 advance = false;
463 postChildren = false;
464 popEntriesEqual();
467 for (;;) {
468 final AbstractTreeIterator t = min();
469 if (t.eof()) {
470 if (depth > 0) {
471 exitSubtree();
472 if (postOrderTraversal) {
473 advance = true;
474 postChildren = true;
475 return true;
477 popEntriesEqual();
478 continue;
480 return false;
483 currentHead = t;
484 if (!filter.include(this)) {
485 skipEntriesEqual();
486 continue;
489 if (recursive && FileMode.TREE.equals(t.mode)) {
490 enterSubtree();
491 continue;
494 advance = true;
495 return true;
497 } catch (StopWalkException stop) {
498 for (final AbstractTreeIterator t : trees)
499 t.stopWalk();
500 return false;
505 * Obtain the tree iterator for the current entry.
506 * <p>
507 * Entering into (or exiting out of) a subtree causes the current tree
508 * iterator instance to be changed for the nth tree. This allows the tree
509 * iterators to manage only one list of items, with the diving handled by
510 * recursive trees.
512 * @param <T>
513 * type of the tree iterator expected by the caller.
514 * @param nth
515 * tree to obtain the current iterator of.
516 * @param clazz
517 * type of the tree iterator expected by the caller.
518 * @return r the current iterator of the requested type; null if the tree
519 * has no entry to match the current path.
521 public <T extends AbstractTreeIterator> T getTree(final int nth,
522 final Class<T> clazz) {
523 final AbstractTreeIterator t = trees[nth];
524 return t.matches == currentHead ? (T) t : null;
528 * Obtain the raw {@link FileMode} bits for the current entry.
529 * <p>
530 * Every added tree supplies mode bits, even if the tree does not contain
531 * the current entry. In the latter case {@link FileMode#MISSING}'s mode
532 * bits (0) are returned.
534 * @param nth
535 * tree to obtain the mode bits from.
536 * @return mode bits for the current entry of the nth tree.
537 * @see FileMode#fromBits(int)
539 public int getRawMode(final int nth) {
540 final AbstractTreeIterator t = trees[nth];
541 return t.matches == currentHead ? t.mode : 0;
545 * Obtain the {@link FileMode} for the current entry.
546 * <p>
547 * Every added tree supplies a mode, even if the tree does not contain the
548 * current entry. In the latter case {@link FileMode#MISSING} is returned.
550 * @param nth
551 * tree to obtain the mode from.
552 * @return mode for the current entry of the nth tree.
554 public FileMode getFileMode(final int nth) {
555 return FileMode.fromBits(getRawMode(nth));
559 * Obtain the ObjectId for the current entry.
560 * <p>
561 * Using this method to compare ObjectId values between trees of this walker
562 * is very inefficient. Applications should try to use
563 * {@link #idEqual(int, int)} or {@link #getObjectId(MutableObjectId, int)}
564 * whenever possible.
565 * <p>
566 * Every tree supplies an object id, even if the tree does not contain the
567 * current entry. In the latter case {@link ObjectId#zeroId()} is returned.
569 * @param nth
570 * tree to obtain the object identifier from.
571 * @return object identifier for the current tree entry.
572 * @see #getObjectId(MutableObjectId, int)
573 * @see #idEqual(int, int)
575 public ObjectId getObjectId(final int nth) {
576 final AbstractTreeIterator t = trees[nth];
577 return t.matches == currentHead ? t.getEntryObjectId() : ObjectId
578 .zeroId();
582 * Obtain the ObjectId for the current entry.
583 * <p>
584 * Every tree supplies an object id, even if the tree does not contain the
585 * current entry. In the latter case {@link ObjectId#zeroId()} is supplied.
586 * <p>
587 * Applications should try to use {@link #idEqual(int, int)} when possible
588 * as it avoids conversion overheads.
590 * @param out
591 * buffer to copy the object id into.
592 * @param nth
593 * tree to obtain the object identifier from.
594 * @see #idEqual(int, int)
596 public void getObjectId(final MutableObjectId out, final int nth) {
597 final AbstractTreeIterator t = trees[nth];
598 if (t.matches == currentHead)
599 t.getEntryObjectId(out);
600 else
601 out.clear();
605 * Compare two tree's current ObjectId values for equality.
607 * @param nthA
608 * first tree to compare the object id from.
609 * @param nthB
610 * second tree to compare the object id from.
611 * @return result of
612 * <code>getObjectId(nthA).equals(getObjectId(nthB))</code>.
613 * @see #getObjectId(int)
615 public boolean idEqual(final int nthA, final int nthB) {
616 final AbstractTreeIterator ch = currentHead;
617 final AbstractTreeIterator a = trees[nthA];
618 final AbstractTreeIterator b = trees[nthB];
619 if (a.matches == ch && b.matches == ch)
620 return a.idEqual(b);
621 if (a.matches != ch && b.matches != ch) {
622 // If neither tree matches the current path node then neither
623 // tree has this entry. In such case the ObjectId is zero(),
624 // and zero() is always equal to zero().
626 return true;
628 return false;
632 * Get the current entry's name within its parent tree.
633 * <p>
634 * This method is not very efficient and is primarily meant for debugging
635 * and final output generation. Applications should try to avoid calling it,
636 * and if invoked do so only once per interesting entry, where the name is
637 * absolutely required for correct function.
639 * @return name of the current entry within the parent tree (or directory).
640 * The name never includes a '/'.
642 public String getNameString() {
643 final AbstractTreeIterator t = currentHead;
644 final int off = t.pathOffset;
645 final int end = t.pathLen;
646 return RawParseUtils.decode(Constants.CHARSET, t.path, off, end);
650 * Get the current entry's complete path.
651 * <p>
652 * This method is not very efficient and is primarily meant for debugging
653 * and final output generation. Applications should try to avoid calling it,
654 * and if invoked do so only once per interesting entry, where the name is
655 * absolutely required for correct function.
657 * @return complete path of the current entry, from the root of the
658 * repository. If the current entry is in a subtree there will be at
659 * least one '/' in the returned string.
661 public String getPathString() {
662 return pathOf(currentHead);
666 * Get the current entry's complete path as a UTF-8 byte array.
668 * @return complete path of the current entry, from the root of the
669 * repository. If the current entry is in a subtree there will be at
670 * least one '/' in the returned string.
672 public byte[] getRawPath() {
673 final AbstractTreeIterator t = currentHead;
674 final int n = t.pathLen;
675 final byte[] r = new byte[n];
676 System.arraycopy(t.path, 0, r, 0, n);
677 return r;
681 * Test if the supplied path matches the current entry's path.
682 * <p>
683 * This method tests that the supplied path is exactly equal to the current
684 * entry, or is one of its parent directories. It is faster to use this
685 * method then to use {@link #getPathString()} to first create a String
686 * object, then test <code>startsWith</code> or some other type of string
687 * match function.
689 * @param p
690 * path buffer to test. Callers should ensure the path does not
691 * end with '/' prior to invocation.
692 * @param pLen
693 * number of bytes from <code>buf</code> to test.
694 * @return < 0 if p is before the current path; 0 if p matches the current
695 * path; 1 if the current path is past p and p will never match
696 * again on this tree walk.
698 public int isPathPrefix(final byte[] p, final int pLen) {
699 final AbstractTreeIterator t = currentHead;
700 final byte[] c = t.path;
701 final int cLen = t.pathLen;
702 int ci;
704 for (ci = 0; ci < cLen && ci < pLen; ci++) {
705 final int c_value = (c[ci] & 0xff) - (p[ci] & 0xff);
706 if (c_value != 0)
707 return c_value;
710 if (ci < cLen) {
711 // Ran out of pattern but we still had current data.
712 // If c[ci] == '/' then pattern matches the subtree.
713 // Otherwise we cannot be certain so we return -1.
715 return c[ci] == '/' ? 0 : -1;
718 if (ci < pLen) {
719 // Ran out of current, but we still have pattern data.
720 // If p[ci] == '/' then pattern matches this subtree,
721 // otherwise we cannot be certain so we return -1.
723 return p[ci] == '/' ? 0 : -1;
726 // Both strings are identical.
728 return 0;
732 * Is the current entry a subtree?
733 * <p>
734 * This method is faster then testing the raw mode bits of all trees to see
735 * if any of them are a subtree. If at least one is a subtree then this
736 * method will return true.
738 * @return true if {@link #enterSubtree()} will work on the current node.
740 public boolean isSubtree() {
741 return FileMode.TREE.equals(currentHead.mode);
745 * Is the current entry a subtree returned after its children?
747 * @return true if the current node is a tree that has been returned after
748 * its children were already processed.
749 * @see #isPostOrderTraversal()
751 public boolean isPostChildren() {
752 return postChildren && isSubtree();
756 * Enter into the current subtree.
757 * <p>
758 * If the current entry is a subtree this method arranges for its children
759 * to be returned before the next sibling following the subtree is returned.
761 * @throws MissingObjectException
762 * a subtree was found, but the subtree object does not exist in
763 * this repository. The repository may be missing objects.
764 * @throws IncorrectObjectTypeException
765 * a subtree was found, and the subtree id does not denote a
766 * tree, but instead names some other non-tree type of object.
767 * The repository may have data corruption.
768 * @throws CorruptObjectException
769 * the contents of a tree did not appear to be a tree. The
770 * repository may have data corruption.
771 * @throws IOException
772 * a loose object or pack file could not be read.
774 public void enterSubtree() throws MissingObjectException,
775 IncorrectObjectTypeException, CorruptObjectException, IOException {
776 final AbstractTreeIterator ch = currentHead;
777 final AbstractTreeIterator[] tmp = new AbstractTreeIterator[trees.length];
778 for (int i = 0; i < trees.length; i++) {
779 final AbstractTreeIterator t = trees[i];
780 final AbstractTreeIterator n;
781 if (t.matches == ch && !t.eof() && FileMode.TREE.equals(t.mode))
782 n = t.createSubtreeIterator(db, idBuffer, curs);
783 else
784 n = new EmptyTreeIterator(t);
785 tmp[i] = n;
787 depth++;
788 advance = false;
789 System.arraycopy(tmp, 0, trees, 0, trees.length);
792 AbstractTreeIterator min() throws CorruptObjectException {
793 int i = 0;
794 AbstractTreeIterator minRef = trees[i];
795 while (minRef.eof() && ++i < trees.length)
796 minRef = trees[i];
797 if (minRef.eof())
798 return minRef;
800 minRef.matches = minRef;
801 while (++i < trees.length) {
802 final AbstractTreeIterator t = trees[i];
803 if (t.eof())
804 continue;
805 final int cmp = t.pathCompare(minRef);
806 if (cmp < 0) {
807 t.matches = t;
808 minRef = t;
809 } else if (cmp == 0) {
810 t.matches = minRef;
814 return minRef;
817 void popEntriesEqual() throws CorruptObjectException {
818 final AbstractTreeIterator ch = currentHead;
819 for (int i = 0; i < trees.length; i++) {
820 final AbstractTreeIterator t = trees[i];
821 if (t.matches == ch) {
822 t.next(1);
823 t.matches = null;
828 void skipEntriesEqual() throws CorruptObjectException {
829 final AbstractTreeIterator ch = currentHead;
830 for (int i = 0; i < trees.length; i++) {
831 final AbstractTreeIterator t = trees[i];
832 if (t.matches == ch) {
833 t.skip();
834 t.matches = null;
839 private void exitSubtree() {
840 depth--;
841 for (int i = 0; i < trees.length; i++)
842 trees[i] = trees[i].parent;
844 AbstractTreeIterator minRef = null;
845 for (final AbstractTreeIterator t : trees) {
846 if (t.matches != t)
847 continue;
848 if (minRef == null || t.pathCompare(minRef) < 0)
849 minRef = t;
851 currentHead = minRef;
854 private CanonicalTreeParser parserFor(final AnyObjectId id)
855 throws IncorrectObjectTypeException, IOException {
856 final CanonicalTreeParser p = new CanonicalTreeParser();
857 p.reset(db, id, curs);
858 return p;
861 static String pathOf(final AbstractTreeIterator t) {
862 return RawParseUtils.decode(Constants.CHARSET, t.path, 0, t.pathLen);