001    /* ===========================================================
002     * JFreeChart : a free chart library for the Java(tm) platform
003     * ===========================================================
004     *
005     * (C) Copyright 2000-2008, 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-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    
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    }