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 }