weka.gui.HierarchyPropertyParser.java Source code

Java tutorial

Introduction

Here is the source code for weka.gui.HierarchyPropertyParser.java

Source

/*
 *   This program 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.
 *
 *   This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 *    HierarchyPropertyParser.java
 *    Copyright (C) 2001-2012 University of Waikato, Hamilton, New Zealand
 *
 */

package weka.gui;

import java.io.Serializable;
import java.util.StringTokenizer;
import java.util.Vector;

/**
 * This class implements a parser to read properties that have a hierarchy(i.e.
 * tree) structure. Conceptually it's similar to the XML DOM/SAX parser but of
 * course is much simpler and uses dot as the seperator of levels instead of
 * back-slash.<br>
 * It provides interfaces to both build a parser tree and traverse the tree. <br>
 * Note that this implementation does not lock the tree when different threads
 * are traversing it simultaneously, i.e. it's NOT synchronized and multi-thread
 * safe. It is recommended that later implementation extending this class
 * provide a locking scheme and override the functions with the "synchronized"
 * modifier (most of them are goToXXX() and information accessing functions).
 * <p>
 * 
 * @author Xin Xu (xx5@cs.waikato.ac.nz)
 * @version $Revision$
 */
public class HierarchyPropertyParser implements Serializable {

    /** for serialization */
    private static final long serialVersionUID = -4151103338506077544L;

    /** Keep track of the root of the tree */
    private final TreeNode m_Root;

    /** Keep track of the current node when traversing the tree */
    private TreeNode m_Current;

    /** The level separate in the path */
    private String m_Seperator = ".";

    /** The depth of the tree */
    private int m_Depth = 0;

    /**
     * The inner class implementing a single tree node. All fields are made public
     * simply for convenient access, Although a severe violation of OO Design
     * principle.
     */
    private class TreeNode implements Serializable {

        /** For serialization */
        private static final long serialVersionUID = 6495148851062003641L;

        /** The parent of this node */
        public TreeNode parent = null;

        /** The value of this node. Always String */
        public String value = null;

        /** The children of this node */
        public Vector<TreeNode> children = null;

        /** The level of this node */
        public int level = 0;

        /** The context of this node */
        public String context = null;
    }

    /** Default constructor */
    public HierarchyPropertyParser() {
        m_Root = new TreeNode();
        m_Root.parent = null;
        m_Root.children = new Vector<TreeNode>();
        goToRoot();
    }

    /**
     * Constructor that builds a tree from the given property with the given
     * delimitor
     * 
     * @param p the given property string
     * @param delim the given dilimitor
     */
    public HierarchyPropertyParser(String p, String delim) throws Exception {
        this();
        build(p, delim);
    }

    /**
     * Set the seperator between levels. Default is dot.
     * 
     * @param s the seperator symbol
     */
    public void setSeperator(String s) {
        m_Seperator = s;
    }

    /**
     * Get the seperator between levels. Default is dot.
     * 
     * @return the seperator symbol
     */
    public String getSeperator() {
        return m_Seperator;
    }

    /**
     * Build a tree from the given property with the given delimitor
     * 
     * @param p the given property
     * @param delim the given delimitor
     */
    public void build(String p, String delim) throws Exception {
        StringTokenizer st = new StringTokenizer(p, delim);
        // System.err.println("delim: "+delim);
        while (st.hasMoreTokens()) {
            String property = st.nextToken().trim();
            if (!isHierachic(property)) {
                throw new Exception("The given property is not in" + "hierachy structure with seperators!");
            }
            add(property);
        }
        goToRoot();
    }

    /**
     * Add the given item of property to the tree
     * 
     * @param property the given item
     */
    public synchronized void add(String property) {
        String[] values = tokenize(property);
        if (m_Root.value == null) {
            m_Root.value = values[0];
        }

        buildBranch(m_Root, values, 1);
    }

    /**
     * Private function to build one branch of the tree based on one property
     * 
     * @param parent the parent of the node to be built
     * @param values the value of one property
     * @param lvl the level of the node to be built in the tree
     */
    private void buildBranch(TreeNode parent, String[] values, int lvl) {
        // Precondition: children is not null

        if (lvl == values.length) { // Parent is leaf
            parent.children = null;
            return;
        }

        if (lvl > (m_Depth - 1)) {
            m_Depth = lvl + 1; // Depth starts from 1
        }

        Vector<TreeNode> kids = parent.children;
        int index = search(kids, values[lvl]);
        if (index != -1) {
            TreeNode newParent = kids.elementAt(index);
            if (newParent.children == null) {
                newParent.children = new Vector<TreeNode>();
            }
            buildBranch(newParent, values, lvl + 1);
        } else {
            TreeNode added = new TreeNode();
            added.parent = parent;
            added.value = values[lvl];
            added.children = new Vector<TreeNode>();
            added.level = lvl;
            if (parent != m_Root) {
                added.context = parent.context + m_Seperator + parent.value;
            } else {
                added.context = parent.value;
            }

            kids.addElement(added);
            buildBranch(added, values, lvl + 1);
        }
    }

    /**
     * Tokenize the given string based on the seperator and put the tokens into an
     * array of strings
     * 
     * @param rawString the given string
     * @return an array of strings
     */
    public String[] tokenize(String rawString) {
        Vector<String> result = new Vector<String>();
        StringTokenizer tk = new StringTokenizer(rawString, m_Seperator);
        while (tk.hasMoreTokens()) {
            result.addElement(tk.nextToken());
        }

        String[] newStrings = new String[result.size()];
        for (int i = 0; i < result.size(); i++) {
            newStrings[i] = result.elementAt(i);
        }

        return newStrings;
    }

    /**
     * Whether the HierarchyPropertyParser contains the given string
     * 
     * @param string the given string
     * @return whether contains
     */
    public boolean contains(String string) {
        String[] item = tokenize(string);
        if (!item[0].equals(m_Root.value)) {
            return false;
        }

        return isContained(m_Root, item, 1);
    }

    /**
     * Private function to decide whether one level of one branch contains the
     * relevant values
     * 
     * @param parent the parent of the node to be searched
     * @param values the value of one property
     * @param lvl the level of the node in question
     * @return whether this branch contains the corresponding values
     */
    private boolean isContained(TreeNode parent, String[] values, int lvl) {
        if (lvl == values.length) {
            return true;
        } else if (lvl > values.length) {
            return false;
        } else {
            Vector<TreeNode> kids = parent.children;
            int index = search(kids, values[lvl]);
            if (index != -1) {
                TreeNode newParent = kids.elementAt(index);
                return isContained(newParent, values, lvl + 1);
            } else {
                return false;
            }
        }
    }

    /**
     * Whether the given string has a hierachy structure with the seperators
     * 
     * @param string the given string
     */
    public boolean isHierachic(String string) {
        int index = string.indexOf(m_Seperator);
        // Seperator not occur or first occurance at the end
        if ((index == (string.length() - 1)) || (index == -1)) {
            return false;
        }

        return true;
    }

    /**
     * Helper function to search for the given target string in a given vector in
     * which the elements' value may hopefully is equal to the target. If such
     * elements are found the first index is returned, otherwise -1
     * 
     * @param vct the given vector
     * @param target the given target string
     * @return the index of the found element, -1 if not found
     */
    public int search(Vector<TreeNode> vct, String target) {
        if (vct == null) {
            return -1;
        }

        for (int i = 0; i < vct.size(); i++) {
            if (target.equals(vct.elementAt(i).value)) {
                return i;
            }
        }

        return -1;
    }

    /**
     * Go to a certain node of the tree according to the specified path Note that
     * the path must be absolute path from the root. <br>
     * For relative path, see goDown(String path).
     * 
     * @param path the given absolute path
     * @return whether the path exists, if false the current position does not
     *         move
     */
    public synchronized boolean goTo(String path) {
        if (!isHierachic(path)) {
            if (m_Root.value.equals(path)) {
                goToRoot();
                return true;
            } else {
                return false;
            }
        }

        TreeNode old = m_Current;
        m_Current = new TreeNode();
        goToRoot();
        String[] nodes = tokenize(path);
        if (!m_Current.value.equals(nodes[0])) {
            return false;
        }

        for (int i = 1; i < nodes.length; i++) {
            int pos = search(m_Current.children, nodes[i]);
            if (pos == -1) {
                m_Current = old;
                return false;
            }
            m_Current = m_Current.children.elementAt(pos);
        }

        return true;
    }

    /**
     * Go to a certain node of the tree down from the current node according to
     * the specified relative path. The path does not contain the value of current
     * node
     * 
     * @param path the given relative path
     * @return whether the path exists, if false the current position does not
     *         move
     */
    public synchronized boolean goDown(String path) {
        if (!isHierachic(path)) {
            return goToChild(path);
        }

        TreeNode old = m_Current;
        m_Current = new TreeNode();
        String[] nodes = tokenize(path);
        int pos = search(old.children, nodes[0]);
        if (pos == -1) {
            m_Current = old;
            return false;
        }

        m_Current = old.children.elementAt(pos);
        for (int i = 1; i < nodes.length; i++) {
            pos = search(m_Current.children, nodes[i]);
            if (pos == -1) {
                m_Current = old;
                return false;
            }

            m_Current = m_Current.children.elementAt(pos);
        }

        return true;
    }

    /**
     * Go to the root of the tree
     */
    public synchronized void goToRoot() {
        m_Current = m_Root;
    }

    /**
     * Go to the parent from the current position in the tree If the current
     * position is the root, it stays there and does not move
     */
    public synchronized void goToParent() {
        if (m_Current.parent != null) {
            m_Current = m_Current.parent;
        }
    }

    /**
     * Go to one child node from the current position in the tree according to the
     * given value <br>
     * If the child node with the given value cannot be found it returns false,
     * true otherwise. If false, the current position does not change
     * 
     * @param value the value of the given child
     * @return whether the child can be found
     */
    public synchronized boolean goToChild(String value) {
        if (m_Current.children == null) {
            return false;
        }

        int pos = search(m_Current.children, value);
        if (pos == -1) {
            return false;
        }

        m_Current = m_Current.children.elementAt(pos);
        return true;
    }

    /**
     * Go to one child node from the current position in the tree according to the
     * given position <br>
     * 
     * @param pos the position of the given child
     * @exception Exception if the position is out of range or leaf is reached
     */
    public synchronized void goToChild(int pos) throws Exception {
        if ((m_Current.children == null) || (pos < 0) || (pos >= m_Current.children.size())) {
            throw new Exception("Position out of range or leaf reached");
        }

        m_Current = m_Current.children.elementAt(pos);
    }

    /**
     * The number of the children nodes. If current node is leaf, it returns 0.
     * 
     * @return the number of the children nodes of the current position
     */
    public synchronized int numChildren() {
        if (m_Current.children == null) {
            return 0;
        }

        return m_Current.children.size();
    }

    /**
     * The value in the children nodes. If current node is leaf, it returns null.
     * 
     * @return the value in the children nodes
     */
    public synchronized String[] childrenValues() {
        if (m_Current.children == null) {
            return null;
        } else {
            Vector<TreeNode> kids = m_Current.children;
            String[] values = new String[kids.size()];
            for (int i = 0; i < kids.size(); i++) {
                values[i] = kids.elementAt(i).value;
            }
            return values;
        }
    }

    /**
     * The value in the parent node. If current node is root, it returns null.
     * 
     * @return the value in the parent node
     */
    public synchronized String parentValue() {
        if (m_Current.parent != null) {
            return m_Current.parent.value;
        } else {
            return null;
        }
    }

    /**
     * Whether the current position is a leaf
     * 
     * @return whether the current position is a leaf
     */
    public synchronized boolean isLeafReached() {
        return (m_Current.children == null);
    }

    /**
     * Whether the current position is the root
     * 
     * @return whether the current position is the root
     */
    public synchronized boolean isRootReached() {
        return (m_Current.parent == null);
    }

    /**
     * Get the value of current node
     * 
     * @return value level
     */
    public synchronized String getValue() {
        return m_Current.value;
    }

    /**
     * Get the level of current node. Note the level starts from 0
     * 
     * @return the level
     */
    public synchronized int getLevel() {
        return m_Current.level;
    }

    /**
     * Get the depth of the tree, i.e. (the largest level)+1
     * 
     * @return the depth of the tree
     */
    public int depth() {
        return m_Depth;
    }

    /**
     * The context of the current node, i.e. the path from the root to the parent
     * node of the current node, seperated by the seperator. If root, it returns
     * null
     * 
     * @return the context path
     */
    public synchronized String context() {
        return m_Current.context;
    }

    /**
     * The full value of the current node, i.e. its context + seperator + its
     * value. For root, only its value.
     * 
     * @return the context path
     */
    public synchronized String fullValue() {
        if (m_Current == m_Root) {
            return m_Root.value;
        } else {
            return (m_Current.context + m_Seperator + m_Current.value);
        }
    }

    /**
     * Show the whole tree in text format
     * 
     * @return the whole tree in text format
     */
    public String showTree() {
        return showNode(m_Root, null);
    }

    /**
     * Show one node of the tree in text format
     * 
     * @param node the node in question
     * @return the node in text format
     */
    private String showNode(TreeNode node, boolean[] hasBar) {
        StringBuffer text = new StringBuffer();

        for (int i = 0; i < (node.level - 1); i++) {
            if (hasBar[i]) {
                text.append("  |       ");
            } else {
                text.append("          ");
            }
        }

        if (node.level != 0) {
            text.append("  |------ ");
        }
        text.append(node.value + "(" + node.level + ")" + "[" + node.context + "]\n");

        if (node.children != null) {
            for (int i = 0; i < node.children.size(); i++) {
                boolean[] newBar = new boolean[node.level + 1];
                int lvl = node.level;

                if (hasBar != null) {
                    for (int j = 0; j < lvl; j++) {
                        newBar[j] = hasBar[j];
                    }
                }

                if ((i == (node.children.size() - 1))) {
                    newBar[lvl] = false;
                } else {
                    newBar[lvl] = true;
                }

                text.append(showNode(node.children.elementAt(i), newBar));
            }
        }

        return text.toString();
    }

    /**
     * Tests out the parser.
     * 
     * @param args should contain nothing
     */
    public static void main(String args[]) {
        StringBuffer sb = new StringBuffer();
        sb.append("node1.node1_1.node1_1_1.node1_1_1_1, ");
        sb.append("node1.node1_1.node1_1_1.node1_1_1_2, ");
        sb.append("node1.node1_1.node1_1_1.node1_1_1_3, ");
        sb.append("node1.node1_1.node1_1_2.node1_1_2_1, ");
        sb.append("node1.node1_1.node1_1_3.node1_1_3_1, ");
        sb.append("node1.node1_2.node1_2_1.node1_2_1_1, ");
        sb.append("node1.node1_2.node1_2_3.node1_2_3_1, ");
        sb.append("node1.node1_3.node1_3_3.node1_3_3_1, ");
        sb.append("node1.node1_3.node1_3_3.node1_3_3_2, ");

        String p = sb.toString();
        try {
            HierarchyPropertyParser hpp = new HierarchyPropertyParser(p, ", ");
            System.out.println("seperator: " + hpp.getSeperator());
            System.out.println("depth: " + hpp.depth());
            System.out.println("The tree:\n\n" + hpp.showTree());
            hpp.goToRoot();
            System.out.println("goto: " + hpp.goTo("node1.node1_2.node1_2_1") + ": " + hpp.getValue() + " | "
                    + hpp.fullValue() + " leaf? " + hpp.isLeafReached());
            System.out.println("go down(wrong): " + hpp.goDown("node1"));
            System.out.println("Stay still? " + hpp.getValue());
            System.out.println("go to child: " + hpp.goToChild("node1_2_1_1") + ": " + hpp.getValue() + " | "
                    + hpp.fullValue() + " leaf? " + hpp.isLeafReached() + " root? " + hpp.isRootReached());
            System.out.println("parent: " + hpp.parentValue());
            System.out.println("level: " + hpp.getLevel());
            System.out.println("context: " + hpp.context());
            hpp.goToRoot();
            System.out.println("After gotoRoot. leaf? " + hpp.isLeafReached() + " root? " + hpp.isRootReached());
            System.out.println("Go down(correct): " + hpp.goDown("node1_1.node1_1_1") + " value: " + hpp.getValue()
                    + " | " + hpp.fullValue() + " level: " + hpp.getLevel() + " leaf? " + hpp.isLeafReached()
                    + " root? " + hpp.isRootReached());
            hpp.goToParent();
            System.out.println("value: " + hpp.getValue() + " | " + hpp.fullValue());
            System.out.println("level: " + hpp.getLevel());

            String[] chd = hpp.childrenValues();
            for (int i = 0; i < chd.length; i++) {
                System.out.print("children " + i + ": " + chd[i]);
                hpp.goDown(chd[i]);
                System.out.println("real value: " + hpp.getValue() + " | " + hpp.fullValue() + "(level: "
                        + hpp.getLevel() + ")");
                hpp.goToParent();
            }

            System.out.println("Another way to go to root:" + hpp.goTo("node1") + ": " + hpp.getValue() + " | "
                    + hpp.fullValue());
        } catch (Exception e) {
            System.out.println(e.getMessage());
            e.printStackTrace();
        }
    }
}