weka.gui.HierarchyPropertyParser Maven / Gradle / Ivy
Show all versions of weka-stable 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* HierarchyPropertyParser.java
* Copyright (C) 2001 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: 9917 $
*/
public class HierarchyPropertyParser
implements Serializable {
/** for serialization */
private static final long serialVersionUID = -4151103338506077544L;
/** Keep track of the root of the tree */
private 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{
/** 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(Messages.getInstance().getString("HierarchyPropertyParser_Build_Exception_Text"));
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 = (TreeNode)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] = (String)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) // Parent is leaf
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 = (TreeNode)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(((TreeNode)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 = (TreeNode)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 = (TreeNode)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 = (TreeNode)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) // Not root
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) // Leaf
return false;
int pos = search(m_Current.children, value);
if(pos == -1)
return false;
m_Current = (TreeNode)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(Messages.getInstance().getString("HierarchyPropertyParser_GoToChild_Exception_Text"));
m_Current = (TreeNode)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) // Leaf
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) // Leaf
return null;
else{
Vector kids = m_Current.children;
String[] values = new String[kids.size()];
for(int i=0; i < kids.size(); i++)
values[i] = ((TreeNode)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) // Not root
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((TreeNode)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(Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_First") + hpp.getSeperator());
System.out.println(Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_Second") + hpp.depth());
System.out.println(Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_Third") + hpp.showTree());
hpp.goToRoot();
System.out.println(Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_Fourth") + hpp.goTo("node1.node1_2.node1_2_1")
+": "+hpp.getValue()+" | "+hpp.fullValue()+
Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_Fifth") + hpp.isLeafReached());
System.out.println(Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_Sixth") + hpp.goDown("node1"));
System.out.println(Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_Seventh") + hpp.getValue());
System.out.println(Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_Eighth") + hpp.goToChild("node1_2_1_1")
+": "+hpp.getValue()+" | "+hpp.fullValue()+
Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_Nineth") + hpp.isLeafReached()
+ Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_Tenth") + hpp.isRootReached());
System.out.println(Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_Eleventh") + hpp.parentValue());
System.out.println(Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_Twelveth") + hpp.getLevel());
System.out.println(Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_Thirteenth") + hpp.context());
hpp.goToRoot();
System.out.println(Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_Fourteenth") + hpp.isLeafReached()
+ Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_Fifteenth") + hpp.isRootReached());
System.out.println(Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_Sixteenth") +
hpp.goDown("node1_1.node1_1_1")+
Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_Seventeenth") + hpp.getValue()+" | "+hpp.fullValue()
+ Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_Eighteenth") + hpp.getLevel()
+ Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_Nineteenth") + hpp.isLeafReached()
+ Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_Twenty") + hpp.isRootReached());
hpp.goToParent();
System.out.println(Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_TwentyFirst") + hpp.getValue()+" | "+hpp.fullValue());
System.out.println(Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_TwentySecond") + hpp.getLevel());
String[] chd = hpp.childrenValues();
for(int i=0; i < chd.length; i++){
System.out.print(Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_TwentyThird") + i + ": " + chd[i]);
hpp.goDown(chd[i]);
System.out.println(Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_TwentyFourth") + hpp.getValue()+" | "+
hpp.fullValue()+
Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_TwentyFifth") + hpp.getLevel()+Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_TwentySixth"));
hpp.goToParent();
}
System.out.println(Messages.getInstance().getString("HierarchyPropertyParser_Main_Text_TwentySeventh") + hpp.goTo("node1")
+": "+hpp.getValue()+" | "+hpp.fullValue());
}catch(Exception e){
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}