001    /* ===========================================================
002     * JFreeChart : a free chart library for the Java(tm) platform
003     * ===========================================================
004     *
005     * (C) Copyright 2000-2005, 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, 2004, by Object Refinery Limited.
031     *
032     * Original Author:  David Gilbert (for Object Refinery Limited);
033     * Contributor(s):   -;
034     *
035     * $Id: CSV.java,v 1.3.2.1 2005/10/25 21:33:38 mungady Exp $
036     *
037     * Changes
038     * -------
039     * 24-Nov-2003 : Version 1 (DG);
040     *
041     */
042    
043    package org.jfree.data.io;
044    
045    import java.io.BufferedReader;
046    import java.io.IOException;
047    import java.io.Reader;
048    import java.util.List;
049    
050    import org.jfree.data.category.CategoryDataset;
051    import org.jfree.data.category.DefaultCategoryDataset;
052    
053    /**
054     * A utility class for reading {@link CategoryDataset} data from a CSV file.  
055     * This initial version is very basic, and won't handle errors in the data 
056     * file very gracefully.
057     */
058    public class CSV {
059    
060        /** The field delimiter. */
061        private char fieldDelimiter;
062        
063        /** The text delimiter. */
064        private char textDelimiter;
065         
066        /** 
067         * Creates a new CSV reader where the field delimiter is a comma, and the 
068         * text delimiter is a double-quote.
069         */
070        public CSV() {
071            this(',', '"');    
072        }
073        
074        /**
075         * Creates a new reader with the specified field and text delimiters.
076         * 
077         * @param fieldDelimiter  the field delimiter (usually a comma, semi-colon,
078         *                        colon, tab or space).
079         * @param textDelimiter  the text delimiter (usually a single or double 
080         *                       quote).
081         */
082        public CSV(char fieldDelimiter, char textDelimiter) {
083            this.fieldDelimiter = fieldDelimiter;
084            this.textDelimiter = textDelimiter;
085        }
086        
087        /**
088         * Reads a {@link CategoryDataset} from a CSV file or input source.
089         * 
090         * @param in  the input source.
091         * 
092         * @return A category dataset.
093         * 
094         * @throws IOException if there is an I/O problem.
095         */
096        public CategoryDataset readCategoryDataset(Reader in) throws IOException {
097            
098            DefaultCategoryDataset dataset = new DefaultCategoryDataset();
099            BufferedReader reader = new BufferedReader(in);
100            List columnKeys = null;
101            int lineIndex = 0;
102            String line = reader.readLine();
103            while (line != null) {
104                if (lineIndex == 0) {  // first line contains column keys
105                    columnKeys = extractColumnKeys(line);
106                }
107                else {  // remaining lines contain a row key and data values
108                    extractRowKeyAndData(line, dataset, columnKeys);
109                }
110                line = reader.readLine();
111                lineIndex++;
112            }
113            return dataset;     
114             
115        }
116        
117        /**
118         * Extracts the column keys from a string.
119         * 
120         * @param line  a line from the input file.
121         * 
122         * @return A list of column keys.
123         */
124        private List extractColumnKeys(String line) {
125            List keys = new java.util.ArrayList();
126            int fieldIndex = 0;
127            int start = 0;
128            for (int i = 0; i < line.length(); i++) {
129                if (line.charAt(i) == this.fieldDelimiter) {
130                    if (fieldIndex > 0) {  // first field is ignored, since 
131                                           // column 0 is for row keys
132                        String key = line.substring(start, i);
133                        keys.add(removeStringDelimiters(key));
134                    }
135                    start = i + 1;
136                    fieldIndex++;
137                }
138            }
139            String key = line.substring(start, line.length());
140            keys.add(removeStringDelimiters(key));
141            return keys;        
142        }
143        
144        /**
145         * Extracts the row key and data for a single line from the input source.
146         * 
147         * @param line  the line from the input source.
148         * @param dataset  the dataset to be populated.
149         * @param columnKeys  the column keys.
150         */
151        private void extractRowKeyAndData(String line,
152                                          DefaultCategoryDataset dataset,
153                                          List columnKeys) {
154            Comparable rowKey = null;
155            int fieldIndex = 0;
156            int start = 0;
157            for (int i = 0; i < line.length(); i++) {
158                if (line.charAt(i) == this.fieldDelimiter) {
159                    if (fieldIndex == 0) {  // first field contains the row key
160                        String key = line.substring(start, i);
161                        rowKey = removeStringDelimiters(key);
162                    }
163                    else {  // remaining fields contain values
164                        Double value = Double.valueOf(
165                            removeStringDelimiters(line.substring(start, i))
166                        );
167                        dataset.addValue(
168                            value, rowKey, 
169                            (Comparable) columnKeys.get(fieldIndex - 1)
170                        );
171                    }
172                    start = i + 1;
173                    fieldIndex++;
174                }
175            }
176            Double value = Double.valueOf(
177                removeStringDelimiters(line.substring(start, line.length()))
178            );
179            dataset.addValue(
180                value, rowKey, (Comparable) columnKeys.get(fieldIndex - 1)
181            ); 
182        }
183        
184        /**
185         * Removes the string delimiters from a key (as well as any white space 
186         * outside the delimiters).
187         * 
188         * @param key  the key (including delimiters).
189         * 
190         * @return The key without delimiters.
191         */
192        private String removeStringDelimiters(String key) {
193            String k = key.trim();
194            if (k.charAt(0) == this.textDelimiter) {
195                k = k.substring(1);
196            }
197            if (k.charAt(k.length() - 1) == this.textDelimiter) {
198                k = k.substring(0, k.length() - 1);
199            }
200            return k;
201        }
202        
203    }