source: branches/MetisMQI/src/main/java/weka/classifiers/trees/UserClassifier.java @ 38

Last change on this file since 38 was 29, checked in by gnappo, 14 years ago

Taggata versione per la demo e aggiunto branch.

File size: 44.9 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 *    UserClassifier.java
19 *    Copyright (C) 1999 University of Waikato, Hamilton, New Zealand
20 *
21 */
22
23package weka.classifiers.trees;
24
25import weka.classifiers.Classifier;
26import weka.classifiers.AbstractClassifier;
27import weka.classifiers.functions.LinearRegression;
28import weka.core.Capabilities;
29import weka.core.Drawable;
30import weka.core.FastVector;
31import weka.core.Instance;
32import weka.core.Instances;
33import weka.core.RevisionHandler;
34import weka.core.RevisionUtils;
35import weka.core.TechnicalInformation;
36import weka.core.TechnicalInformationHandler;
37import weka.core.Utils;
38import weka.core.Capabilities.Capability;
39import weka.core.TechnicalInformation.Field;
40import weka.core.TechnicalInformation.Type;
41import weka.filters.Filter;
42import weka.filters.unsupervised.attribute.Remove;
43import weka.gui.GenericObjectEditor;
44import weka.gui.PropertyDialog;
45import weka.gui.treevisualizer.PlaceNode1;
46import weka.gui.treevisualizer.PlaceNode2;
47import weka.gui.treevisualizer.TreeDisplayEvent;
48import weka.gui.treevisualizer.TreeDisplayListener;
49import weka.gui.treevisualizer.TreeVisualizer;
50import weka.gui.visualize.VisualizePanel;
51import weka.gui.visualize.VisualizePanelEvent;
52import weka.gui.visualize.VisualizePanelListener;
53
54import java.awt.Frame;
55import java.awt.event.ActionEvent;
56import java.awt.event.ActionListener;
57import java.awt.event.WindowAdapter;
58import java.awt.event.WindowEvent;
59import java.io.Serializable;
60
61import javax.swing.JFrame;
62import javax.swing.JOptionPane;
63import javax.swing.JTabbedPane;
64
65
66/**
67 <!-- globalinfo-start -->
68 * Interactively classify through visual means. You are Presented with a scatter graph of the data against two user selectable attributes, as well as a view of the decision tree. You can create binary splits by creating polygons around data plotted on the scatter graph, as well as by allowing another classifier to take over at points in the decision tree should you see fit.<br/>
69 * <br/>
70 * For more information see:<br/>
71 * <br/>
72 * Malcolm Ware, Eibe Frank, Geoffrey Holmes, Mark Hall, Ian H. Witten (2001). Interactive machine learning: letting users build classifiers. Int. J. Hum.-Comput. Stud.. 55(3):281-292.
73 * <p/>
74 <!-- globalinfo-end -->
75 *
76 <!-- technical-bibtex-start -->
77 * BibTeX:
78 * <pre>
79 * &#64;article{Ware2001,
80 *    author = {Malcolm Ware and Eibe Frank and Geoffrey Holmes and Mark Hall and Ian H. Witten},
81 *    journal = {Int. J. Hum.-Comput. Stud.},
82 *    number = {3},
83 *    pages = {281-292},
84 *    title = {Interactive machine learning: letting users build classifiers},
85 *    volume = {55},
86 *    year = {2001},
87 *    PS = {http://www.cs.waikato.ac.nz/\~ml/publications/2000/00MW-etal-Interactive-ML.ps}
88 * }
89 * </pre>
90 * <p/>
91 <!-- technical-bibtex-end -->
92 *
93 <!-- options-start -->
94 * Valid options are: <p/>
95 *
96 * <pre> -D
97 *  If set, classifier is run in debug mode and
98 *  may output additional info to the console</pre>
99 *
100 <!-- options-end -->
101 *
102 * @author Malcolm Ware (mfw4@cs.waikato.ac.nz)
103 * @version $Revision: 5987 $
104 */
105public class UserClassifier 
106  extends AbstractClassifier
107  implements Drawable, TreeDisplayListener, VisualizePanelListener,
108             TechnicalInformationHandler {
109 
110  /** for serialization */
111  static final long serialVersionUID = 6483901103562809843L;
112
113  /** I am not sure if these are strictly adhered to in visualizepanel
114   * so I am making them private to avoid confusion, (note that they will
115   * be correct in this class, VLINE and HLINE aren't used).
116   */
117  private static final int LEAF = 0;
118  private static final int RECTANGLE = 1;
119  private static final int POLYGON = 2;
120  private static final int POLYLINE = 3;
121  private static final int VLINE = 5;
122  private static final int HLINE =6;
123 
124
125  /** The tree display panel. */
126  private transient TreeVisualizer m_tView = null;
127  /** The instances display. */
128  private transient VisualizePanel m_iView = null;
129  /** Two references to the structure of the decision tree. */
130  private TreeClass m_top, m_focus;
131  /** The next number that can be used as a unique id for a node. */
132  private int m_nextId;
133  /** The tabbed window for the tree and instances view. */
134  private transient JTabbedPane m_reps;
135  /** The window. */
136  private transient JFrame m_mainWin;
137  /** The status of whether there is a decision tree ready or not. */
138  private boolean m_built=false;
139  /** A list of other m_classifiers. */
140  private GenericObjectEditor m_classifiers;
141  /** A window for selecting other classifiers. */
142  private PropertyDialog m_propertyDialog;
143
144  /** Register the property editors we need */
145  static {
146     GenericObjectEditor.registerEditors();
147  }
148
149  /**
150   * Main method for testing this class.
151   *
152   * @param argv should contain command line options (see setOptions)
153   */
154  public static void main(String [] argv) {
155    runClassifier(new UserClassifier(), argv);
156  }
157
158  /**
159   * @return a string that represents this objects tree.
160   */
161  public String toString() {
162    if (!m_built) {
163
164      return "Tree Not Built";
165    }
166    StringBuffer text = new StringBuffer();
167    try {
168      m_top.toString(0, text);
169     
170      m_top.objectStrings(text);
171
172    } catch(Exception e) {
173      System.out.println("error: " + e.getMessage());
174    }
175   
176    return text.toString();
177  }
178
179  /**
180   * Receives user choices from the tree view, and then deals with these
181   * choices.
182   * @param e The choice.
183   */
184  public void userCommand(TreeDisplayEvent e) {
185   
186    if (m_propertyDialog != null) {
187      m_propertyDialog.dispose();
188      m_propertyDialog = null;
189    }
190    try {
191      if (m_iView == null || m_tView == null) {
192        //throw exception
193      }
194      if (e.getCommand() == TreeDisplayEvent.NO_COMMAND) {
195        //do nothing
196      }
197      else if (e.getCommand() == TreeDisplayEvent.ADD_CHILDREN) {
198        //highlight the particular node and reset the vis panel
199        if (m_top == null) {
200          //this shouldn't happen , someone elses code would
201          //have to have added a trigger to this listener.
202          System.out.println("Error : Received event from a TreeDisplayer"
203                             + " that is unknown to the classifier.");
204        }
205        else {
206          m_tView.setHighlight(e.getID());
207          /*if (m_iView == null)
208            {
209            m_iView = new VisualizePanel(this);
210            m_iView.setSize(400, 300);
211            }*/
212          m_focus = m_top.getNode(e.getID());
213          m_iView.setInstances(m_focus.m_training);
214          if (m_focus.m_attrib1 >= 0) {
215            m_iView.setXIndex(m_focus.m_attrib1);
216          }
217          if (m_focus.m_attrib2 >= 0) {
218            m_iView.setYIndex(m_focus.m_attrib2);
219          }
220          m_iView.setColourIndex(m_focus.m_training.classIndex());
221          if (((Double)((FastVector)m_focus.m_ranges.elementAt(0)).
222               elementAt(0)).intValue() != LEAF) {
223            m_iView.setShapes(m_focus.m_ranges);
224          }
225          //m_iView.setSIndex(2);
226        }
227      }
228      else if (e.getCommand() == TreeDisplayEvent.REMOVE_CHILDREN) {
229        /*if (m_iView == null)
230          {
231          m_iView = new VisualizePanel(this);
232          m_iView.setSize(400, 300);
233          }*/
234        m_focus = m_top.getNode(e.getID());
235        m_iView.setInstances(m_focus.m_training);
236        if (m_focus.m_attrib1 >= 0) {
237          m_iView.setXIndex(m_focus.m_attrib1);
238        }
239        if (m_focus.m_attrib2 >= 0) {
240          m_iView.setYIndex(m_focus.m_attrib2);
241        }
242        m_iView.setColourIndex(m_focus.m_training.classIndex());
243        if (((Double)((FastVector)m_focus.m_ranges.elementAt(0)).
244             elementAt(0)).intValue() != LEAF) {
245          m_iView.setShapes(m_focus.m_ranges);
246        }
247        //m_iView.setSIndex(2);
248        //now to remove all the stuff
249        m_focus.m_set1 = null;
250        m_focus.m_set2 = null;
251        m_focus.setInfo(m_focus.m_attrib1, m_focus.m_attrib2, null);
252        //tree_frame.getContentPane().removeAll();
253        m_tView = new TreeVisualizer(this, graph(), new PlaceNode2());
254        //tree_frame.getContentPane().add(m_tView);
255        m_reps.setComponentAt(0, m_tView);
256        //tree_frame.getContentPane().doLayout();
257        m_tView.setHighlight(m_focus.m_identity);
258      }
259      else if (e.getCommand() == TreeDisplayEvent.CLASSIFY_CHILD) {
260        /*if (m_iView == null)
261          {
262          m_iView = new VisualizePanel(this);
263          m_iView.setSize(400, 300);
264          }*/
265        m_focus = m_top.getNode(e.getID());
266        m_iView.setInstances(m_focus.m_training);
267        if (m_focus.m_attrib1 >= 0) {
268          m_iView.setXIndex(m_focus.m_attrib1);
269        }
270        if (m_focus.m_attrib2 >= 0) {
271          m_iView.setYIndex(m_focus.m_attrib2);
272        }
273        m_iView.setColourIndex(m_focus.m_training.classIndex());
274        if (((Double)((FastVector)m_focus.m_ranges.elementAt(0)).
275             elementAt(0)).intValue() != LEAF) {
276          m_iView.setShapes(m_focus.m_ranges);
277        }
278       
279        Classifier classifierAtNode = m_focus.getClassifier();
280        if (classifierAtNode != null) {
281          m_classifiers.setValue(classifierAtNode);
282        }
283        m_propertyDialog = new PropertyDialog((Frame) null, m_classifiers, 
284                                              m_mainWin.getLocationOnScreen().x,
285                                              m_mainWin.getLocationOnScreen().y);
286        m_propertyDialog.setVisible(true);
287       
288        //note property dialog may change all the time
289        //but the generic editor which has the listeners does not
290        //so at the construction of the editor is when I am going to add
291        //the listeners.
292       
293       
294       
295        //focus.setClassifier(new IB1());
296        //tree_frame.getContentPane().removeAll();
297        //////m_tView = new Displayer(this, graph(), new PlaceNode2());
298        //tree_frame.getContentPane().add(m_tView);
299        //tree_frame.getContentPane().doLayout();
300        /////////////reps.setComponentAt(0, m_tView);
301        m_tView.setHighlight(m_focus.m_identity);
302      }
303      /*else if (e.getCommand() == e.SEND_INSTANCES) {
304        TreeClass source = m_top.getNode(e.getID());
305        m_iView.setExtInstances(source.m_training);
306        }*/
307      else if (e.getCommand() == TreeDisplayEvent.ACCEPT) {
308       
309        int well = JOptionPane.showConfirmDialog(m_mainWin, 
310                                                 "Are You Sure...\n"
311                                                 + "Click Yes To Accept The"
312                                                 + " Tree" 
313                                                 + "\n Click No To Return",
314                                                 "Accept Tree", 
315                                                 JOptionPane.YES_NO_OPTION);
316       
317        if (well == 0) {
318          m_mainWin.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
319          m_mainWin.dispose();
320          blocker(false);  //release the thread waiting at blocker to
321          //continue.
322        }
323       
324      }
325    } catch(Exception er) {
326      System.out.println("Error : " + er);
327      System.out.println("Part of user input so had to catch here");
328      er.printStackTrace();
329    }
330  }
331
332  /**
333   * This receives shapes from the data view.
334   * It then enters these shapes into the decision tree structure.
335   * @param e Contains the shapes, and other info.
336   */
337  public void userDataEvent(VisualizePanelEvent e) {
338   
339    if (m_propertyDialog != null) {
340      m_propertyDialog.dispose();
341      m_propertyDialog = null;
342    }
343   
344    try {
345      if (m_focus != null) {
346       
347
348        double wdom = e.getInstances1().numInstances() 
349          + e.getInstances2().numInstances();
350        if (wdom == 0) {
351          wdom = 1;
352        }
353       
354        TreeClass tmp = m_focus;
355        m_focus.m_set1 = new TreeClass(null, e.getAttribute1(), 
356                                       e.getAttribute2(), m_nextId, 
357                                       e.getInstances1().numInstances() / wdom,
358                                       e.getInstances1(), m_focus);
359       
360        m_focus.m_set2 = new TreeClass(null, e.getAttribute1(), 
361                                       e.getAttribute2(), m_nextId, 
362                                       e.getInstances2().numInstances() / wdom,
363                                       e.getInstances2(), m_focus); 
364        //this needs the other instance
365       
366       
367        //tree_frame.getContentPane().removeAll(); 
368        m_focus.setInfo(e.getAttribute1(), e.getAttribute2(), e.getValues());
369        //System.out.println(graph());
370        m_tView = new TreeVisualizer(this, graph(), new PlaceNode2());
371        //tree_frame.getContentPane().add(m_tView);
372        //tree_frame.getContentPane().doLayout();
373        m_reps.setComponentAt(0, m_tView);
374       
375        m_focus = m_focus.m_set2;
376        m_tView.setHighlight(m_focus.m_identity);
377        m_iView.setInstances(m_focus.m_training);
378        if (tmp.m_attrib1 >= 0) {
379          m_iView.setXIndex(tmp.m_attrib1);
380        }
381        if (tmp.m_attrib2 >= 0) {
382          m_iView.setYIndex(tmp.m_attrib2);
383        }
384        m_iView.setColourIndex(m_focus.m_training.classIndex());
385        if (((Double)((FastVector)m_focus.m_ranges.elementAt(0)).
386             elementAt(0)).intValue() != LEAF) {
387          m_iView.setShapes(m_focus.m_ranges);
388        }
389        //m_iView.setSIndex(2);
390      }
391      else {
392        System.out.println("Somehow the focus is null");
393      }
394    } catch(Exception er) {
395      System.out.println("Error : " + er);
396      System.out.println("Part of user input so had to catch here");
397      //er.printStackTrace();
398    }
399   
400  }
401 
402  /**
403   * Constructor
404   */
405  public UserClassifier() {
406    //do nothing here except set alot of variables to default values
407    m_top = null;
408    m_tView = null;
409    m_iView = null;
410    m_nextId = 0; 
411   
412  }
413 
414 /**
415   *  Returns the type of graph this classifier
416   *  represents.
417   *  @return Drawable.TREE
418   */   
419  public int graphType() {
420      return Drawable.TREE;
421  }
422
423  /**
424   * @return A string formatted with a dotty representation of the decision
425   * tree.
426   * @throws Exception if String can't be built properly.
427   */
428  public String graph() throws Exception {
429    //create a dotty rep of the tree from here
430    StringBuffer text = new StringBuffer();
431    text.append("digraph UserClassifierTree {\n" +
432                "node [fontsize=10]\n" +
433                "edge [fontsize=10 style=bold]\n");
434   
435    m_top.toDotty(text);
436    return text.toString() +"}\n";
437   
438   
439  }
440 
441  /**
442   * A function used to stop the code that called buildclassifier
443   * from continuing on before the user has finished the decision tree.
444   * @param tf True to stop the thread, False to release the thread that is
445   * waiting there (if one).
446   */
447  private synchronized void blocker(boolean tf) {
448    if (tf) {
449      try {
450        wait();
451      } catch(InterruptedException e) {
452      }
453    }
454    else {
455      notifyAll();
456    }
457   
458    //System.out.println("out");
459  }
460
461  /**
462   * This will return a string describing the classifier.
463   * @return The string.
464   */
465  public String globalInfo() {
466
467    return "Interactively classify through visual means."
468      + " You are Presented with a scatter graph of the data against two user"
469      + " selectable attributes, as well as a view of the decision tree."
470      + " You can create binary splits by creating polygons around data"
471      + " plotted on the scatter graph, as well as by allowing another"
472      + " classifier to take over at points in the decision tree should you"
473      + " see fit.\n\n"
474      + "For more information see:\n\n"
475      + getTechnicalInformation().toString();
476  }
477
478  /**
479   * Returns an instance of a TechnicalInformation object, containing
480   * detailed information about the technical background of this class,
481   * e.g., paper reference or book this class is based on.
482   *
483   * @return the technical information about this class
484   */
485  public TechnicalInformation getTechnicalInformation() {
486    TechnicalInformation        result;
487   
488    result = new TechnicalInformation(Type.ARTICLE);
489    result.setValue(Field.AUTHOR, "Malcolm Ware and Eibe Frank and Geoffrey Holmes and Mark Hall and Ian H. Witten");
490    result.setValue(Field.YEAR, "2001");
491    result.setValue(Field.TITLE, "Interactive machine learning: letting users build classifiers");
492    result.setValue(Field.JOURNAL, "Int. J. Hum.-Comput. Stud.");
493    result.setValue(Field.VOLUME, "55");
494    result.setValue(Field.NUMBER, "3");
495    result.setValue(Field.PAGES, "281-292");
496    result.setValue(Field.PS, "http://www.cs.waikato.ac.nz/~ml/publications/2000/00MW-etal-Interactive-ML.ps");
497   
498    return result;
499  }
500
501  /**
502   * Returns default capabilities of the classifier.
503   *
504   * @return      the capabilities of this classifier
505   */
506  public Capabilities getCapabilities() {
507    Capabilities result = super.getCapabilities();
508    result.disableAll();
509
510    // attributes
511    result.enable(Capability.NOMINAL_ATTRIBUTES);
512    result.enable(Capability.NUMERIC_ATTRIBUTES);
513    result.enable(Capability.DATE_ATTRIBUTES);
514    result.enable(Capability.STRING_ATTRIBUTES);
515    result.enable(Capability.RELATIONAL_ATTRIBUTES);
516    result.enable(Capability.MISSING_VALUES);
517
518    // class
519    result.enable(Capability.NOMINAL_CLASS);
520    result.enable(Capability.NUMERIC_CLASS);
521    result.enable(Capability.DATE_CLASS);
522    result.enable(Capability.MISSING_CLASS_VALUES);
523
524    // instances
525    result.setMinimumNumberInstances(0);
526   
527    return result;
528  }
529
530  /**
531   * Call this function to build a decision tree for the training
532   * data provided.
533   * @param i The training data.
534   * @throws Exception if can't build classification properly.
535   */
536  public void buildClassifier(Instances i) throws Exception {
537    // can classifier handle the data?
538    getCapabilities().testWithFail(i);
539
540    // remove instances with missing class
541    i = new Instances(i);
542    i.deleteWithMissingClass();
543   
544    //construct a visualizer
545    //construct a tree displayer and feed both then do nothing
546    //note that I will display at the bottom of each split how many
547    //fall into each catagory
548   
549    m_classifiers = new GenericObjectEditor(true);
550    m_classifiers.setClassType(Classifier.class);
551    m_classifiers.setValue(new weka.classifiers.rules.ZeroR());
552   
553    ((GenericObjectEditor.GOEPanel)m_classifiers.getCustomEditor())
554      .addOkListener(new ActionListener() {
555          public void actionPerformed(ActionEvent e) {
556            //I want to use the focus variable but to trust it I need
557            //to kill the window if anything gets changed by either
558            //editor
559            try {
560              m_focus.m_set1 = null;
561              m_focus.m_set2 = null;
562              m_focus.setInfo(m_focus.m_attrib1, m_focus.m_attrib2, null);
563              m_focus.setClassifier((Classifier)m_classifiers.getValue());
564              /*              m_classifiers = new GenericObjectEditor();
565              m_classifiers.setClassType(Classifier.class);
566              m_classifiers.setValue(new weka.classifiers.rules.ZeroR());
567              ((GenericObjectEditor.GOEPanel)m_classifiers.getCustomEditor())
568              .addOkListener(this); */
569              m_tView = new TreeVisualizer(UserClassifier.this, graph(), 
570                                     new PlaceNode2());
571              m_tView.setHighlight(m_focus.m_identity);
572              m_reps.setComponentAt(0, m_tView);
573              m_iView.setShapes(null);
574            } catch(Exception er) {
575              System.out.println("Error : " + er);
576              System.out.println("Part of user input so had to catch here");
577              JOptionPane.showMessageDialog(
578                         null,
579                         "Unable to use " + m_focus.getClassifier().getClass().getName() 
580                         + " at this node.\n"
581                         + "This exception was produced:\n"
582                         + er.toString(),
583                         "UserClassifier",
584                         JOptionPane.ERROR_MESSAGE);
585            }
586          }
587        });
588   
589    m_built = false;
590    m_mainWin = new JFrame();
591   
592    m_mainWin.addWindowListener(new WindowAdapter() {
593        public void windowClosing(WindowEvent e) {
594          int well = JOptionPane.showConfirmDialog(m_mainWin, 
595                                                   "Are You Sure...\n"
596                                                   + "Click Yes To Accept"
597                                                   + " The Tree" 
598                                                   + "\n Click No To Return",
599                                                   "Accept Tree", 
600                                                   JOptionPane.YES_NO_OPTION);
601         
602          if (well == 0) {
603            m_mainWin.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
604            blocker(false);
605           
606          }
607          else {
608            m_mainWin.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
609          }
610        }
611      });
612   
613    m_reps = new JTabbedPane();
614    m_mainWin.getContentPane().add(m_reps);
615   
616    //make a backup of the instances so that any changes don't go past here.
617    Instances te = new Instances(i, i.numInstances());
618    for (int noa = 0; noa < i.numInstances(); noa++) {
619      te.add(i.instance(noa));
620    }
621   
622    te.deleteWithMissingClass(); //remove all instances with a missing class
623    //from training
624   
625    m_top = new TreeClass(null, 0, 0, m_nextId, 1, te, null);
626    m_focus = m_top;
627    //System.out.println(graph());
628    m_tView = new TreeVisualizer(this, graph(), new PlaceNode1());
629   
630    m_reps.add("Tree Visualizer", m_tView);
631    //tree_frame = new JFrame();
632    //tree_frame.getContentPane().add(m_tView);
633    //tree_frame.setSize(800,600);
634    //tree_frame.setVisible(true);
635   
636    m_tView.setHighlight(m_top.m_identity);
637    m_iView = new VisualizePanel(this);
638    //m_iView.setSize(400, 300);
639    m_iView.setInstances(m_top.m_training);
640    m_iView.setColourIndex(te.classIndex());
641    //vis_frame = new JFrame();
642    //vis_frame.getContentPane().add(m_iView);
643    //vis_frame.setSize(400, 300);
644    //vis_frame.setVisible(true);
645    m_reps.add("Data Visualizer", m_iView);
646    m_mainWin.setSize(560, 420);
647    m_mainWin.setVisible(true);
648    blocker(true);          //a call so that the main thread of
649    //execution has to wait for the all clear message from the user.
650   
651    //so that it can be garbage
652    if (m_propertyDialog != null) {
653      m_propertyDialog.dispose();
654      m_propertyDialog = null;
655    }
656   
657    //collected
658    m_classifiers = null;
659    m_built = true;
660  }
661
662  /**
663   * Call this function to get a double array filled with the probability
664   * of how likely each class type is the class of the instance.
665   * @param i The instance to classify.
666   * @return A double array filled with the probalities of each class type.
667   * @throws Exception if can't classify instance.
668   */
669  public double[] distributionForInstance(Instance i) throws Exception {
670
671    if (!m_built) {
672      return null;
673    }
674   
675    double[] res = m_top.calcClassType(i);
676    if (m_top.m_training.classAttribute().isNumeric()) {
677      return res;
678    }
679
680    double most_likely = 0, highest = -1;
681    double count = 0;
682    for (int noa = 0; noa < m_top.m_training.numClasses(); noa++) {
683      count += res[noa];
684      if (res[noa] > highest) {
685        most_likely = noa;
686        highest = res[noa];
687      }
688    }
689   
690    if (count <= 0) {
691      //not sure how this happened.
692      return null;
693    }
694
695    for (int noa = 0; noa < m_top.m_training.numClasses(); noa++) {
696      res[noa] = res[noa] / count;
697    }
698    //System.out.println("ret");
699   
700    return res;
701  }
702 
703  /**
704   * Inner class used to represent the actual decision tree structure and data.
705   */
706  private class TreeClass 
707    implements Serializable, RevisionHandler {
708   
709    /** for serialization */
710    static final long serialVersionUID = 595663560871347434L;
711   
712    /**
713     * This contains the info for the coords of the shape converted
714     * to attrib coords,
715     * for polygon the first attrib is the number of points,
716     * This is not more object oriented because that would
717     * be over kill.
718     */
719    public FastVector m_ranges;
720
721    /** the first attribute */
722    public int m_attrib1;
723   
724    /** the second attribute */
725    public int m_attrib2;
726   
727    public TreeClass m_set1;
728    public TreeClass m_set2;
729
730    /** the parent */
731    public TreeClass m_parent;
732
733    /** A string to uniquely identify this node. */
734    public String m_identity;
735   
736    /** the weight of this node */
737    public double m_weight;
738   
739    /** the training instances for this node */
740    public Instances m_training;
741   
742    /** Used instead of the standard leaf if one exists. */
743    public Classifier m_classObject;
744
745    /** Used on the instances while classifying if one exists. */
746    public Filter m_filter;
747   
748    /**
749     * Constructs a TreeClass node  with all the important information.
750     * @param r A FastVector containing the shapes, null if it's a leaf node.
751     * @param a1 The first attribute.
752     * @param a2 The second attribute.
753     * @param id The unique id number for this node.
754     * @param w The weight of this node.
755     * @param i The instances that make it to this node from the training data.
756     * @param p the parent
757     * @throws Exception if can't use 'i' properly.
758     */
759    public TreeClass(FastVector r, int a1, int a2, int id, double w, 
760                     Instances i, TreeClass p) throws Exception {
761      m_set1 = null;
762      m_set2 = null;
763      m_ranges = r;
764      m_classObject = null;
765      m_filter = null;
766      m_training = i;
767      m_attrib1 = a1;
768      m_attrib2 = a2;
769      m_identity = "N" + String.valueOf(id);
770      m_weight = w;
771      m_parent = p;
772      m_nextId++;
773      if (m_ranges == null) {
774       
775        setLeaf();
776        //this will fill the ranges array with the
777        //number of times each class type occurs for the instances.
778        /*m_ranges = new FastVector(1);
779          m_ranges.addElement(new FastVector(i.numClasses() + 1));
780          FastVector tmp = (FastVector)m_ranges.elementAt(0);
781          tmp.addElement(new Double(0));
782          for (int noa = 0; noa < i.numClasses(); noa++) {
783          tmp.addElement(new Double(0));
784          }
785          for (int noa = 0; noa < i.numInstances(); noa++) {
786          tmp.setElementAt(new Double(((Double)tmp.elementAt
787          ((int)i.instance(noa).
788          classValue() + 1)).doubleValue() +
789          i.instance(noa).weight()),
790          (int)i.instance(noa).classValue() + 1); 
791          //this gets the current class value and alters it and replaces it
792          }*/
793      }     
794    }
795   
796    /**
797     * Call this to set an alternate classifier For this node.
798     * @param c The alternative classifier to use.
799     * @throws Exception if alternate classifier can't build classification.
800     */
801    public void setClassifier(Classifier c) throws Exception {
802      m_classObject = c;
803      m_classObject.buildClassifier(m_training);
804    }
805
806    /**
807     * Get the alternate classifier at this node. Returns null if there is
808     * no classifier.
809     *
810     * @return the alternate classifier at this node, or null if there is none.
811     */
812    public Classifier getClassifier() {
813      return m_classObject;
814    }
815   
816    /**
817     * Call this to set this node with different information to what
818     * it was created with.
819     * @param at1 The first attribute.
820     * @param at2 The second attribute.
821     * @param ar The shapes at this node, null if leaf node, or
822     * alternate classifier.
823     * @throws Exception if leaf node and cant't create leaf info.
824     */
825    public void setInfo(int at1, int at2, FastVector ar) throws Exception {
826      m_classObject = null;
827      m_filter = null;
828      m_attrib1 = at1;
829      m_attrib2 = at2;
830      m_ranges = ar;
831     
832      //FastVector tmp;
833      if (m_ranges == null) {
834        setLeaf();
835        /*
836        //this will fill the ranges array with the number of times
837        //each class type occurs for the instances.
838          if (m_training != null) {
839            m_ranges = new FastVector(1);
840            m_ranges.addElement(new FastVector(m_training.numClasses() + 1));
841            tmp = (FastVector)m_ranges.elementAt(0);
842            tmp.addElement(new Double(0));
843            for (int noa = 0; noa < m_training.numClasses(); noa++) {
844              tmp.addElement(new Double(0));
845            }
846            for (int noa = 0; noa < m_training.numInstances(); noa++) {
847              tmp.setElementAt(new Double(((Double)tmp.elementAt
848                                           ((int)m_training.instance(noa).
849                                            classValue() + 1)).doubleValue() +
850                                          m_training.instance(noa).weight()),
851                               (int)m_training.instance(noa).classValue() + 1);
852              //this gets the current class val and alters it and replaces it
853              }
854              }*/
855      }
856    }
857   
858    /**
859     * This sets up the informtion about this node such as the s.d or the
860     * number of each class.
861     * @throws Exception if problem with training instances.
862     */
863    private void setLeaf() throws Exception {
864      //this will fill the ranges array with the number of times
865      //each class type occurs for the instances.
866      //System.out.println("ihere");
867      if (m_training != null ) {
868       
869        if (m_training.classAttribute().isNominal()) {
870          FastVector tmp;
871         
872          //System.out.println("ehlpe");
873          m_ranges = new FastVector(1);
874          m_ranges.addElement(new FastVector(m_training.numClasses() + 1));
875          tmp = (FastVector)m_ranges.elementAt(0);
876          tmp.addElement(new Double(0));
877          for (int noa = 0; noa < m_training.numClasses(); noa++) {
878            tmp.addElement(new Double(0));
879          }
880          for (int noa = 0; noa < m_training.numInstances(); noa++) {
881            tmp.setElementAt(new Double(((Double)tmp.elementAt
882                                         ((int)m_training.instance(noa).
883                                          classValue() + 1)).doubleValue() + 
884                                        m_training.instance(noa).weight()), 
885                             (int)m_training.instance(noa).classValue() + 1);
886            //this gets the current class val and alters it and replaces it
887          }
888        }
889        else {
890          //then calc the standard deviation.
891          m_ranges = new FastVector(1);
892          double t1 = 0;
893          for (int noa = 0; noa < m_training.numInstances(); noa++) {
894            t1 += m_training.instance(noa).classValue();
895          }
896         
897          if (m_training.numInstances() != 0) {
898            t1 /= m_training.numInstances();
899          }
900          double t2 = 0;
901          for (int noa = 0; noa < m_training.numInstances(); noa++) {
902            t2 += Math.pow(m_training.instance(noa).classValue() - t1, 2);
903          }
904          FastVector tmp;
905          if (m_training.numInstances() != 0) {
906            t1 = Math.sqrt(t2 / m_training.numInstances());
907            m_ranges.addElement(new FastVector(2));
908            tmp = (FastVector)m_ranges.elementAt(0);
909            tmp.addElement(new Double(0));
910            tmp.addElement(new Double(t1));
911          }
912          else {
913            m_ranges.addElement(new FastVector(2));
914            tmp = (FastVector)m_ranges.elementAt(0);
915            tmp.addElement(new Double(0));
916            tmp.addElement(new Double(Double.NaN));
917          }
918        }
919      }
920    }
921
922    /**
923     * This will recursively go through the tree and return inside the
924     * array the weightings of each of the class types
925     * for this instance. Note that this function returns an otherwise
926     * unreferenced double array so there are no worry's about
927     * making changes.
928     *
929     * @param i The instance to test
930     * @return A double array containing the results.
931     * @throws Exception if can't use instance i properly.
932     */
933    public double[] calcClassType(Instance i) throws Exception {
934      //note that it will be the same calcs for both numeric and nominal
935      //attrib types.
936      //note the weightings for returning stuff will need to be modified
937      //to work properly but will do for now.
938      double x = 0, y = 0;
939      if (m_attrib1 >= 0) {
940        x = i.value(m_attrib1);
941      }
942      if (m_attrib2 >= 0) {
943        y = i.value(m_attrib2);
944      }
945      double[] rt;
946      if (m_training.classAttribute().isNominal()) {
947        rt = new double[m_training.numClasses()];
948      }
949      else {
950        rt = new double[1];
951      }
952
953      FastVector tmp;
954      if (m_classObject != null) {
955        //then use the classifier.
956        if (m_training.classAttribute().isNominal()) {
957          rt[(int)m_classObject.classifyInstance(i)] = 1;
958        }
959        else {
960          if (m_filter != null) {
961            m_filter.input(i);
962            rt[0] = m_classObject.classifyInstance(m_filter.output());
963          }
964          else {
965            rt[0] = m_classObject.classifyInstance(i);
966          }
967        }
968        //System.out.println("j48");
969        return rt;
970      }
971      else if (((Double)((FastVector)m_ranges.elementAt(0)).
972                elementAt(0)).intValue() == LEAF) {
973        //System.out.println("leaf");
974        //then this is a leaf
975        //rt = new double[m_training.numClasses()];
976       
977        if (m_training.classAttribute().isNumeric()) {
978         
979          setLinear();
980          m_filter.input(i);
981          rt[0] = m_classObject.classifyInstance(m_filter.output());
982          return rt;
983        }
984       
985        int totaler = 0;
986        tmp = (FastVector)m_ranges.elementAt(0);
987        for (int noa = 0; noa < m_training.numClasses();noa++) {
988          rt[noa] = ((Double)tmp.elementAt(noa + 1)).doubleValue();
989          totaler += rt[noa];
990        }
991        for (int noa = 0; noa < m_training.numClasses(); noa++) {
992          rt[noa] = rt[noa] / totaler;
993        }
994        return rt;
995      }
996     
997      for (int noa = 0; noa < m_ranges.size(); noa++) {
998       
999        tmp = (FastVector)m_ranges.elementAt(noa);
1000       
1001        if (((Double)tmp.elementAt(0)).intValue() 
1002            == VLINE && !Utils.isMissingValue(x)) {
1003         
1004        }
1005        else if (((Double)tmp.elementAt(0)).intValue() 
1006                 == HLINE && !Utils.isMissingValue(y)) {
1007         
1008        }
1009        else if (Utils.isMissingValue(x) || Utils.isMissingValue(y)) {
1010          //System.out.println("miss");
1011          //then go down both branches using their weights
1012          rt = m_set1.calcClassType(i);
1013          double[] tem = m_set2.calcClassType(i);
1014          if (m_training.classAttribute().isNominal()) {
1015            for (int nob = 0; nob < m_training.numClasses(); nob++) {
1016              rt[nob] *= m_set1.m_weight;
1017              rt[nob] += tem[nob] * m_set2.m_weight;
1018            }
1019          }
1020          else {
1021            rt[0] *= m_set1.m_weight;
1022            rt[0] += tem[0] * m_set2.m_weight;
1023          }
1024          return rt;
1025        }
1026        else if (((Double)tmp.elementAt(0)).intValue() == RECTANGLE) {
1027          //System.out.println("RECT");
1028          if (x >= ((Double)tmp.elementAt(1)).doubleValue() && 
1029              x <= ((Double)tmp.elementAt(3)).doubleValue() && 
1030              y <= ((Double)tmp.elementAt(2)).doubleValue() && 
1031              y >= ((Double)tmp.elementAt(4)).doubleValue()) {
1032            //then falls inside the rectangle
1033            //System.out.println("true");
1034            rt = m_set1.calcClassType(i);
1035            return rt;
1036          }
1037         
1038        }
1039        else if (((Double)tmp.elementAt(0)).intValue() == POLYGON) {
1040          if (inPoly(tmp, x, y)) {
1041            rt = m_set1.calcClassType(i);
1042            return rt;
1043          }
1044        }
1045        else if (((Double)tmp.elementAt(0)).intValue() == POLYLINE) {
1046          if (inPolyline(tmp, x, y)) {
1047            rt = m_set1.calcClassType(i);
1048            return rt;
1049          }
1050        }
1051      }
1052      //is outside the split
1053      if (m_set2 != null) {
1054        rt = m_set2.calcClassType(i);
1055      }
1056      return rt;
1057    }
1058   
1059    /**
1060     * This function gets called to set the node to use a linear regression
1061     * and attribute filter.
1062     * @throws Exception If can't set a default linear egression model.
1063     */
1064    private void setLinear() throws Exception {
1065      //then set default behaviour for node.
1066      //set linear regression combined with attribute filter
1067     
1068      //find the attributes used for splitting.
1069      boolean[] attributeList = new boolean[m_training.numAttributes()];
1070      for (int noa = 0; noa < m_training.numAttributes(); noa++) {
1071        attributeList[noa] = false;
1072      }
1073     
1074      TreeClass temp = this;
1075      attributeList[m_training.classIndex()] = true;
1076      while (temp != null) {
1077        attributeList[temp.m_attrib1] = true;
1078        attributeList[temp.m_attrib2] = true;
1079        temp = temp.m_parent;
1080      }
1081      int classind = 0;
1082     
1083     
1084      //find the new class index
1085      for (int noa = 0; noa < m_training.classIndex(); noa++) {
1086        if (attributeList[noa]) {
1087          classind++;
1088        }
1089      }
1090      //count how many attribs were used
1091      int count = 0;
1092      for (int noa = 0; noa < m_training.numAttributes(); noa++) {
1093        if (attributeList[noa]) {
1094          count++;
1095        }
1096      }
1097     
1098      //fill an int array with the numbers of those attribs
1099      int[] attributeList2 = new int[count];
1100      count = 0;
1101      for (int noa = 0; noa < m_training.numAttributes(); noa++) {
1102        if (attributeList[noa]) {
1103          attributeList2[count] = noa;
1104          count++;
1105        }
1106      }
1107     
1108      m_filter = new Remove();
1109      ((Remove)m_filter).setInvertSelection(true);
1110      ((Remove)m_filter).setAttributeIndicesArray(attributeList2);
1111      m_filter.setInputFormat(m_training);
1112     
1113      Instances temp2 = Filter.useFilter(m_training, m_filter);
1114      temp2.setClassIndex(classind);
1115      m_classObject = new LinearRegression();
1116      m_classObject.buildClassifier(temp2);
1117    }
1118   
1119    /**
1120     * Call to find out if an instance is in a polyline.
1121     * @param ob The polyline to check.
1122     * @param x The value of attribute1 to check.
1123     * @param y The value of attribute2 to check.
1124     * @return True if inside, false if not.
1125     */
1126    private boolean inPolyline(FastVector ob, double x, double y) {
1127      //this works similar to the inPoly below except that
1128      //the first and last lines are treated as extending infinite
1129      //in one direction and
1130      //then infinitly in the x dirction their is a line that will
1131      //normaly be infinite but
1132      //can be finite in one or both directions
1133     
1134      int countx = 0;
1135      double vecx, vecy;
1136      double change;
1137      double x1, y1, x2, y2;
1138     
1139      for (int noa = 1; noa < ob.size() - 4; noa+= 2) {
1140        y1 = ((Double)ob.elementAt(noa+1)).doubleValue();
1141        y2 = ((Double)ob.elementAt(noa+3)).doubleValue();
1142        x1 = ((Double)ob.elementAt(noa)).doubleValue();
1143        x2 = ((Double)ob.elementAt(noa+2)).doubleValue();
1144        vecy = y2 - y1;
1145        vecx = x2 - x1;
1146        if (noa == 1 && noa == ob.size() - 6) {
1147          //then do special test first and last edge
1148          if (vecy != 0) {
1149            change = (y - y1) / vecy;
1150            if (vecx * change + x1 >= x) {
1151              //then intersection
1152              countx++;
1153            }
1154          }
1155        }
1156        else if (noa == 1) {
1157          if ((y < y2 && vecy > 0) || (y > y2 && vecy < 0)) {
1158            //now just determine intersection or not
1159            change = (y - y1) / vecy;
1160            if (vecx * change + x1 >= x) {
1161              //then intersection on horiz
1162              countx++;
1163            }
1164          }
1165        }
1166        else if (noa == ob.size() - 6) {
1167          //then do special test on last edge
1168          if ((y <= y1 && vecy < 0) || (y >= y1 && vecy > 0)) {
1169            change = (y - y1) / vecy;
1170            if (vecx * change + x1 >= x) {
1171              countx++;
1172            }
1173          }
1174         
1175        }
1176        else if ((y1 <= y && y < y2) || (y2 < y && y <= y1)) {
1177          //then continue tests.
1178          if (vecy == 0) {
1179            //then lines are parallel stop tests in
1180            //ofcourse it should never make it this far
1181          }
1182          else {
1183            change = (y - y1) / vecy;
1184            if (vecx * change + x1 >= x) {
1185              //then intersects on horiz
1186              countx++;
1187            }
1188          }
1189        }
1190       
1191      }
1192     
1193      //now check for intersection with the infinity line
1194      y1 = ((Double)ob.elementAt(ob.size() - 2)).doubleValue();
1195      y2 = ((Double)ob.elementAt(ob.size() - 1)).doubleValue();
1196     
1197      if (y1 > y2) {
1198        //then normal line
1199        if (y1 >= y && y > y2) {
1200          countx++;
1201        }
1202      }
1203      else {
1204        //then the line segment is inverted
1205        if (y1 >= y || y > y2) {
1206          countx++;
1207        }
1208      }
1209     
1210      if ((countx % 2) == 1) {
1211        return true;
1212      }
1213      else {
1214        return false;
1215      }
1216    }
1217   
1218    /**
1219     * Call this to determine if an instance is in a polygon.
1220     * @param ob The polygon.
1221     * @param x The value of attribute 1.
1222     * @param y The value of attribute 2.
1223     * @return True if in polygon, false if not.
1224     */
1225    private boolean inPoly(FastVector ob, double x, double y) {
1226      int count = 0;
1227      double vecx, vecy;
1228      double change;
1229      double x1, y1, x2, y2;
1230      for (int noa = 1; noa < ob.size() - 2; noa += 2) {
1231        y1 = ((Double)ob.elementAt(noa+1)).doubleValue();
1232        y2 = ((Double)ob.elementAt(noa+3)).doubleValue();
1233        if ((y1 <= y && y < y2) || (y2 < y && y <= y1)) {
1234          //then continue tests.
1235          vecy = y2 - y1;
1236          if (vecy == 0) {
1237            //then lines are parallel stop tests for this line
1238          }
1239          else {
1240            x1 = ((Double)ob.elementAt(noa)).doubleValue();
1241            x2 = ((Double)ob.elementAt(noa+2)).doubleValue();
1242            vecx = x2 - x1;
1243            change = (y - y1) / vecy;
1244            if (vecx * change + x1 >= x) {
1245              //then add to count as an intersected line
1246              count++;
1247            }
1248          }
1249         
1250        }
1251      }
1252      if ((count % 2) == 1) {
1253        //then lies inside polygon
1254        //System.out.println("in");
1255        return true;
1256      }
1257      else {
1258        //System.out.println("out");
1259        return false;
1260      }
1261      //System.out.println("WHAT?!?!?!?!!?!??!?!");
1262      //return false;
1263    }
1264
1265    /**
1266     * Goes through the tree structure recursively and returns the node that
1267     * has the id.
1268     * @param id The node to find.
1269     * @return The node that matches the id.
1270     */
1271    public TreeClass getNode(String id) {
1272      //returns the treeclass object with the particular ident
1273      if (id.equals(m_identity)) {
1274        return this;
1275      }
1276     
1277      if (m_set1 != null) {
1278        TreeClass tmp = m_set1.getNode(id);
1279        if (tmp != null) {
1280          return tmp;
1281        }
1282      }
1283      if (m_set2 != null) {
1284        TreeClass tmp = m_set2.getNode(id);
1285        if (tmp != null) {
1286          return tmp;
1287        }
1288      }
1289      return null;
1290    }
1291   
1292    /**
1293     * Returns a string containing a bit of information about this node, in
1294     * alternate form.
1295     * @param s The string buffer to fill.
1296     * @throws Exception if can't create label.
1297     */
1298    public void getAlternateLabel(StringBuffer s) throws Exception {
1299     
1300      //StringBuffer s = new StringBuffer();
1301     
1302      FastVector tmp = (FastVector)m_ranges.elementAt(0);
1303     
1304      if (m_classObject != null && m_training.classAttribute().isNominal()) {
1305        s.append("Classified by " + m_classObject.getClass().getName());
1306      }
1307      else if (((Double)tmp.elementAt(0)).intValue() == LEAF) {
1308        if (m_training.classAttribute().isNominal()) {
1309          double high = -1000;
1310          int num = 0;
1311          double count = 0;
1312          for (int noa = 0; noa < m_training.classAttribute().numValues();
1313               noa++) {
1314            if (((Double)tmp.elementAt(noa + 1)).doubleValue() > high) {
1315              high = ((Double)tmp.elementAt(noa + 1)).doubleValue();
1316              num  = noa + 1;
1317            }
1318            count += ((Double)tmp.elementAt(noa + 1)).doubleValue();
1319          }
1320          s.append(m_training.classAttribute().value(num-1) + "(" + count);
1321          if (count > high) {
1322            s.append("/" + (count - high));
1323          }
1324          s.append(")");
1325        }
1326        else {
1327          if (m_classObject == null 
1328              && ((Double)tmp.elementAt(0)).intValue() == LEAF) {
1329            setLinear();
1330          }
1331          s.append("Standard Deviation = " 
1332                   + Utils.doubleToString(((Double)tmp.elementAt(1))
1333                                          .doubleValue(), 6));
1334         
1335        }
1336      }
1337      else {
1338        s.append("Split on ");
1339        s.append(m_training.attribute(m_attrib1).name() + " AND ");
1340        s.append(m_training.attribute(m_attrib2).name());
1341       
1342       
1343      }
1344     
1345      //return s.toString();
1346    }
1347   
1348    /**
1349     * Returns a string containing a bit of information about this node.
1350     * @param s The stringbuffer to fill.
1351     * @throws Exception if can't create label.
1352     */
1353    public void getLabel(StringBuffer s) throws Exception {
1354      //for now just return identity
1355      //StringBuffer s = new StringBuffer();
1356     
1357      FastVector tmp = (FastVector)m_ranges.elementAt(0);
1358     
1359     
1360      if (m_classObject != null && m_training.classAttribute().isNominal()) {
1361        s.append("Classified by\\n" + m_classObject.getClass().getName());
1362      }
1363      else if (((Double)tmp.elementAt(0)).intValue() == LEAF) {
1364       
1365        if (m_training.classAttribute().isNominal()) {
1366          boolean first = true;
1367          for (int noa = 0; noa < m_training.classAttribute().numValues(); 
1368               noa++) {
1369            if (((Double)tmp.elementAt(noa + 1)).doubleValue() > 0) {
1370              if (first)
1371                {
1372                  s.append("[" + m_training.classAttribute().value(noa));
1373                  first = false;
1374                }
1375              else
1376                {
1377                  s.append("\\n[" + m_training.classAttribute().value(noa));
1378                }
1379              s.append(", " + ((Double)tmp.elementAt(noa + 1)).doubleValue() 
1380                       + "]");
1381            }     
1382          }
1383        }
1384        else {
1385          if (m_classObject == null 
1386              && ((Double)tmp.elementAt(0)).intValue() == LEAF) {
1387            setLinear();
1388          }
1389          s.append("Standard Deviation = " 
1390                   + Utils.doubleToString(((Double)tmp.elementAt(1))
1391                   .doubleValue(), 6));
1392        }
1393      }
1394      else {
1395        s.append("Split on\\n");
1396        s.append(m_training.attribute(m_attrib1).name() + " AND\\n");
1397        s.append(m_training.attribute(m_attrib2).name());
1398      }
1399      //return s.toString();
1400    }
1401
1402    /**
1403     * Converts The tree structure to a dotty string.
1404     * @param t The stringbuffer to fill with the dotty structure.
1405     * @throws Exception if can't convert structure to dotty.
1406     */
1407    public void toDotty(StringBuffer t) throws Exception {
1408      //this will recursively create all the dotty info for the structure
1409      t.append(m_identity + " [label=\"");
1410      getLabel(t);
1411      t.append("\" ");
1412      //System.out.println(((Double)((FastVector)ranges.elementAt(0)).
1413      //elementAt(0)).intValue() + " A num ");
1414      if (((Double)((FastVector)m_ranges.elementAt(0)).elementAt(0)).intValue()
1415          == LEAF) {
1416        t.append("shape=box ");
1417      }
1418      else {
1419        t.append("shape=ellipse ");
1420      }
1421      t.append("style=filled color=gray95]\n");
1422     
1423      if (m_set1 != null) {
1424        t.append(m_identity + "->");
1425        t.append(m_set1.m_identity + " [label=\"True\"]\n");//the edge for
1426        //the left
1427        m_set1.toDotty(t);
1428      }
1429      if (m_set2 != null) {
1430        t.append(m_identity + "->");
1431        t.append(m_set2.m_identity + " [label=\"False\"]\n"); //the edge for
1432        //the
1433        //right
1434        m_set2.toDotty(t);
1435      }
1436     
1437    }
1438   
1439    /**
1440     * This will append the class Object in the tree to the string buffer.
1441     * @param t The stringbuffer.
1442     */
1443    public void objectStrings(StringBuffer t) {
1444     
1445      if (m_classObject != null) {
1446        t.append("\n\n" + m_identity +" {\n" + m_classObject.toString()+"\n}");
1447      }
1448      if (m_set1 != null) {
1449        m_set1.objectStrings(t);
1450      }
1451      if (m_set2 != null) {
1452        m_set2.objectStrings(t);
1453      }
1454    }
1455   
1456    /**
1457     * Converts the tree structure to a string. for people to read.
1458     * @param l How deep this node is in the tree.
1459     * @param t The stringbuffer to fill with the string.
1460     * @throws Exception if can't convert th string.
1461     */
1462    public void toString(int l, StringBuffer t) throws Exception {
1463     
1464      if (((Double)((FastVector)m_ranges.elementAt(0)).elementAt(0)).intValue()
1465          == LEAF) {
1466        t.append(": " + m_identity + " ");
1467        getAlternateLabel(t);
1468      }
1469      if (m_set1 != null) {
1470        t.append("\n");
1471        for (int noa = 0; noa < l; noa++) {
1472          t.append("|   ");
1473         
1474        }
1475        getAlternateLabel(t);
1476        t.append(" (In Set)");
1477        m_set1.toString(l+1, t);
1478      }
1479      if (m_set2 != null) {
1480        t.append("\n");
1481        for (int noa = 0; noa < l; noa++) {
1482          t.append("|   ");
1483        }
1484        getAlternateLabel(t);
1485        t.append(" (Not in Set)");
1486        m_set2.toString(l+1, t);
1487      }
1488      //return t.toString();
1489    }
1490   
1491    /**
1492     * Returns the revision string.
1493     *
1494     * @return          the revision
1495     */
1496    public String getRevision() {
1497      return RevisionUtils.extract("$Revision: 5987 $");
1498    }
1499  }
1500 
1501  /**
1502   * Returns the revision string.
1503   *
1504   * @return            the revision
1505   */
1506  public String getRevision() {
1507    return RevisionUtils.extract("$Revision: 5987 $");
1508  }
1509}
Note: See TracBrowser for help on using the repository browser.