/*******************************************************************************
* 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);
}
}