1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.healthmarketscience.jackcess.util;
18
19 import com.healthmarketscience.jackcess.ColumnBuilder;
20 import com.healthmarketscience.jackcess.DataType;
21 import com.healthmarketscience.jackcess.Database;
22 import com.healthmarketscience.jackcess.Table;
23 import com.healthmarketscience.jackcess.TableBuilder;
24 import com.healthmarketscience.jackcess.impl.ByteUtil;
25 import com.healthmarketscience.jackcess.impl.DatabaseImpl;
26 import java.io.BufferedReader;
27 import java.io.EOFException;
28 import java.io.File;
29 import java.io.FileReader;
30 import java.io.IOException;
31 import java.sql.ResultSet;
32 import java.sql.ResultSetMetaData;
33 import java.sql.SQLException;
34 import java.util.ArrayList;
35 import java.util.List;
36 import java.util.regex.Matcher;
37 import java.util.regex.Pattern;
38
39
40
41
42
43
44
45
46
47
48 public class ImportUtil
49 {
50
51 private static final int COPY_TABLE_BATCH_SIZE = 200;
52
53
54 static final String LINE_SEPARATOR = System.getProperty("line.separator");
55
56 private ImportUtil() {}
57
58
59
60
61
62
63
64
65 public static List<ColumnBuilder> toColumns(ResultSetMetaData md)
66 throws SQLException, IOException
67 {
68 List<ColumnBuilder> columns = new ArrayList<ColumnBuilder>();
69 for (int i = 1; i <= md.getColumnCount(); i++) {
70 ColumnBuilderlumnBuilder.html#ColumnBuilder">ColumnBuilder column = new ColumnBuilder(md.getColumnLabel(i))
71 .escapeName();
72 int lengthInUnits = md.getColumnDisplaySize(i);
73 column.setSQLType(md.getColumnType(i), lengthInUnits);
74 DataType type = column.getType();
75
76
77
78 if(type.isTrueVariableLength() && !type.isLongValue()) {
79 column.setLengthInUnits((short)lengthInUnits);
80 }
81 if(type.getHasScalePrecision()) {
82 int scale = md.getScale(i);
83 int precision = md.getPrecision(i);
84 if(type.isValidScale(scale)) {
85 column.setScale((byte)scale);
86 }
87 if(type.isValidPrecision(precision)) {
88 column.setPrecision((byte)precision);
89 }
90 }
91 columns.add(column);
92 }
93 return columns;
94 }
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110 public static String importResultSet(ResultSet source, Database db,
111 String name)
112 throws SQLException, IOException
113 {
114 return importResultSet(source, db, name, SimpleImportFilter.INSTANCE);
115 }
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132 public static String importResultSet(ResultSet source, Database db,
133 String name, ImportFilter filter)
134 throws SQLException, IOException
135 {
136 return importResultSet(source, db, name, filter, false);
137 }
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154 public static String importResultSet(ResultSet source, Database db,
155 String name, ImportFilter filter,
156 boolean useExistingTable)
157 throws SQLException, IOException
158 {
159 ResultSetMetaData md = source.getMetaData();
160
161 name = TableBuilder.escapeIdentifier(name);
162 Table table = null;
163 if(!useExistingTable || ((table = db.getTable(name)) == null)) {
164 List<ColumnBuilder> columns = toColumns(md);
165 table = createUniqueTable(db, name, columns, md, filter);
166 }
167
168 List<Object[]> rows = new ArrayList<Object[]>(COPY_TABLE_BATCH_SIZE);
169 int numColumns = md.getColumnCount();
170
171 while (source.next()) {
172 Object[] row = new Object[numColumns];
173 for (int i = 0; i < row.length; i++) {
174 row[i] = source.getObject(i + 1);
175 }
176 row = filter.filterRow(row);
177 if(row == null) {
178 continue;
179 }
180 rows.add(row);
181 if (rows.size() == COPY_TABLE_BATCH_SIZE) {
182 table.addRows(rows);
183 rows.clear();
184 }
185 }
186 if (rows.size() > 0) {
187 table.addRows(rows);
188 }
189
190 return table.getName();
191 }
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208 public static String importFile(File f, Database db, String name,
209 String delim)
210 throws IOException
211 {
212 return importFile(f, db, name, delim, SimpleImportFilter.INSTANCE);
213 }
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231 public static String importFile(File f, Database db, String name,
232 String delim, ImportFilter filter)
233 throws IOException
234 {
235 return importFile(f, db, name, delim, ExportUtil.DEFAULT_QUOTE_CHAR,
236 filter, false);
237 }
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259 public static String importFile(File f, Database db, String name,
260 String delim, char quote,
261 ImportFilter filter,
262 boolean useExistingTable)
263 throws IOException
264 {
265 return importFile(f, db, name, delim, quote, filter, useExistingTable, true);
266 }
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289 public static String importFile(File f, Database db, String name,
290 String delim, char quote,
291 ImportFilter filter,
292 boolean useExistingTable,
293 boolean header)
294 throws IOException
295 {
296 BufferedReader in = null;
297 try {
298 in = new BufferedReader(new FileReader(f));
299 return importReader(in, db, name, delim, quote, filter,
300 useExistingTable, header);
301 } finally {
302 ByteUtil.closeQuietly(in);
303 }
304 }
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321 public static String importReader(BufferedReader in, Database db,
322 String name, String delim)
323 throws IOException
324 {
325 return importReader(in, db, name, delim, SimpleImportFilter.INSTANCE);
326 }
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344 public static String importReader(BufferedReader in, Database db,
345 String name, String delim,
346 ImportFilter filter)
347 throws IOException
348 {
349 return importReader(in, db, name, delim, filter, false);
350 }
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371 public static String importReader(BufferedReader in, Database db,
372 String name, String delim,
373 ImportFilter filter,
374 boolean useExistingTable)
375 throws IOException
376 {
377 return importReader(in, db, name, delim, ExportUtil.DEFAULT_QUOTE_CHAR,
378 filter, useExistingTable);
379 }
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401 public static String importReader(BufferedReader in, Database db,
402 String name, String delim, char quote,
403 ImportFilter filter,
404 boolean useExistingTable)
405 throws IOException
406 {
407 return importReader(in, db, name, delim, quote, filter, useExistingTable,
408 true);
409 }
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430 public static String importReader(BufferedReader in, Database db,
431 String name, String delim, char quote,
432 ImportFilter filter,
433 boolean useExistingTable, boolean header)
434 throws IOException
435 {
436 String line = in.readLine();
437 if(DatabaseImpl.isBlank(line)) {
438 return null;
439 }
440
441 Pattern delimPat = Pattern.compile(delim);
442
443 try {
444 name = TableBuilder.escapeIdentifier(name);
445 Table table = null;
446 if(!useExistingTable || ((table = db.getTable(name)) == null)) {
447
448 List<ColumnBuilder> columns = new ArrayList<ColumnBuilder>();
449 Object[] columnNames = splitLine(line, delimPat, quote, in, 0);
450
451 for (int i = 0; i < columnNames.length; i++) {
452 columns.add(new ColumnBuilder((String)columnNames[i], DataType.TEXT)
453 .escapeName()
454 .setLength((short)DataType.TEXT.getMaxSize())
455 .toColumn());
456 }
457
458 table = createUniqueTable(db, name, columns, null, filter);
459
460
461 header = true;
462 }
463
464 List<Object[]> rows = new ArrayList<Object[]>(COPY_TABLE_BATCH_SIZE);
465 int numColumns = table.getColumnCount();
466
467 if(!header) {
468
469 Object[] data = splitLine(line, delimPat, quote, in, numColumns);
470 data = filter.filterRow(data);
471 if(data != null) {
472 rows.add(data);
473 }
474 }
475
476 while ((line = in.readLine()) != null)
477 {
478 Object[] data = splitLine(line, delimPat, quote, in, numColumns);
479 data = filter.filterRow(data);
480 if(data == null) {
481 continue;
482 }
483 rows.add(data);
484 if (rows.size() == COPY_TABLE_BATCH_SIZE) {
485 table.addRows(rows);
486 rows.clear();
487 }
488 }
489 if (rows.size() > 0) {
490 table.addRows(rows);
491 }
492
493 return table.getName();
494
495 } catch(SQLException e) {
496 throw new IOException(e.getMessage(), e);
497 }
498 }
499
500
501
502
503
504 private static Object[] splitLine(String line, Pattern delim, char quote,
505 BufferedReader in, int numColumns)
506 throws IOException
507 {
508 List<String> tokens = new ArrayList<String>();
509 StringBuilder sb = new StringBuilder();
510 Matcher m = delim.matcher(line);
511 int idx = 0;
512
513 while(idx < line.length()) {
514
515 if(line.charAt(idx) == quote) {
516
517
518 sb.setLength(0);
519 ++idx;
520 while(true) {
521
522 int endIdx = line.indexOf(quote, idx);
523
524 if(endIdx >= 0) {
525
526 sb.append(line, idx, endIdx);
527 ++endIdx;
528 if((endIdx < line.length()) && (line.charAt(endIdx) == quote)) {
529
530
531 sb.append(quote);
532
533 idx = endIdx + 1;
534
535 } else {
536
537
538 idx = endIdx;
539 break;
540 }
541
542 } else {
543
544
545 sb.append(line, idx, line.length());
546 sb.append(LINE_SEPARATOR);
547
548 idx = 0;
549 line = in.readLine();
550 if(line == null) {
551 throw new EOFException("Missing end of quoted value " + sb);
552 }
553 }
554 }
555
556 tokens.add(sb.toString());
557
558
559 idx = (m.find(idx) ? m.end() : line.length());
560
561 } else if(m.find(idx)) {
562
563
564 tokens.add(line.substring(idx, m.start()));
565 idx = m.end();
566
567 } else {
568
569
570 tokens.add(line.substring(idx));
571 idx = line.length();
572 }
573 }
574
575 return tokens.toArray(new Object[Math.max(tokens.size(), numColumns)]);
576 }
577
578
579
580
581 private static Table createUniqueTable(Database db, String name,
582 List<ColumnBuilder> columns,
583 ResultSetMetaData md,
584 ImportFilter filter)
585 throws IOException, SQLException
586 {
587
588 String baseName = name;
589 int counter = 2;
590 while(db.getTable(name) != null) {
591 name = baseName + (counter++);
592 }
593
594 return new TableBuilder(name)
595 .addColumns(filter.filterColumns(columns, md))
596 .toTable(db);
597 }
598
599
600
601
602 public static class Builder
603 {
604 private Database _db;
605 private String _tableName;
606 private String _delim = ExportUtil.DEFAULT_DELIMITER;
607 private char _quote = ExportUtil.DEFAULT_QUOTE_CHAR;
608 private ImportFilter _filter = SimpleImportFilter.INSTANCE;
609 private boolean _useExistingTable;
610 private boolean _header = true;
611
612 public Builder(Database db) {
613 this(db, null);
614 }
615
616 public Builder(Database db, String tableName) {
617 _db = db;
618 _tableName = tableName;
619 }
620
621 public Builder setDatabase(Database db) {
622 _db = db;
623 return this;
624 }
625
626 public Builder setTableName(String tableName) {
627 _tableName = tableName;
628 return this;
629 }
630
631 public Builder setDelimiter(String delim) {
632 _delim = delim;
633 return this;
634 }
635
636 public Builder setQuote(char quote) {
637 _quote = quote;
638 return this;
639 }
640
641 public Builder setFilter(ImportFilter filter) {
642 _filter = filter;
643 return this;
644 }
645
646 public Builder setUseExistingTable(boolean useExistingTable) {
647 _useExistingTable = useExistingTable;
648 return this;
649 }
650
651 public Builder setHeader(boolean header) {
652 _header = header;
653 return this;
654 }
655
656
657
658
659 public String importResultSet(ResultSet source)
660 throws SQLException, IOException
661 {
662 return ImportUtil.importResultSet(source, _db, _tableName, _filter,
663 _useExistingTable);
664 }
665
666
667
668
669 public String importFile(File f) throws IOException {
670 return ImportUtil.importFile(f, _db, _tableName, _delim, _quote, _filter,
671 _useExistingTable, _header);
672 }
673
674
675
676
677 public String importReader(BufferedReader reader) throws IOException {
678 return ImportUtil.importReader(reader, _db, _tableName, _delim, _quote,
679 _filter, _useExistingTable, _header);
680 }
681 }
682
683 }