    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2007, 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     * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
    025     * in the United States and other countries.]
    026     *
    027     * --------
    028     * CSV.java
    029     * --------
    030     * (C) Copyright 2003-2007, 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
    041    package org.jfree.data.io;
    042
    043    import java.io.BufferedReader;
    044    import java.io.IOException;
    045    import java.io.Reader;
    046    import java.util.List;
    047
    048    import org.jfree.data.category.CategoryDataset;
    049    import 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     */
    056    public 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    }