swingx-master/swingx-core/src/main/java/org/jdesktop/swingx/tree/TreeUtilities.java
package org.jdesktop.swingx.tree;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.Vector;
import java.util.logging.Logger;

import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

/**
 * Contains convenience classes/methods for handling hierarchical Swing structures.
 * 
 * @author Jeanette Winzenburg, Berlin
 */
@SuppressWarnings({ "unchecked", "rawtypes" })
public class TreeUtilities {

    /**
     * An enumeration that is always empty. 
     */
    public static final Enumeration EMPTY_ENUMERATION
        = new Enumeration() {
            @Override
            public boolean hasMoreElements() { return false; }
            @Override
            public Object nextElement() {
                throw new NoSuchElementException("No more elements");
            }
    };

    /**
     * Implementation of a preorder traversal of a TreeModel.
     */
    public static class PreorderModelEnumeration implements Enumeration {
        protected Deque<Enumeration> stack;
        protected TreeModel model;
        // the last component is the current subtree to travers
        private TreePath path;
        
        /**
         * Instantiates a preorder traversal starting from the root of the 
         * TreeModel.
         * 
         * @param model the TreeModel to travers.
         */
        public PreorderModelEnumeration(TreeModel model) {
            this(model, model.getRoot());
        }
        
        /**
         * Instantiates a preorder traversal of the TreeModel which
         * starts at the given node. It iterates over all nodes of the
         * subtree, only.
         * 
         * @param model the TreeModel to travers.
         * @param node the node to start
         */
        public PreorderModelEnumeration(TreeModel model, Object node) {
            this.model = model;
            stack = new ArrayDeque<Enumeration>();
            pushNodeAsEnumeration(node);
        }

        /**
         * Instantiates a preorder traversal of the TreeModel which starts at the
         * last path component of the given TreePath. It iterates over all nodes
         * of the subtree and all of its siblings, with the same end as a traversal
         * starting at the model's roolt would have.
         * 
         * @param model the TreeModel to travers.
         * @param path the TreePath to start from
         */
        public PreorderModelEnumeration(TreeModel model, TreePath path) {
            this(model, path.getLastPathComponent());
            this.path = path;
        }
        
        @Override
        public boolean hasMoreElements() {
            return (!stack.isEmpty() && stack.peek().hasMoreElements());
        }

        @Override
        public Object nextElement() {
            Enumeration enumer = stack.peek();
            Object  node = enumer.nextElement();
            Enumeration children = children(model, node);

            if (!enumer.hasMoreElements()) {
                stack.pop();
            }
            if (children.hasMoreElements()) {
                stack.push(children);
            }
            if (!hasMoreElements()) {
                // check if there are more subtrees to travers
                // and update internal state accordingly
                updateSubtree();
            }
            return node;
        }

        /**
         * 
         */
        private void updateSubtree() {
            if (path == null) return;
            TreePath parentPath = path.getParentPath();
            if (parentPath == null) {
                // root
                path = null;
                return;
            }
            Object parent = parentPath.getLastPathComponent();
            Object currentNode = path.getLastPathComponent();
            int currentIndex = model.getIndexOfChild(parent, currentNode);
            if (currentIndex +1 < model.getChildCount(parent)) {
                // use sibling
                Object child = model.getChild(parent, currentIndex + 1);
                path = parentPath.pathByAddingChild(child);
                pushNodeAsEnumeration(child);
            } else {
                path = parentPath;
                // up one level
                updateSubtree();
            }
        }

        private void pushNodeAsEnumeration(Object node) {
            // single element enum
            Vector v = new Vector(1);
            v.add(node);
            stack.push(v.elements()); //children(model));
        }
        

    }  // End of class PreorderEnumeration

    
    /**
     * Implementation of a breadthFirst traversal of a subtree in a TreeModel.
     */
    public static class BreadthFirstModelEnumeration implements Enumeration {
        protected Queue<Enumeration> queue;
        private TreeModel model;
        
        public BreadthFirstModelEnumeration(TreeModel model) {
            this(model, model.getRoot());
        }
        
        public BreadthFirstModelEnumeration(TreeModel model, Object node) {
            this.model = model;
            // Vector is just used for getting an Enumeration easily
            Vector v = new Vector(1);
            v.addElement(node);    
            queue = new ArrayDeque<Enumeration>();
            queue.offer(v.elements());
        }
        
        @Override
        public boolean hasMoreElements() {
            return !queue.isEmpty() &&
                    queue.peek().hasMoreElements();
        }
        
        @Override
        public Object nextElement() {
            // look at head
            Enumeration enumer = queue.peek();
            Object node = enumer.nextElement();
            Enumeration children = children(model, node);
            
            if (!enumer.hasMoreElements()) {
                // remove head
                queue.poll();
            }
            if (children.hasMoreElements()) {
                // add at tail
                queue.offer(children);
            }
            return node;
        }
        
    }  // End of class BreadthFirstEnumeration
    
    
    /**
     * Implementation of a postorder traversal of a subtree in a TreeModel.
     */
    public static class PostorderModelEnumeration implements Enumeration {
        protected TreeModel model;
        protected Object root;
        protected Enumeration children;
        protected Enumeration subtree;
        
        public PostorderModelEnumeration(TreeModel model) {
            this(model, model.getRoot());
        }
        
        public PostorderModelEnumeration(TreeModel model, Object node) {
            this.model = model;
            root = node;
            children = children(model, root);
            subtree = EMPTY_ENUMERATION;
        }
        
        @Override
        public boolean hasMoreElements() {
            return root != null;
        }
        
        @Override
        public Object nextElement() {
            Object retval;
            
            if (subtree.hasMoreElements()) {
                retval = subtree.nextElement();
            } else if (children.hasMoreElements()) {
                subtree = new PostorderModelEnumeration(model,
                        children.nextElement());
                retval = subtree.nextElement();
            } else {
                retval = root;
                root = null;
            }
            
            return retval;
        }
        
    }  // End of class PostorderEnumeration
    
    /**
     * Implementation of a preorder traversal of a subtree with nodes of type TreeNode.
     */
    public static class PreorderNodeEnumeration<M extends TreeNode> implements Enumeration<M> {
        protected Deque<Enumeration<M>> stack;

        public PreorderNodeEnumeration(M rootNode) {
            // Vector is just used for getting an Enumeration easily
            Vector<M> v = new Vector<M>(1);
            v.addElement(rootNode);     
            stack = new ArrayDeque<Enumeration<M>>();
            stack.push(v.elements());
        }

        @Override
        public boolean hasMoreElements() {
            return (!stack.isEmpty() &&
                    stack.peek().hasMoreElements());
        }

        @Override
        public M nextElement() {
            Enumeration<M> enumer = stack.peek();
            M node = enumer.nextElement();
            Enumeration<M> children = getChildren(node);

            if (!enumer.hasMoreElements()) {
                stack.pop();
            }
            if (children.hasMoreElements()) {
                stack.push(children);
            }
            return node;
        }

        protected Enumeration<M> getChildren(M node) {
            Enumeration<M> children = node.children();
            return children;
        }

    }  // End of class PreorderEnumeration

    /**
     * Implementation of a postorder traversal of a subtree with nodes of type TreeNode.
     */
    public static class PostorderNodeEnumeration<M extends TreeNode> implements Enumeration<M> {
        protected M root;
        protected Enumeration<M> children;
        protected Enumeration<M> subtree;

        public PostorderNodeEnumeration(M rootNode) {
            super();
            root = rootNode;
            children = getChildren(rootNode);
            subtree = EMPTY_ENUMERATION;
        }
        @Override
        public boolean hasMoreElements() {
            return root != null;
        }

        @Override
        public M nextElement() {
            M retval;

            if (subtree.hasMoreElements()) {
                retval = subtree.nextElement();
            } else if (children.hasMoreElements()) {
                subtree = createSubTree(children.nextElement());
                retval = subtree.nextElement();
            } else {
                retval = root;
                root = null;
            }

            return retval;
        }

        /**
         * Creates and returns a PostorderEnumeration on the given node.
         * 
         * @param node the node to create the PostorderEnumeration for
         * @return the PostorderEnumeration on the given node
         */
        protected PostorderNodeEnumeration<M> createSubTree(M node) {
            return new PostorderNodeEnumeration<M>(node);
        }
        
        /**
         * Returns an enumeration on the children of the root node.
         * @param node
         * @return
         */
        protected Enumeration<M> getChildren(M node) {
            return node.children();
        }
        

    }  // End of class PostorderEnumeration


    /**
     * Implementation of a breadthFirst traversal of a subtree with nodes of type TreeNode.
     */
    public static class BreadthFirstNodeEnumeration<M extends TreeNode> implements Enumeration<M> {
        protected Queue<Enumeration<M>> queue;
        
        public BreadthFirstNodeEnumeration(M rootNode) {
            // Vector is just used for getting an Enumeration easily
            Vector<M> v = new Vector<M>(1);
            v.addElement(rootNode);    
            queue = new ArrayDeque<Enumeration<M>>();
            queue.offer(v.elements());
        }
        
        @Override
        public boolean hasMoreElements() {
            return !queue.isEmpty() &&
                    queue.peek().hasMoreElements();
        }
        
        @Override
        public M nextElement() {
            // look at head
            Enumeration<M> enumer = queue.peek();
            M node = enumer.nextElement();
            Enumeration<M> children = getChildren(node);
            
            if (!enumer.hasMoreElements()) {
                // remove head
                queue.poll();
            }
            if (children.hasMoreElements()) {
                // add at tail
                queue.offer(children);
            }
            return node;
        }

        protected Enumeration<M> getChildren(M node) {
            Enumeration<M> children = node.children();
            return children;
        }
        
        
    }  // End of class BreadthFirstEnumeration

    /**
     * Creates and returns an Enumeration across the direct children of the 
     * rootNode in the given TreeModel. 
     * 
     * @param model the TreeModel which contains parent, must not be null
     * @return an Enumeration across the direct children of the model's root, the enumeration
     *    is empty if the root is null or contains no children
     */
    public static Enumeration children(TreeModel model) {
        return children(model, model.getRoot());
    }
    
    /**
     * Creates and returns an Enumeration across the direct children of parentNode
     * in the given TreeModel. 
     * 
     * @param model the TreeModel which contains parent, must not be null
     * @param parent the parent of the enumerated children
     * @return an Enumeration across the direct children of parent, the enumeration
     *    is empty if the parent is null or contains no children
     */
    public static Enumeration children(final TreeModel model, final Object parent) {
        if (parent == null || model.isLeaf(parent)) {
            return EMPTY_ENUMERATION;
        }
        Enumeration<?> e = new Enumeration() {

            int currentIndex = 0;
            @Override
            public boolean hasMoreElements() {
                return model.getChildCount(parent) > currentIndex;
            }

            @Override
            public Object nextElement() {
                return model.getChild(parent, currentIndex++);
            }
            
        };
        return e;
    }
    
    private TreeUtilities() {}
    
    @SuppressWarnings("unused")
    private static final Logger LOG = Logger.getLogger(TreeUtilities.class
            .getName());
}
