platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/DataTree.java
changeset 40 eb3c938c7fef
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/DataTree.java	Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,281 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.dtree;
+
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * A <code>DataTree</code> represents the complete information of a source tree.
+ * The tree contains all information within its branches, and has no relation to any
+ * other source trees (no parent and no children).
+ */
+public class DataTree extends AbstractDataTree {
+
+	/**
+	 * the root node of the tree
+	 */
+	private DataTreeNode rootNode;
+
+	/**
+	 * Creates a new empty tree
+	 */
+	public DataTree() {
+		this.empty();
+	}
+
+	/**
+	 * Returns a copy of the node subtree rooted at the given key.
+	 *
+	 * @param key
+	 *	Key of subtree to copy
+	 */
+	public AbstractDataTreeNode copyCompleteSubtree(IPath key) {
+		DataTreeNode node = findNodeAt(key);
+		if (node == null) {
+			handleNotFound(key);
+		}
+		return copyHierarchy(node);
+	}
+
+	/**
+	 * Returns a deep copy of a node and all its children.
+	 *
+	 * @param node
+	 *	Node to be copied.
+	 */
+	DataTreeNode copyHierarchy(DataTreeNode node) {
+		DataTreeNode newNode;
+		int size = node.size();
+		if (size == 0) {
+			newNode = new DataTreeNode(node.getName(), node.getData());
+		} else {
+			AbstractDataTreeNode[] children = node.getChildren();
+			DataTreeNode[] newChildren = new DataTreeNode[size];
+			for (int i = size; --i >= 0;) {
+				newChildren[i] = this.copyHierarchy((DataTreeNode) children[i]);
+			}
+			newNode = new DataTreeNode(node.getName(), node.getData(), newChildren);
+		}
+
+		return newNode;
+	}
+
+	/**
+	 * Creates a new child in the tree.
+	 * @see AbstractDataTree#createChild(IPath, String)
+	 */
+	public void createChild(IPath parentKey, String localName) {
+		createChild(parentKey, localName, null);
+	}
+
+	/**
+	 * Creates a new child in the tree.
+	 * @see AbstractDataTree#createChild(IPath, String, Object)
+	 */
+	public void createChild(IPath parentKey, String localName, Object data) {
+		DataTreeNode node = findNodeAt(parentKey);
+		if (node == null)
+			handleNotFound(parentKey);
+		if (this.isImmutable())
+			handleImmutableTree();
+		/* If node already exists, replace */
+		if (node.includesChild(localName)) {
+			node.replaceChild(localName, new DataTreeNode(localName, data));
+		} else {
+			this.replaceNode(parentKey, node.copyWithNewChild(localName, new DataTreeNode(localName, data)));
+		}
+	}
+
+	/**
+	 * Creates and returns an instance of the receiver.  This is an 
+	 * implementation of the factory method creational pattern for allowing
+	 * abstract methods to create instances
+	 */
+	protected AbstractDataTree createInstance() {
+		return new DataTree();
+	}
+
+	/**
+	 * Creates or replaces a subtree in the tree.  The parent node must exist.
+	 *
+	 * @param key
+	 *	Key of parent node whose subtree we want to create/replace.
+	 * @param subtree
+	 *	New node to insert into tree.
+	 */
+	public void createSubtree(IPath key, AbstractDataTreeNode subtree) {
+
+		// Copy it since destructive mods are allowed on the original
+		// and shouldn't affect this tree.
+		DataTreeNode newNode = copyHierarchy((DataTreeNode) subtree);
+
+		if (this.isImmutable()) {
+			handleImmutableTree();
+		}
+
+		if (key.isRoot()) {
+			setRootNode(newNode);
+		} else {
+			String localName = key.lastSegment();
+			newNode.setName(localName); // Another mod, but it's OK we've already copied
+
+			IPath parentKey = key.removeLastSegments(1);
+
+			DataTreeNode node = findNodeAt(parentKey);
+			if (node == null) {
+				handleNotFound(parentKey);
+			}
+
+			/* If node already exists, replace */
+			if (node.includesChild(localName)) {
+				node.replaceChild(localName, newNode);
+			}
+
+			this.replaceNode(parentKey, node.copyWithNewChild(localName, newNode));
+		}
+	}
+
+	/**
+	 * Deletes a child of the tree.
+	 * @see AbstractDataTree#deleteChild(IPath, String)
+	 */
+	public void deleteChild(IPath parentKey, String localName) {
+		if (this.isImmutable())
+			handleImmutableTree();
+		DataTreeNode node = findNodeAt(parentKey);
+		if (node == null || (!node.includesChild(localName))) {
+			handleNotFound(node == null ? parentKey : parentKey.append(localName));
+		} else {
+			this.replaceNode(parentKey, node.copyWithoutChild(localName));
+		}
+	}
+
+	/**
+	 * Initializes the receiver.
+	 * @see AbstractDataTree#empty()
+	 */
+	public void empty() {
+		this.setRootNode(new DataTreeNode(null, null));
+	}
+
+	/**
+	 * Returns the specified node if it is present, otherwise returns null.
+	 *
+	 * @param key
+	 *	Key of node to return
+	 */
+	public DataTreeNode findNodeAt(IPath key) {
+		AbstractDataTreeNode node = this.getRootNode();
+		int keyLength = key.segmentCount();
+		for (int i = 0; i < keyLength; i++) {
+			try {
+				node = node.childAt(key.segment(i));
+			} catch (ObjectNotFoundException notFound) {
+				return null;
+			}
+		}
+		return (DataTreeNode) node;
+	}
+
+	/**
+	 * Returns the data at the specified node.
+	 *
+	 * @param key
+	 *	Node whose data to return.
+	 */
+	public Object getData(IPath key) {
+		DataTreeNode node = findNodeAt(key);
+		if (node == null) {
+			handleNotFound(key);
+			return null;
+		}
+		return node.getData();
+	}
+
+	/**
+	 * Returns the names of the children of a node.
+	 * @see AbstractDataTree#getNamesOfChildren(IPath)
+	 */
+	public String[] getNamesOfChildren(IPath parentKey) {
+		DataTreeNode parentNode;
+		parentNode = findNodeAt(parentKey);
+		if (parentNode == null) {
+			handleNotFound(parentKey);
+			return null;
+		}
+		return parentNode.namesOfChildren();
+	}
+
+	/**
+	 * Returns the root node of the tree
+	 */
+	AbstractDataTreeNode getRootNode() {
+		return rootNode;
+	}
+
+	/**
+	 * Returns true if the receiver includes a node with
+	 * the given key, false otherwise.
+	 */
+	public boolean includes(IPath key) {
+		return (findNodeAt(key) != null);
+	}
+
+	/**
+	 * Returns an object containing:
+	 * 	- a flag indicating whether the specified node was found
+	 *  - the data for the node, if it was found
+	 * @param key
+	 *	key of node for which we want to retrieve data.
+	 */
+	public DataTreeLookup lookup(IPath key) {
+		DataTreeNode node = this.findNodeAt(key);
+		if (node == null)
+			return DataTreeLookup.newLookup(key, false, null);
+		return DataTreeLookup.newLookup(key, true, node.getData());
+	}
+
+	/**
+	 * Replaces the node at the specified key with the given node
+	 */
+	protected void replaceNode(IPath key, DataTreeNode node) {
+		DataTreeNode found;
+		if (key.isRoot()) {
+			this.setRootNode(node);
+		} else {
+			found = this.findNodeAt(key.removeLastSegments(1));
+			found.replaceChild(key.lastSegment(), node);
+		}
+	}
+
+	/**
+	 * Sets the data at the specified node.
+	 * @see AbstractDataTree#setData(IPath, Object)
+	 */
+	public void setData(IPath key, Object data) {
+		DataTreeNode node = this.findNodeAt(key);
+		if (this.isImmutable())
+			handleImmutableTree();
+		if (node == null) {
+			handleNotFound(key);
+		} else {
+			node.setData(data);
+		}
+	}
+
+	/**
+	 * Sets the root node of the tree
+	 * @see AbstractDataTree#setRootNode(AbstractDataTreeNode)
+	 */
+	void setRootNode(DataTreeNode aNode) {
+		rootNode = aNode;
+	}
+}