    001/* ===========================================================
    002 * JFreeChart : a free chart library for the Java(tm) platform
    003 * ===========================================================
    004 *
    005 * (C) Copyright 2000-2011, by Object Refinery Limited and Contributors.
    006 *
    007 * Project Info:  http://www.jfree.org/jfreechart/index.html
    008 *
    009 * This library is free software; you can redistribute it and/or modify it
    010 * under the terms of the GNU Lesser General Public License as published by
    011 * the Free Software Foundation; either version 2.1 of the License, or
    012 * (at your option) any later version.
    013 *
    014 * This library is distributed in the hope that it will be useful, but
    015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    017 * License for more details.
    018 *
    019 * You should have received a copy of the GNU Lesser General Public
    020 * License along with this library; if not, write to the Free Software
    021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
    022 * USA.
    023 *
    024 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    025 * Other names may be trademarks of their respective owners.]
    026 *
    027 * --------
    028 * CSV.java
    029 * --------
    030 * (C) Copyright 2003-2008, by Object Refinery Limited.
    031 *
    032 * Original Author:  David Gilbert (for Object Refinery Limited);
    033 * Contributor(s):   -;
    034 *
    035 * Changes
    036 * -------
    037 * 24-Nov-2003 : Version 1 (DG);
    038 *
    039 */
    040
    041package org.jfree.data.io;
    042
    043import java.io.BufferedReader;
    044import java.io.IOException;
    045import java.io.Reader;
    046import java.util.List;
    047
    048import org.jfree.data.category.CategoryDataset;
    049import org.jfree.data.category.DefaultCategoryDataset;
    050
    051/**
    052 * A utility class for reading {@link CategoryDataset} data from a CSV file.
    053 * This initial version is very basic, and won't handle errors in the data
    054 * file very gracefully.
    055 */
    056public class CSV {
    057
    058    /** The field delimiter. */
    059    private char fieldDelimiter;
    060
    061    /** The text delimiter. */
    062    private char textDelimiter;
    063
    064    /**
    065     * Creates a new CSV reader where the field delimiter is a comma, and the
    066     * text delimiter is a double-quote.
    067     */
    068    public CSV() {
    069        this(',', '"');
    070    }
    071
    072    /**
    073     * Creates a new reader with the specified field and text delimiters.
    074     *
    075     * @param fieldDelimiter  the field delimiter (usually a comma, semi-colon,
    076     *                        colon, tab or space).
    077     * @param textDelimiter  the text delimiter (usually a single or double
    078     *                       quote).
    079     */
    080    public CSV(char fieldDelimiter, char textDelimiter) {
    081        this.fieldDelimiter = fieldDelimiter;
    082        this.textDelimiter = textDelimiter;
    083    }
    084
    085    /**
    086     * Reads a {@link CategoryDataset} from a CSV file or input source.
    087     *
    088     * @param in  the input source.
    089     *
    090     * @return A category dataset.
    091     *
    092     * @throws IOException if there is an I/O problem.
    093     */
    094    public CategoryDataset readCategoryDataset(Reader in) throws IOException {
    095
    096        DefaultCategoryDataset dataset = new DefaultCategoryDataset();
    097        BufferedReader reader = new BufferedReader(in);
    098        List columnKeys = null;
    099        int lineIndex = 0;
    100        String line = reader.readLine();
    101        while (line != null) {
    102            if (lineIndex == 0) {  // first line contains column keys
    103                columnKeys = extractColumnKeys(line);
    104            }
    105            else {  // remaining lines contain a row key and data values
    106                extractRowKeyAndData(line, dataset, columnKeys);
    107            }
    108            line = reader.readLine();
    109            lineIndex++;
    110        }
    111        return dataset;
    112
    113    }
    114
    115    /**
    116     * Extracts the column keys from a string.
    117     *
    118     * @param line  a line from the input file.
    119     *
    120     * @return A list of column keys.
    121     */
    122    private List extractColumnKeys(String line) {
    123        List keys = new java.util.ArrayList();
    124        int fieldIndex = 0;
    125        int start = 0;
    126        for (int i = 0; i < line.length(); i++) {
    127            if (line.charAt(i) == this.fieldDelimiter) {
    128                if (fieldIndex > 0) {  // first field is ignored, since
    129                                       // column 0 is for row keys
    130                    String key = line.substring(start, i);
    131                    keys.add(removeStringDelimiters(key));
    132                }
    133                start = i + 1;
    134                fieldIndex++;
    135            }
    136        }
    137        String key = line.substring(start, line.length());
    138        keys.add(removeStringDelimiters(key));
    139        return keys;
    140    }
    141
    142    /**
    143     * Extracts the row key and data for a single line from the input source.
    144     *
    145     * @param line  the line from the input source.
    146     * @param dataset  the dataset to be populated.
    147     * @param columnKeys  the column keys.
    148     */
    149    private void extractRowKeyAndData(String line,
    150                                      DefaultCategoryDataset dataset,
    151                                      List columnKeys) {
    152        Comparable rowKey = null;
    153        int fieldIndex = 0;
    154        int start = 0;
    155        for (int i = 0; i < line.length(); i++) {
    156            if (line.charAt(i) == this.fieldDelimiter) {
    157                if (fieldIndex == 0) {  // first field contains the row key
    158                    String key = line.substring(start, i);
    159                    rowKey = removeStringDelimiters(key);
    160                }
    161                else {  // remaining fields contain values
    162                    Double value = Double.valueOf(
    163                        removeStringDelimiters(line.substring(start, i))
    164                    );
    165                    dataset.addValue(
    166                        value, rowKey,
    167                        (Comparable) columnKeys.get(fieldIndex - 1)
    168                    );
    169                }
    170                start = i + 1;
    171                fieldIndex++;
    172            }
    173        }
    174        Double value = Double.valueOf(
    175            removeStringDelimiters(line.substring(start, line.length()))
    176        );
    177        dataset.addValue(
    178            value, rowKey, (Comparable) columnKeys.get(fieldIndex - 1)
    179        );
    180    }
    181
    182    /**
    183     * Removes the string delimiters from a key (as well as any white space
    184     * outside the delimiters).
    185     *
    186     * @param key  the key (including delimiters).
    187     *
    188     * @return The key without delimiters.
    189     */
    190    private String removeStringDelimiters(String key) {
    191        String k = key.trim();
    192        if (k.charAt(0) == this.textDelimiter) {
    193            k = k.substring(1);
    194        }
    195        if (k.charAt(k.length() - 1) == this.textDelimiter) {
    196            k = k.substring(0, k.length() - 1);
    197        }
    198        return k;
    199    }
    200
    201}