/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * GenericObjectEditor.java * Copyright (C) 2002 Len Trigg, Xin Xu, Richard Kirkby * */ package weka.gui; import weka.gui.PropertySheetPanel; import weka.gui.SelectedTagEditor; import weka.gui.FileEditor; import weka.gui.PropertyDialog; import weka.core.OptionHandler; import weka.core.Utils; import weka.core.Tag; import weka.core.SelectedTag; import weka.core.SerializedObject; import weka.core.Utils; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.FontMetrics; import java.awt.GridLayout; import java.awt.Window; import java.awt.Frame; import java.awt.event.MouseEvent; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.beans.PropertyEditor; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Properties; import java.util.StringTokenizer; import java.util.Vector; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JMenuBar; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.event.MenuListener; import javax.swing.event.MenuEvent; import javax.swing.event.MouseInputAdapter; import javax.swing.JTree; import javax.swing.tree.TreeSelectionModel; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreePath; import javax.swing.event.TreeExpansionListener; import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.event.TreeSelectionEvent; /** * A PropertyEditor for objects that themselves have been defined as * editable in the GenericObjectEditor configuration file, which lists * possible values that can be selected from, and themselves configured. * The configuration file is called "GenericObjectEditor.props" and * may live in either the location given by "user.home" or the current * directory (this last will take precedence), and a default properties * file is read from the weka distribution. For speed, the properties * file is read only once when the class is first loaded -- this may need * to be changed if we ever end up running in a Java OS ;-). * * @author Len Trigg (trigg@cs.waikato.ac.nz) * @author Xin Xu (xx5@cs.waikato.ac.nz) * @author Richard Kirkby (rkirkby@cs.waikato.ac.nz) * @version $Revision: 1.1.1.1 $ */ public class GenericObjectEditor implements PropertyEditor, CustomPanelSupplier { /** The object being configured */ protected Object m_Object; /** Holds a copy of the current object that can be reverted to if the user decides to cancel */ protected Object m_Backup; /** Handles property change notification */ protected PropertyChangeSupport m_Support = new PropertyChangeSupport(this); /** The Class of objects being edited */ protected Class m_ClassType; /** The model containing the list of names to select from */ protected HierarchyPropertyParser m_ObjectNames; /** The GUI component for editing values, created when needed */ protected GOEPanel m_EditorComponent; /** True if the GUI component is needed */ protected boolean m_Enabled = true; /** The name of the properties file */ protected static String PROPERTY_FILE = "weka/gui/GenericObjectEditor.props"; /** Contains the editor properties */ protected static Properties EDITOR_PROPERTIES; /** The tree node of the current object so we can re-select it for the user */ protected DefaultMutableTreeNode m_treeNodeOfCurrentObject; /** The property panel created for the objects */ protected PropertyPanel m_ObjectPropertyPanel; protected boolean m_canChangeClassInDialog; /** Loads the configuration property file */ static { // Allow a properties file in the current directory to override try { EDITOR_PROPERTIES = Utils.readProperties(PROPERTY_FILE); java.util.Enumeration keys = (java.util.Enumeration)EDITOR_PROPERTIES.propertyNames(); if (!keys.hasMoreElements()) { throw new Exception("Failed to read a property file for the " +"generic object editor"); } } catch (Exception ex) { JOptionPane.showMessageDialog(null, "Could not read a configuration file for the generic object\n" +"editor. An example file is included with the Weka distribution.\n" +"This file should be named \"" + PROPERTY_FILE + "\" and\n" +"should be placed either in your user home (which is set\n" + "to \"" + System.getProperties().getProperty("user.home") + "\")\n" + "or the directory that java was started from\n", "GenericObjectEditor", JOptionPane.ERROR_MESSAGE); } } /** * Creates a popup menu containing a tree that is aware * of the screen dimensions. */ public class JTreePopupMenu extends JPopupMenu { /** The tree */ JTree m_tree; /** The scroller */ JScrollPane m_scroller; /** * Constructs a new popup menu. * * @param tree the tree to put in the menu */ public JTreePopupMenu(JTree tree) { m_tree = tree; JPanel treeView = new JPanel(); treeView.setLayout(new BorderLayout()); treeView.add(m_tree, BorderLayout.NORTH); m_scroller = new JScrollPane(treeView); m_scroller.setPreferredSize(new Dimension(300, 400)); add(m_scroller); } /** * Displays the menu, making sure it will fit on the screen. * * @param invoker the component thast invoked the menu * @param x the x location of the popup * @param y the y location of the popup */ public void show(Component invoker, int x, int y) { super.show(invoker, x, y); // calculate available screen area for popup java.awt.Point location = getLocationOnScreen(); java.awt.Dimension screenSize = getToolkit().getScreenSize(); int maxWidth = (int) (screenSize.getWidth() - location.getX()); int maxHeight = (int) (screenSize.getHeight() - location.getY()); // if the part of the popup goes off the screen then resize it Dimension scrollerSize = m_scroller.getPreferredSize(); int height = (int) scrollerSize.getHeight(); int width = (int) scrollerSize.getWidth(); if (width > maxWidth) width = maxWidth; if (height > maxHeight) height = maxHeight; // commit any size changes m_scroller.setPreferredSize(new Dimension(width, height)); revalidate(); pack(); } } /** * Handles the GUI side of editing values. */ public class GOEPanel extends JPanel { /** The component that performs classifier customization */ protected PropertySheetPanel m_ChildPropertySheet; /** The name of the current class */ protected JLabel m_ClassNameLabel; /** Open object from disk */ protected JButton m_OpenBut; /** Save object to disk */ protected JButton m_SaveBut; /** ok button */ protected JButton m_okBut; /** cancel button */ protected JButton m_cancelBut; /** The filechooser for opening and saving object files */ protected JFileChooser m_FileChooser; /** Creates the GUI editor component */ public GOEPanel() { m_Backup = copyObject(m_Object); m_ClassNameLabel = new JLabel("None"); m_ClassNameLabel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); m_ChildPropertySheet = new PropertySheetPanel(); m_ChildPropertySheet.addPropertyChangeListener (new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { m_Support.firePropertyChange("", null, null); } }); m_OpenBut = new JButton("Open..."); m_OpenBut.setToolTipText("Load a configured object"); m_OpenBut.setEnabled(true); m_OpenBut.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { Object object = openObject(); if (object != null) { // setValue takes care of: Making sure obj is of right type, // and firing property change. setValue(object); // Need a second setValue to get property values filled in OK. // Not sure why. setValue(object); } } }); m_SaveBut = new JButton("Save..."); m_SaveBut.setToolTipText("Save the current configured object"); m_SaveBut.setEnabled(true); m_SaveBut.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { saveObject(m_Object); } }); m_okBut = new JButton("OK"); m_okBut.setEnabled(true); m_okBut.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { m_Backup = copyObject(m_Object); if ((getTopLevelAncestor() != null) && (getTopLevelAncestor() instanceof Window)) { Window w = (Window) getTopLevelAncestor(); w.dispose(); } } }); m_cancelBut = new JButton("Cancel"); m_cancelBut.setEnabled(true); m_cancelBut.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (m_Backup != null) { m_Object = copyObject(m_Backup); // To fire property change m_Support.firePropertyChange("", null, null); m_ObjectNames = getClassesFromProperties(); updateObjectNames(); updateChildPropertySheet(); } if ((getTopLevelAncestor() != null) && (getTopLevelAncestor() instanceof Window)) { Window w = (Window) getTopLevelAncestor(); w.dispose(); } } }); setLayout(new BorderLayout()); if (m_canChangeClassInDialog) { JButton chooseButton = createChooseClassButton(); JPanel top = new JPanel(); top.setLayout(new BorderLayout()); top.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); top.add(chooseButton, BorderLayout.WEST); top.add(m_ClassNameLabel, BorderLayout.CENTER); add(top, BorderLayout.NORTH); } else { add(m_ClassNameLabel, BorderLayout.NORTH); } add(m_ChildPropertySheet, BorderLayout.CENTER); // Since we resize to the size of the property sheet, a scrollpane isn't // typically needed // add(new JScrollPane(m_ChildPropertySheet), BorderLayout.CENTER); JPanel okcButs = new JPanel(); okcButs.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); okcButs.setLayout(new GridLayout(1, 4, 5, 5)); okcButs.add(m_OpenBut); okcButs.add(m_SaveBut); okcButs.add(m_okBut); okcButs.add(m_cancelBut); add(okcButs, BorderLayout.SOUTH); if (m_ClassType != null) { m_ObjectNames = getClassesFromProperties(); if (m_Object != null) { updateObjectNames(); updateChildPropertySheet(); } } } /** * Enables/disables the cancel button. * * @param flag true to enable cancel button, false * to disable it */ protected void setCancelButton(boolean flag) { if(m_cancelBut != null) m_cancelBut.setEnabled(flag); } /** * Opens an object from a file selected by the user. * * @return the loaded object, or null if the operation was cancelled */ protected Object openObject() { if (m_FileChooser == null) { createFileChooser(); } int returnVal = m_FileChooser.showOpenDialog(this); if (returnVal == JFileChooser.APPROVE_OPTION) { File selected = m_FileChooser.getSelectedFile(); try { ObjectInputStream oi = new ObjectInputStream(new BufferedInputStream(new FileInputStream(selected))); Object obj = oi.readObject(); oi.close(); if (!m_ClassType.isAssignableFrom(obj.getClass())) { throw new Exception("Object not of type: " + m_ClassType.getName()); } return obj; } catch (Exception ex) { JOptionPane.showMessageDialog(this, "Couldn't read object: " + selected.getName() + "\n" + ex.getMessage(), "Open object file", JOptionPane.ERROR_MESSAGE); } } return null; } /** * Opens an object from a file selected by the user. * * @return the loaded object, or null if the operation was cancelled */ protected void saveObject(Object object) { if (m_FileChooser == null) { createFileChooser(); } int returnVal = m_FileChooser.showSaveDialog(this); if (returnVal == JFileChooser.APPROVE_OPTION) { File sFile = m_FileChooser.getSelectedFile(); try { ObjectOutputStream oo = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(sFile))); oo.writeObject(object); oo.close(); } catch (Exception ex) { JOptionPane.showMessageDialog(this, "Couldn't write to file: " + sFile.getName() + "\n" + ex.getMessage(), "Save object", JOptionPane.ERROR_MESSAGE); } } } /** * Creates the file chooser the user will use to save/load files with. */ protected void createFileChooser() { m_FileChooser = new JFileChooser(new File(System.getProperty("user.dir"))); m_FileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); } /** * Makes a copy of an object using serialization * @param source the object to copy * @return a copy of the source object */ protected Object copyObject(Object source) { Object result = null; try { SerializedObject so = new SerializedObject(source); result = so.getObject(); setCancelButton(true); } catch (Exception ex) { setCancelButton(false); System.err.println("GenericObjectEditor: Problem making backup object"); System.err.println(ex); } return result; } /** * Allows customization of the action label on the dialog. * @param newLabel the new string for the ok button */ public void setOkButtonText(String newLabel) { m_okBut.setText(newLabel); } /** * This is used to hook an action listener to the ok button * @param a The action listener. */ public void addOkListener(ActionListener a) { m_okBut.addActionListener(a); } /** * This is used to hook an action listener to the cancel button * @param a The action listener. */ public void addCancelListener(ActionListener a) { m_cancelBut.addActionListener(a); } /** * This is used to remove an action listener from the ok button * @param a The action listener */ public void removeOkListener(ActionListener a) { m_okBut.removeActionListener(a); } /** * This is used to remove an action listener from the cancel button * @param a The action listener */ public void removeCancelListener(ActionListener a) { m_cancelBut.removeActionListener(a); } /** Updates the child property sheet, and creates if needed */ public void updateChildPropertySheet() { // Update the object name displayed String className = "None"; if (m_Object != null) { className = m_Object.getClass().getName(); } m_ClassNameLabel.setText(className); // Set the object as the target of the propertysheet m_ChildPropertySheet.setTarget(m_Object); // Adjust size of containing window if possible if ((getTopLevelAncestor() != null) && (getTopLevelAncestor() instanceof Window)) { ((Window) getTopLevelAncestor()).pack(); } } } /** * Default constructor. */ public GenericObjectEditor() { this(false); } /** * Constructor that allows specifying whether it is possible * to change the class within the editor dialog. * * @param canChangeClassInDialog whether the user can change the class */ public GenericObjectEditor(boolean canChangeClassInDialog) { m_canChangeClassInDialog = canChangeClassInDialog; } /** Called when the class of object being edited changes. */ protected HierarchyPropertyParser getClassesFromProperties() { HierarchyPropertyParser hpp = new HierarchyPropertyParser(); String className = m_ClassType.getName(); String typeOptions = EDITOR_PROPERTIES.getProperty(className); if (typeOptions == null) { /* System.err.println("Warning: No configuration property found in\n" + PROPERTY_FILE + "\n" + "for " + className); */ } else { try { hpp.build(typeOptions, ", "); } catch (Exception ex) { System.err.println("Invalid property: " + typeOptions); } } return hpp; } /** * Updates the list of selectable object names, adding any new names to the list. */ protected void updateObjectNames() { if (m_ObjectNames == null) { m_ObjectNames = getClassesFromProperties(); } if (m_Object != null) { String className = m_Object.getClass().getName(); if(!m_ObjectNames.contains(className)){ m_ObjectNames.add(className); } } } /** * Sets whether the editor is "enabled", meaning that the current * values will be painted. * * @param newVal a value of type 'boolean' */ public void setEnabled(boolean newVal) { if (newVal != m_Enabled) { m_Enabled = newVal; } } /** * Sets the class of values that can be edited. * * @param type a value of type 'Class' */ public void setClassType(Class type) { m_ClassType = type; m_ObjectNames = getClassesFromProperties(); } /** * Sets the current object to be the default, taken as the first item in * the chooser */ public void setDefaultValue() { if (m_ClassType == null) { System.err.println("No ClassType set up for GenericObjectEditor!!"); return; } HierarchyPropertyParser hpp = getClassesFromProperties(); try{ if(hpp.depth() > 0){ hpp.goToRoot(); while(!hpp.isLeafReached()) hpp.goToChild(0); String defaultValue = hpp.fullValue(); setValue(Class.forName(defaultValue).newInstance()); } }catch(Exception ex){ System.err.println("Problem loading the first class: "+ hpp.fullValue()); ex.printStackTrace(); } } /** * Sets the current Object. If the Object is in the * Object chooser, this becomes the selected item (and added * to the chooser if necessary). * * @param o an object that must be a Object. */ public void setValue(Object o) { if (m_ClassType == null) { System.err.println("No ClassType set up for GenericObjectEditor!!"); return; } if (!m_ClassType.isAssignableFrom(o.getClass())) { System.err.println("setValue object not of correct type!"); return; } setObject(o); if (m_EditorComponent != null) m_EditorComponent.repaint(); updateObjectNames(); } /** * Sets the current Object. * * @param c a value of type 'Object' */ protected void setObject(Object c) { // This should really call equals() for comparison. boolean trueChange ; if (getValue() != null) { trueChange = (!c.equals(getValue())); } else trueChange = true; m_Backup = m_Object; m_Object = c; if (m_EditorComponent != null) { m_EditorComponent.updateChildPropertySheet(); } if (trueChange) { m_Support.firePropertyChange("", null, null); } } /** * Gets the current Object. * * @return the current Object */ public Object getValue() { return m_Object; } /** * Supposedly returns an initialization string to create a Object * identical to the current one, including it's state, but this doesn't * appear possible given that the initialization string isn't supposed to * contain multiple statements. * * @return the java source code initialisation string */ public String getJavaInitializationString() { return "new " + m_Object.getClass().getName() + "()"; } /** * Returns true to indicate that we can paint a representation of the * Object. * * @return true */ public boolean isPaintable() { return true; } /** * Paints a representation of the current Object. * * @param gfx the graphics context to use * @param box the area we are allowed to paint into */ public void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box) { if (m_Enabled) { String rep; if (m_Object != null) { rep = m_Object.getClass().getName(); } else { rep = "None"; } int dotPos = rep.lastIndexOf('.'); if (dotPos != -1) { rep = rep.substring(dotPos + 1); } /* if (m_Object instanceof OptionHandler) { rep += " " + Utils.joinOptions(((OptionHandler)m_Object) .getOptions()); } */ java.awt.Font originalFont = gfx.getFont(); gfx.setFont(originalFont.deriveFont(java.awt.Font.BOLD)); FontMetrics fm = gfx.getFontMetrics(); int vpad = (box.height - fm.getHeight()) / 2; gfx.drawString(rep, 2, fm.getHeight() + vpad); int repwidth = fm.stringWidth(rep); gfx.setFont(originalFont); if (m_Object instanceof OptionHandler) { gfx.drawString(" " + Utils.joinOptions(((OptionHandler)m_Object).getOptions()), repwidth + 2, fm.getHeight() + vpad); } } } /** * Returns null as we don't support getting/setting values as text. * * @return null */ public String getAsText() { return null; } /** * Returns null as we don't support getting/setting values as text. * * @param text the text value * @exception IllegalArgumentException as we don't support * getting/setting values as text. */ public void setAsText(String text) { throw new IllegalArgumentException(text); } /** * Returns null as we don't support getting values as tags. * * @return null */ public String[] getTags() { return null; } /** * Returns true because we do support a custom editor. * * @return true */ public boolean supportsCustomEditor() { return true; } /** * Returns the array editing component. * * @return a value of type 'java.awt.Component' */ public java.awt.Component getCustomEditor() { if (m_EditorComponent == null) { m_EditorComponent = new GOEPanel(); } return m_EditorComponent; } /** * Adds a PropertyChangeListener who will be notified of value changes. * * @param l a value of type 'PropertyChangeListener' */ public void addPropertyChangeListener(PropertyChangeListener l) { m_Support.addPropertyChangeListener(l); } /** * Removes a PropertyChangeListener. * * @param l a value of type 'PropertyChangeListener' */ public void removePropertyChangeListener(PropertyChangeListener l) { m_Support.removePropertyChangeListener(l); } /** * Gets the custom panel used for editing the object. * */ public JPanel getCustomPanel() { JButton chooseButton = createChooseClassButton(); m_ObjectPropertyPanel = new PropertyPanel(this, true); JPanel customPanel = new JPanel(); customPanel.setLayout(new BorderLayout()); customPanel.add(chooseButton, BorderLayout.WEST); customPanel.add(m_ObjectPropertyPanel, BorderLayout.CENTER); return customPanel; } /** * Creates a button that when clicked will enable the user to change * the class of the object being edited. */ protected JButton createChooseClassButton() { JButton setButton = new JButton("Set"); // anonymous action listener shows a JTree popup and allows the user // to choose the class they want setButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { JPopupMenu popup = getChooseClassPopupMenu(); // show the popup where the source component is if (e.getSource() instanceof Component) { Component comp = (Component) e.getSource(); popup.show(comp, comp.getX(), comp.getY()); popup.pack(); } } }); return setButton; } /** * Returns a popup menu that allows the user to change * the class of object. * * @return a JPopupMenu that when shown will let the user choose the class */ public JPopupMenu getChooseClassPopupMenu() { updateObjectNames(); // create the tree, and find the path to the current class m_treeNodeOfCurrentObject = null; final JTree tree = createTree(m_ObjectNames); if (m_treeNodeOfCurrentObject != null) { tree.setSelectionPath(new TreePath(m_treeNodeOfCurrentObject.getPath())); } tree.getSelectionModel().setSelectionMode (TreeSelectionModel.SINGLE_TREE_SELECTION); // create the popup final JPopupMenu popup = new JTreePopupMenu(tree); // respond when the user chooses a class tree.addTreeSelectionListener(new TreeSelectionListener() { public void valueChanged(TreeSelectionEvent e) { DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent(); if (node == null) return; Object nodeInfo = node.getUserObject(); if (node.isLeaf()) { TreePath selectedPath = tree.getSelectionPath(); StringBuffer classSelected = new StringBuffer(); // recreate class name from path for (int i=0; i<selectedPath.getPathCount(); i++) { if (i>0) classSelected.append("."); classSelected.append((String) ((DefaultMutableTreeNode) selectedPath.getPathComponent(i)) .getUserObject()); } classSelected(classSelected.toString()); popup.setVisible(false); } } }); return popup; } /** * Creates a JTree from an object heirarchy. * * @param hpp the hierarchy of objects to mirror in the tree * @return a JTree representation of the hierarchy */ protected JTree createTree(HierarchyPropertyParser hpp) { hpp.goToRoot(); DefaultMutableTreeNode root = new DefaultMutableTreeNode(hpp.getValue()); addChildrenToTree(root, hpp); JTree tree = new JTree(root); return tree; } /** * Recursively builds a JTree from an object heirarchy. * Also updates m_treeNodeOfCurrentObject if the current object * is discovered during creation. * * @param tree the root of the tree to add children to * @param hpp the hierarchy of objects to mirror in the tree */ protected void addChildrenToTree(DefaultMutableTreeNode tree, HierarchyPropertyParser hpp) { try { for (int i=0; i<hpp.numChildren(); i++) { hpp.goToChild(i); DefaultMutableTreeNode child = new DefaultMutableTreeNode(hpp.getValue()); if ((m_Object != null) && m_Object.getClass().getName().equals(hpp.fullValue())) { m_treeNodeOfCurrentObject = child; } tree.add(child); addChildrenToTree(child, hpp); hpp.goToParent(); } } catch (Exception e) { e.printStackTrace(); } } /** * Called when the user selects an class type to change to. * * @param className the name of the class that was selected */ protected void classSelected(String className) { try { if ((m_Object != null) && m_Object.getClass().getName().equals(className)) { return; } setValue(Class.forName(className).newInstance()); //m_ObjectPropertyPanel.showPropertyDialog(); if (m_EditorComponent != null) { m_EditorComponent.updateChildPropertySheet(); } } catch (Exception ex) { JOptionPane.showMessageDialog(null, "Could not create an example of\n" + className + "\n" + "from the current classpath", "Class load failed", JOptionPane.ERROR_MESSAGE); ex.printStackTrace(); try { if(m_Backup != null) setValue(m_Backup); else setDefaultValue(); } catch(Exception e) { System.err.println(ex.getMessage()); ex.printStackTrace(); } } } /** * Tests out the Object editor from the command line. * * @param args may contain the class name of a Object to edit */ public static void main(String [] args) { try { System.err.println("---Registering Weka Editors---"); java.beans.PropertyEditorManager .registerEditor(weka.experiment.ResultProducer.class, GenericObjectEditor.class); java.beans.PropertyEditorManager .registerEditor(weka.experiment.SplitEvaluator.class, GenericObjectEditor.class); java.beans.PropertyEditorManager .registerEditor(weka.classifiers.Classifier.class, GenericObjectEditor.class); java.beans.PropertyEditorManager .registerEditor(weka.attributeSelection.ASEvaluation.class, GenericObjectEditor.class); java.beans.PropertyEditorManager .registerEditor(weka.attributeSelection.ASSearch.class, GenericObjectEditor.class); java.beans.PropertyEditorManager .registerEditor(SelectedTag.class, SelectedTagEditor.class); java.beans.PropertyEditorManager .registerEditor(java.io.File.class, FileEditor.class); GenericObjectEditor ce = new GenericObjectEditor(true); ce.setClassType(weka.classifiers.Classifier.class); Object initial = new weka.classifiers.rules.ZeroR(); if (args.length > 0){ ce.setClassType(Class.forName(args[0])); if(args.length > 1){ initial = (Object)Class.forName(args[1]).newInstance(); ce.setValue(initial); } else ce.setDefaultValue(); } else ce.setValue(initial); PropertyDialog pd = new PropertyDialog(ce, 100, 100); pd.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { PropertyEditor pe = ((PropertyDialog)e.getSource()).getEditor(); Object c = (Object)pe.getValue(); String options = ""; if (c instanceof OptionHandler) { options = Utils.joinOptions(((OptionHandler)c).getOptions()); } System.out.println(c.getClass().getName() + " " + options); System.exit(0); } }); } catch (Exception ex) { ex.printStackTrace(); System.err.println(ex.getMessage()); } } }