0001: /*
0002: * This program is free software; you can redistribute it and/or modify
0003: * it under the terms of the GNU General Public License as published by
0004: * the Free Software Foundation; either version 2 of the License, or
0005: * (at your option) any later version.
0006: *
0007: * This program is distributed in the hope that it will be useful,
0008: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0009: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0010: * GNU General Public License for more details.
0011: *
0012: * You should have received a copy of the GNU General Public License
0013: * along with this program; if not, write to the Free Software
0014: * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
0015: */
0016:
0017: /*
0018: * GenericObjectEditor.java
0019: * Copyright (C) 2002 University of Waikato, Hamilton, New Zealand
0020: *
0021: */
0022:
0023: package weka.gui;
0024:
0025: import weka.core.Capabilities;
0026: import weka.core.CapabilitiesHandler;
0027: import weka.core.ClassDiscovery;
0028: import weka.core.OptionHandler;
0029: import weka.core.SerializedObject;
0030: import weka.core.Utils;
0031: import weka.core.Capabilities.Capability;
0032: import weka.gui.CheckBoxList.CheckBoxListModel;
0033:
0034: import java.awt.BorderLayout;
0035: import java.awt.Component;
0036: import java.awt.Dimension;
0037: import java.awt.FlowLayout;
0038: import java.awt.FontMetrics;
0039: import java.awt.GridLayout;
0040: import java.awt.Window;
0041: import java.awt.event.ActionEvent;
0042: import java.awt.event.ActionListener;
0043: import java.awt.event.WindowAdapter;
0044: import java.awt.event.WindowEvent;
0045: import java.beans.PropertyChangeEvent;
0046: import java.beans.PropertyChangeListener;
0047: import java.beans.PropertyChangeSupport;
0048: import java.beans.PropertyEditor;
0049: import java.beans.PropertyEditorManager;
0050: import java.io.BufferedInputStream;
0051: import java.io.BufferedOutputStream;
0052: import java.io.File;
0053: import java.io.FileInputStream;
0054: import java.io.FileOutputStream;
0055: import java.io.ObjectInputStream;
0056: import java.io.ObjectOutputStream;
0057: import java.lang.reflect.Array;
0058: import java.util.Enumeration;
0059: import java.util.Hashtable;
0060: import java.util.Properties;
0061: import java.util.StringTokenizer;
0062: import java.util.Vector;
0063:
0064: import javax.swing.BorderFactory;
0065: import javax.swing.JButton;
0066: import javax.swing.JDialog;
0067: import javax.swing.JFileChooser;
0068: import javax.swing.JLabel;
0069: import javax.swing.JOptionPane;
0070: import javax.swing.JPanel;
0071: import javax.swing.JPopupMenu;
0072: import javax.swing.JScrollPane;
0073: import javax.swing.JTree;
0074: import javax.swing.event.TreeSelectionEvent;
0075: import javax.swing.event.TreeSelectionListener;
0076: import javax.swing.tree.DefaultMutableTreeNode;
0077: import javax.swing.tree.TreePath;
0078: import javax.swing.tree.TreeSelectionModel;
0079:
0080: /**
0081: * A PropertyEditor for objects. It can be used either in a static or a dynamic
0082: * way. <br>
0083: * <br>
0084: * In the <b>static</b> way (<code>USE_DYNAMIC</code> is <code>false</code>) the
0085: * objects have been defined as editable in the GenericObjectEditor
0086: * configuration file, which lists possible values that can be selected from,
0087: * and themselves configured. The configuration file is called
0088: * "GenericObjectEditor.props" and may live in either the location given by
0089: * "user.home" or the current directory (this last will take precedence), and a
0090: * default properties file is read from the Weka distribution. For speed, the
0091: * properties file is read only once when the class is first loaded -- this may
0092: * need to be changed if we ever end up running in a Java OS ;-). <br>
0093: * <br>
0094: * If it is used in a <b>dynamic</b> way (the <code>UseDynamic</code> property
0095: * of the GenericPropertiesCreator props file is set to <code>true</code>)
0096: * then the classes to list are discovered by the
0097: * <code>GenericPropertiesCreator</code> class (it checks the complete classpath).
0098: *
0099: * @see GenericPropertiesCreator
0100: * @see GenericPropertiesCreator#useDynamic()
0101: * @see GenericPropertiesCreator#CREATOR_FILE
0102: * @see weka.core.ClassDiscovery
0103: *
0104: * @author Len Trigg (trigg@cs.waikato.ac.nz)
0105: * @author Xin Xu (xx5@cs.waikato.ac.nz)
0106: * @author Richard Kirkby (rkirkby@cs.waikato.ac.nz)
0107: * @author FracPete (fracpete at waikato dot ac dot nz)
0108: * @version $Revision: 1.57 $
0109: */
0110: public class GenericObjectEditor implements PropertyEditor,
0111: CustomPanelSupplier {
0112:
0113: /** The object being configured */
0114: protected Object m_Object;
0115:
0116: /** Holds a copy of the current object that can be reverted to
0117: if the user decides to cancel */
0118: protected Object m_Backup;
0119:
0120: /** Handles property change notification */
0121: protected PropertyChangeSupport m_Support = new PropertyChangeSupport(
0122: this );
0123:
0124: /** The Class of objects being edited */
0125: protected Class m_ClassType;
0126:
0127: /** The model containing the list of names to select from */
0128: protected Hashtable m_ObjectNames;
0129:
0130: /** The GUI component for editing values, created when needed */
0131: protected GOEPanel m_EditorComponent;
0132:
0133: /** True if the GUI component is needed */
0134: protected boolean m_Enabled = true;
0135:
0136: /** The name of the properties file */
0137: protected static String PROPERTY_FILE = "weka/gui/GenericObjectEditor.props";
0138:
0139: /** Contains the editor properties */
0140: protected static Properties EDITOR_PROPERTIES;
0141:
0142: /** the properties files containing the class/editor mappings */
0143: public static final String GUIEDITORS_PROPERTY_FILE = "weka/gui/GUIEditors.props";
0144:
0145: /** The tree node of the current object so we can re-select it for the user */
0146: protected GOETreeNode m_treeNodeOfCurrentObject;
0147:
0148: /** The property panel created for the objects */
0149: protected PropertyPanel m_ObjectPropertyPanel;
0150:
0151: /** whether the class can be changed */
0152: protected boolean m_canChangeClassInDialog;
0153:
0154: /** whether the Weka Editors were already registered */
0155: protected static boolean m_EditorsRegistered;
0156:
0157: /** for filtering the tree based on the Capabilities of the leaves */
0158: protected Capabilities m_CapabilitiesFilter = null;
0159:
0160: /**
0161: * Loads the configuration property file (USE_DYNAMIC is FALSE) or determines
0162: * the classes dynamically (USE_DYNAMIC is TRUE)
0163: * @see #USE_DYNAMIC
0164: * @see GenericPropertiesCreator
0165: */
0166: static {
0167:
0168: try {
0169: GenericPropertiesCreator creator = new GenericPropertiesCreator();
0170:
0171: // dynamic approach?
0172: if (creator.useDynamic()) {
0173: try {
0174: creator.execute(false);
0175: EDITOR_PROPERTIES = creator.getOutputProperties();
0176: } catch (Exception e) {
0177: JOptionPane
0178: .showMessageDialog(
0179: null,
0180: "Could not determine the properties for the generic object\n"
0181: + "editor. This exception was produced:\n"
0182: + e.toString(),
0183: "GenericObjectEditor",
0184: JOptionPane.ERROR_MESSAGE);
0185: }
0186: } else {
0187: // Allow a properties file in the current directory to override
0188: try {
0189: EDITOR_PROPERTIES = Utils
0190: .readProperties(PROPERTY_FILE);
0191: java.util.Enumeration keys = (java.util.Enumeration) EDITOR_PROPERTIES
0192: .propertyNames();
0193: if (!keys.hasMoreElements()) {
0194: throw new Exception(
0195: "Failed to read a property file for the "
0196: + "generic object editor");
0197: }
0198: } catch (Exception ex) {
0199: JOptionPane
0200: .showMessageDialog(
0201: null,
0202: "Could not read a configuration file for the generic object\n"
0203: + "editor. An example file is included with the Weka distribution.\n"
0204: + "This file should be named \""
0205: + PROPERTY_FILE
0206: + "\" and\n"
0207: + "should be placed either in your user home (which is set\n"
0208: + "to \""
0209: + System
0210: .getProperties()
0211: .getProperty(
0212: "user.home")
0213: + "\")\n"
0214: + "or the directory that java was started from\n",
0215: "GenericObjectEditor",
0216: JOptionPane.ERROR_MESSAGE);
0217: }
0218: }
0219: } catch (Exception e) {
0220: JOptionPane.showMessageDialog(null,
0221: "Could not initialize the GenericPropertiesCreator. "
0222: + "This exception was produced:\n"
0223: + e.toString(), "GenericObjectEditor",
0224: JOptionPane.ERROR_MESSAGE);
0225: }
0226: }
0227:
0228: /**
0229: * A specialized TreeNode for supporting filtering via Capabilities
0230: */
0231: public class GOETreeNode extends DefaultMutableTreeNode {
0232:
0233: /** for serialization */
0234: static final long serialVersionUID = -1707872446682150133L;
0235:
0236: /** color for "no support" */
0237: public final static String NO_SUPPORT = "red";
0238:
0239: /** color for "maybe support" */
0240: public final static String MAYBE_SUPPORT = "blue";
0241:
0242: /** the Capabilities object to use for filtering */
0243: protected Capabilities m_Capabilities = null;
0244:
0245: /**
0246: * Creates a tree node that has no parent and no children, but which
0247: * allows children.
0248: */
0249: public GOETreeNode() {
0250: super ();
0251: }
0252:
0253: /**
0254: * Creates a tree node with no parent, no children, but which allows
0255: * children, and initializes it with the specified user object.
0256: *
0257: * @param userObject an Object provided by the user that constitutes
0258: * the node's data
0259: */
0260: public GOETreeNode(Object userObject) {
0261: super (userObject);
0262: }
0263:
0264: /**
0265: * Creates a tree node with no parent, no children, initialized with the
0266: * specified user object, and that allows children only if specified.
0267:
0268: * @param userObject an Object provided by the user that constitutes
0269: * the node's data
0270: * @param allowsChildren if true, the node is allowed to have child nodes
0271: * -- otherwise, it is always a leaf node
0272: */
0273: public GOETreeNode(Object userObject, boolean allowsChildren) {
0274: super (userObject, allowsChildren);
0275: }
0276:
0277: /**
0278: * generates if necessary a Capabilities object for the given leaf
0279: */
0280: protected void initCapabilities() {
0281: String classname;
0282: Class cls;
0283: Object obj;
0284:
0285: if (m_Capabilities != null)
0286: return;
0287: if (!isLeaf())
0288: return;
0289:
0290: classname = getClassnameFromPath(new TreePath(getPath()));
0291: try {
0292: cls = Class.forName(classname);
0293: if (!ClassDiscovery.hasInterface(
0294: CapabilitiesHandler.class, cls))
0295: return;
0296:
0297: obj = cls.newInstance();
0298: m_Capabilities = ((CapabilitiesHandler) obj)
0299: .getCapabilities();
0300: } catch (Exception e) {
0301: // ignore it
0302: }
0303: }
0304:
0305: /**
0306: * returns a string representation of this treenode
0307: *
0308: * @return the text to display
0309: */
0310: public String toString() {
0311: String result;
0312:
0313: result = super .toString();
0314:
0315: if (m_CapabilitiesFilter != null) {
0316: initCapabilities();
0317: if (m_Capabilities != null) {
0318: if (m_Capabilities
0319: .supportsMaybe(m_CapabilitiesFilter)
0320: && !m_Capabilities
0321: .supports(m_CapabilitiesFilter))
0322: result = "<html><font color=\"" + MAYBE_SUPPORT
0323: + "\">" + result + "</font></i><html>";
0324: else if (!m_Capabilities
0325: .supports(m_CapabilitiesFilter))
0326: result = "<html><font color=\"" + NO_SUPPORT
0327: + "\">" + result + "</font></i><html>";
0328: }
0329: }
0330:
0331: return result;
0332: }
0333: }
0334:
0335: /**
0336: * A dialog for selecting Capabilities to look for in the GOE tree.
0337: */
0338: public class CapabilitiesFilterDialog extends JDialog {
0339:
0340: /** for serialization */
0341: static final long serialVersionUID = -7845503345689646266L;
0342:
0343: /** the dialog itself */
0344: protected JDialog m_Self;
0345:
0346: /** the popup to display again */
0347: protected JPopupMenu m_Popup = null;
0348:
0349: /** the capabilities used for initializing the dialog */
0350: protected Capabilities m_Capabilities = new Capabilities(null);
0351:
0352: /** the label, listing the name of the superclass */
0353: protected JLabel m_InfoLabel = new JLabel();
0354:
0355: /** the list with all the capabilities */
0356: protected CheckBoxList m_List = new CheckBoxList();
0357:
0358: /** the OK button */
0359: protected JButton m_OkButton = new JButton("OK");
0360:
0361: /** the Cancel button */
0362: protected JButton m_CancelButton = new JButton("Cancel");
0363:
0364: /**
0365: * creates a dialog to choose Capabilities from
0366: */
0367: public CapabilitiesFilterDialog() {
0368: super ();
0369:
0370: m_Self = this ;
0371:
0372: initGUI();
0373: }
0374:
0375: /**
0376: * sets up the GUI
0377: */
0378: protected void initGUI() {
0379: JPanel panel;
0380: CheckBoxListModel model;
0381:
0382: setTitle("Filtering Capabilities...");
0383: setLayout(new BorderLayout());
0384:
0385: panel = new JPanel(new BorderLayout());
0386: panel
0387: .setBorder(BorderFactory.createEmptyBorder(5, 5, 5,
0388: 5));
0389: getContentPane().add(panel, BorderLayout.NORTH);
0390: m_InfoLabel
0391: .setText("<html>"
0392: + m_ClassType.getName().replaceAll(".*\\.",
0393: "")
0394: + "s"
0395: + " have to support <i>at least</i> the following capabilities <br>"
0396: + "(the ones highlighted <font color=\""
0397: + GOETreeNode.NO_SUPPORT
0398: + "\">"
0399: + GOETreeNode.NO_SUPPORT
0400: + "</font> don't meet these requirements <br>"
0401: + "the ones highlighted <font color=\""
0402: + GOETreeNode.MAYBE_SUPPORT + "\">"
0403: + GOETreeNode.MAYBE_SUPPORT
0404: + "</font> possibly meet them):"
0405: + "</html>");
0406: panel.add(m_InfoLabel, BorderLayout.CENTER);
0407:
0408: // list
0409: getContentPane().add(new JScrollPane(m_List),
0410: BorderLayout.CENTER);
0411: model = (CheckBoxListModel) m_List.getModel();
0412: for (Capability cap : Capability.values())
0413: model.addElement(cap);
0414:
0415: // buttons
0416: panel = new JPanel(new FlowLayout(FlowLayout.CENTER));
0417: getContentPane().add(panel, BorderLayout.SOUTH);
0418:
0419: m_OkButton.setMnemonic('O');
0420: m_OkButton.addActionListener(new ActionListener() {
0421: public void actionPerformed(ActionEvent e) {
0422: updateCapabilities();
0423: if (m_CapabilitiesFilter == null)
0424: m_CapabilitiesFilter = new Capabilities(null);
0425: m_CapabilitiesFilter.assign(m_Capabilities);
0426: m_Self.setVisible(false);
0427: showPopup();
0428: }
0429: });
0430: panel.add(m_OkButton);
0431:
0432: m_CancelButton.setMnemonic('C');
0433: m_CancelButton.addActionListener(new ActionListener() {
0434: public void actionPerformed(ActionEvent e) {
0435: m_Self.setVisible(false);
0436: showPopup();
0437: }
0438: });
0439: panel.add(m_CancelButton);
0440: pack();
0441: }
0442:
0443: /**
0444: * transfers the Capabilities object to the JList
0445: *
0446: * @see #m_Capabilities
0447: * @see #m_List
0448: */
0449: protected void updateList() {
0450: CheckBoxListModel model;
0451:
0452: model = (CheckBoxListModel) m_List.getModel();
0453:
0454: for (Capability cap : Capability.values())
0455: model.setChecked(model.indexOf(cap), m_Capabilities
0456: .handles(cap));
0457: }
0458:
0459: /**
0460: * transfers the selected Capabilities from the JList to the
0461: * Capabilities object
0462: *
0463: * @see #m_Capabilities
0464: * @see #m_List
0465: */
0466: protected void updateCapabilities() {
0467: CheckBoxListModel model;
0468:
0469: model = (CheckBoxListModel) m_List.getModel();
0470:
0471: for (Capability cap : Capability.values()) {
0472: if (model.getChecked(model.indexOf(cap)))
0473: m_Capabilities.enable(cap);
0474: else
0475: m_Capabilities.disable(cap);
0476: }
0477: }
0478:
0479: /**
0480: * sets the initial capabilities
0481: *
0482: * @param value the capabilities to use
0483: */
0484: public void setCapabilities(Capabilities value) {
0485: if (value != null)
0486: m_Capabilities.assign(value);
0487: else
0488: m_Capabilities = new Capabilities(null);
0489:
0490: updateList();
0491: }
0492:
0493: /**
0494: * returns the currently selected capabilities
0495: *
0496: * @return the currently selected capabilities
0497: */
0498: public Capabilities getCapabilities() {
0499: return m_Capabilities;
0500: }
0501:
0502: /**
0503: * sets the JPopupMenu to display again after closing the dialog
0504: *
0505: * @param value the JPopupMenu to display again
0506: */
0507: public void setPopup(JPopupMenu value) {
0508: m_Popup = value;
0509: }
0510:
0511: /**
0512: * returns the currently set JPopupMenu
0513: *
0514: * @return the current JPopupMenu
0515: */
0516: public JPopupMenu getPopup() {
0517: return m_Popup;
0518: }
0519:
0520: /**
0521: * if a JPopupMenu is set, it is displayed again. Displaying this dialog
0522: * closes any JPopupMenu automatically.
0523: */
0524: public void showPopup() {
0525: if (getPopup() != null)
0526: getPopup().setVisible(true);
0527: }
0528: }
0529:
0530: /**
0531: * Creates a popup menu containing a tree that is aware
0532: * of the screen dimensions.
0533: */
0534: public class JTreePopupMenu extends JPopupMenu {
0535:
0536: /** for serialization */
0537: static final long serialVersionUID = -3404546329655057387L;
0538:
0539: /** the popup itself */
0540: private JPopupMenu m_Self;
0541:
0542: /** The tree */
0543: private JTree m_tree;
0544:
0545: /** The scroller */
0546: private JScrollPane m_scroller;
0547:
0548: /** The filter button in case of CapabilitiesHandlers */
0549: private JButton m_FilterButton = new JButton("Filter...");
0550:
0551: /** The remove filter button in case of CapabilitiesHandlers */
0552: private JButton m_RemoveFilterButton = new JButton(
0553: "Remove filter");
0554:
0555: /** The button for closing the popup again */
0556: private JButton m_CloseButton = new JButton("Close");
0557:
0558: /**
0559: * Constructs a new popup menu.
0560: *
0561: * @param tree the tree to put in the menu
0562: */
0563: public JTreePopupMenu(JTree tree) {
0564:
0565: m_Self = this ;
0566:
0567: setLayout(new BorderLayout());
0568: JPanel panel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
0569: add(panel, BorderLayout.SOUTH);
0570:
0571: if (ClassDiscovery.hasInterface(CapabilitiesHandler.class,
0572: m_ClassType)) {
0573: // filter
0574: m_FilterButton.setMnemonic('F');
0575: m_FilterButton.addActionListener(new ActionListener() {
0576: public void actionPerformed(ActionEvent e) {
0577: if (e.getSource() == m_FilterButton) {
0578: CapabilitiesFilterDialog dialog = new CapabilitiesFilterDialog();
0579: dialog
0580: .setCapabilities(m_CapabilitiesFilter);
0581: dialog.setPopup(m_Self);
0582: dialog.setVisible(true);
0583: repaint();
0584: }
0585: }
0586: });
0587: panel.add(m_FilterButton);
0588:
0589: // remove
0590: m_RemoveFilterButton.setMnemonic('R');
0591: m_RemoveFilterButton
0592: .addActionListener(new ActionListener() {
0593: public void actionPerformed(ActionEvent e) {
0594: if (e.getSource() == m_RemoveFilterButton) {
0595: m_CapabilitiesFilter = null;
0596: repaint();
0597: }
0598: }
0599: });
0600: panel.add(m_RemoveFilterButton);
0601: }
0602:
0603: // close
0604: m_CloseButton.setMnemonic('C');
0605: m_CloseButton.addActionListener(new ActionListener() {
0606: public void actionPerformed(ActionEvent e) {
0607: if (e.getSource() == m_CloseButton) {
0608: m_Self.setVisible(false);
0609: }
0610: }
0611: });
0612: panel.add(m_CloseButton);
0613:
0614: m_tree = tree;
0615:
0616: JPanel treeView = new JPanel();
0617: treeView.setLayout(new BorderLayout());
0618: treeView.add(m_tree, BorderLayout.NORTH);
0619:
0620: // make backgrounds look the same
0621: treeView.setBackground(m_tree.getBackground());
0622:
0623: m_scroller = new JScrollPane(treeView);
0624:
0625: m_scroller.setPreferredSize(new Dimension(300, 400));
0626: m_scroller.getVerticalScrollBar().setUnitIncrement(20);
0627:
0628: add(m_scroller);
0629: }
0630:
0631: /**
0632: * Displays the menu, making sure it will fit on the screen.
0633: *
0634: * @param invoker the component thast invoked the menu
0635: * @param x the x location of the popup
0636: * @param y the y location of the popup
0637: */
0638: public void show(Component invoker, int x, int y) {
0639:
0640: super .show(invoker, x, y);
0641:
0642: // calculate available screen area for popup
0643: java.awt.Point location = getLocationOnScreen();
0644: java.awt.Dimension screenSize = getToolkit()
0645: .getScreenSize();
0646: int maxWidth = (int) (screenSize.getWidth() - location
0647: .getX());
0648: int maxHeight = (int) (screenSize.getHeight() - location
0649: .getY());
0650:
0651: // if the part of the popup goes off the screen then resize it
0652: Dimension scrollerSize = m_scroller.getPreferredSize();
0653: int height = (int) scrollerSize.getHeight();
0654: int width = (int) scrollerSize.getWidth();
0655: if (width > maxWidth)
0656: width = maxWidth;
0657: if (height > maxHeight)
0658: height = maxHeight;
0659:
0660: // commit any size changes
0661: m_scroller.setPreferredSize(new Dimension(width, height));
0662: revalidate();
0663: pack();
0664: }
0665: }
0666:
0667: /**
0668: * Handles the GUI side of editing values.
0669: */
0670: public class GOEPanel extends JPanel {
0671:
0672: /** for serialization */
0673: static final long serialVersionUID = 3656028520876011335L;
0674:
0675: /** The component that performs classifier customization */
0676: protected PropertySheetPanel m_ChildPropertySheet;
0677:
0678: /** The name of the current class */
0679: protected JLabel m_ClassNameLabel;
0680:
0681: /** Open object from disk */
0682: protected JButton m_OpenBut;
0683:
0684: /** Save object to disk */
0685: protected JButton m_SaveBut;
0686:
0687: /** ok button */
0688: protected JButton m_okBut;
0689:
0690: /** cancel button */
0691: protected JButton m_cancelBut;
0692:
0693: /** The filechooser for opening and saving object files */
0694: protected JFileChooser m_FileChooser;
0695:
0696: /** Creates the GUI editor component */
0697: public GOEPanel() {
0698:
0699: m_Backup = copyObject(m_Object);
0700:
0701: m_ClassNameLabel = new JLabel("None");
0702: m_ClassNameLabel.setBorder(BorderFactory.createEmptyBorder(
0703: 5, 5, 5, 5));
0704:
0705: m_ChildPropertySheet = new PropertySheetPanel();
0706: m_ChildPropertySheet
0707: .addPropertyChangeListener(new PropertyChangeListener() {
0708: public void propertyChange(
0709: PropertyChangeEvent evt) {
0710: m_Support
0711: .firePropertyChange("", null, null);
0712: }
0713: });
0714:
0715: m_OpenBut = new JButton("Open...");
0716: m_OpenBut.setToolTipText("Load a configured object");
0717: m_OpenBut.setEnabled(true);
0718: m_OpenBut.addActionListener(new ActionListener() {
0719: public void actionPerformed(ActionEvent e) {
0720: Object object = openObject();
0721: if (object != null) {
0722: // setValue takes care of: Making sure obj is of right type,
0723: // and firing property change.
0724: setValue(object);
0725: // Need a second setValue to get property values filled in OK.
0726: // Not sure why.
0727: setValue(object);
0728: }
0729: }
0730: });
0731:
0732: m_SaveBut = new JButton("Save...");
0733: m_SaveBut
0734: .setToolTipText("Save the current configured object");
0735: m_SaveBut.setEnabled(true);
0736: m_SaveBut.addActionListener(new ActionListener() {
0737: public void actionPerformed(ActionEvent e) {
0738: saveObject(m_Object);
0739: }
0740: });
0741:
0742: m_okBut = new JButton("OK");
0743: m_okBut.setEnabled(true);
0744: m_okBut.addActionListener(new ActionListener() {
0745: public void actionPerformed(ActionEvent e) {
0746:
0747: m_Backup = copyObject(m_Object);
0748: if ((getTopLevelAncestor() != null)
0749: && (getTopLevelAncestor() instanceof Window)) {
0750: Window w = (Window) getTopLevelAncestor();
0751: w.dispose();
0752: }
0753: }
0754: });
0755:
0756: m_cancelBut = new JButton("Cancel");
0757: m_cancelBut.setEnabled(true);
0758: m_cancelBut.addActionListener(new ActionListener() {
0759: public void actionPerformed(ActionEvent e) {
0760: if (m_Backup != null) {
0761:
0762: m_Object = copyObject(m_Backup);
0763:
0764: // To fire property change
0765: m_Support.firePropertyChange("", null, null);
0766: m_ObjectNames = getClassesFromProperties();
0767: updateObjectNames();
0768: updateChildPropertySheet();
0769: }
0770: if ((getTopLevelAncestor() != null)
0771: && (getTopLevelAncestor() instanceof Window)) {
0772: Window w = (Window) getTopLevelAncestor();
0773: w.dispose();
0774: }
0775: }
0776: });
0777:
0778: setLayout(new BorderLayout());
0779:
0780: if (m_canChangeClassInDialog) {
0781: JButton chooseButton = createChooseClassButton();
0782: JPanel top = new JPanel();
0783: top.setLayout(new BorderLayout());
0784: top.setBorder(BorderFactory.createEmptyBorder(5, 5, 5,
0785: 5));
0786: top.add(chooseButton, BorderLayout.WEST);
0787: top.add(m_ClassNameLabel, BorderLayout.CENTER);
0788: add(top, BorderLayout.NORTH);
0789: } else {
0790: add(m_ClassNameLabel, BorderLayout.NORTH);
0791: }
0792:
0793: add(m_ChildPropertySheet, BorderLayout.CENTER);
0794: // Since we resize to the size of the property sheet, a scrollpane isn't
0795: // typically needed
0796: // add(new JScrollPane(m_ChildPropertySheet), BorderLayout.CENTER);
0797:
0798: JPanel okcButs = new JPanel();
0799: okcButs.setBorder(BorderFactory.createEmptyBorder(5, 5, 5,
0800: 5));
0801: okcButs.setLayout(new GridLayout(1, 4, 5, 5));
0802: okcButs.add(m_OpenBut);
0803: okcButs.add(m_SaveBut);
0804: okcButs.add(m_okBut);
0805: okcButs.add(m_cancelBut);
0806: add(okcButs, BorderLayout.SOUTH);
0807:
0808: if (m_ClassType != null) {
0809: m_ObjectNames = getClassesFromProperties();
0810: if (m_Object != null) {
0811: updateObjectNames();
0812: updateChildPropertySheet();
0813: }
0814: }
0815: }
0816:
0817: /**
0818: * Enables/disables the cancel button.
0819: *
0820: * @param flag true to enable cancel button, false
0821: * to disable it
0822: */
0823: protected void setCancelButton(boolean flag) {
0824:
0825: if (m_cancelBut != null)
0826: m_cancelBut.setEnabled(flag);
0827: }
0828:
0829: /**
0830: * Opens an object from a file selected by the user.
0831: *
0832: * @return the loaded object, or null if the operation was cancelled
0833: */
0834: protected Object openObject() {
0835:
0836: if (m_FileChooser == null) {
0837: createFileChooser();
0838: }
0839: int returnVal = m_FileChooser.showOpenDialog(this );
0840: if (returnVal == JFileChooser.APPROVE_OPTION) {
0841: File selected = m_FileChooser.getSelectedFile();
0842: try {
0843: ObjectInputStream oi = new ObjectInputStream(
0844: new BufferedInputStream(
0845: new FileInputStream(selected)));
0846: Object obj = oi.readObject();
0847: oi.close();
0848: if (!m_ClassType.isAssignableFrom(obj.getClass())) {
0849: throw new Exception("Object not of type: "
0850: + m_ClassType.getName());
0851: }
0852: return obj;
0853: } catch (Exception ex) {
0854: JOptionPane.showMessageDialog(this ,
0855: "Couldn't read object: "
0856: + selected.getName() + "\n"
0857: + ex.getMessage(),
0858: "Open object file",
0859: JOptionPane.ERROR_MESSAGE);
0860: }
0861: }
0862: return null;
0863: }
0864:
0865: /**
0866: * Saves an object to a file selected by the user.
0867: *
0868: * @param object the object to save
0869: */
0870: protected void saveObject(Object object) {
0871:
0872: if (m_FileChooser == null) {
0873: createFileChooser();
0874: }
0875: int returnVal = m_FileChooser.showSaveDialog(this );
0876: if (returnVal == JFileChooser.APPROVE_OPTION) {
0877: File sFile = m_FileChooser.getSelectedFile();
0878: try {
0879: ObjectOutputStream oo = new ObjectOutputStream(
0880: new BufferedOutputStream(
0881: new FileOutputStream(sFile)));
0882: oo.writeObject(object);
0883: oo.close();
0884: } catch (Exception ex) {
0885: JOptionPane.showMessageDialog(this ,
0886: "Couldn't write to file: "
0887: + sFile.getName() + "\n"
0888: + ex.getMessage(), "Save object",
0889: JOptionPane.ERROR_MESSAGE);
0890: }
0891: }
0892: }
0893:
0894: /**
0895: * Creates the file chooser the user will use to save/load files with.
0896: */
0897: protected void createFileChooser() {
0898:
0899: m_FileChooser = new JFileChooser(new File(System
0900: .getProperty("user.dir")));
0901: m_FileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
0902: }
0903:
0904: /**
0905: * Makes a copy of an object using serialization
0906: * @param source the object to copy
0907: * @return a copy of the source object
0908: */
0909: protected Object copyObject(Object source) {
0910:
0911: Object result = null;
0912: try {
0913: result = GenericObjectEditor.this .makeCopy(source);
0914: setCancelButton(true);
0915:
0916: } catch (Exception ex) {
0917: setCancelButton(false);
0918: System.err
0919: .println("GenericObjectEditor: Problem making backup object");
0920: System.err.println(ex);
0921: }
0922: return result;
0923: }
0924:
0925: /**
0926: * Allows customization of the action label on the dialog.
0927: * @param newLabel the new string for the ok button
0928: */
0929: public void setOkButtonText(String newLabel) {
0930:
0931: m_okBut.setText(newLabel);
0932: }
0933:
0934: /**
0935: * This is used to hook an action listener to the ok button
0936: * @param a The action listener.
0937: */
0938: public void addOkListener(ActionListener a) {
0939:
0940: m_okBut.addActionListener(a);
0941: }
0942:
0943: /**
0944: * This is used to hook an action listener to the cancel button
0945: * @param a The action listener.
0946: */
0947: public void addCancelListener(ActionListener a) {
0948:
0949: m_cancelBut.addActionListener(a);
0950: }
0951:
0952: /**
0953: * This is used to remove an action listener from the ok button
0954: * @param a The action listener
0955: */
0956: public void removeOkListener(ActionListener a) {
0957:
0958: m_okBut.removeActionListener(a);
0959: }
0960:
0961: /**
0962: * This is used to remove an action listener from the cancel button
0963: * @param a The action listener
0964: */
0965: public void removeCancelListener(ActionListener a) {
0966:
0967: m_cancelBut.removeActionListener(a);
0968: }
0969:
0970: /** Updates the child property sheet, and creates if needed */
0971: public void updateChildPropertySheet() {
0972:
0973: // Update the object name displayed
0974: String className = "None";
0975: if (m_Object != null) {
0976: className = m_Object.getClass().getName();
0977: }
0978: m_ClassNameLabel.setText(className);
0979:
0980: // Set the object as the target of the propertysheet
0981: m_ChildPropertySheet.setTarget(m_Object);
0982:
0983: // Adjust size of containing window if possible
0984: if ((getTopLevelAncestor() != null)
0985: && (getTopLevelAncestor() instanceof Window)) {
0986: ((Window) getTopLevelAncestor()).pack();
0987: }
0988: }
0989: }
0990:
0991: /**
0992: * Default constructor.
0993: */
0994: public GenericObjectEditor() {
0995:
0996: this (false);
0997: }
0998:
0999: /**
1000: * Constructor that allows specifying whether it is possible
1001: * to change the class within the editor dialog.
1002: *
1003: * @param canChangeClassInDialog whether the user can change the class
1004: */
1005: public GenericObjectEditor(boolean canChangeClassInDialog) {
1006:
1007: m_canChangeClassInDialog = canChangeClassInDialog;
1008: }
1009:
1010: /**
1011: * registers all the editors in Weka
1012: */
1013: public static void registerEditors() {
1014: Properties props;
1015: Enumeration enm;
1016: String name;
1017: String value;
1018: Class baseCls;
1019: Class cls;
1020:
1021: if (m_EditorsRegistered)
1022: return;
1023:
1024: System.err.println("---Registering Weka Editors---");
1025: m_EditorsRegistered = true;
1026:
1027: // load properties
1028: try {
1029: props = Utils.readProperties(GUIEDITORS_PROPERTY_FILE);
1030: } catch (Exception e) {
1031: props = new Properties();
1032: e.printStackTrace();
1033: }
1034:
1035: enm = props.propertyNames();
1036: while (enm.hasMoreElements()) {
1037: name = enm.nextElement().toString();
1038: value = props.getProperty(name, "");
1039: try {
1040: // array class?
1041: if (name.endsWith("[]")) {
1042: baseCls = Class.forName(name.substring(0, name
1043: .indexOf("[]")));
1044: cls = Array.newInstance(baseCls, 1).getClass();
1045: } else {
1046: cls = Class.forName(name);
1047: }
1048: // register
1049: PropertyEditorManager.registerEditor(cls, Class
1050: .forName(value));
1051: } catch (Exception e) {
1052: System.err.println("Problem registering " + name + "/"
1053: + value + ": " + e);
1054: }
1055: }
1056: }
1057:
1058: /**
1059: * Returns the backup object (may be null if there is no
1060: * backup.
1061: *
1062: * @return the backup object
1063: */
1064: public Object getBackup() {
1065: return m_Backup;
1066: }
1067:
1068: /**
1069: * returns the name of the root element of the given class name,
1070: * <code>null</code> if it doesn't contain the separator
1071: *
1072: * @param clsname the full classname
1073: * @param separator the separator
1074: * @return string the root element
1075: */
1076: protected static String getRootFromClass(String clsname,
1077: String separator) {
1078: if (clsname.indexOf(separator) > -1)
1079: return clsname.substring(0, clsname.indexOf(separator));
1080: else
1081: return null;
1082: }
1083:
1084: /**
1085: * parses the given string of classes separated by ", " and returns the
1086: * a hashtable with as many entries as there are different root elements in
1087: * the class names (the key is the root element). E.g. if there's only
1088: * "weka." as the prefix for all classes the a hashtable of size 1 is returned.
1089: * if NULL is the input, then NULL is also returned.
1090: *
1091: * @param classes the classnames to work on
1092: * @return for each distinct root element in the classnames, one entry in
1093: * the hashtable (with the root element as key)
1094: */
1095: public static Hashtable sortClassesByRoot(String classes) {
1096: Hashtable roots;
1097: Hashtable result;
1098: Enumeration enm;
1099: int i;
1100: StringTokenizer tok;
1101: String clsname;
1102: Vector list;
1103: HierarchyPropertyParser hpp;
1104: String separator;
1105: String root;
1106: String tmpStr;
1107:
1108: if (classes == null)
1109: return null;
1110:
1111: roots = new Hashtable();
1112: hpp = new HierarchyPropertyParser();
1113: separator = hpp.getSeperator();
1114:
1115: // go over all classnames and store them in the hashtable, with the
1116: // root element as the key
1117: tok = new StringTokenizer(classes, ", ");
1118: while (tok.hasMoreElements()) {
1119: clsname = tok.nextToken();
1120: root = getRootFromClass(clsname, separator);
1121: if (root == null)
1122: continue;
1123:
1124: // already stored?
1125: if (!roots.containsKey(root)) {
1126: list = new Vector();
1127: roots.put(root, list);
1128: } else {
1129: list = (Vector) roots.get(root);
1130: }
1131:
1132: list.add(clsname);
1133: }
1134:
1135: // build result
1136: result = new Hashtable();
1137: enm = roots.keys();
1138: while (enm.hasMoreElements()) {
1139: root = (String) enm.nextElement();
1140: list = (Vector) roots.get(root);
1141: tmpStr = "";
1142: for (i = 0; i < list.size(); i++) {
1143: if (i > 0)
1144: tmpStr += ",";
1145: tmpStr += (String) list.get(i);
1146: }
1147: result.put(root, tmpStr);
1148: }
1149:
1150: return result;
1151: }
1152:
1153: /**
1154: * Called when the class of object being edited changes.
1155: *
1156: * @return the hashtable containing the HierarchyPropertyParsers for the root
1157: * elements
1158: */
1159: protected Hashtable getClassesFromProperties() {
1160:
1161: Hashtable hpps = new Hashtable();
1162: String className = m_ClassType.getName();
1163: Hashtable typeOptions = sortClassesByRoot(EDITOR_PROPERTIES
1164: .getProperty(className));
1165: if (typeOptions == null) {
1166: /*
1167: System.err.println("Warning: No configuration property found in\n"
1168: + PROPERTY_FILE + "\n"
1169: + "for " + className);
1170: */
1171: } else {
1172: try {
1173: Enumeration enm = typeOptions.keys();
1174: while (enm.hasMoreElements()) {
1175: String root = (String) enm.nextElement();
1176: String typeOption = (String) typeOptions.get(root);
1177: HierarchyPropertyParser hpp = new HierarchyPropertyParser();
1178: hpp.build(typeOption, ", ");
1179: hpps.put(root, hpp);
1180: }
1181: } catch (Exception ex) {
1182: System.err.println("Invalid property: " + typeOptions);
1183: }
1184: }
1185: return hpps;
1186: }
1187:
1188: /**
1189: * Updates the list of selectable object names, adding any new names to the list.
1190: */
1191: protected void updateObjectNames() {
1192:
1193: if (m_ObjectNames == null) {
1194: m_ObjectNames = getClassesFromProperties();
1195: }
1196:
1197: if (m_Object != null) {
1198: String className = m_Object.getClass().getName();
1199: String root = getRootFromClass(className,
1200: new HierarchyPropertyParser().getSeperator());
1201: HierarchyPropertyParser hpp = (HierarchyPropertyParser) m_ObjectNames
1202: .get(root);
1203: if (hpp != null) {
1204: if (!hpp.contains(className)) {
1205: hpp.add(className);
1206: }
1207: }
1208: }
1209: }
1210:
1211: /**
1212: * Sets whether the editor is "enabled", meaning that the current
1213: * values will be painted.
1214: *
1215: * @param newVal a value of type 'boolean'
1216: */
1217: public void setEnabled(boolean newVal) {
1218:
1219: if (newVal != m_Enabled) {
1220: m_Enabled = newVal;
1221: }
1222: }
1223:
1224: /**
1225: * Sets the class of values that can be edited.
1226: *
1227: * @param type a value of type 'Class'
1228: */
1229: public void setClassType(Class type) {
1230:
1231: m_ClassType = type;
1232: m_ObjectNames = getClassesFromProperties();
1233: }
1234:
1235: /**
1236: * Sets the current object to be the default, taken as the first item in
1237: * the chooser
1238: */
1239: public void setDefaultValue() {
1240:
1241: if (m_ClassType == null) {
1242: System.err
1243: .println("No ClassType set up for GenericObjectEditor!!");
1244: return;
1245: }
1246:
1247: Hashtable hpps = getClassesFromProperties();
1248: HierarchyPropertyParser hpp = null;
1249: Enumeration enm = hpps.elements();
1250:
1251: try {
1252: while (enm.hasMoreElements()) {
1253: hpp = (HierarchyPropertyParser) enm.nextElement();
1254: if (hpp.depth() > 0) {
1255: hpp.goToRoot();
1256: while (!hpp.isLeafReached())
1257: hpp.goToChild(0);
1258:
1259: String defaultValue = hpp.fullValue();
1260: setValue(Class.forName(defaultValue).newInstance());
1261: }
1262: }
1263: } catch (Exception ex) {
1264: System.err.println("Problem loading the first class: "
1265: + hpp.fullValue());
1266: ex.printStackTrace();
1267: }
1268: }
1269:
1270: /**
1271: * Sets the current Object. If the Object is in the
1272: * Object chooser, this becomes the selected item (and added
1273: * to the chooser if necessary).
1274: *
1275: * @param o an object that must be a Object.
1276: */
1277: public void setValue(Object o) {
1278:
1279: if (m_ClassType == null) {
1280: System.err
1281: .println("No ClassType set up for GenericObjectEditor!!");
1282: return;
1283: }
1284: if (!m_ClassType.isAssignableFrom(o.getClass())) {
1285: System.err.println("setValue object not of correct type!");
1286: return;
1287: }
1288:
1289: setObject(o);
1290:
1291: if (m_EditorComponent != null)
1292: m_EditorComponent.repaint();
1293:
1294: updateObjectNames();
1295: }
1296:
1297: /**
1298: * Sets the current Object.
1299: *
1300: * @param c a value of type 'Object'
1301: */
1302: protected void setObject(Object c) {
1303:
1304: // This should really call equals() for comparison.
1305: boolean trueChange;
1306: if (getValue() != null) {
1307: trueChange = (!c.equals(getValue()));
1308: } else
1309: trueChange = true;
1310:
1311: m_Backup = m_Object;
1312:
1313: m_Object = c;
1314:
1315: if (m_EditorComponent != null) {
1316: m_EditorComponent.updateChildPropertySheet();
1317: }
1318: if (trueChange) {
1319: m_Support.firePropertyChange("", null, null);
1320: }
1321: }
1322:
1323: /**
1324: * Gets the current Object.
1325: *
1326: * @return the current Object
1327: */
1328: public Object getValue() {
1329:
1330: Object result = null;
1331: try {
1332: result = makeCopy(m_Object);
1333: } catch (Exception ex) {
1334: ex.printStackTrace();
1335: }
1336: return result;
1337: }
1338:
1339: /**
1340: * Supposedly returns an initialization string to create a Object
1341: * identical to the current one, including it's state, but this doesn't
1342: * appear possible given that the initialization string isn't supposed to
1343: * contain multiple statements.
1344: *
1345: * @return the java source code initialisation string
1346: */
1347: public String getJavaInitializationString() {
1348:
1349: return "new " + m_Object.getClass().getName() + "()";
1350: }
1351:
1352: /**
1353: * Returns true to indicate that we can paint a representation of the
1354: * Object.
1355: *
1356: * @return true
1357: */
1358: public boolean isPaintable() {
1359:
1360: return true;
1361: }
1362:
1363: /**
1364: * Paints a representation of the current Object.
1365: *
1366: * @param gfx the graphics context to use
1367: * @param box the area we are allowed to paint into
1368: */
1369: public void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box) {
1370:
1371: if (m_Enabled) {
1372: String rep;
1373: if (m_Object != null) {
1374: rep = m_Object.getClass().getName();
1375: } else {
1376: rep = "None";
1377: }
1378: int dotPos = rep.lastIndexOf('.');
1379: if (dotPos != -1) {
1380: rep = rep.substring(dotPos + 1);
1381: }
1382: /*
1383: if (m_Object instanceof OptionHandler) {
1384: rep += " " + Utils.joinOptions(((OptionHandler)m_Object)
1385: .getOptions());
1386: }
1387: */
1388: java.awt.Font originalFont = gfx.getFont();
1389: gfx.setFont(originalFont.deriveFont(java.awt.Font.BOLD));
1390:
1391: FontMetrics fm = gfx.getFontMetrics();
1392: int vpad = (box.height - fm.getHeight()) / 2;
1393: gfx.drawString(rep, 2, fm.getHeight() + vpad);
1394: int repwidth = fm.stringWidth(rep);
1395:
1396: gfx.setFont(originalFont);
1397: if (m_Object instanceof OptionHandler) {
1398: gfx.drawString(" "
1399: + Utils.joinOptions(((OptionHandler) m_Object)
1400: .getOptions()), repwidth + 2, fm
1401: .getHeight()
1402: + vpad);
1403: }
1404: }
1405: }
1406:
1407: /**
1408: * Returns null as we don't support getting/setting values as text.
1409: *
1410: * @return null
1411: */
1412: public String getAsText() {
1413:
1414: return null;
1415: }
1416:
1417: /**
1418: * Returns null as we don't support getting/setting values as text.
1419: *
1420: * @param text the text value
1421: * @throws IllegalArgumentException as we don't support
1422: * getting/setting values as text.
1423: */
1424: public void setAsText(String text) {
1425:
1426: throw new IllegalArgumentException(text);
1427: }
1428:
1429: /**
1430: * Returns null as we don't support getting values as tags.
1431: *
1432: * @return null
1433: */
1434: public String[] getTags() {
1435:
1436: return null;
1437: }
1438:
1439: /**
1440: * Returns true because we do support a custom editor.
1441: *
1442: * @return true
1443: */
1444: public boolean supportsCustomEditor() {
1445:
1446: return true;
1447: }
1448:
1449: /**
1450: * Returns the array editing component.
1451: *
1452: * @return a value of type 'java.awt.Component'
1453: */
1454: public java.awt.Component getCustomEditor() {
1455:
1456: if (m_EditorComponent == null) {
1457: m_EditorComponent = new GOEPanel();
1458: }
1459: return m_EditorComponent;
1460: }
1461:
1462: /**
1463: * Adds a PropertyChangeListener who will be notified of value changes.
1464: *
1465: * @param l a value of type 'PropertyChangeListener'
1466: */
1467: public void addPropertyChangeListener(PropertyChangeListener l) {
1468:
1469: m_Support.addPropertyChangeListener(l);
1470: }
1471:
1472: /**
1473: * Removes a PropertyChangeListener.
1474: *
1475: * @param l a value of type 'PropertyChangeListener'
1476: */
1477: public void removePropertyChangeListener(PropertyChangeListener l) {
1478:
1479: m_Support.removePropertyChangeListener(l);
1480: }
1481:
1482: /**
1483: * Gets the custom panel used for editing the object.
1484: *
1485: * @return the panel
1486: */
1487: public JPanel getCustomPanel() {
1488:
1489: JButton chooseButton = createChooseClassButton();
1490: m_ObjectPropertyPanel = new PropertyPanel(this , true);
1491:
1492: JPanel customPanel = new JPanel();
1493: customPanel.setLayout(new BorderLayout());
1494: customPanel.add(chooseButton, BorderLayout.WEST);
1495: customPanel.add(m_ObjectPropertyPanel, BorderLayout.CENTER);
1496: return customPanel;
1497: }
1498:
1499: /**
1500: * Creates a button that when clicked will enable the user to change
1501: * the class of the object being edited.
1502: *
1503: * @return the choose button
1504: */
1505: protected JButton createChooseClassButton() {
1506:
1507: JButton setButton = new JButton("Choose");
1508:
1509: // anonymous action listener shows a JTree popup and allows the user
1510: // to choose the class they want
1511: setButton.addActionListener(new ActionListener() {
1512: public void actionPerformed(ActionEvent e) {
1513:
1514: JPopupMenu popup = getChooseClassPopupMenu();
1515:
1516: // show the popup where the source component is
1517: if (e.getSource() instanceof Component) {
1518: Component comp = (Component) e.getSource();
1519: popup.show(comp, comp.getX(), comp.getY());
1520: popup.pack();
1521: }
1522: }
1523: });
1524:
1525: return setButton;
1526: }
1527:
1528: /**
1529: * creates a classname from the given path
1530: *
1531: * @param path the path to generate the classname from
1532: * @return the generated classname
1533: */
1534: protected String getClassnameFromPath(TreePath path) {
1535: StringBuffer classname = new StringBuffer();
1536:
1537: // recreate class name from path
1538: int start = 0;
1539: if (m_ObjectNames.size() > 1)
1540: start = 1;
1541:
1542: for (int i = start; i < path.getPathCount(); i++) {
1543: if (i > start)
1544: classname.append(".");
1545: classname.append((String) ((GOETreeNode) path
1546: .getPathComponent(i)).getUserObject());
1547: }
1548:
1549: return classname.toString();
1550: }
1551:
1552: /**
1553: * Returns a popup menu that allows the user to change
1554: * the class of object.
1555: *
1556: * @return a JPopupMenu that when shown will let the user choose the class
1557: */
1558: public JPopupMenu getChooseClassPopupMenu() {
1559:
1560: updateObjectNames();
1561:
1562: // create the tree, and find the path to the current class
1563: m_treeNodeOfCurrentObject = null;
1564: final JTree tree = createTree(m_ObjectNames);
1565: if (m_treeNodeOfCurrentObject != null) {
1566: tree.setSelectionPath(new TreePath(
1567: m_treeNodeOfCurrentObject.getPath()));
1568: }
1569: tree.getSelectionModel().setSelectionMode(
1570: TreeSelectionModel.SINGLE_TREE_SELECTION);
1571:
1572: // create the popup
1573: final JPopupMenu popup = new JTreePopupMenu(tree);
1574:
1575: // respond when the user chooses a class
1576: tree.addTreeSelectionListener(new TreeSelectionListener() {
1577: public void valueChanged(TreeSelectionEvent e) {
1578: GOETreeNode node = (GOETreeNode) tree
1579: .getLastSelectedPathComponent();
1580:
1581: if (node == null)
1582: return;
1583:
1584: if (node.isLeaf()) {
1585: classSelected(getClassnameFromPath(tree
1586: .getSelectionPath()));
1587: popup.setVisible(false);
1588: }
1589: }
1590: });
1591:
1592: return popup;
1593: }
1594:
1595: /**
1596: * Creates a JTree from an object heirarchy.
1597: *
1598: * @param hpps the hierarchy of objects to mirror in the tree
1599: * @return a JTree representation of the hierarchy
1600: */
1601: protected JTree createTree(Hashtable hpps) {
1602: GOETreeNode super Root;
1603: Enumeration enm;
1604: HierarchyPropertyParser hpp;
1605:
1606: if (hpps.size() > 1)
1607: super Root = new GOETreeNode("root");
1608: else
1609: super Root = null;
1610:
1611: enm = hpps.elements();
1612: while (enm.hasMoreElements()) {
1613: hpp = (HierarchyPropertyParser) enm.nextElement();
1614: hpp.goToRoot();
1615: GOETreeNode root = new GOETreeNode(hpp.getValue());
1616: addChildrenToTree(root, hpp);
1617:
1618: if (super Root == null)
1619: super Root = root;
1620: else
1621: super Root.add(root);
1622: }
1623:
1624: JTree tree = new JTree(super Root);
1625:
1626: return tree;
1627: }
1628:
1629: /**
1630: * Recursively builds a JTree from an object heirarchy.
1631: * Also updates m_treeNodeOfCurrentObject if the current object
1632: * is discovered during creation.
1633: *
1634: * @param tree the root of the tree to add children to
1635: * @param hpp the hierarchy of objects to mirror in the tree
1636: */
1637: protected void addChildrenToTree(GOETreeNode tree,
1638: HierarchyPropertyParser hpp) {
1639:
1640: try {
1641: for (int i = 0; i < hpp.numChildren(); i++) {
1642: hpp.goToChild(i);
1643: GOETreeNode child = new GOETreeNode(hpp.getValue());
1644: if ((m_Object != null)
1645: && m_Object.getClass().getName().equals(
1646: hpp.fullValue())) {
1647: m_treeNodeOfCurrentObject = child;
1648: }
1649: tree.add(child);
1650: addChildrenToTree(child, hpp);
1651: hpp.goToParent();
1652: }
1653: } catch (Exception e) {
1654: e.printStackTrace();
1655: }
1656: }
1657:
1658: /**
1659: * Called when the user selects an class type to change to.
1660: *
1661: * @param className the name of the class that was selected
1662: */
1663: protected void classSelected(String className) {
1664:
1665: try {
1666: if ((m_Object != null)
1667: && m_Object.getClass().getName().equals(className)) {
1668: return;
1669: }
1670:
1671: setValue(Class.forName(className).newInstance());
1672: //m_ObjectPropertyPanel.showPropertyDialog();
1673: if (m_EditorComponent != null) {
1674: m_EditorComponent.updateChildPropertySheet();
1675: }
1676: } catch (Exception ex) {
1677: JOptionPane.showMessageDialog(null,
1678: "Could not create an example of\n" + className
1679: + "\n" + "from the current classpath",
1680: "Class load failed", JOptionPane.ERROR_MESSAGE);
1681: ex.printStackTrace();
1682: try {
1683: if (m_Backup != null)
1684: setValue(m_Backup);
1685: else
1686: setDefaultValue();
1687: } catch (Exception e) {
1688: System.err.println(ex.getMessage());
1689: ex.printStackTrace();
1690: }
1691: }
1692: }
1693:
1694: /**
1695: * Sets the capabilities to use for filtering.
1696: *
1697: * @param value the object to get the filter capabilities from
1698: */
1699: public void setCapabilitiesFilter(Capabilities value) {
1700: m_CapabilitiesFilter = new Capabilities(null);
1701: m_CapabilitiesFilter.assign(value);
1702: }
1703:
1704: /**
1705: * Returns the current Capabilities filter, can be null.
1706: *
1707: * @return the current Capabiliities used for filtering
1708: */
1709: public Capabilities getCapabilitiesFilter() {
1710: return m_CapabilitiesFilter;
1711: }
1712:
1713: /**
1714: * Removes the current Capabilities filter.
1715: */
1716: public void removeCapabilitiesFilter() {
1717: m_CapabilitiesFilter = null;
1718: }
1719:
1720: /**
1721: * Makes a copy of an object using serialization
1722: * @param source the object to copy
1723: * @return a copy of the source object
1724: * @exception Exception if the copy fails
1725: */
1726: public Object makeCopy(Object source) throws Exception {
1727: SerializedObject so = new SerializedObject(source);
1728: Object result = so.getObject();
1729: return result;
1730: }
1731:
1732: /**
1733: * Tests out the Object editor from the command line.
1734: *
1735: * @param args may contain the class name of a Object to edit
1736: */
1737: public static void main(String[] args) {
1738:
1739: try {
1740: GenericObjectEditor.registerEditors();
1741: GenericObjectEditor ce = new GenericObjectEditor(true);
1742: ce.setClassType(weka.classifiers.Classifier.class);
1743: Object initial = new weka.classifiers.rules.ZeroR();
1744: if (args.length > 0) {
1745: ce.setClassType(Class.forName(args[0]));
1746: if (args.length > 1) {
1747: initial = (Object) Class.forName(args[1])
1748: .newInstance();
1749: ce.setValue(initial);
1750: } else
1751: ce.setDefaultValue();
1752: } else
1753: ce.setValue(initial);
1754:
1755: PropertyDialog pd = new PropertyDialog(ce, 100, 100);
1756: pd.addWindowListener(new WindowAdapter() {
1757: public void windowClosing(WindowEvent e) {
1758: PropertyEditor pe = ((PropertyDialog) e.getSource())
1759: .getEditor();
1760: Object c = (Object) pe.getValue();
1761: String options = "";
1762: if (c instanceof OptionHandler) {
1763: options = Utils.joinOptions(((OptionHandler) c)
1764: .getOptions());
1765: }
1766: System.out.println(c.getClass().getName() + " "
1767: + options);
1768: System.exit(0);
1769: }
1770: });
1771: } catch (Exception ex) {
1772: ex.printStackTrace();
1773: System.err.println(ex.getMessage());
1774: }
1775: }
1776: }
|