001package net.sf.cpsolver.ifs.util; 002 003import java.io.BufferedReader; 004import java.io.File; 005import java.io.FileReader; 006import java.io.FileWriter; 007import java.io.IOException; 008import java.io.PrintWriter; 009import java.io.Serializable; 010import java.util.ArrayList; 011import java.util.Calendar; 012import java.util.Collection; 013import java.util.Date; 014import java.util.HashMap; 015import java.util.Iterator; 016import java.util.List; 017import 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 Tomáš Müller<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 042public 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}