001    package net.sf.cpsolver.ifs.util;
002    
003    import java.io.BufferedReader;
004    import java.io.File;
005    import java.io.FileReader;
006    import java.io.FileWriter;
007    import java.io.IOException;
008    import java.io.PrintWriter;
009    import java.io.Serializable;
010    import java.util.Calendar;
011    import java.util.Collection;
012    import java.util.Date;
013    import java.util.Enumeration;
014    import java.util.Hashtable;
015    import java.util.Locale;
016    import java.util.Vector;
017    
018    /** Support for CSV (comma separated) text files.
019     * 
020     * @version
021     * IFS 1.1 (Iterative Forward Search)<br>
022     * Copyright (C) 2006 Tomáš Müller<br>
023     * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
024     * Lazenska 391, 76314 Zlin, Czech Republic<br>
025     * <br>
026     * This library is free software; you can redistribute it and/or
027     * modify it under the terms of the GNU Lesser General Public
028     * License as published by the Free Software Foundation; either
029     * version 2.1 of the License, or (at your option) any later version.
030     * <br><br>
031     * This library is distributed in the hope that it will be useful,
032     * but WITHOUT ANY WARRANTY; without even the implied warranty of
033     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
034     * Lesser General Public License for more details.
035     * <br><br>
036     * You should have received a copy of the GNU Lesser General Public
037     * License along with this library; if not, write to the Free Software
038     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
039     */
040    
041    public class CSVFile implements Serializable {
042            private static final long serialVersionUID = 1L;
043            Hashtable iHeaderMap = null;
044            CSVLine iHeader = null;
045            Vector iLines = null;
046            String iSeparator = ",";
047            String iQuotationMark = "\"";
048            
049            public CSVFile() {}
050            
051            public CSVFile(File file) throws IOException {
052                    load(file);
053            }
054            
055            public CSVFile(File file, String separator) throws IOException {
056                    setSeparator(separator);
057                    load(file);
058            }
059            
060            public CSVFile(File file, String separator, String quotationMark) throws IOException {
061                    setSeparator(separator);
062                    setQuotationMark(quotationMark);
063                    load(file);
064            }
065            
066            public void setSeparator(String separator) { iSeparator = separator; }
067            public String getSeparator() { return iSeparator; }
068            public void setQuotationMark(String quotationMark) { iQuotationMark = quotationMark; }
069            public String getQuotationMark() { return iQuotationMark; }
070    
071            public void load(File file) throws IOException {
072                    BufferedReader reader = null;
073                    try {
074                            reader = new BufferedReader(new FileReader(file));
075                            iHeader = new CSVLine(reader.readLine()); //read header
076                            iHeaderMap = new Hashtable();
077                            iLines = new Vector();
078                            int idx = 0;
079                            for (Enumeration e=iHeader.fields();e.hasMoreElements();idx++) {
080                                    CSVField field = (CSVField)e.nextElement();
081                                    iHeaderMap.put(field.toString(),new Integer(idx));
082                            }
083                            String line = null;
084                            while ((line=reader.readLine())!=null) {
085                                    if (line.trim().length()==0) continue;
086                                    iLines.addElement(new CSVLine(line));
087                            }
088                    } finally {
089                            if (reader!=null) reader.close();
090                    }
091            }
092            
093            public void save(File file) throws IOException {
094                    PrintWriter writer = null;
095                    try {
096                            writer = new PrintWriter(new FileWriter(file));
097                            if (iHeader!=null) 
098                                    writer.println(iHeader.toString());
099                    
100                            if (iLines!=null) {
101                                    for (Enumeration e=iLines.elements();e.hasMoreElements();) {
102                                            Object line = e.nextElement();
103                                            writer.println(line.toString());
104                                    }
105                            }
106                    
107                            writer.flush();
108                    } finally {
109                            if (writer!=null) writer.close();
110                    }
111            }
112            
113            public CSVLine getHeader() { return iHeader; }
114            public void setHeader(CSVLine header) { iHeader = header; }
115            public Vector getLines() { return iLines; }
116            public int size() { return iLines.size(); }
117            public boolean isEmpty() { return iLines.isEmpty(); }
118            public CSVLine getLine(int idx) { return (CSVLine)iLines.elementAt(idx); }
119            public Enumeration lines() { return iLines.elements(); }
120            public void addLine(CSVLine line) { 
121                    if (iLines==null) iLines = new Vector();
122                    iLines.addElement(line);
123            }
124            public void addLine(String line) { 
125                    if (iLines==null) iLines = new Vector();
126                    iLines.addElement(line);
127            }
128            public Vector filter(CSVFilter filter) {
129                    Vector ret = new Vector();
130                    for (Enumeration e=iLines.elements();e.hasMoreElements();) {
131                            CSVLine line = (CSVLine)e.nextElement();
132                            if (filter.match(line))
133                                    ret.addElement(line);
134                    }
135                    return ret;
136            }
137            
138            public CSVLine addLine() {
139                    CSVLine line = new CSVLine(); 
140                    addLine(line);
141                    return line;
142            }
143            public CSVLine addLine(CSVField fields[]) {
144                    CSVLine line = new CSVLine(fields);
145                    addLine(line);
146                    return line;
147            }
148            public CSVLine addLine(Collection fields) {
149                    CSVLine line = new CSVLine(fields);
150                    addLine(line);
151                    return line;
152            }
153            public CSVLine setHeader(CSVField fields[]) {
154                    CSVLine header = new CSVLine(fields);
155                    setHeader(header);
156                    return header;
157            }
158            public CSVLine setHeader(Collection fields) {
159                    CSVLine header = new CSVLine(fields);
160                    setHeader(header);
161                    return header;
162            }
163            
164        /** Representation of a  line of a CSV file */
165            public class CSVLine implements Serializable {
166                    private static final long serialVersionUID = 1L;
167                    Vector iFields = new Vector(iHeader==null?10:iHeader.size());
168                    
169                    public CSVLine(String line) {
170                            int idx = 0;
171                            int newIdx = 0;
172                            int fromIdx = 0; 
173                            while ((newIdx = line.indexOf(iSeparator, fromIdx))>=0) {
174                                    String field = line.substring(idx, newIdx);
175                                    if (iQuotationMark!=null && field.startsWith(iQuotationMark) && !field.endsWith(iQuotationMark)) {
176                                            fromIdx = newIdx + iSeparator.length();
177                                            continue;
178                                    }
179                                    iFields.addElement(new CSVField(field, iQuotationMark));
180                                    idx = newIdx + iSeparator.length();
181                                    fromIdx = idx;
182                            }
183                            iFields.addElement(new CSVField(line.substring(idx), iQuotationMark));
184                    }
185                    public CSVLine() {}
186                    public CSVLine(CSVField fields[]) {
187                            for (int i=0;i<fields.length;i++)
188                                    iFields.addElement(fields[i]);
189                    }
190                    public CSVLine(Collection fields) {
191                            iFields.addAll(fields);
192                    }
193                    
194                    public Vector getFields() { return iFields; }
195                    public int size() { return iFields.size(); }
196                    public boolean isEmpty() { return iFields.isEmpty(); }
197                    public CSVField getField(int idx) { 
198                            try {
199                                    return (CSVField)iFields.elementAt(idx);
200                            } catch (ArrayIndexOutOfBoundsException e) {
201                                    return null;
202                            }
203                    }
204                    public void setField(int idx, CSVField field) {
205                            iFields.setElementAt(field, idx);
206                    }
207                    public Enumeration fields() { return iFields.elements(); }
208                    public CSVField getField(String name) {
209                            Integer idx = (Integer)iHeaderMap.get(name);
210                            return (idx==null?null:getField(idx.intValue()));
211                    }
212                    public void setField(String name, CSVField field) {
213                            Integer idx = (Integer)iHeaderMap.get(name);
214                            if (idx!=null) setField(idx.intValue(), field);
215                    }
216                    public String toString() {
217                            StringBuffer sb = new StringBuffer();
218                            for (Enumeration e=iFields.elements();e.hasMoreElements();) {
219                                    CSVField field = (CSVField)e.nextElement();
220                                    if (field!=null)
221                                            sb.append((iQuotationMark==null?"":iQuotationMark)+(field==null?"":field.toString())+(iQuotationMark==null?"":iQuotationMark));
222                                    if (e.hasMoreElements()) sb.append(iSeparator);
223                            }
224                            return sb.toString();
225                    }
226                    public void debug(int offset, PrintWriter out) {
227                            int idx=0;
228                            for (Enumeration e=iFields.elements();e.hasMoreElements();idx++) {
229                                    CSVField field = (CSVField)e.nextElement();
230                                    if (field==null || field.toString().length()==0) continue;
231                                    for (int i=0;i<offset;i++)
232                                            out.print(" ");
233                                    out.println(iHeader.getField(idx)+"="+(iQuotationMark==null?"":iQuotationMark)+field+(iQuotationMark==null?"":iQuotationMark));
234                            }
235                    }
236            }
237            
238        /** Representation of a field of a CSV file */
239            public static class CSVField implements Serializable {
240                    private static final long serialVersionUID = 1L;
241                    String iField = null;
242                    public CSVField(String field, String quotationMark) {
243                            field = field.trim();
244                            if (quotationMark!=null && field.startsWith(quotationMark) && field.endsWith(quotationMark)) 
245                                    field = field.substring(1, field.length()-1);
246                            iField = field.trim();
247                    }
248                    public CSVField(Object field) { set(field==null?"":field.toString()); }
249                    public CSVField(int field) { set(field); }
250                    public CSVField(boolean field) { set(field); }
251                    public CSVField(double field) { set(field); }
252                    public CSVField(long field) { set(field); }
253                    public CSVField(float field) { set(field); }
254                    
255                    public void set(Object value) { iField = (value==null?"":value.toString()); }
256                    public void set(int value) { iField = String.valueOf(value); }
257                    public void set(boolean value) { iField = (value?"1":"0"); }
258                    public void set(double value) { iField = String.valueOf(value); }
259                    public void set(long value) { iField = String.valueOf(value); }
260                    public void set(float value) { iField = String.valueOf(value); }
261                    
262                    public String toString() { return (iField==null?"":iField); }
263                    public boolean isEmpty() { return (iField.length()==0); }
264                    public int toInt() { return toInt(0); }
265                    public int toInt(int defaultValue) { 
266                            try {
267                                    return Integer.parseInt(iField);
268                            } catch (NumberFormatException e) {
269                                    return defaultValue;
270                            }
271                    }
272                    public long toLong() { return toLong(0); }
273                    public long toLong(long defaultValue) { 
274                            try {
275                                    return Long.parseLong(iField);
276                            } catch (NumberFormatException e) {
277                                    return defaultValue;
278                            }
279                    }
280                    public double toDouble() { return toDouble(0); }
281                    public double toDouble(double defaultValue) { 
282                            try {
283                                    return Double.parseDouble(iField);
284                            } catch (NumberFormatException e) {
285                                    return defaultValue;
286                            }
287                    }
288                    public Date toDate() {
289                            int month = Integer.parseInt(iField.substring(0,2));
290                            int day = Integer.parseInt(iField.substring(3,5));
291                            int year = Integer.parseInt(iField.substring(6,8));
292                            Calendar c = Calendar.getInstance(Locale.US);
293                            c.set(year,month-1,day,0,0,0);
294                            return c.getTime();
295                    }
296                    public boolean toBoolean() { return "Y".equalsIgnoreCase(iField) || "on".equalsIgnoreCase(iField) || "true".equalsIgnoreCase(iField) || "1".equalsIgnoreCase(iField); }
297            }
298            
299        /** An interface for filtering lines of a CSV file */
300            public static interface CSVFilter {
301                    public boolean match(CSVLine line);
302            }
303            
304            public static CSVFilter eq(String name, String value) {
305                    return (new CSVFilter() {
306                            String n,v;
307                            public boolean match(CSVLine line) {
308                                    return line.getField(n).equals(v);
309                            }
310                            private CSVFilter set(String n, String v) { this.n=n; this.v=v; return this; }
311                    }).set(name, value);
312            }
313            
314            public static CSVFilter and(CSVFilter first, CSVFilter second) {
315                    return (new CSVFilter() {
316                            CSVFilter a,b;
317                            public boolean match(CSVLine line) {
318                                    return a.match(line) && b.match(line);
319                            }
320                            private CSVFilter set(CSVFilter a, CSVFilter b) { this.a=a; this.b=b; return this; }
321                    }).set(first, second);
322            }
323    
324            public static CSVFilter or(CSVFilter first, CSVFilter second) {
325                    return (new CSVFilter() {
326                            CSVFilter a,b;
327                            public boolean match(CSVLine line) {
328                                    return a.match(line) || b.match(line);
329                            }
330                            private CSVFilter set(CSVFilter a, CSVFilter b) { this.a=a; this.b=b; return this; }
331                    }).set(first, second);
332            }
333    
334            public static CSVFilter not(CSVFilter filter) {
335                    return (new CSVFilter() {
336                            CSVFilter f;
337                            public boolean match(CSVLine line) {
338                                    return !f.match(line);
339                            }
340                            private CSVFilter set(CSVFilter f) { this.f=f; return this; }
341                    }).set(filter);
342            }
343    }