Java tutorial
/* * 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(); } } }