Package KFM

Source Code of KFM.KFM_HierarchyManager$KFM_HierarchyNode

/*
*  This software and supporting documentation were developed by
*
*    Siemens Corporate Technology
*    Competence Center Knowledge Management and Business Transformation
*    D-81730 Munich, Germany
*
*    Authors (representing a really great team ;-) )
*            Stefan B. Augustin, Thorbj�rn Hansen, Manfred Langen
*
*  This software is Open Source under GNU General Public License (GPL).
*  Read the text of this license in LICENSE.TXT
*  or look at www.opensource.org/licenses/
*
*  Once more we emphasize, that:
*  THIS SOFTWARE IS MADE AVAILABLE,  AS IS,  WITHOUT ANY WARRANTY
*  REGARDING  THE  SOFTWARE,  ITS  PERFORMANCE OR
*  FITNESS FOR ANY PARTICULAR USE, FREEDOM FROM ANY COMPUTER DISEASES OR
*  ITS CONFORMITY TO ANY SPECIFICATION. THE ENTIRE RISK AS TO QUALITY AND
*  PERFORMANCE OF THE SOFTWARE IS WITH THE USER.
*
*/


// KFM_HierarchyManager

// ************ package ******************************************************
package KFM;

// ************ imports ******************************************************

import KFM.Exceptions.*;

// java packages

import java.util.Hashtable;
import java.util.Vector;

/** Cache and navigation utility class for any hierarchical (tree-like) structure.
*
* <H2>Features</H2>
*
* <P> Objects of this class can store all or part of the nodes of a tree-like structure.
*     Each node is identified by a unique attribute (of class Object, maybe an Id or a name).
*     Each node also has a father node (of the same class).
*     Additionally, each node can have further attributes of any type.</P>
*
* <P> Class KFM_HierarchyManager offers methods to fill the hierarchy with nodes
*     and to navigate from a node upwards in the hierarchy. It does currently <B>not</B>
*     offer methods to navigate downwards a tree.</P>
*
* <H2> Usage </H2>
*
* <P> This class is used in SieMap to cache category pathes (to avoid repeated
*     SELECT statements to the Portal DB for categories where the path info is already fetched).
*     See Portal_ApplicationPage.constructPath() and PortalDb.getCategoryPath() </P>
*
* <P> For a further usage example, see method main(). </P>
*
* <H2> To do </H2>
*
* <P> The first version of this class only has methods that were needed in the first usage.
*     More methods come to mind, but I leave room for this class to grow :-) </P>
*
* <P> This method should be made threadsafe later. </P>
*
* @version 1.0 (2000 09 05)
*/
public class KFM_HierarchyManager
{
    // ************************************************************
    // Inner classes
    // ************************************************************

    /** KFM_HierarchyNode holds the information of a node of KFM_HierarchyManager,
     *  which is the identification of its father node and the list of its further attributes.
     *
     * @see KFM_HierarchyManager
     */
    static public class KFM_HierarchyNode {

        public Object mFatherId;
        public Hashtable mAttributes;
    }

    // ************************************************************
    // Variables
    // ************************************************************

    /**
     * The container for all the nodes of a KFM_HierarchyManager object.
     * Each key is the identification attribute of a node, each value is a KFM_HierarchyNode
     */
    protected Hashtable mNodeContainer;


    // ************************************************************
    // Methods
    // ************************************************************

    /**
     * Create an empty KFM_HierarchyManager.
     */
    public KFM_HierarchyManager ()
    {
        mNodeContainer = new Hashtable();
    }

    /**
     * Either create a new node with a given identification and father
     * or (if anId is already present) change the father of the given node.
     * If the node has no father, simply provide a null value.
     *
     * @param anId the identification attribute of the node
     * @param aFatherId the identification attribute of the father node (or null)
     */
    public void createOrSetNode (Object anId, Object aFatherId)
    {
        createOrSetNode(anId, aFatherId, new Hashtable());
    }

    /**
     * Either create a new node with a given identification, father and further attributes
     * or (if anId is already present) change father and further attributes of the given node.
     * If the node has no father or no attributes, simply provide null values.
     *
     * @param anId the identification attribute of the node
     * @param aFatherId the identification attribute of the father node (or null)
     * @param anAttributeList the list of named attributes of the node (or null),
     *                        the keys are Strings, the values may be of any type
     */
    public void createOrSetNode (Object anId, Object aFatherId, Hashtable anAttributeList)
    {
        KFM_HierarchyNode tNode = new KFM_HierarchyNode();
        tNode.mFatherId = aFatherId;
        tNode.mAttributes = anAttributeList;

        mNodeContainer.put(anId, tNode);
    }

    /**
     * Either create a new node with a given identification, father and one furher attribute
     * or (if anId is already present) change father and the further attribute of the given node.
     * If the node has no father, simply provide a null value.
     *
     * @param anId the identification attribute of the node
     * @param aFatherId the identification attribute of the father node (or null)
     * @param anAttribute the name of a further attribute of the node (not null)
     * @param aValue the value of a further attribute of the node (not null)
     */
    public void createOrSetNode (Object anId, Object anFatherId, String anAttribute, Object aValue)
    {
        Hashtable tAttributes = new Hashtable();
        tAttributes.put(anAttribute, aValue);
        createOrSetNode(anId, anFatherId, tAttributes);
    }

    /**
     * For a given node, return the identification attribute of the father node.
     *
     * @param anId the identification attribute of the node
     * @return the identification attribute of the father node (or null if the node has no father)
     * @exception KFM_NoSuchObjectException if no node is contained for anId
     */
    public Object getFatherNode (Object anId) throws KFM_NoSuchObjectException
    {
        Object tNode = mNodeContainer.get(anId);

        if (tNode == null)
            throw new KFM_NoSuchObjectException(anId);

        return ((KFM_HierarchyNode)tNode).mFatherId;
    }

    /**
     * For a given node, return the attribute list.
     *
     * @param anId the identification attribute of the node
     * @return the attribute list of the node (or null if the node has no attributes)
     * @exception KFM_NoSuchObjectException if no node is contained for anId
     */
    public Hashtable getAttributes (Object anId) throws KFM_NoSuchObjectException
    {
        Object tNode = mNodeContainer.get(anId);

        if (tNode == null)
            throw new KFM_NoSuchObjectException(anId);

        return ((KFM_HierarchyNode)tNode).mAttributes;
    }

    /**
     * For a given node, return the value of a specific attribute.
     *
     * @param anId the identification attribute of the node
     * @param aName the name of the specific attribute
     * @return the attribute value of the node (or null if the node does not have this attribute)
     * @exception KFM_NoSuchObjectException if no node is contained for anId
     */
    public Object getAttribute (Object anId, String aName) throws KFM_NoSuchObjectException
    {
        Hashtable tAttributes = getAttributes(anId);

        if (tAttributes == null)
            return null;

        return tAttributes.get(aName);
    }

    /**
     * For a given node, set the value of a specific attribute.
     *
     * @param anId the identification attribute of the node
     * @param aName the name of the specific attribute
     * @param aValue the value of the specific attribute
     * @exception KFM_NoSuchObjectException if no node is contained for anId
     */
    public void setAttribute (Object anId, String aName, Object aValue) throws KFM_NoSuchObjectException
    {
        Hashtable tAttributes = getAttributes(anId);
        tAttributes.put(aName, aValue);
    }

    /**
     * For a Vector of given nodes, return a Vector with the values of a specific attribute of these nodes.
     *
     * <P> Note: You can combine this with getIdPath() to get a specific attribute for a node and
     *     all its direct and indirect father nodes. Use it like this: </P>
     * <PRE>
     *    Vector tIds = mHierarchyMgr.getIdPath(tId, true, -1);
     *    Vector tTitles = mHierarchyMgr.getAttribute(tIds, "Name");
     * </PRE>
     *
     * @param anIdVector the Vector with the identification attribute of the nodes
     * @param aName the name of the specific attribute
     * @return the Vector of attribute values of the node (a Vector entry can also be null)
     * @exception KFM_NoSuchObjectException if no node is contained for one of the Ids in anIdVector
     */
    public Vector getAttribute (Vector anIdVector, String aName) throws KFM_NoSuchObjectException
    {
        Vector tResult = new Vector();

        // handle special case
        if (anIdVector == null)
            return tResult;

        Hashtable tAttributes;
        int tSize = anIdVector.size();
        for (int i=0; i<tSize; i++) {
            // May throw KFM_NoSuchNodeException
            tAttributes = getAttributes(anIdVector.elementAt(i));
            if (tAttributes == null) {
                tResult.addElement(null);
            } else {
                tResult.addElement(tAttributes.get(aName));
            }
        }

        return tResult;
    }

    /**
     * For a given node, return a Vector with the identification attributes of the node and all the
     * direct and indirect father nodes.
     * Note: this method does take care of infinite loops by stopping after a given number of steps.
     * Use the slower getIdPathLoopSafe() if you want an Exception to happen in case of loops.
     *
     * @param anId the identification attribute of the node
     * @param aNodeFirstFlag if true, the Vector is filled in that the node is the first element,
     *                       if false, the node will be the last element
     * @param aSizeLimit the maximum size of the resulting Vector (this can be used to avoid loops) or -1
     * @return the Vector with identification attributes of the node and all the father nodes
     * @exception KFM_NoSuchObjectException if no node is contained for anId (or any father node Id,
     *                                    in which case the tree is corrupted)
     */
    public Vector getIdPath (Object anId, boolean aNodeFirstFlag, int aSizeLimit)
                                                throws KFM_NoSuchObjectException
    {
        if (anId == null)
            throw new KFM_NoSuchObjectException();

        Vector tAllIds = new Vector();
        if (aSizeLimit == 0)
            return tAllIds;

        tAllIds.addElement(anId);

        // May throw KFM_NoSuchNodeException
        Object tCurrentFather = getFatherNode(anId);

        boolean tApplySizeLimit = (aSizeLimit >= 0);

        while ((tCurrentFather != null) && (!tApplySizeLimit || tAllIds.size() < aSizeLimit)) {
            if (aNodeFirstFlag) {
                // append next father at the end of the Vector
                tAllIds.addElement(tCurrentFather);
            } else {
                // insert next father at the beginning of the Vector
                tAllIds.insertElementAt(tCurrentFather, 0);
            }

            // May throw KFM_NoSuchNodeException
            tCurrentFather = getFatherNode(tCurrentFather);
        }

        return tAllIds;
    }

    /**
     * For a given node, return a Vector with the identification attributes of the node and
     * all the direct and indirect father nodes.
     * Note: this method does take care of infinite loops by checking the father chain
     * (two father nodes are considered to be the same, if they are equal(), not only ==).
     * Use the faster getIdPath() if you want to avoid loops by limiting the Vector size.
     *
     * @param anId the identification attribute of the node
     * @param aNodeFirstFlag if true, the Vector is filled in that the node is the first element,
     *                       if false, the node will be the last element
     * @return the Vector with identification attributes of the node and all the father nodes
     * @exception KFM_NoSuchObjectException if no node is contained for anId (or any father node Id,
     *                                    in which case the tree is corrupted)
     * @exception KFM_InfiniteLoopException if a loop within the father hierarchy was detected
     */
    public Vector getIdPathLoopSafe (Object anId, boolean aNodeFirstFlag)
                                        throws KFM_NoSuchObjectException, KFM_InfiniteLoopException
    {
        if (anId == null)
            throw new KFM_NoSuchObjectException();

        Vector tAllIds = new Vector();
        tAllIds.addElement(anId);

        // May throw KFM_NoSuchNodeException
        Object tCurrentFather = getFatherNode(anId);

        while (tCurrentFather != null) {
            // check for a loop
            for (int i = tAllIds.size()-1; i >= 0; i--) {
                if (tCurrentFather.equals(tAllIds.elementAt(i))) {
                    throw new KFM_InfiniteLoopException(anId);
                }
            }

            if (aNodeFirstFlag) {
                // append next father at the end of the Vector
                tAllIds.addElement(tCurrentFather);
            } else {
                // insert next father at the beginning of the Vector
                tAllIds.insertElementAt(tCurrentFather, 0);
            }

            // May throw KFM_NoSuchNodeException
            tCurrentFather = getFatherNode(tCurrentFather);
        }

        return tAllIds;
    }

    /**
     * Tests if the specified node is contained in the hierarchy.
     *
     * @param anId the identification attribute of the node
     * @return  <code>true</code> if the specified node is contained in the hierarchy;
     *          <code>false</code> otherwise.
     */
    public boolean containsNode(Object anId)
    {
        if (anId == null)
            return false;

        return mNodeContainer.containsKey(anId);
    }


    /**
     * Method for testing the functionality of KFM_HierarchyManager.
     */
    public static void main(String[] args)
    {
        System.out.println("Testing KFM_HierarchyManager.");
        System.out.println("=============================");

        KFM_HierarchyManager tManager = new KFM_HierarchyManager();
        // Create node 500; no father; on attribute "Title" with value "Yeah"
        tManager.createOrSetNode(new Long(500), null, "Title", "Yeah");
        System.out.println("Creating node 500 o.k.");

        // Create some further leave nodes, all with a Hashtable full of attributes
        // (for simplicity, we use the same Hashtable)
        Hashtable tAttributes = new Hashtable();
        tAttributes.put("Title", "Yogi");
        tAttributes.put("Summary", "A very dangerous bear");
        tManager.createOrSetNode(new Long(501), null, tAttributes);
        System.out.println("Creating node 501 o.k.");
        tManager.createOrSetNode(new Long(502), null, tAttributes);
        System.out.println("Creating node 502 o.k.");
        tManager.createOrSetNode(new Long(503), null, tAttributes);
        System.out.println("Creating node 503 o.k.");

        // Create two child nodes, all with a Hashtable full of attributes
        tManager.createOrSetNode(new Long(504), new Long(500), tAttributes);
        System.out.println("Creating node 504 as a child of node 500 o.k.");
        tManager.createOrSetNode(new Long(505), new Long(504), tAttributes);
        System.out.println("Creating node 505 as a child of node 504 o.k.");

        // Test if node 504 is contained
        if (tManager.containsNode(new Long(504))) {
            System.out.println("Testing for the presence of node 504; it is there");
        } else {
            System.out.println("Testing for the presence of node 504; oops, it is not there");
        }

        // Test if node 506 is contained
        if (tManager.containsNode(new Long(506))) {
            System.out.println("Testing for the presence of node 506; oops, it is there");
        } else {
            System.out.println("Testing for the presence of node 506; it is not there");
        }

        // Get all fathers of node 505 as a Vector, direct father first
        try {
            Vector tFathers = tManager.getIdPath(new Long(505), true, -1);
            System.out.println("All fathers of node 505 are:");
            // Print them
            for (int i=0; i< tFathers.size(); i++)
                System.out.println("    " + (Long) (tFathers.elementAt(i)));
        } catch (KFM_NoSuchObjectException ex) {
                System.out.println("Oops, caught KFM_NoSuchNodeException");
        }

        // Get all fathers of node 505 as a Vector, direct father last
        try {
            Vector tFathers = tManager.getIdPath(new Long(505), false, -1);
            System.out.println("All fathers of node 505 (reverse order) are:");
            // Print them
            for (int i=0; i< tFathers.size(); i++)
                System.out.println("    " + (Long) (tFathers.elementAt(i)));
        } catch (KFM_NoSuchObjectException ex) {
                System.out.println("Oops, caught KFM_NoSuchNodeException");
        }

        // Introduce a loop into the hierarchy
        tManager.createOrSetNode(new Long(500), new Long(505), tAttributes);
        System.out.println("Creating node 500 as a child of node 505 (this is a loop) o.k.");

        // Get all fathers of node 505 as a Vector, direct father first
        // This may result in an endless loop, so limit the result Vector
        try {
            Vector tFathers = tManager.getIdPath(new Long(505), true, 10);
            System.out.println("All fathers of node 505 are:");
            // Print them
            for (int i=0; i< tFathers.size(); i++)
                System.out.println("    " + (Long) (tFathers.elementAt(i)));
        } catch (KFM_NoSuchObjectException ex) {
                System.out.println("Oops, caught KFM_NoSuchNodeException");
        }

        // Now use getAllFathersLoopSafe() which must throw an KFM_HierarchyLoopException
        try {
            Vector tFathers = tManager.getIdPathLoopSafe(new Long(505), true);
            System.out.println("All fathers of node 505 are:");
            // Print them
            for (int i=0; i< tFathers.size(); i++)
                System.out.println("    " + (Long) (tFathers.elementAt(i)));
        } catch (KFM_NoSuchObjectException ex) {
                System.out.println("Oops, caught KFM_NoSuchNodeException");
        } catch (KFM_InfiniteLoopException ex) {
                System.out.println("Yes, we caught KFM_HierarchyLoopException");
        }

        // Get all attributes called "Title" from node 505 and its fathers
        try {
            // Careful, we still have the loop
            Vector tFathers = tManager.getIdPath(new Long(505), true, 10);
            Vector tTitles = tManager.getAttribute(tFathers, "Title");
            System.out.println("All titles of node 505 (and its fathers) are:");
            // Print them
            for (int i=0; i< tTitles.size(); i++)
                System.out.println("    " + (String) (tTitles.elementAt(i)));
        } catch (KFM_NoSuchObjectException ex) {
                System.out.println("Oops, caught KFM_NoSuchNodeException");
        }
    }
}
TOP

Related Classes of KFM.KFM_HierarchyManager$KFM_HierarchyNode

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.