/**
 * From Java Examples in a Nutshell, Chapter 19
 */
package org.autoplot.spase;

import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.*;                // Core DOM classes
import org.w3c.dom.traversal.*;      // TreeWalker and related DOM classes
import org.xml.sax.*;                // Xerces DOM parser uses some SAX classes
import javax.swing.*;                // Swing classes 
import javax.swing.tree.*;           // TreeModel and related classes
import javax.swing.event.*;          // Tree-related event classes
import java.io.*;                    // For reading the input XML file
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;


/**
 * This class implements the Swing TreeModel interface so that the DOM tree
 * returned by a TreeWalker can be displayed in a JTree component.
 **/
public class DOMTreeWalkerTreeModel implements TreeModel {
    TreeWalker walker;  // The TreeWalker we're modeling for JTree
    
    /** Create a TreeModel for the specified TreeWalker */
    public DOMTreeWalkerTreeModel(TreeWalker walker) { this.walker = walker; }

    /** 
     * Create a TreeModel for a TreeWalker that returns all nodes
     * in the specified document
     **/
    public DOMTreeWalkerTreeModel(Document document) {
        DocumentTraversal dt = (DocumentTraversal)document;
        walker = dt.createTreeWalker(document, NodeFilter.SHOW_ALL,null,false);
    }

    /** 
     * Create a TreeModel for a TreeWalker that returns the specified 
     * element and all of its descendant nodes.
     **/
    public DOMTreeWalkerTreeModel(Element element) {
        DocumentTraversal dt = (DocumentTraversal)element.getOwnerDocument();
        walker = dt.createTreeWalker(element, NodeFilter.SHOW_ALL, null,false);
    }

    // Return the root of the tree 
    public Object getRoot() { return new TreeNode( walker.getRoot() ); }
    
    // Is this node a leaf? (Leaf nodes are displayed differently by JTree)
    public boolean isLeaf(Object node) {
        return ((TreeNode)node).isLeaf();
    }
    
    // How many children does this node have?
    public int getChildCount(Object node) {
        walker.setCurrentNode(((TreeNode)node).getDomNode());   // Set the current node
        // TreeWalker doesn't count children for us, so we count ourselves
        int numkids = 0;
        Node child = walker.firstChild();    // Start with the first child
        while(child != null) {               // Loop 'till there are no more
            numkids++;                       // Update the count
            child = walker.nextSibling();    // Get next child
        }
        return numkids;                      // This is the number of children
    }
    
    // Return the specified child of a parent node.
    public Object getChild(Object parent, int index) {
        walker.setCurrentNode(((TreeNode)parent).getDomNode());  // Set the current node
        // TreeWalker provides sequential access to children, not random
        // access, so we've got to loop through the kids one by one
        Node child = walker.firstChild();
        while(index-- > 0) child = walker.nextSibling();
        return new TreeNode(child);
    }
    
    // Return the index of the child node in the parent node
    public int getIndexOfChild(Object parent, Object child) {
        walker.setCurrentNode(((TreeNode)parent).getDomNode());    // Set current node
        int index = 0;
        Node c = walker.firstChild();           // Start with first child
        while((c != child) && (c != null)) {    // Loop 'till we find a match
            index++;
            c = walker.nextSibling();           // Get the next child
        }
        return index;                           // Return matching position
    }
    
    // Only required for editable trees; unimplemented here.
    public void valueForPathChanged(TreePath path, Object newvalue) {}
    
    // This TreeModel never fires any events (since it is not editable)
    // so event listener registration methods are left unimplemented
    public void addTreeModelListener(TreeModelListener l) {}
    public void removeTreeModelListener(TreeModelListener l) {}

    /**
     * This main() method demonstrates the use of this class, the use of the
     * Xerces DOM parser, and the creation of a DOM Level 2 TreeWalker object.
     **/
    public static void main(String[] args) throws IOException, SAXException, ParserConfigurationException {

        DocumentBuilder builder;
        builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        InputSource source = new InputSource(new FileReader(args[0]));
        Document document = builder.parse(source);
            
        // If we're using a DOM Level 2 implementation, then our Document
        // object ought to implement DocumentTraversal 
        DocumentTraversal traversal = (DocumentTraversal)document;

        // For this demonstration, we create a NodeFilter that filters out
        // Text nodes containing only space; these just clutter up the tree
        NodeFilter filter = new NodeFilter() {
                public short acceptNode(Node n) {
                    if (n.getNodeType() == Node.TEXT_NODE) { 
                        // Use trim() to strip off leading and trailing space.
                        // If nothing is left, then reject the node
                        if (((Text)n).getData().trim().length() == 0)
                            return NodeFilter.FILTER_REJECT;
                    }
                    return NodeFilter.FILTER_ACCEPT;
                }
            };

        // This set of flags says to "show" all node types except comments
        int whatToShow = NodeFilter.SHOW_ALL & ~NodeFilter.SHOW_COMMENT;

        // Create a TreeWalker using the filter and the flags
        TreeWalker walker = traversal.createTreeWalker(document, whatToShow,
                                                       filter, false);

        // Instantiate a TreeModel and a JTree to display it
        JTree tree = new JTree(new DOMTreeWalkerTreeModel(walker));
        
        // Create a frame and a scrollpane to display the tree, and pop them up
        JFrame frame = new JFrame("DOMTreeWalkerTreeModel Demo");
        frame.getContentPane().add(new JScrollPane(tree));
        frame.setSize(500, 250);
        frame.setVisible(true);
    }
}