
info.monitorenter.cpdetector.util.collections.ITreeNode Maven / Gradle / Ivy
/*
* ITreenode, a basic interface for a tree containing an inner default implementation.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this collection are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the cpDetector code in [sub] packages info.monitorenter and
* cpdetector.
*
* The Initial Developer of the Original Code is
* Achim Westermann .
*
* Portions created by the Initial Developer are Copyright (c) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** *
*
* If you modify or optimize the code in a useful way please let me know.
* [email protected]
*
**/
package info.monitorenter.cpdetector.util.collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
/**
* Basic interface for a tree node. It is an approach at node-level instead of a
* procedural interface from outside. Working with a tree from outside very
* often is not as fast and incurs additional memory constumption for saving
* states and partial results (no recursion may keep traversal-related data on
* the heap, but the current positions for step-operations has to saved).
* Supporting an "outside-interface" for a tree is recommended by using a
* visitor pattern but may also be done in a classical way.
*
* Every node of the tree may carry a so-called "user-Object". While the
* structure of the tree models the relation of these objects, they themselve
* contain the data needed.
*
*
* @author Achim Westermann
*/
public interface ITreeNode {
/**
* Returned from {@link #getParent()} of the root node of the tree.
*
*/
public static ITreeNode ROOT = new DefaultTreeNode();
/**
* Returns the "user" Object that may be carried by the ITreeNode,
* or null, if no one was assigned.
*
*
* @return The user Object carried by this treenode or null, if no one was
* assigned.
*/
public Object getUserObject();
/**
* Assigns the "user" Object that may be carried by the ITreeNode.
*
*
* @return The previous user Object that was carried or null, if no one was
* assigned.
*/
public Object setUserObject(Object store);
/**
*
* Marks this ITreeNode instance (e.g. for being visited or
* invisible). Marking may be used to traverse the tree in an asynchronous
* manner from outside (TreeIterator) or for other desireable tasks.
*
*
* Subsequent calls to this method should not change the state to "unmarked".
*
*
*/
public void mark();
/**
*
* Unmarks this ITreeNode instance (e.g. for being visited or
* invisible). Marking may be used to traverse the tree in an asynchronous
* manner from outside (TreeIterator) or for other desireable tasks.
*
*
* Subsequent calls to this method should not change the state to "marked".
*
*/
public void unmark();
/**
*
* Check, wether this ITreeNode is marked.
*
*
* @return True, if this ITreeNode is marked, false else.
*/
public boolean isMarked();
/**
*
* Returns the amount of child ITreeNodes of this
* ITreeNode.
*
*/
public int getChildCount();
/**
*
* Get the amount of direct and indirect childs of this ITreeNode.
*
*
* @return The total amount of all ITreeNode instances of this
* subtree (including this node).
*/
public int getSubtreeCount();
/**
*
* The {@link java.util.Iterator}returned will not traverse the whole subtree
* but only the direct child nodes of this ITreeNode.
*
*
* @return An {@link java.util.Iterator}over the direct childs of this
* ITreeNode.
*/
public Iterator getChilds();
/**
*
* Get the parent node of this ITreeNode. If the TreeNode has no
* parent node (e.g. a member of an implementation for the parent node is
* null), {@link #ROOT}has to be returned.
*
*
* @return The parent ITreeNode of this node or {@link #ROOT}, if
* this node is the root.
*/
public ITreeNode getParent();
/**
*
* This method should not be called from outside. It is a callback for the
* method {@link #addChildNode(info.monitorenter.cpdetector.util.collections.ITreeNode)}and should be used by
* implementations of that method but not any further. Else inconsistencies
* could occur: A node thinks it is the father of another node which itself
* does not know about that relation any more (remove itself from the old
* parent member's list could avoid it).
*
*
* @param parent
* The new parental node.
*/
public void setParent(ITreeNode parent);
/**
*
* Adds the given ITreeNode to the set of this instances childs.
* Note, that the given node has to get to know, that it has a new parent
* (implementation detail).
*
*
* @param node
* The new child ITreeNode whose parent this instance will
* become.
* @return True, if the operation was succesful: For example some
* implementations (e.g. with support for unique user Objects in
* childs on the same level with the same parent node) may dissallow
* duplicate child nodes.
*/
public boolean addChildNode(ITreeNode node);
/**
*
* Adds all given ITreeNode instances to the set of this instances
* childs. This operation should delegate to {@link #addChildNode(info.monitorenter.cpdetector.util.collections.ITreeNode)}.
*
*
* @param nodes
* An arry of ITreeNode instances.
* @return True, if all childs could be added (null instances are skipped),
* false else.
*/
public boolean addChildNodes(ITreeNode[] nodes);
/**
*
* Comfortable method for adding child nodes. Could be expressed as:
*
*
* ...
* ITreeNode ret = new <TreeNodeImpl>(m_userObject);
* if(this.addChildNode(ret){
* return ret;
* }
* else{
* return null;
* }
* ...
*
*
*
*
*
* @param userObject
* The Object that should be carried by the new ITreeNode
* or null, if no one is desired.
* @return The newly allocated ITreeNode or null, if the
* operation failed .
*/
public ITreeNode addChild(Object userObject);
/**
* Adds all given Objects to newly instanciated ITreeNode instances
* that are added to the set of this instances childs. This operation should
* delegate to {@link #addChild(Object)}.
*
*
* @param userObjects
* An arry of Objects instances which will become the
* m_userObject members of the newly created ITreeNode
* instances.
*
* @return An array containing all new ITreeNode instances created
* (they contain the user objects).
*/
public ITreeNode[] addChildren(Object[] userObjects);
/**
*
* Remove the given ITreeNode from this node. If the operation is
* successful the given node will not have any parent node (e.g. null for
* parent member) but be the root node of it's subtree.
*
*
* The operation may fail, if the given ITreeNode is no child of
* this node, or the implementation permits the removal.
*
*
* @param node
* A child of this ITreeNode (by the means of the
* {@link #equals(Object)}operation) .
* @return True, if the removal was successful, false if not.
*/
public boolean removeChild(ITreeNode node);
/**
* Remove the ITreeNode and it's whole subree! in this
* node's subtree, that contains a user Object equal to the given argument.
* The search is performed in a recursive manner quitting when the first
* removal could be performed.
*
*
* @param userObject
* The user Object identifying the treenode to remove.
*
* @return The TreeNode (being the new root of it's subtree) that
* was removed from this node's subtree or null, if no
* ITreeNode contained an equal user Object.
*/
public ITreeNode remove(Object userObject);
/**
* Remove all child nodes of the ITreeNode.
*
*
* @return A {@link java.util.List}containing all removed child nodes. An
* empty List should be provided instead of null!
*/
public List removeAllChildren();
/**
*
* Get all child nodes of the ITreeNode.
*
*
* @return A {@link java.util.List}containing all child nodes. An empty List
* should be provided instead of null!
*/
public List getAllChildren();
/**
* Find out, wether a certain "user" Object is carried by any of this
* ITreeNodes subtree.
*
*
* @param userObject
* Any Object, that might be contained in this tree -
* Identification is done by the means of the Objects
* {@link Object#equals(Object)}- method.
*
* @return True, if any node of this subtree (including this
* ITreeNode contained a user Object that was equal to the
* given argument.
*/
public boolean contains(Object userObject);
/**
*
* Find out, wether the given ITreeNode is a member of this node's
* subtree.
*
*
* @param node
* A ITreeNode that migth possibly linked to (or be) this
* instance by the means of the equals operation.
* @return True, if an ITreeNode equal to the argument was found in
* the subtree started by this node.
*
* @see #equals(Object)
*/
public boolean containsNode(ITreeNode node);
/**
*
* Find out, wether this ITreeNode is a childless leaf.
*
*
* @return True, if this ITreeNode has no child nodes.
*/
public boolean isLeaf();
/**
*
* Find out, wether there is no path to a higher parental node from this
* ITreeNode.
*
*
* @return True if this poor node is an orphan.
*/
public boolean isRoot();
/**
* @param l
* An empty List: After this call it will be filled with
* {@link info.monitorenter.cpdetector.util.collections.ITreeNode}instances starting from the root node to the
* current node that is invoked.
*/
public void getPathFromRoot(List l);
/**
* @param l
* An empty List: After this call it will be filled with the
* {@link #getUserObject()}instances starting from the root node to
* the current node that was invoked.
*
*/
public void getUserObjectPathFromRoot(List l);
/**
*
* Convenience method recommended for usage by implemenations.
*
*
* @param o
* Possibly a ITreeNode that has to be checked for
* equality.
* @return True, if your implementation judges both instances equal.
*
* @see #containsNode(info.monitorenter.cpdetector.util.collections.ITreeNode node)
* @see #removeChild(info.monitorenter.cpdetector.util.collections.ITreeNode node)
*/
public boolean equals(Object o);
/**
*
* Generic operations in default implementations may need to allocate new
* instances but have to choose the right type to support the provided
* invariants.
*
*
* If you provide a subclass that has invariants you should overload this.
*
*
* @return A new allocated instance of the concrete impelementation.
*/
public ITreeNode newInstance();
/**
*
* Plain-forward implementation of the {@link info.monitorenter.cpdetector.util.collections.ITreeNode}interface.
*
*
* This implementation covers many of the algorithms that may be somewhat
* tricky to implement in an elegant way for beginners. Subclasses may add
* other policies and constraints to achieve a special invariant with a
* certain behaviour.
*
*
* @author
* Create a ITreeNode without a parent that carries the given
* user Object.
*
*
* @param userObject
* An Object that is desired to be stored in the node.
*/
public DefaultTreeNode(final Object userObject) {
this();
this.m_userObject = userObject;
}
/**
* Create a ITreeNode without a parent that carries the given
* user Object and has the given ITreeNode as child.
*
*
* @param userObject
* An Object that is desired to be stored in the node.
* @param child
* The first child of this node.
*/
public DefaultTreeNode(final Object userObject, final ITreeNode child) {
this(userObject);
this.addChildNode(child);
}
/**
*
* Create a ITreeNode without a parent that carries the given
* user Object and has the given ITreeNode instances as
* m_children.
*
*
* Perhaps the most useful constructor. It allows to construct trees in a
* short overviewable way:
*
*
* new DefaultTreeNode(
* new Integer(1),
* new ITreeNode[]{
* new DefaultTreeNode(
* new Integer(2)
* ),
* new DefaultTreeNode(
* new Integer(3),
* new DefaultTreeNode(
* new Integer
* )
* )
* );
*
*
*
*
* @param userObject
* An Object that is desired to be stored in the node.
*
* @param children
* An array of ITreeNode instances that will become
* childs of this node.
*
*/
public DefaultTreeNode(final Object userObject, final ITreeNode[] children) {
this(userObject);
for (int i = 0; i < children.length; i++) {
this.addChildNode(children[i]);
}
}
/**
* The given Object will be stored in a newly created
* ITreeNode which will get assigned this node to be it's father,
* while this instance will store the new node in it's list
* (unconditionally: e.g. no test, if already contained).
*
*
* @return See the {@link info.monitorenter.cpdetector.util.collections.ITreeNode}{interface}: Null may be returned in
* case of failure!!!
*
* @see info.monitorenter.cpdetector.util.collections.ITreeNode#addChild(Object)
*/
public final ITreeNode addChild(final Object userObject) {
ITreeNode ret = this.newInstance();
ret.setUserObject(userObject);
if (this.addChildNode(ret)) {
return ret;
} else {
return ret.getParent();
}
}
/**
* The given ITreeNode will become this node to be it's father,
* while this instance will store the given node in it's list if not null
* (unconditionally: e.g. no test, if already contained).
*
*
* @param node
* the node to add as child.
*
* @see info.monitorenter.cpdetector.util.collections.ITreeNode#addChildNode(info.monitorenter.cpdetector.util.collections.ITreeNode)
*/
public boolean addChildNode(final ITreeNode node) {
if (node == null) {
return false;
}
node.setParent(this);
this.m_children.add(node);
return true;
}
/*
* (non-Javadoc)
*
* @see aw.util.collections.ITreeNode#contains(java.lang.Object)
*/
public final boolean contains(Object userObject) {
if ((this.m_userObject != null) && (this.m_userObject.equals(userObject))) {
return true;
} else {
if (!this.isLeaf()) {
Iterator it = this.m_children.iterator();
while (it.hasNext()) {
if (((ITreeNode) it.next()).contains(userObject))
return true;
}
return false;
}
return false;
}
}
/*
* (non-Javadoc)
*
* @see aw.util.collections.ITreeNode#containsNode(aw.util.collections.ITreeNode)
*/
public final boolean containsNode(ITreeNode node) {
if (this.equals(node)) {
return true;
} else {
if (!this.isLeaf()) {
Iterator it = this.m_children.iterator();
while (it.hasNext()) {
if (((ITreeNode) it.next()).contains(node)) {
return true;
}
}
return false;
}
return false;
}
}
/*
* (non-Javadoc)
*
* @see aw.util.collections.ITreeNode#getChildCount()
*/
public final int getChildCount() {
return this.m_children.size();
}
/*
* (non-Javadoc)
*
* @see aw.util.collections.ITreeNode#getChilds()
*/
public final Iterator getChilds() {
return this.m_children.iterator();
}
/*
* (non-Javadoc)
*
* @see aw.util.collections.ITreeNode#getParent()
*/
public final ITreeNode getParent() {
return (this.m_parent == null) ? ROOT : this.m_parent;
}
/*
* (non-Javadoc)
*
* @see aw.util.collections.ITreeNode#getSubtreeCount()
*/
public final int getSubtreeCount() {
// hehehe: clever double-use of child detection and partial result...
int ret = this.m_children.size();
if (ret > 0) {
Iterator it = this.m_children.iterator();
while (it.hasNext()) {
ret += ((ITreeNode) it.next()).getSubtreeCount();
}
}
if (this.m_parent == ROOT) {
ret++; // root has to count itself...
}
return ret;
}
/*
* (non-Javadoc)
*
* @see aw.util.collections.ITreeNode#getUserObject()
*/
public final Object getUserObject() {
return this.m_userObject;
}
/*
* (non-Javadoc)
*
* @see aw.util.collections.ITreeNode#mark()
*/
public final void mark() {
this.marked = true;
}
public final boolean isMarked() {
return this.marked;
}
/**
*
* The search is a "prefix-search":
*
*
*
*
*
* A
* / \
* B C
* / \
* D E
*
*
*
*
*
* The search will be done in the order: A,B,D,E,C. If this
* ITreeNode contains the user Object equal to the argument,
* itself will be returned. This ITreeNode may be the root!
*
*
* @see info.monitorenter.cpdetector.util.collections.ITreeNode#remove(Object)
*/
public final ITreeNode remove(final Object userObject) {
ITreeNode ret = null;
if ((this.m_userObject != null) && (this.m_userObject.equals(userObject))) {
this.m_parent.removeChild(this);
this.m_parent = null;
ret = this;
} else {
if (!this.isLeaf()) {
Iterator it = this.m_children.iterator();
while (it.hasNext()) {
ret = ((ITreeNode) it.next());
if (ret != null) {
break;
}
}
} else
return null;
}
return ret;
}
/*
* (non-Javadoc)
*
* @see aw.util.collections.ITreeNode#removeAllChilds()
*/
public final List removeAllChildren() {
SortedSet ret = this.m_children;
Iterator it = ret.iterator();
while (it.hasNext()) {
((ITreeNode) it.next()).setParent(null);
}
this.m_children = new TreeSet();
return new LinkedList(ret);
}
/*
* (non-Javadoc)
*
* @see aw.util.collections.ITreeNode#removeChild(aw.util.collections.ITreeNode)
*/
public boolean removeChild(ITreeNode node) {
return this.m_children.remove(node);
}
/*
* (non-Javadoc)
*
* @see aw.util.collections.ITreeNode#setUserObject()
*/
public final Object setUserObject(Object store) {
Object ret = this.m_userObject;
this.m_userObject = store;
return ret;
}
/*
* (non-Javadoc)
*
* @see aw.util.collections.ITreeNode#unmark()
*/
public final void unmark() {
this.marked = false;
}
/*
* (non-Javadoc)
*
* @see aw.util.collections.ITreeNode#setParent(aw.util.collections.ITreeNode)
*/
public final void setParent(final ITreeNode parent) {
if (this.m_parent != null) {
// will call: node.setParent(null);
this.m_parent.removeChild(this);
}
this.m_parent = parent;
}
/*
* (non-Javadoc)
*
* @see aw.util.collections.ITreeNode#isLeaf()
*/
public final boolean isLeaf() {
return this.m_children.size() == 0;
}
/*
* (non-Javadoc)
*
* @see aw.util.collections.ITreeNode#isRoot()
*/
public final boolean isRoot() {
return this.m_parent == null;
}
public String toString() {
StringBuffer ret = new StringBuffer();
this.toStringInternal(ret, 1);
return ret.toString();
}
protected void toStringInternal(StringBuffer buf, int depth) {
if (this.isLeaf()) {
buf.append("-> ");
}
buf.append('(').append(String.valueOf(this.m_userObject)).append(')');
StringBuffer spaceCollect = new StringBuffer();
for (int i = depth; i > 0; i--) {
spaceCollect.append(" ");
}
String indent = spaceCollect.toString();
Iterator it = this.getChilds();
while (it.hasNext()) {
buf.append("\n").append(indent);
((DefaultTreeNode) it.next()).toStringInternal(buf, depth + 1);
}
}
/*
* (non-Javadoc)
*
* @see aw.util.collections.ITreeNode#addChildNodes(aw.util.collections.ITreeNode[])
*/
public final boolean addChildNodes(ITreeNode[] nodes) {
boolean ret = true;
for (int i = 0; i < nodes.length; i++) {
ret &= this.addChildNode(nodes[i]);
}
return ret;
}
/*
* (non-Javadoc)
*
* @see aw.util.collections.ITreeNode#addChildren(java.lang.Object[])
*/
public final ITreeNode[] addChildren(Object[] userObjects) {
List treeNodes = new LinkedList(); // can't know the size, as they might
// contain null.
ITreeNode newNode = null;
for (int i = 0; i < userObjects.length; i++) {
newNode = this.addChild(userObjects[i]);
if (newNode != null) {
treeNodes.add(newNode);
}
}
return (ITreeNode[]) treeNodes.toArray(new ITreeNode[treeNodes.size()]);
}
/*
* (non-Javadoc)
*
* @see aw.util.collections.ITreeNode#getAllChildren()
*/
public final List getAllChildren() {
return new LinkedList(this.m_children);
}
/*
* (non-Javadoc)
*
* @see aw.util.collections.ITreeNode#newInstance()
*/
public ITreeNode newInstance() {
return new DefaultTreeNode();
}
public void getPathFromRoot(List l) {
if (this.isRoot()) {
l.add(this);
} else {
this.getParent().getPathFromRoot(l);
l.add(this);
}
}
/*
* (non-Javadoc)
*
* @see cpdetector.util.collections.ITreeNode#getUserObjectPathFromRoot(java.util.List)
*/
public void getUserObjectPathFromRoot(List l) {
List collect = new LinkedList();
this.getPathFromRoot(collect);
Iterator it = collect.iterator();
while (it.hasNext()) {
l.add(((ITreeNode) it.next()).getUserObject());
}
}
public int compareTo(final ITreeNode o) throws ClassCastException {
ITreeNode other = (ITreeNode) o;
return ((Comparable) this.m_userObject).compareTo(other.getUserObject());
}
}
}