org.apache.cocoon.forms.formmodel.tree.TreeWalker.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.cocoon.forms.formmodel.tree.TreeWalker.java

Source

/*
 * Copyright 1999-2005 The Apache Software Foundation.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.cocoon.forms.formmodel.tree;

import java.util.Collections;
import java.util.Iterator;

import org.apache.commons.collections.ArrayStack;

/**
 * A helper to crawl a tree and quickly access important node-related information.
 * <p>
 * It's an <code>Iterator</code> on the "current level" of the tree. This level starts
 * at the root node (and therefore obviously contains only one element), and can then
 * be changed to children of the current node using {@link #enterChildren()} or popped
 * back to the parent level using {@link #leave()}.
 * <p>
 * The {@link #next()} method will return the next node in the iteration,
 * and set the current node used by many convenience methods giving information about
 * that node.
 * <p>
 * This class was primarily written for page templates containing {@link Tree}s (see
 * <code>org/apache/cocoon/forms/generation/jx-macros.xml</code>) but can of course be
 * used in other places as well.
 * 
 * @version $Id: TreeWalker.java 190962 2005-06-16 17:17:00Z sylvain $
 */
public class TreeWalker implements Iterator {
    ArrayStack stack = new ArrayStack();
    Tree tree;
    Object node;
    TreePath path;
    Iterator iter;

    public TreeWalker(Tree tree) {
        // Root node has no siblings
        this.iter = Collections.EMPTY_LIST.iterator();
        this.tree = tree;
        this.node = tree.getModel().getRoot();
        this.path = TreePath.ROOT_PATH;

        stack.push(this.iter);
        stack.push(this.node);
    }

    /**
     * Starts iterating the children of the current node. The current iterator is pushed
     * on a stack and will be restored on {@link #leave()}.
     * <p>
     * Right after calling this method, there is no current node. Calling {@link #next()}
     * will move to the first child, if any.
     * 
     * @return the current tree walker (i.e. <code>this</code>).
     */
    public TreeWalker enterChildren() {
        Iterator newIter;
        if (isLeaf()) {
            newIter = Collections.EMPTY_LIST.iterator();
        } else {
            newIter = tree.getModel().getChildren(node).iterator();
        }
        this.stack.push(this.iter);
        this.stack.push(this.path);
        this.stack.push(this.node);
        this.iter = newIter;
        this.node = null;
        this.path = null;

        return this;
    }

    /**
     * Go back to the parent node, restoring the iterator at this node.
     */
    public void leave() {
        this.node = this.stack.pop();
        this.path = (TreePath) this.stack.pop();
        this.iter = (Iterator) this.stack.pop();
        this.path = this.path.getParentPath();
    }

    /**
     * Are there more nodes to iterate on at this level?
     */
    public boolean hasNext() {
        return this.iter.hasNext();
    }

    /**
     * Get the next node in the current iteration level.
     */
    public Object next() {
        this.node = iter.next();

        this.path = new TreePath((TreePath) this.stack.peek(1),
                tree.getModel().getChildKey(stack.peek(), this.node));
        return this.node;
    }

    /**
     * Required by the <code>Iterator</code> interface, but not supported here.
     * 
     * @throws UnsupportedOperationException whenever called.
     */
    public void remove() {
        throw new UnsupportedOperationException();
    }

    /**
     * Get the current depth of this walker (can be used e.g. to compute indentation margins
     * or CSS styles). If root node is visible (see {@link Tree#isRootVisible()), depth 0 is
     * for the root. Otherwise, children of the root node are at depth 0.
     * 
     * @return the current depth
     */
    public int getDepth() {
        return path.getPathCount() - (this.tree.isRootVisible() ? 1 : 2);
    }

    /**
     * Get the current node, which is the result of the last call to {@link #next()} (except if
     * {@link #enterChildren()} or {@link #leave()} where called inbetween.
     * 
     * @return the current node.
     */
    public Object getNode() {
        return this.node;
    }

    /**
     * Get the path of the current node.
     * 
     * @return the path
     */
    public TreePath getPath() {
        return this.path;
    }

    /**
     * Is the current node a leaf?
     */
    public boolean isLeaf() {
        return this.tree.getModel().isLeaf(this.node);
    }

    /**
     * Is the current node expanded?
     */
    public boolean isExpanded() {
        return this.tree.isExpanded(this.path);
    }

    /**
     * Is the current node collapsed?
     */
    public boolean isCollapsed() {
        return this.tree.isCollapsed(this.path);
    }

    /**
     * Is the current node visible (i.e. its parent is expanded)?
     */
    public boolean isVisible() {
        return this.tree.isVisible(this.path);
    }

    /**
     * Is the current node selected?
     */
    public boolean isSelected() {
        return this.tree.isPathSelected(this.path);
    }

    /**
     * Get the "icon type" that should be used for this node, according to the common
     * visual paradigms used to render trees:
     * <ul>
     * <li>"<code>leaf</code>" for leaf nodes (will be e.g. a file icon),</li>
     * <li>"<code>expanded</code>" for non-leaf expanded nodes (will be e.g. a "minus" icon)</li>
     * <li>"<code>collapsed</code>" for non-leaf collapsed nodes (will be e.g. a "plus" icon)</li>
     * </ul>
     * 
     * @return the icon type
     */
    public String getIconType() {
        if (isLeaf()) {
            return "leaf";
        } else if (isExpanded()) {
            return "expanded";
        } else {
            return "collapsed";
        }
    }

    /**
     * Get the "selection type" that should be used for this node, that can be used e.g. as
     * a CSS class name:
     * <ul>
     * <li>"<code>selected</code>" for selected nodes,</li>
     * <li>"<code>unselected</code>" for unselected nodes.</li>
     * </ul>
     * 
     * @return the selection type
     */
    public String getSelectionType() {
        return this.tree.isPathSelected(this.path) ? "selected" : "unselected";
    }
}