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

at.spardat.xma.datasource.TabularData Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (c) 2003, 2007 s IT Solutions AT Spardat GmbH .
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     s IT Solutions AT Spardat GmbH - initial API and implementation
 *******************************************************************************/

// @(#) $Id: TabularData.java 2089 2007-11-28 13:56:13Z s3460 $
package at.spardat.xma.datasource;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.StringTokenizer;

import at.spardat.xma.mdl.Atom;

/**
 * A TabularDatatable stores data in a row/column matrix of Atoms in
 * memory, with column header names.
 * It can be read from a File or an InputStream and written
 * to an OutputStream or File in CSV format. The "UTF-8"
 * encoding is used.
 *
 */
public class TabularData implements ITabularData {

    private final static String COL_DELIM = ",";
    private final static String NEW_LINE = "\r\n";
    private final static String ENCODING = "UTF-8";

    /**
     * Holds the column names
     */
    TabularDataHeader       header = new TabularDataHeader();

    /**
     * Holds the rows of the table, type of contained objects is TabularDataRow.
     */
    ArrayList               rows = new ArrayList();

    /**
     * Creates an empty table.
     *
     */
    public TabularData() {

    }

    /**
     * Creates a table by reading it in from a UTF-8 encoded CSV file.
     *
     * In CSV format, each record is stored on a separate line,
     * with the column header names on the first line.
     * Fields on a line are separated by commas. Spaces before and
     * after the comma are not allowed. Every value must be delimted
     * using double quotes.
     *
     * @param file the file to read from
     */
    public static TabularData readFrom (File file) throws IOException {
        FileInputStream     fis = new FileInputStream (file);
        return readFrom (fis);
    }

    /**
     * Creates a table by reading it from an InputStream in UTF-8-encoded CSV
     * format. This method closes the inputStream when finished.
     *
     * @param inputStream - The InputStream to read from.
     * @return The table read.
     * @throws IOException and RuntimeExceptions if inputStream could not be read.
     */
    public static TabularData readFrom (InputStream inputStream) throws IOException {
        TabularData         result = null;

        if (inputStream == null) {
            throw new IllegalArgumentException("Inputstream is null.");
        }

        InputStreamReader   streamReader = null;
        BufferedReader      reader = null;

        // Create BufferedReader
        try {
            streamReader = new InputStreamReader(inputStream, ENCODING);
            reader = new BufferedReader(streamReader);

            // Read header
            String []       colNames = null;
            String          firstLine = reader.readLine();
            if (firstLine == null) throw new IOException ("Not a single line in stream.");
            colNames = readHeader (firstLine);

            // create TabularDomData or TabularData, depending on header information
            result = TabularDomData.isDomainColStructure(colNames) ? new TabularDomData() : new TabularData();
            // add columns
            for (int i = 0; i < colNames.length; i++) {
                result.addColumn(colNames[i]);
            }
            // Read data
            String          line;
            while ((line = reader.readLine()) != null) {
                line = line.trim();
                if (line.length() == 0) continue;
                result.readRow (line);
            }
        } finally {
            reader.close();
            streamReader.close();
            inputStream.close();
        }
        return result;
    }

    /**
     * Reads the header from a text line.
     *
     * @param line the line of text holding the header info
     * @return Array of column names
     * @throws IOException if columns cannot be read from line
     */
    private static String[] readHeader (String line) throws IOException {
        if (line == null) throw new IllegalArgumentException();
        ArrayList           cols = new ArrayList();
        StringTokenizer     rowStrings = new StringTokenizer(line, COL_DELIM);
        while (rowStrings.hasMoreElements()) {

            String columnName = rowStrings.nextToken().trim();
            if (columnName.length() == 0) {
                throw new IOException("Empty column name.");
            }

            cols.add(columnName);
        }
        if (cols.size() == 0) throw new IOException ("Not a single column available.");
        String [] colArr = new String[cols.size()];
        cols.toArray (colArr);
        return colArr;
    }

    /**
     * Reads row data from a BufferedReader.
     *
     * @param reader - The BufferedReader to read the row from.
     * @throws IOException
     */
    private void readRow (String line) throws IOException {
        StringTokenizer rowStrings = new StringTokenizer(line, COL_DELIM);

        TabularDataRow row = new TabularDataRow (this);
        while (rowStrings.hasMoreElements()) {
            String transportString = rowStrings.nextToken();
            Atom atom = Atom.newTransportInstance(transportString);
            row.add(atom);
        }
        addRow(row);
    }

    /**
     * Creates a table containing given data
     *
     * @param columnNames - The column header names
     * @param rows - The data as a matrix of strings
     */
    public TabularData(String[] columnNames, Atom[][] rows)
        throws IllegalArgumentException {

        // Header
        for (int i = 0; i < columnNames.length; i++) {
            addColumn(columnNames[i]);
        }

        // Rows
        for (int rowIndex = 0; rowIndex < rows.length; rowIndex++) {
            TabularDataRow row = new TabularDataRow(this);
            for (int colIndex = 0;
                colIndex < rows[rowIndex].length;
                colIndex++) {
                Atom atom = rows[rowIndex][colIndex];
                row.add(atom);
            }
            addRow(row);
        }
    }

    /**
     * Add a column. Note that adding columns is only allowed after construction, when there
     * are no rows in the table.
     *
     * @param name the name of the column to be added.
     * @exception IllegalArgumentException if the name is not valid or a column with that name already exists.
     * @exception IllegalStateException if there are already rows in the table.
     */
    public void addColumn (String name) throws IllegalArgumentException {

        if (name == null || name.length() == 0) throw new IllegalArgumentException ();

        if (size() > 0) {
            throw new IllegalStateException("Not empty.");
        }

        header.addColumn(name);
    }

    /**
     * @see at.spardat.xma.datasource.ITabularData#size()
     */
    public int size() {
        return rows.size();
    }

    /**
     * @see at.spardat.xma.datasource.ITabularData#numCols()
     */
    public int numCols() {
        return header.size();
    }

    /**
     * Gets the table row with the index row.
     *
     * @param row the index of the row.
     * @return TabularDataRow, never null
     * @exception RuntimeException if row out of bounds.
     */
    public TabularDataRow getRow(int row) {
        return (TabularDataRow) rows.get(row);  // IndexOutOfBoundsEx if out of bounds
    }

    /**
     * Adds a row in the table.
     *
     * @param r the row to add.
     * @exception IllegalArgumentException if no columns have been defined yet.
     */
    public void addRow (TabularDataRow r) throws IllegalArgumentException {

        if (header.size() == 0) {
            throw new IllegalArgumentException("No columns known.");
        }

        rows.add(r);
    }

    /**
     * Get the value from a cell specified bei row and col.
     *
     * @param row - The row index.
     * @param col - The column index.
     * @see at.spardat.xma.datasource.ITabularData#getCell(int, int)
     */
    public Atom getCell(int row, int col) {

        TabularDataRow currRow = getRow(row);
        return (Atom) currRow.get(col);
    }

    /**
     * Returns the value from a cell specified bei row and columnName.
     *
     * @param row - The row index.
     * @param columnName - The name of the column.
     */
    public Atom getCell(int row, String columnName) {

        TabularDataRow currRow = getRow(row);
        return (Atom) currRow.get(columnName);
    }

    /**
     * Returns the index of the column with the name colName.
     *
     * @param colName the name of the column
     * @return the index of the column or -1, if there is no such column.
     */
    public int getColumnIndex(String colName) {

        return header.getColumnIndex(colName);
    }

    /**
     * Returns the header name of the n'th column in the table
     *
     * @param col the column index
     * @exception IllegalArgumentException if col is out of range.
     */
    public String getColumnName (int col) {

        return header.getColumnName(col);
    }

    /**
     * Writes a TabularData to an OutputStream.
     *
     * @param outputStream - The OutputStream to write the TabularData to.
     * @throws IOException
     */
    public void write (OutputStream outputStream) throws IOException {

        OutputStreamWriter oWriter = null;
        BufferedWriter     bWriter = null;

        try {

            oWriter = new OutputStreamWriter (outputStream, ENCODING);
            bWriter = new BufferedWriter (oWriter);
            // Write header
            writeHeader (bWriter);

            // Write rows
            for (int i=0, size=rows.size(); iWriter.
     *
     * @param writer the Writer to write the header to.
     * @throws IOException
     */
    private void writeHeader (Writer writer) throws IOException {

        int numCols = header.size();

        for (int col = 0; col < numCols; col++) {
            if (col > 0) {
                writer.write (COL_DELIM);
            }
            writer.write (header.getColumnName(col));
        }
        writer.write (NEW_LINE);
    }

    /**
     * Writes a row to an Writer.
     *
     * @param writer the Writer to write the row to.
     * @param row the index of the row to write.
     * @throws IOException
     */
    private void writeRow (Writer writer, int row) throws IOException {

        int numCols = header.size();
        TabularDataRow rowData = getRow(row);

        for (int col = 0; col < numCols; col++) {
            if (col > 0) {
                writer.write (COL_DELIM);
            }
            Atom value = rowData.get(col);
            writer.write (value.toTransportString());
        }
        writer.write (NEW_LINE);
    }

    /**
     * Store the table in to a given file, in CSV format.
     *
     * @param file - The file to write to.
     */
    public void save(File file) throws IOException {
        write(new FileOutputStream(file));
    }

    /**
     * Clears the table data.
     *
     */
    public void clear() {
        rows.clear();
    }

    /**
     * Maps this TabularData to an integer hash code. Since this hash code is
     * used for probabilistic uptodate checks, be sure to include any
     * table data in the calculation and ensure that the generated hash
     * is equally distributed over the int domain.
     *
     * @see java.lang.Object#hashCode()
     */
    public int hashCode() {
        int hashHeader = header.hashCode();
        int hashRows   = rows.hashCode();
        return hashHeader ^ hashRows;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy