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.ArrayList;
011    import java.util.Calendar;
012    import java.util.Collection;
013    import java.util.Date;
014    import java.util.HashMap;
015    import java.util.Iterator;
016    import java.util.List;
017    import java.util.Locale;
018    
019    /**
020     * Support for CSV (comma separated) text files.
021     * 
022     * @version IFS 1.2 (Iterative Forward Search)<br>
023     *          Copyright (C) 2006 - 2010 Tomas Muller<br>
024     *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
025     *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
026     * <br>
027     *          This library is free software; you can redistribute it and/or modify
028     *          it under the terms of the GNU Lesser General Public License as
029     *          published by the Free Software Foundation; either version 3 of the
030     *          License, or (at your option) any later version. <br>
031     * <br>
032     *          This library is distributed in the hope that it will be useful, but
033     *          WITHOUT ANY WARRANTY; without even the implied warranty of
034     *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
035     *          Lesser General Public License for more details. <br>
036     * <br>
037     *          You should have received a copy of the GNU Lesser General Public
038     *          License along with this library; if not see
039     *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
040     */
041    
042    public class CSVFile implements Serializable {
043        private static final long serialVersionUID = 1L;
044        HashMap<String, Integer> iHeaderMap = null;
045        CSVLine iHeader = null;
046        List<CSVLine> iLines = null;
047        String iSeparator = ",";
048        String iQuotationMark = "\"";
049    
050        public CSVFile() {
051        }
052    
053        public CSVFile(File file) throws IOException {
054            load(file);
055        }
056    
057        public CSVFile(File file, String separator) throws IOException {
058            setSeparator(separator);
059            load(file);
060        }
061    
062        public CSVFile(File file, String separator, String quotationMark) throws IOException {
063            setSeparator(separator);
064            setQuotationMark(quotationMark);
065            load(file);
066        }
067    
068        public void setSeparator(String separator) {
069            iSeparator = separator;
070        }
071    
072        public String getSeparator() {
073            return iSeparator;
074        }
075    
076        public void setQuotationMark(String quotationMark) {
077            iQuotationMark = quotationMark;
078        }
079    
080        public String getQuotationMark() {
081            return iQuotationMark;
082        }
083    
084        public void load(File file) throws IOException {
085            BufferedReader reader = null;
086            try {
087                reader = new BufferedReader(new FileReader(file));
088                iHeader = new CSVLine(reader.readLine()); // read header
089                iHeaderMap = new HashMap<String, Integer>();
090                iLines = new ArrayList<CSVLine>();
091                int idx = 0;
092                for (Iterator<CSVField> i = iHeader.fields(); i.hasNext(); idx++) {
093                    CSVField field = i.next();
094                    iHeaderMap.put(field.toString(), idx);
095                }
096                String line = null;
097                while ((line = reader.readLine()) != null) {
098                    if (line.trim().length() == 0)
099                        continue;
100                    iLines.add(new CSVLine(line));
101                }
102            } finally {
103                if (reader != null)
104                    reader.close();
105            }
106        }
107    
108        public void save(File file) throws IOException {
109            PrintWriter writer = null;
110            try {
111                writer = new PrintWriter(new FileWriter(file));
112                if (iHeader != null)
113                    writer.println(iHeader.toString());
114    
115                if (iLines != null) {
116                    for (CSVLine line : iLines) {
117                        writer.println(line.toString());
118                    }
119                }
120    
121                writer.flush();
122            } finally {
123                if (writer != null)
124                    writer.close();
125            }
126        }
127    
128        public CSVLine getHeader() {
129            return iHeader;
130        }
131    
132        public void setHeader(CSVLine header) {
133            iHeader = header;
134        }
135    
136        public List<CSVLine> getLines() {
137            return iLines;
138        }
139    
140        public int size() {
141            return iLines.size();
142        }
143    
144        public boolean isEmpty() {
145            return iLines.isEmpty();
146        }
147    
148        public CSVLine getLine(int idx) {
149            return iLines.get(idx);
150        }
151    
152        public Iterator<CSVLine> lines() {
153            return iLines.iterator();
154        }
155    
156        public void addLine(CSVLine line) {
157            if (iLines == null)
158                iLines = new ArrayList<CSVLine>();
159            iLines.add(line);
160        }
161    
162        public void addLine(String line) {
163            if (iLines == null)
164                iLines = new ArrayList<CSVLine>();
165            iLines.add(new CSVLine(line));
166        }
167    
168        public List<CSVLine> filter(CSVFilter filter) {
169            List<CSVLine> ret = new ArrayList<CSVLine>();
170            for (CSVLine line : iLines) {
171                if (filter.match(line))
172                    ret.add(line);
173            }
174            return ret;
175        }
176    
177        public CSVLine addLine() {
178            CSVLine line = new CSVLine();
179            addLine(line);
180            return line;
181        }
182    
183        public CSVLine addLine(CSVField fields[]) {
184            CSVLine line = new CSVLine(fields);
185            addLine(line);
186            return line;
187        }
188    
189        public CSVLine addLine(Collection<CSVField> fields) {
190            CSVLine line = new CSVLine(fields);
191            addLine(line);
192            return line;
193        }
194    
195        public CSVLine setHeader(CSVField fields[]) {
196            CSVLine header = new CSVLine(fields);
197            setHeader(header);
198            return header;
199        }
200    
201        public CSVLine setHeader(Collection<CSVField> fields) {
202            CSVLine header = new CSVLine(fields);
203            setHeader(header);
204            return header;
205        }
206    
207        /** Representation of a line of a CSV file */
208        public class CSVLine implements Serializable {
209            private static final long serialVersionUID = 1L;
210            List<CSVField> iFields = new ArrayList<CSVField>(iHeader == null ? 10 : iHeader.size());
211    
212            public CSVLine(String line) {
213                int idx = 0;
214                int newIdx = 0;
215                int fromIdx = 0;
216                while ((newIdx = line.indexOf(iSeparator, fromIdx)) >= 0) {
217                    String field = line.substring(idx, newIdx);
218                    if (iQuotationMark != null && field.startsWith(iQuotationMark) && (!field.endsWith(iQuotationMark) || field.length() == 1)) {
219                        fromIdx = newIdx + iSeparator.length();
220                        continue;
221                    }
222                    iFields.add(new CSVField(field, iQuotationMark));
223                    idx = newIdx + iSeparator.length();
224                    fromIdx = idx;
225                }
226                iFields.add(new CSVField(line.substring(idx), iQuotationMark));
227            }
228    
229            public CSVLine() {
230            }
231    
232            public CSVLine(CSVField fields[]) {
233                for (int i = 0; i < fields.length; i++)
234                    iFields.add(fields[i]);
235            }
236    
237            public CSVLine(Collection<CSVField> fields) {
238                iFields.addAll(fields);
239            }
240    
241            public List<CSVField> getFields() {
242                return iFields;
243            }
244    
245            public int size() {
246                return iFields.size();
247            }
248    
249            public boolean isEmpty() {
250                return iFields.isEmpty();
251            }
252    
253            public CSVField getField(int idx) {
254                try {
255                    return iFields.get(idx);
256                } catch (ArrayIndexOutOfBoundsException e) {
257                    return null;
258                }
259            }
260    
261            public void setField(int idx, CSVField field) {
262                iFields.set(idx, field);
263            }
264    
265            public Iterator<CSVField> fields() {
266                return iFields.iterator();
267            }
268    
269            public CSVField getField(String name) {
270                Integer idx = iHeaderMap.get(name);
271                return (idx == null ? null : getField(idx.intValue()));
272            }
273    
274            public void setField(String name, CSVField field) {
275                Integer idx = iHeaderMap.get(name);
276                if (idx != null)
277                    setField(idx.intValue(), field);
278            }
279    
280            @Override
281            public String toString() {
282                StringBuffer sb = new StringBuffer();
283                for (Iterator<CSVField> i = iFields.iterator(); i.hasNext();) {
284                    CSVField field = i.next();
285                    if (field != null)
286                        sb.append((iQuotationMark == null ? "" : iQuotationMark) + field.toString()
287                                + (iQuotationMark == null ? "" : iQuotationMark));
288                    if (i.hasNext())
289                        sb.append(iSeparator);
290                }
291                return sb.toString();
292            }
293    
294            public void debug(int offset, PrintWriter out) {
295                int idx = 0;
296                for (Iterator<CSVField> i = iFields.iterator(); i.hasNext();) {
297                    CSVField field = i.next();
298                    if (field == null || field.toString().length() == 0)
299                        continue;
300                    for (int j = 0; j < offset; j++)
301                        out.print(" ");
302                    out.println(iHeader.getField(idx) + "=" + (iQuotationMark == null ? "" : iQuotationMark) + field
303                            + (iQuotationMark == null ? "" : iQuotationMark));
304                }
305            }
306        }
307    
308        /** Representation of a field of a CSV file */
309        public static class CSVField implements Serializable {
310            private static final long serialVersionUID = 1L;
311            String iField = null;
312    
313            public CSVField(String field, String quotationMark) {
314                field = field.trim();
315                if (quotationMark != null && field.startsWith(quotationMark) && field.endsWith(quotationMark))
316                    field = field.substring(1, field.length() - 1);
317                iField = field;
318            }
319    
320            public CSVField(Object field) {
321                set(field == null ? "" : field.toString());
322            }
323    
324            public CSVField(int field) {
325                set(field);
326            }
327    
328            public CSVField(boolean field) {
329                set(field);
330            }
331    
332            public CSVField(double field) {
333                set(field);
334            }
335    
336            public CSVField(long field) {
337                set(field);
338            }
339    
340            public CSVField(float field) {
341                set(field);
342            }
343    
344            public void set(Object value) {
345                iField = (value == null ? "" : value.toString());
346            }
347    
348            public void set(int value) {
349                iField = String.valueOf(value);
350            }
351    
352            public void set(boolean value) {
353                iField = (value ? "1" : "0");
354            }
355    
356            public void set(double value) {
357                iField = String.valueOf(value);
358            }
359    
360            public void set(long value) {
361                iField = String.valueOf(value);
362            }
363    
364            public void set(float value) {
365                iField = String.valueOf(value);
366            }
367    
368            @Override
369            public String toString() {
370                return (iField == null ? "" : iField);
371            }
372    
373            public boolean isEmpty() {
374                return (iField.length() == 0);
375            }
376    
377            public int toInt() {
378                return toInt(0);
379            }
380    
381            public int toInt(int defaultValue) {
382                try {
383                    return Integer.parseInt(iField);
384                } catch (NumberFormatException e) {
385                    return defaultValue;
386                }
387            }
388    
389            public long toLong() {
390                return toLong(0);
391            }
392    
393            public long toLong(long defaultValue) {
394                try {
395                    return Long.parseLong(iField);
396                } catch (NumberFormatException e) {
397                    return defaultValue;
398                }
399            }
400    
401            public double toDouble() {
402                return toDouble(0);
403            }
404    
405            public double toDouble(double defaultValue) {
406                try {
407                    return Double.parseDouble(iField);
408                } catch (NumberFormatException e) {
409                    return defaultValue;
410                }
411            }
412    
413            public Date toDate() {
414                int month = Integer.parseInt(iField.substring(0, 2));
415                int day = Integer.parseInt(iField.substring(3, 5));
416                int year = Integer.parseInt(iField.substring(6, 8));
417                Calendar c = Calendar.getInstance(Locale.US);
418                c.set(year, month - 1, day, 0, 0, 0);
419                return c.getTime();
420            }
421    
422            public boolean toBoolean() {
423                return "Y".equalsIgnoreCase(iField) || "on".equalsIgnoreCase(iField) || "true".equalsIgnoreCase(iField)
424                        || "1".equalsIgnoreCase(iField);
425            }
426        }
427    
428        /** An interface for filtering lines of a CSV file */
429        public static interface CSVFilter {
430            public boolean match(CSVLine line);
431        }
432    
433        public static CSVFilter eq(String name, String value) {
434            return (new CSVFilter() {
435                String n, v;
436    
437                @Override
438                public boolean match(CSVLine line) {
439                    return line.getField(n).equals(v);
440                }
441    
442                private CSVFilter set(String n, String v) {
443                    this.n = n;
444                    this.v = v;
445                    return this;
446                }
447            }).set(name, value);
448        }
449    
450        public static CSVFilter and(CSVFilter first, CSVFilter second) {
451            return (new CSVFilter() {
452                CSVFilter a, b;
453    
454                @Override
455                public boolean match(CSVLine line) {
456                    return a.match(line) && b.match(line);
457                }
458    
459                private CSVFilter set(CSVFilter a, CSVFilter b) {
460                    this.a = a;
461                    this.b = b;
462                    return this;
463                }
464            }).set(first, second);
465        }
466    
467        public static CSVFilter or(CSVFilter first, CSVFilter second) {
468            return (new CSVFilter() {
469                CSVFilter a, b;
470    
471                @Override
472                public boolean match(CSVLine line) {
473                    return a.match(line) || b.match(line);
474                }
475    
476                private CSVFilter set(CSVFilter a, CSVFilter b) {
477                    this.a = a;
478                    this.b = b;
479                    return this;
480                }
481            }).set(first, second);
482        }
483    
484        public static CSVFilter not(CSVFilter filter) {
485            return (new CSVFilter() {
486                CSVFilter f;
487    
488                @Override
489                public boolean match(CSVLine line) {
490                    return !f.match(line);
491                }
492    
493                private CSVFilter set(CSVFilter f) {
494                    this.f = f;
495                    return this;
496                }
497            }).set(filter);
498        }
499    }