alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (VariableHeightLayoutCache.java)

This example Java source code file (VariableHeightLayoutCache.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

awt, defaultmutabletreenode, enumeration, event, gui, hashtable, i\'m, nosuchelementexception, object, rectangle, stack, swing, treemodel, treepath, treestatenode, util, variableheightlayoutcache, vector, visibletreestatenodeenumeration

The VariableHeightLayoutCache.java Java example source code

/*
 * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package javax.swing.tree;

import javax.swing.event.TreeModelEvent;
import java.awt.Rectangle;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.NoSuchElementException;
import java.util.Stack;
import java.util.Vector;

import sun.swing.SwingUtilities2;

/**
 * NOTE: This will become more open in a future release.
 * <p>
 * <strong>Warning:
 * Serialized objects of this class will not be compatible with
 * future Swing releases. The current serialization support is
 * appropriate for short term storage or RMI between applications running
 * the same version of Swing.  As of 1.4, support for long term storage
 * of all JavaBeans™
 * has been added to the <code>java.beans package.
 * Please see {@link java.beans.XMLEncoder}.
 *
 * @author Rob Davis
 * @author Ray Ryan
 * @author Scott Violet
 */

public class VariableHeightLayoutCache extends AbstractLayoutCache {
    /**
     * The array of nodes that are currently visible, in the order they
     * are displayed.
     */
    private Vector<Object> visibleNodes;

    /**
     * This is set to true if one of the entries has an invalid size.
     */
    private boolean           updateNodeSizes;

    /**
     * The root node of the internal cache of nodes that have been shown.
     * If the treeModel is vending a network rather than a true tree,
     * there may be one cached node for each path to a modeled node.
     */
    private TreeStateNode     root;

    /**
     * Used in getting sizes for nodes to avoid creating a new Rectangle
     * every time a size is needed.
     */
    private Rectangle         boundsBuffer;

    /**
     * Maps from <code>TreePath to a TreeStateNode.
     */
    private Hashtable<TreePath, TreeStateNode> treePathMapping;

    /**
     * A stack of stacks.
     */
    private Stack<Stack tempStacks;


    public VariableHeightLayoutCache() {
        super();
        tempStacks = new Stack<Stack();
        visibleNodes = new Vector<Object>();
        boundsBuffer = new Rectangle();
        treePathMapping = new Hashtable<TreePath, TreeStateNode>();
    }

    /**
     * Sets the <code>TreeModel that will provide the data.
     *
     * @param newModel the <code>TreeModel that is to provide the data
     * @beaninfo
     *        bound: true
     *  description: The TreeModel that will provide the data.
     */
    public void setModel(TreeModel newModel) {
        super.setModel(newModel);
        rebuild(false);
    }

    /**
     * Determines whether or not the root node from
     * the <code>TreeModel is visible.
     *
     * @param rootVisible true if the root node of the tree is to be displayed
     * @see #rootVisible
     * @beaninfo
     *        bound: true
     *  description: Whether or not the root node
     *               from the TreeModel is visible.
     */
    public void setRootVisible(boolean rootVisible) {
        if(isRootVisible() != rootVisible && root != null) {
            if(rootVisible) {
                root.updatePreferredSize(0);
                visibleNodes.insertElementAt(root, 0);
            }
            else if(visibleNodes.size() > 0) {
                visibleNodes.removeElementAt(0);
                if(treeSelectionModel != null)
                    treeSelectionModel.removeSelectionPath
                        (root.getTreePath());
            }
            if(treeSelectionModel != null)
                treeSelectionModel.resetRowSelection();
            if(getRowCount() > 0)
                getNode(0).setYOrigin(0);
            updateYLocationsFrom(0);
            visibleNodesChanged();
        }
        super.setRootVisible(rootVisible);
    }

    /**
     * Sets the height of each cell.  If the specified value
     * is less than or equal to zero the current cell renderer is
     * queried for each row's height.
     *
     * @param rowHeight the height of each cell, in pixels
     * @beaninfo
     *        bound: true
     *  description: The height of each cell.
     */
    public void setRowHeight(int rowHeight) {
        if(rowHeight != getRowHeight()) {
            super.setRowHeight(rowHeight);
            invalidateSizes();
            this.visibleNodesChanged();
        }
    }

    /**
     * Sets the renderer that is responsible for drawing nodes in the tree.
     * @param nd the renderer
     */
    public void setNodeDimensions(NodeDimensions nd) {
        super.setNodeDimensions(nd);
        invalidateSizes();
        visibleNodesChanged();
    }

    /**
     * Marks the path <code>path expanded state to
     * <code>isExpanded.
     * @param path the <code>TreePath of interest
     * @param isExpanded true if the path should be expanded, otherwise false
     */
    public void setExpandedState(TreePath path, boolean isExpanded) {
        if(path != null) {
            if(isExpanded)
                ensurePathIsExpanded(path, true);
            else {
                TreeStateNode        node = getNodeForPath(path, false, true);

                if(node != null) {
                    node.makeVisible();
                    node.collapse();
                }
            }
        }
    }

    /**
     * Returns true if the path is expanded, and visible.
     * @return true if the path is expanded and visible, otherwise false
     */
    public boolean getExpandedState(TreePath path) {
        TreeStateNode       node = getNodeForPath(path, true, false);

        return (node != null) ? (node.isVisible() && node.isExpanded()) :
                                 false;
    }

    /**
      * Returns the <code>Rectangle enclosing the label portion
      * into which the item identified by <code>path will be drawn.
      *
      * @param path  the path to be drawn
      * @param placeIn the bounds of the enclosing rectangle
      * @return the bounds of the enclosing rectangle or <code>null
      *    if the node could not be ascertained
      */
    public Rectangle getBounds(TreePath path, Rectangle placeIn) {
        TreeStateNode       node = getNodeForPath(path, true, false);

        if(node != null) {
            if(updateNodeSizes)
                updateNodeSizes(false);
            return node.getNodeBounds(placeIn);
        }
        return null;
    }

    /**
      * Returns the path for <code>row.  If row
      * is not visible, <code>null is returned.
      *
      * @param row the location of interest
      * @return the path for <code>row, or null
      * if <code>row is not visible
      */
    public TreePath getPathForRow(int row) {
        if(row >= 0 && row < getRowCount()) {
            return getNode(row).getTreePath();
        }
        return null;
    }

    /**
      * Returns the row where the last item identified in path is visible.
      * Will return -1 if any of the elements in path are not
      * currently visible.
      *
      * @param path the <code>TreePath of interest
      * @return the row where the last item in path is visible
      */
    public int getRowForPath(TreePath path) {
        if(path == null)
            return -1;

        TreeStateNode    visNode = getNodeForPath(path, true, false);

        if(visNode != null)
            return visNode.getRow();
        return -1;
    }

    /**
     * Returns the number of visible rows.
     * @return the number of visible rows
     */
    public int getRowCount() {
        return visibleNodes.size();
    }

    /**
     * Instructs the <code>LayoutCache that the bounds for
     * <code>path are invalid, and need to be updated.
     *
     * @param path the <code>TreePath which is now invalid
     */
    public void invalidatePathBounds(TreePath path) {
        TreeStateNode       node = getNodeForPath(path, true, false);

        if(node != null) {
            node.markSizeInvalid();
            if(node.isVisible())
                updateYLocationsFrom(node.getRow());
        }
    }

    /**
     * Returns the preferred height.
     * @return the preferred height
     */
    public int getPreferredHeight() {
        // Get the height
        int           rowCount = getRowCount();

        if(rowCount > 0) {
            TreeStateNode  node = getNode(rowCount - 1);

            return node.getYOrigin() + node.getPreferredHeight();
        }
        return 0;
    }

    /**
     * Returns the preferred width and height for the region in
     * <code>visibleRegion.
     *
     * @param bounds  the region being queried
     */
    public int getPreferredWidth(Rectangle bounds) {
        if(updateNodeSizes)
            updateNodeSizes(false);

        return getMaxNodeWidth();
    }

    /**
      * Returns the path to the node that is closest to x,y.  If
      * there is nothing currently visible this will return <code>null,
      * otherwise it will always return a valid path.
      * If you need to test if the
      * returned object is exactly at x, y you should get the bounds for
      * the returned path and test x, y against that.
      *
      * @param x  the x-coordinate
      * @param y  the y-coordinate
      * @return the path to the node that is closest to x, y
      */
    public TreePath getPathClosestTo(int x, int y) {
        if(getRowCount() == 0)
            return null;

        if(updateNodeSizes)
            updateNodeSizes(false);

        int                row = getRowContainingYLocation(y);

        return getNode(row).getTreePath();
    }

    /**
     * Returns an <code>Enumerator that increments over the visible paths
     * starting at the passed in location. The ordering of the enumeration
     * is based on how the paths are displayed.
     *
     * @param path the location in the <code>TreePath to start
     * @return an <code>Enumerator that increments over the visible
     *     paths
     */
    public Enumeration<TreePath> getVisiblePathsFrom(TreePath path) {
        TreeStateNode       node = getNodeForPath(path, true, false);

        if(node != null) {
            return new VisibleTreeStateNodeEnumeration(node);
        }
        return null;
    }

    /**
     * Returns the number of visible children for <code>path.
     * @return the number of visible children for <code>path
     */
    public int getVisibleChildCount(TreePath path) {
        TreeStateNode         node = getNodeForPath(path, true, false);

        return (node != null) ? node.getVisibleChildCount() : 0;
    }

    /**
     * Informs the <code>TreeState that it needs to recalculate
     * all the sizes it is referencing.
     */
    public void invalidateSizes() {
        if(root != null)
            root.deepMarkSizeInvalid();
        if(!isFixedRowHeight() && visibleNodes.size() > 0) {
            updateNodeSizes(true);
        }
    }

    /**
      * Returns true if the value identified by <code>path is
      * currently expanded.
      * @return true if the value identified by <code>path is
      *    currently expanded
      */
    public boolean isExpanded(TreePath path) {
        if(path != null) {
            TreeStateNode     lastNode = getNodeForPath(path, true, false);

            return (lastNode != null && lastNode.isExpanded());
        }
        return false;
    }

    //
    // TreeModelListener methods
    //

    /**
     * Invoked after a node (or a set of siblings) has changed in some
     * way. The node(s) have not changed locations in the tree or
     * altered their children arrays, but other attributes have
     * changed and may affect presentation. Example: the name of a
     * file has changed, but it is in the same location in the file
     * system.
     *
     * <p>e.path returns the path the parent of the
     * changed node(s).
     *
     * <p>e.childIndices returns the index(es) of the
     * changed node(s).
     *
     * @param e the <code>TreeModelEvent of interest
     */
    public void treeNodesChanged(TreeModelEvent e) {
        if(e != null) {
            int               changedIndexs[];
            TreeStateNode     changedNode;

            changedIndexs = e.getChildIndices();
            changedNode = getNodeForPath(SwingUtilities2.getTreePath(e, getModel()), false, false);
            if(changedNode != null) {
                Object            changedValue = changedNode.getValue();

                /* Update the size of the changed node, as well as all the
                   child indexs that are passed in. */
                changedNode.updatePreferredSize();
                if(changedNode.hasBeenExpanded() && changedIndexs != null) {
                    int                counter;
                    TreeStateNode      changedChildNode;

                    for(counter = 0; counter < changedIndexs.length;
                        counter++) {
                        changedChildNode = (TreeStateNode)changedNode
                                    .getChildAt(changedIndexs[counter]);
                        /* Reset the user object. */
                        changedChildNode.setUserObject
                                    (treeModel.getChild(changedValue,
                                                     changedIndexs[counter]));
                        changedChildNode.updatePreferredSize();
                    }
                }
                else if (changedNode == root) {
                    // Null indicies for root indicates it changed.
                    changedNode.updatePreferredSize();
                }
                if(!isFixedRowHeight()) {
                    int          aRow = changedNode.getRow();

                    if(aRow != -1)
                        this.updateYLocationsFrom(aRow);
                }
                this.visibleNodesChanged();
            }
        }
    }


    /**
     * Invoked after nodes have been inserted into the tree.
     *
     * <p>e.path returns the parent of the new nodes.
     * <p>e.childIndices returns the indices of the new nodes in
     * ascending order.
     *
     * @param e the <code>TreeModelEvent of interest
     */
    public void treeNodesInserted(TreeModelEvent e) {
        if(e != null) {
            int               changedIndexs[];
            TreeStateNode     changedParentNode;

            changedIndexs = e.getChildIndices();
            changedParentNode = getNodeForPath(SwingUtilities2.getTreePath(e, getModel()), false, false);
            /* Only need to update the children if the node has been
               expanded once. */
            // PENDING(scott): make sure childIndexs is sorted!
            if(changedParentNode != null && changedIndexs != null &&
               changedIndexs.length > 0) {
                if(changedParentNode.hasBeenExpanded()) {
                    boolean            makeVisible;
                    int                counter;
                    Object             changedParent;
                    TreeStateNode      newNode;
                    int                oldChildCount = changedParentNode.
                                          getChildCount();

                    changedParent = changedParentNode.getValue();
                    makeVisible = ((changedParentNode == root &&
                                    !rootVisible) ||
                                   (changedParentNode.getRow() != -1 &&
                                    changedParentNode.isExpanded()));
                    for(counter = 0;counter < changedIndexs.length;counter++)
                    {
                        newNode = this.createNodeAt(changedParentNode,
                                                    changedIndexs[counter]);
                    }
                    if(oldChildCount == 0) {
                        // Update the size of the parent.
                        changedParentNode.updatePreferredSize();
                    }
                    if(treeSelectionModel != null)
                        treeSelectionModel.resetRowSelection();
                    /* Update the y origins from the index of the parent
                       to the end of the visible rows. */
                    if(!isFixedRowHeight() && (makeVisible ||
                                               (oldChildCount == 0 &&
                                        changedParentNode.isVisible()))) {
                        if(changedParentNode == root)
                            this.updateYLocationsFrom(0);
                        else
                            this.updateYLocationsFrom(changedParentNode.
                                                      getRow());
                        this.visibleNodesChanged();
                    }
                    else if(makeVisible)
                        this.visibleNodesChanged();
                }
                else if(treeModel.getChildCount(changedParentNode.getValue())
                        - changedIndexs.length == 0) {
                    changedParentNode.updatePreferredSize();
                    if(!isFixedRowHeight() && changedParentNode.isVisible())
                        updateYLocationsFrom(changedParentNode.getRow());
                }
            }
        }
    }

    /**
     * Invoked after nodes have been removed from the tree.  Note that
     * if a subtree is removed from the tree, this method may only be
     * invoked once for the root of the removed subtree, not once for
     * each individual set of siblings removed.
     *
     * <p>e.path returns the former parent of the deleted nodes.
     *
     * <p>e.childIndices returns the indices the nodes had
     * before they were deleted in ascending order.
     *
     * @param e the <code>TreeModelEvent of interest
     */
    public void treeNodesRemoved(TreeModelEvent e) {
        if(e != null) {
            int               changedIndexs[];
            TreeStateNode     changedParentNode;

            changedIndexs = e.getChildIndices();
            changedParentNode = getNodeForPath(SwingUtilities2.getTreePath(e, getModel()), false, false);
            // PENDING(scott): make sure that changedIndexs are sorted in
            // ascending order.
            if(changedParentNode != null && changedIndexs != null &&
               changedIndexs.length > 0) {
                if(changedParentNode.hasBeenExpanded()) {
                    boolean            makeInvisible;
                    int                counter;
                    int                removedRow;
                    TreeStateNode      removedNode;

                    makeInvisible = ((changedParentNode == root &&
                                      !rootVisible) ||
                                     (changedParentNode.getRow() != -1 &&
                                      changedParentNode.isExpanded()));
                    for(counter = changedIndexs.length - 1;counter >= 0;
                        counter--) {
                        removedNode = (TreeStateNode)changedParentNode.
                                getChildAt(changedIndexs[counter]);
                        if(removedNode.isExpanded()) {
                            removedNode.collapse(false);
                        }

                        /* Let the selection model now. */
                        if(makeInvisible) {
                            removedRow = removedNode.getRow();
                            if(removedRow != -1) {
                                visibleNodes.removeElementAt(removedRow);
                            }
                        }
                        changedParentNode.remove(changedIndexs[counter]);
                    }
                    if(changedParentNode.getChildCount() == 0) {
                        // Update the size of the parent.
                        changedParentNode.updatePreferredSize();
                        if (changedParentNode.isExpanded() &&
                                   changedParentNode.isLeaf()) {
                            // Node has become a leaf, collapse it.
                            changedParentNode.collapse(false);
                        }
                    }
                    if(treeSelectionModel != null)
                        treeSelectionModel.resetRowSelection();
                    /* Update the y origins from the index of the parent
                       to the end of the visible rows. */
                    if(!isFixedRowHeight() && (makeInvisible ||
                               (changedParentNode.getChildCount() == 0 &&
                                changedParentNode.isVisible()))) {
                        if(changedParentNode == root) {
                            /* It is possible for first row to have been
                               removed if the root isn't visible, in which
                               case ylocations will be off! */
                            if(getRowCount() > 0)
                                getNode(0).setYOrigin(0);
                            updateYLocationsFrom(0);
                        }
                        else
                            updateYLocationsFrom(changedParentNode.getRow());
                        this.visibleNodesChanged();
                    }
                    else if(makeInvisible)
                        this.visibleNodesChanged();
                }
                else if(treeModel.getChildCount(changedParentNode.getValue())
                        == 0) {
                    changedParentNode.updatePreferredSize();
                    if(!isFixedRowHeight() && changedParentNode.isVisible())
                        this.updateYLocationsFrom(changedParentNode.getRow());
                }
            }
        }
    }

    /**
     * Invoked after the tree has drastically changed structure from a
     * given node down.  If the path returned by <code>e.getPath
     * is of length one and the first element does not identify the
     * current root node the first element should become the new root
     * of the tree.
     *
     * <p>e.path holds the path to the node.
     * <p>e.childIndices returns null.
     *
     * @param e the <code>TreeModelEvent of interest
     */
    public void treeStructureChanged(TreeModelEvent e) {
        if(e != null)
        {
            TreePath          changedPath = SwingUtilities2.getTreePath(e, getModel());
            TreeStateNode     changedNode;

            changedNode = getNodeForPath(changedPath, false, false);

            // Check if root has changed, either to a null root, or
            // to an entirely new root.
            if(changedNode == root ||
               (changedNode == null &&
                ((changedPath == null && treeModel != null &&
                  treeModel.getRoot() == null) ||
                 (changedPath != null && changedPath.getPathCount() == 1)))) {
                rebuild(true);
            }
            else if(changedNode != null) {
                int                              nodeIndex, oldRow;
                TreeStateNode                    newNode, parent;
                boolean                          wasExpanded, wasVisible;
                int                              newIndex;

                wasExpanded = changedNode.isExpanded();
                wasVisible = (changedNode.getRow() != -1);
                /* Remove the current node and recreate a new one. */
                parent = (TreeStateNode)changedNode.getParent();
                nodeIndex = parent.getIndex(changedNode);
                if(wasVisible && wasExpanded) {
                    changedNode.collapse(false);
                }
                if(wasVisible)
                    visibleNodes.removeElement(changedNode);
                changedNode.removeFromParent();
                createNodeAt(parent, nodeIndex);
                newNode = (TreeStateNode)parent.getChildAt(nodeIndex);
                if(wasVisible && wasExpanded)
                    newNode.expand(false);
                newIndex = newNode.getRow();
                if(!isFixedRowHeight() && wasVisible) {
                    if(newIndex == 0)
                        updateYLocationsFrom(newIndex);
                    else
                        updateYLocationsFrom(newIndex - 1);
                    this.visibleNodesChanged();
                }
                else if(wasVisible)
                    this.visibleNodesChanged();
            }
        }
    }


    //
    // Local methods
    //

    private void visibleNodesChanged() {
    }

    /**
     * Adds a mapping for node.
     */
    private void addMapping(TreeStateNode node) {
        treePathMapping.put(node.getTreePath(), node);
    }

    /**
     * Removes the mapping for a previously added node.
     */
    private void removeMapping(TreeStateNode node) {
        treePathMapping.remove(node.getTreePath());
    }

    /**
     * Returns the node previously added for <code>path. This may
     * return null, if you to create a node use getNodeForPath.
     */
    private TreeStateNode getMapping(TreePath path) {
        return treePathMapping.get(path);
    }

    /**
     * Retursn the bounds for row, <code>row by reference in
     * <code>placeIn. If placeIn is null a new
     * Rectangle will be created and returned.
     */
    private Rectangle getBounds(int row, Rectangle placeIn) {
        if(updateNodeSizes)
            updateNodeSizes(false);

        if(row >= 0 && row < getRowCount()) {
            return getNode(row).getNodeBounds(placeIn);
        }
        return null;
    }

    /**
     * Completely rebuild the tree, all expanded state, and node caches are
     * removed. All nodes are collapsed, except the root.
     */
    private void rebuild(boolean clearSelection) {
        Object rootObject;

        treePathMapping.clear();
        if(treeModel != null && (rootObject = treeModel.getRoot()) != null) {
            root = createNodeForValue(rootObject);
            root.path = new TreePath(rootObject);
            addMapping(root);
            root.updatePreferredSize(0);
            visibleNodes.removeAllElements();
            if (isRootVisible())
                visibleNodes.addElement(root);
            if(!root.isExpanded())
                root.expand();
            else {
                Enumeration cursor = root.children();
                while(cursor.hasMoreElements()) {
                    visibleNodes.addElement(cursor.nextElement());
                }
                if(!isFixedRowHeight())
                    updateYLocationsFrom(0);
            }
        }
        else {
            visibleNodes.removeAllElements();
            root = null;
        }
        if(clearSelection && treeSelectionModel != null) {
            treeSelectionModel.clearSelection();
        }
        this.visibleNodesChanged();
    }

    /**
      * Creates a new node to represent the node at <I>childIndex in
      * <I>parents children.  This should be called if the node doesn't
      * already exist and <I>parent has been expanded at least once.
      * The newly created node will be made visible if <I>parent is
      * currently expanded.  This does not update the position of any
      * cells, nor update the selection if it needs to be.  If succesful
      * in creating the new TreeStateNode, it is returned, otherwise
      * null is returned.
      */
    private TreeStateNode createNodeAt(TreeStateNode parent,
                                         int childIndex) {
        boolean                isParentRoot;
        Object                 newValue;
        TreeStateNode          newChildNode;

        newValue = treeModel.getChild(parent.getValue(), childIndex);
        newChildNode = createNodeForValue(newValue);
        parent.insert(newChildNode, childIndex);
        newChildNode.updatePreferredSize(-1);
        isParentRoot = (parent == root);
        if(newChildNode != null && parent.isExpanded() &&
           (parent.getRow() != -1 || isParentRoot)) {
            int                 newRow;

            /* Find the new row to insert this newly visible node at. */
            if(childIndex == 0) {
                if(isParentRoot && !isRootVisible())
                    newRow = 0;
                else
                    newRow = parent.getRow() + 1;
            }
            else if(childIndex == parent.getChildCount())
                newRow = parent.getLastVisibleNode().getRow() + 1;
            else {
                TreeStateNode          previousNode;

                previousNode = (TreeStateNode)parent.
                    getChildAt(childIndex - 1);
                newRow = previousNode.getLastVisibleNode().getRow() + 1;
            }
            visibleNodes.insertElementAt(newChildNode, newRow);
        }
        return newChildNode;
    }

    /**
      * Returns the TreeStateNode identified by path.  This mirrors
      * the behavior of getNodeForPath, but tries to take advantage of
      * path if it is an instance of AbstractTreePath.
      */
    private TreeStateNode getNodeForPath(TreePath path,
                                           boolean onlyIfVisible,
                                           boolean shouldCreate) {
        if(path != null) {
            TreeStateNode      node;

            node = getMapping(path);
            if(node != null) {
                if(onlyIfVisible && !node.isVisible())
                    return null;
                return node;
            }

            // Check all the parent paths, until a match is found.
            Stack<TreePath> paths;

            if(tempStacks.size() == 0) {
                paths = new Stack<TreePath>();
            }
            else {
                paths = tempStacks.pop();
            }

            try {
                paths.push(path);
                path = path.getParentPath();
                node = null;
                while(path != null) {
                    node = getMapping(path);
                    if(node != null) {
                        // Found a match, create entries for all paths in
                        // paths.
                        while(node != null && paths.size() > 0) {
                            path = paths.pop();
                            node.getLoadedChildren(shouldCreate);

                            int            childIndex = treeModel.
                                      getIndexOfChild(node.getUserObject(),
                                                  path.getLastPathComponent());

                            if(childIndex == -1 ||
                               childIndex >= node.getChildCount() ||
                               (onlyIfVisible && !node.isVisible())) {
                                node = null;
                            }
                            else
                                node = (TreeStateNode)node.getChildAt
                                               (childIndex);
                        }
                        return node;
                    }
                    paths.push(path);
                    path = path.getParentPath();
                }
            }
            finally {
                paths.removeAllElements();
                tempStacks.push(paths);
            }
            // If we get here it means they share a different root!
            // We could throw an exception...
        }
        return null;
    }

    /**
      * Updates the y locations of all of the visible nodes after
      * location.
      */
    private void updateYLocationsFrom(int location) {
        if(location >= 0 && location < getRowCount()) {
            int                    counter, maxCounter, newYOrigin;
            TreeStateNode          aNode;

            aNode = getNode(location);
            newYOrigin = aNode.getYOrigin() + aNode.getPreferredHeight();
            for(counter = location + 1, maxCounter = visibleNodes.size();
                counter < maxCounter;counter++) {
                aNode = (TreeStateNode)visibleNodes.
                    elementAt(counter);
                aNode.setYOrigin(newYOrigin);
                newYOrigin += aNode.getPreferredHeight();
            }
        }
    }

    /**
      * Resets the y origin of all the visible nodes as well as messaging
      * all the visible nodes to updatePreferredSize().  You should not
      * normally have to call this.  Expanding and contracting the nodes
      * automaticly adjusts the locations.
      * updateAll determines if updatePreferredSize() is call on all nodes
      * or just those that don't have a valid size.
      */
    private void updateNodeSizes(boolean updateAll) {
        int                      aY, counter, maxCounter;
        TreeStateNode            node;

        updateNodeSizes = false;
        for(aY = counter = 0, maxCounter = visibleNodes.size();
            counter < maxCounter; counter++) {
            node = (TreeStateNode)visibleNodes.elementAt(counter);
            node.setYOrigin(aY);
            if(updateAll || !node.hasValidSize())
                node.updatePreferredSize(counter);
            aY += node.getPreferredHeight();
        }
    }

    /**
      * Returns the index of the row containing location.  If there
      * are no rows, -1 is returned.  If location is beyond the last
      * row index, the last row index is returned.
      */
    private int getRowContainingYLocation(int location) {
        if(isFixedRowHeight()) {
            if(getRowCount() == 0)
                return -1;
            return Math.max(0, Math.min(getRowCount() - 1,
                                        location / getRowHeight()));
        }

        int                    max, maxY, mid, min, minY;
        TreeStateNode          node;

        if((max = getRowCount()) <= 0)
            return -1;
        mid = min = 0;
        while(min < max) {
            mid = (max - min) / 2 + min;
            node = (TreeStateNode)visibleNodes.elementAt(mid);
            minY = node.getYOrigin();
            maxY = minY + node.getPreferredHeight();
            if(location < minY) {
                max = mid - 1;
            }
            else if(location >= maxY) {
                min = mid + 1;
            }
            else
                break;
        }
        if(min == max) {
            mid = min;
            if(mid >= getRowCount())
                mid = getRowCount() - 1;
        }
        return mid;
    }

    /**
     * Ensures that all the path components in path are expanded, accept
     * for the last component which will only be expanded if expandLast
     * is true.
     * Returns true if succesful in finding the path.
     */
    private void ensurePathIsExpanded(TreePath aPath, boolean expandLast) {
        if(aPath != null) {
            // Make sure the last entry isn't a leaf.
            if(treeModel.isLeaf(aPath.getLastPathComponent())) {
                aPath = aPath.getParentPath();
                expandLast = true;
            }
            if(aPath != null) {
                TreeStateNode     lastNode = getNodeForPath(aPath, false,
                                                            true);

                if(lastNode != null) {
                    lastNode.makeVisible();
                    if(expandLast)
                        lastNode.expand();
                }
            }
        }
    }

    /**
     * Returns the AbstractTreeUI.VisibleNode displayed at the given row
     */
    private TreeStateNode getNode(int row) {
        return (TreeStateNode)visibleNodes.elementAt(row);
    }

    /**
      * Returns the maximum node width.
      */
    private int getMaxNodeWidth() {
        int                     maxWidth = 0;
        int                     nodeWidth;
        int                     counter;
        TreeStateNode           node;

        for(counter = getRowCount() - 1;counter >= 0;counter--) {
            node = this.getNode(counter);
            nodeWidth = node.getPreferredWidth() + node.getXOrigin();
            if(nodeWidth > maxWidth)
                maxWidth = nodeWidth;
        }
        return maxWidth;
    }

    /**
      * Responsible for creating a TreeStateNode that will be used
      * to track display information about value.
      */
    private TreeStateNode createNodeForValue(Object value) {
        return new TreeStateNode(value);
    }


    /**
     * TreeStateNode is used to keep track of each of
     * the nodes that have been expanded. This will also cache the preferred
     * size of the value it represents.
     */
    private class TreeStateNode extends DefaultMutableTreeNode {
        /** Preferred size needed to draw the user object. */
        protected int             preferredWidth;
        protected int             preferredHeight;

        /** X location that the user object will be drawn at. */
        protected int             xOrigin;

        /** Y location that the user object will be drawn at. */
        protected int             yOrigin;

        /** Is this node currently expanded? */
        protected boolean         expanded;

        /** Has this node been expanded at least once? */
        protected boolean         hasBeenExpanded;

        /** Path of this node. */
        protected TreePath        path;


        public TreeStateNode(Object value) {
            super(value);
        }

        //
        // Overriden DefaultMutableTreeNode methods
        //

        /**
         * Messaged when this node is added somewhere, resets the path
         * and adds a mapping from path to this node.
         */
        public void setParent(MutableTreeNode parent) {
            super.setParent(parent);
            if(parent != null) {
                path = ((TreeStateNode)parent).getTreePath().
                                       pathByAddingChild(getUserObject());
                addMapping(this);
            }
        }

        /**
         * Messaged when this node is removed from its parent, this messages
         * <code>removedFromMapping to remove all the children.
         */
        public void remove(int childIndex) {
            TreeStateNode     node = (TreeStateNode)getChildAt(childIndex);

            node.removeFromMapping();
            super.remove(childIndex);
        }

        /**
         * Messaged to set the user object. This resets the path.
         */
        public void setUserObject(Object o) {
            super.setUserObject(o);
            if(path != null) {
                TreeStateNode      parent = (TreeStateNode)getParent();

                if(parent != null)
                    resetChildrenPaths(parent.getTreePath());
                else
                    resetChildrenPaths(null);
            }
        }

        /**
         * Returns the children of the receiver.
         * If the receiver is not currently expanded, this will return an
         * empty enumeration.
         */
        public Enumeration children() {
            if (!this.isExpanded()) {
                return DefaultMutableTreeNode.EMPTY_ENUMERATION;
            } else {
                return super.children();
            }
        }

        /**
         * Returns true if the receiver is a leaf.
         */
        public boolean isLeaf() {
            return getModel().isLeaf(this.getValue());
        }

        //
        // VariableHeightLayoutCache
        //

        /**
         * Returns the location and size of this node.
         */
        public Rectangle getNodeBounds(Rectangle placeIn) {
            if(placeIn == null)
                placeIn = new Rectangle(getXOrigin(), getYOrigin(),
                                        getPreferredWidth(),
                                        getPreferredHeight());
            else {
                placeIn.x = getXOrigin();
                placeIn.y = getYOrigin();
                placeIn.width = getPreferredWidth();
                placeIn.height = getPreferredHeight();
            }
            return placeIn;
        }

        /**
         * @return x location to draw node at.
         */
        public int getXOrigin() {
            if(!hasValidSize())
                updatePreferredSize(getRow());
            return xOrigin;
        }

        /**
         * Returns the y origin the user object will be drawn at.
         */
        public int getYOrigin() {
            if(isFixedRowHeight()) {
                int      aRow = getRow();

                if(aRow == -1)
                    return -1;
                return getRowHeight() * aRow;
            }
            return yOrigin;
        }

        /**
         * Returns the preferred height of the receiver.
         */
        public int getPreferredHeight() {
            if(isFixedRowHeight())
                return getRowHeight();
            else if(!hasValidSize())
                updatePreferredSize(getRow());
            return preferredHeight;
        }

        /**
         * Returns the preferred width of the receiver.
         */
        public int getPreferredWidth() {
            if(!hasValidSize())
                updatePreferredSize(getRow());
            return preferredWidth;
        }

        /**
         * Returns true if this node has a valid size.
         */
        public boolean hasValidSize() {
            return (preferredHeight != 0);
        }

        /**
         * Returns the row of the receiver.
         */
        public int getRow() {
            return visibleNodes.indexOf(this);
        }

        /**
         * Returns true if this node has been expanded at least once.
         */
        public boolean hasBeenExpanded() {
            return hasBeenExpanded;
        }

        /**
         * Returns true if the receiver has been expanded.
         */
        public boolean isExpanded() {
            return expanded;
        }

        /**
         * Returns the last visible node that is a child of this
         * instance.
         */
        public TreeStateNode getLastVisibleNode() {
            TreeStateNode                node = this;

            while(node.isExpanded() && node.getChildCount() > 0)
                node = (TreeStateNode)node.getLastChild();
            return node;
        }

        /**
         * Returns true if the receiver is currently visible.
         */
        public boolean isVisible() {
            if(this == root)
                return true;

            TreeStateNode        parent = (TreeStateNode)getParent();

            return (parent != null && parent.isExpanded() &&
                    parent.isVisible());
        }

        /**
         * Returns the number of children this will have. If the children
         * have not yet been loaded, this messages the model.
         */
        public int getModelChildCount() {
            if(hasBeenExpanded)
                return super.getChildCount();
            return getModel().getChildCount(getValue());
        }

        /**
         * Returns the number of visible children, that is the number of
         * children that are expanded, or leafs.
         */
        public int getVisibleChildCount() {
            int               childCount = 0;

            if(isExpanded()) {
                int         maxCounter = getChildCount();

                childCount += maxCounter;
                for(int counter = 0; counter < maxCounter; counter++)
                    childCount += ((TreeStateNode)getChildAt(counter)).
                                    getVisibleChildCount();
            }
            return childCount;
        }

        /**
         * Toggles the receiver between expanded and collapsed.
         */
        public void toggleExpanded() {
            if (isExpanded()) {
                collapse();
            } else {
                expand();
            }
        }

        /**
         * Makes the receiver visible, but invoking
         * <code>expandParentAndReceiver on the superclass.
         */
        public void makeVisible() {
            TreeStateNode       parent = (TreeStateNode)getParent();

            if(parent != null)
                parent.expandParentAndReceiver();
        }

        /**
         * Expands the receiver.
         */
        public void expand() {
            expand(true);
        }

        /**
         * Collapses the receiver.
         */
        public void collapse() {
            collapse(true);
        }

        /**
         * Returns the value the receiver is representing. This is a cover
         * for getUserObject.
         */
        public Object getValue() {
            return getUserObject();
        }

        /**
         * Returns a TreePath instance for this node.
         */
        public TreePath getTreePath() {
            return path;
        }

        //
        // Local methods
        //

        /**
         * Recreates the receivers path, and all its children's paths.
         */
        protected void resetChildrenPaths(TreePath parentPath) {
            removeMapping(this);
            if(parentPath == null)
                path = new TreePath(getUserObject());
            else
                path = parentPath.pathByAddingChild(getUserObject());
            addMapping(this);
            for(int counter = getChildCount() - 1; counter >= 0; counter--)
                ((TreeStateNode)getChildAt(counter)).resetChildrenPaths(path);
        }

        /**
         * Sets y origin the user object will be drawn at to
         * <I>newYOrigin.
         */
        protected void setYOrigin(int newYOrigin) {
            yOrigin = newYOrigin;
        }

        /**
         * Shifts the y origin by <code>offset.
         */
        protected void shiftYOriginBy(int offset) {
            yOrigin += offset;
        }

        /**
         * Updates the receivers preferredSize by invoking
         * <code>updatePreferredSize with an argument of -1.
         */
        protected void updatePreferredSize() {
            updatePreferredSize(getRow());
        }

        /**
         * Updates the preferred size by asking the current renderer
         * for the Dimension needed to draw the user object this
         * instance represents.
         */
        protected void updatePreferredSize(int index) {
            Rectangle       bounds = getNodeDimensions(this.getUserObject(),
                                                       index, getLevel(),
                                                       isExpanded(),
                                                       boundsBuffer);

            if(bounds == null) {
                xOrigin = 0;
                preferredWidth = preferredHeight = 0;
                updateNodeSizes = true;
            }
            else if(bounds.height == 0) {
                xOrigin = 0;
                preferredWidth = preferredHeight = 0;
                updateNodeSizes = true;
            }
            else {
                xOrigin = bounds.x;
                preferredWidth = bounds.width;
                if(isFixedRowHeight())
                    preferredHeight = getRowHeight();
                else
                    preferredHeight = bounds.height;
            }
        }

        /**
         * Marks the receivers size as invalid. Next time the size, location
         * is asked for it will be obtained.
         */
        protected void markSizeInvalid() {
            preferredHeight = 0;
        }

        /**
         * Marks the receivers size, and all its descendants sizes, as invalid.
         */
        protected void deepMarkSizeInvalid() {
            markSizeInvalid();
            for(int counter = getChildCount() - 1; counter >= 0; counter--)
                ((TreeStateNode)getChildAt(counter)).deepMarkSizeInvalid();
        }

        /**
         * Returns the children of the receiver. If the children haven't
         * been loaded from the model and
         * <code>createIfNeeded is true, the children are first
         * loaded.
         */
        protected Enumeration getLoadedChildren(boolean createIfNeeded) {
            if(!createIfNeeded || hasBeenExpanded)
                return super.children();

            TreeStateNode   newNode;
            Object          realNode = getValue();
            TreeModel       treeModel = getModel();
            int             count = treeModel.getChildCount(realNode);

            hasBeenExpanded = true;

            int    childRow = getRow();

            if(childRow == -1) {
                for (int i = 0; i < count; i++) {
                    newNode = createNodeForValue
                        (treeModel.getChild(realNode, i));
                    this.add(newNode);
                    newNode.updatePreferredSize(-1);
                }
            }
            else {
                childRow++;
                for (int i = 0; i < count; i++) {
                    newNode = createNodeForValue
                        (treeModel.getChild(realNode, i));
                    this.add(newNode);
                    newNode.updatePreferredSize(childRow++);
                }
            }
            return super.children();
        }

        /**
         * Messaged from expand and collapse. This is meant for subclassers
         * that may wish to do something interesting with this.
         */
        protected void didAdjustTree() {
        }

        /**
         * Invokes <code>expandParentAndReceiver on the parent,
         * and expands the receiver.
         */
        protected void expandParentAndReceiver() {
            TreeStateNode       parent = (TreeStateNode)getParent();

            if(parent != null)
                parent.expandParentAndReceiver();
            expand();
        }

        /**
         * Expands this node in the tree.  This will load the children
         * from the treeModel if this node has not previously been
         * expanded.  If <I>adjustTree is true the tree and selection
         * are updated accordingly.
         */
        protected void expand(boolean adjustTree) {
            if (!isExpanded() && !isLeaf()) {
                boolean         isFixed = isFixedRowHeight();
                int             startHeight = getPreferredHeight();
                int             originalRow = getRow();

                expanded = true;
                updatePreferredSize(originalRow);

                if (!hasBeenExpanded) {
                    TreeStateNode  newNode;
                    Object         realNode = getValue();
                    TreeModel      treeModel = getModel();
                    int            count = treeModel.getChildCount(realNode);

                    hasBeenExpanded = true;
                    if(originalRow == -1) {
                        for (int i = 0; i < count; i++) {
                            newNode = createNodeForValue(treeModel.getChild
                                                            (realNode, i));
                            this.add(newNode);
                            newNode.updatePreferredSize(-1);
                        }
                    }
                    else {
                        int offset = originalRow + 1;
                        for (int i = 0; i < count; i++) {
                            newNode = createNodeForValue(treeModel.getChild
                                                       (realNode, i));
                            this.add(newNode);
                            newNode.updatePreferredSize(offset);
                        }
                    }
                }

                int i = originalRow;
                Enumeration cursor = preorderEnumeration();
                cursor.nextElement(); // don't add me, I'm already in

                int newYOrigin;

                if(isFixed)
                    newYOrigin = 0;
                else if(this == root && !isRootVisible())
                    newYOrigin = 0;
                else
                    newYOrigin = getYOrigin() + this.getPreferredHeight();
                TreeStateNode   aNode;
                if(!isFixed) {
                    while (cursor.hasMoreElements()) {
                        aNode = (TreeStateNode)cursor.nextElement();
                        if(!updateNodeSizes && !aNode.hasValidSize())
                            aNode.updatePreferredSize(i + 1);
                        aNode.setYOrigin(newYOrigin);
                        newYOrigin += aNode.getPreferredHeight();
                        visibleNodes.insertElementAt(aNode, ++i);
                    }
                }
                else {
                    while (cursor.hasMoreElements()) {
                        aNode = (TreeStateNode)cursor.nextElement();
                        visibleNodes.insertElementAt(aNode, ++i);
                    }
                }

                if(adjustTree && (originalRow != i ||
                                  getPreferredHeight() != startHeight)) {
                    // Adjust the Y origin of any nodes following this row.
                    if(!isFixed && ++i < getRowCount()) {
                        int              counter;
                        int              heightDiff = newYOrigin -
                            (getYOrigin() + getPreferredHeight()) +
                            (getPreferredHeight() - startHeight);

                        for(counter = visibleNodes.size() - 1;counter >= i;
                            counter--)
                            ((TreeStateNode)visibleNodes.elementAt(counter)).
                                shiftYOriginBy(heightDiff);
                    }
                    didAdjustTree();
                    visibleNodesChanged();
                }

                // Update the rows in the selection
                if(treeSelectionModel != null) {
                    treeSelectionModel.resetRowSelection();
                }
            }
        }

        /**
         * Collapses this node in the tree.  If <I>adjustTree is
         * true the tree and selection are updated accordingly.
         */
        protected void collapse(boolean adjustTree) {
            if (isExpanded()) {
                Enumeration cursor = preorderEnumeration();
                cursor.nextElement(); // don't remove me, I'm still visible
                int rowsDeleted = 0;
                boolean isFixed = isFixedRowHeight();
                int lastYEnd;
                if(isFixed)
                    lastYEnd = 0;
                else
                    lastYEnd = getPreferredHeight() + getYOrigin();
                int startHeight = getPreferredHeight();
                int startYEnd = lastYEnd;
                int myRow = getRow();

                if(!isFixed) {
                    while(cursor.hasMoreElements()) {
                        TreeStateNode node = (TreeStateNode)cursor.
                            nextElement();
                        if (node.isVisible()) {
                            rowsDeleted++;
                            //visibleNodes.removeElement(node);
                            lastYEnd = node.getYOrigin() +
                                node.getPreferredHeight();
                        }
                    }
                }
                else {
                    while(cursor.hasMoreElements()) {
                        TreeStateNode node = (TreeStateNode)cursor.
                            nextElement();
                        if (node.isVisible()) {
                            rowsDeleted++;
                            //visibleNodes.removeElement(node);
                        }
                    }
                }

                // Clean up the visible nodes.
                for (int counter = rowsDeleted + myRow; counter > myRow;
                     counter--) {
                    visibleNodes.removeElementAt(counter);
                }

                expanded = false;

                if(myRow == -1)
                    markSizeInvalid();
                else if (adjustTree)
                    updatePreferredSize(myRow);

                if(myRow != -1 && adjustTree &&
                   (rowsDeleted > 0 || startHeight != getPreferredHeight())) {
                    // Adjust the Y origin of any rows following this one.
                    startYEnd += (getPreferredHeight() - startHeight);
                    if(!isFixed && (myRow + 1) < getRowCount() &&
                       startYEnd != lastYEnd) {
                        int                 counter, maxCounter, shiftAmount;

                        shiftAmount = startYEnd - lastYEnd;
                        for(counter = myRow + 1, maxCounter =
                                visibleNodes.size();
                            counter < maxCounter;counter++)
                            ((TreeStateNode)visibleNodes.elementAt(counter))
                                .shiftYOriginBy(shiftAmount);
                    }
                    didAdjustTree();
                    visibleNodesChanged();
                }
                if(treeSelectionModel != null && rowsDeleted > 0 &&
                   myRow != -1) {
                    treeSelectionModel.resetRowSelection();
                }
            }
        }

        /**
         * Removes the receiver, and all its children, from the mapping
         * table.
         */
        protected void removeFromMapping() {
            if(path != null) {
                removeMapping(this);
                for(int counter = getChildCount() - 1; counter >= 0; counter--)
                    ((TreeStateNode)getChildAt(counter)).removeFromMapping();
            }
        }
    } // End of VariableHeightLayoutCache.TreeStateNode


    /**
     * An enumerator to iterate through visible nodes.
     */
    private class VisibleTreeStateNodeEnumeration implements
                     Enumeration<TreePath> {
        /** Parent thats children are being enumerated. */
        protected TreeStateNode       parent;
        /** Index of next child. An index of -1 signifies parent should be
         * visibled next. */
        protected int                 nextIndex;
        /** Number of children in parent. */
        protected int                 childCount;

        protected VisibleTreeStateNodeEnumeration(TreeStateNode node) {
            this(node, -1);
        }

        protected VisibleTreeStateNodeEnumeration(TreeStateNode parent,
                                                  int startIndex) {
            this.parent = parent;
            this.nextIndex = startIndex;
            this.childCount = this.parent.getChildCount();
        }

        /**
         * @return true if more visible nodes.
         */
        public boolean hasMoreElements() {
            return (parent != null);
        }

        /**
         * @return next visible TreePath.
         */
        public TreePath nextElement() {
            if(!hasMoreElements())
                throw new NoSuchElementException("No more visible paths");

            TreePath                retObject;

            if(nextIndex == -1) {
                retObject = parent.getTreePath();
            }
            else {
                TreeStateNode   node = (TreeStateNode)parent.
                                        getChildAt(nextIndex);

                retObject = node.getTreePath();
            }
            updateNextObject();
            return retObject;
        }

        /**
         * Determines the next object by invoking <code>updateNextIndex
         * and if not succesful <code>findNextValidParent.
         */
        protected void updateNextObject() {
            if(!updateNextIndex()) {
                findNextValidParent();
            }
        }

        /**
         * Finds the next valid parent, this should be called when nextIndex
         * is beyond the number of children of the current parent.
         */
        protected boolean findNextValidParent() {
            if(parent == root) {
                // mark as invalid!
                parent = null;
                return false;
            }
            while(parent != null) {
                TreeStateNode      newParent = (TreeStateNode)parent.
                                                  getParent();

                if(newParent != null) {
                    nextIndex = newParent.getIndex(parent);
                    parent = newParent;
                    childCount = parent.getChildCount();
                    if(updateNextIndex())
                        return true;
                }
                else
                    parent = null;
            }
            return false;
        }

        /**
         * Updates <code>nextIndex returning false if it is beyond
         * the number of children of parent.
         */
        protected boolean updateNextIndex() {
            // nextIndex == -1 identifies receiver, make sure is expanded
            // before descend.
            if(nextIndex == -1 && !parent.isExpanded())
                return false;

            // Check that it can have kids
            if(childCount == 0)
                return false;
            // Make sure next index not beyond child count.
            else if(++nextIndex >= childCount)
                return false;

            TreeStateNode       child = (TreeStateNode)parent.
                                        getChildAt(nextIndex);

            if(child != null && child.isExpanded()) {
                parent = child;
                nextIndex = -1;
                childCount = child.getChildCount();
            }
            return true;
        }
    } // VariableHeightLayoutCache.VisibleTreeStateNodeEnumeration
}

Other Java examples (source code examples)

Here is a short list of links related to this Java VariableHeightLayoutCache.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.