UserClassifier.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 40k
Category:

Windows Develop

Development Platform:

Java

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