package org.nnsoft.guice.rocoto.variables;
/*
* Copyright 2009-2012 The 99 Software Foundation
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.util.ArrayList;
import java.util.List;
/**
* Basic implementation of a tree.
*
* @param <T>
*/
final class Tree<T>
{
/** Current tree node data */
private T data;
/** Parent node */
private Tree<T> parent = null;
/** Children */
private List<Tree<T>> children = new ArrayList<Tree<T>>();
/**
* Default constructor
*
* @param data
*/
public Tree( T data )
{
this.data = data;
}
/**
* @return True if parent is not null
*/
public boolean isRoot()
{
return parent == null;
}
/**
* @return True if no children
*/
public boolean isLeaf()
{
return children.isEmpty();
}
/**
* Add a new leaf to this tree
*
* @param child
* @return Tree node of the newly added leaf
*/
public Tree<T> addLeaf( T child )
{
Tree<T> leaf = new Tree<T>( child );
leaf.parent = this;
children.add( leaf );
return leaf;
}
/**
* @return Parent node, or null if this node is root
*/
public Tree<T> getParent()
{
return parent;
}
/**
* Remove this tree from its parent, if any.
*/
public void removeFromParent()
{
if ( !isRoot() )
{
getParent().removeSubtree( this );
}
}
/**
* Remove given subtree
*
* @param subtree
*/
public void removeSubtree( Tree<T> subtree )
{
if ( children.remove( subtree ) )
{
subtree.parent = null;
}
}
/**
* @return Node data
*/
public T getData()
{
return data;
}
/**
*
* @return Node depth
*/
public int getDepth()
{
int depth = 0;
Tree<T> curr = parent;
while ( curr != null )
{
curr = curr.parent;
depth++;
}
return depth;
}
/**
* @return Subtrees
*/
public List<Tree<T>> getChildren()
{
return children;
}
/**
* Add a subtree, provided tree is not modified.
*
* @param subtree
* @return subtree copy added
*/
public Tree<T> addSubtree( Tree<T> subtree )
{
Tree<T> copy = addLeaf( subtree.data );
copy.children = new ArrayList<Tree<T>>( subtree.children );
return copy;
}
/**
* @param element
* @return true if element is contained in this node subtrees.
*/
public boolean inSubtrees( T element )
{
for ( Tree<T> child : getChildren() )
{
if ( child.isElement( element ) || child.inSubtrees( element ) )
{
return true;
}
}
return false;
}
/**
* @param element
* @return True if element is equal to this node data.
*/
public boolean isElement( T element )
{
return ( data.equals( element ) );
}
/**
*
* @param element
* @return true if element is is in the ancestors of this node
*/
public boolean inAncestors( T element )
{
if ( !isRoot() )
{
return getParent().isElement( element ) || getParent().inAncestors( element );
}
return false;
}
/**
* @return Root node, this if current node is root
*/
public Tree<T> getRoot()
{
if ( isRoot() )
{
return this;
}
return getParent().getRoot();
}
@Override
public String toString()
{
StringBuilder buffer = new StringBuilder();
toString( buffer, 0 );
return buffer.toString();
}
private void toString( StringBuilder buffer, int level )
{
// Create proper indent
// Search for next cousins in each level
StringBuilder indent = new StringBuilder();
Tree<T> prev;
Tree<T> curr = this;
for ( int i = level - 1; i >= 0; i-- )
{
prev = curr;
curr = prev.parent;
if ( i == level - 1 )
{
indent.append( " _|" );
}
else
{
indent.append( " " );
if ( i < level && curr.children.indexOf( prev ) < curr.children.size() - 1 )
{
indent.append( "|" );
}
else
{
indent.append( " " );
}
}
}
buffer.append( indent.reverse() );
// Print data
buffer.append( getData() ).append( "\n" );
// Print subtrees
for ( Tree<T> child : getChildren() )
{
child.toString( buffer, level + 1 );
}
}
}