weka.gui.HierarchyPropertyParser Maven / Gradle / Ivy
Show all versions of weka-dev Show documentation
/*
* 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 .
*/
/*
* 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.
* It provides interfaces to both build a parser tree and traverse the tree.
* 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).
*
*
* @author Xin Xu ([email protected])
* @version $Revision: 11269 $
*/
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 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();
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 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();
}
buildBranch(newParent, values, lvl + 1);
} else {
TreeNode added = new TreeNode();
added.parent = parent;
added.value = values[lvl];
added.children = new Vector();
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 result = new Vector();
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 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 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.
* 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
* 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
*
* @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 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();
}
}
}