package org.statcato.spreadsheet;

import java.util.Vector;
import javax.swing.table.*;
import javax.swing.event.*;
import java.util.Enumeration;
import org.statcato.Statcato;

/**
 * A table model for Spreadsheet.
 * 

* The table data is represented by vectors of vectors of Cell. * The first row of the table contains the column/variable names. * The row and column numbers of table Cells are not guaranteed to be * accurate. Their correctness is ensured when a row/column is retrieved. * * @author Margaret Yau * @version %I%, %G% * @see Spreadsheet * @since 1.0 */ public class SpreadsheetModel extends AbstractTableModel implements TableModelListener{ private boolean DEBUG = false; private int numColumns = 50; // initial number of columns private int numRows = 301; // inital number of rows private Vector> data; // vectors of vectors of cells private Vector columnNames; private Statcato app; /** * Constructor given the parent frame. * * @param app parent frame */ public SpreadsheetModel(Statcato app) { this.app = app; initialize(); } /** * Constructor given the parent frame, number of rows, and number of columns. * * @param app parent frame * @param rows number of rows * @param columns number of columns */ public SpreadsheetModel(Statcato app, int rows, int columns) { numRows = rows; numColumns = columns; this.app = app; initialize(); } /** * Initializes the table model by putting column names into its * corresponding data structor and cells into the underlying table structure. * */ private void initialize() { data = new Vector>(numRows); columnNames = new Vector(numColumns); int row, col; // add column names for (col = 0; col < numColumns; ++col) { columnNames.addElement(getColumnLabel(col)); } // add variable names row data.addElement(new Vector(numColumns)); for (col = 0; col < numColumns; ++col) { data.elementAt(0).addElement(new Cell("", 0, col)); } // add the rest of the rows for (row = 1; row < numRows; ++row) { data.addElement(new Vector(numColumns)); for (col = 0; col < numColumns; ++col) { data.elementAt(row).addElement(new Cell("", row, col)); } } addTableModelListener(this); } /** * Returns the vector of cells in the given column number. * * @param col column number * @return vector of cells in the given column */ public Vector getColumn(int col) { if (col < 0 || col >= numColumns) return null; Vector column = new Vector(getRowCount()); for (int row = 1; row < getRowCount(); ++row) { Cell cell = data.elementAt(row).elementAt(col); cell.setRow(row); cell.setColumn(col); column.addElement(cell); } return column; } /** * Returns the vector of cells in the given row number. * * @param row row number * @return vector of cells in the given row */ public Vector getRow(int row) { Vector rowVec = (Vector) data.elementAt(row); for (int col = 0; col < rowVec.size(); ++col) { rowVec.elementAt(col).setColumn(col); rowVec.elementAt(col).setRow(row); } return rowVec; } /** * Returns the column label for the given column number. * * @param column number * @return column label */ public static String getColumnLabel(int column) { return "C"+(column+1); } /** * Returns the column number embedded in the given column label. * * @param label column label * @return column number */ public int getColumnNumber(String label) { if (label.equals("")) return -1; else if ((!label.startsWith("C") && !label.startsWith("c"))) { // check if label is a variable name label = label.trim(); // remove whitespaces at beginning and end // remove double quotes at beginning and end if (label.startsWith("\"") && label.length() > 1) label = label.substring(1); if (label.endsWith("\"") && label.length() > 1) label = label.substring(0, label.length() - 1); int loc = getVariableIndex(label); return loc; } else { // Cx try { int col = Integer.parseInt(label.substring(1)) - 1; if (col >= 0 && col < getColumnCount()) return col; return -1; } catch (NumberFormatException e) { // variable name starts with c/C // check if label is a variable name int loc = getVariableIndex(label.trim()); return loc; } } } /** * Returns the index of the given variable name or -1 if not found. * * @param label column label * @return index into the variable name vector */ private int getVariableIndex(String label) { Vector variables = data.elementAt(0); for (int i = 0; i < variables.size(); ++i) { if (variables.elementAt(i).getContents().trim().equals(label)) return i; } return -1; } /** * Returns the row number from the given row label. * * @param label row label * @return row number */ public int getRowNumber(String label) { try { int row = Integer.parseInt(label); if (row >= 1 && row < getRowCount()) return row; return -1; } catch (NumberFormatException e) { return -1; } } /** * Returns the row number of the last non-empty row. * * @return row number */ public int getLastNonEmptyRow() { int lastNonEmptyRow = -1; for (int r = 0; r < getRowCount(); ++r) { for (int c = 0; c < getColumnCount(); ++c) { Cell cdata = (Cell) getValueAt(r, c); if (SpreadsheetModel.hasData(cdata)) { lastNonEmptyRow = r; break; } } } return lastNonEmptyRow; } /** * returns the column number of the last non-empty column in the given row. * * @param row number in which the last non-empty column is found * @return column number */ public int getLastNonEmptyColumn(int row) { int lastNonEmptyCol = -1; for (int c = 0; c < getColumnCount(); ++c) { Cell cdata = (Cell) getValueAt(row, c); if (SpreadsheetModel.hasData(cdata)) { lastNonEmptyCol = c; } } return lastNonEmptyCol; } /** * Returns the column number of the last column with data. * * @return column number */ public int getLastNonEmptyColumn() { int lastCol = 0; int col; for (int i = 0; i < getLastNonEmptyRow(); ++i) { col = getLastNonEmptyColumn(i); if (col > lastCol) lastCol = col; } return lastCol; } public void tableChanged(TableModelEvent e) { /* int row = e.getFirstRow(); int column = e.getColumn(); TableModel model = (TableModel)e.getSource(); String columnName = model.getColumnName(column); Object data = model.getValueAt(row, column); if (column == 0 && row > 0) { // row label } * */ } public int getColumnCount() { return data.elementAt(0).size(); } public int getRowCount() { return data.size(); } /** * Returns the column name at the given column. * * @param col column number * @return column name */ @Override public String getColumnName(int col) { return columnNames.elementAt(col); } /** * Returns the variable name at the given column. * * @param col column number * @return variable name */ public String getVariableName(int col) { return data.elementAt(0).elementAt(col).getContents(); } public Object getValueAt(int row, int col) { return data.elementAt(row).elementAt(col); } @Override public Class getColumnClass(int c) { return Object.class; } @Override public boolean isCellEditable(int row, int col) { return true; } /** * Returns the column number of columns that contain data. * * @return vector of Integers representing column numbers */ public Vector getColumnsWithData() { Vector columns = new Vector(); for (int i=0; i < getColumnCount(); i++) { for (int j=1; j < getRowCount(); j++) { if (hasData(getValueAt(j,i))) { columns.addElement(new Integer(i)); break; } } } return columns; } /** * Gets all the column numbers. * * @return a vector of integers (column numbers) */ public Vector getAllColumnNumbers() { Vector columns = new Vector(); for (int i = 0; i < getColumnCount(); ++i) columns.addElement(new Integer(i)); return columns; } /** * Returns true if and only if the given cell has data. * A cell has data if its content is not an empty string. * * @param cell cell object to check if it contains data * @return true iff the given cell has data */ public static boolean hasData(Object cell) { return !((Cell)cell).getContents().equals(""); } /** * Sets the value at the given row and column to the given value. * If the target row is the variable row, checks if the given variable * name already exists. * * @param value Object to set * @param row row number * @param col column number */ @Override public void setValueAt(Object value, int row, int col) { if (row == 0) { String var = ((String) value).trim(); // check for invalid variabl ename if (var.indexOf('\'') != -1) { app.showErrorDialog("Variable name cannot contain the character '"); return; } if (var.indexOf('\"') != -1) { app.showErrorDialog("Variable name cannot contain the character \""); return; } // check if the given variable name already exists if (!var.equals("")) { for (int i = 0; i < getColumnCount(); ++i) { if (var.equals(data.elementAt(0).elementAt(i).getContents())) { app.showErrorDialog("Variable name " + var + " already exists!"); return; } } } } boolean empty = false; if (value.getClass() == String.class) { data.elementAt(row).set(col, new Cell((String)value, row, col)); if (((String)value).equals("")) empty = true; } else if (value.getClass() == Cell.class) { data.elementAt(row).set(col, (Cell)value); if (((Cell)value).getContents().equals("")) empty = true; } else data.elementAt(row).set(col, new Cell(value.toString(), row, col)); fireTableCellUpdated(row, col); } /** * Set the string value at the specified row and column. * * @param value string value * @param row row * @param col column */ public void setStringValueAt(String value, int row, int col) { Cell cell = (Cell) getValueAt(row, col); cell.setContents(value); fireTableCellUpdated(row, col); } /** * Sets the contents of the given column to the given data vector. * * @param column column number * @param data vector of strings */ public void setColumn(int column, Vector data) { int row = 1; for (Enumeration e = data.elements(); e.hasMoreElements();) { setValueAt(new Cell((String)e.nextElement(), row, column), row, column); row++; } } /** * Sets the contents of the given column to the given cell vector. * * @param column column number * @param data vector of cells */ public void setCellColumn(int column, Vector data) { int row = 1; for (Enumeration e = data.elements(); e.hasMoreElements();) { setValueAt(new Cell(((Cell)e.nextElement()).getContents(), row, column), row, column); row++; } } /** * Sets the contents of the given row to the given data vector. * * @param row row number * @param data vector of strings */ public void setRow(int row, Vector data) { int col = 0; for (Enumeration e = data.elements(); e.hasMoreElements();) { //setValueAt(new Cell((String)e.nextElement(), row, col), row, col); setValueAt((String)e.nextElement(), row, col); col++; } } /** * Sets the underlying table structure to the given data matrix. * * @param data vector of vectors of strings */ @SuppressWarnings("unchecked") public void setData(Vector> data) { int r = 1, c = 0; Vector Row; for (Enumeration e = data.elements(); e.hasMoreElements();) { Row = (Vector) e.nextElement(); c = 0; for (Enumeration f = Row.elements(); f.hasMoreElements();) { setValueAt((String)f.nextElement(), r, c); c++; } r++; } } /** * Returns the table data as a string of tab-dlimited values. * * @return string containing table data values separated by tab */ public String getTabDelimitedValues() { String s = ""; int lastRow = getLastNonEmptyRow(); for (int i = 0; i <= lastRow; i++) { for (int j = 0; j <= getLastNonEmptyColumn(i); j++) { s += (String)data.elementAt(i).elementAt(j).getContents(); s += "\t"; } s += "\n"; } return s; } /** * Returns the table data as a string of comma-separated values. * * @return string containing table data values separated by comma */ public String getCommaSeparatedValues() { String s = ""; int lastRow = getLastNonEmptyRow(); for (int i = 0; i <= lastRow; i++) { for (int j = 0; j <= getLastNonEmptyColumn(i); j++) { s += (String)data.elementAt(i).elementAt(j).getContents(); s += ","; } s += "\n"; } return s; } /** * Clears the contents in the cell at the specified row and column. * * @param row row number * @param col column number */ public void clearCell(int row, int col) { data.elementAt(row).elementAt(col).clear(); fireTableCellUpdated(row, col); } /** * Deletes the entire row at the specified row number. Shifts all * rows below upward by one row. * * @param row row number */ public void deleteRow(int row) { if (row <= 0) {// don't delete variable row for (int col = 0; col < getColumnCount(); ++col) { data.elementAt(0).elementAt(col).clear(); } } else { // cannot delete the first row (variables) data.removeElementAt(row); Vector rowVec = new Vector(getColumnCount()); for (int col = 0; col < getColumnCount(); ++col) { rowVec.addElement(new Cell("", row, col)); } data.addElement(rowVec); fireTableDataChanged(); } fireTableDataChanged(); } /** * Deletes the entire column at the specified column number. * Shifts all columns on the right to the left by one column. * * @param column column number */ public void deleteColumn(int column) { Vector rowVec; for (int row = 0; row < getRowCount(); row++) { rowVec = data.elementAt(row); rowVec.removeElementAt(column); rowVec.addElement(new Cell("", row, getColumnCount()-1)); } fireTableDataChanged(); } /** * Deletes the cell at the specified row and column. Shifts data * up to replace the removed cell. * * @param row row number * @param column column number */ public void deleteCell(int row, int column) { if (row > 0) { // cannot delete the first row (variables) // clear cell at row, column data.elementAt(row).elementAt(column).clear(); // copy the cells below the deleted cell to one cell up for (int i = row + 1; i < getRowCount(); ++i) { Cell cell2 = data.elementAt(i).elementAt(column); Cell cell1 = data.elementAt(i-1).elementAt(column); cell1.setContents(cell2.getContents()); } data.elementAt(getRowCount() - 1).elementAt(column).clear(); fireTableDataChanged(); } } /** * Deletes the cells within the specified range. * * @param minRow minimum row * @param maxRow maximum row * @param minCol minimum col * @param maxCol maximum col */ public void deleteCells(int minRow, int maxRow, int minCol, int maxCol) { if (minRow == 0 && maxRow == 0) {// don't delete variable row for (int col = minCol; col <= maxCol; ++col) { data.elementAt(0).elementAt(col).clear(); } fireTableDataChanged(); return; } if (minRow <= 0) minRow = 1; for (int col = minCol; col <= maxCol; ++col) { // clear cells within [minRow, maxRow] for (int row = minRow; row <= maxRow; ++row) data.elementAt(row).elementAt(col).clear(); // copy the cells below the deleted cells to one cell up for (int row = maxRow + 1; row < getRowCount(); ++row) { Cell cell2 = data.elementAt(row).elementAt(col); Cell cell1 = data.elementAt(row-(maxRow-minRow+1)).elementAt(col); cell1.setContents(cell2.getContents()); cell2.clear(); } } fireTableDataChanged(); } /** * Inserts a blank row at the specified row number. * * @param row row number */ public void insertRow(int row) { Vector rowVec = new Vector(getColumnCount()); for (int col = 0; col < getColumnCount(); ++col) { rowVec.addElement(new Cell("", row, col)); } if (row == getRowCount()) { data.addElement(rowVec); } else { data.insertElementAt(rowVec, row); } numRows++; fireTableStructureChanged(); fireTableDataChanged(); } /** * Inserts a blank column at the specified column number. * @param col column number */ public void insertColumn(int col) { Vector rowVec; for (int row = 0; row < getRowCount(); row++) { rowVec = data.elementAt(row); rowVec.insertElementAt(new Cell("", row, col), col); } columnNames.insertElementAt(getColumnLabel(col), col); for (int i = col + 1; i < getColumnCount(); i++) { columnNames.setElementAt(getColumnLabel(i), i); } numColumns++; fireTableStructureChanged(); fireTableDataChanged(); } /** * Inserts a blank cell at the specified row and column. * * @param row row number * @param col column number */ public void insertCell(int row, int col) { // copy the cells to one cell dowm at the insertion point insertRow(getRowCount()); for (int i = getRowCount() - 2; i >= row; --i) { Cell cell2 = data.elementAt(i).elementAt(col); Cell cell1 = data.elementAt(i+1).elementAt(col); cell1.setContents(cell2.getContents()); cell2.clear(); } fireTableStructureChanged(); fireTableDataChanged(); } /** * Prints debug data. */ public void printDebugData() { for (int i=0; i < getRowCount(); i++) { System.out.print(" row " + i + ":"); for (int j=0; j < getColumnCount(); j++) { System.out.print(" " + data.elementAt(i).elementAt(j)); } System.out.println(); } System.out.println("--------------------------"); } }

Ads help maintain this website.