/*
 * NodeFinder.java
 *
 * Created on 10 September 2003, 11:36
 */


import java.util.Vector;
import java.util.HashSet;
import java.util.Enumeration;

import javax.media.j3d.Node;
import javax.media.j3d.Group;
import javax.media.j3d.Link;
import javax.media.j3d.SharedGroup;
import javax.media.j3d.CapabilityNotSetException;

/** Utility class for searching a scene graph to recover all instances of a
 *  given node type.
 *
 * @author  Ewan Borland
 */
public abstract class NodeFinder
{
    /** Set containing all the shared groups that have already been visited in
     *  the scene graph traversal.*/
    private static HashSet     visitedSharedGroups   = null;

    /** List of all the nodes discovered by traversing the scene graph.*/
    private static Vector      foundNodes            = null;

    /** The class of nodes that are to be found.*/
    private static Class       nodeClass             = null;

    /** Object for synchronization.*/
    private static Object       sync                = new Object();



    /** Creates a new instance of NodeFinderInitializes the data structures
     *  necessary for scene graph traversal.
     */
    static
    {
        // Initialize the data structures
        visitedSharedGroups = new HashSet();
        foundNodes          = new Vector(10, 1);

    } // static



    /** Find all nodes of the given class that are contained in the scenegraph
     *  below the given root node.
     *
     *  @param root The root node of the scene graph to be searched.
     *
     *  @param nodeClassName A string containing the fully qualified class name
     *   for the type of node to be found e.g. "javax.media.j3d.Shape3D"
     *
     *  @return An Enumeration of all the nodes found.
     *
     *  @throws ClassNotFoundException If the named class cannot be located.
     *  @throws CapabilityNotSetException If called on a live or compiled scene
     *  graph without the necessary capabilities set.
     */
    public static Enumeration findNodes(Node root, String nodeClassName)
    throws ClassNotFoundException, CapabilityNotSetException
    {
        // Since this method makes use of static member variables make sure only
        // one thread can be performing a search at any one time.
        synchronized(sync)
        {
            // Generate the class from the class name
            nodeClass  = Class.forName(nodeClassName);

            // Reset the data structures
            visitedSharedGroups.clear();
            foundNodes.clear();

            // Find the nodes
            findNodes(root);

            // Return the result
            return foundNodes.elements();
        }

    } // findNodes



    /** Find all the nodes of the type specified that are contained in the
    *  scene graph starting at root.
    *
    *  @param root The root node of the subgraph to be searched.
    *
    *  @throws CapabilityNotSetException if called on a live or compiled scene
    *  graph without the necessary capabilities set.
    */
    private static void findNodes(Node root) throws CapabilityNotSetException
    {
        // If this is a null tree then return
        if (root == null)
            return;

        // If the current root node is of the required type then add it to the
        // list of found nodes.
        if (nodeClass.isInstance(root))
            foundNodes.add(root);

        // If root is a shared group then make sure it hasn't been visited already
        if (root instanceof SharedGroup)
        {
            // If it has been visited previously then return
            if (visitedSharedGroups.contains(root))
                return;

            // If not then add it to the list of visited shared groups
            else
                visitedSharedGroups.add(root);
        }

        // If root is a group then traverse all of its children
        if (root instanceof Group)
        {
            Enumeration childEnum = ((Group)root).getAllChildren();
            while(childEnum.hasMoreElements())
                findNodes((Node)childEnum.nextElement());
        }

        // If root is a link then traverse its shared group
        else if (root instanceof Link)
            findNodes(((Link)root).getSharedGroup());

    } // findNodes

} // NodeFinder