KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > internal > watson > ElementTree


1 /*******************************************************************************
2  * Copyright (c) 2000, 2005 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.core.internal.watson;
12
13 import java.util.HashMap JavaDoc;
14 import org.eclipse.core.internal.dtree.*;
15 import org.eclipse.core.internal.utils.Messages;
16 import org.eclipse.core.internal.utils.StringPool;
17 import org.eclipse.core.runtime.*;
18 import org.eclipse.osgi.util.NLS;
19
20 /**
21  * An ElementTree can be viewed as a generic rooted tree that stores
22  * a hierarchy of elements. An element in the tree consists of a
23  * (name, data, children) 3-tuple. The name can be any String, and
24  * the data can be any Object. The children are a collection of zero
25  * or more elements that logically fall below their parent in the tree.
26  * The implementation makes no guarantees about the ordering of children.
27  *
28  * Elements in the tree are referenced by a key that consists of the names
29  * of all elements on the path from the root to that element in the tree.
30  * For example, if root node "a" has child "b", which has child "c", element
31  * "c" can be referenced in the tree using the key (/a/b/c). Keys are represented
32  * using IPath objects, where the Paths are relative to the root element of the
33  * tree.
34  * @see IPath
35  *
36  * Each ElementTree has a single root element that is created implicitly and
37  * is always present in any tree. This root corresponds to the key (/),
38  * or the singleton <code>Path.ROOT</code>. The root element cannot be created
39  * or deleted, and its data and name cannot be set. The root element's children
40  * however can be modified (added, deleted, etc). The root path can be obtained
41  * using the <code>getRoot()</code> method.
42  *
43  * ElementTrees are modified in generations. The method <code>newEmptyDelta()</code>
44  * returns a new tree generation that can be modified arbitrarily by the user.
45  * For the purpose of explanation, we call such a tree "active".
46  * When the method <code>immutable()</code> is called, that tree generation is
47  * frozen, and can never again be modified. A tree must be immutable before
48  * a new tree generation can start. Since all ancestor trees are immutable,
49  * different active trees can have ancestors in common without fear of
50  * thread corruption problems.
51  *
52  * Internally, any single tree generation is simply stored as the
53  * set of changes between itself and its most recent ancestor (its parent).
54  * This compact delta representation allows chains of element trees to
55  * be created at relatively low cost. Clients of the ElementTree can
56  * instantaneously "undo" sets of changes by navigating up to the parent
57  * tree using the <code>getParent()</code> method.
58  *
59  * Although the delta representation is compact, extremely long delta
60  * chains make for a large structure that is potentially slow to query.
61  * For this reason, the client is encouraged to minimize delta chain
62  * lengths using the <code>collapsing(int)</code> and <code>makeComplete()</code>
63  * methods. The <code>getDeltaDepth()</code> method can be used to
64  * discover the length of the delta chain. The entire delta chain can
65  * also be re-oriented in terms of the current element tree using the
66  * <code>reroot()</code> operation.
67  *
68  * Classes are also available for tree serialization and navigation.
69  * @see ElementTreeReader
70  * @see ElementTreeWriter
71  * @see ElementTreeIterator
72  *
73  * Finally, why are ElementTrees in a package called "watson"?
74  * - "It's ElementTree my dear Watson, ElementTree."
75  */

76 public class ElementTree {
77     protected DeltaDataTree tree;
78     protected IElementTreeData userData;
79
80     private class ChildIDsCache {
81         ChildIDsCache(IPath path, IPath[] childPaths) {
82             this.path = path;
83             this.childPaths = childPaths;
84         }
85
86         IPath path;
87         IPath[] childPaths;
88     }
89
90     private volatile ChildIDsCache childIDsCache = null;
91
92     private volatile DataTreeLookup lookupCache = null;
93
94     private volatile DataTreeLookup lookupCacheIgnoreCase = null;
95
96     private static int treeCounter = 0;
97     private int treeStamp;
98
99     /**
100      * Creates a new empty element tree.
101      */

102     public ElementTree() {
103         initialize(new DeltaDataTree());
104     }
105
106     /**
107      * Creates an element tree given its internal node representation.
108      */

109     protected ElementTree(DataTreeNode rootNode) {
110         initialize(rootNode);
111     }
112
113     /**
114      * Creates a new element tree with the given data tree as its representation.
115      */

116     protected ElementTree(DeltaDataTree tree) {
117         initialize(tree);
118     }
119
120     /**
121      * Creates a new empty delta element tree having the
122      * given tree as its parent.
123      */

124     protected ElementTree(ElementTree parent) {
125         if (!parent.isImmutable()) {
126             parent.immutable();
127         }
128
129         /* copy the user data forward */
130         IElementTreeData data = parent.getTreeData();
131         if (data != null) {
132             userData = (IElementTreeData) data.clone();
133         }
134
135         initialize(parent.tree.newEmptyDeltaTree());
136     }
137
138     /**
139      * Collapses this tree so that the given ancestor becomes its
140      * immediate parent. Afterwards, this tree will still have exactly the
141      * same contents, but its internal structure will be compressed.
142      *
143      * <p> This operation should be used to collapse chains of
144      * element trees created by newEmptyDelta()/immutable().
145      *
146      * <p>This element tree must be immutable at the start of this operation,
147      * and will be immutable afterwards.
148      * @return this tree.
149      */

150     public synchronized ElementTree collapseTo(ElementTree parent) {
151         Assert.isTrue(tree.isImmutable());
152         if (this == parent) {
153             //already collapsed
154
return this;
155         }
156         //collapse my tree to be a forward delta of the parent's tree.
157
tree.collapseTo(parent.tree, DefaultElementComparator.getComparator());
158         return this;
159     }
160
161     /**
162      * Creates the indicated element and sets its element info.
163      * The parent element must be present, otherwise an IllegalArgumentException
164      * is thrown. If the indicated element is already present in the tree,
165      * its element info is replaced and any existing children are
166      * deleted.
167      *
168      * @param key element key
169      * @param data element data, or <code>null</code>
170      */

171     public synchronized void createElement(IPath key, Object JavaDoc data) {
172         /* don't allow modification of the implicit root */
173         if (key.isRoot())
174             return;
175
176         // Clear the child IDs cache in case it's referring to this parent. This is conservative.
177
childIDsCache = null;
178
179         IPath parent = key.removeLastSegments(1);
180         try {
181             tree.createChild(parent, key.lastSegment(), data);
182         } catch (ObjectNotFoundException e) {
183             elementNotFound(parent);
184         }
185         // Set the lookup to be this newly created object.
186
lookupCache = DataTreeLookup.newLookup(key, true, data, true);
187         lookupCacheIgnoreCase = null;
188     }
189
190     /**
191      * Creates or replaces the subtree below the given path with
192      * the given tree. The subtree can only have one child below
193      * the root, which will become the node specified by the given
194      * key in this tree.
195      *
196      * @param key The path of the new subtree in this tree.
197      * @see #getSubtree(IPath)
198      */

199     public synchronized void createSubtree(IPath key, ElementTree subtree) {
200         /* don't allow creating subtrees at the root */
201         if (key.isRoot()) {
202             throw new IllegalArgumentException JavaDoc(Messages.watson_noModify);
203         }
204
205         // Clear the child IDs cache in case it's referring to this parent.
206
// This is conservative.
207
childIDsCache = null;
208         // Clear the lookup cache, in case the element being created is the same
209
// as for the last lookup.
210
lookupCache = lookupCacheIgnoreCase = null;
211         try {
212             /* don't copy the implicit root node of the subtree */
213             IPath[] children = subtree.getChildren(subtree.getRoot());
214             if (children.length != 1) {
215                 throw new IllegalArgumentException JavaDoc(Messages.watson_illegalSubtree);
216             }
217
218             /* get the subtree for the specified key */
219             DataTreeNode node = (DataTreeNode) subtree.tree.copyCompleteSubtree(children[0]);
220
221             /* insert the subtree in this tree */
222             tree.createSubtree(key, node);
223
224         } catch (ObjectNotFoundException e) {
225             elementNotFound(key);
226         }
227     }
228
229     /**
230      * Deletes the indicated element and its descendents.
231      * The element must be present.
232      */

233     public synchronized void deleteElement(IPath key) {
234         /* don't allow modification of the implicit root */
235         if (key.isRoot())
236             return;
237
238         // Clear the child IDs cache in case it's referring to this parent.
239
// This is conservative.
240
childIDsCache = null;
241         // Clear the lookup cache, in case the element being deleted is the same
242
// as for the last lookup.
243
lookupCache = lookupCacheIgnoreCase = null;
244         try {
245             tree.deleteChild(key.removeLastSegments(1), key.lastSegment());
246         } catch (ObjectNotFoundException e) {
247             elementNotFound(key);
248         }
249     }
250
251     /**
252      * Complains that an element was not found
253      */

254     protected void elementNotFound(IPath key) {
255         throw new IllegalArgumentException JavaDoc(NLS.bind(Messages.watson_elementNotFound, key));
256     }
257
258     /**
259      * Given an array of element trees, returns the index of the
260      * oldest tree. The oldest tree is the tree such that no
261      * other tree in the array is a descendent of that tree.
262      * Note that this counter-intuitive concept of oldest is based on the
263      * ElementTree orientation such that the complete tree is always the
264      * newest tree.
265      */

266     public static int findOldest(ElementTree[] trees) {
267
268         /* first put all the trees in a hashtable */
269         HashMap JavaDoc candidates = new HashMap JavaDoc((int) (trees.length * 1.5 + 1));
270         for (int i = 0; i < trees.length; i++) {
271             candidates.put(trees[i], trees[i]);
272         }
273
274         /* keep removing parents until only one tree remains */
275         ElementTree oldestSoFar = null;
276         while (candidates.size() > 0) {
277             /* get a new candidate */
278             ElementTree current = (ElementTree) candidates.values().iterator().next();
279
280             /* remove this candidate from the table */
281             candidates.remove(current);
282
283             /* remove all of this element's parents from the list of candidates*/
284             ElementTree parent = current.getParent();
285
286             /* walk up chain until we hit the root or a tree we have already tested */
287             while (parent != null && parent != oldestSoFar) {
288                 candidates.remove(parent);
289                 parent = parent.getParent();
290             }
291
292             /* the current candidate is the oldest tree seen so far */
293             oldestSoFar = current;
294
295             /* if the table is now empty, we have a winner */
296         }
297         Assert.isNotNull(oldestSoFar);
298
299         /* return the appropriate index */
300         for (int i = 0; i < trees.length; i++) {
301             if (trees[i] == oldestSoFar) {
302                 return i;
303             }
304         }
305         Assert.isTrue(false, "Should not get here"); //$NON-NLS-1$
306
return -1;
307     }
308
309     /**
310      * Returns the number of children of the element
311      * specified by the given path.
312      * The given element must be present in this tree.
313      */

314     public synchronized int getChildCount(IPath key) {
315         Assert.isNotNull(key);
316         return getChildIDs(key).length;
317     }
318
319     /**
320      * Returns the IDs of the children of the specified element.
321      * If the specified element is null, returns the root element path.
322      */

323     protected IPath[] getChildIDs(IPath key) {
324         ChildIDsCache cache = childIDsCache; // Grab it in case it's replaced concurrently.
325
if (cache != null && cache.path == key) {
326             return cache.childPaths;
327         }
328         try {
329             if (key == null)
330                 return new IPath[] {tree.rootKey()};
331             IPath[] children = tree.getChildren(key);
332             childIDsCache = new ChildIDsCache(key, children); // Cache the result
333
return children;
334         } catch (ObjectNotFoundException e) {
335             elementNotFound(key);
336             return null; // can't get here
337
}
338     }
339
340     /**
341      * Returns the paths of the children of the element
342      * specified by the given path.
343      * The given element must be present in this tree.
344      */

345     public synchronized IPath[] getChildren(IPath key) {
346         Assert.isNotNull(key);
347         return getChildIDs(key);
348     }
349
350     /**
351      * Returns the internal data tree.
352      */

353     public DeltaDataTree getDataTree() {
354         return tree;
355     }
356
357     /**
358      * Returns the element data for the given element identifier.
359      * The given element must be present in this tree.
360      */

361     public synchronized Object JavaDoc getElementData(IPath key) {
362         /* don't allow modification of the implicit root */
363         if (key.isRoot())
364             return null;
365         DataTreeLookup lookup = lookupCache; // Grab it in case it's replaced concurrently.
366
if (lookup == null || lookup.key != key)
367             lookupCache = lookup = tree.lookup(key);
368         if (lookup.isPresent)
369             return lookup.data;
370         elementNotFound(key);
371         return null; // can't get here
372
}
373
374     /**
375      * Returns the element data for the given element identifier.
376      * The given element must be present in this tree.
377      */

378     public synchronized Object JavaDoc getElementDataIgnoreCase(IPath key) {
379         /* don't allow modification of the implicit root */
380         if (key.isRoot())
381             return null;
382         DataTreeLookup lookup = lookupCacheIgnoreCase; // Grab it in case it's replaced concurrently.
383
if (lookup == null || lookup.key != key)
384             lookupCacheIgnoreCase = lookup = tree.lookupIgnoreCase(key);
385         if (lookup.isPresent)
386             return lookup.data;
387         elementNotFound(key);
388         return null; // can't get here
389
}
390
391     /**
392      * Returns the names of the children of the specified element.
393      * The specified element must exist in the tree.
394      * If the specified element is null, returns the root element path.
395      */

396     public synchronized String JavaDoc[] getNamesOfChildren(IPath key) {
397         try {
398             if (key == null)
399                 return new String JavaDoc[] {""}; //$NON-NLS-1$
400
return tree.getNamesOfChildren(key);
401         } catch (ObjectNotFoundException e) {
402             elementNotFound(key);
403             return null; // can't get here
404
}
405     }
406
407     /**
408      * Returns the parent tree, or <code>null</code> if there is no parent.
409      */

410     public ElementTree getParent() {
411         DeltaDataTree parentTree = tree.getParent();
412         if (parentTree == null) {
413             return null;
414         }
415         // The parent ElementTree is stored as the node data of the parent DeltaDataTree,
416
// to simplify canonicalization in the presence of rerooting.
417
return (ElementTree) parentTree.getData(tree.rootKey());
418     }
419
420     /**
421      * Returns the root node of this tree.
422      */

423     public IPath getRoot() {
424         return getChildIDs(null)[0];
425     }
426
427     /**
428      * Returns the subtree rooted at the given key. In the resulting tree,
429      * the implicit root node (designated by Path.ROOT), has a single child,
430      * which is the node specified by the given key in this tree.
431      *
432      * The subtree must be present in this tree.
433      *
434      * @see #createSubtree(IPath, ElementTree)
435      */

436     public ElementTree getSubtree(IPath key) {
437         /* the subtree of the root of this tree is just this tree */
438         if (key.isRoot()) {
439             return this;
440         }
441         try {
442             DataTreeNode elementNode = (DataTreeNode) tree.copyCompleteSubtree(key);
443             return new ElementTree(elementNode);
444         } catch (ObjectNotFoundException e) {
445             elementNotFound(key);
446             return null;
447         }
448     }
449
450     /**
451      * Returns the user data associated with this tree.
452      */

453     public IElementTreeData getTreeData() {
454         return userData;
455     }
456
457     /**
458      * Returns true if there have been changes in the tree between the two
459      * given layers. The two must be related and new must be newer than old.
460      * That is, new must be an ancestor of old.
461      */

462     public static boolean hasChanges(ElementTree newLayer, ElementTree oldLayer, IElementComparator comparator, boolean inclusive) {
463         // if any of the layers are null, assume that things have changed
464
if (newLayer == null || oldLayer == null)
465             return true;
466         if (newLayer == oldLayer)
467             return false;
468         //if the tree data has changed, then the tree has changed
469
if (comparator.compare(newLayer.getTreeData(), oldLayer.getTreeData()) != IElementComparator.K_NO_CHANGE)
470             return true;
471
472         // The tree structure has the top layer(s) (i.e., tree) parentage pointing down to a complete
473
// layer whose parent is null. The bottom layers (i.e., operationTree) point up to the
474
// common complete layer whose parent is null. The complete layer moves up as
475
// changes happen. To see if any changes have happened, we should consider only
476
// layers whose parent is not null. That is, skip the complete layer as it will clearly not be
477
// empty.
478

479         // look down from the current layer (always inclusive) if the top layer is mutable
480
ElementTree stopLayer = null;
481         if (newLayer.isImmutable())
482             // if the newLayer is immutable, the tree structure all points up so ensure that
483
// when searching up, we stop at newLayer (inclusive)
484
stopLayer = newLayer.getParent();
485         else {
486             ElementTree layer = newLayer;
487             while (layer != null && layer.getParent() != null) {
488                 if (!layer.getDataTree().isEmptyDelta())
489                     return true;
490                 layer = layer.getParent();
491             }
492         }
493
494         // look up from the layer at which we started to null or newLayer's parent (variably inclusive)
495
// depending on whether newLayer is mutable.
496
ElementTree layer = inclusive ? oldLayer : oldLayer.getParent();
497         while (layer != null && layer.getParent() != stopLayer) {
498             if (!layer.getDataTree().isEmptyDelta())
499                 return true;
500             layer = layer.getParent();
501         }
502         // didn't find anything that changed
503
return false;
504     }
505
506     /**
507      * Makes this tree immutable (read-only); ignored if it is already
508      * immutable.
509      */

510     public synchronized void immutable() {
511         if (!tree.isImmutable()) {
512             tree.immutable();
513             /* need to clear the lookup cache since it reports whether results were found
514              in the topmost delta, and the order of deltas is changing */

515             lookupCache = lookupCacheIgnoreCase = null;
516             /* reroot the delta chain at this tree */
517             tree.reroot();
518         }
519     }
520
521     /**
522      * Returns true if this element tree includes an element with the given
523      * key, false otherwise.
524      */

525     public synchronized boolean includes(IPath key) {
526         DataTreeLookup lookup = lookupCache; // Grab it in case it's replaced concurrently.
527
if (lookup == null || lookup.key != key) {
528             lookupCache = lookup = tree.lookup(key);
529         }
530         return lookup.isPresent;
531     }
532
533     /**
534      * Returns true if this element tree includes an element with the given
535      * key, ignoring the case of the key, and false otherwise.
536      */

537     public boolean includesIgnoreCase(IPath key) {
538         DataTreeLookup lookup = lookupCacheIgnoreCase; // Grab it in case it's replaced concurrently.
539
if (lookup == null || lookup.key != key) {
540             lookupCacheIgnoreCase = lookup = tree.lookupIgnoreCase(key);
541         }
542         return lookup.isPresent;
543     }
544
545     protected void initialize(DataTreeNode rootNode) {
546         /* create the implicit root node */
547         initialize(new DeltaDataTree(new DataTreeNode(null, null, new AbstractDataTreeNode[] {rootNode})));
548     }
549
550     protected void initialize(DeltaDataTree newTree) {
551         // Keep this element tree as the data of the root node.
552
// Useful for canonical results for ElementTree.getParent().
553
// see getParent().
554
treeStamp = treeCounter++;
555         newTree.setData(newTree.rootKey(), this);
556         this.tree = newTree;
557     }
558
559     /**
560      * Returns whether this tree is immutable.
561      */

562     public boolean isImmutable() {
563         return tree.isImmutable();
564     }
565
566     /**
567      * Merges a chain of deltas for a certain subtree to this tree.
568      * If this tree has any data in the specified subtree, it will
569      * be overwritten. The receiver tree must be open, and it will
570      * be made immutable during the merge operation. The trees in the
571      * provided array will be replaced by new trees that have been
572      * merged into the receiver's delta chain.
573      *
574      * @param path The path of the subtree chain to merge
575      * @param trees The chain of trees to merge. The trees can be
576      * in any order, but they must all form a simple ancestral chain.
577      * @return A new open tree with the delta chain merged in.
578      */

579     public ElementTree mergeDeltaChain(IPath path, ElementTree[] trees) {
580         if (path == null || trees == null) {
581             throw new IllegalArgumentException JavaDoc(NLS.bind(Messages.watson_nullArg, "ElementTree.mergeDeltaChain")); //$NON-NLS-1$
582
}
583
584         /* The tree has to be open */
585         if (isImmutable()) {
586             throw new IllegalArgumentException JavaDoc(Messages.watson_immutable);
587         }
588         ElementTree current = this;
589         if (trees.length > 0) {
590             /* find the oldest tree to be merged */
591             ElementTree toMerge = trees[findOldest(trees)];
592
593             /* merge the trees from oldest to newest */
594             while (toMerge != null) {
595                 if (path.isRoot()) {
596                     //copy all the children
597
IPath[] children = toMerge.getChildren(Path.ROOT);
598                     for (int i = 0; i < children.length; i++) {
599                         current.createSubtree(children[i], toMerge.getSubtree(children[i]));
600                     }
601                 } else {
602                     //just copy the specified node
603
current.createSubtree(path, toMerge.getSubtree(path));
604                 }
605                 current.immutable();
606
607                 /* replace the tree in the array */
608                 /* we have to go through all trees because there may be duplicates */
609                 for (int i = 0; i < trees.length; i++) {
610                     if (trees[i] == toMerge) {
611                         trees[i] = current;
612                     }
613                 }
614                 current = current.newEmptyDelta();
615                 toMerge = toMerge.getParent();
616             }
617         }
618         return current;
619     }
620
621     /**
622      * Creates a new element tree which is represented as a delta on this one.
623      * Initially they have the same content. Subsequent changes to the new
624      * tree will not affect this one.
625      */

626     public synchronized ElementTree newEmptyDelta() {
627         // Don't want old trees hanging onto cached infos.
628
lookupCache = lookupCacheIgnoreCase = null;
629         return new ElementTree(this);
630     }
631
632     /**
633      * Returns a mutable copy of the element data for the given path.
634      * This copy will be held onto in the most recent delta.
635      * ElementTree data MUST implement the IElementTreeData interface
636      * for this method to work. If the data does not define that interface
637      * this method will fail.
638      */

639     public synchronized Object JavaDoc openElementData(IPath key) {
640         Assert.isTrue(!isImmutable());
641
642         /* don't allow modification of the implicit root */
643         if (key.isRoot())
644             return null;
645         DataTreeLookup lookup = lookupCache; // Grab it in case it's replaced concurrently.
646
if (lookup == null || lookup.key != key) {
647             lookupCache = lookup = tree.lookup(key);
648         }
649         if (lookup.isPresent) {
650             if (lookup.foundInFirstDelta)
651                 return lookup.data;
652             /**
653              * The node has no data in the most recent delta.
654              * Pull it up to the present delta by setting its data with a clone.
655              */

656             IElementTreeData oldData = (IElementTreeData) lookup.data;
657             if (oldData != null) {
658                 try {
659                     Object JavaDoc newData = oldData.clone();
660                     tree.setData(key, newData);
661                     lookupCache = lookupCacheIgnoreCase = null;
662                     return newData;
663                 } catch (ObjectNotFoundException e) {
664                     elementNotFound(key);
665                 }
666             }
667         } else {
668             elementNotFound(key);
669         }
670         return null;
671     }
672
673     /**
674      * Sets the element for the given element identifier.
675      * The given element must be present in this tree.
676      * @param key element identifier
677      * @param data element info, or <code>null</code>
678      */

679     public synchronized void setElementData(IPath key, Object JavaDoc data) {
680         /* don't allow modification of the implicit root */
681         if (key.isRoot())
682             return;
683
684         Assert.isNotNull(key);
685         // Clear the lookup cache, in case the element being modified is the same
686
// as for the last lookup.
687
lookupCache = lookupCacheIgnoreCase = null;
688         try {
689             tree.setData(key, data);
690         } catch (ObjectNotFoundException e) {
691             elementNotFound(key);
692         }
693     }
694
695     /**
696      * Sets the user data associated with this tree.
697      */

698     public void setTreeData(IElementTreeData data) {
699         userData = data;
700     }
701
702     /* (non-Javadoc
703      * Method declared on IStringPoolParticipant
704      */

705     public void shareStrings(StringPool set) {
706         tree.storeStrings(set);
707     }
708
709     /**
710      * Returns a string representation of this element tree's
711      * structure suitable for debug purposes.
712      */

713     public String JavaDoc toDebugString() {
714         final StringBuffer JavaDoc buffer = new StringBuffer JavaDoc("\n"); //$NON-NLS-1$
715
IElementContentVisitor visitor = new IElementContentVisitor() {
716             public boolean visitElement(ElementTree aTree, IPathRequestor elementID, Object JavaDoc elementContents) {
717                 buffer.append(elementID.requestPath() + " " + elementContents + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
718
return true;
719             }
720         };
721         new ElementTreeIterator(this, Path.ROOT).iterate(visitor);
722         return buffer.toString();
723     }
724
725     public String JavaDoc toString() {
726         return "ElementTree(" + treeStamp + ")"; //$NON-NLS-1$ //$NON-NLS-2$
727
}
728 }
729
Popular Tags