source: src/main/java/weka/gui/GenericObjectEditor.java @ 6

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

Import di weka.

File size: 49.7 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 *    GenericObjectEditor.java
19 *    Copyright (C) 2002 University of Waikato, Hamilton, New Zealand
20 *
21 */
22
23package weka.gui;
24
25import weka.core.Capabilities;
26import weka.core.CapabilitiesHandler;
27import weka.core.ClassDiscovery;
28import weka.core.OptionHandler;
29import weka.core.SerializedObject;
30import weka.core.Utils;
31import weka.core.Capabilities.Capability;
32import weka.gui.CheckBoxList.CheckBoxListModel;
33
34import java.awt.BorderLayout;
35import java.awt.Component;
36import java.awt.Dimension;
37import java.awt.FlowLayout;
38import java.awt.FontMetrics;
39import java.awt.Frame;
40import java.awt.GridLayout;
41import java.awt.Window;
42import java.awt.event.ActionEvent;
43import java.awt.event.ActionListener;
44import java.awt.event.WindowAdapter;
45import java.awt.event.WindowEvent;
46import java.beans.PropertyChangeEvent;
47import java.beans.PropertyChangeListener;
48import java.beans.PropertyChangeSupport;
49import java.beans.PropertyEditor;
50import java.beans.PropertyEditorManager;
51import java.io.BufferedInputStream;
52import java.io.BufferedOutputStream;
53import java.io.File;
54import java.io.FileInputStream;
55import java.io.FileOutputStream;
56import java.io.ObjectInputStream;
57import java.io.ObjectOutputStream;
58import java.lang.reflect.Array;
59import java.util.Enumeration;
60import java.util.Hashtable;
61import java.util.Properties;
62import java.util.StringTokenizer;
63import java.util.Vector;
64
65import javax.swing.BorderFactory;
66import javax.swing.JButton;
67import javax.swing.JDialog;
68import javax.swing.JFileChooser;
69import javax.swing.JLabel;
70import javax.swing.JOptionPane;
71import javax.swing.JPanel;
72import javax.swing.JPopupMenu;
73import javax.swing.JScrollPane;
74import javax.swing.JTree;
75import javax.swing.event.TreeSelectionEvent;
76import javax.swing.event.TreeSelectionListener;
77import javax.swing.tree.DefaultMutableTreeNode;
78import javax.swing.tree.TreePath;
79import javax.swing.tree.TreeSelectionModel;
80
81/**
82 * A PropertyEditor for objects. It can be used either in a static or a dynamic
83 * way. <br>
84 * <br>
85 * In the <b>static</b> way (<code>USE_DYNAMIC</code> is <code>false</code>) the
86 * objects have been defined as editable in the GenericObjectEditor
87 * configuration file, which lists possible values that can be selected from,
88 * and themselves configured. The configuration file is called
89 * "GenericObjectEditor.props" and may live in either the location given by
90 * "user.home" or the current directory (this last will take precedence), and a
91 * default properties file is read from the Weka distribution. For speed, the
92 * properties file is read only once when the class is first loaded -- this may
93 * need to be changed if we ever end up running in a Java OS ;-). <br>
94 * <br>
95 * If it is used in a <b>dynamic</b> way (the <code>UseDynamic</code> property
96 * of the GenericPropertiesCreator props file is set to <code>true</code>)
97 * then the classes to list are discovered by the
98 * <code>GenericPropertiesCreator</code> class (it checks the complete classpath).
99 *
100 * @see GenericPropertiesCreator
101 * @see GenericPropertiesCreator#useDynamic()
102 * @see GenericPropertiesCreator#CREATOR_FILE
103 * @see weka.core.ClassDiscovery
104 *
105 * @author Len Trigg (trigg@cs.waikato.ac.nz)
106 * @author Xin Xu (xx5@cs.waikato.ac.nz)
107 * @author Richard Kirkby (rkirkby@cs.waikato.ac.nz)
108 * @author FracPete (fracpete at waikato dot ac dot nz)
109 * @version $Revision: 5841 $
110 */
111public class GenericObjectEditor implements PropertyEditor, CustomPanelSupplier {
112 
113  /** The object being configured. */
114  protected Object m_Object;
115 
116  /** Holds a copy of the current object that can be reverted to
117      if the user decides to cancel. */
118  protected Object m_Backup;
119   
120  /** Handles property change notification. */
121  protected PropertyChangeSupport m_Support = new PropertyChangeSupport(this);
122   
123  /** The Class of objects being edited. */
124  protected Class m_ClassType;
125   
126  /** The model containing the list of names to select from. */
127  protected Hashtable m_ObjectNames;
128
129  /** The GUI component for editing values, created when needed. */
130  protected GOEPanel m_EditorComponent;
131   
132  /** True if the GUI component is needed. */
133  protected boolean m_Enabled = true;
134   
135  /** The name of the properties file. */
136  protected static String PROPERTY_FILE = "weka/gui/GenericObjectEditor.props";
137   
138  /** Contains the editor properties. */
139  protected static Properties EDITOR_PROPERTIES;
140
141  /** the properties files containing the class/editor mappings. */
142  public static final String GUIEDITORS_PROPERTY_FILE = "weka/gui/GUIEditors.props";
143
144  /** The tree node of the current object so we can re-select it for the user. */
145  protected GOETreeNode m_treeNodeOfCurrentObject;
146
147  /** The property panel created for the objects. */
148  protected PropertyPanel m_ObjectPropertyPanel;
149   
150  /** whether the class can be changed. */
151  protected boolean m_canChangeClassInDialog;
152 
153  /** whether the Weka Editors were already registered. */
154  protected static boolean m_EditorsRegistered;
155
156  /** for filtering the tree based on the Capabilities of the leaves. */
157  protected Capabilities m_CapabilitiesFilter = null;
158 
159  /**
160   * Loads the configuration property file (USE_DYNAMIC is FALSE) or determines
161   * the classes dynamically (USE_DYNAMIC is TRUE)
162   * @see #USE_DYNAMIC
163   * @see GenericPropertiesCreator
164   */
165  static {
166
167    try {
168      GenericPropertiesCreator creator = new GenericPropertiesCreator();
169
170      // dynamic approach?
171      if (creator.useDynamic()) {
172        try {
173          creator.execute(false);
174          EDITOR_PROPERTIES = creator.getOutputProperties();
175        }
176        catch (Exception e) {
177          JOptionPane.showMessageDialog(
178              null,
179              "Could not determine the properties for the generic object\n"
180              + "editor. This exception was produced:\n"
181              + e.toString(),
182              "GenericObjectEditor",
183              JOptionPane.ERROR_MESSAGE);
184        }
185      }
186      else {
187        // Allow a properties file in the current directory to override
188        try {
189          EDITOR_PROPERTIES = Utils.readProperties(PROPERTY_FILE);
190          java.util.Enumeration keys = 
191            (java.util.Enumeration)EDITOR_PROPERTIES.propertyNames();
192          if (!keys.hasMoreElements()) {
193            throw new Exception("Failed to read a property file for the "
194                +"generic object editor");
195          }
196        }
197        catch (Exception ex) {
198          JOptionPane.showMessageDialog(
199              null,
200              "Could not read a configuration file for the generic object\n"
201              +"editor. An example file is included with the Weka distribution.\n"
202              +"This file should be named \"" + PROPERTY_FILE + "\" and\n"
203              +"should be placed either in your user home (which is set\n"
204              + "to \"" + System.getProperties().getProperty("user.home") + "\")\n"
205              + "or the directory that java was started from\n",
206              "GenericObjectEditor",
207              JOptionPane.ERROR_MESSAGE);
208        }
209      }
210    }
211    catch (Exception e) {
212      JOptionPane.showMessageDialog(
213          null,
214          "Could not initialize the GenericPropertiesCreator. "
215          + "This exception was produced:\n"
216          + e.toString(),
217          "GenericObjectEditor",
218          JOptionPane.ERROR_MESSAGE);
219    }
220  }
221
222  /**
223   * A specialized TreeNode for supporting filtering via Capabilities.
224   */
225  public class GOETreeNode
226    extends DefaultMutableTreeNode {
227   
228    /** for serialization. */
229    static final long serialVersionUID = -1707872446682150133L;
230   
231    /** color for "no support". */
232    public final static String NO_SUPPORT = "silver";
233   
234    /** color for "maybe support". */
235    public final static String MAYBE_SUPPORT = "blue";
236   
237    /** the Capabilities object to use for filtering. */
238    protected Capabilities m_Capabilities = null;
239   
240    /**
241     * Creates a tree node that has no parent and no children, but which
242     * allows children.
243     */
244    public GOETreeNode() {
245      super();
246    }
247   
248    /**
249     * Creates a tree node with no parent, no children, but which allows
250     * children, and initializes it with the specified user object.
251     *
252     * @param userObject        an Object provided by the user that constitutes
253     *                          the node's data
254     */
255    public GOETreeNode(Object userObject) {
256      super(userObject);
257    }
258   
259    /**
260     * Creates a tree node with no parent, no children, initialized with the
261     * specified user object, and that allows children only if specified.
262
263     * @param userObject        an Object provided by the user that constitutes
264     *                          the node's data
265     * @param allowsChildren    if true, the node is allowed to have child nodes
266     *                          -- otherwise, it is always a leaf node
267     */
268    public GOETreeNode(Object userObject, boolean allowsChildren) {
269      super(userObject, allowsChildren);
270    }
271   
272    /**
273     * generates if necessary a Capabilities object for the given leaf.
274     */
275    protected void initCapabilities() {
276      String    classname;
277      Class     cls;
278      Object    obj;
279     
280      if (m_Capabilities != null)
281        return;
282      if (!isLeaf())
283        return;
284     
285      classname = getClassnameFromPath(new TreePath(getPath()));
286      try {
287        cls = Class.forName(classname);
288        if (!ClassDiscovery.hasInterface(CapabilitiesHandler.class, cls))
289          return;
290       
291        obj = cls.newInstance();
292        m_Capabilities = ((CapabilitiesHandler) obj).getCapabilities();
293      }
294      catch (Exception e) {
295        // ignore it
296      }
297    }
298   
299    /**
300     * returns a string representation of this treenode.
301     *
302     * @return          the text to display
303     */
304    public String toString() {
305      String    result;
306     
307      result = super.toString();
308     
309      if (m_CapabilitiesFilter != null) {
310        initCapabilities();
311        if (m_Capabilities != null) {
312          if (m_Capabilities.supportsMaybe(m_CapabilitiesFilter) && !m_Capabilities.supports(m_CapabilitiesFilter))
313            result = "<html><font color=\"" + MAYBE_SUPPORT + "\">" + result + "</font></i><html>";
314          else if (!m_Capabilities.supports(m_CapabilitiesFilter))
315            result = "<html><font color=\"" + NO_SUPPORT + "\">" + result + "</font></i><html>";
316        }
317      }
318     
319      return result;
320    }
321  }
322 
323  /**
324   * A dialog for selecting Capabilities to look for in the GOE tree.
325   */
326  public class CapabilitiesFilterDialog 
327    extends JDialog {
328   
329    /** for serialization. */
330    static final long serialVersionUID = -7845503345689646266L;
331   
332    /** the dialog itself. */
333    protected JDialog m_Self;
334   
335    /** the popup to display again. */
336    protected JPopupMenu m_Popup = null;
337   
338    /** the capabilities used for initializing the dialog. */
339    protected Capabilities m_Capabilities = new Capabilities(null);
340
341    /** the label, listing the name of the superclass. */
342    protected JLabel m_InfoLabel = new JLabel();
343   
344    /** the list with all the capabilities. */
345    protected CheckBoxList m_List = new CheckBoxList();
346   
347    /** the OK button. */
348    protected JButton m_OkButton = new JButton("OK");
349   
350    /** the Cancel button. */
351    protected JButton m_CancelButton = new JButton("Cancel");
352   
353    /**
354     * creates a dialog to choose Capabilities from.
355     */
356    public CapabilitiesFilterDialog() {
357      super();
358
359      m_Self = this;
360     
361      initGUI();
362    }
363   
364    /**
365     * sets up the GUI.
366     */
367    protected void initGUI() {
368      JPanel                    panel;
369      CheckBoxListModel         model;
370
371      setTitle("Filtering Capabilities...");
372      setLayout(new BorderLayout());
373     
374      panel = new JPanel(new BorderLayout());
375      panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
376      getContentPane().add(panel, BorderLayout.NORTH);
377      m_InfoLabel.setText(
378            "<html>"
379          + m_ClassType.getName().replaceAll(".*\\.", "") + "s"
380          + " have to support <i>at least</i> the following capabilities <br>"
381          + "(the ones highlighted <font color=\"" + GOETreeNode.NO_SUPPORT + "\">" + GOETreeNode.NO_SUPPORT + "</font> don't meet these requirements <br>"
382          + "the ones highlighted  <font color=\"" + GOETreeNode.MAYBE_SUPPORT + "\">" + GOETreeNode.MAYBE_SUPPORT + "</font> possibly meet them):"
383          + "</html>");
384      panel.add(m_InfoLabel, BorderLayout.CENTER);
385     
386      // list
387      getContentPane().add(new JScrollPane(m_List), BorderLayout.CENTER);
388      model = (CheckBoxListModel) m_List.getModel();
389      for (Capability cap: Capability.values())
390        model.addElement(cap);
391     
392      // buttons
393      panel = new JPanel(new FlowLayout(FlowLayout.CENTER));
394      getContentPane().add(panel, BorderLayout.SOUTH);
395     
396      m_OkButton.setMnemonic('O');
397      m_OkButton.addActionListener(new ActionListener() {
398        public void actionPerformed(ActionEvent e) {
399          updateCapabilities();
400          if (m_CapabilitiesFilter == null)
401            m_CapabilitiesFilter = new Capabilities(null);
402          m_CapabilitiesFilter.assign(m_Capabilities);
403          m_Self.setVisible(false);
404          showPopup();
405        }
406      });
407      panel.add(m_OkButton);
408     
409      m_CancelButton.setMnemonic('C');
410      m_CancelButton.addActionListener(new ActionListener() {
411        public void actionPerformed(ActionEvent e) {
412          m_Self.setVisible(false);
413          showPopup();
414        }
415      });
416      panel.add(m_CancelButton);
417      pack();
418    }
419
420    /**
421     * transfers the Capabilities object to the JList.
422     *
423     * @see #m_Capabilities
424     * @see #m_List
425     */
426    protected void updateList() {
427      CheckBoxListModel         model;
428     
429      model = (CheckBoxListModel) m_List.getModel();
430
431      for (Capability cap: Capability.values())
432        model.setChecked(model.indexOf(cap), m_Capabilities.handles(cap));
433    }
434   
435    /**
436     * transfers the selected Capabilities from the JList to the
437     * Capabilities object.
438     *
439     * @see #m_Capabilities
440     * @see #m_List
441     */
442    protected void updateCapabilities() {
443      CheckBoxListModel         model;
444     
445      model = (CheckBoxListModel) m_List.getModel();
446
447      for (Capability cap: Capability.values()) {
448        if (model.getChecked(model.indexOf(cap)))
449          m_Capabilities.enable(cap);
450        else
451          m_Capabilities.disable(cap);
452      }
453    }
454   
455    /**
456     * sets the initial capabilities.
457     *
458     * @param value the capabilities to use
459     */
460    public void setCapabilities(Capabilities value) {
461      if (value != null)
462        m_Capabilities.assign(value);
463      else
464        m_Capabilities = new Capabilities(null);
465     
466      updateList();
467    }
468   
469    /**
470     * returns the currently selected capabilities.
471     *
472     * @return the currently selected capabilities
473     */
474    public Capabilities getCapabilities() {
475      return m_Capabilities;
476    }
477   
478    /**
479     * sets the JPopupMenu to display again after closing the dialog.
480     *
481     * @param value the JPopupMenu to display again
482     */
483    public void setPopup(JPopupMenu value) {
484      m_Popup = value;
485    }
486   
487    /**
488     * returns the currently set JPopupMenu.
489     *
490     * @return the current JPopupMenu
491     */
492    public JPopupMenu getPopup() {
493      return m_Popup;
494    }
495   
496    /**
497     * if a JPopupMenu is set, it is displayed again. Displaying this dialog
498     * closes any JPopupMenu automatically.
499     */
500    public void showPopup() {
501      if (getPopup() != null)
502        getPopup().setVisible(true);
503    }
504  }
505 
506  /**
507   * Creates a popup menu containing a tree that is aware
508   * of the screen dimensions.
509   */
510  public class JTreePopupMenu 
511    extends JPopupMenu {
512   
513    /** for serialization. */
514    static final long serialVersionUID = -3404546329655057387L;
515
516    /** the popup itself. */
517    private JPopupMenu m_Self;
518   
519    /** The tree. */
520    private JTree m_tree;
521
522    /** The scroller. */
523    private JScrollPane m_scroller;
524
525    /** The filter button in case of CapabilitiesHandlers. */
526    private JButton m_FilterButton = new JButton("Filter...");
527
528    /** The remove filter button in case of CapabilitiesHandlers. */
529    private JButton m_RemoveFilterButton = new JButton("Remove filter");
530   
531    /** The button for closing the popup again. */
532    private JButton m_CloseButton = new JButton("Close");
533   
534    /**
535     * Constructs a new popup menu.
536     *
537     * @param tree the tree to put in the menu
538     */
539    public JTreePopupMenu(JTree tree) {
540
541      m_Self = this;
542     
543      setLayout(new BorderLayout());
544      JPanel panel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
545      add(panel, BorderLayout.SOUTH);
546
547      if (ClassDiscovery.hasInterface(CapabilitiesHandler.class, m_ClassType)) {
548        // filter
549        m_FilterButton.setMnemonic('F');
550        m_FilterButton.addActionListener(new ActionListener() {
551          public void actionPerformed(ActionEvent e) {
552            if (e.getSource() == m_FilterButton) {
553              CapabilitiesFilterDialog dialog = new CapabilitiesFilterDialog();
554              dialog.setCapabilities(m_CapabilitiesFilter);
555              dialog.setPopup(m_Self);
556              dialog.setVisible(true);
557              m_Support.firePropertyChange("", null, null);
558              repaint();
559            }
560          }
561        });
562        panel.add(m_FilterButton);
563       
564        // remove
565        m_RemoveFilterButton.setMnemonic('R');
566        m_RemoveFilterButton.addActionListener(new ActionListener() {
567          public void actionPerformed(ActionEvent e) {
568            if (e.getSource() == m_RemoveFilterButton) {
569              m_CapabilitiesFilter = null;
570              m_Support.firePropertyChange("", null, null);
571              repaint();
572            }
573          }
574        });
575        panel.add(m_RemoveFilterButton);
576      }
577
578      // close
579      m_CloseButton.setMnemonic('C');
580      m_CloseButton.addActionListener(new ActionListener() {
581        public void actionPerformed(ActionEvent e) {
582          if (e.getSource() == m_CloseButton) {
583            m_Self.setVisible(false);
584          }
585        }
586      });
587      panel.add(m_CloseButton);
588     
589      m_tree = tree;
590     
591      JPanel treeView = new JPanel();
592      treeView.setLayout(new BorderLayout());
593      treeView.add(m_tree, BorderLayout.NORTH);
594     
595      // make backgrounds look the same
596      treeView.setBackground(m_tree.getBackground());
597
598      m_scroller = new JScrollPane(treeView);
599     
600      m_scroller.setPreferredSize(new Dimension(300, 400));
601      m_scroller.getVerticalScrollBar().setUnitIncrement(20);
602
603      add(m_scroller);
604    }
605
606    /**
607     * Displays the menu, making sure it will fit on the screen.
608     *
609     * @param invoker the component thast invoked the menu
610     * @param x the x location of the popup
611     * @param y the y location of the popup
612     */
613    public void show(Component invoker, int x, int y) {
614
615      super.show(invoker, x, y);
616
617      // calculate available screen area for popup
618      java.awt.Point location = getLocationOnScreen();
619      java.awt.Dimension screenSize = getToolkit().getScreenSize();
620      int maxWidth = (int) (screenSize.getWidth() - location.getX());
621      int maxHeight = (int) (screenSize.getHeight() - location.getY());
622
623      // if the part of the popup goes off the screen then resize it
624      Dimension scrollerSize = m_scroller.getPreferredSize();
625      int height = (int) scrollerSize.getHeight();
626      int width = (int) scrollerSize.getWidth();
627      if (width > maxWidth) width = maxWidth;
628      if (height > maxHeight) height = maxHeight;
629     
630      // commit any size changes
631      m_scroller.setPreferredSize(new Dimension(width, height));
632      revalidate();
633      pack();
634    }
635  }
636
637  /**
638   * Handles the GUI side of editing values.
639   */
640  public class GOEPanel 
641    extends JPanel {
642   
643    /** for serialization. */
644    static final long serialVersionUID = 3656028520876011335L;
645   
646    /** The component that performs classifier customization. */
647    protected PropertySheetPanel m_ChildPropertySheet;
648   
649    /** The name of the current class. */
650    protected JLabel m_ClassNameLabel;
651
652    /** Open object from disk. */
653    protected JButton m_OpenBut;
654   
655    /** Save object to disk. */
656    protected JButton m_SaveBut;
657   
658    /** ok button. */
659    protected JButton m_okBut;
660   
661    /** cancel button. */
662    protected JButton m_cancelBut;
663   
664    /** The filechooser for opening and saving object files. */
665    protected JFileChooser m_FileChooser;
666   
667    /** Creates the GUI editor component. */
668    public GOEPanel() {
669   
670      m_Backup = copyObject(m_Object);
671     
672      m_ClassNameLabel = new JLabel("None");
673      m_ClassNameLabel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
674
675      m_ChildPropertySheet = new PropertySheetPanel();
676      m_ChildPropertySheet.addPropertyChangeListener
677        (new PropertyChangeListener() {
678            public void propertyChange(PropertyChangeEvent evt) {
679              m_Support.firePropertyChange("", null, null);
680            }
681          });
682     
683      m_OpenBut = new JButton("Open...");
684      m_OpenBut.setToolTipText("Load a configured object");
685      m_OpenBut.setEnabled(true);
686      m_OpenBut.addActionListener(new ActionListener() {
687          public void actionPerformed(ActionEvent e) {
688            Object object = openObject();
689            if (object != null) {
690              // setValue takes care of: Making sure obj is of right type,
691              // and firing property change.
692              setValue(object);
693              // Need a second setValue to get property values filled in OK.
694              // Not sure why.
695              setValue(object);
696            }
697          }
698        });
699     
700      m_SaveBut = new JButton("Save...");
701      m_SaveBut.setToolTipText("Save the current configured object");
702      m_SaveBut.setEnabled(true);
703      m_SaveBut.addActionListener(new ActionListener() {
704          public void actionPerformed(ActionEvent e) {
705            saveObject(m_Object);
706          }
707        });
708     
709      m_okBut = new JButton("OK");
710      m_okBut.setEnabled(true);
711      m_okBut.addActionListener(new ActionListener() {
712          public void actionPerformed(ActionEvent e) {
713
714            m_Backup = copyObject(m_Object);
715            if ((getTopLevelAncestor() != null)
716                && (getTopLevelAncestor() instanceof Window)) {
717              Window w = (Window) getTopLevelAncestor();
718              w.dispose();
719            }
720          }
721        });
722     
723      m_cancelBut = new JButton("Cancel");
724      m_cancelBut.setEnabled(true);
725      m_cancelBut.addActionListener(new ActionListener() {
726          public void actionPerformed(ActionEvent e) {           
727            if (m_Backup != null) {
728       
729              m_Object = copyObject(m_Backup);
730             
731              // To fire property change
732              m_Support.firePropertyChange("", null, null);
733              m_ObjectNames = getClassesFromProperties();
734              updateObjectNames();
735              updateChildPropertySheet();
736            }
737            if ((getTopLevelAncestor() != null)
738                && (getTopLevelAncestor() instanceof Window)) {
739              Window w = (Window) getTopLevelAncestor();
740              w.dispose();
741            }
742          }
743        });
744     
745      setLayout(new BorderLayout());
746
747      if (m_canChangeClassInDialog) {
748        JButton chooseButton = createChooseClassButton();
749        JPanel top = new JPanel();
750        top.setLayout(new BorderLayout());
751        top.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
752        top.add(chooseButton, BorderLayout.WEST);
753        top.add(m_ClassNameLabel, BorderLayout.CENTER);
754        add(top, BorderLayout.NORTH);
755      } else {
756        add(m_ClassNameLabel, BorderLayout.NORTH);
757      }
758
759      add(m_ChildPropertySheet, BorderLayout.CENTER);
760      // Since we resize to the size of the property sheet, a scrollpane isn't
761      // typically needed
762      // add(new JScrollPane(m_ChildPropertySheet), BorderLayout.CENTER);
763     
764      JPanel okcButs = new JPanel();
765      okcButs.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
766      okcButs.setLayout(new GridLayout(1, 4, 5, 5));
767      okcButs.add(m_OpenBut);
768      okcButs.add(m_SaveBut);
769      okcButs.add(m_okBut);
770      okcButs.add(m_cancelBut);
771      add(okcButs, BorderLayout.SOUTH);
772
773      if (m_ClassType != null) {
774        m_ObjectNames = getClassesFromProperties();
775        if (m_Object != null) {
776          updateObjectNames();
777          updateChildPropertySheet();
778        }
779      }
780    }
781   
782    /**
783     * Enables/disables the cancel button.
784     *
785     * @param flag true to enable cancel button, false
786     * to disable it
787     */
788    protected void setCancelButton(boolean flag) {
789
790      if(m_cancelBut != null)
791        m_cancelBut.setEnabled(flag);
792    }
793   
794    /**
795     * Opens an object from a file selected by the user.
796     *
797     * @return the loaded object, or null if the operation was cancelled
798     */
799    protected Object openObject() {
800     
801      if (m_FileChooser == null) {
802        createFileChooser();
803      }
804      int returnVal = m_FileChooser.showOpenDialog(this);
805      if (returnVal == JFileChooser.APPROVE_OPTION) {
806        File selected = m_FileChooser.getSelectedFile();
807        try {
808          ObjectInputStream oi = new ObjectInputStream(new BufferedInputStream(new FileInputStream(selected)));
809          Object obj = oi.readObject();
810          oi.close();
811          if (!m_ClassType.isAssignableFrom(obj.getClass())) {
812            throw new Exception("Object not of type: " + m_ClassType.getName());
813          }
814          return obj;
815        } catch (Exception ex) {
816          JOptionPane.showMessageDialog(this,
817                                        "Couldn't read object: "
818                                        + selected.getName() 
819                                        + "\n" + ex.getMessage(),
820                                        "Open object file",
821                                        JOptionPane.ERROR_MESSAGE);
822        }
823      }
824      return null;
825    }
826   
827    /**
828     * Saves an object to a file selected by the user.
829     *
830     * @param object the object to save
831     */
832    protected void saveObject(Object object) {
833     
834      if (m_FileChooser == null) {
835        createFileChooser();
836      }
837      int returnVal = m_FileChooser.showSaveDialog(this);
838      if (returnVal == JFileChooser.APPROVE_OPTION) {
839        File sFile = m_FileChooser.getSelectedFile();
840        try {
841          ObjectOutputStream oo = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(sFile)));
842          oo.writeObject(object);
843          oo.close();
844        } catch (Exception ex) {
845          JOptionPane.showMessageDialog(this,
846                                        "Couldn't write to file: "
847                                        + sFile.getName() 
848                                        + "\n" + ex.getMessage(),
849                                        "Save object",
850                                        JOptionPane.ERROR_MESSAGE);
851        }
852      }
853    }
854
855    /**
856     * Creates the file chooser the user will use to save/load files with.
857     */
858    protected void createFileChooser() {
859     
860      m_FileChooser = new JFileChooser(new File(System.getProperty("user.dir")));
861      m_FileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
862    }
863   
864    /**
865     * Makes a copy of an object using serialization.
866     *
867     * @param source the object to copy
868     * @return a copy of the source object
869     */
870    protected Object copyObject(Object source) {
871
872      Object result = null;
873      try {
874        result = GenericObjectEditor.this.makeCopy(source);
875        setCancelButton(true);
876       
877      } catch (Exception ex) {
878        setCancelButton(false);
879        System.err.println("GenericObjectEditor: Problem making backup object");
880        System.err.println(ex);
881      }
882      return result;
883    }
884   
885    /**
886     * Allows customization of the action label on the dialog.
887     *
888     * @param newLabel the new string for the ok button
889     */
890    public void setOkButtonText(String newLabel) {
891
892      m_okBut.setText(newLabel);
893    }
894
895    /**
896     * This is used to hook an action listener to the ok button.
897     *
898     * @param a The action listener.
899     */
900    public void addOkListener(ActionListener a) {
901
902      m_okBut.addActionListener(a);
903    }
904   
905    /**
906     * This is used to hook an action listener to the cancel button.
907     *
908     * @param a The action listener.
909     */
910    public void addCancelListener(ActionListener a) {
911
912      m_cancelBut.addActionListener(a);
913    }
914       
915    /**
916     * This is used to remove an action listener from the ok button.
917     *
918     * @param a The action listener
919     */
920    public void removeOkListener(ActionListener a) {
921
922      m_okBut.removeActionListener(a);
923    }
924   
925    /**
926     * This is used to remove an action listener from the cancel button.
927     *
928     * @param a The action listener
929     */
930    public void removeCancelListener(ActionListener a) {
931
932      m_cancelBut.removeActionListener(a);
933    }
934   
935    /**
936     * Updates the child property sheet, and creates if needed.
937     */
938    public void updateChildPropertySheet() {
939     
940      // Update the object name displayed
941      String className = "None";
942      if (m_Object != null) {
943        className = m_Object.getClass().getName();
944      }
945      m_ClassNameLabel.setText(className);
946
947      // Set the object as the target of the propertysheet
948      m_ChildPropertySheet.setTarget(m_Object);
949
950      // Adjust size of containing window if possible
951      if ((getTopLevelAncestor() != null)
952          && (getTopLevelAncestor() instanceof Window)) {
953        ((Window) getTopLevelAncestor()).pack();
954      }
955    }   
956  }
957
958  /**
959   * Default constructor.
960   */
961  public GenericObjectEditor() {
962
963    this(false);
964  }
965
966  /**
967   * Constructor that allows specifying whether it is possible
968   * to change the class within the editor dialog.
969   *
970   * @param canChangeClassInDialog whether the user can change the class
971   */
972  public GenericObjectEditor(boolean canChangeClassInDialog) {
973
974    m_canChangeClassInDialog = canChangeClassInDialog;
975  }
976 
977  /**
978   * registers all the editors in Weka.
979   */
980  public static void registerEditors() {
981    Properties          props;
982    Enumeration         enm;
983    String              name;
984    String              value;
985    Class               baseCls;
986    Class               cls;
987
988    if (m_EditorsRegistered)
989      return;
990   
991    System.err.println("---Registering Weka Editors---");
992    m_EditorsRegistered = true;
993
994    // load properties
995    try {
996      props = Utils.readProperties(GUIEDITORS_PROPERTY_FILE);
997    }
998    catch (Exception e) {
999      props = new Properties();
1000      e.printStackTrace();
1001    }
1002   
1003    enm = props.propertyNames();
1004    while (enm.hasMoreElements()) {
1005      name  = enm.nextElement().toString();
1006      value = props.getProperty(name, "");
1007      try {
1008        // array class?
1009        if (name.endsWith("[]")) {
1010          baseCls = Class.forName(name.substring(0, name.indexOf("[]")));
1011          cls = Array.newInstance(baseCls, 1).getClass();
1012        }
1013        else {
1014          cls = Class.forName(name);
1015        }
1016        // register
1017        PropertyEditorManager.registerEditor(cls, Class.forName(value));
1018      }
1019      catch (Exception e) {
1020        System.err.println("Problem registering " + name + "/" + value + ": " + e);
1021      }
1022    }
1023  }
1024
1025  /**
1026   * Sets whether the user can change the class in the dialog.
1027   *
1028   * @param value       if true then the user can change the class
1029   */
1030  public void setCanChangeClassInDialog(boolean value) {
1031    m_canChangeClassInDialog = value;
1032  }
1033 
1034  /**
1035   * Returns whether the user can change the class in the dialog.
1036   *
1037   * @return            true if the user can change the class
1038   */
1039  public boolean getCanChangeClassInDialog() {
1040    return m_canChangeClassInDialog;
1041  }
1042 
1043  /**
1044   * Returns the backup object (may be null if there is no
1045   * backup.
1046   *
1047   * @return the backup object
1048   */
1049  public Object getBackup() {
1050    return m_Backup;
1051  }
1052 
1053  /**
1054   * returns the name of the root element of the given class name,
1055   * <code>null</code> if it doesn't contain the separator.
1056   *
1057   * @param clsname the full classname
1058   * @param separator the separator
1059   * @return string the root element
1060   */
1061  protected static String getRootFromClass(String clsname, String separator) {
1062    if (clsname.indexOf(separator) > -1)
1063      return clsname.substring(0, clsname.indexOf(separator));
1064    else
1065      return null;
1066  }
1067 
1068  /**
1069   * parses the given string of classes separated by ", " and returns the
1070   * a hashtable with as many entries as there are different root elements in
1071   * the class names (the key is the root element). E.g. if there's only
1072   * "weka." as the prefix for all classes the a hashtable of size 1 is returned.
1073   * if NULL is the input, then NULL is also returned.
1074   *
1075   * @param classes the classnames to work on
1076   * @return for each distinct root element in the classnames, one entry in
1077   * the hashtable (with the root element as key)
1078   */
1079  public static Hashtable sortClassesByRoot(String classes) {
1080    Hashtable                 roots;
1081    Hashtable                 result;
1082    Enumeration               enm;
1083    int                       i;
1084    StringTokenizer           tok;
1085    String                    clsname;
1086    Vector                    list;
1087    HierarchyPropertyParser   hpp;
1088    String                    separator;
1089    String                    root;
1090    String                    tmpStr;
1091   
1092    if (classes == null)
1093      return null;
1094   
1095    roots     = new Hashtable();
1096    hpp       = new HierarchyPropertyParser();
1097    separator = hpp.getSeperator();
1098   
1099    // go over all classnames and store them in the hashtable, with the
1100    // root element as the key
1101    tok   = new StringTokenizer(classes, ", ");
1102    while (tok.hasMoreElements()) {
1103      clsname = tok.nextToken();
1104      root    = getRootFromClass(clsname, separator);
1105      if (root == null)
1106        continue;
1107     
1108      // already stored?
1109      if (!roots.containsKey(root)) {
1110        list = new Vector();
1111        roots.put(root, list);
1112      }
1113      else {
1114        list = (Vector) roots.get(root);
1115      }
1116     
1117      list.add(clsname);
1118    }
1119   
1120    // build result
1121    result = new Hashtable();
1122    enm    = roots.keys();
1123    while (enm.hasMoreElements()) {
1124      root = (String) enm.nextElement();
1125      list = (Vector) roots.get(root);
1126      tmpStr = "";
1127      for (i = 0; i < list.size(); i++) {
1128        if (i > 0)
1129          tmpStr += ",";
1130        tmpStr += (String) list.get(i);
1131      }
1132      result.put(root, tmpStr);
1133    }
1134     
1135    return result;
1136  }
1137
1138  /**
1139   * Called when the class of object being edited changes.
1140   *
1141   * @return the hashtable containing the HierarchyPropertyParsers for the root
1142   *         elements
1143   */
1144  protected Hashtable getClassesFromProperties() {         
1145
1146    Hashtable hpps = new Hashtable();
1147    String className = m_ClassType.getName();
1148    Hashtable typeOptions = sortClassesByRoot(EDITOR_PROPERTIES.getProperty(className));
1149    if (typeOptions == null) {
1150      /*
1151      System.err.println("Warning: No configuration property found in\n"
1152                         + PROPERTY_FILE + "\n"
1153                         + "for " + className);
1154      */
1155    } else {               
1156      try {
1157        Enumeration enm = typeOptions.keys();
1158        while (enm.hasMoreElements()) {
1159          String root = (String) enm.nextElement();
1160          String typeOption = (String) typeOptions.get(root);
1161          HierarchyPropertyParser hpp = new HierarchyPropertyParser();
1162          hpp.build(typeOption, ", ");
1163          hpps.put(root, hpp);
1164        }
1165      } catch (Exception ex) {
1166        System.err.println("Invalid property: " + typeOptions);
1167      }     
1168    }
1169    return hpps;
1170  }
1171 
1172  /**
1173   * Updates the list of selectable object names, adding any new names to the list.
1174   */
1175  protected void updateObjectNames() {
1176   
1177    if (m_ObjectNames == null) {
1178      m_ObjectNames = getClassesFromProperties();
1179    }
1180   
1181    if (m_Object != null) {
1182      String className = m_Object.getClass().getName();
1183      String root = getRootFromClass(className, new HierarchyPropertyParser().getSeperator());
1184      HierarchyPropertyParser hpp = (HierarchyPropertyParser) m_ObjectNames.get(root);
1185      if (hpp != null) {
1186        if(!hpp.contains(className)){
1187          hpp.add(className);
1188        }
1189      }
1190    }
1191  }
1192 
1193  /**
1194   * Sets whether the editor is "enabled", meaning that the current
1195   * values will be painted.
1196   *
1197   * @param newVal a value of type 'boolean'
1198   */
1199  public void setEnabled(boolean newVal) {
1200   
1201    if (newVal != m_Enabled) {
1202      m_Enabled = newVal;
1203    }
1204  }
1205 
1206  /**
1207   * Sets the class of values that can be edited.
1208   *
1209   * @param type a value of type 'Class'
1210   */
1211  public void setClassType(Class type) {
1212   
1213    m_ClassType = type;
1214    m_ObjectNames = getClassesFromProperties();
1215  }
1216 
1217  /**
1218   * Sets the current object to be the default, taken as the first item in
1219   * the chooser.
1220   */
1221  public void setDefaultValue() {
1222   
1223    if (m_ClassType == null) {
1224      System.err.println("No ClassType set up for GenericObjectEditor!!");
1225      return;
1226    }   
1227   
1228    Hashtable hpps = getClassesFromProperties();
1229    HierarchyPropertyParser hpp = null;
1230    Enumeration enm = hpps.elements();
1231   
1232    try{
1233      while (enm.hasMoreElements()) {
1234        hpp = (HierarchyPropertyParser) enm.nextElement(); 
1235        if(hpp.depth() > 0) {           
1236          hpp.goToRoot();
1237          while(!hpp.isLeafReached())
1238            hpp.goToChild(0);
1239         
1240          String defaultValue = hpp.fullValue();
1241          setValue(Class.forName(defaultValue).newInstance());
1242        }
1243      }
1244    }catch(Exception ex){
1245      System.err.println("Problem loading the first class: "+
1246                         hpp.fullValue());
1247      ex.printStackTrace();
1248    }
1249  }
1250 
1251  /**
1252   * Sets the current Object. If the Object is in the
1253   * Object chooser, this becomes the selected item (and added
1254   * to the chooser if necessary).
1255   *
1256   * @param o an object that must be a Object.
1257   */
1258  public void setValue(Object o) {
1259   
1260    if (m_ClassType == null) {
1261      System.err.println("No ClassType set up for GenericObjectEditor!!");
1262      return;
1263    }
1264    if (!m_ClassType.isAssignableFrom(o.getClass())) {
1265      System.err.println("setValue object not of correct type!");
1266      return;
1267    }
1268   
1269    setObject(o);
1270
1271    if (m_EditorComponent != null) m_EditorComponent.repaint();
1272
1273    updateObjectNames();
1274  }
1275 
1276  /**
1277   * Sets the current Object.
1278   *
1279   * @param c a value of type 'Object'
1280   */
1281  protected void setObject(Object c) {
1282   
1283    // This should really call equals() for comparison.
1284    boolean trueChange ;
1285    if (getValue() != null) {
1286      trueChange = (!c.equals(getValue()));
1287    }
1288    else
1289      trueChange = true;
1290   
1291    m_Backup = m_Object;
1292   
1293    m_Object = c;
1294   
1295    if (m_EditorComponent != null) {
1296      m_EditorComponent.updateChildPropertySheet();
1297    }
1298    if (trueChange) {
1299      m_Support.firePropertyChange("", null, null);
1300    }
1301  }
1302 
1303  /**
1304   * Gets the current Object.
1305   *
1306   * @return the current Object
1307   */
1308  public Object getValue() {
1309   
1310    Object result = null;
1311    try {
1312      result = makeCopy(m_Object);
1313    } catch (Exception ex) {
1314      ex.printStackTrace();
1315    }
1316    return result;
1317  }
1318 
1319  /**
1320   * Supposedly returns an initialization string to create a Object
1321   * identical to the current one, including it's state, but this doesn't
1322   * appear possible given that the initialization string isn't supposed to
1323   * contain multiple statements.
1324   *
1325   * @return the java source code initialisation string
1326   */
1327  public String getJavaInitializationString() {
1328
1329    return "new " + m_Object.getClass().getName() + "()";
1330  }
1331
1332  /**
1333   * Returns true to indicate that we can paint a representation of the
1334   * Object.
1335   *
1336   * @return true
1337   */
1338  public boolean isPaintable() {
1339
1340    return true;
1341  }
1342
1343  /**
1344   * Paints a representation of the current Object.
1345   *
1346   * @param gfx the graphics context to use
1347   * @param box the area we are allowed to paint into
1348   */
1349  public void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box) {
1350
1351    if (m_Enabled) {
1352      String rep;
1353      if (m_Object != null) {
1354        rep = m_Object.getClass().getName();
1355      } else {
1356        rep = "None";
1357      }
1358      int dotPos = rep.lastIndexOf('.');
1359      if (dotPos != -1) {
1360        rep = rep.substring(dotPos + 1);
1361      }
1362      /*
1363      if (m_Object instanceof OptionHandler) {
1364        rep += " " + Utils.joinOptions(((OptionHandler)m_Object)
1365                                       .getOptions());
1366      }
1367      */
1368      java.awt.Font originalFont = gfx.getFont();
1369      gfx.setFont(originalFont.deriveFont(java.awt.Font.BOLD));
1370
1371      FontMetrics fm = gfx.getFontMetrics();
1372      int vpad = (box.height - fm.getHeight());
1373      gfx.drawString(rep, 2, fm.getAscent() + vpad);
1374      int repwidth = fm.stringWidth(rep);
1375
1376      gfx.setFont(originalFont);
1377      if (m_Object instanceof OptionHandler) {
1378        gfx.drawString(" " + Utils.joinOptions(((OptionHandler)m_Object).getOptions()),
1379                                               repwidth + 2, fm.getAscent() + vpad);
1380      }
1381    }
1382  }
1383
1384  /**
1385   * Returns null as we don't support getting/setting values as text.
1386   *
1387   * @return null
1388   */
1389  public String getAsText() {
1390
1391    return null;
1392  }
1393
1394  /**
1395   * Returns null as we don't support getting/setting values as text.
1396   *
1397   * @param text the text value
1398   * @throws IllegalArgumentException as we don't support
1399   * getting/setting values as text.
1400   */
1401  public void setAsText(String text) {
1402
1403    throw new IllegalArgumentException(text);
1404  }
1405
1406  /**
1407   * Returns null as we don't support getting values as tags.
1408   *
1409   * @return null
1410   */
1411  public String[] getTags() {
1412
1413    return null;
1414  }
1415
1416  /**
1417   * Returns true because we do support a custom editor.
1418   *
1419   * @return true
1420   */
1421  public boolean supportsCustomEditor() {
1422
1423    return true;
1424  }
1425 
1426  /**
1427   * Returns the array editing component.
1428   *
1429   * @return a value of type 'java.awt.Component'
1430   */
1431  public java.awt.Component getCustomEditor() {
1432
1433    if (m_EditorComponent == null) {
1434      m_EditorComponent = new GOEPanel();
1435    }
1436    return m_EditorComponent;
1437  }
1438
1439  /**
1440   * Adds a PropertyChangeListener who will be notified of value changes.
1441   *
1442   * @param l a value of type 'PropertyChangeListener'
1443   */
1444  public void addPropertyChangeListener(PropertyChangeListener l) {
1445
1446    m_Support.addPropertyChangeListener(l);
1447  }
1448
1449  /**
1450   * Removes a PropertyChangeListener.
1451   *
1452   * @param l a value of type 'PropertyChangeListener'
1453   */
1454  public void removePropertyChangeListener(PropertyChangeListener l) {
1455
1456    m_Support.removePropertyChangeListener(l);
1457  }
1458
1459  /**
1460   * Gets the custom panel used for editing the object.
1461   *
1462   * @return the panel
1463   */
1464  public JPanel getCustomPanel() {
1465    final JButton chooseButton = createChooseClassButton();   
1466    m_ObjectPropertyPanel = new PropertyPanel(this, true);
1467   
1468    JPanel customPanel = new JPanel() {
1469      public void setEnabled(boolean enabled) {
1470        super.setEnabled(enabled);
1471        chooseButton.setEnabled(enabled);
1472      }
1473    };
1474    customPanel.setLayout(new BorderLayout());
1475    customPanel.add(chooseButton, BorderLayout.WEST);
1476    customPanel.add(m_ObjectPropertyPanel, BorderLayout.CENTER);
1477    return customPanel;
1478  }
1479
1480  /**
1481   * Creates a button that when clicked will enable the user to change
1482   * the class of the object being edited.
1483   *
1484   * @return the choose button
1485   */
1486  protected JButton createChooseClassButton() {
1487
1488    JButton setButton = new JButton("Choose");
1489
1490    // anonymous action listener shows a JTree popup and allows the user
1491    // to choose the class they want
1492    setButton.addActionListener(new ActionListener() {
1493        public void actionPerformed(ActionEvent e) {
1494
1495          JPopupMenu popup = getChooseClassPopupMenu();
1496
1497          // show the popup where the source component is
1498          if (e.getSource() instanceof Component) {
1499            Component comp = (Component) e.getSource();
1500            popup.show(comp, comp.getX(), comp.getY());
1501            popup.pack();
1502            popup.repaint();
1503          }
1504        }
1505      });
1506
1507    return setButton;
1508  }
1509
1510  /**
1511   * creates a classname from the given path.
1512   *
1513   * @param path        the path to generate the classname from
1514   * @return            the generated classname
1515   */
1516  protected String getClassnameFromPath(TreePath path) {
1517    StringBuffer classname = new StringBuffer();
1518   
1519    // recreate class name from path
1520    int start = 0;
1521    if (m_ObjectNames.size() > 1)
1522      start = 1;
1523
1524    for (int i = start; i < path.getPathCount(); i++) {
1525      if (i>start) classname.append(".");
1526      classname.append(
1527          (String) ((GOETreeNode) path.getPathComponent(i)).getUserObject());
1528    }
1529   
1530    return classname.toString();
1531  }
1532 
1533  /**
1534   * Returns a popup menu that allows the user to change
1535   * the class of object.
1536   *
1537   * @return a JPopupMenu that when shown will let the user choose the class
1538   */
1539  public JPopupMenu getChooseClassPopupMenu() {
1540
1541    updateObjectNames();
1542
1543    // create the tree, and find the path to the current class
1544    m_treeNodeOfCurrentObject = null;
1545    final JTree tree = createTree(m_ObjectNames);
1546    if (m_treeNodeOfCurrentObject != null) {
1547      tree.setSelectionPath(new TreePath(m_treeNodeOfCurrentObject.getPath()));
1548    }
1549    tree.getSelectionModel().setSelectionMode
1550      (TreeSelectionModel.SINGLE_TREE_SELECTION);
1551
1552    // create the popup
1553    final JPopupMenu popup = new JTreePopupMenu(tree);
1554
1555    // respond when the user chooses a class
1556    tree.addTreeSelectionListener(new TreeSelectionListener() {
1557        public void valueChanged(TreeSelectionEvent e) {
1558          GOETreeNode node = (GOETreeNode) tree.getLastSelectedPathComponent();
1559         
1560          if (node == null) 
1561            return;
1562         
1563          if (node.isLeaf()) {
1564            /*if (node.m_Capabilities != null && m_CapabilitiesFilter != null) {
1565              if (!node.m_Capabilities.supportsMaybe(m_CapabilitiesFilter) &&
1566                  !node.m_Capabilities.supports(m_CapabilitiesFilter)) {
1567                return;
1568              }
1569            } */
1570            classSelected(getClassnameFromPath(tree.getSelectionPath()));
1571            popup.setVisible(false);
1572          }
1573        }
1574      });
1575   
1576    return popup;
1577  }
1578
1579  /**
1580   * Creates a JTree from an object heirarchy.
1581   *
1582   * @param hpps the hierarchy of objects to mirror in the tree
1583   * @return a JTree representation of the hierarchy
1584   */
1585  protected JTree createTree(Hashtable hpps) {
1586    GOETreeNode             superRoot;
1587    Enumeration             enm;
1588    HierarchyPropertyParser hpp;
1589   
1590    if (hpps.size() > 1)
1591      superRoot = new GOETreeNode("root");
1592    else
1593      superRoot = null;
1594
1595    enm = hpps.elements();
1596    while (enm.hasMoreElements()) {
1597      hpp = (HierarchyPropertyParser) enm.nextElement();
1598      hpp.goToRoot();
1599      GOETreeNode root = new GOETreeNode(hpp.getValue());
1600      addChildrenToTree(root, hpp);
1601     
1602      if (superRoot == null)
1603        superRoot = root;
1604      else
1605        superRoot.add(root);
1606    }
1607   
1608    JTree tree = new JTree(superRoot);
1609   
1610    return tree;
1611  }
1612
1613  /**
1614   * Recursively builds a JTree from an object heirarchy.
1615   * Also updates m_treeNodeOfCurrentObject if the current object
1616   * is discovered during creation.
1617   *
1618   * @param tree the root of the tree to add children to
1619   * @param hpp the hierarchy of objects to mirror in the tree
1620   */
1621  protected void addChildrenToTree(GOETreeNode tree,
1622                                   HierarchyPropertyParser hpp) {
1623
1624    try {
1625      for (int i=0; i<hpp.numChildren(); i++) {
1626        hpp.goToChild(i);
1627        GOETreeNode child = new GOETreeNode(hpp.getValue());
1628        if ((m_Object != null) &&
1629            m_Object.getClass().getName().equals(hpp.fullValue())) {
1630          m_treeNodeOfCurrentObject = child;
1631        }
1632        tree.add(child);
1633        addChildrenToTree(child, hpp);
1634        hpp.goToParent();
1635      }
1636    } catch (Exception e) {
1637      e.printStackTrace();
1638    }
1639  }
1640
1641  /**
1642   * Called when the user selects an class type to change to.
1643   *
1644   * @param className the name of the class that was selected
1645   */
1646  protected void classSelected(String className) {
1647
1648    try {                   
1649      if ((m_Object != null) && m_Object.getClass().getName().equals(className)) {
1650        return;
1651      }
1652     
1653      setValue(Class.forName(className).newInstance());
1654      //m_ObjectPropertyPanel.showPropertyDialog();
1655      if (m_EditorComponent != null) {
1656        m_EditorComponent.updateChildPropertySheet();
1657      }
1658    } catch (Exception ex) {
1659      JOptionPane.showMessageDialog(null,
1660                                    "Could not create an example of\n"
1661                                    + className + "\n"
1662                                    + "from the current classpath",
1663                                    "Class load failed",
1664                                    JOptionPane.ERROR_MESSAGE);
1665      ex.printStackTrace();
1666      try {
1667        if(m_Backup != null)
1668          setValue(m_Backup);
1669        else
1670          setDefaultValue();                   
1671      } catch(Exception e) {
1672        System.err.println(ex.getMessage());
1673        ex.printStackTrace();
1674      }
1675    }
1676  }
1677 
1678  /**
1679   * Sets the capabilities to use for filtering.
1680   *
1681   * @param value       the object to get the filter capabilities from
1682   */
1683  public void setCapabilitiesFilter(Capabilities value) {
1684    m_CapabilitiesFilter = new Capabilities(null);
1685    m_CapabilitiesFilter.assign(value);
1686  }
1687 
1688  /**
1689   * Returns the current Capabilities filter, can be null.
1690   *
1691   * @return            the current Capabiliities used for filtering
1692   */
1693  public Capabilities getCapabilitiesFilter() {
1694    return m_CapabilitiesFilter;
1695  }
1696 
1697  /**
1698   * Removes the current Capabilities filter.
1699   */
1700  public void removeCapabilitiesFilter() {
1701    m_CapabilitiesFilter = null;
1702  }
1703
1704  /**
1705   * Makes a copy of an object using serialization.
1706   *
1707   * @param source the object to copy
1708   * @return a copy of the source object
1709   * @exception Exception if the copy fails
1710   */
1711  public static Object makeCopy(Object source) throws Exception {
1712    SerializedObject so = new SerializedObject(source);
1713    Object result = so.getObject();
1714    return result;
1715  }
1716 
1717  /**
1718   * Returns the available classnames for a certain property in the
1719   * props file.
1720   *
1721   * @param property    the property to get the classnames for
1722   * @return            the classnames
1723   */
1724  public static Vector<String> getClassnames(String property) {
1725    Vector<String>      result;
1726    String              value;
1727    String[]            items;
1728    int                 i;
1729   
1730    result = new Vector<String>();
1731   
1732    value = EDITOR_PROPERTIES.getProperty(property, "").replaceAll(" ", "").trim();
1733    if (value.length() > 0) {
1734      items = value.split(",");
1735      for (i = 0; i < items.length; i++)
1736        result.add(items[i]);
1737    }
1738   
1739    return result;
1740  }
1741
1742  /**
1743   * Tests out the Object editor from the command line.
1744   *
1745   * @param args may contain the class name of a Object to edit
1746   */
1747  public static void main(String [] args) {
1748
1749    try {
1750      GenericObjectEditor.registerEditors();
1751      GenericObjectEditor ce = new GenericObjectEditor(true);
1752      ce.setClassType(weka.classifiers.Classifier.class);
1753      Object initial = new weka.classifiers.rules.ZeroR();
1754      if (args.length > 0){
1755        ce.setClassType(Class.forName(args[0]));
1756        if(args.length > 1){
1757          initial = (Object)Class.forName(args[1]).newInstance();
1758          ce.setValue(initial);
1759        }
1760        else
1761          ce.setDefaultValue();
1762      }
1763      else       
1764        ce.setValue(initial);
1765     
1766      PropertyDialog pd = new PropertyDialog((Frame) null, ce, 100, 100);
1767      pd.addWindowListener(new WindowAdapter() {
1768          public void windowClosing(WindowEvent e) {
1769            PropertyEditor pe = ((PropertyDialog)e.getSource()).getEditor();
1770            Object c = (Object)pe.getValue();
1771            String options = "";
1772            if (c instanceof OptionHandler) {
1773              options = Utils.joinOptions(((OptionHandler)c).getOptions());
1774            }
1775            System.out.println(c.getClass().getName() + " " + options);
1776            System.exit(0);
1777          }
1778        });
1779      pd.setVisible(true);
1780    } catch (Exception ex) {
1781      ex.printStackTrace();
1782      System.err.println(ex.getMessage());
1783    }
1784  }
1785}
Note: See TracBrowser for help on using the repository browser.