1 /*
2 * Copyright (C) 2008-2009, Google Inc.
3 * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
4 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
5 * and other copyright owners as documented in the project's IP log.
6 *
7 * This program and the accompanying materials are made available
8 * under the terms of the Eclipse Distribution License v1.0 which
9 * accompanies this distribution, is reproduced below, and is
10 * available at http://www.eclipse.org/org/documents/edl-v10.php
11 *
12 * All rights reserved.
13 *
14 * Redistribution and use in source and binary forms, with or
15 * without modification, are permitted provided that the following
16 * conditions are met:
17 *
18 * - Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 *
21 * - Redistributions in binary form must reproduce the above
22 * copyright notice, this list of conditions and the following
23 * disclaimer in the documentation and/or other materials provided
24 * with the distribution.
25 *
26 * - Neither the name of the Eclipse Foundation, Inc. nor the
27 * names of its contributors may be used to endorse or promote
28 * products derived from this software without specific prior
29 * written permission.
30 *
31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
32 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
33 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
34 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
36 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
37 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
38 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
39 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
40 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
41 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
43 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44 */
45
46 package org.eclipse.jgit.treewalk;
47
48 import java.io.IOException;
49 import java.nio.ByteBuffer;
50 import java.nio.CharBuffer;
51
52 import org.eclipse.jgit.attributes.AttributesNode;
53 import org.eclipse.jgit.attributes.AttributesHandler;
54 import org.eclipse.jgit.dircache.DirCacheCheckout;
55 import org.eclipse.jgit.errors.CorruptObjectException;
56 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
57 import org.eclipse.jgit.lib.Constants;
58 import org.eclipse.jgit.lib.FileMode;
59 import org.eclipse.jgit.lib.MutableObjectId;
60 import org.eclipse.jgit.lib.ObjectId;
61 import org.eclipse.jgit.lib.ObjectReader;
62 import org.eclipse.jgit.treewalk.filter.TreeFilter;
63 import org.eclipse.jgit.util.Paths;
64
65 /**
66 * Walks a Git tree (directory) in Git sort order.
67 * <p>
68 * A new iterator instance should be positioned on the first entry, or at eof.
69 * Data for the first entry (if not at eof) should be available immediately.
70 * <p>
71 * Implementors must walk a tree in the Git sort order, which has the following
72 * odd sorting:
73 * <ol>
74 * <li>A.c</li>
75 * <li>A/c</li>
76 * <li>A0c</li>
77 * </ol>
78 * <p>
79 * In the second item, <code>A</code> is the name of a subtree and
80 * <code>c</code> is a file within that subtree. The other two items are files
81 * in the root level tree.
82 *
83 * @see CanonicalTreeParser
84 */
85 public abstract class AbstractTreeIterator {
86 /** Default size for the {@link #path} buffer. */
87 protected static final int DEFAULT_PATH_SIZE = 128;
88
89 /** A dummy object id buffer that matches the zero ObjectId. */
90 protected static final byte[] zeroid = new byte[Constants.OBJECT_ID_LENGTH];
91
92 /**
93 * Iterator for the parent tree; null if we are the root iterator.
94 * <p>
95 * Used by {@link TreeWalk} and {@link AttributesHandler}
96 *
97 * @since 4.3
98 */
99 public final AbstractTreeIterator parent;
100
101 /** The iterator this current entry is path equal to. */
102 AbstractTreeIterator matches;
103
104 /**
105 * Parsed rules of .gitattributes file if it exists.
106 *
107 * @since 4.2
108 */
109 protected AttributesNode attributesNode;
110
111 /**
112 * Number of entries we moved forward to force a D/F conflict match.
113 *
114 * @see NameConflictTreeWalk
115 */
116 int matchShift;
117
118 /**
119 * Mode bits for the current entry.
120 * <p>
121 * A numerical value from FileMode is usually faster for an iterator to
122 * obtain from its data source so this is the preferred representation.
123 *
124 * @see org.eclipse.jgit.lib.FileMode
125 */
126 protected int mode;
127
128 /**
129 * Path buffer for the current entry.
130 * <p>
131 * This buffer is pre-allocated at the start of walking and is shared from
132 * parent iterators down into their subtree iterators. The sharing allows
133 * the current entry to always be a full path from the root, while each
134 * subtree only needs to populate the part that is under their control.
135 */
136 protected byte[] path;
137
138 /**
139 * Position within {@link #path} this iterator starts writing at.
140 * <p>
141 * This is the first offset in {@link #path} that this iterator must
142 * populate during {@link #next}. At the root level (when {@link #parent}
143 * is null) this is 0. For a subtree iterator the index before this position
144 * should have the value '/'.
145 */
146 protected final int pathOffset;
147
148 /**
149 * Total length of the current entry's complete path from the root.
150 * <p>
151 * This is the number of bytes within {@link #path} that pertain to the
152 * current entry. Values at this index through the end of the array are
153 * garbage and may be randomly populated from prior entries.
154 */
155 protected int pathLen;
156
157 /** Create a new iterator with no parent. */
158 protected AbstractTreeIterator() {
159 parent = null;
160 path = new byte[DEFAULT_PATH_SIZE];
161 pathOffset = 0;
162 }
163
164 /**
165 * Create a new iterator with no parent and a prefix.
166 * <p>
167 * The prefix path supplied is inserted in front of all paths generated by
168 * this iterator. It is intended to be used when an iterator is being
169 * created for a subsection of an overall repository and needs to be
170 * combined with other iterators that are created to run over the entire
171 * repository namespace.
172 *
173 * @param prefix
174 * position of this iterator in the repository tree. The value
175 * may be null or the empty string to indicate the prefix is the
176 * root of the repository. A trailing slash ('/') is
177 * automatically appended if the prefix does not end in '/'.
178 */
179 protected AbstractTreeIterator(final String prefix) {
180 parent = null;
181
182 if (prefix != null && prefix.length() > 0) {
183 final ByteBuffer b;
184
185 b = Constants.CHARSET.encode(CharBuffer.wrap(prefix));
186 pathLen = b.limit();
187 path = new byte[Math.max(DEFAULT_PATH_SIZE, pathLen + 1)];
188 b.get(path, 0, pathLen);
189 if (path[pathLen - 1] != '/')
190 path[pathLen++] = '/';
191 pathOffset = pathLen;
192 } else {
193 path = new byte[DEFAULT_PATH_SIZE];
194 pathOffset = 0;
195 }
196 }
197
198 /**
199 * Create a new iterator with no parent and a prefix.
200 * <p>
201 * The prefix path supplied is inserted in front of all paths generated by
202 * this iterator. It is intended to be used when an iterator is being
203 * created for a subsection of an overall repository and needs to be
204 * combined with other iterators that are created to run over the entire
205 * repository namespace.
206 *
207 * @param prefix
208 * position of this iterator in the repository tree. The value
209 * may be null or the empty array to indicate the prefix is the
210 * root of the repository. A trailing slash ('/') is
211 * automatically appended if the prefix does not end in '/'.
212 */
213 protected AbstractTreeIterator(final byte[] prefix) {
214 parent = null;
215
216 if (prefix != null && prefix.length > 0) {
217 pathLen = prefix.length;
218 path = new byte[Math.max(DEFAULT_PATH_SIZE, pathLen + 1)];
219 System.arraycopy(prefix, 0, path, 0, pathLen);
220 if (path[pathLen - 1] != '/')
221 path[pathLen++] = '/';
222 pathOffset = pathLen;
223 } else {
224 path = new byte[DEFAULT_PATH_SIZE];
225 pathOffset = 0;
226 }
227 }
228
229 /**
230 * Create an iterator for a subtree of an existing iterator.
231 *
232 * @param p
233 * parent tree iterator.
234 */
235 protected AbstractTreeIterator(final AbstractTreeIterator p) {
236 parent = p;
237 path = p.path;
238 pathOffset = p.pathLen + 1;
239
240 try {
241 path[pathOffset - 1] = '/';
242 } catch (ArrayIndexOutOfBoundsException e) {
243 growPath(p.pathLen);
244 path[pathOffset - 1] = '/';
245 }
246 }
247
248 /**
249 * Create an iterator for a subtree of an existing iterator.
250 * <p>
251 * The caller is responsible for setting up the path of the child iterator.
252 *
253 * @param p
254 * parent tree iterator.
255 * @param childPath
256 * path array to be used by the child iterator. This path must
257 * contain the path from the top of the walk to the first child
258 * and must end with a '/'.
259 * @param childPathOffset
260 * position within <code>childPath</code> where the child can
261 * insert its data. The value at
262 * <code>childPath[childPathOffset-1]</code> must be '/'.
263 */
264 protected AbstractTreeIterator(final AbstractTreeIterator p,
265 final byte[] childPath, final int childPathOffset) {
266 parent = p;
267 path = childPath;
268 pathOffset = childPathOffset;
269 }
270
271 /**
272 * Grow the path buffer larger.
273 *
274 * @param len
275 * number of live bytes in the path buffer. This many bytes will
276 * be moved into the larger buffer.
277 */
278 protected void growPath(final int len) {
279 setPathCapacity(path.length << 1, len);
280 }
281
282 /**
283 * Ensure that path is capable to hold at least {@code capacity} bytes
284 *
285 * @param capacity
286 * the amount of bytes to hold
287 * @param len
288 * the amount of live bytes in path buffer
289 */
290 protected void ensurePathCapacity(final int capacity, final int len) {
291 if (path.length >= capacity)
292 return;
293 final byte[] o = path;
294 int current = o.length;
295 int newCapacity = current;
296 while (newCapacity < capacity && newCapacity > 0)
297 newCapacity <<= 1;
298 setPathCapacity(newCapacity, len);
299 }
300
301 /**
302 * Set path buffer capacity to the specified size
303 *
304 * @param capacity
305 * the new size
306 * @param len
307 * the amount of bytes to copy
308 */
309 private void setPathCapacity(int capacity, int len) {
310 final byte[] o = path;
311 final byte[] n = new byte[capacity];
312 System.arraycopy(o, 0, n, 0, len);
313 for (AbstractTreeIterator p = this; p != null && p.path == o; p = p.parent)
314 p.path = n;
315 }
316
317 /**
318 * Compare the path of this current entry to another iterator's entry.
319 *
320 * @param p
321 * the other iterator to compare the path against.
322 * @return -1 if this entry sorts first; 0 if the entries are equal; 1 if
323 * p's entry sorts first.
324 */
325 public int pathCompare(final AbstractTreeIterator p) {
326 return pathCompare(p, p.mode);
327 }
328
329 int pathCompare(final AbstractTreeIterator p, final int pMode) {
330 // Its common when we are a subtree for both parents to match;
331 // when this happens everything in path[0..cPos] is known to
332 // be equal and does not require evaluation again.
333 //
334 int cPos = alreadyMatch(this, p);
335 return pathCompare(p.path, cPos, p.pathLen, pMode, cPos);
336 }
337
338 /**
339 * Seek the iterator on a file, if present.
340 *
341 * @param name
342 * file name to find (will not find a directory).
343 * @return true if the file exists in this tree; false otherwise.
344 * @throws CorruptObjectException
345 * tree is invalid.
346 * @since 4.2
347 */
348 public boolean findFile(String name) throws CorruptObjectException {
349 return findFile(Constants.encode(name));
350 }
351
352 /**
353 * Seek the iterator on a file, if present.
354 *
355 * @param name
356 * file name to find (will not find a directory).
357 * @return true if the file exists in this tree; false otherwise.
358 * @throws CorruptObjectException
359 * tree is invalid.
360 * @since 4.2
361 */
362 public boolean findFile(byte[] name) throws CorruptObjectException {
363 for (; !eof(); next(1)) {
364 int cmp = pathCompare(name, 0, name.length, 0, pathOffset);
365 if (cmp == 0) {
366 return true;
367 } else if (cmp > 0) {
368 return false;
369 }
370 }
371 return false;
372 }
373
374 /**
375 * Compare the path of this current entry to a raw buffer.
376 *
377 * @param buf
378 * the raw path buffer.
379 * @param pos
380 * position to start reading the raw buffer.
381 * @param end
382 * one past the end of the raw buffer (length is end - pos).
383 * @param pathMode
384 * the mode of the path.
385 * @return -1 if this entry sorts first; 0 if the entries are equal; 1 if
386 * p's entry sorts first.
387 */
388 public int pathCompare(byte[] buf, int pos, int end, int pathMode) {
389 return pathCompare(buf, pos, end, pathMode, 0);
390 }
391
392 private int pathCompare(byte[] b, int bPos, int bEnd, int bMode, int aPos) {
393 return Paths.compare(
394 path, aPos, pathLen, mode,
395 b, bPos, bEnd, bMode);
396 }
397
398 private static int alreadyMatch(AbstractTreeIterator a,
399 AbstractTreeIterator b) {
400 for (;;) {
401 final AbstractTreeIterator ap = a.parent;
402 final AbstractTreeIterator bp = b.parent;
403 if (ap == null || bp == null)
404 return 0;
405 if (ap.matches == bp.matches)
406 return a.pathOffset;
407 a = ap;
408 b = bp;
409 }
410 }
411
412 /**
413 * Check if the current entry of both iterators has the same id.
414 * <p>
415 * This method is faster than {@link #getEntryObjectId()} as it does not
416 * require copying the bytes out of the buffers. A direct {@link #idBuffer}
417 * compare operation is performed.
418 *
419 * @param otherIterator
420 * the other iterator to test against.
421 * @return true if both iterators have the same object id; false otherwise.
422 */
423 public boolean idEqual(final AbstractTreeIterator otherIterator) {
424 return ObjectId.equals(idBuffer(), idOffset(),
425 otherIterator.idBuffer(), otherIterator.idOffset());
426 }
427
428 /** @return true if the entry has a valid ObjectId. */
429 public abstract boolean hasId();
430
431 /**
432 * Get the object id of the current entry.
433 *
434 * @return an object id for the current entry.
435 */
436 public ObjectId getEntryObjectId() {
437 return ObjectId.fromRaw(idBuffer(), idOffset());
438 }
439
440 /**
441 * Obtain the ObjectId for the current entry.
442 *
443 * @param out
444 * buffer to copy the object id into.
445 */
446 public void getEntryObjectId(final MutableObjectId out) {
447 out.fromRaw(idBuffer(), idOffset());
448 }
449
450 /** @return the file mode of the current entry. */
451 public FileMode getEntryFileMode() {
452 return FileMode.fromBits(mode);
453 }
454
455 /** @return the file mode of the current entry as bits */
456 public int getEntryRawMode() {
457 return mode;
458 }
459
460 /** @return path of the current entry, as a string. */
461 public String getEntryPathString() {
462 return TreeWalk.pathOf(this);
463 }
464
465 /**
466 * Get the current entry path buffer.
467 * <p>
468 * Note that the returned byte[] has to be used together with
469 * {@link #getEntryPathLength()} (only use bytes up to this length).
470 *
471 * @return the internal buffer holding the current path.
472 */
473 public byte[] getEntryPathBuffer() {
474 return path;
475 }
476
477 /** @return length of the path in {@link #getEntryPathBuffer()}. */
478 public int getEntryPathLength() {
479 return pathLen;
480 }
481
482 /**
483 * Get the current entry's path hash code.
484 * <p>
485 * This method computes a hash code on the fly for this path, the hash is
486 * suitable to cluster objects that may have similar paths together.
487 *
488 * @return path hash code; any integer may be returned.
489 */
490 public int getEntryPathHashCode() {
491 int hash = 0;
492 for (int i = Math.max(0, pathLen - 16); i < pathLen; i++) {
493 byte c = path[i];
494 if (c != ' ')
495 hash = (hash >>> 2) + (c << 24);
496 }
497 return hash;
498 }
499
500 /**
501 * Get the byte array buffer object IDs must be copied out of.
502 * <p>
503 * The id buffer contains the bytes necessary to construct an ObjectId for
504 * the current entry of this iterator. The buffer can be the same buffer for
505 * all entries, or it can be a unique buffer per-entry. Implementations are
506 * encouraged to expose their private buffer whenever possible to reduce
507 * garbage generation and copying costs.
508 *
509 * @return byte array the implementation stores object IDs within.
510 * @see #getEntryObjectId()
511 */
512 public abstract byte[] idBuffer();
513
514 /**
515 * Get the position within {@link #idBuffer()} of this entry's ObjectId.
516 *
517 * @return offset into the array returned by {@link #idBuffer()} where the
518 * ObjectId must be copied out of.
519 */
520 public abstract int idOffset();
521
522 /**
523 * Create a new iterator for the current entry's subtree.
524 * <p>
525 * The parent reference of the iterator must be <code>this</code>,
526 * otherwise the caller would not be able to exit out of the subtree
527 * iterator correctly and return to continue walking <code>this</code>.
528 *
529 * @param reader
530 * reader to load the tree data from.
531 * @return a new parser that walks over the current subtree.
532 * @throws IncorrectObjectTypeException
533 * the current entry is not actually a tree and cannot be parsed
534 * as though it were a tree.
535 * @throws IOException
536 * a loose object or pack file could not be read.
537 */
538 public abstract AbstractTreeIterator createSubtreeIterator(
539 ObjectReader reader) throws IncorrectObjectTypeException,
540 IOException;
541
542 /**
543 * Create a new iterator as though the current entry were a subtree.
544 *
545 * @return a new empty tree iterator.
546 */
547 public EmptyTreeIterator createEmptyTreeIterator() {
548 return new EmptyTreeIterator(this);
549 }
550
551 /**
552 * Create a new iterator for the current entry's subtree.
553 * <p>
554 * The parent reference of the iterator must be <code>this</code>, otherwise
555 * the caller would not be able to exit out of the subtree iterator
556 * correctly and return to continue walking <code>this</code>.
557 *
558 * @param reader
559 * reader to load the tree data from.
560 * @param idBuffer
561 * temporary ObjectId buffer for use by this method.
562 * @return a new parser that walks over the current subtree.
563 * @throws IncorrectObjectTypeException
564 * the current entry is not actually a tree and cannot be parsed
565 * as though it were a tree.
566 * @throws IOException
567 * a loose object or pack file could not be read.
568 */
569 public AbstractTreeIterator createSubtreeIterator(
570 final ObjectReader reader, final MutableObjectId idBuffer)
571 throws IncorrectObjectTypeException, IOException {
572 return createSubtreeIterator(reader);
573 }
574
575 /**
576 * Position this iterator on the first entry.
577 *
578 * The default implementation of this method uses {@code back(1)} until
579 * {@code first()} is true. This is most likely not the most efficient
580 * method of repositioning the iterator to its first entry, so subclasses
581 * are strongly encouraged to override the method.
582 *
583 * @throws CorruptObjectException
584 * the tree is invalid.
585 */
586 public void reset() throws CorruptObjectException {
587 while (!first())
588 back(1);
589 }
590
591 /**
592 * Is this tree iterator positioned on its first entry?
593 * <p>
594 * An iterator is positioned on the first entry if <code>back(1)</code>
595 * would be an invalid request as there is no entry before the current one.
596 * <p>
597 * An empty iterator (one with no entries) will be
598 * <code>first() && eof()</code>.
599 *
600 * @return true if the iterator is positioned on the first entry.
601 */
602 public abstract boolean first();
603
604 /**
605 * Is this tree iterator at its EOF point (no more entries)?
606 * <p>
607 * An iterator is at EOF if there is no current entry.
608 *
609 * @return true if we have walked all entries and have none left.
610 */
611 public abstract boolean eof();
612
613 /**
614 * Move to next entry, populating this iterator with the entry data.
615 * <p>
616 * The delta indicates how many moves forward should occur. The most common
617 * delta is 1 to move to the next entry.
618 * <p>
619 * Implementations must populate the following members:
620 * <ul>
621 * <li>{@link #mode}</li>
622 * <li>{@link #path} (from {@link #pathOffset} to {@link #pathLen})</li>
623 * <li>{@link #pathLen}</li>
624 * </ul>
625 * as well as any implementation dependent information necessary to
626 * accurately return data from {@link #idBuffer()} and {@link #idOffset()}
627 * when demanded.
628 *
629 * @param delta
630 * number of entries to move the iterator by. Must be a positive,
631 * non-zero integer.
632 * @throws CorruptObjectException
633 * the tree is invalid.
634 */
635 public abstract void next(int delta) throws CorruptObjectException;
636
637 /**
638 * Move to prior entry, populating this iterator with the entry data.
639 * <p>
640 * The delta indicates how many moves backward should occur.The most common
641 * delta is 1 to move to the prior entry.
642 * <p>
643 * Implementations must populate the following members:
644 * <ul>
645 * <li>{@link #mode}</li>
646 * <li>{@link #path} (from {@link #pathOffset} to {@link #pathLen})</li>
647 * <li>{@link #pathLen}</li>
648 * </ul>
649 * as well as any implementation dependent information necessary to
650 * accurately return data from {@link #idBuffer()} and {@link #idOffset()}
651 * when demanded.
652 *
653 * @param delta
654 * number of entries to move the iterator by. Must be a positive,
655 * non-zero integer.
656 * @throws CorruptObjectException
657 * the tree is invalid.
658 */
659 public abstract void back(int delta) throws CorruptObjectException;
660
661 /**
662 * Advance to the next tree entry, populating this iterator with its data.
663 * <p>
664 * This method behaves like <code>seek(1)</code> but is called by
665 * {@link TreeWalk} only if a {@link TreeFilter} was used and ruled out the
666 * current entry from the results. In such cases this tree iterator may
667 * perform special behavior.
668 *
669 * @throws CorruptObjectException
670 * the tree is invalid.
671 */
672 public void skip() throws CorruptObjectException {
673 next(1);
674 }
675
676 /**
677 * Indicates to the iterator that no more entries will be read.
678 * <p>
679 * This is only invoked by TreeWalk when the iteration is aborted early due
680 * to a {@link org.eclipse.jgit.errors.StopWalkException} being thrown from
681 * within a TreeFilter.
682 */
683 public void stopWalk() {
684 // Do nothing by default. Most iterators do not care.
685 }
686
687 /**
688 * @return true if the iterator implements {@link #stopWalk()}.
689 * @since 4.2
690 */
691 protected boolean needsStopWalk() {
692 return false;
693 }
694
695 /**
696 * @return the length of the name component of the path for the current entry
697 */
698 public int getNameLength() {
699 return pathLen - pathOffset;
700 }
701
702 /**
703 * JGit internal API for use by {@link DirCacheCheckout}
704 *
705 * @return start of name component part within {@link #getEntryPathBuffer()}
706 * @since 2.0
707 */
708 public int getNameOffset() {
709 return pathOffset;
710 }
711
712 /**
713 * Get the name component of the current entry path into the provided
714 * buffer.
715 *
716 * @param buffer
717 * the buffer to get the name into, it is assumed that buffer can
718 * hold the name
719 * @param offset
720 * the offset of the name in the buffer
721 * @see #getNameLength()
722 */
723 public void getName(byte[] buffer, int offset) {
724 System.arraycopy(path, pathOffset, buffer, offset, pathLen - pathOffset);
725 }
726
727 @SuppressWarnings("nls")
728 @Override
729 public String toString() {
730 return getClass().getSimpleName() + "[" + getEntryPathString() + "]"; //$NON-NLS-1$
731 }
732
733 /**
734 * @return whether or not this Iterator is iterating through the Work Tree
735 *
736 * @since 4.3
737 */
738 public boolean isWorkTree() {
739 return false;
740 }
741 }