All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alee.extended.tree.ExTreeModel Maven / Gradle / Ivy

The newest version!
/*
 * This file is part of WebLookAndFeel library.
 *
 * WebLookAndFeel library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * WebLookAndFeel library 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 for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with WebLookAndFeel library.  If not, see .
 */

package com.alee.extended.tree;

import com.alee.laf.WebLookAndFeel;
import com.alee.laf.tree.*;
import com.alee.utils.CollectionUtils;
import com.alee.utils.compare.Filter;

import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import java.util.*;

/**
 * {@link WebTreeModel} extension that is based on data from {@link ExTreeDataProvider}.
 * All data is always instantly loaded based on the provided {@link ExTreeDataProvider} which allows sorting and filtering for all nodes.
 *
 * @param  {@link AsyncUniqueNode} type
 * @author Mikle Garin
 * @see WebExTree
 * @see ExTreeDataProvider
 */
public class ExTreeModel extends WebTreeModel implements FilterableNodes, SortableNodes
{
    /**
     * {@link ExTreeDataProvider} used by this model
     */
    protected final ExTreeDataProvider dataProvider;

    /**
     * Cache for children nodes returned by data provider (parent ID -> list of raw child nodes).
     * This map contains raw children which weren't affected by sorting and filtering operations.
     * If children needs to be re-sorted or re-filtered they are simply taken from the cache and re-organized once again.
     */
    protected transient Map> rawNodeChildrenCache;

    /**
     * Nodes cache.
     * Used for quick node search within the tree.
     */
    protected transient Map nodeById;

    /**
     * Nodes parent cache.
     * Used for node parent retrieval within the tree.
     */
    protected transient Map parentById;

    /**
     * {@link WebTree} that uses this model
     */
    protected transient WebTree tree;

    /**
     * Root node cache.
     * Cached when root is requested for the first time.
     */
    protected transient N rootNode;

    /**
     * {@link Filter} for {@link AsyncUniqueNode}s.
     */
    protected transient Filter filter;

    /**
     * {@link Comparator} for {@link AsyncUniqueNode}s.
     */
    protected transient Comparator comparator;

    /**
     * Constructs default ex tree model using custom data provider.
     *
     * @param dataProvider {@link ExTreeDataProvider} this model should be based on
     */
    public ExTreeModel ( final ExTreeDataProvider dataProvider )
    {
        super ( null );
        this.dataProvider = dataProvider;
    }

    /**
     * Returns {@link ExTreeDataProvider} used by this model.
     *
     * @return {@link ExTreeDataProvider} used by this model
     */
    public ExTreeDataProvider getDataProvider ()
    {
        return dataProvider;
    }

    /**
     * Installs this {@link ExTreeModel} into the specified {@link WebTree}.
     *
     * @param tree {@link WebTree}
     */
    public void install ( final WebTree tree )
    {
        WebLookAndFeel.checkEventDispatchThread ();
        this.rawNodeChildrenCache = new HashMap> ( 10 );
        this.nodeById = new HashMap ( 50 );
        this.parentById = new HashMap ( 50 );
        this.tree = tree;
        this.rootNode = loadRootNode ();
        loadTreeData ( getRootNode () );
    }

    /**
     * Uninstalls this {@link ExTreeModel} from the specified {@link WebTree}.
     *
     * @param tree {@link WebTree}
     */
    public void uninstall ( final WebTree tree )
    {
        WebLookAndFeel.checkEventDispatchThread ();
        this.rootNode = null;
        this.tree = null;
        this.parentById = null;
        this.nodeById = null;
        this.rawNodeChildrenCache = null;
    }

    /**
     * Returns whether or not this {@link ExTreeModel} is installed into some {@link WebTree}.
     *
     * @return {@code true} if this {@link ExTreeModel} is installed into some {@link WebTree}, {@code false} otherwise
     */
    public boolean isInstalled ()
    {
        return tree != null;
    }

    /**
     * Checks whether or not this {@link ExTreeModel} is installed into some {@link WebTree}.
     * If it is not installed - {@link IllegalStateException} is thrown to emphasize problem.
     */
    protected void checkInstalled ()
    {
        if ( !isInstalled () )
        {
            throw new IllegalStateException ( "This operation cannot be performed before model is installed into WebAsyncTree" );
        }
    }

    /**
     * Returns root node provided by {@link ExTreeDataProvider}.
     *
     * @return root node provided by {@link ExTreeDataProvider}
     */
    protected N loadRootNode ()
    {
        // Event Dispatch Thread check
        WebLookAndFeel.checkEventDispatchThread ();

        // Ensure model is installed
        checkInstalled ();

        // Retrieving root node
        final N rootNode = getDataProvider ().getRoot ();

        // Caching root node
        cacheNodeById ( rootNode );
        cacheParentId ( rootNode, null );

        return rootNode;
    }

    /**
     * Forces model to cache the whole structure so any node can be accessed right away.
     * Note that this might take some time in case tree structure is large as it will be fully loaded.
     * Though this doesn't force any repaints or other visual updates, so the speed depends only on ExTreeDataProvider.
     *
     * This method is mostly used to ensure that at any given time tree has all of its nodes.
     * That heavily simplifies work with the tree in case you need to access random nodes in the tree directly.
     * In case this is not your goal it is probably better to use {@link AsyncTreeModel}.
     *
     * @param parent node to load children for
     */
    protected void loadTreeData ( final N parent )
    {
        // Loading children
        final List children = getDataProvider ().getChildren ( parent );

        // Caching nodes
        setRawChildren ( parent, children );
        cacheNodesById ( children );
        cacheParentId ( children, parent.getId () );

        // Inserting loaded nodes if any of them are displayed
        final List displayedChildren = filterAndSort ( parent, children );
        if ( displayedChildren != null && displayedChildren.size () > 0 )
        {
            super.insertNodesInto ( displayedChildren, parent, 0 );
        }

        // Forcing child nodes to load their structures
        for ( final N child : children )
        {
            loadTreeData ( child );
        }
    }

    @Override
    public N getRoot ()
    {
        // Event Dispatch Thread check
        WebLookAndFeel.checkEventDispatchThread ();

        // Ensure model is installed
        checkInstalled ();

        // Returning root node
        return rootNode;
    }

    @Override
    public N getChild ( final Object parent, final int index )
    {
        // Event Dispatch Thread check
        WebLookAndFeel.checkEventDispatchThread ();

        // Ensure model is installed
        checkInstalled ();

        // Looking for child node
        return ( N ) super.getChild ( parent, index );
    }

    @Override
    public void reload ( final TreeNode node )
    {
        // Event Dispatch Thread check
        WebLookAndFeel.checkEventDispatchThread ();

        // Ensure model is installed
        checkInstalled ();

        final N reloadedNode = ( N ) node;

        // Cancels tree editing
        tree.cancelEditing ();

        // Cleaning up nodes cache
        clearRawChildren ( reloadedNode, false );

        // Removing all old children if such exist
        // We don't need to inform about child nodes removal here due to later structural update call
        reloadedNode.removeAllChildren ();

        // Forcing structure reload
        loadTreeData ( reloadedNode );

        // Forcing children reload
        super.reload ( reloadedNode );
    }

    @Override
    public void valueForPathChanged ( final TreePath path, final Object newValue )
    {
        // Ensure model is installed
        checkInstalled ();

        // Perform default operations
        super.valueForPathChanged ( path, newValue );

        // Updating filtering and sorting for parent of this node unless it is root node
        final N node = tree.getNodeForPath ( path );
        if ( node != null )
        {
            final WebTreeNode parent = node.getParent ();
            if ( parent != null )
            {
                filterAndSort ( ( N ) parent, false );
            }
        }
    }

    /**
     * Sets child nodes for the specified node.
     * This method might be used to manually change tree node children without causing any structure corruptions.
     *
     * @param parent   node to process
     * @param children new node children
     */
    public void setChildNodes ( final N parent, final List children )
    {
        removeNodesFromParent ( parent );
        addChildNodes ( parent, children );
    }

    /**
     * Adds child node for the specified node.
     * This method might be used to manually change tree node children without causing any structure corruptions.
     *
     * @param parent node to process
     * @param child  new node child
     */
    public void addChildNode ( final N parent, final N child )
    {
        insertNodeInto ( child, parent, getRawChildrenCount ( parent ) );
    }

    /**
     * Adds child nodes for the specified node.
     * This method might be used to manually change tree node children without causing any structure corruptions.
     *
     * @param parent   node to process
     * @param children new node children
     */
    public void addChildNodes ( final N parent, final List children )
    {
        insertNodesInto ( children, parent, getRawChildrenCount ( parent ) );
    }

    /**
     * Inserts new child node into parent node at the specified index.
     * This method might be used to manually change tree node children without causing any structure corruptions.
     *
     * @param child  new child node
     * @param parent parent node
     * @param index  insert index
     */
    @Override
    public void insertNodeInto ( final MutableTreeNode child, final MutableTreeNode parent, final int index )
    {
        // Event Dispatch Thread check
        WebLookAndFeel.checkEventDispatchThread ();

        // Ensure model is installed
        checkInstalled ();

        final N childNode = ( N ) child;
        final N parentNode = ( N ) parent;

        // Caching node
        addRawChild ( parentNode, childNode, index );
        cacheNodeById ( childNode );
        cacheParentId ( childNode, parentNode.getId () );

        // Clearing nodes children caches
        // That might be required in case nodes were moved inside of the tree
        clearRawChildren ( childNode, false );

        // Inserting node
        super.insertNodeInto ( childNode, parentNode, Math.min ( index, parentNode.getChildCount () ) );

        // Loading data for newly added node
        loadTreeData ( childNode );

        // Updating parent node sorting and filtering
        filterAndSort ( parentNode, false );
    }

    /**
     * Inserts a list of child nodes into parent node.
     * This method might be used to manually change tree node children without causing any structure corruptions.
     *
     * @param children list of new child nodes
     * @param parent   parent node
     * @param index    insert index
     */
    @Override
    public void insertNodesInto ( final List children, final N parent, final int index )
    {
        // Event Dispatch Thread check
        WebLookAndFeel.checkEventDispatchThread ();

        // Ensure model is installed
        checkInstalled ();

        // Caching nodes
        addRawChildren ( parent, children, index );
        cacheNodesById ( children );
        cacheParentId ( children, parent.getId () );

        // Clearing nodes children caches
        // That might be required in case nodes were moved inside of the tree
        clearRawChildren ( children, false );

        // Performing actual nodes insertion
        super.insertNodesInto ( children, parent, Math.min ( index, parent.getChildCount () ) );

        // Loading data for newly added nodes
        for ( final N child : children )
        {
            loadTreeData ( child );
        }

        // Updating parent node sorting and filtering
        filterAndSort ( parent, false );
    }

    /**
     * Inserts an array of child nodes into parent node.
     * This method might be used to manually change tree node children without causing any structure corruptions.
     *
     * @param children array of new child nodes
     * @param parent   parent node
     * @param index    insert index
     */
    @Override
    public void insertNodesInto ( final N[] children, final N parent, final int index )
    {
        // Event Dispatch Thread check
        WebLookAndFeel.checkEventDispatchThread ();

        // Ensure model is installed
        checkInstalled ();

        // Caching nodes
        addRawChildren ( parent, children, index );
        cacheNodesById ( children );
        cacheParentId ( children, parent.getId () );

        // Clearing nodes children caches
        // That might be required in case nodes were moved inside of the tree
        clearRawChildren ( children, false );

        // Inserting nodes
        super.insertNodesInto ( children, parent, Math.min ( index, parent.getChildCount () ) );

        // Loading data for newly added nodes
        for ( final N child : children )
        {
            loadTreeData ( child );
        }

        // Updating parent node sorting and filtering
        filterAndSort ( parent, false );
    }

    @Override
    public void removeNodeFromParent ( final MutableTreeNode node )
    {
        // Event Dispatch Thread check
        WebLookAndFeel.checkEventDispatchThread ();

        // Ensure model is installed
        checkInstalled ();

        final N child = ( N ) node;
        final N parent = findParent ( child.getId () );

        // Clearing nodes children caches
        removeRawChild ( parent, child );
        clearRawChildren ( child, true );

        // Removing node children so they won't mess up anything when we place node back into tree
        // This will basically strip node from unnecessary children which will be reloaded upon node addition into other ExTreeModel
        child.removeAllChildren ();

        // Removing actual node if it is needed, node might not be present in the tree due to filtering
        if ( child.getParent () == parent )
        {
            // Removing node from parent
            super.removeNodeFromParent ( node );
        }
    }

    @Override
    public void removeNodesFromParent ( final N parent )
    {
        // Event Dispatch Thread check
        WebLookAndFeel.checkEventDispatchThread ();

        // Ensure model is installed
        checkInstalled ();

        // Clearing node children caches
        clearRawChildren ( parent, false );

        // Removing node children
        super.removeNodesFromParent ( parent );
    }

    @Override
    public void removeNodesFromParent ( final N[] nodes )
    {
        // Event Dispatch Thread check
        WebLookAndFeel.checkEventDispatchThread ();

        // Ensure model is installed
        checkInstalled ();

        // Redirecting to another method
        removeNodesFromParent ( CollectionUtils.toList ( nodes ) );
    }

    @Override
    public void removeNodesFromParent ( final List nodes )
    {
        // Event Dispatch Thread check
        WebLookAndFeel.checkEventDispatchThread ();

        // Ensure model is installed
        checkInstalled ();

        // Removing node caches
        final List visible = new ArrayList ( nodes.size () );
        for ( final N child : nodes )
        {
            final N parent = findParent ( child.getId () );

            // Clearing nodes children caches
            removeRawChild ( parent, child );
            clearRawChildren ( child, true );

            // Removing node children so they won't mess up anything when we place node back into tree
            // This will basically strip node from unnecessary children which will be reloaded upon node addition into other ExTreeModel
            child.removeAllChildren ();

            // Saving nodes visible in the tree structure
            if ( child.getParent () == parent )
            {
                visible.add ( child );
            }
        }

        // Removing actual nodes if it is needed, nodes might not be present in the tree due to filtering
        super.removeNodesFromParent ( visible );
    }

    @Override
    public Filter getFilter ()
    {
        return filter;
    }

    @Override
    public void setFilter ( final Filter filter )
    {
        this.filter = filter;
        filter ();
    }

    @Override
    public void clearFilter ()
    {
        setFilter ( null );
    }

    @Override
    public void filter ()
    {
        filterAndSort ( true );
    }

    @Override
    public void filter ( final N node )
    {
        filterAndSort ( node, false );
    }

    @Override
    public void filter ( final N node, final boolean recursively )
    {
        filterAndSort ( node, recursively );
    }

    @Override
    public Comparator getComparator ()
    {
        return comparator;
    }

    @Override
    public void setComparator ( final Comparator comparator )
    {
        this.comparator = comparator;
        sort ();
    }

    @Override
    public void clearComparator ()
    {
        setComparator ( null );
    }

    @Override
    public void sort ()
    {
        filterAndSort ( true );
    }

    @Override
    public void sort ( final N node )
    {
        filterAndSort ( node, false );
    }

    @Override
    public void sort ( final N node, final boolean recursively )
    {
        filterAndSort ( node, recursively );
    }

    /**
     * Updates sorting and filtering for the root node children.
     *
     * @param recursively whether should update the whole children structure recursively or not
     */
    public void filterAndSort ( final boolean recursively )
    {
        filterAndSort ( null, recursively );
    }

    /**
     * Updates filtering and sorting for the specified {@link UniqueNode} children.
     *
     * @param parent      {@link UniqueNode} for which children filtering and sorting should be updated
     * @param recursively whether should update filtering and sorting for all {@link UniqueNode} children recursively
     */
    public void filterAndSort ( final N parent, final boolean recursively )
    {
        // Operation might have finished after model was removed from the tree
        if ( isInstalled () )
        {
            // Event Dispatch Thread check
            WebLookAndFeel.checkEventDispatchThread ();

            // Determining actual parent
            final N actualParent = parent != null ? parent : getRoot ();

            // Saving tree state to restore it right after children update
            final TreeState treeState = tree.getTreeState ( actualParent );

            // Updating root node children
            if ( recursively )
            {
                filterAndSortRecursively ( actualParent );
            }
            else
            {
                filterAndSort ( actualParent );
            }

            // Informing tree about possible major structure changes
            nodeStructureChanged ( actualParent );

            // Restoring tree state including all selections and expansions
            tree.setTreeState ( treeState, actualParent );
        }
    }

    /**
     * Updates filtering and sorting for the specified {@link UniqueNode} children recursively.
     *
     * @param parent {@link UniqueNode} for which children filtering and sorting should be updated
     */
    protected void filterAndSortRecursively ( final N parent )
    {
        // Filtering and sorting children of the specified parent first
        filterAndSort ( parent );

        // Now performing the same for each of the remaining children (not raw anymore to avoid unnecessary operations)
        for ( int i = 0; i < parent.getChildCount (); i++ )
        {
            filterAndSortRecursively ( ( N ) parent.getChildAt ( i ) );
        }
    }

    /**
     * Updates filtering and sorting for the specified {@link UniqueNode} children.
     *
     * @param parent {@link UniqueNode} for which children filtering and sorting should be updated
     */
    protected void filterAndSort ( final N parent )
    {
        // Removing old children
        parent.removeAllChildren ();

        // Filtering and sorting raw children
        final List children = getRawChildren ( parent );
        final List realChildren = filterAndSort ( parent, children );

        // Adding new children
        for ( final N child : realChildren )
        {
            parent.add ( child );
        }
    }

    /**
     * Returns {@link List} of filtered and sorted {@link UniqueNode}s.
     *
     * @param parent   {@link UniqueNode} for which children filtering and sorting should be updated
     * @param children {@link List} of {@link UniqueNode}s to filter and sort
     * @return {@link List} of filtered and sorted {@link UniqueNode}s
     */
    protected List filterAndSort ( final N parent, final List children )
    {
        final List result;
        if ( CollectionUtils.notEmpty ( children ) )
        {
            // Data provider
            final ExTreeDataProvider dataProvider = getDataProvider ();

            // Filtering children
            final Filter dataProviderFilter = dataProvider.getChildrenFilter ( parent, children );
            final Filter treeFilter = tree instanceof FilterableNodes ? ( ( FilterableNodes ) tree ).getFilter () : null;
            final Filter modelFilter = getFilter ();
            result = CollectionUtils.filter ( children, dataProviderFilter, treeFilter, modelFilter );

            // Sorting children
            final Comparator dataProviderComparator = dataProvider.getChildrenComparator ( parent, result );
            final Comparator treeComparator = tree instanceof SortableNodes ? ( ( SortableNodes ) tree ).getComparator () : null;
            final Comparator modelComparator = getComparator ();
            CollectionUtils.sort ( result, dataProviderComparator, treeComparator, modelComparator );
        }
        else
        {
            // Simply return an empty array if there is no children
            result = new ArrayList ( 0 );
        }
        return result;
    }

    /**
     * Returns {@link UniqueNode} with the specified identifier if it is in the model, {@code null} if it is not.
     *
     * @param nodeId {@link UniqueNode} identifier
     * @return {@link UniqueNode} with the specified identifier if it is in the model, {@code null} if it is not
     */
    public N findNode ( final String nodeId )
    {
        // Ensure model is installed
        checkInstalled ();

        // Get node from cache
        return nodeById.get ( nodeId );
    }

    /**
     * Returns raw parent for the specified {@link UniqueNode}.
     *
     * @param node {@link UniqueNode} to find raw parent for
     * @return raw parent for the specified {@link UniqueNode}
     */
    public N getRawParent ( final N node )
    {
        // Ensure model is installed
        checkInstalled ();

        // Find actual parent
        final N parent = ( N ) node.getParent ();
        return parent != null ? parent : findParent ( node.getId () );
    }

    /**
     * Returns raw children for the specified {@link UniqueNode}.
     *
     * @param parent {@link UniqueNode} to return raw children for
     * @return raw children for the specified {@link UniqueNode}
     */
    public List getRawChildren ( final N parent )
    {
        // Ensure model is installed
        checkInstalled ();

        // Get actual children from cache
        final List children = rawNodeChildrenCache.get ( parent.getId () );
        if ( children == null )
        {
            throw new RuntimeException ( "Raw children are not available for node: " + parent );
        }
        return children;
    }

    /**
     * Returns child {@link UniqueNode} at the specified index in parent {@link UniqueNode}.
     *
     * @param parent parent {@link UniqueNode}
     * @param index  child {@link UniqueNode} index
     * @return child {@link UniqueNode} at the specified index in parent {@link UniqueNode}
     */
    public N getRawChildAt ( final N parent, final int index )
    {
        // Ensure model is installed
        checkInstalled ();

        // Get actual child at specified index from cache
        final List children = rawNodeChildrenCache.get ( parent.getId () );
        if ( children == null )
        {
            throw new RuntimeException ( "Raw children are not available for node: " + parent );
        }
        return children.get ( index );
    }

    /**
     * Sets raw children for the {@link UniqueNode} with the specified identifier
     *
     * @param parent {@link UniqueNode} identifier to set raw children for
     * @param nodes  {@link List} of {@link UniqueNode}s to set as children
     */
    protected void setRawChildren ( final N parent, final List nodes )
    {
        rawNodeChildrenCache.put ( parent.getId (), nodes );
    }

    /**
     * Returns raw children count for the specified {@link UniqueNode}.
     *
     * @param parent {@link UniqueNode} to return raw children count for
     * @return raw children count for the specified {@link UniqueNode}
     */
    public int getRawChildrenCount ( final N parent )
    {
        return getRawChildren ( parent ).size ();
    }

    /**
     * Adds raw child to the specified {@link UniqueNode}.
     *
     * @param parent {@link UniqueNode} to add child to
     * @param node   {@link UniqueNode} child
     * @param index  index to add child at
     */
    protected void addRawChild ( final N parent, final N node, final int index )
    {
        getRawChildren ( parent ).add ( index, node );
    }

    /**
     * Adds raw childred to the specified {@link UniqueNode}.
     *
     * @param parent {@link UniqueNode} to add children to
     * @param nodes  {@link List} of {@link UniqueNode} children
     * @param index  index to add children at
     */
    protected void addRawChildren ( final N parent, final List nodes, final int index )
    {
        getRawChildren ( parent ).addAll ( index, nodes );
    }

    /**
     * Adds raw childred to the specified {@link UniqueNode}.
     *
     * @param parent {@link UniqueNode} to add children to
     * @param nodes  {@link List} of {@link UniqueNode} children
     * @param index  index to add children at
     */
    protected void addRawChildren ( final N parent, final N[] nodes, final int index )
    {
        final List cachedChildren = getRawChildren ( parent );
        for ( int i = nodes.length - 1; i >= 0; i-- )
        {
            cachedChildren.add ( index, nodes[ i ] );
        }
    }

    /**
     * Removes raw child from the specified {@link UniqueNode}.
     *
     * @param parent {@link UniqueNode} to remove child from
     * @param node   {@link UniqueNode} child to remove
     */
    protected void removeRawChild ( final N parent, final N node )
    {
        getRawChildren ( parent ).remove ( node );
    }

    /**
     * Clears node and all of its child nodes children cached states.
     *
     * @param node      node to clear cache for
     * @param clearNode whether should clear node cache or not
     */
    protected void clearRawChildren ( final N node, final boolean clearNode )
    {
        // Clears node cache
        if ( clearNode )
        {
            nodeById.remove ( node.getId () );
            parentById.remove ( node.getId () );
        }

        // Clears node raw children cache
        final List children = rawNodeChildrenCache.remove ( node.getId () );
        if ( CollectionUtils.notEmpty ( children ) )
        {
            clearRawChildren ( children, true );
        }
    }

    /**
     * Clears nodes children cached states.
     *
     * @param nodes      nodes to clear cache for
     * @param clearNodes whether should clear nodes cache or not
     */
    protected void clearRawChildren ( final List nodes, final boolean clearNodes )
    {
        for ( final N node : nodes )
        {
            clearRawChildren ( node, clearNodes );
        }
    }

    /**
     * Clears nodes children cached states.
     *
     * @param nodes      nodes to clear cache for
     * @param clearNodes whether should clear nodes cache or not
     */
    protected void clearRawChildren ( final N[] nodes, final boolean clearNodes )
    {
        for ( final N node : nodes )
        {
            clearRawChildren ( node, clearNodes );
        }
    }

    /**
     * Caches node by its IDs.
     *
     * @param node node to cache
     */
    protected void cacheNodeById ( final N node )
    {
        nodeById.put ( node.getId (), node );
    }

    /**
     * Caches nodes by their IDs.
     *
     * @param nodes list of nodes to cache
     */
    protected void cacheNodesById ( final List nodes )
    {
        for ( final N node : nodes )
        {
            cacheNodeById ( node );
        }
    }

    /**
     * Caches nodes by their IDs.
     *
     * @param nodes array of nodes to cache
     */
    protected void cacheNodesById ( final N[] nodes )
    {
        for ( final N node : nodes )
        {
            cacheNodeById ( node );
        }
    }

    /**
     * Returns parent of the {@link UniqueNode} with the specified identifier, {@code null} if it cannot be found.
     *
     * @param nodeId {@link UniqueNode} identifier
     * @return parent of the {@link UniqueNode} with the specified identifier, {@code null} if it cannot be found
     */
    public N findParent ( final String nodeId )
    {
        // Ensure model is installed
        checkInstalled ();

        // Get parent from cache
        final String parentId = parentById.get ( nodeId );
        return findNode ( parentId );
    }

    /**
     * Caches {@link UniqueNode} parent identifier.
     *
     * @param node     {@link UniqueNode}
     * @param parentId {@link UniqueNode} parent identifier
     */
    protected void cacheParentId ( final N node, final String parentId )
    {
        parentById.put ( node.getId (), parentId );
    }

    /**
     * Caches parent identifier for {@link List} of {@link UniqueNode}.
     *
     * @param nodes    {@link List} of {@link UniqueNode}s
     * @param parentId {@link UniqueNode} parent identifier
     */
    protected void cacheParentId ( final List nodes, final String parentId )
    {
        for ( final N node : nodes )
        {
            cacheParentId ( node, parentId );
        }
    }

    /**
     * Caches parent identifier for array of {@link UniqueNode}.
     *
     * @param nodes    array of {@link UniqueNode}s
     * @param parentId {@link UniqueNode} parent identifier
     */
    protected void cacheParentId ( final N[] nodes, final String parentId )
    {
        for ( final N node : nodes )
        {
            cacheParentId ( node, parentId );
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy