source: src/main/java/weka/gui/HierarchyPropertyParser.java @ 20

Last change on this file since 20 was 4, checked in by gnappo, 14 years ago

Import di weka.

File size: 18.2 KB
Line 
1/*
2 *    This program is free software; you can redistribute it and/or modify
3 *    it under the terms of the GNU General Public License as published by
4 *    the Free Software Foundation; either version 2 of the License, or
5 *    (at your option) any later version.
6 *
7 *    This program is distributed in the hope that it will be useful,
8 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
9 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 *    GNU General Public License for more details.
11 *
12 *    You should have received a copy of the GNU General Public License
13 *    along with this program; if not, write to the Free Software
14 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
15 */
16
17/*
18 *    HierarchyPropertyParser.java
19 *    Copyright (C) 2001 University of Waikato, Hamilton, New Zealand
20 *
21 */
22
23package weka.gui;
24
25import java.io.Serializable;
26import java.util.StringTokenizer;
27import java.util.Vector;
28
29/**
30 * This class implements a parser to read properties that have
31 * a hierarchy(i.e. tree) structure.  Conceptually it's similar to
32 * the XML DOM/SAX parser but of course is much simpler and
33 * uses dot as the seperator of levels instead of back-slash.<br>
34 * It provides interfaces to both build a parser tree and traverse
35 * the tree. <br>
36 * Note that this implementation does not lock the tree when different
37 * threads are traversing it simultaneously, i.e. it's NOT synchronized
38 * and multi-thread safe.  It is recommended that later implementation
39 * extending this class provide a locking scheme and override the
40 * functions with the "synchronized" modifier (most of them are
41 * goToXXX() and information accessing functions).<p>
42 *
43 * @author Xin Xu (xx5@cs.waikato.ac.nz)
44 * @version $Revision: 1.5 $
45 */
46public class HierarchyPropertyParser
47    implements Serializable {
48
49    /** for serialization */
50    private static final long serialVersionUID = -4151103338506077544L;
51
52    /** Keep track of the root of the tree */
53    private TreeNode m_Root;
54   
55    /** Keep track of the current node when traversing the tree */
56    private TreeNode m_Current;
57
58    /** The level separate in the path */
59    private String m_Seperator = ".";
60   
61    /** The depth of the tree */
62    private int m_Depth = 0;
63       
64    /**
65     * The inner class implementing a single tree node.
66     * All fields are made public simply for convenient access,
67     * Although a severe violation of OO Design principle.
68     */
69    private class TreeNode{
70        /** The parent of this node */
71        public TreeNode parent = null;
72       
73        /** The value of this node.  Always String  */
74        public String value = null;
75
76        /** The children of this node */
77        public Vector children = null; 
78
79        /** The level of this node */
80        public int level = 0;
81
82        /** The context of this node */
83        public String context = null;
84    }
85   
86    /** Default constructor */
87    public HierarchyPropertyParser(){
88        m_Root = new TreeNode();
89        m_Root.parent = null;
90        m_Root.children = new Vector();
91        goToRoot();
92    }
93   
94    /**
95     * Constructor that builds a tree from the given property with
96     * the given delimitor
97     *
98     * @param p the given property string
99     * @param delim the given dilimitor
100     */
101    public HierarchyPropertyParser(String p, String delim) throws Exception {
102        this();         
103        build(p, delim);
104    }
105   
106    /**
107     * Set the seperator between levels.  Default is dot.
108     *
109     * @param s the seperator symbol
110     */
111    public void setSeperator(String s){
112        m_Seperator = s;
113    }
114
115    /**
116     * Get the seperator between levels.  Default is dot.
117     *
118     * @return the seperator symbol
119     */
120    public String getSeperator(){ return m_Seperator; }
121   
122    /**
123     * Build a tree from the given property with the given delimitor
124     *
125     * @param p the given property
126     * @param delim the given delimitor
127     */
128    public void build(String p, String delim) throws Exception {
129        StringTokenizer st = new StringTokenizer(p, delim);
130        //System.err.println("delim: "+delim);
131        while(st.hasMoreTokens()){
132            String property = st.nextToken().trim();
133            if(!isHierachic(property))
134                throw new Exception("The given property is not in"+
135                                    "hierachy structure with seperators!");
136            add(property);         
137        }
138        goToRoot();
139    }
140
141    /**
142     * Add the given item of property to the tree
143     *
144     * @param property the given item
145     */ 
146    public synchronized void add(String property){
147        String[] values = tokenize(property);
148        if(m_Root.value == null)
149            m_Root.value = values[0];
150       
151        buildBranch(m_Root, values, 1);
152    }
153   
154    /**
155     * Private function to build one branch of the tree
156     * based on one property
157     *
158     * @param parent the parent of the node to be built
159     * @param values the value of one property
160     * @param lvl the level of the node to be built in the tree
161     */
162    private void buildBranch(TreeNode parent, String[] values, int lvl){
163        // Precondition: children is not null
164       
165        if(lvl == values.length){  // Parent is leaf
166            parent.children = null;
167            return;
168        }
169       
170        if(lvl > (m_Depth-1))
171            m_Depth = lvl+1;  // Depth starts from 1
172       
173        Vector kids = parent.children;
174        int index = search(kids, values[lvl]);
175        if(index != -1){
176            TreeNode newParent = (TreeNode)kids.elementAt(index);
177            if(newParent.children == null)
178                newParent.children = new Vector();
179            buildBranch(newParent, values, lvl+1);
180        }
181        else{
182            TreeNode added = new TreeNode();
183            added.parent = parent;
184            added.value = values[lvl];
185            added.children = new Vector();
186            added.level = lvl;
187            if(parent != m_Root)
188                added.context = parent.context + m_Seperator + parent.value;
189            else
190                added.context = parent.value;
191           
192            kids.addElement(added);
193            buildBranch(added, values, lvl+1);
194        }
195    }
196   
197    /**
198     * Tokenize the given string based on the seperator and
199     * put the tokens into an array of strings
200     *
201     * @param rawString the given string
202     * @return an array of strings
203     */
204    public String[] tokenize(String rawString){
205        Vector result = new Vector();
206        StringTokenizer tk = new StringTokenizer(rawString, m_Seperator);
207        while(tk.hasMoreTokens())
208            result.addElement(tk.nextToken());
209       
210        String[] newStrings = new String[result.size()];
211        for(int i=0; i < result.size(); i++)
212            newStrings[i] = (String)result.elementAt(i);
213       
214        return newStrings;
215    }
216   
217    /**
218     * Whether the HierarchyPropertyParser contains the given
219     * string
220     *
221     * @param string the given string
222     * @return whether contains
223     */
224    public boolean contains(String string){
225        String[] item = tokenize(string);
226        if(!item[0].equals(m_Root.value))
227            return false;
228       
229        return isContained(m_Root, item, 1);
230    }
231   
232    /**
233     * Private function to decide whether one level of one branch
234     * contains the relevant values
235     *
236     * @param parent the parent of the node to be searched
237     * @param values the value of one property
238     * @param lvl the level of the node in question
239     * @return whether this branch contains the corresponding values
240     */
241    private boolean isContained(TreeNode parent, String[] values, int lvl){
242        if(lvl == values.length)  // Parent is leaf
243            return true;
244        else if(lvl > values.length)
245            return false;
246        else{
247            Vector kids = parent.children;
248            int index = search(kids, values[lvl]);
249            if(index != -1){
250                TreeNode newParent = (TreeNode)kids.elementAt(index);
251                return isContained(newParent, values, lvl+1);
252            }
253            else
254                return false;
255        }
256    }
257   
258    /**
259     * Whether the given string has a hierachy structure with
260     * the seperators
261     *
262     * @param string the given string
263     */
264    public boolean isHierachic(String string){
265        int index = string.indexOf(m_Seperator); 
266        // Seperator not occur or first occurance at the end
267        if((index == (string.length()-1)) || (index == -1))
268            return false;
269       
270        return true;
271    }
272
273    /**
274     * Helper function to search for the given target string in a
275     * given vector in which the elements' value may hopefully is equal
276     * to the target.  If such elements are found the first index
277     * is returned, otherwise -1
278     *
279     * @param vct the given vector
280     * @param target the given target string
281     * @return the index of the found element, -1 if not found
282     */
283    public int search(Vector vct, String target){
284        if(vct == null)
285            return -1;
286       
287        for(int i=0; i < vct.size(); i++)
288            if(target.equals(((TreeNode)vct.elementAt(i)).value))
289                return i;
290       
291        return -1;
292    }
293
294    /**
295     * Go to a certain node of the tree according to the specified path
296     * Note that the path must be absolute path from the root.  <br>
297     * For relative path, see goDown(String path).
298     *
299     * @param path the given absolute path
300     * @return whether the path exists, if false the current position does
301     * not move
302     */
303    public synchronized boolean goTo(String path){
304        if(!isHierachic(path)){
305            if(m_Root.value.equals(path)){
306                goToRoot();
307                return true;
308            }
309            else
310                return false;
311        }
312       
313        TreeNode old = m_Current;
314        m_Current = new TreeNode(); 
315        goToRoot();
316        String[] nodes = tokenize(path);
317        if(!m_Current.value.equals(nodes[0]))
318            return false;
319
320        for(int i=1; i < nodes.length; i++){
321            int pos = search(m_Current.children, nodes[i]);
322            if(pos == -1){
323                m_Current = old;
324                return false;
325            }
326            m_Current = (TreeNode)m_Current.children.elementAt(pos);
327        }
328       
329        return true;
330    }
331
332    /**
333     * Go to a certain node of the tree down from the current node
334     * according to the specified relative path.  The path does not
335     * contain the value of current node
336     *
337     * @param path the given relative path
338     * @return whether the path exists, if false the current position does
339     * not move
340     */
341    public synchronized boolean goDown(String path){
342        if(!isHierachic(path))
343            return goToChild(path);
344
345        TreeNode old = m_Current;
346        m_Current = new TreeNode(); 
347        String[] nodes = tokenize(path);
348        int pos = search(old.children, nodes[0]);
349        if(pos == -1){
350            m_Current = old;
351            return false;
352        }
353     
354        m_Current = (TreeNode)old.children.elementAt(pos);
355        for(int i=1; i < nodes.length; i++){
356            pos = search(m_Current.children, nodes[i]);
357            if(pos == -1){
358                m_Current = old;
359                return false;
360            }
361           
362            m_Current = (TreeNode)m_Current.children.elementAt(pos);
363        }
364       
365        return true;
366    }
367
368    /**
369     * Go to the root of the tree
370     */   
371    public synchronized void goToRoot(){
372        m_Current=m_Root;
373    }
374
375    /**
376     * Go to the parent from the current position in the tree
377     * If the current position is the root, it stays there and does
378     * not move
379     */
380    public synchronized void goToParent(){
381        if(m_Current.parent != null)  // Not root
382            m_Current = m_Current.parent;
383    }
384   
385    /**
386     * Go to one child node from the current position in the tree
387     * according to the given value <br>
388     * If the child node with the given value cannot be found it
389     * returns false, true otherwise.  If false, the current position
390     * does not change
391     *
392     * @param value the value of the given child
393     * @return whether the child can be found
394     */
395    public synchronized boolean goToChild(String value){
396        if(m_Current.children == null) // Leaf
397            return false;
398       
399        int pos = search(m_Current.children, value);
400        if(pos == -1)
401            return false;
402       
403        m_Current = (TreeNode)m_Current.children.elementAt(pos);       
404        return true;
405    }
406   
407    /**
408     * Go to one child node from the current position in the tree
409     * according to the given position <br>
410     *
411     * @param pos the position of the given child
412     * @exception Exception if the position is out of range or leaf is reached
413     */
414    public synchronized void goToChild(int pos) throws Exception {
415        if((m_Current.children == null) || 
416           (pos < 0) || (pos >= m_Current.children.size()))
417            throw new Exception("Position out of range or leaf reached");
418       
419        m_Current = (TreeNode)m_Current.children.elementAt(pos);
420    }
421
422    /**
423     * The number of the children nodes. If current node is leaf, it
424     * returns 0.
425     *
426     * @return the number of the children nodes of the current position
427     */   
428    public synchronized int numChildren(){
429        if(m_Current.children == null) // Leaf
430            return 0;
431       
432        return m_Current.children.size(); 
433    }
434   
435    /**
436     * The value in the children nodes. If current node is leaf, it
437     * returns null.
438     *
439     * @return the value in the children nodes
440     */   
441    public synchronized String[] childrenValues(){
442        if(m_Current.children == null) // Leaf
443            return null;
444        else{
445            Vector kids = m_Current.children;
446            String[] values = new String[kids.size()];
447            for(int i=0; i < kids.size(); i++)
448                values[i] = ((TreeNode)kids.elementAt(i)).value;
449            return values;
450        }
451    }
452
453    /**
454     * The value in the parent node. If current node is root, it
455     * returns null.
456     *
457     * @return the value in the parent node
458     */   
459    public synchronized String parentValue(){
460        if(m_Current.parent != null)  // Not root
461            return m_Current.parent.value;
462        else return null;
463    }
464
465    /**
466     * Whether the current position is a leaf
467     *
468     * @return whether the current position is a leaf
469     */
470    public synchronized boolean isLeafReached(){
471        return (m_Current.children == null);
472    }
473   
474    /**
475     * Whether the current position is the root
476     *
477     * @return whether the current position is the root
478     */
479    public synchronized boolean isRootReached(){
480        return (m_Current.parent == null);
481    }
482   
483    /**
484     * Get the value of current node
485     *
486     * @return value level
487     */ 
488    public synchronized String getValue(){ return m_Current.value; }
489   
490    /**
491     * Get the level of current node.  Note the level starts from 0
492     *
493     * @return the level
494     */ 
495    public synchronized int getLevel(){ return m_Current.level; }
496
497    /**
498     * Get the depth of the tree, i.e. (the largest level)+1
499     *
500     * @return the depth of the tree
501     */
502    public int depth(){ return m_Depth; }
503   
504    /**
505     * The context of the current node, i.e. the path from the
506     * root to the parent node of the current node, seperated by
507     * the seperator.  If root, it returns null
508     *
509     * @return the context path
510     */
511    public synchronized String context(){
512        return m_Current.context;
513    }
514   
515    /**
516     * The full value of the current node, i.e. its context + seperator
517     * + its value.  For root, only its value.
518     *
519     * @return the context path
520     */
521    public synchronized String fullValue(){
522        if(m_Current == m_Root)
523            return m_Root.value;
524        else   
525            return (m_Current.context + m_Seperator + m_Current.value);
526    }
527
528
529    /**
530     * Show the whole tree in text format
531     *
532     * @return the whole tree in text format
533     */
534    public String showTree(){
535        return showNode(m_Root, null);
536    }
537   
538    /**
539     * Show one node of the tree in text format
540     *
541     * @param node the node in question
542     * @return the node in text format
543     */
544    private String showNode(TreeNode node, boolean[] hasBar){
545        StringBuffer text =  new StringBuffer();       
546
547        for(int i=0; i < (node.level - 1); i++)
548            if(hasBar[i])
549                text.append("  |       ");
550            else
551                text.append("          ");
552
553        if(node.level != 0)
554            text.append("  |------ ");
555        text.append(node.value+"("+node.level+")"+"["+node.context+"]\n");
556
557        if(node.children != null)
558            for(int i=0; i < node.children.size(); i++){
559                boolean[] newBar = new boolean[node.level+1];
560                int lvl = node.level;
561
562                if(hasBar != null)
563                    for(int j=0; j < lvl; j++)
564                        newBar[j] = hasBar[j];
565               
566                if((i == (node.children.size()-1)))
567                    newBar[lvl] = false;
568                else
569                    newBar[lvl] = true;
570
571                text.append(showNode((TreeNode)node.children.elementAt(i), newBar));           
572            }
573       
574        return text.toString();
575    }
576   
577    /**
578     * Tests out the parser.
579     *
580     * @param args should contain nothing
581     */
582    public static void main(String args[]){
583        StringBuffer sb = new StringBuffer();
584        sb.append("node1.node1_1.node1_1_1.node1_1_1_1, ");
585        sb.append("node1.node1_1.node1_1_1.node1_1_1_2, ");
586        sb.append("node1.node1_1.node1_1_1.node1_1_1_3, ");
587        sb.append("node1.node1_1.node1_1_2.node1_1_2_1, ");
588        sb.append("node1.node1_1.node1_1_3.node1_1_3_1, ");
589        sb.append("node1.node1_2.node1_2_1.node1_2_1_1, ");
590        sb.append("node1.node1_2.node1_2_3.node1_2_3_1, ");
591        sb.append("node1.node1_3.node1_3_3.node1_3_3_1, ");
592        sb.append("node1.node1_3.node1_3_3.node1_3_3_2, ");
593
594        String p = sb.toString();
595        try{
596            HierarchyPropertyParser hpp = new HierarchyPropertyParser(p, ", ");
597            System.out.println("seperator: "+hpp.getSeperator());
598            System.out.println("depth: "+hpp.depth());
599            System.out.println("The tree:\n\n"+hpp.showTree());
600            hpp.goToRoot();
601            System.out.println("goto: "+hpp.goTo("node1.node1_2.node1_2_1")
602                               +": "+hpp.getValue()+" | "+hpp.fullValue()+
603                               " leaf? "+ hpp.isLeafReached());
604            System.out.println("go down(wrong): "+hpp.goDown("node1"));
605            System.out.println("Stay still? "+hpp.getValue());
606            System.out.println("go to child: "+hpp.goToChild("node1_2_1_1")
607                               +": "+hpp.getValue()+" | "+hpp.fullValue()+
608                               " leaf? "+ hpp.isLeafReached()
609                               +" root? "+ hpp.isRootReached());
610            System.out.println("parent: "+hpp.parentValue());
611            System.out.println("level: "+hpp.getLevel());
612            System.out.println("context: "+hpp.context());
613            hpp.goToRoot();
614            System.out.println("After gotoRoot. leaf? "+ hpp.isLeafReached()
615                               +" root? "+ hpp.isRootReached());
616            System.out.println("Go down(correct): "+
617                               hpp.goDown("node1_1.node1_1_1")+
618                               " value: "+hpp.getValue()+" | "+hpp.fullValue()
619                               +" level: "+hpp.getLevel()
620                               +" leaf? "+ hpp.isLeafReached()
621                               +" root? "+ hpp.isRootReached());
622            hpp.goToParent();
623            System.out.println("value: "+hpp.getValue()+" | "+hpp.fullValue());
624            System.out.println("level: "+hpp.getLevel());
625           
626            String[] chd = hpp.childrenValues();
627            for(int i=0; i < chd.length; i++){
628                System.out.print("children "+i+": "+chd[i]);
629                hpp.goDown(chd[i]);
630                System.out.println("real value: "+hpp.getValue()+" | "+
631                                   hpp.fullValue()+
632                                   "(level: "+hpp.getLevel()+")");
633                hpp.goToParent();
634            }
635           
636            System.out.println("Another way to go to root:"+hpp.goTo("node1")
637                               +": "+hpp.getValue()+" | "+hpp.fullValue()); 
638        }catch(Exception e){
639            System.out.println(e.getMessage());
640            e.printStackTrace();
641        }
642    }
643}
Note: See TracBrowser for help on using the repository browser.