/*
 This file is part of the software framework JStatCom, 
 a library for building applications for data based analysis.

 Copyright (C) 2000-2005  Markus Kraetzig

 This library is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License as
 published by the Free Software Foundation; either version 2.1 of the
 License, or (at your option) any later version.

 This library 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
 Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 USA
 */

package com.jstatcom.ts;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;

import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;

import com.jstatcom.component.StdMessages;
import com.jstatcom.util.UString;

/**
 * A table model to be used with the TSTable component for
 * representing merged time series data. The model takes an array of time series
 * with possibly different time ranges but equal sub periodicities and merges
 * the series to a common time range, which includes all ranges.
 * 
 * @author Markus Kraetzig
 */
public final class TSTableModel extends AbstractTableModel {
    /**
     * The array for storing the data.
     */
    private Double[][] dataArray = new Double[0][0];

    /**
     * The array for storing the TS names.
     */
    private String[] namesArray = new String[1];

    /**
     * The array for storing the row header.
     */
    private String[] rowHeader = new String[0];

    private boolean editable = true;

    private TSDate startDate = null;

    /**
     * TSTableModel default constructor.
     */
    public TSTableModel() {
        super();
    }

    /**
     * Finds the index of the row, where the given Double value
     * appears first, starting from a given row index. It also gives back the
     * row with the closest match for val. In case of an exact
     * match, both values are the same. In case of no exact match, the first
     * value is -1 and the second the index of the row containing
     * the value that is closest to val.
     * 
     * @param val
     *            the value to search for
     * @param startIndex
     *            index to start from, if out of range start from 0
     * @return two dimensional integer array of indices:
     *         
     * @throws IllegalStateException
     *             if (getRowCount() == 0)
     * 
     */
    public int[] findRowIndexOf(Double val, int startIndex) {

        if (getRowCount() == 0)
            throw new IllegalStateException("Nothing to search for.");

        if (startIndex < 0 || startIndex > getRowCount() - 1)
            startIndex = 0;

        if (val == null)
            return new int[] { -1, -1 };

        double minDiff = Double.POSITIVE_INFINITY;
        int minIndex = -1;
        for (int i = startIndex; i < getRowCount(); i++)
            for (int j = 0; j < getColumnCount(); j++) {
                double tmpDiff = Math.abs(dataArray[i][j].doubleValue()
                        - val.doubleValue());
                if (tmpDiff == 0 || dataArray[i][j].equals(val))
                    return new int[] { i, i };
                if (tmpDiff < minDiff) {
                    minDiff = tmpDiff;
                    minIndex = i;
                }
            }
        return new int[] { -1, minIndex };

    }

    /**
     * Gets column class. The method always returns String.class,
     * to make sure that the string editor is choosen.
     * 
     * @return String.class
     * @param col
     *            does not matter
     */
    public Class getColumnClass(int col) {
        return String.class;
    }

    /**
     * Gets number of displayed TS.
     * 
     * @return column count
     */
    public int getColumnCount() {
        if (dataArray.length > 0)
            return dataArray[0].length;
        return 0;

    }

    /**
     * Gets the name of the TS displayed in a certain column.
     * 
     * @return time series name
     * @param col
     *            column index
     */
    public String getColumnName(int col) {
        if (namesArray.length > 0)
            return namesArray[col];

        return null;
    }

    /**
     * Gets underlying observation for a given row and column as a
     * double value.
     * 
     * @return the observation as double
     */
    public double getDoubleValueAt(int row, int col) {
        if (getRowCount() > 0 && getColumnCount() > 0)
            return dataArray[row][col].doubleValue();
        return Double.NaN;
    }

    /**
     * Gets number of displayed observations in time range of all
     * TS.
     * 
     * @return row count
     */
    public int getRowCount() {
        return dataArray.length;

    }

    /**
     * Gets a JViewport that can be used as row header for a
     * scrollpane. This contains the time axis that can be shown with the table
     * together with an index for the current row. Use the method with the
     * JScrollPane that encloses a TSTable using
     * this model.
     * 
     * @param table
     *            the JTable to synchronize selection with
     * @return JViewport to be used as a row header for a
     *         JScrollPane
     */
    public JViewport getRowHeader(JTable table) {
        AbstractTableModel rowHModel = new AbstractTableModel() {
            public Object getValueAt(int row, int col) {
                if (col == 0)
                    return rowHeader[row];

                return (row + 1) + "";
            }

            public int getColumnCount() {
                return 2;
            }

            public int getRowCount() {
                return dataArray.length;

            }

            public boolean isCellEditable(int row, int col) {
                return false;
            }
        };
        JTable headerCol = new JTable(rowHModel) {
            public Component prepareRenderer(TableCellRenderer renderer,
                    int row, int column) {

                Object value = getValueAt(row, column);
                boolean isSelected = isCellSelected(row, column);
                boolean hasFocus = false;

                return renderer.getTableCellRendererComponent(this, value,
                        isSelected, hasFocus, row, column);
            }
        };
        String proto1 = rowHeader[rowHeader.length - 1];
        String proto2 = rowHeader.length + "";
        double sw1 = headerCol.getFontMetrics(headerCol.getFont()).stringWidth(
                proto1);
        double sw2 = headerCol.getFontMetrics(headerCol.getFont()).stringWidth(
                proto2);

        headerCol.createDefaultColumnsFromModel();
        headerCol.setBackground(Color.lightGray);
        headerCol.setSelectionBackground(Color.lightGray);
        headerCol.setColumnSelectionAllowed(false);
        headerCol.setRowSelectionAllowed(true);
        headerCol.setShowGrid(true);
        headerCol.setSelectionModel(table.getSelectionModel());

        headerCol.getColumnModel().getColumn(0).setMinWidth(
                new Double(sw1).intValue() + 15);
        JViewport jv = new JViewport();
        jv.setView(headerCol);
        jv.setPreferredSize(new Dimension(
                new Double(sw1 + sw2).intValue() + 30, 10000));

        return jv;
    }

    /**
     * Gets the TSDate where the very first observation in the
     * model occurs.
     * 
     * @return the starting date for all series in the model
     */
    public TSDate getStartTSDate() {
        return startDate;
    }

    /**
     * Gets underlying observation for a given row and column.
     * 
     * @param row
     *            index for row
     * @param col
     *            index for column
     * @return a Double wrapping the observation
     */
    public Object getValueAt(int row, int col) {
        if (getRowCount() > 0 && getColumnCount() > 0)
            return dataArray[row][col];
        return null;
    }

    /**
     * Gets whether given cell can be edited. This is the same for all cells and
     * set for the whole model via the editable property.
     * 
     * @return the editable property for the whole table
     * @param row
     *            index for row
     * @param col
     *            index for column
     */
    public boolean isCellEditable(int row, int col) {
        return editable;
    }

    /**
     * Sets the TS to be held by the model. It merges the series
     * and creates the data and names arrays.
     * 
     * @param tsArray
     *            an array of TS objects with the same sub
     *            periodicity
     */
    public void setTS(TS[] tsArray) {

        double[][] data = TS.getMergedData(tsArray);
        namesArray = new String[tsArray.length];
        for (int i = 0; i < tsArray.length; i++)
            namesArray[i] = tsArray[i].name();
        dataArray = new Double[data.length][data[0].length];
        for (int i = 0; i < data.length; i++)
            for (int j = 0; j < data[0].length; j++)
                dataArray[i][j] = new Double(data[i][j]);
        TSDateRange tsRange = TSDateRange.getMergedRange(tsArray);
        rowHeader = tsRange.timeAxisStringArray(); 
        startDate = tsRange.lowerBound();
        fireTableDataChanged();
        fireTableStructureChanged();

    }


    // set data
    public void setArrays(Double[][] data, String[] names) {
        namesArray=names;
        dataArray=data;
//        dataArray = new Double[data.length][data[0].length];
//        for (int i = 0; i < data.length; i++)
//          for (int j = 0; j < data[0].length; j++)
//                dataArray[i][j] = new Double(data[i][j]);
       // TSDateRange tsRange = TSDateRange.getMergedRange(tsArray);
        rowHeader = namesArray;
        startDate = null;
        fireTableDataChanged();
        fireTableStructureChanged();

    }


   // insert table names
   public void updateTable(String[] array) {
        namesArray = array;
        fireTableDataChanged();
        fireTableStructureChanged();
  }
    /**
     * Sets data at a given position and displays an error message if input
     * cannot be parsed to a double.
     * 
     * @param obj
     *            Object that the method tries to parse to a Double
     * @param row
     *            index for row
     * @param col
     *            index for column
     */
    public void setValueAt(Object obj, int row, int col) {
        Double previousValue = (Double) getValueAt(row, col);

        if ((obj instanceof Double)) {
            dataArray[row][col] = (Double) obj;
            if (!previousValue.equals(dataArray[row][col]))
                fireTableCellUpdated(row, col);
            return;
        }
        String text = obj.toString().trim();
        Double val = UString.parseToNumber(obj.toString());
        if (val == null) {
            StdMessages.errorInput("Input \"" + text
                    + "\" is not a valid number.\nThe previous value {"
                    + previousValue + "} is restored.");

            dataArray[row][col] = previousValue;
        } else if (!val.equals(previousValue)) {
            dataArray[row][col] = val;
            fireTableCellUpdated(row, col);
        }
    }

}

 

Ads help maintain this website.