/**
* Copyright (c) 2003-2009, Xith3D Project Group all rights reserved.
*
* Portions based on the Java3D interface, Copyright by Sun Microsystems.
* Many thanks to the developers of Java3D and Sun Microsystems for their
* innovation and design.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the 'Xith3D Project Group' nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) A
* RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE
*/
package org.xith3d.scenegraph;
import java.util.ArrayList;
import java.util.List;
import org.jagatoo.logging.ProfileTimer;
import org.openmali.spatial.bounds.BoundingSphere;
import org.openmali.spatial.bounds.Bounds;
import org.xith3d.render.CanvasPeer;
import org.xith3d.render.Clipper;
import org.xith3d.render.ClipperInfo;
import org.xith3d.render.ScissorRect;
import org.xith3d.render.preprocessing.ShadowAtom;
import org.xith3d.scenegraph.traversal.TraversalCallback;
import org.xith3d.scenegraph.utils.CopyListener;
import org.xith3d.utility.logging.X3DLog;
/**
* A Node, that can hold a list of child Nodes.
*
* @author David Yazel
* @author Marvin Froehlich (aka Qudus)
* @author Amos Wenger (aka BlueSky)
*/
public abstract class GroupNode extends Node
{
protected final GroupNode hostGroup;
/**
* The list of children nodes in this Group.
*/
protected Node[] children = null;
protected int numChildren = 0;
protected long totalNumChildren = 0L;
protected long totalNumShapes = 0L;
private ShadowAtom shadowAtom = null;
private ScissorRect scissorRect = null;
private Clipper clipper = null;
private static final Bounds EMPTY_BOUNDS = new BoundingSphere();
private static BoundingSphere tmpBounds = new BoundingSphere();
private GroupNode pickHost = null;
private boolean isPickHost = false;
/**
* {@inheritDoc}
*/
@Override
protected boolean setLive( boolean live )
{
if ( !super.setLive( live ) )
return ( false );
final int n = numChildren();
for ( int i = 0; i < n; i++ )
{
getChild( i ).setLive( live );
}
return ( true );
}
/**
* Recursively searches for (grand-)children to set the pick-host property.
* Stops the seach, if a child is a GroupNode and the pick-host flag is
* set.
*
* @param pickHost the GroupNode, which serves as the pick-host
*/
protected void setPickHost( GroupNode pickHost )
{
if ( !isPickHost() )
{
this.pickHost = pickHost;
final int n = numChildren();
for ( int i = 0; i < n; i++ )
{
final Node child = getChild( i );
if ( child instanceof Leaf )
{
( (Leaf)child ).setPickHost( pickHost );
}
else if ( child instanceof GroupNode )
{
( (GroupNode)child ).setPickHost( pickHost );
}
}
}
}
/**
* @return this GroupNode's pick-host
*/
public final GroupNode getPickHost()
{
return ( pickHost );
}
/**
* Sets this Group to be a pick-host or not.
* A Shape3D provides a method getPickHost(), which will return the
* (deepest) GroupNode, which is a pick-host.
*
* @see Leaf#getPickHost()
*
* @param isPickHost
*/
public void setPickHost( boolean isPickHost )
{
if ( this.isPickHost == isPickHost )
return;
this.isPickHost = isPickHost;
final GroupNode ph;
if ( isPickHost() )
ph = this;
else
ph = getPickHost();
final int n = numChildren();
for ( int i = 0; i < n; i++ )
{
final Node child = getChild( i );
if ( child instanceof Leaf )
{
( (Leaf)child ).setPickHost( ph );
}
else if ( child instanceof GroupNode )
{
final GroupNode g = (GroupNode)child;
if ( !g.isPickHost() )
g.setPickHost( ph );
}
}
}
/**
* @return the pick-host flag of this GroupNode.
* A Shape3D provides a method getPickHost(), which will return the
* (deepest) GroupNode, which is a pick-host.
*
* @see Shape3D#getPickHost()
*/
public boolean isPickHost()
{
return ( isPickHost );
}
final void setAtom( ShadowAtom shadowAtom )
{
this.shadowAtom = shadowAtom;
}
final ShadowAtom getAtom()
{
return ( shadowAtom );
}
protected final void checkChild( Node child, int index )
{
if ( child == this )
throw new IllegalSceneGraphOperation( "You cannot add a Group to itself." );
else if ( child == null )
throw new IllegalSceneGraphOperation( "You cannot add null to a group." );
else if ( child.getParent() != null )
throw new IllegalSceneGraphOperation( "This Node already has a parent." );
else if ( child instanceof BranchGroup )
throw new IllegalSceneGraphOperation( "You cannot add a (root) BranchGroup to another group." );
else if ( ( index > numChildren ) || ( index < 0 ) )
throw new ArrayIndexOutOfBoundsException( "Illegal index " + index );
}
@Override
protected void mergeInheritedNodes( InheritedNodeAttributes in )
{
super.mergeInheritedNodes( in );
final int n = numChildren();
for ( int i = 0; i < n; i++ )
{
getChild( i ).mergeInheritedNodes( in );
}
}
@Override
protected void unmergeInheritedNodes( InheritedNodeAttributes in )
{
super.unmergeInheritedNodes( in );
final int n = numChildren();
for ( int i = 0; i < n; i++ )
{
getChild( i ).unmergeInheritedNodes( in );
}
}
@Override
protected void unmergeInheritedLight( Light light )
{
super.unmergeInheritedLight( light );
final int n = numChildren();
for ( int i = 0; i < n; i++ )
{
getChild( i ).unmergeInheritedLight( light );
}
}
@Override
protected void unmergeInheritedFog( Fog fog )
{
super.unmergeInheritedFog( fog );
final int n = numChildren();
for ( int i = 0; i < n; i++ )
{
getChild( i ).unmergeInheritedFog( fog );
}
}
protected boolean ensureCapacity( int minCapacity )
{
if ( children == null )
{
children = new Node[ Math.max( minCapacity, 8 ) ];
return ( true );
}
final int oldCapacity = children.length;
if ( minCapacity > oldCapacity )
{
final int newCapacity = ( oldCapacity * 3 ) / 2 + 1;
final Node[] newArray = new Node[ newCapacity ];
System.arraycopy( children, 0, newArray, 0, oldCapacity );
children = newArray;
return ( true );
}
return ( false );
}
void addTotalNumChildrenFromChild( long additionalTotalNumChildren, long additionalTotalNumShapes )
{
this.totalNumChildren += additionalTotalNumChildren;
this.totalNumShapes += additionalTotalNumShapes;
if ( getParent() != null )
getParent().addTotalNumChildrenFromChild( additionalTotalNumChildren, additionalTotalNumShapes );
}
/**
* Insert a child at the specified index. The parent of the child is set to
* this object. If this object is live then the child is set live.
*/
public void addChild( Node child, int index )
{
if ( hostGroup != null )
{
hostGroup.addChild( child, index );
return;
}
checkChild( child, index );
if ( SceneGraph.CHECK_FOR_ILLEGAL_MODIFICATION )
SceneGraph.checkForIllegalModification( this );
ensureCapacity( index + 1 );
if ( index < numChildren )
{
System.arraycopy( children, index, children, index + 1, numChildren - index );
}
children[ index ] = child;
numChildren++;
child.setParent( this ); // this will make the child tree live/not
final long tnc = totalNumChildren;
final long tns = totalNumShapes;
if ( child instanceof GroupNode )
{
final GroupNode childGroup = (GroupNode)child;
childGroup.setPickHost( this.isPickHost() ? this : getPickHost() );
totalNumChildren += 1 + childGroup.totalNumChildren;
totalNumShapes += childGroup.totalNumShapes;
}
else if ( child instanceof Leaf )
{
totalNumChildren += 1;
if ( child instanceof Shape3D )
{
totalNumShapes += 1;
}
( (Leaf)child ).setPickHost( this.isPickHost() ? this : getPickHost() );
}
if ( this.getParent() != null )
{
this.getParent().addTotalNumChildrenFromChild( totalNumChildren - tnc, totalNumShapes - tns );
}
if ( child instanceof Light )
{
getInheritedNodeAttributes().addLight( (Light)child );
mergeInheritedNodes( this.getInheritedNodeAttributes() );
}
else if ( child instanceof Fog )
{
getInheritedNodeAttributes().addFog( (Fog)child );
mergeInheritedNodes( this.getInheritedNodeAttributes() );
}
else
{
child.mergeInheritedNodes( this.getInheritedNodeAttributes() );
}
if ( getModListener() != null )
getModListener().onChildAddedToGroup( this, child );
}
/**
* Add a child to the group as the last child in the group. The parent of
* the child is set to this object. If this object is live then the child is
* made live.
*/
public final void addChild( Node child )
{
addChild( child, numChildren() );
}
/**
* Remove the child at the specified index. The parent of the child is set
* to null. The child is made not live.
*/
public Node removeChild( int index )
{
if ( hostGroup != null )
{
return ( hostGroup.removeChild( index ) );
}
if ( SceneGraph.CHECK_FOR_ILLEGAL_MODIFICATION )
SceneGraph.checkForIllegalModification( this );
if ( index >= numChildren )
throw new IllegalArgumentException( "This child does not exist in this group" );
final Node child = children[ index ];
if ( child == null )
throw new IllegalArgumentException( "This child does not exist in this group" );
if ( getModListener() != null )
getModListener().onChildRemovedFromGroup( this, child );
children[ index ] = null;
System.arraycopy( children, index + 1, children, index, numChildren - index - 1 );
numChildren--;
child.setParent( null ); // this will make the child tree not
// live
child.setModListener( null );
final long tnc = totalNumChildren;
final long tns = totalNumShapes;
if ( child instanceof GroupNode )
{
final GroupNode childGroup = (GroupNode)child;
( (GroupNode)child ).setPickHost( (GroupNode)null );
totalNumChildren -= 1 + childGroup.totalNumChildren;
}
else if ( child instanceof Leaf )
{
totalNumChildren -= 1;
if ( child instanceof Shape3D )
{
totalNumShapes -= 1;
}
( (Leaf)child ).setPickHost( (GroupNode)null );
}
if ( this.getParent() != null )
{
this.getParent().addTotalNumChildrenFromChild( totalNumChildren - tnc, totalNumShapes - tns );
}
if ( child instanceof Light )
{
unmergeInheritedLight( (Light)child );
child.getInheritedNodeAttributes().removeLight( (Light)child );
}
else if ( child instanceof Fog )
{
unmergeInheritedFog( (Fog)child );
child.getInheritedNodeAttributes().removeFog( (Fog)child );
}
else
{
child.unmergeInheritedNodes( this.getInheritedNodeAttributes() );
}
return ( child );
}
/**
* @param child
*
* @return the index of the given child Node in the list of this Group's children.
*/
public final int indexOf( Node child )
{
if ( hostGroup != null )
{
return ( hostGroup.indexOf( child ) );
}
if ( child == null )
throw new NullPointerException( "child is null" );
for ( int i = 0; i < numChildren; i++ )
{
if ( child.equals( children[ i ] ) )
return ( i );
}
return ( -1 );
}
/**
* Remove a child from the group. The parent of the child is set to null.
* The child is made not live.
*/
public final int removeChild( Node child )
{
final int index = indexOf( child );
if ( index == -1 )
throw new IllegalArgumentException( "This child does not exist in this group" );
removeChild( index );
return ( index );
}
/**
* Removes all the children of this group.
*/
public void removeAllChildren()
{
final int n = numChildren();
for ( int i = n - 1; i >= 0; i-- )
removeChild( i );
}
/**
* Set the child at the specified index. If an existing child is at that
* location then it is removed, its parent is set to null and it is set not
* live. If this object is live then the child is set live.
*
* @param child
* @param index
*
* @return the Node, that previously lived at the given position
*/
public Node setChild( Node child, int index )
{
if ( hostGroup != null )
{
return ( hostGroup.setChild( child, index ) );
}
checkChild( child, index );
final Node prevChild;
if ( index == numChildren )
prevChild = null;
else
prevChild = removeChild( index );
addChild( child, index );
return ( prevChild );
}
/**
* @return the child at the specified index.
*/
public final Node getChild( int index )
{
if ( hostGroup != null )
{
return ( hostGroup.getChild( index ) );
}
if ( ( index < 0 ) || ( index >= numChildren ) )
//throw new ArrayIndexOutOfBoundsException( "index " + index + " is greater than max-index " + ( numChildren - 1 ) ) );
return ( null );
return ( children[ index ] );
}
/**
* Get the number of children in this group.
*/
public final int numChildren()
{
if ( hostGroup != null )
{
return ( hostGroup.numChildren() );
}
return ( numChildren );
}
/**
* @return the total number of children in this group and its subgroups.
*/
public final long getTotalNumChildren()
{
if ( hostGroup != null )
{
return ( hostGroup.getTotalNumChildren() );
}
return ( totalNumChildren );
}
/**
* @return the total number of shapes in this group and its subgroups.
*/
public final long getTotalNumShapes()
{
if ( hostGroup != null )
{
return ( hostGroup.getTotalNumShapes() );
}
return ( totalNumShapes );
}
/**
* Fills this group's children into the given list.
* Note: The list is not cleared before.
*
* @param list
*/
public final <L extends List<Node>> L getChildren( L list )
{
if ( hostGroup != null )
{
return ( hostGroup.getChildren( list ) );
}
for ( int i = 0; i < numChildren; i++ )
{
list.add( children[ i ] );
}
return ( list );
}
/**
* @return a read-only List of all contained children
*
* @deprecated use {@link #numChildren()} and {@link #getChild(int)} instead.
*/
@Deprecated
public ArrayList< Node > getChildren()
{
return ( getChildren( new ArrayList< Node >( numChildren() ) ) );
}
/**
* {@inheritDoc}
*/
@Override
protected void setBoundsDirtyUpward()
{
boundsDirty = true;
if ( getParent() != null )
{
getParent().setBoundsDirtyUpward();
}
}
/**
* {@inheritDoc}
*/
@Override
protected void setBoundsDirty()
{
setBoundsDirtyUpward();
}
/**
*
* @param node
* @param forceNodeUpdate
*/
protected final void expandBounds( Node node, boolean forceNodeUpdate )
{
// if we already have the bounds then return
if ( isIgnoreBounds() || node.isIgnoreBounds() || !this.boundsAutoCompute )
{
return;
}
ProfileTimer.startProfile( X3DLog.LOG_CHANNEL, "Node::expandBounds" );
//System.out.println( node + ", " + node.getWorldBounds() );
//if (forceNodeUpdate && (this.getTransformGroup() != null))
node.updateBoundsCheap( false, true, false, true );
//System.out.println( node + ", " + node.getWorldBounds() );
final int num = this.numChildren();
if ( ( num == 0 ) || ( ( num == 1 ) && ( this.getChild( 0 ) == node ) ) )
untransformedBounds.set( node.getBounds() );
else
untransformedBounds.combine( node.getBounds() );
bounds.set( untransformedBounds );
if ( this instanceof TransformGroup )
bounds.transform( ( (TransformGroup)this ).getTransform().getMatrix4f() );
getWorldBounds().set( untransformedBounds );
getWorldBounds().transform( getWorldTransform().getMatrix4f() );
final GroupNode parent = getParent();
if ( parent != null )
{
if ( !parent.boundsDirty )
parent.expandBounds( this, false );
else
parent.updateBoundsCheap( true, false, true, false );
}
ProfileTimer.endProfile();
}
/**
* {@inheritDoc}
*/
@Override
protected void updateBoundsCheap( boolean onlyDirty, boolean childrenToo, boolean parentToo, boolean onlyWorld )
{
// if we already have the bounds then return
if ( ( isIgnoreBounds() ) || ( !boundsDirty && onlyDirty ) )
{
return;
}
if ( boundsAutoCompute )
{
if ( numChildren() == 0 )
{
untransformedBounds.set( EMPTY_BOUNDS );
bounds.set( EMPTY_BOUNDS );
getWorldBounds().set( untransformedBounds );
if ( getWorldTransform() != null )
getWorldBounds().transform( getWorldTransform().getMatrix4f() );
}
else
{
boolean firstValidBounds = true;
final int n = numChildren();
for ( int i = 0; i < n; i++ )
{
final Node node = getChild( i );
if ( !node.isIgnoreBounds() )
{
if ( childrenToo )
{
node.updateBoundsCheap( onlyDirty, childrenToo, false, onlyWorld );
}
if ( !onlyWorld )
{
tmpBounds.set( node.untransformedBounds );
if ( this instanceof TransformGroup )
tmpBounds.transform( ( (TransformGroup)this ).getTransform().getMatrix4f() );
}
if ( firstValidBounds )
{
firstValidBounds = false;
if ( !onlyWorld )
{
untransformedBounds.set( node.getBounds() );
}
getWorldBounds().set( node.getWorldBounds() );
}
else
{
if ( !onlyWorld )
{
untransformedBounds.combine( node.getBounds() );
}
getWorldBounds().combine( node.getWorldBounds() );
}
}
}
//if (!onlyWorld)
{
bounds.set( untransformedBounds );
if ( this instanceof TransformGroup )
bounds.transform( ( (TransformGroup)this ).getTransform().getMatrix4f() );
}
}
boundsDirty = false;
if ( parentToo )
{
final GroupNode parent = getParent();
if ( parent != null )
{
parent.boundsDirty = true;
parent.updateBoundsCheap( onlyDirty, false, parentToo, onlyWorld );
}
}
}
else
{
super.updateBoundsCheap( onlyDirty, childrenToo, parentToo, onlyWorld );
}
}
/**
* {@inheritDoc}
*/
@Override
public void updateBounds( boolean onlyDirty )
{
updateBoundsCheap( onlyDirty, true, true, false );
}
public void setShowBounds( boolean show, boolean childrenToo )
{
super.setShowBounds( show );
if ( childrenToo )
{
final int numChildren = numChildren();
for ( int i = 0; i < numChildren; i++ )
{
final Node child = getChild( i );
if ( child instanceof GroupNode )
( (GroupNode)child ).setShowBounds( show, childrenToo );
else
child.setShowBounds( show );
}
}
}
@Override
protected void mergeInheritedScissorRect( ScissorRect scissorRect )
{
if ( ( this.scissorRect != null ) && ( this.scissorRect != scissorRect ) )
return;
super.mergeInheritedScissorRect( scissorRect );
final int n = numChildren();
for ( int i = 0; i < n; i++ )
{
getChild( i ).mergeInheritedScissorRect( scissorRect );
}
}
/**
* Sets this Group's ScissorRect
*
* @param scissorRect the new ScissorRect
*/
public void setScissorRect( ScissorRect scissorRect )
{
if ( this.scissorRect == scissorRect )
return;
final ScissorRect oldValue = this.scissorRect;
this.scissorRect = scissorRect;
mergeInheritedScissorRect( scissorRect );
if ( getModListener() != null )
getModListener().onScissorRectChanged( this, oldValue, scissorRect );
}
/**
* @return this Group's ScissorBox
*/
public final ScissorRect getScissorRect()
{
return ( scissorRect );
}
@Override
protected void mergeInheritedClipper( Clipper clipper, ClipperInfo clipperInfo )
{
if ( ( this.clipper != null ) && ( this.clipper != clipper ) )
return;
if ( ( clipper != null ) && ( clipperInfo == null ) )
{
TransformGroup tg = this.getTransformGroup();
clipperInfo = new ClipperInfo( clipper, ( tg == null ) ? null : tg.getTransform() );
}
super.mergeInheritedClipper( clipper, clipperInfo );
final int n = numChildren();
for ( int i = 0; i < n; i++ )
{
getChild( i ).mergeInheritedClipper( clipper, clipperInfo );
}
}
/**
* Sets this Group's Clipper
*
* @param clipper the new Clipper
*/
public void setClipper( Clipper clipper )
{
if ( this.clipper == clipper )
return;
Clipper oldValue = this.clipper;
this.clipper = clipper;
mergeInheritedClipper( clipper, null );
if ( getModListener() != null )
getModListener().onClipperChanged( this, oldValue, clipper );
}
/**
* @return this Group's Clipper
*/
public final Clipper getClipper()
{
return ( clipper );
}
/**
* @return a new instance of this class. This is invoked by the sharedCopy() method.
*
* @see #sharedCopy(CopyListener)
*/
protected abstract GroupNode newInstance();
/**
* {@inheritDoc}
*/
@Override
public GroupNode sharedCopy( CopyListener listener )
{
final GroupNode newGroup = newInstance();
final int numChildren = numChildren();
for ( int i = 0; i < numChildren; i++ )
{
newGroup.addChild( getChild( i ).sharedCopy( listener ) );
}
/*
newGroup.setBoundsAutoCompute( false );
newGroup.setBounds( node.getBounds() );
*/
if ( this.isOccluder() )
newGroup.setIsOccluder( true );
newGroup.boundsDirty = true;
newGroup.updateBounds( true );
newGroup.setPickable( this.isPickable() );
newGroup.setRenderable( this.isRenderable() );
newGroup.setName( this.getName() );
if ( listener != null )
{
listener.onNodeCopied( this, newGroup, true );
}
return ( newGroup );
}
/**
* {@inheritDoc}
*/
@Override
public GroupNode sharedCopy()
{
return ( (GroupNode)super.sharedCopy() );
}
/**
* {@inheritDoc}
*/
@Override
public void absorbDetails( Node node )
{
assert ( node instanceof GroupNode ) : "The given node is not a GroupNode.";
final GroupNode group = (GroupNode)node;
removeAllChildren();
final int n = numChildren();
for ( int i = 0; i < n; i++ )
{
addChild( getChild( i ).sharedCopy() );
}
/*
newGroup.setBoundsAutoCompute( false );
newGroup.setBounds( node.getBounds() );
*/
setIsOccluder( group.isOccluder() );
this.boundsDirty = true;
updateBounds( true );
setPickable( group.isPickable() );
setPickable( group.isRenderable() );
}
/**
* {@inheritDoc}
*/
@Override
protected void dump( int indent )
{
System.out.println( getIndentString( indent ) + this );
for ( int i = 0; i < numChildren(); i++ )
{
getChild( i ).dump( indent + 1 );
}
}
/**
* Dumps this Group and it's children to stdout.
*/
public void dump()
{
System.out.println( "dumping group " + this.toString() );
dump( 0 );
}
/**
* {@inheritDoc}
*/
@Override
public void freeOpenGLResources( CanvasPeer canvasPeer )
{
for ( int i = 0; i < numChildren(); i++ )
{
getChild( i ).freeOpenGLResources( canvasPeer );
}
}
/**
* Traverses the scenegraph from this node on. If this Node is a Group it
* will recusively run through each child.
*
* @param callback the listener is notified of any traversed Node on the way
* @return if false, the whole traversal will stop
*/
@Override
public boolean traverse( TraversalCallback callback )
{
if ( !callback.traversalOperation( this ) )
return ( false );
if ( callback.traversalCheckGroup( this ) )
{
final int numChildren = numChildren();
for ( int i = 0; i < numChildren; i++ )
{
if ( !getChild( i ).traverse( callback ) )
return ( false );
}
}
return ( true );
}
/**
* @param name the name to be searched for
* @return the first child named "name", or <i>null</i> if not foud
*/
public Node findFirst( final String name )
{
class MyTraversalCallback implements TraversalCallback
{
public Node foundNode = null;
public boolean traversalCheckGroup( GroupNode group )
{
// Explorate each group (we never know..)
return ( true );
}
public boolean traversalOperation( Node node )
{
if ( ( ( name == null && node.getName() == null ) ) || ( node.getName().equals( name ) ) )
{
foundNode = node;
return ( false );
}
return ( true );
}
}
MyTraversalCallback tcb = new MyTraversalCallback();
this.traverse( tcb );
return ( tcb.foundNode );
}
/**
* @param name the name to be searched for
* @return all the found children named "name", or <i>null</i> if none foud
*/
public List< Node > findAll( final String name )
{
class MyTraversalCallback implements TraversalCallback
{
public ArrayList< Node > foundNodes = null;
public boolean traversalCheckGroup( GroupNode group )
{
// Explorate each group (we never know..)
return ( true );
}
public boolean traversalOperation( Node node )
{
if ( ( ( name == null && node.getName() == null ) ) || ( node.getName().equals( name ) ) )
{
if ( foundNodes == null )
{
foundNodes = new ArrayList< Node >();
}
foundNodes.add( node );
}
return ( true );
}
}
MyTraversalCallback tcb = new MyTraversalCallback();
this.traverse( tcb );
return ( tcb.foundNodes );
}
/**
* @param searchedClass the class to search for
* @return a list of the first node of the specified class in this group, or
* <i>null</i> if not found
*/
public < NT extends Node > NT findFirst( final Class< NT > searchedClass )
{
class MyTraversalCallback implements TraversalCallback
{
public NT foundNode = null;
public boolean traversalCheckGroup( GroupNode group )
{
// Explorate each group (we never know..)
return ( true );
}
@SuppressWarnings( "unchecked" )
public boolean traversalOperation( Node node )
{
if ( searchedClass.isAssignableFrom( node.getClass() ) )
{
foundNode = (NT)node;
return ( false );
}
return ( true );
}
}
MyTraversalCallback tcb = new MyTraversalCallback();
this.traverse( tcb );
return ( tcb.foundNode );
}
/**
* @param searchedClass the class to search for
* @return a list of all nodes of the specified class found in this group or
* <i>null</i> if none found
*/
public < NT extends Node > List< NT > findAll( final Class< NT > searchedClass )
{
class MyTraversalCallback implements TraversalCallback
{
public ArrayList< NT > foundNodes = null;
public boolean traversalCheckGroup( GroupNode group )
{
// Explorate each group (we never know..)
return ( true );
}
@SuppressWarnings( "unchecked" )
public boolean traversalOperation( Node node )
{
if ( searchedClass.isAssignableFrom( node.getClass() ) )
{
if ( foundNodes == null )
{
foundNodes = (ArrayList< NT >)new ArrayList();
}
foundNodes.add( (NT)node );
return ( true );
}
return ( true );
}
}
MyTraversalCallback tcb = new MyTraversalCallback();
this.traverse( tcb );
return ( tcb.foundNodes );
}
/**
* Constructs a new Group object.
*
* @param hostGroup the group to be forwarded all the add/remove methods.
*/
public GroupNode( GroupNode hostGroup )
{
super();
this.hostGroup = hostGroup;
if ( hostGroup == null )
{
this.children = null;
this.numChildren = 0;
}
else
{
this.children = new Node[] { hostGroup };
this.numChildren = 1;
}
}
/**
* Constructs a new Group object.
*/
public GroupNode()
{
this( null );
}
}