/******************************************************************************* * Copyright (c) 2000, 2009 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.core.internal.watson; import org.eclipse.core.internal.dtree.AbstractDataTreeNode; import org.eclipse.core.internal.dtree.DataTreeNode; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; /** * A class for performing operations on each element in an element tree. * For example, this can be used to print the contents of a tree. * <p> * When creating an ElementTree iterator, an element tree and root path must be * supplied. When the <code>iterate()</code> method is called, a visitor object * must be provided. The visitor is called once for each node of the tree. For * each node, the visitor is passed the entire tree, the object in the tree at * that node, and a callback for requesting the full path of that node. * <p> * <b>Example:</b> <code><pre> // printing a crude representation of the poster child IElementContentVisitor visitor= new IElementContentVisitor() { public boolean visitElement(ElementTree tree, IPathRequestor requestor, Object elementContents) { System.out.println(requestor.requestPath() + " -> " + elementContents); return true; } }); ElementTreeIterator iterator = new ElementTreeIterator(tree, Path.ROOT); iterator.iterate(visitor); </pre></code> */ public class ElementTreeIterator implements IPathRequestor { //for path requestor private String[] segments = new String[10]; private int nextFreeSegment; /* the tree being visited */ private ElementTree tree; /* the root of the subtree to visit */ private IPath path; /* the immutable data tree being visited */ private DataTreeNode treeRoot; /** * Creates a new element tree iterator for visiting the given tree starting * at the given path. */ public ElementTreeIterator(ElementTree tree, IPath path) { this.tree = tree; this.path = path; //treeRoot can be null if deleted concurrently //must copy the tree while owning the tree's monitor to prevent concurrent deletion while creating visitor's copy synchronized (tree) { treeRoot = (DataTreeNode) tree.getDataTree().safeCopyCompleteSubtree(path); } } /** * Iterates through the given element tree and visit each element (node) * passing in the element's ID and element object. */ private void doIteration(DataTreeNode node, IElementContentVisitor visitor) { //push the name of this node to the requestor stack if (nextFreeSegment >= segments.length) { grow(); } segments[nextFreeSegment++] = node.getName(); //do the visit if (visitor.visitElement(tree, this, node.getData())) { //recurse AbstractDataTreeNode[] children = node.getChildren(); for (int i = children.length; --i >= 0;) { doIteration((DataTreeNode) children[i], visitor); } } //pop the segment from the requestor stack nextFreeSegment--; if (nextFreeSegment < 0) nextFreeSegment = 0; } /** * Method grow. */ private void grow() { //grow the segments array int oldLen = segments.length; String[] newPaths = new String[oldLen * 2]; System.arraycopy(segments, 0, newPaths, 0, oldLen); segments = newPaths; } /** * Iterates through this iterator's tree and visits each element in the * subtree rooted at the given path. The visitor is passed each element's * data and a request callback for obtaining the path. */ public void iterate(IElementContentVisitor visitor) { if (path.isRoot()) { //special visit for root element to use special treeData if (visitor.visitElement(tree, this, tree.getTreeData())) { if (treeRoot == null) return; AbstractDataTreeNode[] children = treeRoot.getChildren(); for (int i = children.length; --i >= 0;) { doIteration((DataTreeNode) children[i], visitor); } } } else { if (treeRoot == null) return; push(path, path.segmentCount() - 1); doIteration(treeRoot, visitor); } } /** * Push the first "toPush" segments of this path. */ private void push(IPath pathToPush, int toPush) { if (toPush <= 0) return; for (int i = 0; i < toPush; i++) { if (nextFreeSegment >= segments.length) { grow(); } segments[nextFreeSegment++] = pathToPush.segment(i); } } public String requestName() { if (nextFreeSegment == 0) return ""; //$NON-NLS-1$ return segments[nextFreeSegment - 1]; } public IPath requestPath() { if (nextFreeSegment == 0) return Path.ROOT; int length = nextFreeSegment; for (int i = 0; i < nextFreeSegment; i++) { length += segments[i].length(); } StringBuffer pathBuf = new StringBuffer(length); for (int i = 0; i < nextFreeSegment; i++) { pathBuf.append('/'); pathBuf.append(segments[i]); } return new Path(null, pathBuf.toString()); } }