weka.gui.arffviewer.ArffTable.java Source code

Java tutorial

Introduction

Here is the source code for weka.gui.arffviewer.ArffTable.java

Source

/*
 *   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 <http://www.gnu.org/licenses/>.
 */

/*
 * 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$
 */
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<ChangeListener> 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<ChangeListener>();
        }

        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<Object> 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<ChangeListener> 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);
    }
}