You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ExportUtil.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. /*
  2. Copyright (c) 2007 Health Market Science, Inc.
  3. This library is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU Lesser General Public
  5. License as published by the Free Software Foundation; either
  6. version 2.1 of the License, or (at your option) any later version.
  7. This library is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  10. Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public
  12. License along with this library; if not, write to the Free Software
  13. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  14. USA
  15. You can contact Health Market Science at info@healthmarketscience.com
  16. or at the following address:
  17. Health Market Science
  18. 2700 Horizon Drive
  19. Suite 200
  20. King of Prussia, PA 19406
  21. */
  22. package com.healthmarketscience.jackcess;
  23. import java.io.BufferedWriter;
  24. import java.io.File;
  25. import java.io.FileWriter;
  26. import java.io.IOException;
  27. import java.util.ArrayList;
  28. import java.util.Collection;
  29. import java.util.HashSet;
  30. import java.util.Iterator;
  31. import java.util.List;
  32. import java.util.Map;
  33. import java.util.regex.Pattern;
  34. import org.apache.commons.logging.Log;
  35. import org.apache.commons.logging.LogFactory;
  36. /**
  37. *
  38. * @author Frank Gerbig
  39. */
  40. public class ExportUtil {
  41. private static final Log LOG = LogFactory.getLog(ExportUtil.class);
  42. public static final String DEFAULT_DELIMITER = ",";
  43. public static final char DEFAULT_QUOTE_CHAR = '"';
  44. public static final String DEFAULT_FILE_EXT = "csv";
  45. private ExportUtil() {
  46. }
  47. /**
  48. * Copy all tables into new delimited text files <br>
  49. * Equivalent to: {@code exportAll(db, dir, "csv");}
  50. *
  51. * @param db
  52. * Database the table to export belongs to
  53. * @param dir
  54. * The directory where the new files will be created
  55. *
  56. * @see #exportAll(Database,File,String)
  57. * @see Builder
  58. */
  59. public static void exportAll(Database db, File dir)
  60. throws IOException {
  61. exportAll(db, dir, DEFAULT_FILE_EXT);
  62. }
  63. /**
  64. * Copy all tables into new delimited text files <br>
  65. * Equivalent to: {@code exportFile(db, name, f, false, null, '"',
  66. * SimpleExportFilter.INSTANCE);}
  67. *
  68. * @param db
  69. * Database the table to export belongs to
  70. * @param dir
  71. * The directory where the new files will be created
  72. * @param ext
  73. * The file extension of the new files
  74. *
  75. * @see #exportFile(Database,String,File,boolean,String,char,ExportFilter)
  76. * @see Builder
  77. */
  78. public static void exportAll(Database db, File dir,
  79. String ext) throws IOException {
  80. for (String tableName : db.getTableNames()) {
  81. exportFile(db, tableName, new File(dir, tableName + "." + ext), false,
  82. DEFAULT_DELIMITER, DEFAULT_QUOTE_CHAR, SimpleExportFilter.INSTANCE);
  83. }
  84. }
  85. /**
  86. * Copy all tables into new delimited text files <br>
  87. * Equivalent to: {@code exportFile(db, name, f, false, null, '"',
  88. * SimpleExportFilter.INSTANCE);}
  89. *
  90. * @param db
  91. * Database the table to export belongs to
  92. * @param dir
  93. * The directory where the new files will be created
  94. * @param ext
  95. * The file extension of the new files
  96. * @param header
  97. * If <code>true</code> the first line contains the column names
  98. *
  99. * @see #exportFile(Database,String,File,boolean,String,char,ExportFilter)
  100. * @see Builder
  101. */
  102. public static void exportAll(Database db, File dir,
  103. String ext, boolean header)
  104. throws IOException {
  105. for (String tableName : db.getTableNames()) {
  106. exportFile(db, tableName, new File(dir, tableName + "." + ext), header,
  107. DEFAULT_DELIMITER, DEFAULT_QUOTE_CHAR, SimpleExportFilter.INSTANCE);
  108. }
  109. }
  110. /**
  111. * Copy all tables into new delimited text files <br>
  112. * Equivalent to: {@code exportFile(db, name, f, false, null, '"',
  113. * SimpleExportFilter.INSTANCE);}
  114. *
  115. * @param db
  116. * Database the table to export belongs to
  117. * @param dir
  118. * The directory where the new files will be created
  119. * @param ext
  120. * The file extension of the new files
  121. * @param header
  122. * If <code>true</code> the first line contains the column names
  123. * @param delim
  124. * The column delimiter, <code>null</code> for default (comma)
  125. * @param quote
  126. * The quote character
  127. * @param filter
  128. * valid export filter
  129. *
  130. * @see #exportFile(Database,String,File,boolean,String,char,ExportFilter)
  131. * @see Builder
  132. */
  133. public static void exportAll(Database db, File dir,
  134. String ext, boolean header, String delim,
  135. char quote, ExportFilter filter)
  136. throws IOException {
  137. for (String tableName : db.getTableNames()) {
  138. exportFile(db, tableName, new File(dir, tableName + "." + ext), header,
  139. delim, quote, filter);
  140. }
  141. }
  142. /**
  143. * Copy a table into a new delimited text file <br>
  144. * Equivalent to: {@code exportFile(db, name, f, false, null, '"',
  145. * SimpleExportFilter.INSTANCE);}
  146. *
  147. * @param db
  148. * Database the table to export belongs to
  149. * @param tableName
  150. * Name of the table to export
  151. * @param f
  152. * New file to create
  153. *
  154. * @see #exportFile(Database,String,File,boolean,String,char,ExportFilter)
  155. * @see Builder
  156. */
  157. public static void exportFile(Database db, String tableName,
  158. File f) throws IOException {
  159. exportFile(db, tableName, f, false, DEFAULT_DELIMITER, DEFAULT_QUOTE_CHAR,
  160. SimpleExportFilter.INSTANCE);
  161. }
  162. /**
  163. * Copy a table into a new delimited text file <br>
  164. * Nearly equivalent to: {@code exportWriter(db, name, new BufferedWriter(f),
  165. * header, delim, quote, filter);}
  166. *
  167. * @param db
  168. * Database the table to export belongs to
  169. * @param tableName
  170. * Name of the table to export
  171. * @param f
  172. * New file to create
  173. * @param header
  174. * If <code>true</code> the first line contains the column names
  175. * @param delim
  176. * The column delimiter, <code>null</code> for default (comma)
  177. * @param quote
  178. * The quote character
  179. * @param filter
  180. * valid export filter
  181. *
  182. * @see #exportWriter(Database,String,BufferedWriter,boolean,String,char,ExportFilter)
  183. * @see Builder
  184. */
  185. public static void exportFile(Database db, String tableName,
  186. File f, boolean header, String delim, char quote,
  187. ExportFilter filter) throws IOException {
  188. BufferedWriter out = null;
  189. try {
  190. out = new BufferedWriter(new FileWriter(f));
  191. exportWriter(db, tableName, out, header, delim, quote, filter);
  192. out.close();
  193. } finally {
  194. if (out != null) {
  195. try {
  196. out.close();
  197. } catch (Exception ex) {
  198. LOG.warn("Could not close file " + f.getAbsolutePath(), ex);
  199. }
  200. }
  201. }
  202. }
  203. /**
  204. * Copy a table in this database into a new delimited text file <br>
  205. * Equivalent to: {@code exportWriter(db, name, out, false, null, '"',
  206. * SimpleExportFilter.INSTANCE);}
  207. *
  208. * @param db
  209. * Database the table to export belongs to
  210. * @param tableName
  211. * Name of the table to export
  212. * @param out
  213. * Writer to export to
  214. *
  215. * @see #exportWriter(Database,String,BufferedWriter,boolean,String,char,ExportFilter)
  216. * @see Builder
  217. */
  218. public static void exportWriter(Database db, String tableName,
  219. BufferedWriter out) throws IOException {
  220. exportWriter(db, tableName, out, false, DEFAULT_DELIMITER,
  221. DEFAULT_QUOTE_CHAR, SimpleExportFilter.INSTANCE);
  222. }
  223. /**
  224. * Copy a table in this database into a new delimited text file. <br>
  225. * Equivalent to: {@code exportWriter(Cursor.createCursor(db.getTable(tableName)), out, header, delim, quote, filter);}
  226. *
  227. * @param db
  228. * Database the table to export belongs to
  229. * @param tableName
  230. * Name of the table to export
  231. * @param out
  232. * Writer to export to
  233. * @param header
  234. * If <code>true</code> the first line contains the column names
  235. * @param delim
  236. * The column delimiter, <code>null</code> for default (comma)
  237. * @param quote
  238. * The quote character
  239. * @param filter
  240. * valid export filter
  241. *
  242. * @see #exportWriter(Cursor,BufferedWriter,boolean,String,char,ExportFilter)
  243. * @see Builder
  244. */
  245. public static void exportWriter(Database db, String tableName,
  246. BufferedWriter out, boolean header, String delim,
  247. char quote, ExportFilter filter)
  248. throws IOException
  249. {
  250. exportWriter(Cursor.createCursor(db.getTable(tableName)), out, header,
  251. delim, quote, filter);
  252. }
  253. /**
  254. * Copy a table in this database into a new delimited text file.
  255. *
  256. * @param cursor
  257. * Cursor to export
  258. * @param out
  259. * Writer to export to
  260. * @param header
  261. * If <code>true</code> the first line contains the column names
  262. * @param delim
  263. * The column delimiter, <code>null</code> for default (comma)
  264. * @param quote
  265. * The quote character
  266. * @param filter
  267. * valid export filter
  268. *
  269. * @see Builder
  270. */
  271. public static void exportWriter(Cursor cursor,
  272. BufferedWriter out, boolean header, String delim,
  273. char quote, ExportFilter filter)
  274. throws IOException
  275. {
  276. String delimiter = (delim == null) ? DEFAULT_DELIMITER : delim;
  277. // create pattern which will indicate whether or not a value needs to be
  278. // quoted or not (contains delimiter, separator, or newline)
  279. Pattern needsQuotePattern = Pattern.compile(
  280. "(?:" + Pattern.quote(delimiter) + ")|(?:" +
  281. Pattern.quote("" + quote) + ")|(?:[\n\r])");
  282. List<Column> origCols = cursor.getTable().getColumns();
  283. List<Column> columns = new ArrayList<Column>(origCols);
  284. columns = filter.filterColumns(columns);
  285. Collection<String> columnNames = null;
  286. if(!origCols.equals(columns)) {
  287. // columns have been filtered
  288. columnNames = new HashSet<String>();
  289. for (Column c : columns) {
  290. columnNames.add(c.getName());
  291. }
  292. }
  293. // print the header row (if desired)
  294. if (header) {
  295. for (Iterator<Column> iter = columns.iterator(); iter.hasNext();) {
  296. writeValue(out, iter.next().getName(), quote, needsQuotePattern);
  297. if (iter.hasNext()) {
  298. out.write(delimiter);
  299. }
  300. }
  301. out.newLine();
  302. }
  303. // print the data rows
  304. Map<String, Object> row;
  305. Object[] unfilteredRowData = new Object[columns.size()];
  306. while ((row = cursor.getNextRow(columnNames)) != null) {
  307. // fill raw row data in array
  308. for (int i = 0; i < columns.size(); i++) {
  309. unfilteredRowData[i] = columns.get(i).getRowValue(row);
  310. }
  311. // apply filter
  312. Object[] rowData = filter.filterRow(unfilteredRowData);
  313. if(rowData == null) {
  314. continue;
  315. }
  316. // print row
  317. for (int i = 0; i < columns.size(); i++) {
  318. Object obj = rowData[i];
  319. if(obj != null) {
  320. String value = null;
  321. if(obj instanceof byte[]) {
  322. value = ByteUtil.toHexString((byte[])obj);
  323. } else {
  324. value = String.valueOf(rowData[i]);
  325. }
  326. writeValue(out, value, quote, needsQuotePattern);
  327. }
  328. if (i < columns.size() - 1) {
  329. out.write(delimiter);
  330. }
  331. }
  332. out.newLine();
  333. }
  334. out.flush();
  335. }
  336. private static void writeValue(BufferedWriter out, String value, char quote,
  337. Pattern needsQuotePattern)
  338. throws IOException
  339. {
  340. if(!needsQuotePattern.matcher(value).find()) {
  341. // no quotes necessary
  342. out.write(value);
  343. return;
  344. }
  345. // wrap the value in quotes and handle internal quotes
  346. out.write(quote);
  347. for (int i = 0; i < value.length(); ++i) {
  348. char c = value.charAt(i);
  349. if (c == quote) {
  350. out.write(quote);
  351. }
  352. out.write(c);
  353. }
  354. out.write(quote);
  355. }
  356. /**
  357. * Builder which simplifies configuration of an export operation.
  358. */
  359. public static class Builder
  360. {
  361. private Database _db;
  362. private String _tableName;
  363. private String _ext = DEFAULT_FILE_EXT;
  364. private Cursor _cursor;
  365. private String _delim = DEFAULT_DELIMITER;
  366. private char _quote = DEFAULT_QUOTE_CHAR;
  367. private ExportFilter _filter = SimpleExportFilter.INSTANCE;
  368. private boolean _header;
  369. public Builder(Database db) {
  370. this(db, null);
  371. }
  372. public Builder(Database db, String tableName) {
  373. _db = db;
  374. _tableName = tableName;
  375. }
  376. public Builder(Cursor cursor) {
  377. _cursor = cursor;
  378. }
  379. public Builder setDatabase(Database db) {
  380. _db = db;
  381. return this;
  382. }
  383. public Builder setTableName(String tableName) {
  384. _tableName = tableName;
  385. return this;
  386. }
  387. public Builder setCursor(Cursor cursor) {
  388. _cursor = cursor;
  389. return this;
  390. }
  391. public Builder setDelimiter(String delim) {
  392. _delim = delim;
  393. return this;
  394. }
  395. public Builder setQuote(char quote) {
  396. _quote = quote;
  397. return this;
  398. }
  399. public Builder setFilter(ExportFilter filter) {
  400. _filter = filter;
  401. return this;
  402. }
  403. public Builder setHeader(boolean header) {
  404. _header = header;
  405. return this;
  406. }
  407. public Builder setFileNameExtension(String ext) {
  408. _ext = ext;
  409. return this;
  410. }
  411. /**
  412. * @see ExportUtil#exportAll(Database,File,String,boolean,String,char,ExportFilter)
  413. */
  414. public void exportAll(File dir) throws IOException {
  415. ExportUtil.exportAll(_db, dir, _ext, _header, _delim, _quote, _filter);
  416. }
  417. /**
  418. * @see ExportUtil#exportFile(Database,String,File,boolean,String,char,ExportFilter)
  419. */
  420. public void exportFile(File f) throws IOException {
  421. ExportUtil.exportFile(_db, _tableName, f, _header, _delim, _quote,
  422. _filter);
  423. }
  424. /**
  425. * @see ExportUtil#exportWriter(Database,String,BufferedWriter,boolean,String,char,ExportFilter)
  426. * @see ExportUtil#exportWriter(Cursor,BufferedWriter,boolean,String,char,ExportFilter)
  427. */
  428. public void exportWriter(BufferedWriter writer) throws IOException {
  429. if(_cursor != null) {
  430. ExportUtil.exportWriter(_cursor, writer, _header, _delim,
  431. _quote, _filter);
  432. } else {
  433. ExportUtil.exportWriter(_db, _tableName, writer, _header, _delim,
  434. _quote, _filter);
  435. }
  436. }
  437. }
  438. }