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

weka.gui.arffviewer.ArffTable Maven / Gradle / Ivy

Go to download

The Waikato Environment for Knowledge Analysis (WEKA), a machine learning workbench. This version represents the developer version, the "bleeding edge" of development, you could say. New functionality gets added to this version.

There is a newer version: 3.9.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 .
 */

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

package weka.gui.arffviewer;

import java.awt.Component;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;

import javax.swing.AbstractCellEditor;
import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.TableModelEvent;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableModel;

import weka.core.Attribute;
import weka.core.Instances;
import weka.core.SerializedObject;
import weka.gui.ComponentHelper;
import weka.gui.JTableHelper;
import weka.gui.ViewerDialog;

/**
 * A specialized JTable for the Arff-Viewer.
 * 
 * 
 * @author FracPete (fracpete at waikato dot ac dot nz)
 * @version $Revision: 14583 $
 */
public class ArffTable extends JTable {

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

  /**
   * a special Editor for editing the relation attribute.
   */
  protected class RelationalCellEditor extends AbstractCellEditor implements
    TableCellEditor {

    /** for serialization */
    private static final long serialVersionUID = 657969163293205963L;

    /** the button for opening the dialog */
    protected JButton m_Button;

    /** the current instances */
    protected Instances m_CurrentInst;

    /** the row index this editor is for */
    protected int m_RowIndex;

    /** the column index this editor is for */
    protected int m_ColumnIndex;

    /**
     * initializes the editor
     * 
     * @param rowIndex the row index
     * @param columnIndex the column index
     */
    public RelationalCellEditor(int rowIndex, int columnIndex) {
      super();

      m_CurrentInst = getInstancesAt(rowIndex, columnIndex);
      m_RowIndex = rowIndex;
      m_ColumnIndex = columnIndex;

      m_Button = new JButton("...");
      m_Button.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent evt) {
          ViewerDialog dialog;
          int result;

          dialog = new ViewerDialog(null);
          dialog.setTitle("Relational attribute Viewer - "
            + ((ArffSortedTableModel) getModel()).getInstances()
              .attribute(((ArffSortedTableModel)getModel()).getAttributeIndex(columnIndex)).name());
          result = dialog.showDialog(m_CurrentInst);
          if (result == ViewerDialog.APPROVE_OPTION) {
            m_CurrentInst = dialog.getInstances();
            fireEditingStopped();
          } else {
            fireEditingCanceled();
          }
        }
      });
    }

    /**
     * returns the underlying instances at the given position
     * 
     * @param rowIndex the row index
     * @param columnIndex the column index
     * @return the corresponding instances
     */
    protected Instances getInstancesAt(int rowIndex, int columnIndex) {
      Instances result;
      ArffSortedTableModel model;
      double value;

      model = (ArffSortedTableModel) getModel();
      value = model.getInstancesValueAt(rowIndex, columnIndex);
      result = model.getInstances().attribute(model.getAttributeIndex(columnIndex))
        .relation((int) value);

      return result;
    }

    /**
     * Sets an initial value for the editor. This will cause the editor to
     * stopEditing and lose any partially edited value if the editor is editing
     * when this method is called.
     * 
     * @param table the table this editor belongs to
     * @param value the value to edit
     * @param isSelected whether the cell is selected
     * @param row the row index
     * @param column the column index
     * @return the
     */
    @Override
    public Component getTableCellEditorComponent(JTable table, Object value,
      boolean isSelected, int row, int column) {
      return m_Button;
    }

    /**
     * Returns the value contained in the editor.
     * 
     * @return the value contained in the editor
     */
    @Override
    public Object getCellEditorValue() {
      return m_CurrentInst;
    }
  }

  /** the search string */
  private String m_SearchString;
  /** the listeners for changes */
  private HashSet m_ChangeListeners;

  /**
   * initializes with no model
   */
  public ArffTable() {
    this(new ArffSortedTableModel(""));
  }

  /**
   * initializes with the given model
   * 
   * @param model the model to use
   */
  public ArffTable(TableModel model) {
    super(model);

    setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
  }

  /**
   * sets the new model
   * 
   * @param model the model to use
   */
  @Override
  public void setModel(TableModel model) {
    ArffSortedTableModel arffModel;

    // initialize the search
    m_SearchString = null;

    // init the listeners
    if (m_ChangeListeners == null) {
      m_ChangeListeners = new HashSet();
    }

    super.setModel(model);

    if (model == null) {
      return;
    }

    if (!(model instanceof ArffSortedTableModel)) {
      return;
    }

    arffModel = (ArffSortedTableModel) model;
    arffModel.addMouseListenerToHeader(this);
    arffModel.addTableModelListener(this);
    arffModel.sort(0);
    setLayout();
    setSelectedColumn(0);

    // disable column moving
    if (getTableHeader() != null) {
      getTableHeader().setReorderingAllowed(false);
    }
  }

  /**
   * returns the cell editor for the given cell
   * 
   * @param row the row index
   * @param column the column index
   * @return the cell editor
   */
  @Override
  public TableCellEditor getCellEditor(int row, int column) {
    TableCellEditor result;

    // relational attribute?
    if ((getModel() instanceof ArffSortedTableModel)
      && (((ArffSortedTableModel) getModel()).getType(column) == Attribute.RELATIONAL)) {
      result = new RelationalCellEditor(row, column);
      // default
    } else {
      result = super.getCellEditor(row, column);
    }

    return result;
  }

  /**
   * returns whether the model is read-only
   * 
   * @return true if model is read-only
   */
  public boolean isReadOnly() {
    return ((ArffSortedTableModel) getModel()).isReadOnly();
  }

  /**
   * sets whether the model is read-only
   * 
   * @param value if true the model is set to read-only
   */
  public void setReadOnly(boolean value) {
    ((ArffSortedTableModel) getModel()).setReadOnly(value);
  }

  /**
   * sets the cell renderer and calcs the optimal column width
   */
  private void setLayout() {
    ArffSortedTableModel arffModel;
    int i;
    JComboBox combo;
    Enumeration enm;

    arffModel = (ArffSortedTableModel) getModel();

    for (i = 0; i < getColumnCount(); i++) {
      // optimal colwidths (only according to header!)
      JTableHelper.setOptimalHeaderWidth(this, i);

      // CellRenderer
      getColumnModel().getColumn(i)
        .setCellRenderer(new ArffTableCellRenderer());

      // CellEditor
      if (i > 0) {
        if (arffModel.getType(i) == Attribute.NOMINAL) {
          combo = new JComboBox();
          combo.addItem(null);
          enm = arffModel.getInstances().attribute(arffModel.getAttributeIndex(i)).enumerateValues();
          while (enm.hasMoreElements()) {
            Object o = enm.nextElement();
            if (o instanceof SerializedObject) {
              ((SerializedObject) o).getObject();
            }
            combo.addItem(o);
          }
          getColumnModel().getColumn(i).setCellEditor(
            new DefaultCellEditor(combo));
        } else {
          getColumnModel().getColumn(i).setCellEditor(null);
        }
      }
    }
  }

  /**
   * returns the basically the attribute name of the column and not the HTML
   * column name via getColumnName(int)
   * 
   * @param columnIndex the column index
   * @return the plain name
   */
  public String getPlainColumnName(int columnIndex) {
    ArffSortedTableModel arffModel;
    String result;

    result = "";

    if (getModel() == null) {
      return result;
    }
    if (!(getModel() instanceof ArffSortedTableModel)) {
      return result;
    }

    arffModel = (ArffSortedTableModel) getModel();

    if ((columnIndex >= 0) && (columnIndex < getColumnCount())) {
      if (columnIndex == 0) {
        result = "No.";
      } else {
        if (arffModel.getAttributeIndex(columnIndex) < 0) {
          result = "Weight";
        } else {
          result = arffModel.getAttributeAt(columnIndex).name();
        }
      }
    }

    return result;
  }

  /**
   * returns the selected content in a StringSelection that can be copied to the
   * clipboard and used in Excel, if nothing is selected the whole table is
   * copied to the clipboard
   * 
   * @return the current selection
   */
  public StringSelection getStringSelection() {
    StringSelection result;
    int[] indices;
    int i;
    int n;
    StringBuffer tmp;

    result = null;

    // nothing selected? -> all
    if (getSelectedRow() == -1) {
      // really?
      if (ComponentHelper.showMessageBox(getParent(), "Question...",
        "Do you really want to copy the whole table?",
        JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) != JOptionPane.YES_OPTION) {
        return result;
      }

      indices = new int[getRowCount()];
      for (i = 0; i < indices.length; i++) {
        indices[i] = i;
      }
    } else {
      indices = getSelectedRows();
    }

    // get header
    tmp = new StringBuffer();
    for (i = 0; i < getColumnCount(); i++) {
      if (i > 0) {
        tmp.append("\t");
      }
      tmp.append(getPlainColumnName(i));
    }
    tmp.append("\n");

    // get content
    for (i = 0; i < indices.length; i++) {
      for (n = 0; n < getColumnCount(); n++) {
        if (n > 0) {
          tmp.append("\t");
        }
        tmp.append(getValueAt(indices[i], n).toString());
      }
      tmp.append("\n");
    }

    result = new StringSelection(tmp.toString());

    return result;
  }

  /**
   * sets the search string to look for in the table, NULL or "" disables the
   * search
   * 
   * @param searchString the search string to use
   */
  public void setSearchString(String searchString) {
    this.m_SearchString = searchString;
    repaint();
  }

  /**
   * returns the search string, can be NULL if no search string is set
   * 
   * @return the current search string
   */
  public String getSearchString() {
    return m_SearchString;
  }

  /**
   * sets the selected column
   * 
   * @param index the column to select
   */
  public void setSelectedColumn(int index) {
    getColumnModel().getSelectionModel().clearSelection();
    getColumnModel().getSelectionModel().setSelectionInterval(index, index);
    resizeAndRepaint();
    if (getTableHeader() != null) {
      getTableHeader().resizeAndRepaint();
    }
  }

  /**
   * This fine grain notification tells listeners the exact range of cells,
   * rows, or columns that changed.
   * 
   * @param e the table event
   */
  @Override
  public void tableChanged(TableModelEvent e) {
    super.tableChanged(e);

    setLayout();
    notifyListener();
  }

  /**
   * notfies all listener of the change
   */
  private void notifyListener() {
    Iterator iter;

    iter = m_ChangeListeners.iterator();
    while (iter.hasNext()) {
      iter.next().stateChanged(new ChangeEvent(this));
    }
  }

  /**
   * Adds a ChangeListener to the panel
   * 
   * @param l the listener to add
   */
  public void addChangeListener(ChangeListener l) {
    m_ChangeListeners.add(l);
  }

  /**
   * Removes a ChangeListener from the panel
   * 
   * @param l the listener to remove
   */
  public void removeChangeListener(ChangeListener l) {
    m_ChangeListeners.remove(l);
  }
}