/******************************************************************************* * Copyright (c) 2000, 2006 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.internal.utils.Messages; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.osgi.util.NLS; /** * Data trees can be viewed as generic multi-leaf trees. The tree points to a single * rootNode, and each node can contain an arbitrary number of children.<p> * * <p>Internally, data trees can be either complete trees (DataTree class), or delta * trees (<code>DeltaDataTree</code> class). A DataTree is a stand-alone tree * that contains all its own data. A <code>DeltaDataTree</code> only stores the * differences between itself and its parent tree. This sparse representation allows * the API user to retain chains of delta trees that represent incremental changes to * a system. Using the delta trees, the user can undo changes to a tree by going up to * the parent tree. * * <p>Both representations of the tree support the same API, so the user of a tree * never needs to know if they're dealing with a complete tree or a chain of deltas. * Delta trees support an extended API of delta operations. See the <code>DeltaDataTree * </code> class for details. * * @see DataTree * @see DeltaDataTree */ public abstract class AbstractDataTree { /** * Whether modifications to the given source tree are allowed */ private boolean immutable = false; /** * Singleton indicating no children */ protected static final IPath[] NO_CHILDREN = new IPath[0]; /** * Creates a new empty tree */ public AbstractDataTree() { this.empty(); } /** * Returns a copy of the receiver, which shares the receiver's * instance variables. */ protected AbstractDataTree copy() { AbstractDataTree newTree = this.createInstance(); newTree.setImmutable(this.isImmutable()); newTree.setRootNode(this.getRootNode()); return newTree; } /** * Returns a copy of the node subtree rooted at the given key. * */ public abstract AbstractDataTreeNode copyCompleteSubtree(IPath key); /** * Creates a new child in the tree. If a child with such a name exists, * it is replaced with the new child * * @param parentKey key of parent for new child. * @param localName name for new child. * @exception ObjectNotFoundException * parentKey does not exist in the receiver * @exception RuntimeException * receiver is immutable */ public abstract void createChild(IPath parentKey, String localName); /** * Creates a new child in the tree. If a child with such a name exists, * it is replaced with the new child * * @param parentKey key of parent for new child. * @param localName name for new child. * @param object the data for the new child * @exception ObjectNotFoundException * parentKey does not exist in the receiver * @exception RuntimeException * receiver is immutable */ public abstract void createChild(IPath parentKey, String localName, Object object); /** * Creates and returns a new instance of the tree. This is an * implementation of the factory method creational pattern for allowing * abstract methods to create instances. * * @return the new tree. */ protected abstract AbstractDataTree createInstance(); /** * Creates or replaces a subtree in the tree. The parent node must exist. * * @param key key of parent of subtree to create/replace * @param subtree new subtree to add to tree * @exception RuntimeException receiver is immutable */ public abstract void createSubtree(IPath key, AbstractDataTreeNode subtree); /** * Deletes a child from the tree. * * <p>Note: this method requires both parentKey and localName, * making it impossible to delete the root node. * * @param parentKey parent of node to delete. * @param localName name of node to delete. * @exception ObjectNotFoundException * a child of parentKey with name localName does not exist in the receiver * @exception RuntimeException * receiver is immutable */ public abstract void deleteChild(IPath parentKey, String localName); /** * Initializes the receiver so that it is a complete, empty tree. The * result does not represent a delta on another tree. An empty tree is * defined to have a root node with null data and no children. */ public abstract void empty(); /** * Returns the key of a node in the tree. * * @param parentKey * parent of child to retrieve. * @param index * index of the child to retrieve in its parent. * @exception ObjectNotFoundException * parentKey does not exist in the receiver * @exception ArrayIndexOutOfBoundsException * if no child with the given index (runtime exception) */ public IPath getChild(IPath parentKey, int index) { /* Get name of given child of the parent */ String child = getNameOfChild(parentKey, index); return parentKey.append(child); } /** * Returns the number of children of a node * * @param parentKey * key of the node for which we want to retreive the number of children * @exception ObjectNotFoundException * parentKey does not exist in the receiver */ public int getChildCount(IPath parentKey) { return getNamesOfChildren(parentKey).length; } /** * Returns the keys of all children of a node. * * @param parentKey * key of parent whose children we want to retrieve. * @exception ObjectNotFoundException * parentKey does not exist in the receiver */ public IPath[] getChildren(IPath parentKey) { String names[] = getNamesOfChildren(parentKey); int len = names.length; if (len == 0) return NO_CHILDREN; IPath answer[] = new IPath[len]; for (int i = 0; i < len; i++) { answer[i] = parentKey.append(names[i]); } return answer; } /** * Returns the data of a node. * * @param key * key of node for which we want to retrieve data. * @exception ObjectNotFoundException * key does not exist in the receiver */ public abstract Object getData(IPath key); /** * Returns the local name of a node in the tree * * @param parentKey * parent of node whose name we want to retrieve * @param index * index of node in its parent * @exception ObjectNotFoundException * parentKey does not exist in the receiver * @exception ArrayIndexOutOfBoundsException * if no child with the given index */ public String getNameOfChild(IPath parentKey, int index) { String childNames[] = getNamesOfChildren(parentKey); /* Return the requested child as long as its in range */ return childNames[index]; } /** * Returns the local names for the children of a node * * @param parentKey * key of node whose children we want to retrieve * @exception ObjectNotFoundException * parentKey does not exist in the receiver */ public abstract String[] getNamesOfChildren(IPath parentKey); /** * Returns the root node of the tree. * * <p>Both subclasses must be able to return their root node. However subclasses * can have different types of root nodes, so this is not enforced as an abstract * method */ AbstractDataTreeNode getRootNode() { throw new AbstractMethodError(Messages.dtree_subclassImplement); } /** * Handles the case where an attempt was made to modify * the tree when it was in an immutable state. Throws * an unchecked exception. */ static void handleImmutableTree() { throw new RuntimeException(Messages.dtree_immutable); } /** * Handles the case where an attempt was made to manipulate * an element in the tree that does not exist. Throws an * unchecked exception. */ static void handleNotFound(IPath key) { throw new ObjectNotFoundException(NLS.bind(Messages.dtree_notFound, key)); } /** * Makes the tree immutable */ public void immutable() { immutable = true; } /** * Returns true if the receiver includes a node with the given key, false * otherwise. * * @param key * key of node to find */ public abstract boolean includes(IPath key); /** * Returns true if the tree is immutable, and false otherwise. */ public boolean isImmutable() { return immutable; } /** * 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 abstract DataTreeLookup lookup(IPath key); /** * Returns the key of the root node. */ public IPath rootKey() { return Path.ROOT; } /** * Sets the data of a node. * * @param key * key of node for which to set data * @param data * new data value for node * @exception ObjectNotFoundException * the nodeKey does not exist in the receiver * @exception IllegalArgumentException * receiver is immutable */ public abstract void setData(IPath key, Object data); /** * Sets the immutable field. */ void setImmutable(boolean bool) { immutable = bool; } /** * Sets the root node of the tree. * * <p>Both subclasses must be able to set their root node. However subclasses * can have different types of root nodes, so this is not enforced as an abstract * method */ void setRootNode(AbstractDataTreeNode node) { throw new Error(Messages.dtree_subclassImplement); } }