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
* CostMatrixEditor.java
19
* Copyright (C) 2002 University of Waikato, Hamilton, New Zealand
25
import weka.classifiers.CostMatrix;
27
import java.awt.BorderLayout;
28
import java.awt.Component;
29
import java.awt.Graphics;
30
import java.awt.GridBagConstraints;
31
import java.awt.GridBagLayout;
32
import java.awt.GridLayout;
33
import java.awt.Insets;
34
import java.awt.Rectangle;
35
import java.awt.event.ActionEvent;
36
import java.awt.event.ActionListener;
37
import java.beans.PropertyChangeListener;
38
import java.beans.PropertyChangeSupport;
39
import java.beans.PropertyEditor;
40
import java.io.BufferedReader;
41
import java.io.BufferedWriter;
43
import java.io.FileReader;
44
import java.io.FileWriter;
45
import java.io.Reader;
46
import java.io.Writer;
48
import javax.swing.JButton;
49
import javax.swing.JFileChooser;
50
import javax.swing.JLabel;
51
import javax.swing.JOptionPane;
52
import javax.swing.JPanel;
53
import javax.swing.JTable;
54
import javax.swing.JTextField;
55
import javax.swing.SwingConstants;
56
import javax.swing.event.TableModelEvent;
57
import javax.swing.event.TableModelListener;
58
import javax.swing.table.AbstractTableModel;
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.
65
* @author Richard Kirkby (rkirkby@cs.waikato.ac.nz)
66
* @version $Revision: 1.11 $
68
public class CostMatrixEditor
69
implements PropertyEditor {
71
/** The cost matrix being edited */
72
private CostMatrix m_matrix;
74
/** A helper class for notifying listeners */
75
private PropertyChangeSupport m_propSupport;
77
/** An instance of the custom editor */
78
private CustomEditor m_customEditor;
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")));
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.
88
private class CostMatrixTableModel
89
extends AbstractTableModel {
91
/** for serialization */
92
static final long serialVersionUID = -2762326138357037181L;
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.
98
* @return the row count
100
public int getRowCount() {
102
return m_matrix.size();
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.
109
* @return the row count
111
public int getColumnCount() {
113
return m_matrix.size();
117
* Returns a value at the specified position in the cost matrix.
119
* @param row the row position
120
* @param column the column position
123
public Object getValueAt(int row, int column) {
125
// return new Double(m_matrix.getElement(row, column));
127
return m_matrix.getCell(row, column);
128
} catch (Exception ex) {
129
ex.printStackTrace();
131
return new Double(0.0);
135
* Sets a value at a specified position in the cost matrix.
137
* @param aValue the new value (should be of type Double).
138
* @param rowIndex the row position
139
* @param columnIndex the column position
141
public void setValueAt(Object aValue,
145
// double value = ((Double) aValue).doubleValue();
146
// m_matrix.setElement(rowIndex, columnIndex, value);
147
// try to parse it as a double first
150
val = new Double(((String)aValue));
151
double value = val.doubleValue();
152
} catch (Exception ex) {
156
m_matrix.setCell(rowIndex, columnIndex, aValue);
158
m_matrix.setCell(rowIndex, columnIndex, val);
160
fireTableCellUpdated(rowIndex, columnIndex);
164
* Indicates whether a cell in the table is editable. In this case all cells
165
* are editable so true is always returned.
167
* @param rowIndex the row position
168
* @param columnIndex the column position
171
public boolean isCellEditable(int rowIndex,
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.
182
* @param columnIndex the column position
183
* @return Double.class
185
public Class getColumnClass(int columnIndex) {
192
* This class presents a GUI for editing the cost matrix, and saving and
193
* loading from files.
195
private class CustomEditor
197
implements ActionListener, TableModelListener {
199
/** for serialization */
200
static final long serialVersionUID = -2931593489871197274L;
202
/** The table model of the cost matrix being edited */
203
private CostMatrixTableModel m_tableModel;
205
/** The button for setting default matrix values */
206
private JButton m_defaultButton;
208
/** The button for opening a cost matrix from a file */
209
private JButton m_openButton;
211
/** The button for saving a cost matrix to a file */
212
private JButton m_saveButton;
214
/** The field for changing the size of the cost matrix */
215
private JTextField m_classesField;
217
/** The button for resizing a matrix */
218
private JButton m_resizeButton;
221
* Constructs a new CustomEditor.
224
public CustomEditor() {
226
// set up the file chooser
227
m_fileChooser.setFileFilter(
228
new ExtensionFileFilter(CostMatrix.FILE_EXTENSION,
231
m_fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
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());
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);
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);
252
JPanel rightPanel = new JPanel();
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);
263
gridBag.setConstraints(m_openButton, gbc);
264
rightPanel.add(m_openButton);
266
gridBag.setConstraints(m_saveButton, gbc);
267
rightPanel.add(m_saveButton);
269
gridBag.setConstraints(classesPanel, gbc);
270
rightPanel.add(classesPanel);
272
gridBag.setConstraints(m_resizeButton, gbc);
273
rightPanel.add(m_resizeButton);
275
JPanel fill = new JPanel();
276
gbc.weightx = 1.0; gbc.weighty = 1.0;
277
gbc.fill = GridBagConstraints.BOTH;
279
gridBag.setConstraints(fill, gbc);
280
rightPanel.add(fill);
282
m_tableModel = new CostMatrixTableModel();
283
m_tableModel.addTableModelListener(this);
284
JTable matrixTable = new JTable(m_tableModel);
286
setLayout(new BorderLayout());
287
add(matrixTable, BorderLayout.CENTER);
288
add(rightPanel, BorderLayout.EAST);
292
* Responds to the user perfoming an action.
294
* @param e the action event that occured
296
public void actionPerformed(ActionEvent e) {
298
if (e.getSource() == m_defaultButton) {
299
m_matrix.initialize();
301
} else if (e.getSource() == m_openButton) {
303
} else if (e.getSource() == m_saveButton) {
305
} else if ( (e.getSource() == m_classesField)
306
|| (e.getSource() == m_resizeButton) ) {
308
int newNumClasses = Integer.parseInt(m_classesField.getText());
309
if (newNumClasses > 0 && newNumClasses != m_matrix.size()) {
310
setValue(new CostMatrix(newNumClasses));
312
} catch (Exception ex) {}
317
* Responds to a change in the cost matrix table.
319
* @param e the tabel model event that occured
321
public void tableChanged(TableModelEvent e) {
323
m_propSupport.firePropertyChange(null, null, null);
327
* Responds to a change in structure of the matrix being edited.
330
public void matrixChanged() {
332
m_tableModel.fireTableStructureChanged();
333
m_classesField.setText("" + m_matrix.size());
337
* Prompts the user to open a matrix, and attemps to load it.
340
private void openMatrix() {
342
int returnVal = m_fileChooser.showOpenDialog(this);
343
if(returnVal == JFileChooser.APPROVE_OPTION) {
344
File selectedFile = m_fileChooser.getSelectedFile();
345
Reader reader = null;
347
reader = new BufferedReader(new FileReader(selectedFile));
349
new CostMatrix(reader);
352
} catch (Exception ex) {
353
JOptionPane.showMessageDialog(this,
354
"Error reading file '"
355
+ selectedFile.getName()
356
+ "':\n" + ex.getMessage(),
358
JOptionPane.ERROR_MESSAGE);
359
System.out.println(ex.getMessage());
365
* Prompts the user to save a matrix, and attemps to save it.
368
private void saveMatrix() {
370
int returnVal = m_fileChooser.showSaveDialog(this);
371
if(returnVal == JFileChooser.APPROVE_OPTION) {
372
File selectedFile = m_fileChooser.getSelectedFile();
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);
382
Writer writer = null;
384
writer = new BufferedWriter(new FileWriter(selectedFile));
385
m_matrix.write(writer);
387
} catch (Exception ex) {
388
JOptionPane.showMessageDialog(this,
389
"Error writing file '"
390
+ selectedFile.getName()
391
+ "':\n" + ex.getMessage(),
393
JOptionPane.ERROR_MESSAGE);
394
System.out.println(ex.getMessage());
401
* Constructs a new CostMatrixEditor.
404
public CostMatrixEditor() {
406
m_matrix = new CostMatrix(2);
407
m_propSupport = new PropertyChangeSupport(this);
408
m_customEditor = new CustomEditor();
412
* Sets the value of the CostMatrix to be edited.
414
* @param value a CostMatrix object to be edited
416
public void setValue(Object value) {
418
m_matrix = (CostMatrix) value;
419
m_customEditor.matrixChanged();
423
* Gets the cost matrix that is being edited.
425
* @return the edited CostMatrix object
427
public Object getValue() {
433
* Indicates whether the object can be represented graphically. In this case
438
public boolean isPaintable() {
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.
447
* @param gfx the graphics context to draw the representation to
448
* @param box the bounds within which the representation should fit.
450
public void paintValue(Graphics gfx,
453
gfx.drawString(m_matrix.size() + " x " + m_matrix.size() + " cost matrix",
454
box.x, box.y + box.height);
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.
462
* @return the initialization string
464
public String getJavaInitializationString() {
466
return ("new CostMatrix(" + m_matrix.size() + ")");
470
* Some objects can be represented as text, but a cost matrix cannot.
474
public String getAsText() {
480
* Some objects can be represented as text, but a cost matrix cannot.
482
* @param text ignored
483
* @throws IllegalArgumentException always throws an IllegalArgumentException
485
public void setAsText(String text) {
486
throw new IllegalArgumentException("CostMatrixEditor: "
487
+ "CostMatrix properties cannot be "
488
+ "expressed as text");
492
* Some objects can return tags, but a cost matrix cannot.
496
public String[] getTags() {
502
* Gets a GUI component with which the user can edit the cost matrix.
504
* @return an editor GUI component
506
public Component getCustomEditor() {
508
return m_customEditor;
512
* Indicates whether the cost matrix can be edited in a GUI, which it can.
516
public boolean supportsCustomEditor() {
522
* Adds an object to the list of those that wish to be informed when the
523
* cost matrix changes.
525
* @param listener a new listener to add to the list
527
public void addPropertyChangeListener(PropertyChangeListener listener) {
529
m_propSupport.addPropertyChangeListener(listener);
533
* Removes an object from the list of those that wish to be informed when the
534
* cost matrix changes.
536
* @param listener the listener to remove from the list
538
public void removePropertyChangeListener(PropertyChangeListener listener) {
540
m_propSupport.removePropertyChangeListener(listener);