You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TreeWalk.java 33KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999
  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.
  5. *
  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
  10. *
  11. * All rights reserved.
  12. *
  13. * Redistribution and use in source and binary forms, with or
  14. * without modification, are permitted provided that the following
  15. * conditions are met:
  16. *
  17. * - Redistributions of source code must retain the above copyright
  18. * notice, this list of conditions and the following disclaimer.
  19. *
  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.
  24. *
  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.
  29. *
  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.
  43. */
  44. package org.eclipse.jgit.treewalk;
  45. import java.io.IOException;
  46. import org.eclipse.jgit.errors.CorruptObjectException;
  47. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  48. import org.eclipse.jgit.errors.MissingObjectException;
  49. import org.eclipse.jgit.errors.StopWalkException;
  50. import org.eclipse.jgit.lib.AnyObjectId;
  51. import org.eclipse.jgit.lib.Constants;
  52. import org.eclipse.jgit.lib.FileMode;
  53. import org.eclipse.jgit.lib.MutableObjectId;
  54. import org.eclipse.jgit.lib.ObjectId;
  55. import org.eclipse.jgit.lib.ObjectReader;
  56. import org.eclipse.jgit.lib.Repository;
  57. import org.eclipse.jgit.revwalk.RevTree;
  58. import org.eclipse.jgit.treewalk.filter.PathFilter;
  59. import org.eclipse.jgit.treewalk.filter.TreeFilter;
  60. import org.eclipse.jgit.util.RawParseUtils;
  61. /**
  62. * Walks one or more {@link AbstractTreeIterator}s in parallel.
  63. * <p>
  64. * This class can perform n-way differences across as many trees as necessary.
  65. * <p>
  66. * Each tree added must have the same root as existing trees in the walk.
  67. * <p>
  68. * A TreeWalk instance can only be used once to generate results. Running a
  69. * second time requires creating a new TreeWalk instance, or invoking
  70. * {@link #reset()} and adding new trees before starting again. Resetting an
  71. * existing instance may be faster for some applications as some internal
  72. * buffers may be recycled.
  73. * <p>
  74. * TreeWalk instances are not thread-safe. Applications must either restrict
  75. * usage of a TreeWalk instance to a single thread, or implement their own
  76. * synchronization at a higher level.
  77. * <p>
  78. * Multiple simultaneous TreeWalk instances per {@link Repository} are
  79. * permitted, even from concurrent threads.
  80. */
  81. public class TreeWalk {
  82. private static final AbstractTreeIterator[] NO_TREES = {};
  83. /**
  84. * Open a tree walk and filter to exactly one path.
  85. * <p>
  86. * The returned tree walk is already positioned on the requested path, so
  87. * the caller should not need to invoke {@link #next()} unless they are
  88. * looking for a possible directory/file name conflict.
  89. *
  90. * @param reader
  91. * the reader the walker will obtain tree data from.
  92. * @param path
  93. * single path to advance the tree walk instance into.
  94. * @param trees
  95. * one or more trees to walk through, all with the same root.
  96. * @return a new tree walk configured for exactly this one path; null if no
  97. * path was found in any of the trees.
  98. * @throws IOException
  99. * reading a pack file or loose object failed.
  100. * @throws CorruptObjectException
  101. * an tree object could not be read as its data stream did not
  102. * appear to be a tree, or could not be inflated.
  103. * @throws IncorrectObjectTypeException
  104. * an object we expected to be a tree was not a tree.
  105. * @throws MissingObjectException
  106. * a tree object was not found.
  107. */
  108. public static TreeWalk forPath(final ObjectReader reader, final String path,
  109. final AnyObjectId... trees) throws MissingObjectException,
  110. IncorrectObjectTypeException, CorruptObjectException, IOException {
  111. TreeWalk tw = new TreeWalk(reader);
  112. PathFilter f = PathFilter.create(path);
  113. tw.setFilter(f);
  114. tw.reset(trees);
  115. tw.setRecursive(false);
  116. while (tw.next()) {
  117. if (f.isDone(tw)) {
  118. return tw;
  119. } else if (tw.isSubtree()) {
  120. tw.enterSubtree();
  121. }
  122. }
  123. return null;
  124. }
  125. /**
  126. * Open a tree walk and filter to exactly one path.
  127. * <p>
  128. * The returned tree walk is already positioned on the requested path, so
  129. * the caller should not need to invoke {@link #next()} unless they are
  130. * looking for a possible directory/file name conflict.
  131. *
  132. * @param db
  133. * repository to read tree object data from.
  134. * @param path
  135. * single path to advance the tree walk instance into.
  136. * @param trees
  137. * one or more trees to walk through, all with the same root.
  138. * @return a new tree walk configured for exactly this one path; null if no
  139. * path was found in any of the trees.
  140. * @throws IOException
  141. * reading a pack file or loose object failed.
  142. * @throws CorruptObjectException
  143. * an tree object could not be read as its data stream did not
  144. * appear to be a tree, or could not be inflated.
  145. * @throws IncorrectObjectTypeException
  146. * an object we expected to be a tree was not a tree.
  147. * @throws MissingObjectException
  148. * a tree object was not found.
  149. */
  150. public static TreeWalk forPath(final Repository db, final String path,
  151. final AnyObjectId... trees) throws MissingObjectException,
  152. IncorrectObjectTypeException, CorruptObjectException, IOException {
  153. ObjectReader reader = db.newObjectReader();
  154. try {
  155. return forPath(reader, path, trees);
  156. } finally {
  157. reader.release();
  158. }
  159. }
  160. /**
  161. * Open a tree walk and filter to exactly one path.
  162. * <p>
  163. * The returned tree walk is already positioned on the requested path, so
  164. * the caller should not need to invoke {@link #next()} unless they are
  165. * looking for a possible directory/file name conflict.
  166. *
  167. * @param db
  168. * repository to read tree object data from.
  169. * @param path
  170. * single path to advance the tree walk instance into.
  171. * @param tree
  172. * the single tree to walk through.
  173. * @return a new tree walk configured for exactly this one path; null if no
  174. * path was found in any of the trees.
  175. * @throws IOException
  176. * reading a pack file or loose object failed.
  177. * @throws CorruptObjectException
  178. * an tree object could not be read as its data stream did not
  179. * appear to be a tree, or could not be inflated.
  180. * @throws IncorrectObjectTypeException
  181. * an object we expected to be a tree was not a tree.
  182. * @throws MissingObjectException
  183. * a tree object was not found.
  184. */
  185. public static TreeWalk forPath(final Repository db, final String path,
  186. final RevTree tree) throws MissingObjectException,
  187. IncorrectObjectTypeException, CorruptObjectException, IOException {
  188. return forPath(db, path, new ObjectId[] { tree });
  189. }
  190. private final ObjectReader reader;
  191. private final MutableObjectId idBuffer = new MutableObjectId();
  192. private TreeFilter filter;
  193. AbstractTreeIterator[] trees;
  194. private boolean recursive;
  195. private boolean postOrderTraversal;
  196. private int depth;
  197. private boolean advance;
  198. private boolean postChildren;
  199. AbstractTreeIterator currentHead;
  200. /**
  201. * Create a new tree walker for a given repository.
  202. *
  203. * @param repo
  204. * the repository the walker will obtain data from.
  205. */
  206. public TreeWalk(final Repository repo) {
  207. this(repo.newObjectReader());
  208. }
  209. /**
  210. * Create a new tree walker for a given repository.
  211. *
  212. * @param or
  213. * the reader the walker will obtain tree data from.
  214. */
  215. public TreeWalk(final ObjectReader or) {
  216. reader = or;
  217. filter = TreeFilter.ALL;
  218. trees = NO_TREES;
  219. }
  220. /** @return the reader this walker is using to load objects. */
  221. public ObjectReader getObjectReader() {
  222. return reader;
  223. }
  224. /**
  225. * Release any resources used by this walker's reader.
  226. * <p>
  227. * A walker that has been released can be used again, but may need to be
  228. * released after the subsequent usage.
  229. */
  230. public void release() {
  231. reader.release();
  232. }
  233. /**
  234. * Get the currently configured filter.
  235. *
  236. * @return the current filter. Never null as a filter is always needed.
  237. */
  238. public TreeFilter getFilter() {
  239. return filter;
  240. }
  241. /**
  242. * Set the tree entry filter for this walker.
  243. * <p>
  244. * Multiple filters may be combined by constructing an arbitrary tree of
  245. * <code>AndTreeFilter</code> or <code>OrTreeFilter</code> instances to
  246. * describe the boolean expression required by the application. Custom
  247. * filter implementations may also be constructed by applications.
  248. * <p>
  249. * Note that filters are not thread-safe and may not be shared by concurrent
  250. * TreeWalk instances. Every TreeWalk must be supplied its own unique
  251. * filter, unless the filter implementation specifically states it is (and
  252. * always will be) thread-safe. Callers may use {@link TreeFilter#clone()}
  253. * to create a unique filter tree for this TreeWalk instance.
  254. *
  255. * @param newFilter
  256. * the new filter. If null the special {@link TreeFilter#ALL}
  257. * filter will be used instead, as it matches every entry.
  258. * @see org.eclipse.jgit.treewalk.filter.AndTreeFilter
  259. * @see org.eclipse.jgit.treewalk.filter.OrTreeFilter
  260. */
  261. public void setFilter(final TreeFilter newFilter) {
  262. filter = newFilter != null ? newFilter : TreeFilter.ALL;
  263. }
  264. /**
  265. * Is this walker automatically entering into subtrees?
  266. * <p>
  267. * If the walker is recursive then the caller will not see a subtree node
  268. * and instead will only receive file nodes in all relevant subtrees.
  269. *
  270. * @return true if automatically entering subtrees is enabled.
  271. */
  272. public boolean isRecursive() {
  273. return recursive;
  274. }
  275. /**
  276. * Set the walker to enter (or not enter) subtrees automatically.
  277. * <p>
  278. * If recursive mode is enabled the walker will hide subtree nodes from the
  279. * calling application and will produce only file level nodes. If a tree
  280. * (directory) is deleted then all of the file level nodes will appear to be
  281. * deleted, recursively, through as many levels as necessary to account for
  282. * all entries.
  283. *
  284. * @param b
  285. * true to skip subtree nodes and only obtain files nodes.
  286. */
  287. public void setRecursive(final boolean b) {
  288. recursive = b;
  289. }
  290. /**
  291. * Does this walker return a tree entry after it exits the subtree?
  292. * <p>
  293. * If post order traversal is enabled then the walker will return a subtree
  294. * after it has returned the last entry within that subtree. This may cause
  295. * a subtree to be seen by the application twice if {@link #isRecursive()}
  296. * is false, as the application will see it once, call
  297. * {@link #enterSubtree()}, and then see it again as it leaves the subtree.
  298. * <p>
  299. * If an application does not enable {@link #isRecursive()} and it does not
  300. * call {@link #enterSubtree()} then the tree is returned only once as none
  301. * of the children were processed.
  302. *
  303. * @return true if subtrees are returned after entries within the subtree.
  304. */
  305. public boolean isPostOrderTraversal() {
  306. return postOrderTraversal;
  307. }
  308. /**
  309. * Set the walker to return trees after their children.
  310. *
  311. * @param b
  312. * true to get trees after their children.
  313. * @see #isPostOrderTraversal()
  314. */
  315. public void setPostOrderTraversal(final boolean b) {
  316. postOrderTraversal = b;
  317. }
  318. /** Reset this walker so new tree iterators can be added to it. */
  319. public void reset() {
  320. trees = NO_TREES;
  321. advance = false;
  322. depth = 0;
  323. }
  324. /**
  325. * Reset this walker to run over a single existing tree.
  326. *
  327. * @param id
  328. * the tree we need to parse. The walker will execute over this
  329. * single tree if the reset is successful.
  330. * @throws MissingObjectException
  331. * the given tree object does not exist in this repository.
  332. * @throws IncorrectObjectTypeException
  333. * the given object id does not denote a tree, but instead names
  334. * some other non-tree type of object. Note that commits are not
  335. * trees, even if they are sometimes called a "tree-ish".
  336. * @throws CorruptObjectException
  337. * the object claimed to be a tree, but its contents did not
  338. * appear to be a tree. The repository may have data corruption.
  339. * @throws IOException
  340. * a loose object or pack file could not be read.
  341. */
  342. public void reset(final AnyObjectId id) throws MissingObjectException,
  343. IncorrectObjectTypeException, CorruptObjectException, IOException {
  344. if (trees.length == 1) {
  345. AbstractTreeIterator o = trees[0];
  346. while (o.parent != null)
  347. o = o.parent;
  348. if (o instanceof CanonicalTreeParser) {
  349. o.matches = null;
  350. o.matchShift = 0;
  351. ((CanonicalTreeParser) o).reset(reader, id);
  352. trees[0] = o;
  353. } else {
  354. trees[0] = parserFor(id);
  355. }
  356. } else {
  357. trees = new AbstractTreeIterator[] { parserFor(id) };
  358. }
  359. advance = false;
  360. depth = 0;
  361. }
  362. /**
  363. * Reset this walker to run over a set of existing trees.
  364. *
  365. * @param ids
  366. * the trees we need to parse. The walker will execute over this
  367. * many parallel trees if the reset is successful.
  368. * @throws MissingObjectException
  369. * the given tree object does not exist in this repository.
  370. * @throws IncorrectObjectTypeException
  371. * the given object id does not denote a tree, but instead names
  372. * some other non-tree type of object. Note that commits are not
  373. * trees, even if they are sometimes called a "tree-ish".
  374. * @throws CorruptObjectException
  375. * the object claimed to be a tree, but its contents did not
  376. * appear to be a tree. The repository may have data corruption.
  377. * @throws IOException
  378. * a loose object or pack file could not be read.
  379. */
  380. public void reset(final AnyObjectId... ids) throws MissingObjectException,
  381. IncorrectObjectTypeException, CorruptObjectException, IOException {
  382. final int oldLen = trees.length;
  383. final int newLen = ids.length;
  384. final AbstractTreeIterator[] r = newLen == oldLen ? trees
  385. : new AbstractTreeIterator[newLen];
  386. for (int i = 0; i < newLen; i++) {
  387. AbstractTreeIterator o;
  388. if (i < oldLen) {
  389. o = trees[i];
  390. while (o.parent != null)
  391. o = o.parent;
  392. if (o instanceof CanonicalTreeParser && o.pathOffset == 0) {
  393. o.matches = null;
  394. o.matchShift = 0;
  395. ((CanonicalTreeParser) o).reset(reader, ids[i]);
  396. r[i] = o;
  397. continue;
  398. }
  399. }
  400. o = parserFor(ids[i]);
  401. r[i] = o;
  402. }
  403. trees = r;
  404. advance = false;
  405. depth = 0;
  406. }
  407. /**
  408. * Add an already existing tree object for walking.
  409. * <p>
  410. * The position of this tree is returned to the caller, in case the caller
  411. * has lost track of the order they added the trees into the walker.
  412. * <p>
  413. * The tree must have the same root as existing trees in the walk.
  414. *
  415. * @param id
  416. * identity of the tree object the caller wants walked.
  417. * @return position of this tree within the walker.
  418. * @throws MissingObjectException
  419. * the given tree object does not exist in this repository.
  420. * @throws IncorrectObjectTypeException
  421. * the given object id does not denote a tree, but instead names
  422. * some other non-tree type of object. Note that commits are not
  423. * trees, even if they are sometimes called a "tree-ish".
  424. * @throws CorruptObjectException
  425. * the object claimed to be a tree, but its contents did not
  426. * appear to be a tree. The repository may have data corruption.
  427. * @throws IOException
  428. * a loose object or pack file could not be read.
  429. */
  430. public int addTree(final AnyObjectId id) throws MissingObjectException,
  431. IncorrectObjectTypeException, CorruptObjectException, IOException {
  432. return addTree(parserFor(id));
  433. }
  434. /**
  435. * Add an already created tree iterator for walking.
  436. * <p>
  437. * The position of this tree is returned to the caller, in case the caller
  438. * has lost track of the order they added the trees into the walker.
  439. * <p>
  440. * The tree which the iterator operates on must have the same root as
  441. * existing trees in the walk.
  442. *
  443. * @param p
  444. * an iterator to walk over. The iterator should be new, with no
  445. * parent, and should still be positioned before the first entry.
  446. * The tree which the iterator operates on must have the same root
  447. * as other trees in the walk.
  448. *
  449. * @return position of this tree within the walker.
  450. * @throws CorruptObjectException
  451. * the iterator was unable to obtain its first entry, due to
  452. * possible data corruption within the backing data store.
  453. */
  454. public int addTree(final AbstractTreeIterator p)
  455. throws CorruptObjectException {
  456. final int n = trees.length;
  457. final AbstractTreeIterator[] newTrees = new AbstractTreeIterator[n + 1];
  458. System.arraycopy(trees, 0, newTrees, 0, n);
  459. newTrees[n] = p;
  460. p.matches = null;
  461. p.matchShift = 0;
  462. trees = newTrees;
  463. return n;
  464. }
  465. /**
  466. * Get the number of trees known to this walker.
  467. *
  468. * @return the total number of trees this walker is iterating over.
  469. */
  470. public int getTreeCount() {
  471. return trees.length;
  472. }
  473. /**
  474. * Advance this walker to the next relevant entry.
  475. *
  476. * @return true if there is an entry available; false if all entries have
  477. * been walked and the walk of this set of tree iterators is over.
  478. * @throws MissingObjectException
  479. * {@link #isRecursive()} was enabled, a subtree was found, but
  480. * the subtree object does not exist in this repository. The
  481. * repository may be missing objects.
  482. * @throws IncorrectObjectTypeException
  483. * {@link #isRecursive()} was enabled, a subtree was found, and
  484. * the subtree id does not denote a tree, but instead names some
  485. * other non-tree type of object. The repository may have data
  486. * corruption.
  487. * @throws CorruptObjectException
  488. * the contents of a tree did not appear to be a tree. The
  489. * repository may have data corruption.
  490. * @throws IOException
  491. * a loose object or pack file could not be read.
  492. */
  493. public boolean next() throws MissingObjectException,
  494. IncorrectObjectTypeException, CorruptObjectException, IOException {
  495. try {
  496. if (advance) {
  497. advance = false;
  498. postChildren = false;
  499. popEntriesEqual();
  500. }
  501. for (;;) {
  502. final AbstractTreeIterator t = min();
  503. if (t.eof()) {
  504. if (depth > 0) {
  505. exitSubtree();
  506. if (postOrderTraversal) {
  507. advance = true;
  508. postChildren = true;
  509. return true;
  510. }
  511. popEntriesEqual();
  512. continue;
  513. }
  514. return false;
  515. }
  516. currentHead = t;
  517. if (!filter.include(this)) {
  518. skipEntriesEqual();
  519. continue;
  520. }
  521. if (recursive && FileMode.TREE.equals(t.mode)) {
  522. enterSubtree();
  523. continue;
  524. }
  525. advance = true;
  526. return true;
  527. }
  528. } catch (StopWalkException stop) {
  529. for (final AbstractTreeIterator t : trees)
  530. t.stopWalk();
  531. return false;
  532. }
  533. }
  534. /**
  535. * Obtain the tree iterator for the current entry.
  536. * <p>
  537. * Entering into (or exiting out of) a subtree causes the current tree
  538. * iterator instance to be changed for the nth tree. This allows the tree
  539. * iterators to manage only one list of items, with the diving handled by
  540. * recursive trees.
  541. *
  542. * @param <T>
  543. * type of the tree iterator expected by the caller.
  544. * @param nth
  545. * tree to obtain the current iterator of.
  546. * @param clazz
  547. * type of the tree iterator expected by the caller.
  548. * @return r the current iterator of the requested type; null if the tree
  549. * has no entry to match the current path.
  550. */
  551. @SuppressWarnings("unchecked")
  552. public <T extends AbstractTreeIterator> T getTree(final int nth,
  553. final Class<T> clazz) {
  554. final AbstractTreeIterator t = trees[nth];
  555. return t.matches == currentHead ? (T) t : null;
  556. }
  557. /**
  558. * Obtain the raw {@link FileMode} bits for the current entry.
  559. * <p>
  560. * Every added tree supplies mode bits, even if the tree does not contain
  561. * the current entry. In the latter case {@link FileMode#MISSING}'s mode
  562. * bits (0) are returned.
  563. *
  564. * @param nth
  565. * tree to obtain the mode bits from.
  566. * @return mode bits for the current entry of the nth tree.
  567. * @see FileMode#fromBits(int)
  568. */
  569. public int getRawMode(final int nth) {
  570. final AbstractTreeIterator t = trees[nth];
  571. return t.matches == currentHead ? t.mode : 0;
  572. }
  573. /**
  574. * Obtain the {@link FileMode} for the current entry.
  575. * <p>
  576. * Every added tree supplies a mode, even if the tree does not contain the
  577. * current entry. In the latter case {@link FileMode#MISSING} is returned.
  578. *
  579. * @param nth
  580. * tree to obtain the mode from.
  581. * @return mode for the current entry of the nth tree.
  582. */
  583. public FileMode getFileMode(final int nth) {
  584. return FileMode.fromBits(getRawMode(nth));
  585. }
  586. /**
  587. * Obtain the ObjectId for the current entry.
  588. * <p>
  589. * Using this method to compare ObjectId values between trees of this walker
  590. * is very inefficient. Applications should try to use
  591. * {@link #idEqual(int, int)} or {@link #getObjectId(MutableObjectId, int)}
  592. * whenever possible.
  593. * <p>
  594. * Every tree supplies an object id, even if the tree does not contain the
  595. * current entry. In the latter case {@link ObjectId#zeroId()} is returned.
  596. *
  597. * @param nth
  598. * tree to obtain the object identifier from.
  599. * @return object identifier for the current tree entry.
  600. * @see #getObjectId(MutableObjectId, int)
  601. * @see #idEqual(int, int)
  602. */
  603. public ObjectId getObjectId(final int nth) {
  604. final AbstractTreeIterator t = trees[nth];
  605. return t.matches == currentHead ? t.getEntryObjectId() : ObjectId
  606. .zeroId();
  607. }
  608. /**
  609. * Obtain the ObjectId for the current entry.
  610. * <p>
  611. * Every tree supplies an object id, even if the tree does not contain the
  612. * current entry. In the latter case {@link ObjectId#zeroId()} is supplied.
  613. * <p>
  614. * Applications should try to use {@link #idEqual(int, int)} when possible
  615. * as it avoids conversion overheads.
  616. *
  617. * @param out
  618. * buffer to copy the object id into.
  619. * @param nth
  620. * tree to obtain the object identifier from.
  621. * @see #idEqual(int, int)
  622. */
  623. public void getObjectId(final MutableObjectId out, final int nth) {
  624. final AbstractTreeIterator t = trees[nth];
  625. if (t.matches == currentHead)
  626. t.getEntryObjectId(out);
  627. else
  628. out.clear();
  629. }
  630. /**
  631. * Compare two tree's current ObjectId values for equality.
  632. *
  633. * @param nthA
  634. * first tree to compare the object id from.
  635. * @param nthB
  636. * second tree to compare the object id from.
  637. * @return result of
  638. * <code>getObjectId(nthA).equals(getObjectId(nthB))</code>.
  639. * @see #getObjectId(int)
  640. */
  641. public boolean idEqual(final int nthA, final int nthB) {
  642. final AbstractTreeIterator ch = currentHead;
  643. final AbstractTreeIterator a = trees[nthA];
  644. final AbstractTreeIterator b = trees[nthB];
  645. if (a.matches != ch && b.matches != ch) {
  646. // If neither tree matches the current path node then neither
  647. // tree has this entry. In such case the ObjectId is zero(),
  648. // and zero() is always equal to zero().
  649. //
  650. return true;
  651. }
  652. if (!a.hasId() || !b.hasId())
  653. return false;
  654. if (a.matches == ch && b.matches == ch)
  655. return a.idEqual(b);
  656. return false;
  657. }
  658. /**
  659. * Get the current entry's name within its parent tree.
  660. * <p>
  661. * This method is not very efficient and is primarily meant for debugging
  662. * and final output generation. Applications should try to avoid calling it,
  663. * and if invoked do so only once per interesting entry, where the name is
  664. * absolutely required for correct function.
  665. *
  666. * @return name of the current entry within the parent tree (or directory).
  667. * The name never includes a '/'.
  668. */
  669. public String getNameString() {
  670. final AbstractTreeIterator t = currentHead;
  671. final int off = t.pathOffset;
  672. final int end = t.pathLen;
  673. return RawParseUtils.decode(Constants.CHARSET, t.path, off, end);
  674. }
  675. /**
  676. * Get the current entry's complete path.
  677. * <p>
  678. * This method is not very efficient and is primarily meant for debugging
  679. * and final output generation. Applications should try to avoid calling it,
  680. * and if invoked do so only once per interesting entry, where the name is
  681. * absolutely required for correct function.
  682. *
  683. * @return complete path of the current entry, from the root of the
  684. * repository. If the current entry is in a subtree there will be at
  685. * least one '/' in the returned string.
  686. */
  687. public String getPathString() {
  688. return pathOf(currentHead);
  689. }
  690. /**
  691. * Get the current entry's complete path as a UTF-8 byte array.
  692. *
  693. * @return complete path of the current entry, from the root of the
  694. * repository. If the current entry is in a subtree there will be at
  695. * least one '/' in the returned string.
  696. */
  697. public byte[] getRawPath() {
  698. final AbstractTreeIterator t = currentHead;
  699. final int n = t.pathLen;
  700. final byte[] r = new byte[n];
  701. System.arraycopy(t.path, 0, r, 0, n);
  702. return r;
  703. }
  704. /**
  705. * @return The path length of the current entry.
  706. */
  707. public int getPathLength() {
  708. return currentHead.pathLen;
  709. }
  710. /**
  711. * Test if the supplied path matches the current entry's path.
  712. * <p>
  713. * This method tests that the supplied path is exactly equal to the current
  714. * entry, or is one of its parent directories. It is faster to use this
  715. * method then to use {@link #getPathString()} to first create a String
  716. * object, then test <code>startsWith</code> or some other type of string
  717. * match function.
  718. *
  719. * @param p
  720. * path buffer to test. Callers should ensure the path does not
  721. * end with '/' prior to invocation.
  722. * @param pLen
  723. * number of bytes from <code>buf</code> to test.
  724. * @return &lt; 0 if p is before the current path; 0 if p matches the current
  725. * path; 1 if the current path is past p and p will never match
  726. * again on this tree walk.
  727. */
  728. public int isPathPrefix(final byte[] p, final int pLen) {
  729. final AbstractTreeIterator t = currentHead;
  730. final byte[] c = t.path;
  731. final int cLen = t.pathLen;
  732. int ci;
  733. for (ci = 0; ci < cLen && ci < pLen; ci++) {
  734. final int c_value = (c[ci] & 0xff) - (p[ci] & 0xff);
  735. if (c_value != 0)
  736. return c_value;
  737. }
  738. if (ci < cLen) {
  739. // Ran out of pattern but we still had current data.
  740. // If c[ci] == '/' then pattern matches the subtree.
  741. // Otherwise we cannot be certain so we return -1.
  742. //
  743. return c[ci] == '/' ? 0 : -1;
  744. }
  745. if (ci < pLen) {
  746. // Ran out of current, but we still have pattern data.
  747. // If p[ci] == '/' then pattern matches this subtree,
  748. // otherwise we cannot be certain so we return -1.
  749. //
  750. return p[ci] == '/' ? 0 : -1;
  751. }
  752. // Both strings are identical.
  753. //
  754. return 0;
  755. }
  756. /**
  757. * Test if the supplied path matches (being suffix of) the current entry's
  758. * path.
  759. * <p>
  760. * This method tests that the supplied path is exactly equal to the current
  761. * entry, or is relative to one of entry's parent directories. It is faster
  762. * to use this method then to use {@link #getPathString()} to first create
  763. * a String object, then test <code>endsWith</code> or some other type of
  764. * string match function.
  765. *
  766. * @param p
  767. * path buffer to test.
  768. * @param pLen
  769. * number of bytes from <code>buf</code> to test.
  770. * @return true if p is suffix of the current path;
  771. * false if otherwise
  772. */
  773. public boolean isPathSuffix(final byte[] p, final int pLen) {
  774. final AbstractTreeIterator t = currentHead;
  775. final byte[] c = t.path;
  776. final int cLen = t.pathLen;
  777. for (int i = 1; i <= pLen; i++) {
  778. // Pattern longer than current path
  779. if (i > cLen)
  780. return false;
  781. // Current path doesn't match pattern
  782. if (c[cLen - i] != p[pLen - i])
  783. return false;
  784. }
  785. // Whole pattern tested -> matches
  786. return true;
  787. }
  788. /**
  789. * Get the current subtree depth of this walker.
  790. *
  791. * @return the current subtree depth of this walker.
  792. */
  793. public int getDepth() {
  794. return depth;
  795. }
  796. /**
  797. * Is the current entry a subtree?
  798. * <p>
  799. * This method is faster then testing the raw mode bits of all trees to see
  800. * if any of them are a subtree. If at least one is a subtree then this
  801. * method will return true.
  802. *
  803. * @return true if {@link #enterSubtree()} will work on the current node.
  804. */
  805. public boolean isSubtree() {
  806. return FileMode.TREE.equals(currentHead.mode);
  807. }
  808. /**
  809. * Is the current entry a subtree returned after its children?
  810. *
  811. * @return true if the current node is a tree that has been returned after
  812. * its children were already processed.
  813. * @see #isPostOrderTraversal()
  814. */
  815. public boolean isPostChildren() {
  816. return postChildren && isSubtree();
  817. }
  818. /**
  819. * Enter into the current subtree.
  820. * <p>
  821. * If the current entry is a subtree this method arranges for its children
  822. * to be returned before the next sibling following the subtree is returned.
  823. *
  824. * @throws MissingObjectException
  825. * a subtree was found, but the subtree object does not exist in
  826. * this repository. The repository may be missing objects.
  827. * @throws IncorrectObjectTypeException
  828. * a subtree was found, and the subtree id does not denote a
  829. * tree, but instead names some other non-tree type of object.
  830. * The repository may have data corruption.
  831. * @throws CorruptObjectException
  832. * the contents of a tree did not appear to be a tree. The
  833. * repository may have data corruption.
  834. * @throws IOException
  835. * a loose object or pack file could not be read.
  836. */
  837. public void enterSubtree() throws MissingObjectException,
  838. IncorrectObjectTypeException, CorruptObjectException, IOException {
  839. final AbstractTreeIterator ch = currentHead;
  840. final AbstractTreeIterator[] tmp = new AbstractTreeIterator[trees.length];
  841. for (int i = 0; i < trees.length; i++) {
  842. final AbstractTreeIterator t = trees[i];
  843. final AbstractTreeIterator n;
  844. if (t.matches == ch && !t.eof() && FileMode.TREE.equals(t.mode))
  845. n = t.createSubtreeIterator(reader, idBuffer);
  846. else
  847. n = t.createEmptyTreeIterator();
  848. tmp[i] = n;
  849. }
  850. depth++;
  851. advance = false;
  852. System.arraycopy(tmp, 0, trees, 0, trees.length);
  853. }
  854. @SuppressWarnings("unused")
  855. AbstractTreeIterator min() throws CorruptObjectException {
  856. int i = 0;
  857. AbstractTreeIterator minRef = trees[i];
  858. while (minRef.eof() && ++i < trees.length)
  859. minRef = trees[i];
  860. if (minRef.eof())
  861. return minRef;
  862. minRef.matches = minRef;
  863. while (++i < trees.length) {
  864. final AbstractTreeIterator t = trees[i];
  865. if (t.eof())
  866. continue;
  867. final int cmp = t.pathCompare(minRef);
  868. if (cmp < 0) {
  869. t.matches = t;
  870. minRef = t;
  871. } else if (cmp == 0) {
  872. t.matches = minRef;
  873. }
  874. }
  875. return minRef;
  876. }
  877. void popEntriesEqual() throws CorruptObjectException {
  878. final AbstractTreeIterator ch = currentHead;
  879. for (int i = 0; i < trees.length; i++) {
  880. final AbstractTreeIterator t = trees[i];
  881. if (t.matches == ch) {
  882. t.next(1);
  883. t.matches = null;
  884. }
  885. }
  886. }
  887. void skipEntriesEqual() throws CorruptObjectException {
  888. final AbstractTreeIterator ch = currentHead;
  889. for (int i = 0; i < trees.length; i++) {
  890. final AbstractTreeIterator t = trees[i];
  891. if (t.matches == ch) {
  892. t.skip();
  893. t.matches = null;
  894. }
  895. }
  896. }
  897. private void exitSubtree() {
  898. depth--;
  899. for (int i = 0; i < trees.length; i++)
  900. 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;
  907. }
  908. currentHead = minRef;
  909. }
  910. private CanonicalTreeParser parserFor(final AnyObjectId id)
  911. throws IncorrectObjectTypeException, IOException {
  912. final CanonicalTreeParser p = new CanonicalTreeParser();
  913. p.reset(reader, id);
  914. return p;
  915. }
  916. static String pathOf(final AbstractTreeIterator t) {
  917. return RawParseUtils.decode(Constants.CHARSET, t.path, 0, t.pathLen);
  918. }
  919. static String pathOf(final byte[] buf, int pos, int end) {
  920. return RawParseUtils.decode(Constants.CHARSET, buf, pos, end);
  921. }
  922. }