source: src/main/java/weka/gui/CostMatrixEditor.java @ 10

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

Import di weka.

File size: 14.8 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 *    CostMatrixEditor.java
19 *    Copyright (C) 2002 University of Waikato, Hamilton, New Zealand
20 *
21 */
22
23package weka.gui;
24
25import weka.classifiers.CostMatrix;
26
27import java.awt.BorderLayout;
28import java.awt.Component;
29import java.awt.Graphics;
30import java.awt.GridBagConstraints;
31import java.awt.GridBagLayout;
32import java.awt.GridLayout;
33import java.awt.Insets;
34import java.awt.Rectangle;
35import java.awt.event.ActionEvent;
36import java.awt.event.ActionListener;
37import java.beans.PropertyChangeListener;
38import java.beans.PropertyChangeSupport;
39import java.beans.PropertyEditor;
40import java.io.BufferedReader;
41import java.io.BufferedWriter;
42import java.io.File;
43import java.io.FileReader;
44import java.io.FileWriter;
45import java.io.Reader;
46import java.io.Writer;
47
48import javax.swing.JButton;
49import javax.swing.JFileChooser;
50import javax.swing.JLabel;
51import javax.swing.JOptionPane;
52import javax.swing.JPanel;
53import javax.swing.JTable;
54import javax.swing.JTextField;
55import javax.swing.SwingConstants;
56import javax.swing.event.TableModelEvent;
57import javax.swing.event.TableModelListener;
58import javax.swing.table.AbstractTableModel;
59
60/**
61 * Class for editing CostMatrix objects. Brings up a custom editing panel
62 * with which the user can edit the matrix interactively, as well as save
63 * load cost matrices from files.
64 *
65 * @author Richard Kirkby (rkirkby@cs.waikato.ac.nz)
66 * @version $Revision: 1.11 $
67 */
68public class CostMatrixEditor 
69  implements PropertyEditor {
70
71  /** The cost matrix being edited */
72  private CostMatrix m_matrix;
73
74  /** A helper class for notifying listeners */
75  private PropertyChangeSupport m_propSupport;
76
77  /** An instance of the custom editor */
78  private CustomEditor m_customEditor;
79
80  /** The file chooser for the user to select cost files to save and load */
81  private JFileChooser m_fileChooser
82    = new JFileChooser(new File(System.getProperty("user.dir")));
83
84  /**
85   * This class wraps around the cost matrix presenting it as a TableModel
86   * so that it can be displayed and edited in a JTable.
87   */
88  private class CostMatrixTableModel 
89    extends AbstractTableModel {
90   
91    /** for serialization */
92    static final long serialVersionUID = -2762326138357037181L;
93
94    /**
95     * Gets the number of rows in the matrix. Cost matrices are square so it is the
96     * same as the column count, i.e. the size of the matrix.
97     *
98     * @return the row count
99     */
100    public int getRowCount() {
101
102      return m_matrix.size();
103    }
104
105    /**
106     * Gets the number of columns in the matrix. Cost matrices are square so it is
107     * the same as the row count, i.e. the size of the matrix.
108     *
109     * @return the row count
110     */
111    public int getColumnCount() {
112
113      return m_matrix.size();
114    }
115
116    /**
117     * Returns a value at the specified position in the cost matrix.
118     *
119     * @param row the row position
120     * @param column the column position
121     * @return the value
122     */
123    public Object getValueAt(int row, int column) {
124
125      //      return new Double(m_matrix.getElement(row, column));
126      try {
127        return m_matrix.getCell(row, column);
128      } catch (Exception ex) {
129        ex.printStackTrace();
130      }
131      return new Double(0.0);
132    }
133
134    /**
135     * Sets a value at a specified position in the cost matrix.
136     *
137     * @param aValue the new value (should be of type Double).
138     * @param rowIndex the row position
139     * @param columnIndex the column position
140     */
141    public void setValueAt(Object aValue,
142                           int rowIndex,
143                           int columnIndex) {
144
145      //      double value = ((Double) aValue).doubleValue();
146      //      m_matrix.setElement(rowIndex, columnIndex, value);
147      // try to parse it as a double first
148      Double val;
149      try {
150        val = new Double(((String)aValue));
151        double value = val.doubleValue();
152      } catch (Exception ex) {
153        val = null;
154      }
155      if (val == null) {
156        m_matrix.setCell(rowIndex, columnIndex, aValue);
157      } else {
158        m_matrix.setCell(rowIndex, columnIndex, val);
159      }
160      fireTableCellUpdated(rowIndex, columnIndex);
161    }
162
163    /**
164     * Indicates whether a cell in the table is editable. In this case all cells
165     * are editable so true is always returned.
166     *
167     * @param rowIndex the row position
168     * @param columnIndex the column position
169     * @return true
170     */   
171    public boolean isCellEditable(int rowIndex,
172                                  int columnIndex) {
173
174      return true;
175    }
176
177    /**
178     * Indicates the class of the objects within a column of the table. In this
179     * case all columns in the cost matrix consist of double values so Double.class
180     * is always returned.
181     *
182     * @param columnIndex the column position
183     * @return Double.class
184     */   
185    public Class getColumnClass(int columnIndex) {
186
187      return Object.class;
188    }
189  }
190
191  /**
192   * This class presents a GUI for editing the cost matrix, and saving and
193   * loading from files.
194   */
195  private class CustomEditor
196    extends JPanel
197    implements ActionListener, TableModelListener {
198   
199    /** for serialization */
200    static final long serialVersionUID = -2931593489871197274L;
201
202    /** The table model of the cost matrix being edited */
203    private CostMatrixTableModel m_tableModel;
204
205    /** The button for setting default matrix values */
206    private JButton m_defaultButton;
207
208    /** The button for opening a cost matrix from a file */
209    private JButton m_openButton;
210
211    /** The button for saving a cost matrix to a file */
212    private JButton m_saveButton;
213
214    /** The field for changing the size of the cost matrix */
215    private JTextField m_classesField;
216
217    /** The button for resizing a matrix */
218    private JButton m_resizeButton;
219
220    /**
221     * Constructs a new CustomEditor.
222     *
223     */
224    public CustomEditor() {
225
226      // set up the file chooser
227      m_fileChooser.setFileFilter(
228             new ExtensionFileFilter(CostMatrix.FILE_EXTENSION, 
229                                     "Cost files")
230               );
231      m_fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
232
233      // create the buttons + field
234      m_defaultButton = new JButton("Defaults");
235      m_openButton = new JButton("Open...");
236      m_saveButton = new JButton("Save...");
237      m_resizeButton = new JButton("Resize");
238      m_classesField = new JTextField("" + m_matrix.size());
239
240      m_defaultButton.addActionListener(this);
241      m_openButton.addActionListener(this);
242      m_saveButton.addActionListener(this);
243      m_resizeButton.addActionListener(this);
244      m_classesField.addActionListener(this);
245
246      // lay out the GUI
247      JPanel classesPanel = new JPanel();
248      classesPanel.setLayout(new GridLayout(1, 2, 0, 0));
249      classesPanel.add(new JLabel("Classes:", SwingConstants.RIGHT));
250      classesPanel.add(m_classesField);
251
252      JPanel rightPanel = new JPanel();
253     
254      GridBagLayout gridBag = new GridBagLayout();
255      GridBagConstraints gbc = new GridBagConstraints();
256      rightPanel.setLayout(gridBag);
257      gbc.gridx = 0; gbc.gridy = GridBagConstraints.RELATIVE;
258      gbc.insets = new Insets(2, 10, 2, 10);
259      gbc.fill = GridBagConstraints.HORIZONTAL;
260      gridBag.setConstraints(m_defaultButton, gbc);
261      rightPanel.add(m_defaultButton);
262
263      gridBag.setConstraints(m_openButton, gbc);
264      rightPanel.add(m_openButton);
265     
266      gridBag.setConstraints(m_saveButton, gbc);
267      rightPanel.add(m_saveButton);
268
269      gridBag.setConstraints(classesPanel, gbc);
270      rightPanel.add(classesPanel);
271     
272      gridBag.setConstraints(m_resizeButton, gbc);
273      rightPanel.add(m_resizeButton);
274
275      JPanel fill = new JPanel();
276      gbc.weightx = 1.0; gbc.weighty = 1.0;
277      gbc.fill = GridBagConstraints.BOTH;
278     
279      gridBag.setConstraints(fill, gbc);
280      rightPanel.add(fill);
281
282      m_tableModel = new CostMatrixTableModel();
283      m_tableModel.addTableModelListener(this);
284      JTable matrixTable = new JTable(m_tableModel);
285     
286      setLayout(new BorderLayout());
287      add(matrixTable, BorderLayout.CENTER);
288      add(rightPanel, BorderLayout.EAST);
289    }
290
291    /**
292     * Responds to the user perfoming an action.
293     *
294     * @param e the action event that occured
295     */
296    public void actionPerformed(ActionEvent e) {
297     
298      if (e.getSource() == m_defaultButton) {
299        m_matrix.initialize();
300        matrixChanged();
301      } else if (e.getSource() == m_openButton) {
302        openMatrix();
303      } else if (e.getSource() == m_saveButton) {
304        saveMatrix();
305      } else if (    (e.getSource() == m_classesField) 
306                  || (e.getSource() == m_resizeButton) ) {
307        try {
308          int newNumClasses = Integer.parseInt(m_classesField.getText());
309          if (newNumClasses > 0 && newNumClasses != m_matrix.size()) {
310            setValue(new CostMatrix(newNumClasses));
311          }
312        } catch (Exception ex) {}
313      }
314    }
315
316    /**
317     * Responds to a change in the cost matrix table.
318     *
319     * @param e the tabel model event that occured
320     */
321    public void tableChanged(TableModelEvent e) {
322
323      m_propSupport.firePropertyChange(null, null, null);
324    }
325
326    /**
327     * Responds to a change in structure of the matrix being edited.
328     *
329     */
330    public void matrixChanged() {
331
332      m_tableModel.fireTableStructureChanged();
333      m_classesField.setText("" + m_matrix.size());
334    }
335
336    /**
337     * Prompts the user to open a matrix, and attemps to load it.
338     *
339     */
340    private void openMatrix() {
341
342      int returnVal = m_fileChooser.showOpenDialog(this);
343      if(returnVal == JFileChooser.APPROVE_OPTION) {
344        File selectedFile = m_fileChooser.getSelectedFile();
345        Reader reader = null;
346        try {
347          reader = new BufferedReader(new FileReader(selectedFile));
348          m_matrix = 
349            new CostMatrix(reader);
350          reader.close();
351          matrixChanged();
352        } catch (Exception ex) {
353          JOptionPane.showMessageDialog(this, 
354                                        "Error reading file '"
355                                        + selectedFile.getName()
356                                        + "':\n" + ex.getMessage(),
357                                        "Load failed",
358                                        JOptionPane.ERROR_MESSAGE);
359          System.out.println(ex.getMessage());
360        }
361      }
362    }
363
364    /**
365     * Prompts the user to save a matrix, and attemps to save it.
366     *
367     */
368    private void saveMatrix() {
369     
370      int returnVal = m_fileChooser.showSaveDialog(this);
371      if(returnVal == JFileChooser.APPROVE_OPTION) {
372        File selectedFile = m_fileChooser.getSelectedFile();
373
374        // append extension if not already present
375        if (!selectedFile.getName().toLowerCase()
376            .endsWith(CostMatrix.FILE_EXTENSION)) {
377          selectedFile = new File(selectedFile.getParent(), 
378                                  selectedFile.getName() 
379                                  + CostMatrix.FILE_EXTENSION);
380        }
381
382        Writer writer = null;
383        try {
384          writer = new BufferedWriter(new FileWriter(selectedFile));
385          m_matrix.write(writer);
386          writer.close();
387        } catch (Exception ex) {
388          JOptionPane.showMessageDialog(this, 
389                                        "Error writing file '"
390                                        + selectedFile.getName()
391                                        + "':\n" + ex.getMessage(),
392                                        "Save failed",
393                                        JOptionPane.ERROR_MESSAGE);
394          System.out.println(ex.getMessage());
395        }
396      }
397    }
398  }
399
400  /**
401   * Constructs a new CostMatrixEditor.
402   *
403   */
404  public CostMatrixEditor() {
405
406    m_matrix = new CostMatrix(2);
407    m_propSupport = new PropertyChangeSupport(this);
408    m_customEditor = new CustomEditor();
409  }
410
411  /**
412   * Sets the value of the CostMatrix to be edited.
413   *
414   * @param value a CostMatrix object to be edited
415   */
416  public void setValue(Object value) {
417   
418    m_matrix = (CostMatrix) value;
419    m_customEditor.matrixChanged();
420  }
421
422  /**
423   * Gets the cost matrix that is being edited.
424   *
425   * @return the edited CostMatrix object
426   */ 
427  public Object getValue() {
428
429    return m_matrix;
430  }
431
432  /**
433   * Indicates whether the object can be represented graphically. In this case
434   * it can.
435   *
436   * @return true
437   */ 
438  public boolean isPaintable() {
439
440    return true;
441  }
442
443  /**
444   * Paints a graphical representation of the object. For the cost matrix it
445   * prints out the text "X x X matrix", where X is the size of the matrix.
446   *
447   * @param gfx the graphics context to draw the representation to
448   * @param box the bounds within which the representation should fit.
449   */   
450  public void paintValue(Graphics gfx,
451                         Rectangle box) {
452
453    gfx.drawString(m_matrix.size() + " x " + m_matrix.size() + " cost matrix",
454                   box.x, box.y + box.height);
455  }
456
457  /**
458   * Returns the Java code that generates an object the same as the one being edited.
459   * Unfortunately this can't be done in a single line of code, so the code returned
460   * will only build a default cost matrix of the same size.
461   *
462   * @return the initialization string
463   */   
464  public String getJavaInitializationString() {
465
466    return ("new CostMatrix(" + m_matrix.size() + ")");
467  }
468
469  /**
470   * Some objects can be represented as text, but a cost matrix cannot.
471   *
472   * @return null
473   */   
474  public String getAsText() {
475
476    return null;
477  }
478
479  /**
480   * Some objects can be represented as text, but a cost matrix cannot.
481   *
482   * @param text ignored
483   * @throws IllegalArgumentException always throws an IllegalArgumentException
484   */   
485  public void setAsText(String text) {
486    throw new IllegalArgumentException("CostMatrixEditor: "
487                                       + "CostMatrix properties cannot be "
488                                       + "expressed as text");
489  }
490
491  /**
492   * Some objects can return tags, but a cost matrix cannot.
493   *
494   * @return null
495   */ 
496  public String[] getTags() {
497
498    return null;
499  }
500
501  /**
502   * Gets a GUI component with which the user can edit the cost matrix.
503   *
504   * @return an editor GUI component
505   */   
506  public Component getCustomEditor() {
507
508    return m_customEditor;
509  }
510
511  /**
512   * Indicates whether the cost matrix can be edited in a GUI, which it can.
513   *
514   * @return true
515   */     
516  public boolean supportsCustomEditor() {
517
518    return true;
519  }
520
521  /**
522   * Adds an object to the list of those that wish to be informed when the
523   * cost matrix changes.
524   *
525   * @param listener a new listener to add to the list
526   */   
527  public void addPropertyChangeListener(PropertyChangeListener listener) {
528
529    m_propSupport.addPropertyChangeListener(listener);
530  }
531
532  /**
533   * Removes an object from the list of those that wish to be informed when the
534   * cost matrix changes.
535   *
536   * @param listener the listener to remove from the list
537   */ 
538  public void removePropertyChangeListener(PropertyChangeListener listener) {
539
540    m_propSupport.removePropertyChangeListener(listener);
541  }
542}
Note: See TracBrowser for help on using the repository browser.