/*
 * $RCSfile: Node.java,v $
 *
 * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
 *
 * Use is subject to license terms.
 *
 * $Revision: 1.6 $
 * $Date: 2007/02/09 17:18:11 $
 * $State: Exp $
 */

package javax.media.j3d;

import java.util.Hashtable;
import java.util.Enumeration;
import java.lang.reflect.Constructor;

/**
 * The Node class provides an abstract class for all Group and Leaf Nodes.
 * It provides a common framework for constructing a Java 3D scene graph,
 * specifically bounding volumes.
 *
 * <p>
 * For more information, see the
 * <a href="doc-files/intro.html">Introduction to the Java 3D API</a>.
 *
 * <p>
 * NOTE: Applications should <i>not</i> extend this class directly.
 */
public abstract class Node extends SceneGraphObject {

    /**
     * Specifies that this Node will be reported in the pick 
     * SceneGraphPath if a pick occurs.  This capability is only 
     * specifiable for Group nodes; it is ignored for leaf nodes.
     * The default for Group nodes is false.  All interior nodes not 
     * needed for uniqueness in a SceneGraphPath that don't have 
     * ENABLE_PICK_REPORTING set to true will not be reported in the 
     * SceneGraphPath.
     * @see SceneGraphPath
     */
    public static final int
    ENABLE_PICK_REPORTING = CapabilityBits.NODE_ENABLE_PICK_REPORTING;

    /**
     * Specifies that this Node will be reported in the collision 
     * SceneGraphPath if a collision occurs.  This capability is only
     * specifiable for Group nodes; it is ignored for leaf nodes.
     * The default for Group nodes is false.  All interior nodes not 
     * needed for uniqueness in a SceneGraphPath that don't have
     * ENABLE_COLLISION_REPORTING set to true will not be reported
     * in the SceneGraphPath.
     * @see SceneGraphPath
     */  
    public static final int
    ENABLE_COLLISION_REPORTING = CapabilityBits.NODE_ENABLE_COLLISION_REPORTING;

    /**
     * Specifies that this Node allows read access to its bounds
     * information.
     */
    public static final int
    ALLOW_BOUNDS_READ = CapabilityBits.NODE_ALLOW_BOUNDS_READ;

    /**
     * Specifies that this Node allows write access to its bounds
     * information.
     */
    public static final int
    ALLOW_BOUNDS_WRITE = CapabilityBits.NODE_ALLOW_BOUNDS_WRITE;

    /**
     * Specifies that this Node allows reading its pickability state.
     */
    public static final int
    ALLOW_PICKABLE_READ = CapabilityBits.NODE_ALLOW_PICKABLE_READ;

    /**
     * Specifies that this Node allows write access its pickability state.
     */
    public static final int
    ALLOW_PICKABLE_WRITE = CapabilityBits.NODE_ALLOW_PICKABLE_WRITE;

    /**
     * Specifies that this Node allows reading its collidability state.
     */
    public static final int
    ALLOW_COLLIDABLE_READ = CapabilityBits.NODE_ALLOW_COLLIDABLE_READ;

    /**
     * Specifies that this Node allows write access its collidability state.
     */
    public static final int
    ALLOW_COLLIDABLE_WRITE = CapabilityBits.NODE_ALLOW_COLLIDABLE_WRITE;

    /**
     * Specifies that this Node allows read access to its bounds
     * auto compute information.
     */
    public static final int
    ALLOW_AUTO_COMPUTE_BOUNDS_READ = CapabilityBits.NODE_ALLOW_AUTO_COMPUTE_BOUNDS_READ;

    /**
     * Specifies that this Node allows write access to its bounds
     * auto compute information.
     */
    public static final int
    ALLOW_AUTO_COMPUTE_BOUNDS_WRITE = CapabilityBits.NODE_ALLOW_AUTO_COMPUTE_BOUNDS_WRITE;

    /**
     * Specifies that this Node allows read access to its local
     * coordinates to virtual world (Vworld) coordinates transform.
     */
    public static final int
    ALLOW_LOCAL_TO_VWORLD_READ = CapabilityBits.NODE_ALLOW_LOCAL_TO_VWORLD_READ;

    /**
     * Specifies that this Node allows read access to its parent Group node.
     *
     * @since Java 3D 1.4
     */
    public static final int
	ALLOW_PARENT_READ = CapabilityBits.NODE_ALLOW_PARENT_READ;

    /**
     * Specifies that this Node allows read access to its Locale.
     *
     * @since Java 3D 1.4
     */
    public static final int
        ALLOW_LOCALE_READ = CapabilityBits.NODE_ALLOW_LOCALE_READ;

   // Array for setting default read capabilities
    private static final int[] readCapabilities = {
        ALLOW_BOUNDS_READ,
        ALLOW_PICKABLE_READ,
        ALLOW_COLLIDABLE_READ,
        ALLOW_AUTO_COMPUTE_BOUNDS_READ,
        ALLOW_LOCAL_TO_VWORLD_READ,
        ALLOW_PARENT_READ,
        ALLOW_LOCALE_READ
    };

    // for checking for cycles
    private boolean visited = false;


    /**
     * Constructs a Node object with default parameters.  The default
     * values are as follows:
     * <ul>
     * pickable : true<br>
     * collidable : true<br>
     * bounds auto compute : true<br>
     * bounds : N/A (automatically computed)<br>
     * </ul>
     */
    public Node() {
        // set default read capabilities
        setDefaultReadCapabilities(readCapabilities);
    }

    /**

     * @return the parent of this node, or null if this node has no parent
     * @exception CapabilityNotSetException if appropriate capability is
     * not set and this object is part of live or compiled scene graph
     */
    public Node getParent() {
	if (isLiveOrCompiled()) {
	    if(!this.getCapability(ALLOW_PARENT_READ)) {
		throw new CapabilityNotSetException(J3dI18N.getString("Node0"));
	    }
	}

	NodeRetained nr = ((NodeRetained)this.retained).getParent();
	return (nr == null ? null :  (Node) nr.getSource());
    }

    /**
     * Sets the geometric bounds of a node.
     * @param bounds the bounding object for a node
     * @exception CapabilityNotSetException if appropriate capability is
     * not set and this object is part of live or compiled scene graph
     */
    public void setBounds(Bounds bounds) {
	if (isLiveOrCompiled())
	    if(!this.getCapability(ALLOW_BOUNDS_WRITE))
		throw new CapabilityNotSetException(J3dI18N.getString("Node1"));

	((NodeRetained)this.retained).setBounds(bounds);
    }

    /**
     * Returns the bounding object of a node.
     * @return the node's bounding object
     * @exception CapabilityNotSetException if appropriate capability is
     * not set and this object is part of live or compiled scene graph
     * @exception SceneGraphCycleException if there is a cycle in the
     * scene graph
     */
    public Bounds getBounds() {
	
	if (isLiveOrCompiled()) {
	    if(!this.getCapability(ALLOW_BOUNDS_READ)) {
		throw new CapabilityNotSetException(J3dI18N.getString("Node2"));
	    }
	}
	else {
	    // this will throw a SceneGraphCycleException if there is
	    // a cycle
	    checkForCycle();
	}
		
	return ((NodeRetained)this.retained).getBounds();
    }

   /**
     * Returns the collidable value; this value determines whether this node
     * and it's children, if a group node, can be considered for collision
     * purposes; if it is set to false, then neither this node nor any 
     * children nodes will be traversed for collision purposes; the default
     * value is true.   The collidable setting is the way that an 
     * application can perform collision culling.
     *  @return the present collidable value for this node
     */ 
    public boolean getCollidable() {
        if (isLiveOrCompiled())
            if(!this.getCapability(ALLOW_COLLIDABLE_READ))
                throw new CapabilityNotSetException(J3dI18N.getString("Node16"));               

        return ((NodeRetained)retained).getCollidable();
    }

    /**
     * Sets the collidable value; determines whether this node and any of its 
     * children, if a group node, can be considered for collision purposes.
     * @param collidable  the new collidable value for this node
     */  
    public void setCollidable( boolean collidable ) {
        if (isLiveOrCompiled())
            if(!this.getCapability(ALLOW_COLLIDABLE_WRITE))
                throw new CapabilityNotSetException(J3dI18N.getString("Node4"));
	
        ((NodeRetained)retained).setCollidable(collidable);
    }

    /**
     * Turns the automatic calcuation of geometric bounds of a node on/off.
     * @param autoCompute indicates if the node's bounding object is
     * automatically computed.
     * @exception CapabilityNotSetException if appropriate capability is
     * not set and this object is part of live or compiled scene graph
     */
    public void setBoundsAutoCompute(boolean autoCompute) {
	if (isLiveOrCompiled())
	    if(!this.getCapability(ALLOW_AUTO_COMPUTE_BOUNDS_WRITE))
		throw new CapabilityNotSetException(J3dI18N.getString("Node5"));

	((NodeRetained)this.retained).setBoundsAutoCompute(autoCompute);
    }

    /**
     * Gets the value indicating if the automatic calcuation of geometric bounds of a node is on/off.
     * @return the node's auto compute flag for the geometric bounding object
     * @exception CapabilityNotSetException if appropriate capability is
     * not set and this object is part of live or compiled scene graph
     */
    public boolean getBoundsAutoCompute() {
	if (isLiveOrCompiled())
	    if(!this.getCapability(ALLOW_AUTO_COMPUTE_BOUNDS_READ))
		throw new CapabilityNotSetException(J3dI18N.getString("Node6"));

	return ((NodeRetained)this.retained).getBoundsAutoCompute();
    }

    /**
     * Retrieves the local coordinates to virtual world coordinates
     * transform for this node in the scene graph.  This is the composite
     * of all transforms in the scene graph from the root down to
     * <code>this</code> node.  It is only valid
     * for nodes that are part of a live scene graph.
     * If the node is not part of a live scene graph then the coordinates are
     * calculated as if the graph was attached at the origin of a locale.
     * @param t the object that will receive the local coordinates to
     * Vworld coordinates transform.
     * @exception RestrictedAccessException if the node is compiled but not 
     * part of a live scene graph
     * @exception CapabilityNotSetException if appropriate capability is
     * not set and this node is part of live or compiled scene graph
     * @exception IllegalSharingException if the node is a descendant
     * of a SharedGroup node.
     */
    public void getLocalToVworld(Transform3D t) {
        if (isLiveOrCompiled()) {
            if(!this.getCapability(ALLOW_LOCAL_TO_VWORLD_READ))
                    throw new CapabilityNotSetException(J3dI18N.getString("Node8"));
        }
        
	if (!isLive()) {
            // TODO Support compiled graphs
            if (isCompiled())
                throw new RestrictedAccessException(J3dI18N.getString("Node7"));
            
            // In 1.4 we support getLocalToVworld for non live nodes
            ((NodeRetained)this.retained).computeNonLiveLocalToVworld(t, this);
	    //throw new RestrictedAccessException(J3dI18N.getString("Node7"));
        } else {
            ((NodeRetained)this.retained).getLocalToVworld(t);
        }
    }
    

    /**
     * Retrieves the local coordinates to virtual world coordinates
     * transform for the particular path in the scene graph ending with
     * this node.  This is the composite
     * of all transforms in the scene graph from the root down to
     * <code>this</code> node via the specified Link nodes.  It is
     * only valid for nodes that are part of a live scene graph.
     * @param path the specific path from the node to the Locale
     * @param t the object that will receive the local coordinates to
     * Vworld coordinates transform.
     * @exception RestrictedAccessException if the node is <em>not</em>
     * part of a live scene graph
     * @exception CapabilityNotSetException if appropriate capability is
     * not set and this node is part of live scene graph
     * @exception IllegalArgumentException if the specified path does
     * not contain a valid Locale, or if the last node in the path is
     * different from this node
     * @exception IllegalSharingException if the node is not a descendant
     * of a SharedGroup node.
     */
    public void getLocalToVworld(SceneGraphPath path, Transform3D t) {
	if (!isLive()) {
	    throw new RestrictedAccessException(J3dI18N.getString("Node7"));
        } 
        
        if(!this.getCapability(ALLOW_LOCAL_TO_VWORLD_READ))
            throw new CapabilityNotSetException(J3dI18N.getString("Node8"));

        ((NodeRetained)this.retained).getLocalToVworld(path,t);
        
    }

    /**
     * Retrieves the locale to which this node is attached. If the
     * node is not part of a live scene graph, null is returned.
     *
     * @return the locale to which this node is attached.
     *
     * @exception CapabilityNotSetException if appropriate capability is
     * not set and this node is part of live scene graph
     * @exception IllegalSharingException if the node is a descendant
     * of a SharedGroup node.
     *
     * @since Java 3D 1.4
     */
    public Locale getLocale() {
	if (!isLive()) {
	    return null;
	}

	if(!this.getCapability(ALLOW_LOCALE_READ)) {
	    throw new CapabilityNotSetException(J3dI18N.getString("Node17"));
	}

	return ((NodeRetained)this.retained).getLocale();
    }

    /**
     * Duplicates all the nodes of the specified sub-graph.  For Group Nodes
     * the group node is duplicated via a call to <code>cloneNode</code>
     * and then <code>cloneTree</code>
     * is called for each child node.  For Leaf Nodes, component
     * data can either be duplicated or be made a reference to the original
     * data.  Leaf Node cloneTree behavior is determined by the
     * <code>duplicateOnCloneTree</code> flag found in every Leaf Node's
     * component data class and by the <code>forceDuplicate</code> paramter.
     * @return a reference to the cloned sub-graph.
     * @exception DanglingReferenceException When a dangling reference is
     *  discovered during the cloneTree operation.
     * @exception RestrictedAccessException if this object is part of live
     * or compiled scene graph
     * @exception SceneGraphCycleException if there is a cycle in the
     * scene graph
     * @see NodeComponent#setDuplicateOnCloneTree
     */
    public Node cloneTree() {
	return cloneTree(new NodeReferenceTable(), false, false);
    }

    /**
     * Duplicates all the nodes of the specified sub-graph.  For Group Nodes
     * the group node is duplicated via a call to <code>cloneNode</code>
     * and then <code>cloneTree</code> is called for each child node.
     * For Leaf Nodes, component
     * data can either be duplicated or be made a reference to the original
     * data.  Leaf Node cloneTree behavior is determined by the
     * <code>duplicateOnCloneTree</code> flag found in every Leaf Node's
     * component data class and by the <code>forceDuplicate</code> paramter.
     * @param forceDuplicate when set to <code>true</code>, causes the
     *  <code>duplicateOnCloneTree</code> flag to be ignored.  When
     *  <code>false</code>, the value of each node's
     *  <code>duplicateOnCloneTree</code> determines whether data is
     *  duplicated or copied.
     * @return a reference to the cloned scene graph.
     * @exception DanglingReferenceException When a dangling reference is
     *  discovered during the cloneTree operation.
     * @exception RestrictedAccessException if this object is part of live
     * or compiled scene graph
     * @exception SceneGraphCycleException if there is a cycle in the
     * scene graph
     * @see NodeComponent#setDuplicateOnCloneTree
     */
    public Node cloneTree(boolean forceDuplicate) {
	return cloneTree(new NodeReferenceTable(), forceDuplicate, false);
    }

    /**
     * Duplicates all the nodes of the specified sub-graph.  For Group Nodes
     * the group node is duplicated via a call to <code>cloneNode</code> and
     * then <code>cloneTree</code> is called for each child node.  For
     * Leaf Nodes, component
     * data can either be duplicated or be made a reference to the original
     * data.  Leaf Node cloneTree behavior is determined by the
     * <code>duplicateOnCloneTree</code> flag found in every Leaf Node's
     * component data class and by the <code>forceDuplicate</code> paramter.
     *
     * @param forceDuplicate when set to <code>true</code>, causes the
     *  <code>duplicateOnCloneTree</code>
     *  flag to be ignored.  When <code>false</code>, the value of each node's
     *  <code>duplicateOnCloneTree</code> determines whether data is
     *  duplicated or copied.
     *
     * @param allowDanglingReferences when set to <code>true</code> allows
     *  the <code>cloneTree</code>
     *  method to complete even whan a dangling reference is discovered. When
     *  this parameter is <code>false</code> a
     *  <code>DanglingReferenceException</code> is generated as
     *  soon as cloneTree detects this situation.
     *
     * @return a reference to the cloned scene graph.
     *
     * @exception DanglingReferenceException When a dangling reference is
     *  discovered during the cloneTree operation and the 
     *  <code>allowDanglingReference</code> parameter is </code>false</code>.
     * @exception RestrictedAccessException if this object is part of live
     * or compiled scene graph
     * @exception SceneGraphCycleException if there is a cycle in the
     * scene graph
     *
     * @see NodeComponent#setDuplicateOnCloneTree
     */
    public Node cloneTree(boolean forceDuplicate,
			  boolean allowDanglingReferences) {
       return cloneTree(new NodeReferenceTable(), 
			forceDuplicate, allowDanglingReferences); 
    }


    /**
     * Duplicates all the nodes of the specified sub-graph.  For Group Nodes
     * the group node is duplicated via a call to <code>cloneNode</code>
     * and then <code>cloneTree</code>
     * is called for each child node.  For Leaf Nodes, component
     * data can either be duplicated or be made a reference to the original
     * data.  Leaf Node cloneTree behavior is determined by the
     * <code>duplicateOnCloneTree</code> flag found in every Leaf Node's
     * component data class and by the <code>forceDuplicate</code> paramter.
     * @param referenceTable table that stores the mapping between
     *	original and cloned nodes. All previous values in the
     *	referenceTable will be cleared before the clone is made.
     * @return a reference to the cloned sub-graph.
     * @exception DanglingReferenceException When a dangling reference is
     *  discovered during the cloneTree operation.
     * @exception RestrictedAccessException if this object is part of live
     * or compiled scene graph
     * @exception SceneGraphCycleException if there is a cycle in the
     * scene graph
     * @see NodeComponent#setDuplicateOnCloneTree
     * @since Java 3D 1.2
     */
    public Node cloneTree(NodeReferenceTable referenceTable) {
	return cloneTree(referenceTable, false, false);
    }


    /**
     * Duplicates all the nodes of the specified sub-graph.  For Group Nodes
     * the group node is duplicated via a call to <code>cloneNode</code>
     * and then <code>cloneTree</code> is called for each child node.
     * For Leaf Nodes, component
     * data can either be duplicated or be made a reference to the original
     * data.  Leaf Node cloneTree behavior is determined by the
     * <code>duplicateOnCloneTree</code> flag found in every Leaf Node's
     * component data class and by the <code>forceDuplicate</code> paramter.
     * @param referenceTable table that stores the mapping between
     *	original and cloned nodes. All previous values in the
     *	referenceTable will be cleared before the clone is made.
     * @param forceDuplicate when set to <code>true</code>, causes the
     *  <code>duplicateOnCloneTree</code> flag to be ignored.  When
     *  <code>false</code>, the value of each node's
     *  <code>duplicateOnCloneTree</code> determines whether data is
     *  duplicated or copied.
     * @return a reference to the cloned scene graph.
     * @exception DanglingReferenceException When a dangling reference is
     *  discovered during the cloneTree operation.
     * @exception RestrictedAccessException if this object is part of live
     * or compiled scene graph
     * @exception SceneGraphCycleException if there is a cycle in the
     * scene graph
     * @see NodeComponent#setDuplicateOnCloneTree
     * @since Java 3D 1.2
     */
    public Node cloneTree(NodeReferenceTable referenceTable,
			  boolean forceDuplicate) {
       return cloneTree(referenceTable, forceDuplicate, false);
    }

    /**
     * Duplicates all the nodes of the specified sub-graph.  For Group Nodes
     * the group node is duplicated via a call to <code>cloneNode</code>
     * and then <code>cloneTree</code> is called for each child node.
     * For Leaf Nodes, component
     * data can either be duplicated or be made a reference to the original
     * data.  Leaf Node cloneTree behavior is determined by the
     * <code>duplicateOnCloneTree</code> flag found in every Leaf Node's
     * component data class and by the <code>forceDuplicate</code> paramter.
     * @param referenceTable table that stores the mapping between
     *	original and cloned nodes. All previous values in the
     *	referenceTable will be cleared before the clone is made.
     * @param forceDuplicate when set to <code>true</code>, causes the
     *  <code>duplicateOnCloneTree</code> flag to be ignored.  When
     *  <code>false</code>, the value of each node's
     *  <code>duplicateOnCloneTree</code> determines whether data is
     *  duplicated or copied.
     *
     * @param allowDanglingReferences when set to <code>true</code> allows
     *  the <code>cloneTree</code>
     *  method to complete even whan a dangling reference is discovered. When
     *  this parameter is <code>false</code> a
     *  <code>DanglingReferenceException</code> is generated as
     *  soon as cloneTree detects this situation.
     *
     * @return a reference to the cloned scene graph.
     * @exception DanglingReferenceException When a dangling reference is
     *  discovered during the cloneTree operation.
     * @exception RestrictedAccessException if this object is part of live
     * or compiled scene graph
     * @exception SceneGraphCycleException if there is a cycle in the
     * scene graph
     * @see NodeComponent#setDuplicateOnCloneTree
     * @since Java 3D 1.2
     */
    public Node cloneTree(NodeReferenceTable referenceTable,
			  boolean forceDuplicate,
			  boolean allowDanglingReferences) {

	if (!isLiveOrCompiled()) {
	    // this will throw a SceneGraphCycleException if there is
	    // a cycle
	    checkForCycle();
	}
	
        referenceTable.set(allowDanglingReferences, new Hashtable());
        Node n = cloneTree(forceDuplicate, referenceTable.objectHashtable);

        // go through hash table looking for Leaf nodes.
        // call updateNodeReferences for each.
        Enumeration e = referenceTable.objectHashtable.elements();

        while (e.hasMoreElements()) {
            SceneGraphObject o =  (SceneGraphObject) e.nextElement();
	    o.updateNodeReferences(referenceTable);
        }
        return n;
    }

   /**
     * Duplicates all the nodes of the specified sub-graph.  For Group Nodes
     * the group node is duplicated via a call to <code>cloneNode</code> and
     * then <code>cloneTree</code> is called for each child node.  For
     * Leaf Nodes, component
     * data can either be duplicated or be made a reference to the original
     * data.  Leaf Node cloneTree behavior is determined by the
     * <code>duplicateOnCloneTree</code> flag found in every Leaf Node's
     * component data class and by the <code>forceDuplicate</code> paramter.
     *
     * @param forceDuplicate when set to <code>true</code>, causes the
     *  <code>duplicateOnCloneTree</code>
     *  flag to be ignored.  When <code>false</code>, the value of each node's
     *  <code>duplicateOnCloneTree</code> determines whether data is
     *  duplicated or copied.
     *
     * @param nodeHashtable a hashtable used to map orignal node references to
     *  their cloned counterpart.
     *
     * @return a reference to the cloned scene graph.
     *
     * @see NodeComponent#setDuplicateOnCloneTree
     */
    Node cloneTree(boolean forceDuplicate, Hashtable nodeHashtable) {
        Node l;
	this.nodeHashtable = nodeHashtable;
	try {
	    l = cloneNode(forceDuplicate);
	} catch (RuntimeException e) {
	    this.nodeHashtable = null;
	    throw e;
	}
	// must reset to null so that we can tell whether the call is from user 
	// or cloneTree
        this.nodeHashtable = null;  
	nodeHashtable.put(this, l);
	return l;
    }


    /**
     * Used to create a new instance of the node.  This routine is called
     * by <code>cloneTree</code> to duplicate the current node. 
     * <code>cloneNode</code> should be overridden by any user subclassed
     * objects.  All subclasses must have their <code>cloneNode</code>
     * method consist of the following lines:
     * <P><blockquote><pre>
     *     public Node cloneNode(boolean forceDuplicate) {
     *         UserSubClass usc = new UserSubClass();
     *         usc.duplicateNode(this, forceDuplicate);
     *         return usc;
     *     }
     * </pre></blockquote>
     * NOTE: Applications should <i>not</i> call this method directly.
     * It should only be called by the cloneTree method.
     *
     * @param forceDuplicate when set to <code>true</code>, causes the
     *  <code>duplicateOnCloneTree</code> flag to be ignored.  When
     *  <code>false</code>, the value of each node's
     *  <code>duplicateOnCloneTree</code> variable determines whether
     *  NodeComponent data is duplicated or copied.
     *
     * @exception RestrictedAccessException if this object is part of live
     * or compiled scene graph
     *
     * @see Node#cloneTree
     * @see Node#duplicateNode
     * @see NodeComponent#setDuplicateOnCloneTree
     */
    public Node cloneNode(boolean forceDuplicate) {
        throw new RuntimeException(J3dI18N.getString("Node12"));
    }


    /**
     * Copies all node information from <code>originalNode</code> into
     * the current node.  This method is called from the
     * <code>cloneNode</code> method which is, in turn, called by the
     * <code>cloneTree</code> method.
     * <P>
     * For any <code>NodeComponent</code> objects
     * contained by the object being duplicated, each <code>NodeComponent</code>
     * object's <code>duplicateOnCloneTree</code> value is used to determine
     * whether the <code>NodeComponent</code> should be duplicated in the new node
     * or if just a reference to the current node should be placed in the
     * new node.  This flag can be overridden by setting the
     * <code>forceDuplicate</code> parameter in the <code>cloneTree</code>
     * method to <code>true</code>.
     *
     * <br>
     * NOTE: Applications should <i>not</i> call this method directly.
     * It should only be called by the cloneNode method.
     *
     * @param originalNode the original node to duplicate.
     * @param forceDuplicate when set to <code>true</code>, causes the
     *  <code>duplicateOnCloneTree</code> flag to be ignored.  When
     *  <code>false</code>, the value of each node's
     *  <code>duplicateOnCloneTree</code> variable determines whether
     *  NodeComponent data is duplicated or copied.
     *
     * @see Group#cloneNode
     * @see Node#duplicateNode
     * @see Node#cloneTree
     * @see NodeComponent#setDuplicateOnCloneTree
     */
    public void duplicateNode(Node originalNode,
			      boolean forceDuplicate) {
        duplicateAttributes(originalNode, forceDuplicate);
    }

   /**
     * Copies all node information from <code>originalNode</code> into
     * the current node.  This method is called from subclass of
     * <code>duplicateNode</code> method which is, in turn, called by the
     * <code>cloneNode</code> method.
     * <P>
     * For any <i>NodeComponent</i> objects
     * contained by the object being duplicated, each <i>NodeComponent</i>
     * object's <code>duplicateOnCloneTree</code> value is used to determine
     * whether the <i>NodeComponent<i> should be duplicated in the new node
     * or if just a reference to the current node should be placed in the
     * new node.  This flag can be overridden by setting the
     * <code>forceDuplicate</code> parameter in the <code>cloneTree</code>
     * method to <code>true</code>.
     *
     *
     * @param originalNode the original node to duplicate.
     * @param forceDuplicate when set to <code>true</code>, causes the
     *  <code>duplicateOnCloneTree</code> flag to be ignored.  When
     *  <code>false</code>, the value of each node's
     *  <code>duplicateOnCloneTree</code> variable determines whether
     *  NodeComponent data is duplicated or copied.
     *
     * @see Group#cloneNode
     * @see Node#duplicateNode
     * @see Node#cloneTree
     * @see NodeComponent#setDuplicateOnCloneTree
     */
    final void checkDuplicateNode(Node originalNode,
				  boolean forceDuplicate) {
        if (originalNode.nodeHashtable != null) {
	    duplicateAttributes(originalNode, forceDuplicate);
	} else {
	    //  user call cloneNode() or duplicateNode() directly 
	    // instead of via cloneTree() 
	    originalNode.nodeHashtable = new Hashtable();
	    duplicateAttributes(originalNode, forceDuplicate);
	    originalNode.nodeHashtable = null;
	}
    }


    /**
     * Copies all Node information from
     * <code>originalNode</code> into
     * the current node.  This method is called from the
     * <code>cloneNode</code> method which is, in turn, called by the
     * <code>cloneTree</code> method.<P> 
     *
     * @param originalNode the original node to duplicate.
     * @param forceDuplicate when set to <code>true</code>, causes the
     *  <code>duplicateOnCloneTree</code> flag to be ignored.  When
     *  <code>false</code>, the value of each node's
     *  <code>duplicateOnCloneTree</code> variable determines whether
     *  NodeComponent data is duplicated or copied.
     *
     * @exception RestrictedAccessException if originalNode object is part of a live
     *  or compiled scenegraph.
     *
     * @see Node#duplicateNode
     * @see Node#cloneTree
     * @see NodeComponent#setDuplicateOnCloneTree
     */
    void duplicateAttributes(Node originalNode, boolean forceDuplicate) {

	if (originalNode.isLiveOrCompiled()) {
	    throw new RestrictedAccessException(J3dI18N.getString("Node13"));
	}
        super.duplicateSceneGraphObject(originalNode);
	NodeRetained attr = (NodeRetained) originalNode.retained;
	NodeRetained rt = (NodeRetained) retained;

	rt.setPickable(attr.getPickable());
	rt.setCollidable(attr.getCollidable());
    }


    /**
     * When set to <code>true</code> this <code>Node</code> can be Picked.
     * Setting to false indicates that this node and it's children
     * are ALL unpickable.
     *
     * @param pickable Indicates if this node should be pickable or not
     */
    public void setPickable( boolean pickable ) {
	if (isLiveOrCompiled())
	    if(!this.getCapability(ALLOW_PICKABLE_WRITE))
		throw new CapabilityNotSetException(J3dI18N.getString("Node14"));

	((NodeRetained)retained).setPickable(pickable);
    }

    /**
     * Returns true if this <code>Node</code> is pickable,
     * false if it is not pickable.
     */
    public boolean getPickable() {
	if (isLiveOrCompiled())
	    if(!this.getCapability(ALLOW_PICKABLE_READ))
		throw new CapabilityNotSetException(J3dI18N.getString("Node3"));

	return ((NodeRetained)retained).getPickable();
    }

    /**
     * checks for cycles in the scene graph
     */
    void checkForCycle() {
	if (visited) {
	    throw new SceneGraphCycleException(J3dI18N.getString("Node15"));
	}
	visited = true;
	Node parent = getParent();
	if (parent != null) {
	    parent.checkForCycle();
	}
	visited = false;
    }
	
}
