All Downloads are FREE. Search and download functionalities are using the official Maven repository.

weka.gui.CostMatrixEditor Maven / Gradle / Ivy

Go to download

The Waikato Environment for Knowledge Analysis (WEKA), a machine learning workbench. This is the stable version. Apart from bugfixes, this version does not receive any other updates.

There is a newer version: 3.8.6
Show newest version
/*
 *   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 3 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, see .
 */

/*
 *    CostMatrixEditor.java
 *    Copyright (C) 2002-2012 University of Waikato, Hamilton, New Zealand
 *
 */

package weka.gui;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyEditor;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.Reader;
import java.io.Writer;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;

import weka.classifiers.CostMatrix;

/**
 * Class for editing CostMatrix objects. Brings up a custom editing panel with
 * which the user can edit the matrix interactively, as well as save load cost
 * matrices from files.
 * 
 * @author Richard Kirkby ([email protected])
 * @version $Revision: 10216 $
 */
public class CostMatrixEditor implements PropertyEditor {

  /** The cost matrix being edited */
  private CostMatrix m_matrix;

  /** A helper class for notifying listeners */
  private final PropertyChangeSupport m_propSupport;

  /** An instance of the custom editor */
  private final CustomEditor m_customEditor;

  /** The file chooser for the user to select cost files to save and load */
  private final JFileChooser m_fileChooser = new JFileChooser(new File(
    System.getProperty("user.dir")));

  /**
   * This class wraps around the cost matrix presenting it as a TableModel so
   * that it can be displayed and edited in a JTable.
   */
  private class CostMatrixTableModel extends AbstractTableModel {

    /** for serialization */
    static final long serialVersionUID = -2762326138357037181L;

    /**
     * Gets the number of rows in the matrix. Cost matrices are square so it is
     * the same as the column count, i.e. the size of the matrix.
     * 
     * @return the row count
     */
    @Override
    public int getRowCount() {

      return m_matrix.size();
    }

    /**
     * Gets the number of columns in the matrix. Cost matrices are square so it
     * is the same as the row count, i.e. the size of the matrix.
     * 
     * @return the row count
     */
    @Override
    public int getColumnCount() {

      return m_matrix.size();
    }

    /**
     * Returns a value at the specified position in the cost matrix.
     * 
     * @param row the row position
     * @param column the column position
     * @return the value
     */
    @Override
    public Object getValueAt(int row, int column) {

      // return new Double(m_matrix.getElement(row, column));
      try {
        return m_matrix.getCell(row, column);
      } catch (Exception ex) {
        ex.printStackTrace();
      }
      return new Double(0.0);
    }

    /**
     * Sets a value at a specified position in the cost matrix.
     * 
     * @param aValue the new value (should be of type Double).
     * @param rowIndex the row position
     * @param columnIndex the column position
     */
    @Override
    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {

      // double value = ((Double) aValue).doubleValue();
      // m_matrix.setElement(rowIndex, columnIndex, value);
      // try to parse it as a double first
      Double val;
      try {
        val = new Double(((String) aValue));
      } catch (Exception ex) {
        val = null;
      }
      if (val == null) {
        m_matrix.setCell(rowIndex, columnIndex, aValue);
      } else {
        m_matrix.setCell(rowIndex, columnIndex, val);
      }
      fireTableCellUpdated(rowIndex, columnIndex);
    }

    /**
     * Indicates whether a cell in the table is editable. In this case all cells
     * are editable so true is always returned.
     * 
     * @param rowIndex the row position
     * @param columnIndex the column position
     * @return true
     */
    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex) {

      return true;
    }

    /**
     * Indicates the class of the objects within a column of the table. In this
     * case all columns in the cost matrix consist of double values so
     * Double.class is always returned.
     * 
     * @param columnIndex the column position
     * @return Double.class
     */
    @Override
    public Class getColumnClass(int columnIndex) {

      return Object.class;
    }
  }

  /**
   * This class presents a GUI for editing the cost matrix, and saving and
   * loading from files.
   */
  private class CustomEditor extends JPanel implements ActionListener,
    TableModelListener {

    /** for serialization */
    static final long serialVersionUID = -2931593489871197274L;

    /** The table model of the cost matrix being edited */
    private final CostMatrixTableModel m_tableModel;

    /** The button for setting default matrix values */
    private final JButton m_defaultButton;

    /** The button for opening a cost matrix from a file */
    private final JButton m_openButton;

    /** The button for saving a cost matrix to a file */
    private final JButton m_saveButton;

    /** The field for changing the size of the cost matrix */
    private final JTextField m_classesField;

    /** The button for resizing a matrix */
    private final JButton m_resizeButton;

    /**
     * Constructs a new CustomEditor.
     * 
     */
    public CustomEditor() {

      // set up the file chooser
      m_fileChooser.setFileFilter(new ExtensionFileFilter(
        CostMatrix.FILE_EXTENSION, "Cost files"));
      m_fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);

      // create the buttons + field
      m_defaultButton = new JButton("Defaults");
      m_openButton = new JButton("Open...");
      m_saveButton = new JButton("Save...");
      m_resizeButton = new JButton("Resize");
      m_classesField = new JTextField("" + m_matrix.size());

      m_defaultButton.addActionListener(this);
      m_openButton.addActionListener(this);
      m_saveButton.addActionListener(this);
      m_resizeButton.addActionListener(this);
      m_classesField.addActionListener(this);

      // lay out the GUI
      JPanel classesPanel = new JPanel();
      classesPanel.setLayout(new GridLayout(1, 2, 0, 0));
      classesPanel.add(new JLabel("Classes:", SwingConstants.RIGHT));
      classesPanel.add(m_classesField);

      JPanel rightPanel = new JPanel();

      GridBagLayout gridBag = new GridBagLayout();
      GridBagConstraints gbc = new GridBagConstraints();
      rightPanel.setLayout(gridBag);
      gbc.gridx = 0;
      gbc.gridy = GridBagConstraints.RELATIVE;
      gbc.insets = new Insets(2, 10, 2, 10);
      gbc.fill = GridBagConstraints.HORIZONTAL;
      gridBag.setConstraints(m_defaultButton, gbc);
      rightPanel.add(m_defaultButton);

      gridBag.setConstraints(m_openButton, gbc);
      rightPanel.add(m_openButton);

      gridBag.setConstraints(m_saveButton, gbc);
      rightPanel.add(m_saveButton);

      gridBag.setConstraints(classesPanel, gbc);
      rightPanel.add(classesPanel);

      gridBag.setConstraints(m_resizeButton, gbc);
      rightPanel.add(m_resizeButton);

      JPanel fill = new JPanel();
      gbc.weightx = 1.0;
      gbc.weighty = 1.0;
      gbc.fill = GridBagConstraints.BOTH;

      gridBag.setConstraints(fill, gbc);
      rightPanel.add(fill);

      m_tableModel = new CostMatrixTableModel();
      m_tableModel.addTableModelListener(this);
      JTable matrixTable = new JTable(m_tableModel);

      setLayout(new BorderLayout());
      add(matrixTable, BorderLayout.CENTER);
      add(rightPanel, BorderLayout.EAST);
    }

    /**
     * Responds to the user perfoming an action.
     * 
     * @param e the action event that occured
     */
    @Override
    public void actionPerformed(ActionEvent e) {

      if (e.getSource() == m_defaultButton) {
        m_matrix.initialize();
        matrixChanged();
      } else if (e.getSource() == m_openButton) {
        openMatrix();
      } else if (e.getSource() == m_saveButton) {
        saveMatrix();
      } else if ((e.getSource() == m_classesField)
        || (e.getSource() == m_resizeButton)) {
        try {
          int newNumClasses = Integer.parseInt(m_classesField.getText());
          if (newNumClasses > 0 && newNumClasses != m_matrix.size()) {
            setValue(new CostMatrix(newNumClasses));
          }
        } catch (Exception ex) {
        }
      }
    }

    /**
     * Responds to a change in the cost matrix table.
     * 
     * @param e the tabel model event that occured
     */
    @Override
    public void tableChanged(TableModelEvent e) {

      m_propSupport.firePropertyChange(null, null, null);
    }

    /**
     * Responds to a change in structure of the matrix being edited.
     * 
     */
    public void matrixChanged() {

      m_tableModel.fireTableStructureChanged();
      m_classesField.setText("" + m_matrix.size());
    }

    /**
     * Prompts the user to open a matrix, and attemps to load it.
     * 
     */
    private void openMatrix() {

      int returnVal = m_fileChooser.showOpenDialog(this);
      if (returnVal == JFileChooser.APPROVE_OPTION) {
        File selectedFile = m_fileChooser.getSelectedFile();
        Reader reader = null;
        try {
          reader = new BufferedReader(new FileReader(selectedFile));
          m_matrix = new CostMatrix(reader);
          reader.close();
          matrixChanged();
        } catch (Exception ex) {
          JOptionPane.showMessageDialog(this, "Error reading file '"
            + selectedFile.getName() + "':\n" + ex.getMessage(), "Load failed",
            JOptionPane.ERROR_MESSAGE);
          System.out.println(ex.getMessage());
        }
      }
    }

    /**
     * Prompts the user to save a matrix, and attemps to save it.
     * 
     */
    private void saveMatrix() {

      int returnVal = m_fileChooser.showSaveDialog(this);
      if (returnVal == JFileChooser.APPROVE_OPTION) {
        File selectedFile = m_fileChooser.getSelectedFile();

        // append extension if not already present
        if (!selectedFile.getName().toLowerCase()
          .endsWith(CostMatrix.FILE_EXTENSION)) {
          selectedFile = new File(selectedFile.getParent(),
            selectedFile.getName() + CostMatrix.FILE_EXTENSION);
        }

        Writer writer = null;
        try {
          writer = new BufferedWriter(new FileWriter(selectedFile));
          m_matrix.write(writer);
          writer.close();
        } catch (Exception ex) {
          JOptionPane.showMessageDialog(this, "Error writing file '"
            + selectedFile.getName() + "':\n" + ex.getMessage(), "Save failed",
            JOptionPane.ERROR_MESSAGE);
          System.out.println(ex.getMessage());
        }
      }
    }
  }

  /**
   * Constructs a new CostMatrixEditor.
   * 
   */
  public CostMatrixEditor() {

    m_matrix = new CostMatrix(2);
    m_propSupport = new PropertyChangeSupport(this);
    m_customEditor = new CustomEditor();
  }

  /**
   * Sets the value of the CostMatrix to be edited.
   * 
   * @param value a CostMatrix object to be edited
   */
  @Override
  public void setValue(Object value) {

    m_matrix = (CostMatrix) value;
    m_customEditor.matrixChanged();
  }

  /**
   * Gets the cost matrix that is being edited.
   * 
   * @return the edited CostMatrix object
   */
  @Override
  public Object getValue() {

    return m_matrix;
  }

  /**
   * Indicates whether the object can be represented graphically. In this case
   * it can.
   * 
   * @return true
   */
  @Override
  public boolean isPaintable() {

    return true;
  }

  /**
   * Paints a graphical representation of the object. For the cost matrix it
   * prints out the text "X x X matrix", where X is the size of the matrix.
   * 
   * @param gfx the graphics context to draw the representation to
   * @param box the bounds within which the representation should fit.
   */
  @Override
  public void paintValue(Graphics gfx, Rectangle box) {

    gfx.drawString(m_matrix.size() + " x " + m_matrix.size() + " cost matrix",
      box.x, box.y + box.height);
  }

  /**
   * Returns the Java code that generates an object the same as the one being
   * edited. Unfortunately this can't be done in a single line of code, so the
   * code returned will only build a default cost matrix of the same size.
   * 
   * @return the initialization string
   */
  @Override
  public String getJavaInitializationString() {

    return ("new CostMatrix(" + m_matrix.size() + ")");
  }

  /**
   * Some objects can be represented as text, but a cost matrix cannot.
   * 
   * @return null
   */
  @Override
  public String getAsText() {

    return null;
  }

  /**
   * Some objects can be represented as text, but a cost matrix cannot.
   * 
   * @param text ignored
   * @throws IllegalArgumentException always throws an IllegalArgumentException
   */
  @Override
  public void setAsText(String text) {
    throw new IllegalArgumentException("CostMatrixEditor: "
      + "CostMatrix properties cannot be " + "expressed as text");
  }

  /**
   * Some objects can return tags, but a cost matrix cannot.
   * 
   * @return null
   */
  @Override
  public String[] getTags() {

    return null;
  }

  /**
   * Gets a GUI component with which the user can edit the cost matrix.
   * 
   * @return an editor GUI component
   */
  @Override
  public Component getCustomEditor() {

    return m_customEditor;
  }

  /**
   * Indicates whether the cost matrix can be edited in a GUI, which it can.
   * 
   * @return true
   */
  @Override
  public boolean supportsCustomEditor() {

    return true;
  }

  /**
   * Adds an object to the list of those that wish to be informed when the cost
   * matrix changes.
   * 
   * @param listener a new listener to add to the list
   */
  @Override
  public void addPropertyChangeListener(PropertyChangeListener listener) {

    m_propSupport.addPropertyChangeListener(listener);
  }

  /**
   * Removes an object from the list of those that wish to be informed when the
   * cost matrix changes.
   * 
   * @param listener the listener to remove from the list
   */
  @Override
  public void removePropertyChangeListener(PropertyChangeListener listener) {

    m_propSupport.removePropertyChangeListener(listener);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy