platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/DataTree.java
changeset 40 eb3c938c7fef
equal deleted inserted replaced
39:2a03ec4dbf31 40:eb3c938c7fef
       
     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.dtree;
       
    12 
       
    13 import org.eclipse.core.runtime.IPath;
       
    14 
       
    15 /**
       
    16  * A <code>DataTree</code> represents the complete information of a source tree.
       
    17  * The tree contains all information within its branches, and has no relation to any
       
    18  * other source trees (no parent and no children).
       
    19  */
       
    20 public class DataTree extends AbstractDataTree {
       
    21 
       
    22 	/**
       
    23 	 * the root node of the tree
       
    24 	 */
       
    25 	private DataTreeNode rootNode;
       
    26 
       
    27 	/**
       
    28 	 * Creates a new empty tree
       
    29 	 */
       
    30 	public DataTree() {
       
    31 		this.empty();
       
    32 	}
       
    33 
       
    34 	/**
       
    35 	 * Returns a copy of the node subtree rooted at the given key.
       
    36 	 *
       
    37 	 * @param key
       
    38 	 *	Key of subtree to copy
       
    39 	 */
       
    40 	public AbstractDataTreeNode copyCompleteSubtree(IPath key) {
       
    41 		DataTreeNode node = findNodeAt(key);
       
    42 		if (node == null) {
       
    43 			handleNotFound(key);
       
    44 		}
       
    45 		return copyHierarchy(node);
       
    46 	}
       
    47 
       
    48 	/**
       
    49 	 * Returns a deep copy of a node and all its children.
       
    50 	 *
       
    51 	 * @param node
       
    52 	 *	Node to be copied.
       
    53 	 */
       
    54 	DataTreeNode copyHierarchy(DataTreeNode node) {
       
    55 		DataTreeNode newNode;
       
    56 		int size = node.size();
       
    57 		if (size == 0) {
       
    58 			newNode = new DataTreeNode(node.getName(), node.getData());
       
    59 		} else {
       
    60 			AbstractDataTreeNode[] children = node.getChildren();
       
    61 			DataTreeNode[] newChildren = new DataTreeNode[size];
       
    62 			for (int i = size; --i >= 0;) {
       
    63 				newChildren[i] = this.copyHierarchy((DataTreeNode) children[i]);
       
    64 			}
       
    65 			newNode = new DataTreeNode(node.getName(), node.getData(), newChildren);
       
    66 		}
       
    67 
       
    68 		return newNode;
       
    69 	}
       
    70 
       
    71 	/**
       
    72 	 * Creates a new child in the tree.
       
    73 	 * @see AbstractDataTree#createChild(IPath, String)
       
    74 	 */
       
    75 	public void createChild(IPath parentKey, String localName) {
       
    76 		createChild(parentKey, localName, null);
       
    77 	}
       
    78 
       
    79 	/**
       
    80 	 * Creates a new child in the tree.
       
    81 	 * @see AbstractDataTree#createChild(IPath, String, Object)
       
    82 	 */
       
    83 	public void createChild(IPath parentKey, String localName, Object data) {
       
    84 		DataTreeNode node = findNodeAt(parentKey);
       
    85 		if (node == null)
       
    86 			handleNotFound(parentKey);
       
    87 		if (this.isImmutable())
       
    88 			handleImmutableTree();
       
    89 		/* If node already exists, replace */
       
    90 		if (node.includesChild(localName)) {
       
    91 			node.replaceChild(localName, new DataTreeNode(localName, data));
       
    92 		} else {
       
    93 			this.replaceNode(parentKey, node.copyWithNewChild(localName, new DataTreeNode(localName, data)));
       
    94 		}
       
    95 	}
       
    96 
       
    97 	/**
       
    98 	 * Creates and returns an instance of the receiver.  This is an 
       
    99 	 * implementation of the factory method creational pattern for allowing
       
   100 	 * abstract methods to create instances
       
   101 	 */
       
   102 	protected AbstractDataTree createInstance() {
       
   103 		return new DataTree();
       
   104 	}
       
   105 
       
   106 	/**
       
   107 	 * Creates or replaces a subtree in the tree.  The parent node must exist.
       
   108 	 *
       
   109 	 * @param key
       
   110 	 *	Key of parent node whose subtree we want to create/replace.
       
   111 	 * @param subtree
       
   112 	 *	New node to insert into tree.
       
   113 	 */
       
   114 	public void createSubtree(IPath key, AbstractDataTreeNode subtree) {
       
   115 
       
   116 		// Copy it since destructive mods are allowed on the original
       
   117 		// and shouldn't affect this tree.
       
   118 		DataTreeNode newNode = copyHierarchy((DataTreeNode) subtree);
       
   119 
       
   120 		if (this.isImmutable()) {
       
   121 			handleImmutableTree();
       
   122 		}
       
   123 
       
   124 		if (key.isRoot()) {
       
   125 			setRootNode(newNode);
       
   126 		} else {
       
   127 			String localName = key.lastSegment();
       
   128 			newNode.setName(localName); // Another mod, but it's OK we've already copied
       
   129 
       
   130 			IPath parentKey = key.removeLastSegments(1);
       
   131 
       
   132 			DataTreeNode node = findNodeAt(parentKey);
       
   133 			if (node == null) {
       
   134 				handleNotFound(parentKey);
       
   135 			}
       
   136 
       
   137 			/* If node already exists, replace */
       
   138 			if (node.includesChild(localName)) {
       
   139 				node.replaceChild(localName, newNode);
       
   140 			}
       
   141 
       
   142 			this.replaceNode(parentKey, node.copyWithNewChild(localName, newNode));
       
   143 		}
       
   144 	}
       
   145 
       
   146 	/**
       
   147 	 * Deletes a child of the tree.
       
   148 	 * @see AbstractDataTree#deleteChild(IPath, String)
       
   149 	 */
       
   150 	public void deleteChild(IPath parentKey, String localName) {
       
   151 		if (this.isImmutable())
       
   152 			handleImmutableTree();
       
   153 		DataTreeNode node = findNodeAt(parentKey);
       
   154 		if (node == null || (!node.includesChild(localName))) {
       
   155 			handleNotFound(node == null ? parentKey : parentKey.append(localName));
       
   156 		} else {
       
   157 			this.replaceNode(parentKey, node.copyWithoutChild(localName));
       
   158 		}
       
   159 	}
       
   160 
       
   161 	/**
       
   162 	 * Initializes the receiver.
       
   163 	 * @see AbstractDataTree#empty()
       
   164 	 */
       
   165 	public void empty() {
       
   166 		this.setRootNode(new DataTreeNode(null, null));
       
   167 	}
       
   168 
       
   169 	/**
       
   170 	 * Returns the specified node if it is present, otherwise returns null.
       
   171 	 *
       
   172 	 * @param key
       
   173 	 *	Key of node to return
       
   174 	 */
       
   175 	public DataTreeNode findNodeAt(IPath key) {
       
   176 		AbstractDataTreeNode node = this.getRootNode();
       
   177 		int keyLength = key.segmentCount();
       
   178 		for (int i = 0; i < keyLength; i++) {
       
   179 			try {
       
   180 				node = node.childAt(key.segment(i));
       
   181 			} catch (ObjectNotFoundException notFound) {
       
   182 				return null;
       
   183 			}
       
   184 		}
       
   185 		return (DataTreeNode) node;
       
   186 	}
       
   187 
       
   188 	/**
       
   189 	 * Returns the data at the specified node.
       
   190 	 *
       
   191 	 * @param key
       
   192 	 *	Node whose data to return.
       
   193 	 */
       
   194 	public Object getData(IPath key) {
       
   195 		DataTreeNode node = findNodeAt(key);
       
   196 		if (node == null) {
       
   197 			handleNotFound(key);
       
   198 			return null;
       
   199 		}
       
   200 		return node.getData();
       
   201 	}
       
   202 
       
   203 	/**
       
   204 	 * Returns the names of the children of a node.
       
   205 	 * @see AbstractDataTree#getNamesOfChildren(IPath)
       
   206 	 */
       
   207 	public String[] getNamesOfChildren(IPath parentKey) {
       
   208 		DataTreeNode parentNode;
       
   209 		parentNode = findNodeAt(parentKey);
       
   210 		if (parentNode == null) {
       
   211 			handleNotFound(parentKey);
       
   212 			return null;
       
   213 		}
       
   214 		return parentNode.namesOfChildren();
       
   215 	}
       
   216 
       
   217 	/**
       
   218 	 * Returns the root node of the tree
       
   219 	 */
       
   220 	AbstractDataTreeNode getRootNode() {
       
   221 		return rootNode;
       
   222 	}
       
   223 
       
   224 	/**
       
   225 	 * Returns true if the receiver includes a node with
       
   226 	 * the given key, false otherwise.
       
   227 	 */
       
   228 	public boolean includes(IPath key) {
       
   229 		return (findNodeAt(key) != null);
       
   230 	}
       
   231 
       
   232 	/**
       
   233 	 * Returns an object containing:
       
   234 	 * 	- a flag indicating whether the specified node was found
       
   235 	 *  - the data for the node, if it was found
       
   236 	 * @param key
       
   237 	 *	key of node for which we want to retrieve data.
       
   238 	 */
       
   239 	public DataTreeLookup lookup(IPath key) {
       
   240 		DataTreeNode node = this.findNodeAt(key);
       
   241 		if (node == null)
       
   242 			return DataTreeLookup.newLookup(key, false, null);
       
   243 		return DataTreeLookup.newLookup(key, true, node.getData());
       
   244 	}
       
   245 
       
   246 	/**
       
   247 	 * Replaces the node at the specified key with the given node
       
   248 	 */
       
   249 	protected void replaceNode(IPath key, DataTreeNode node) {
       
   250 		DataTreeNode found;
       
   251 		if (key.isRoot()) {
       
   252 			this.setRootNode(node);
       
   253 		} else {
       
   254 			found = this.findNodeAt(key.removeLastSegments(1));
       
   255 			found.replaceChild(key.lastSegment(), node);
       
   256 		}
       
   257 	}
       
   258 
       
   259 	/**
       
   260 	 * Sets the data at the specified node.
       
   261 	 * @see AbstractDataTree#setData(IPath, Object)
       
   262 	 */
       
   263 	public void setData(IPath key, Object data) {
       
   264 		DataTreeNode node = this.findNodeAt(key);
       
   265 		if (this.isImmutable())
       
   266 			handleImmutableTree();
       
   267 		if (node == null) {
       
   268 			handleNotFound(key);
       
   269 		} else {
       
   270 			node.setData(data);
       
   271 		}
       
   272 	}
       
   273 
       
   274 	/**
       
   275 	 * Sets the root node of the tree
       
   276 	 * @see AbstractDataTree#setRootNode(AbstractDataTreeNode)
       
   277 	 */
       
   278 	void setRootNode(DataTreeNode aNode) {
       
   279 		rootNode = aNode;
       
   280 	}
       
   281 }