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

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

Import di weka.

File size: 19.1 KB
Line 
1/*
2 *    This program is free software; you can redistribute it and/or modify
3 *    it under the terms of the GNU General Public License as published by
4 *    the Free Software Foundation; either version 2 of the License, or
5 *    (at your option) any later version.
6 *
7 *    This program is distributed in the hope that it will be useful,
8 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
9 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 *    GNU General Public License for more details.
11 *
12 *    You should have received a copy of the GNU General Public License
13 *    along with this program; if not, write to the Free Software
14 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
15 */
16
17/*
18 *    GenericArrayEditor.java
19 *    Copyright (C) 1999 University of Waikato, Hamilton, New Zealand
20 *
21 */
22
23package weka.gui;
24
25import weka.core.SerializedObject;
26
27import java.awt.BorderLayout;
28import java.awt.Component;
29import java.awt.Dimension;
30import java.awt.Font;
31import java.awt.FontMetrics;
32import java.awt.Frame;
33import java.awt.Graphics;
34import java.awt.GridLayout;
35import java.awt.Insets;
36import java.awt.Rectangle;
37import java.awt.event.ActionEvent;
38import java.awt.event.ActionListener;
39import java.awt.event.MouseAdapter;
40import java.awt.event.MouseEvent;
41import java.awt.event.MouseListener;
42import java.awt.event.WindowAdapter;
43import java.awt.event.WindowEvent;
44import java.beans.PropertyChangeEvent;
45import java.beans.PropertyChangeListener;
46import java.beans.PropertyChangeSupport;
47import java.beans.PropertyEditor;
48import java.beans.PropertyEditorManager;
49import java.lang.reflect.Array;
50
51import javax.swing.DefaultListCellRenderer;
52import javax.swing.DefaultListModel;
53import javax.swing.JButton;
54import javax.swing.JLabel;
55import javax.swing.JList;
56import javax.swing.JOptionPane;
57import javax.swing.JPanel;
58import javax.swing.JScrollPane;
59import javax.swing.ListCellRenderer;
60import javax.swing.SwingConstants;
61import javax.swing.event.ListSelectionEvent;
62import javax.swing.event.ListSelectionListener;
63
64/**
65 * A PropertyEditor for arrays of objects that themselves have
66 * property editors.
67 *
68 * @author Len Trigg (trigg@cs.waikato.ac.nz)
69 * @version $Revision: 6125 $
70 */
71public class GenericArrayEditor
72  extends JPanel
73  implements PropertyEditor {
74
75  /** for serialization. */
76  private static final long serialVersionUID = 3914616975334750480L;
77
78  /** Handles property change notification. */
79  private PropertyChangeSupport m_Support = new PropertyChangeSupport(this);
80
81  /** The label for when we can't edit that type. */
82  private JLabel m_Label = new JLabel("Can't edit", SwingConstants.CENTER);
83 
84  /** The list component displaying current values. */
85  private JList m_ElementList = new JList();
86
87  /** The class of objects allowed in the array. */
88  private Class m_ElementClass = String.class;
89
90  /** The defaultlistmodel holding our data. */
91  private DefaultListModel m_ListModel;
92
93  /** The property editor for the class we are editing. */
94  private PropertyEditor m_ElementEditor;
95
96  /** Click this to delete the selected array values. */
97  private JButton m_DeleteBut = new JButton("Delete");
98
99  /** Click this to edit the selected array value. */
100  private JButton m_EditBut = new JButton("Edit");
101
102  /** Click this to move the selected array value(s) one up. */
103  private JButton m_UpBut = new JButton("Up");
104
105  /** Click this to move the selected array value(s) one down. */
106  private JButton m_DownBut = new JButton("Down");
107
108  /** Click to add the current object configuration to the array. */
109  private JButton m_AddBut = new JButton("Add");
110
111  /** The property editor for editing existing elements. */
112  private PropertyEditor m_Editor = new GenericObjectEditor();
113
114  /** The currently displayed property dialog, if any. */
115  private PropertyDialog m_PD;
116
117  /** Listens to buttons being pressed and taking the appropriate action. */
118  private ActionListener m_InnerActionListener =
119    new ActionListener() {
120
121    public void actionPerformed(ActionEvent e) {
122
123      if (e.getSource() == m_DeleteBut) {
124        int [] selected = m_ElementList.getSelectedIndices();
125        if (selected != null) {
126          for (int i = selected.length - 1; i >= 0; i--) {
127            int current = selected[i];
128            m_ListModel.removeElementAt(current);
129            if (m_ListModel.size() > current) {
130              m_ElementList.setSelectedIndex(current);
131            }
132          }
133          m_Support.firePropertyChange("", null, null);
134        }
135      } else if (e.getSource() == m_EditBut) {
136        ((GenericObjectEditor) m_Editor).setClassType(m_ElementClass);
137        m_Editor.setValue(m_ElementList.getSelectedValue());
138        if (m_Editor.getValue() != null) {
139          if (m_PD == null) {
140            int x = getLocationOnScreen().x;
141            int y = getLocationOnScreen().y;
142            if (PropertyDialog.getParentDialog(GenericArrayEditor.this) != null)
143              m_PD = new PropertyDialog(
144                  PropertyDialog.getParentDialog(GenericArrayEditor.this), 
145                  m_Editor, x, y);
146            else
147              m_PD = new PropertyDialog(
148                  PropertyDialog.getParentFrame(GenericArrayEditor.this), 
149                  m_Editor, x, y);
150            m_PD.setVisible(true);
151          } 
152          else {
153            m_PD.setVisible(true);
154          }
155          m_Support.firePropertyChange("", null, null);
156        }
157      } else if (e.getSource() == m_UpBut) {
158        JListHelper.moveUp(m_ElementList);
159        m_Support.firePropertyChange("", null, null);
160      } else if (e.getSource() == m_DownBut) {
161        JListHelper.moveDown(m_ElementList);
162        m_Support.firePropertyChange("", null, null);
163      } else if (e.getSource() == m_AddBut) {
164        int selected = m_ElementList.getSelectedIndex();
165        Object addObj = m_ElementEditor.getValue();
166       
167        // Make a full copy of the object using serialization
168        try {
169          SerializedObject so = new SerializedObject(addObj);
170          addObj = so.getObject();
171          if (selected != -1) {
172            m_ListModel.insertElementAt(addObj, selected);
173          } else {
174            m_ListModel.addElement(addObj);
175          }
176          m_Support.firePropertyChange("", null, null);
177        } catch (Exception ex) {
178          JOptionPane.showMessageDialog(GenericArrayEditor.this,
179                                        "Could not create an object copy",
180                                        null,
181                                        JOptionPane.ERROR_MESSAGE);
182        }
183      } 
184    }
185  };
186
187  /** Listens to list items being selected and takes appropriate action. */
188  private ListSelectionListener m_InnerSelectionListener =
189    new ListSelectionListener() {
190
191      public void valueChanged(ListSelectionEvent e) {
192
193        if (e.getSource() == m_ElementList) {
194          // Enable the delete/edit button
195          if (m_ElementList.getSelectedIndex() != -1) {
196            m_DeleteBut.setEnabled(true);
197            m_EditBut.setEnabled(m_ElementList.getSelectedIndices().length == 1);
198            m_UpBut.setEnabled(JListHelper.canMoveUp(m_ElementList));
199            m_DownBut.setEnabled(JListHelper.canMoveDown(m_ElementList));
200          }
201          // disable delete/edit button
202          else {
203            m_DeleteBut.setEnabled(false);
204            m_EditBut.setEnabled(false);
205            m_UpBut.setEnabled(false);
206            m_DownBut.setEnabled(false);
207          }
208        }
209      }
210  };
211
212  /** Listens to mouse events and takes appropriate action. */
213  private MouseListener m_InnerMouseListener =
214    new MouseAdapter() {
215
216      public void mouseClicked(MouseEvent e) {
217        if (e.getSource() == m_ElementList) {
218          if (e.getClickCount() == 2) {
219            // unfortunately, locationToIndex only returns the nearest entry
220            // and not the exact one, i.e. if there's one item in the list and
221            // one doublelclicks somewhere in the list, this index will be
222            // returned
223            int index = m_ElementList.locationToIndex(e.getPoint());
224            if (index > -1)
225              m_InnerActionListener.actionPerformed(
226                  new ActionEvent(m_EditBut, 0, ""));
227          }
228        }
229      }
230  };
231   
232
233  /**
234   * Sets up the array editor.
235   */
236  public GenericArrayEditor() {
237
238    setLayout(new BorderLayout());
239    add(m_Label, BorderLayout.CENTER);
240    m_DeleteBut.addActionListener(m_InnerActionListener);
241    m_EditBut.addActionListener(m_InnerActionListener);
242    m_UpBut.addActionListener(m_InnerActionListener);
243    m_DownBut.addActionListener(m_InnerActionListener);
244    m_AddBut.addActionListener(m_InnerActionListener);
245    m_ElementList.addListSelectionListener(m_InnerSelectionListener);
246    m_ElementList.addMouseListener(m_InnerMouseListener);
247    m_AddBut.setToolTipText("Add the current item to the list");
248    m_DeleteBut.setToolTipText("Delete the selected list item");
249    m_EditBut.setToolTipText("Edit the selected list item");
250    m_UpBut.setToolTipText("Move the selected item(s) one up");
251    m_DownBut.setToolTipText("Move the selected item(s) one down");
252  }
253
254  /** This class handles the creation of list cell renderers from the
255   * property editors.
256   */
257  private class EditorListCellRenderer implements ListCellRenderer {
258
259    /** The class of the property editor for array objects. */
260    private Class m_EditorClass;
261
262    /** The class of the array values. */
263    private Class m_ValueClass;
264
265    /**
266     * Creates the list cell renderer.
267     *
268     * @param editorClass The class of the property editor for array objects
269     * @param valueClass The class of the array values
270     */
271    public EditorListCellRenderer(Class editorClass, Class valueClass) {
272      m_EditorClass = editorClass;
273      m_ValueClass = valueClass;
274    }
275
276    /**
277     * Creates a cell rendering component.
278     *
279     * @param list the list that will be rendered in
280     * @param value the cell value
281     * @param index which element of the list to render
282     * @param isSelected true if the cell is selected
283     * @param cellHasFocus true if the cell has the focus
284     * @return the rendering component
285     */
286    public Component getListCellRendererComponent(final JList list,
287                                                  final Object value,
288                                                  final int index,
289                                                  final boolean isSelected,
290                                                  final boolean cellHasFocus) {
291      try {
292        final PropertyEditor e = (PropertyEditor)m_EditorClass.newInstance();
293        if (e instanceof GenericObjectEditor) {
294          //      ((GenericObjectEditor) e).setDisplayOnly(true);
295          ((GenericObjectEditor) e).setClassType(m_ValueClass);
296        }
297        e.setValue(value);
298        return new JPanel() {
299         
300          private static final long serialVersionUID = -3124434678426673334L;
301
302          public void paintComponent(Graphics g) {
303
304            Insets i = this.getInsets();
305            Rectangle box = new Rectangle(i.left, i.top,
306                                          this.getWidth() - i.right,
307                                          this.getHeight() - i.bottom );
308            g.setColor(isSelected
309                       ? list.getSelectionBackground()
310                       : list.getBackground());
311            g.fillRect(0, 0, this.getWidth(), this.getHeight());
312            g.setColor(isSelected
313                       ? list.getSelectionForeground()
314                       : list.getForeground());
315            e.paintValue(g, box);
316          }
317         
318          public Dimension getPreferredSize() {
319
320            Font f = this.getFont();
321            FontMetrics fm = this.getFontMetrics(f);
322            return new Dimension(0, fm.getHeight());
323          }
324        };
325      } catch (Exception ex) {
326        return null;
327      }
328    }
329  }
330
331  /**
332   * Updates the type of object being edited, so attempts to find an
333   * appropriate propertyeditor.
334   *
335   * @param o a value of type 'Object'
336   */
337  private void updateEditorType(Object o) {
338
339    // Determine if the current object is an array
340    m_ElementEditor = null; m_ListModel = null;
341    removeAll();
342    if ((o != null) && (o.getClass().isArray())) {
343      Class elementClass = o.getClass().getComponentType();   
344      PropertyEditor editor = PropertyEditorManager.findEditor(elementClass);
345      Component view = null;
346      ListCellRenderer lcr = new DefaultListCellRenderer();
347      if (editor != null) {
348        if (editor instanceof GenericObjectEditor) {
349          ((GenericObjectEditor) editor).setClassType(elementClass);
350        }
351
352        //setting the value in the editor so that
353        //we don't get a NullPointerException
354        //when we do getAsText() in the constructor of
355        //PropertyValueSelector()
356        if(Array.getLength(o) > 0) {
357          editor.setValue(makeCopy(Array.get(o,0)));
358        } else {
359          if (editor instanceof GenericObjectEditor) {
360            ((GenericObjectEditor)editor).setDefaultValue();
361          } else {   
362            try {
363            editor.setValue(elementClass.newInstance());
364            } catch(Exception ex) {
365              m_ElementEditor=null;
366              System.err.println(ex.getMessage());
367              add(m_Label, BorderLayout.CENTER);
368              m_Support.firePropertyChange("", null, null);
369              validate();
370              return;
371            }
372          }
373        }
374       
375        if (editor.isPaintable() && editor.supportsCustomEditor()) {
376          view = new PropertyPanel(editor);
377          lcr = new EditorListCellRenderer(editor.getClass(), elementClass);
378        } else if (editor.getTags() != null) {
379          view = new PropertyValueSelector(editor);
380        } else if (editor.getAsText() != null) {
381          view = new PropertyText(editor);
382        }
383      }
384      if (view == null) {
385        System.err.println("No property editor for class: "
386                           + elementClass.getName());
387      } else {
388        m_ElementEditor = editor;
389
390        // Create the ListModel and populate it
391        m_ListModel = new DefaultListModel();
392        m_ElementClass = elementClass;
393        for (int i = 0; i < Array.getLength(o); i++) {
394          m_ListModel.addElement(Array.get(o,i));
395        }
396        m_ElementList.setCellRenderer(lcr);
397        m_ElementList.setModel(m_ListModel);
398        if (m_ListModel.getSize() > 0) {
399          m_ElementList.setSelectedIndex(0);
400        } else {
401          m_DeleteBut.setEnabled(false);
402          m_EditBut.setEnabled(false);
403        }
404        m_UpBut.setEnabled(JListHelper.canMoveDown(m_ElementList));
405        m_DownBut.setEnabled(JListHelper.canMoveDown(m_ElementList));
406
407        //have already set the value above in the editor
408        //try {
409          //if (m_ListModel.getSize() > 0) {
410          //  m_ElementEditor.setValue(m_ListModel.getElementAt(0));
411          //} else {
412          //  if (m_ElementEditor instanceof GenericObjectEditor) {
413          //    ((GenericObjectEditor)m_ElementEditor).setDefaultValue();
414          //  } else {
415          //    m_ElementEditor.setValue(m_ElementClass.newInstance());
416          //  }
417          //}
418         
419          JPanel panel = new JPanel();
420          panel.setLayout(new BorderLayout());
421          panel.add(view, BorderLayout.CENTER);
422          panel.add(m_AddBut, BorderLayout.EAST);
423          add(panel, BorderLayout.NORTH);
424          add(new JScrollPane(m_ElementList), BorderLayout.CENTER);
425          JPanel panel2 = new JPanel();
426          panel2.setLayout(new GridLayout(1, 4));
427          panel2.add(m_DeleteBut);
428          panel2.add(m_EditBut);
429          panel2.add(m_UpBut);
430          panel2.add(m_DownBut);
431          add(panel2, BorderLayout.SOUTH);
432          m_ElementEditor
433            .addPropertyChangeListener(new PropertyChangeListener() {
434            public void propertyChange(PropertyChangeEvent e) {
435              repaint();
436            }
437          });
438        //} catch (Exception ex) {
439        //  System.err.println(ex.getMessage());
440        //  m_ElementEditor = null;
441        //}
442      }
443    }
444    if (m_ElementEditor == null) {
445      add(m_Label, BorderLayout.CENTER);
446    }
447    m_Support.firePropertyChange("", null, null);
448    validate();
449  }
450
451  /**
452   * Sets the current object array.
453   *
454   * @param o an object that must be an array.
455   */
456  public void setValue(Object o) {
457
458    // Create a new list model, put it in the list and resize?
459    updateEditorType(o);
460  }
461
462  /**
463   * Gets the current object array.
464   *
465   * @return the current object array
466   */
467  public Object getValue() {
468
469    if (m_ListModel == null) {
470      return null;
471    }
472    // Convert the listmodel to an array of strings and return it.
473    int length = m_ListModel.getSize();
474    Object result = Array.newInstance(m_ElementClass, length);
475    for (int i = 0; i < length; i++) {
476      Array.set(result, i, m_ListModel.elementAt(i));
477    }
478    return result;
479  }
480 
481  /**
482   * Supposedly returns an initialization string to create a classifier
483   * identical to the current one, including it's state, but this doesn't
484   * appear possible given that the initialization string isn't supposed to
485   * contain multiple statements.
486   *
487   * @return the java source code initialisation string
488   */
489  public String getJavaInitializationString() {
490
491    return "null";
492  }
493
494  /**
495   * Returns true to indicate that we can paint a representation of the
496   * string array.
497   *
498   * @return true
499   */
500  public boolean isPaintable() {
501    return true;
502  }
503
504  /**
505   * Paints a representation of the current classifier.
506   *
507   * @param gfx the graphics context to use
508   * @param box the area we are allowed to paint into
509   */
510  public void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box) {
511
512    FontMetrics fm = gfx.getFontMetrics();
513    int vpad = (box.height - fm.getHeight()) / 2;
514    String rep = m_ListModel.getSize() + " " + m_ElementClass.getName();
515    gfx.drawString(rep, 2, fm.getAscent() + vpad + 2);
516  }
517
518  /**
519   * Returns null as we don't support getting/setting values as text.
520   *
521   * @return null
522   */
523  public String getAsText() {
524    return null;
525  }
526
527  /**
528   * Returns null as we don't support getting/setting values as text.
529   *
530   * @param text the text value
531   * @exception IllegalArgumentException as we don't support
532   * getting/setting values as text.
533   */
534  public void setAsText(String text) {
535    throw new IllegalArgumentException(text);
536  }
537
538  /**
539   * Returns null as we don't support getting values as tags.
540   *
541   * @return null
542   */
543  public String[] getTags() {
544    return null;
545  }
546
547  /**
548   * Returns true because we do support a custom editor.
549   *
550   * @return true
551   */
552  public boolean supportsCustomEditor() {
553    return true;
554  }
555 
556  /**
557   * Returns the array editing component.
558   *
559   * @return a value of type 'java.awt.Component'
560   */
561  public java.awt.Component getCustomEditor() {
562    return this;
563  }
564
565  /**
566   * Adds a PropertyChangeListener who will be notified of value changes.
567   *
568   * @param l a value of type 'PropertyChangeListener'
569   */
570  public void addPropertyChangeListener(PropertyChangeListener l) {
571    m_Support.addPropertyChangeListener(l);
572  }
573
574  /**
575   * Removes a PropertyChangeListener.
576   *
577   * @param l a value of type 'PropertyChangeListener'
578   */
579  public void removePropertyChangeListener(PropertyChangeListener l) {
580    m_Support.removePropertyChangeListener(l);
581  }
582
583  /**
584   * Makes a copy of an object using serialization.
585   *
586   * @param source the object to copy
587   * @return a copy of the source object, null if copying fails
588   */
589  public static Object makeCopy(Object source) {
590    Object      result;
591   
592    try {
593      result = GenericObjectEditor.makeCopy(source);
594    }
595    catch (Exception e) {
596      result = null;
597    }
598   
599    return result;
600  }
601
602  /**
603   * Tests out the array editor from the command line.
604   *
605   * @param args ignored
606   */
607  public static void main(String [] args) {
608
609    try {
610      GenericObjectEditor.registerEditors();
611
612      final GenericArrayEditor ce = new GenericArrayEditor();
613
614      final weka.filters.Filter [] initial = new weka.filters.Filter [0];
615        /*
616      {
617        new weka.filters.AddFilter()
618        };*/
619      /*
620      final String [] initial = {
621        "Hello",
622        "There",
623        "Bob"
624        };*/
625      PropertyDialog pd = new PropertyDialog((Frame) null, ce, 100, 100);
626      pd.setSize(200,200);
627      pd.addWindowListener(new WindowAdapter() {
628        private static final long serialVersionUID = -3124434678426673334L;
629        public void windowClosing(WindowEvent e) {
630          System.exit(0);
631        }
632      });
633      ce.setValue(initial);
634      pd.setVisible(true);
635      //ce.validate();
636    } catch (Exception ex) {
637      ex.printStackTrace();
638      System.err.println(ex.getMessage());
639    }
640  }
641
642}
643
Note: See TracBrowser for help on using the repository browser.