📄 genericobjecteditor.java
字号:
/*
* 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.core.OptionHandler;
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.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.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
/**
* A PropertyEditor for objects. It can be used either in a static or a dynamic
* way. <br>
* <br>
* In the <b>static</b> way (<code>USE_DYNAMIC</code> is <code>false</code>) the
* objects 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 ;-). <br>
* <br>
* If it is used in a <b>dynamic</b> way (<code>USE_DYNAMIC</code> is
* <code>true</code>) then the classes to list are discovered by the
* <code>GenericPropertiesCreator</code> class (it checks the complete classpath).
*
* @see #USE_DYNAMIC
* @see GenericPropertiesCreator
* @see GenericPropertiesCreator#CREATOR_FILE
* @see weka.core.RTSI
*
* @author Len Trigg (trigg@cs.waikato.ac.nz)
* @author Xin Xu (xx5@cs.waikato.ac.nz)
* @author Richard Kirkby (rkirkby@cs.waikato.ac.nz)
* @author FracPete (fracpete at waikato dot ac dot nz)
* @version $Revision: 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 Hashtable 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;
/** whether the class can be changed */
protected boolean m_canChangeClassInDialog;
/** whether to generate the properties dynamically or use the static props-file */
protected final static boolean USE_DYNAMIC = true;
/** whether the Weka Editors were already registered */
protected static boolean m_EditorsRegistered;
/**
* Loads the configuration property file (USE_DYNAMIC is FALSE) or determines
* the classes dynamically (USE_DYNAMIC is TRUE)
* @see #USE_DYNAMIC
* @see GenericPropertiesCreator
*/
static {
if (USE_DYNAMIC) {
try {
GenericPropertiesCreator creator = new GenericPropertiesCreator();
creator.execute(false);
EDITOR_PROPERTIES = creator.getOutputProperties();
}
catch (Exception e) {
JOptionPane.showMessageDialog(
null,
"Could not determine the properties for the generic object\n"
+ "editor. This exception was produced:\n"
+ e.toString(),
"GenericObjectEditor",
JOptionPane.ERROR_MESSAGE);
}
}
else {
// 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);
// make backgrounds look the same
treeView.setBackground(m_tree.getBackground());
m_scroller = new JScrollPane(treeView);
m_scroller.setPreferredSize(new Dimension(300, 400));
m_scroller.getVerticalScrollBar().setUnitIncrement(20);
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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -