2
* This program is free software; you can redistribute it and/or modify
3
* it under the terms of the GNU General Public License as published by
4
* the Free Software Foundation; either version 2 of the License, or
5
* (at your option) any later version.
7
* This program is distributed in the hope that it will be useful,
8
* but WITHOUT ANY WARRANTY; without even the implied warranty of
9
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
* GNU General Public License for more details.
12
* You should have received a copy of the GNU General Public License
13
* along with this program; if not, write to the Free Software
14
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
* GenericArrayEditor.java
19
* Copyright (C) 1999 University of Waikato, Hamilton, New Zealand
25
import weka.core.SerializedObject;
27
import java.awt.BorderLayout;
28
import java.awt.Component;
29
import java.awt.Dimension;
31
import java.awt.FontMetrics;
32
import java.awt.Graphics;
33
import java.awt.GridLayout;
34
import java.awt.Insets;
35
import java.awt.Rectangle;
36
import java.awt.event.ActionEvent;
37
import java.awt.event.ActionListener;
38
import java.awt.event.MouseAdapter;
39
import java.awt.event.MouseEvent;
40
import java.awt.event.MouseListener;
41
import java.awt.event.WindowAdapter;
42
import java.awt.event.WindowEvent;
43
import java.beans.PropertyChangeEvent;
44
import java.beans.PropertyChangeListener;
45
import java.beans.PropertyChangeSupport;
46
import java.beans.PropertyEditor;
47
import java.beans.PropertyEditorManager;
48
import java.lang.reflect.Array;
50
import javax.swing.DefaultListCellRenderer;
51
import javax.swing.DefaultListModel;
52
import javax.swing.JButton;
53
import javax.swing.JLabel;
54
import javax.swing.JList;
55
import javax.swing.JOptionPane;
56
import javax.swing.JPanel;
57
import javax.swing.JScrollPane;
58
import javax.swing.ListCellRenderer;
59
import javax.swing.SwingConstants;
60
import javax.swing.event.ListSelectionEvent;
61
import javax.swing.event.ListSelectionListener;
64
* A PropertyEditor for arrays of objects that themselves have
67
* @author Len Trigg (trigg@cs.waikato.ac.nz)
68
* @version $Revision: 1.18 $
70
public class GenericArrayEditor
72
implements PropertyEditor {
74
/** for serialization */
75
private static final long serialVersionUID = 3914616975334750480L;
77
/** Handles property change notification */
78
private PropertyChangeSupport m_Support = new PropertyChangeSupport(this);
80
/** The label for when we can't edit that type */
81
private JLabel m_Label = new JLabel("Can't edit", SwingConstants.CENTER);
83
/** The list component displaying current values */
84
private JList m_ElementList = new JList();
86
/** The class of objects allowed in the array */
87
private Class m_ElementClass = String.class;
89
/** The defaultlistmodel holding our data */
90
private DefaultListModel m_ListModel;
92
/** The property editor for the class we are editing */
93
private PropertyEditor m_ElementEditor;
95
/** Click this to delete the selected array values */
96
private JButton m_DeleteBut = new JButton("Delete");
98
/** Click this to edit the selected array value */
99
private JButton m_EditBut = new JButton("Edit");
101
/** Click this to move the selected array value(s) one up */
102
private JButton m_UpBut = new JButton("Up");
104
/** Click this to move the selected array value(s) one down */
105
private JButton m_DownBut = new JButton("Down");
107
/** Click to add the current object configuration to the array */
108
private JButton m_AddBut = new JButton("Add");
110
/** The property editor for editing existing elements */
111
private PropertyEditor m_Editor = new GenericObjectEditor();
113
/** The currently displayed property dialog, if any */
114
private PropertyDialog m_PD;
116
/** Listens to buttons being pressed and taking the appropriate action */
117
private ActionListener m_InnerActionListener =
118
new ActionListener() {
120
public void actionPerformed(ActionEvent e) {
122
if (e.getSource() == m_DeleteBut) {
123
int [] selected = m_ElementList.getSelectedIndices();
124
if (selected != null) {
125
for (int i = 0; i < selected.length; i++) {
126
int current = selected[i];
127
m_ListModel.removeElementAt(current);
128
if (m_ListModel.size() > current) {
129
m_ElementList.setSelectedIndex(current);
132
m_Support.firePropertyChange("", null, null);
134
} else if (e.getSource() == m_EditBut) {
135
((GenericObjectEditor) m_Editor).setClassType(m_ElementClass);
136
m_Editor.setValue(m_ElementList.getSelectedValue());
137
if (m_Editor.getValue() != null) {
139
int x = getLocationOnScreen().x;
140
int y = getLocationOnScreen().y;
141
m_PD = new PropertyDialog(m_Editor, x, y);
144
m_PD.setVisible(true);
146
m_ElementList.setSelectedValue(m_Editor.getValue(), false);
147
m_Support.firePropertyChange("", null, null);
149
} else if (e.getSource() == m_UpBut) {
150
JListHelper.moveUp(m_ElementList);
151
m_Support.firePropertyChange("", null, null);
152
} else if (e.getSource() == m_DownBut) {
153
JListHelper.moveDown(m_ElementList);
154
m_Support.firePropertyChange("", null, null);
155
} else if (e.getSource() == m_AddBut) {
156
int selected = m_ElementList.getSelectedIndex();
157
Object addObj = m_ElementEditor.getValue();
159
// Make a full copy of the object using serialization
161
SerializedObject so = new SerializedObject(addObj);
162
addObj = so.getObject();
163
if (selected != -1) {
164
m_ListModel.insertElementAt(addObj, selected);
166
m_ListModel.addElement(addObj);
168
m_Support.firePropertyChange("", null, null);
169
} catch (Exception ex) {
170
JOptionPane.showMessageDialog(GenericArrayEditor.this,
171
"Could not create an object copy",
173
JOptionPane.ERROR_MESSAGE);
179
/** Listens to list items being selected and takes appropriate action */
180
private ListSelectionListener m_InnerSelectionListener =
181
new ListSelectionListener() {
183
public void valueChanged(ListSelectionEvent e) {
185
if (e.getSource() == m_ElementList) {
186
// Enable the delete/edit button
187
if (m_ElementList.getSelectedIndex() != -1) {
188
m_DeleteBut.setEnabled(true);
189
m_EditBut.setEnabled(m_ElementList.getSelectedIndices().length == 1);
190
m_UpBut.setEnabled(JListHelper.canMoveUp(m_ElementList));
191
m_DownBut.setEnabled(JListHelper.canMoveDown(m_ElementList));
193
// disable delete/edit button
195
m_DeleteBut.setEnabled(false);
196
m_EditBut.setEnabled(false);
197
m_UpBut.setEnabled(false);
198
m_DownBut.setEnabled(false);
204
/** Listens to mouse events and takes appropriate action */
205
private MouseListener m_InnerMouseListener =
208
public void mouseClicked(MouseEvent e) {
209
if (e.getSource() == m_ElementList) {
210
if (e.getClickCount() == 2) {
211
// unfortunately, locationToIndex only returns the nearest entry
212
// and not the exact one, i.e. if there's one item in the list and
213
// one doublelclicks somewhere in the list, this index will be
215
int index = m_ElementList.locationToIndex(e.getPoint());
217
m_InnerActionListener.actionPerformed(
218
new ActionEvent(m_EditBut, 0, ""));
226
* Sets up the array editor.
228
public GenericArrayEditor() {
230
setLayout(new BorderLayout());
231
add(m_Label, BorderLayout.CENTER);
232
m_DeleteBut.addActionListener(m_InnerActionListener);
233
m_EditBut.addActionListener(m_InnerActionListener);
234
m_UpBut.addActionListener(m_InnerActionListener);
235
m_DownBut.addActionListener(m_InnerActionListener);
236
m_AddBut.addActionListener(m_InnerActionListener);
237
m_ElementList.addListSelectionListener(m_InnerSelectionListener);
238
m_ElementList.addMouseListener(m_InnerMouseListener);
239
m_AddBut.setToolTipText("Add the current item to the list");
240
m_DeleteBut.setToolTipText("Delete the selected list item");
241
m_EditBut.setToolTipText("Edit the selected list item");
242
m_UpBut.setToolTipText("Move the selected item(s) one up");
243
m_DownBut.setToolTipText("Move the selected item(s) one down");
246
/** This class handles the creation of list cell renderers from the
249
private class EditorListCellRenderer implements ListCellRenderer {
251
/** The class of the property editor for array objects */
252
private Class m_EditorClass;
254
/** The class of the array values */
255
private Class m_ValueClass;
258
* Creates the list cell renderer.
260
* @param editorClass The class of the property editor for array objects
261
* @param valueClass The class of the array values
263
public EditorListCellRenderer(Class editorClass, Class valueClass) {
264
m_EditorClass = editorClass;
265
m_ValueClass = valueClass;
269
* Creates a cell rendering component.
271
* @param JList the list that will be rendered in
272
* @param Object the cell value
273
* @param int which element of the list to render
274
* @param boolean true if the cell is selected
275
* @param boolean true if the cell has the focus
276
* @return the rendering component
278
public Component getListCellRendererComponent(final JList list,
281
final boolean isSelected,
282
final boolean cellHasFocus) {
284
final PropertyEditor e = (PropertyEditor)m_EditorClass.newInstance();
285
if (e instanceof GenericObjectEditor) {
286
// ((GenericObjectEditor) e).setDisplayOnly(true);
287
((GenericObjectEditor) e).setClassType(m_ValueClass);
290
return new JPanel() {
292
private static final long serialVersionUID = -3124434678426673334L;
294
public void paintComponent(Graphics g) {
296
Insets i = this.getInsets();
297
Rectangle box = new Rectangle(i.left, i.top,
298
this.getWidth() - i.right,
299
this.getHeight() - i.bottom );
300
g.setColor(isSelected
301
? list.getSelectionBackground()
302
: list.getBackground());
303
g.fillRect(0, 0, this.getWidth(), this.getHeight());
304
g.setColor(isSelected
305
? list.getSelectionForeground()
306
: list.getForeground());
307
e.paintValue(g, box);
310
public Dimension getPreferredSize() {
312
Font f = this.getFont();
313
FontMetrics fm = this.getFontMetrics(f);
314
return new Dimension(0, fm.getHeight());
317
} catch (Exception ex) {
324
* Updates the type of object being edited, so attempts to find an
325
* appropriate propertyeditor.
327
* @param o a value of type 'Object'
329
private void updateEditorType(Object o) {
331
// Determine if the current object is an array
332
m_ElementEditor = null; m_ListModel = null;
334
if ((o != null) && (o.getClass().isArray())) {
335
Class elementClass = o.getClass().getComponentType();
336
PropertyEditor editor = PropertyEditorManager.findEditor(elementClass);
337
Component view = null;
338
ListCellRenderer lcr = new DefaultListCellRenderer();
339
if (editor != null) {
340
if (editor instanceof GenericObjectEditor) {
341
((GenericObjectEditor) editor).setClassType(elementClass);
344
//setting the value in the editor so that
345
//we don't get a NullPointerException
346
//when we do getAsText() in the constructor of
347
//PropertyValueSelector()
348
if(Array.getLength(o) > 0) {
349
editor.setValue(Array.get(o,0));
351
if (editor instanceof GenericObjectEditor) {
352
((GenericObjectEditor)editor).setDefaultValue();
355
editor.setValue(elementClass.newInstance());
356
} catch(Exception ex) {
357
m_ElementEditor=null;
358
System.err.println(ex.getMessage());
359
add(m_Label, BorderLayout.CENTER);
360
m_Support.firePropertyChange("", null, null);
367
if (editor.isPaintable() && editor.supportsCustomEditor()) {
368
view = new PropertyPanel(editor);
369
lcr = new EditorListCellRenderer(editor.getClass(), elementClass);
370
} else if (editor.getTags() != null) {
371
view = new PropertyValueSelector(editor);
372
} else if (editor.getAsText() != null) {
373
view = new PropertyText(editor);
377
System.err.println("No property editor for class: "
378
+ elementClass.getName());
380
m_ElementEditor = editor;
382
// Create the ListModel and populate it
383
m_ListModel = new DefaultListModel();
384
m_ElementClass = elementClass;
385
for (int i = 0; i < Array.getLength(o); i++) {
386
m_ListModel.addElement(Array.get(o,i));
388
m_ElementList.setCellRenderer(lcr);
389
m_ElementList.setModel(m_ListModel);
390
if (m_ListModel.getSize() > 0) {
391
m_ElementList.setSelectedIndex(0);
393
m_DeleteBut.setEnabled(false);
394
m_EditBut.setEnabled(false);
396
m_UpBut.setEnabled(JListHelper.canMoveDown(m_ElementList));
397
m_DownBut.setEnabled(JListHelper.canMoveDown(m_ElementList));
399
//have already set the value above in the editor
401
//if (m_ListModel.getSize() > 0) {
402
// m_ElementEditor.setValue(m_ListModel.getElementAt(0));
404
// if (m_ElementEditor instanceof GenericObjectEditor) {
405
// ((GenericObjectEditor)m_ElementEditor).setDefaultValue();
407
// m_ElementEditor.setValue(m_ElementClass.newInstance());
411
JPanel panel = new JPanel();
412
panel.setLayout(new BorderLayout());
413
panel.add(view, BorderLayout.CENTER);
414
panel.add(m_AddBut, BorderLayout.EAST);
415
add(panel, BorderLayout.NORTH);
416
add(new JScrollPane(m_ElementList), BorderLayout.CENTER);
417
JPanel panel2 = new JPanel();
418
panel2.setLayout(new GridLayout(1, 4));
419
panel2.add(m_DeleteBut);
420
panel2.add(m_EditBut);
422
panel2.add(m_DownBut);
423
add(panel2, BorderLayout.SOUTH);
425
.addPropertyChangeListener(new PropertyChangeListener() {
426
public void propertyChange(PropertyChangeEvent e) {
430
//} catch (Exception ex) {
431
// System.err.println(ex.getMessage());
432
// m_ElementEditor = null;
436
if (m_ElementEditor == null) {
437
add(m_Label, BorderLayout.CENTER);
439
m_Support.firePropertyChange("", null, null);
444
* Sets the current object array.
446
* @param o an object that must be an array.
448
public void setValue(Object o) {
450
// Create a new list model, put it in the list and resize?
455
* Gets the current object array.
457
* @return the current object array
459
public Object getValue() {
461
if (m_ListModel == null) {
464
// Convert the listmodel to an array of strings and return it.
465
int length = m_ListModel.getSize();
466
Object result = Array.newInstance(m_ElementClass, length);
467
for (int i = 0; i < length; i++) {
468
Array.set(result, i, m_ListModel.elementAt(i));
474
* Supposedly returns an initialization string to create a classifier
475
* identical to the current one, including it's state, but this doesn't
476
* appear possible given that the initialization string isn't supposed to
477
* contain multiple statements.
479
* @return the java source code initialisation string
481
public String getJavaInitializationString() {
487
* Returns true to indicate that we can paint a representation of the
492
public boolean isPaintable() {
497
* Paints a representation of the current classifier.
499
* @param gfx the graphics context to use
500
* @param box the area we are allowed to paint into
502
public void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box) {
504
FontMetrics fm = gfx.getFontMetrics();
505
int vpad = (box.height - fm.getAscent()) / 2;
506
String rep = m_ListModel.getSize() + " " + m_ElementClass.getName();
507
gfx.drawString(rep, 2, fm.getHeight() + vpad);
511
* Returns null as we don't support getting/setting values as text.
515
public String getAsText() {
520
* Returns null as we don't support getting/setting values as text.
522
* @param text the text value
523
* @exception IllegalArgumentException as we don't support
524
* getting/setting values as text.
526
public void setAsText(String text) {
527
throw new IllegalArgumentException(text);
531
* Returns null as we don't support getting values as tags.
535
public String[] getTags() {
540
* Returns true because we do support a custom editor.
544
public boolean supportsCustomEditor() {
549
* Returns the array editing component.
551
* @return a value of type 'java.awt.Component'
553
public java.awt.Component getCustomEditor() {
558
* Adds a PropertyChangeListener who will be notified of value changes.
560
* @param l a value of type 'PropertyChangeListener'
562
public void addPropertyChangeListener(PropertyChangeListener l) {
563
m_Support.addPropertyChangeListener(l);
567
* Removes a PropertyChangeListener.
569
* @param l a value of type 'PropertyChangeListener'
571
public void removePropertyChangeListener(PropertyChangeListener l) {
572
m_Support.removePropertyChangeListener(l);
576
* Tests out the array editor from the command line.
578
* @param args ignored
580
public static void main(String [] args) {
583
GenericObjectEditor.registerEditors();
585
final GenericArrayEditor ce = new GenericArrayEditor();
587
final weka.filters.Filter [] initial = new weka.filters.Filter [0];
590
new weka.filters.AddFilter()
593
final String [] initial = {
598
PropertyDialog pd = new PropertyDialog(ce, 100, 100);
600
pd.addWindowListener(new WindowAdapter() {
601
private static final long serialVersionUID = -3124434678426673334L;
602
public void windowClosing(WindowEvent e) {
606
ce.setValue(initial);
608
} catch (Exception ex) {
609
ex.printStackTrace();
610
System.err.println(ex.getMessage());