001: /*
002: * XmlDataFileParser.java
003: *
004: * This file is part of SQL Workbench/J, http://www.sql-workbench.net
005: *
006: * Copyright 2002-2008, Thomas Kellerer
007: * No part of this code maybe reused without the permission of the author
008: *
009: * To contact the author please send an email to: support@sql-workbench.net
010: *
011: */
012: package workbench.db.importer;
013:
014: import java.io.File;
015: import java.io.IOException;
016: import java.io.Reader;
017: import java.sql.SQLException;
018: import java.sql.Types;
019: import java.util.ArrayList;
020: import java.util.Collections;
021: import java.util.Iterator;
022: import java.util.LinkedList;
023: import java.util.List;
024: import javax.xml.parsers.SAXParser;
025: import javax.xml.parsers.SAXParserFactory;
026: import org.xml.sax.Attributes;
027: import org.xml.sax.InputSource;
028: import org.xml.sax.SAXException;
029: import org.xml.sax.SAXParseException;
030: import org.xml.sax.helpers.DefaultHandler;
031: import workbench.db.ColumnIdentifier;
032: import workbench.db.TableIdentifier;
033: import workbench.db.WbConnection;
034: import workbench.db.exporter.XmlRowDataConverter;
035: import workbench.db.importer.modifier.ImportValueModifier;
036: import workbench.interfaces.JobErrorHandler;
037: import workbench.resource.ResourceMgr;
038: import workbench.util.ExceptionUtil;
039: import workbench.interfaces.ImportFileParser;
040: import workbench.log.LogMgr;
041: import workbench.util.FileUtil;
042: import workbench.util.MessageBuffer;
043: import workbench.util.SqlUtil;
044: import workbench.util.StringUtil;
045: import workbench.util.ValueConverter;
046: import workbench.util.WbFile;
047: import workbench.util.WbStringTokenizer;
048:
049: /**
050: *
051: * @author support@sql-workbench.net
052: */
053: public class XmlDataFileParser extends DefaultHandler implements
054: RowDataProducer, ImportFileParser {
055: private String sourceDirectory;
056: private File inputFile;
057: private String tableName;
058: private String tableNameFromFile;
059:
060: private int currentRowNumber = 1;
061: private int colCount;
062: private int realColCount;
063:
064: private List<ColumnIdentifier> columnsToImport;
065: private ColumnIdentifier[] columns;
066: private String encoding = "UTF-8";
067:
068: private Object[] currentRow;
069: private RowDataReceiver receiver;
070: private boolean ignoreCurrentRow = false;
071: private boolean abortOnError = false;
072: private boolean checkDependencies = false;
073: private boolean[] warningAdded;
074: private JobErrorHandler errorHandler;
075: private boolean verboseFormat = true;
076: private boolean formatKnown = false;
077: private String missingColumn;
078: private MessageBuffer messages;
079: private String extensionToUse;
080:
081: private int currentColIndex = 0;
082: private int realColIndex = 0;
083: private long columnLongValue = 0;
084: private String columnDataFile = null;
085: private boolean isNull = false;
086: private StringBuilder chars;
087: private boolean keepRunning;
088: private boolean regularStop;
089: private String rowTag = XmlRowDataConverter.LONG_ROW_TAG;
090: private String columnTag = XmlRowDataConverter.LONG_COLUMN_TAG;
091:
092: private boolean hasErrors = false;
093: private boolean hasWarnings = false;
094:
095: private SAXParser saxParser;
096: private ImportFileHandler fileHandler = new ImportFileHandler();
097: private WbConnection dbConn;
098:
099: private ValueConverter converter = new ValueConverter();
100: private ImportValueModifier valueModifier;
101:
102: public XmlDataFileParser() {
103: SAXParserFactory factory = SAXParserFactory.newInstance();
104: factory.setValidating(false);
105: try {
106: saxParser = factory.newSAXParser();
107: } catch (Exception e) {
108: // should not happen!
109: LogMgr.logError("XmlDataFileParser.<init>",
110: "Error creating XML parser", e);
111: }
112: }
113:
114: public XmlDataFileParser(File inputFile) {
115: this ();
116: this .inputFile = inputFile;
117: }
118:
119: public void setValueModifier(ImportValueModifier mod) {
120: this .valueModifier = mod;
121: }
122:
123: public ImportFileHandler getFileHandler() {
124: return this .fileHandler;
125: }
126:
127: public String getColumns() {
128: return StringUtil
129: .listToString(this .columnsToImport, ',', false);
130: }
131:
132: public String getLastRecord() {
133: return null;
134: }
135:
136: public void setCheckDependencies(boolean flag) {
137: this .checkDependencies = flag;
138: }
139:
140: public boolean hasErrors() {
141: return this .hasErrors;
142: }
143:
144: public boolean hasWarnings() {
145: if (this .hasWarnings)
146: return true;
147: if (this .warningAdded == null)
148: return false;
149: for (boolean b : warningAdded) {
150: if (b)
151: return true;
152: }
153: return false;
154: }
155:
156: public void setValueConverter(ValueConverter convert) {
157: this .converter = convert;
158: }
159:
160: public void setColumns(String columnList) throws SQLException {
161: if (columnList != null && columnList.trim().length() > 0) {
162: WbStringTokenizer tok = new WbStringTokenizer(columnList,
163: ",");
164: this .columnsToImport = new ArrayList<ColumnIdentifier>();
165: while (tok.hasMoreTokens()) {
166: String col = tok.nextToken();
167: if (col == null)
168: continue;
169: col = col.trim();
170: if (col.length() == 0)
171: continue;
172: ColumnIdentifier ci = new ColumnIdentifier(col);
173: this .columnsToImport.add(ci);
174: }
175: } else {
176: this .columnsToImport = null;
177: }
178: checkImportColumns();
179: }
180:
181: /** Define the columns to be imported
182: */
183: public void setColumns(List<ColumnIdentifier> cols)
184: throws SQLException {
185: if (cols != null && cols.size() > 0) {
186: this .columnsToImport = new ArrayList<ColumnIdentifier>(cols
187: .size());
188: Iterator<ColumnIdentifier> itr = cols.iterator();
189: while (itr.hasNext()) {
190: ColumnIdentifier id = itr.next();
191: if (!id.getColumnName().equals(
192: RowDataProducer.SKIP_INDICATOR)) {
193: this .columnsToImport.add(id);
194: }
195: }
196: } else {
197: this .columnsToImport = null;
198: }
199: checkImportColumns();
200: }
201:
202: public void setConnection(WbConnection conn) {
203: this .dbConn = conn;
204: }
205:
206: /**
207: * Check if all columns defined for the import (through the table definition
208: * as part of the XML file, or passed by the user on the command line) are
209: * actually available in the target table.
210: * For this all columns of the target table are retrieved from the database,
211: * and each column that has been defined through setColumns() is checked
212: * whether it exists there. Columns that are not found are dropped from
213: * the list of import columns
214: * If continueOnError == true, a warning is added to the messages. Otherwise
215: * an Exception is thrown.
216: */
217: public void checkTargetColumns() throws SQLException {
218: if (this .dbConn == null)
219: return;
220: if (this .columns == null)
221: return;
222: TableIdentifier tbl = new TableIdentifier(
223: this .tableName == null ? this .tableNameFromFile
224: : this .tableName);
225: if (!this .dbConn.getMetadata().tableExists(tbl)) {
226: String msg = ResourceMgr.getFormattedString(
227: "ErrImportTableNotFound", tbl.getTableName());
228: this .messages.append(msg);
229: this .messages.appendNewLine();
230: throw new SQLException("Table '" + tbl.getTableName()
231: + "' not found!");
232: }
233: List<ColumnIdentifier> tableCols = this .dbConn.getMetadata()
234: .getTableColumns(tbl);
235: List<ColumnIdentifier> validCols = new LinkedList<ColumnIdentifier>();
236:
237: for (int colIndex = 0; colIndex < this .columns.length; colIndex++) {
238: int i = tableCols.indexOf(this .columns[colIndex]);
239:
240: if (i != -1) {
241: // Use the column definition retrieved from the database
242: // to make sure we are using the correct data types when converting the input (String) values
243: // this is also important to get quoting of column names
244: // with special characters correctly (as this is handled by DbMetadata already
245: // but the columns retrieved from the XML file are not quoted correctly)
246: ColumnIdentifier tc = tableCols.get(i);
247: this .columns[colIndex] = tc;
248: validCols.add(tc);
249: } else {
250: String errorColumn = (this .columns[colIndex] != null ? this .columns[colIndex]
251: .getColumnName()
252: : "n/a");
253: String msg = ResourceMgr
254: .getString("ErrImportColumnNotFound");
255: msg = StringUtil.replace(msg, "%column%", errorColumn);
256: msg = StringUtil.replace(msg, "%table%", tbl
257: .getTableExpression());
258: this .messages.append(msg);
259: this .messages.appendNewLine();
260: if (this .abortOnError) {
261: this .hasErrors = true;
262: throw new SQLException("Column " + errorColumn
263: + " not found in target table");
264: } else {
265: this .hasWarnings = true;
266: LogMgr.logWarning(
267: "XmlDataFileParser.checkTargetColumns()",
268: msg);
269: }
270: }
271: }
272:
273: // Make sure we are using the columns collected during the check
274: if (validCols.size() != columns.length) {
275: this .columnsToImport = validCols;
276: this .realColCount = this .columnsToImport.size();
277: }
278: }
279:
280: private void checkImportColumns() throws SQLException {
281: if (this .columnsToImport == null) {
282: this .realColCount = this .colCount;
283: return;
284: }
285:
286: this .missingColumn = null;
287:
288: try {
289: if (this .columns == null)
290: this .readXmlTableDefinition();
291: } catch (Throwable e) {
292: LogMgr.logError("XmlDataFileParser.checkImportColumns()",
293: "Error reading table definition from XML file", e);
294: this .hasErrors = true;
295: throw new SQLException(
296: "Could not read table definition from XML file");
297: }
298:
299: for (ColumnIdentifier c : columnsToImport) {
300: if (!this .containsColumn(c)) {
301: this .missingColumn = c.getColumnName();
302: this .hasErrors = true;
303: throw new SQLException("Import column "
304: + c.getColumnName()
305: + " not present in input file!");
306: }
307: }
308: this .realColCount = this .columnsToImport.size();
309: }
310:
311: /**
312: * Returns the first column from the import columns
313: * that is not found in the import file
314: * @see #setColumns(String)
315: * @see #setColumns(List)
316: */
317: public String getMissingColumn() {
318: return this .missingColumn;
319: }
320:
321: private boolean containsColumn(ColumnIdentifier col) {
322: if (this .columns == null)
323: return false;
324: for (int i = 0; i < this .columns.length; i++) {
325: if (this .columns[i].equals(col))
326: return true;
327: }
328: return false;
329: }
330:
331: public void setTableName(String aName) {
332: this .tableName = aName;
333: }
334:
335: public List<ColumnIdentifier> getColumnsFromFile() {
336: try {
337: if (this .columns == null)
338: this .readXmlTableDefinition();
339: } catch (IOException e) {
340: return Collections.emptyList();
341: } catch (SAXException e) {
342: return Collections.emptyList();
343: }
344: ArrayList<ColumnIdentifier> result = new ArrayList<ColumnIdentifier>(
345: this .columns.length);
346: for (int i = 0; i < this .columns.length; i++) {
347: result.add(this .columns[i]);
348: }
349: return result;
350: }
351:
352: private void detectTagFormat() {
353: try {
354: fileHandler.setMainFile(this .inputFile, this .encoding);
355: XmlTableDefinitionParser tableDef = new XmlTableDefinitionParser(
356: this .fileHandler);
357: detectTagFormat(tableDef);
358: } catch (Exception e) {
359: LogMgr
360: .logError(
361: "XmlDataFileParser",
362: "Could not detect XML tag format. Assuming 'verbose'",
363: e);
364: this .setUseVerboseFormat(true);
365: }
366: }
367:
368: private void detectTagFormat(XmlTableDefinitionParser tableDef) {
369: String format = tableDef.getTagFormat();
370: if (format != null) {
371: if (XmlRowDataConverter.KEY_FORMAT_LONG.equals(format)) {
372: this .setUseVerboseFormat(true);
373: } else if (XmlRowDataConverter.KEY_FORMAT_SHORT
374: .equals(format)) {
375: this .setUseVerboseFormat(false);
376: }
377: }
378: }
379:
380: private void readXmlTableDefinition() throws IOException,
381: SAXException {
382: fileHandler.setMainFile(this .inputFile, this .encoding);
383:
384: XmlTableDefinitionParser tableDef = new XmlTableDefinitionParser(
385: this .fileHandler);
386: this .columns = tableDef.getColumns();
387: this .colCount = this .columns.length;
388: this .tableNameFromFile = tableDef.getTableName();
389: this .warningAdded = new boolean[this .colCount];
390: detectTagFormat(tableDef);
391: }
392:
393: public String getSourceFilename() {
394: if (this .inputFile == null)
395: return null;
396: return this .inputFile.getAbsolutePath();
397: }
398:
399: public void setSourceFile(File file) {
400: this .sourceDirectory = null;
401: this .inputFile = file;
402: }
403:
404: public void setSourceExtension(String ext) {
405: this .extensionToUse = ext;
406: }
407:
408: public void setSourceDirectory(String dir) {
409: File f = new File(dir);
410: if (!f.isDirectory())
411: throw new IllegalArgumentException(dir
412: + " is not a directory");
413: this .sourceDirectory = dir;
414: this .inputFile = null;
415: }
416:
417: public String getSourceDirectory() {
418: return this .sourceDirectory;
419: }
420:
421: public void setAbortOnError(boolean flag) {
422: this .abortOnError = flag;
423: }
424:
425: private void processOneFile() throws Exception {
426: this .keepRunning = true;
427: this .regularStop = false;
428:
429: // readTableDefinition relies on the fileHandler, so this
430: // has to be called after initializing the fileHandler
431: if (this .columns == null)
432: this .readXmlTableDefinition();
433: if (!this .formatKnown) {
434: detectTagFormat();
435: }
436:
437: if (this .columnsToImport == null) {
438: this .realColCount = this .colCount;
439: } else {
440: this .realColCount = this .columnsToImport.size();
441: }
442:
443: // Re-initialize the reader in case we are reading from a ZIP archive
444: // because readTableDefinition() can change the file handler
445: this .fileHandler.setMainFile(this .inputFile, this .encoding);
446:
447: this .messages = new MessageBuffer();
448: this .sendTableDefinition();
449: Reader in = null;
450: boolean finished = false;
451:
452: try {
453: in = this .fileHandler.getMainFileReader();
454: InputSource source = new InputSource(in);
455: saxParser.parse(source, this );
456: } catch (ParsingInterruptedException e) {
457: if (this .regularStop) {
458: this .receiver.importFinished();
459: } else {
460: this .receiver.importCancelled();
461: this .hasErrors = true;
462: }
463: finished = true;
464: } catch (Exception e) {
465: String msg = "Error during parsing of data row: "
466: + (this .currentRowNumber)
467: + ", column: "
468: + this .currentColIndex
469: + ", current data: "
470: + (this .chars == null ? "<n/a>" : "["
471: + this .chars.toString() + "]")
472: + ", message: " + ExceptionUtil.getDisplay(e);
473: LogMgr
474: .logWarning("XmlDataFileParser.processOneFile()",
475: msg);
476: this .messages.append(msg);
477: this .messages.appendNewLine();
478: this .receiver.tableImportError();
479: throw e;
480: } finally {
481: FileUtil.closeQuitely(in);
482: if (!finished) {
483: this .receiver.importFinished();
484: }
485: }
486: }
487:
488: private void reset() {
489: messages = new MessageBuffer();
490: tableName = null;
491: tableNameFromFile = null;
492: ignoreCurrentRow = false;
493: currentColIndex = 0;
494: realColIndex = 0;
495: columnLongValue = 0;
496: isNull = false;
497: chars = null;
498: columns = null;
499: columnsToImport = null;
500: keepRunning = true;
501: }
502:
503: private void processDirectory() throws Exception {
504: File dir = new File(this .sourceDirectory);
505: boolean verbose = this .verboseFormat;
506: if (this .extensionToUse == null)
507: this .extensionToUse = ".xml";
508:
509: FileNameSorter sorter = new FileNameSorter(this .dbConn, dir,
510: extensionToUse, new XmlTableNameResolver(encoding));
511: List<WbFile> toProcess = null;
512: if (this .checkDependencies) {
513: try {
514: toProcess = sorter.getSortedList();
515: } catch (CycleErrorException e) {
516: hasErrors = true;
517: LogMgr.logError("XmlDataFileParser.processDirectory()",
518: "Error when checking dependencies", e);
519: throw e;
520: }
521: } else {
522: toProcess = sorter.getFiles();
523: }
524:
525: for (WbFile sourceFile : toProcess) {
526: if (!this .keepRunning)
527: break;
528: try {
529: this .inputFile = sourceFile;
530: this .reset();
531:
532: // readTableDefinition() might reset the verbose
533: // flag if a new XML structure is used
534: // this ensures, that the flag specified by the
535: // user will be used for files that do not have the
536: // flag in the meta-data tag
537: this .verboseFormat = verbose;
538: this .processOneFile();
539: } catch (ParsingInterruptedException e) {
540: // cancel the import
541: break;
542: } catch (Exception e) {
543: if (this .abortOnError)
544: throw e;
545: }
546: }
547: }
548:
549: public void start() throws Exception {
550: this .hasErrors = false;
551: this .hasWarnings = false;
552: this .keepRunning = true;
553:
554: this .receiver.setTableCount(-1); // clear multi-table flag in receiver
555: this .receiver.setCurrentTable(-1);
556:
557: try {
558: if (this .sourceDirectory == null) {
559: processOneFile();
560: } else {
561: processDirectory();
562: }
563: } finally {
564: try {
565: this .fileHandler.done();
566: } catch (Throwable th) {
567: }
568: }
569: }
570:
571: public void stop() {
572: this .keepRunning = false;
573: this .regularStop = true;
574: }
575:
576: public boolean isCancelled() {
577: return !this .keepRunning && !regularStop;
578: }
579:
580: public void cancel() {
581: this .keepRunning = false;
582: this .regularStop = false;
583: }
584:
585: private void clearRowData() {
586: for (int i = 0; i < this .realColCount; i++) {
587: this .currentRow[i] = null;
588: }
589: this .currentColIndex = 0;
590: this .realColIndex = 0;
591: }
592:
593: public String getEncoding() {
594: return this .encoding;
595: }
596:
597: public void setEncoding(String enc) {
598: this .encoding = enc;
599: }
600:
601: public void setReceiver(RowDataReceiver aReceiver) {
602: this .receiver = aReceiver;
603: }
604:
605: public void startDocument() throws SAXException {
606: Thread.yield();
607: if (!this .keepRunning)
608: throw new ParsingInterruptedException();
609: }
610:
611: public void endDocument() throws SAXException {
612: Thread.yield();
613: if (!this .keepRunning)
614: throw new ParsingInterruptedException();
615: }
616:
617: public void startElement(String namespaceURI, String sName,
618: String qName, Attributes attrs) throws SAXException {
619: Thread.yield();
620: if (!this .keepRunning)
621: throw new ParsingInterruptedException();
622:
623: if (qName.equals(this .rowTag)) {
624: // row definition ended, start a new row
625: this .clearRowData();
626: this .chars = null;
627: } else if (qName.equals(this .columnTag)) {
628: this .chars = new StringBuilder();
629: String attrValue = attrs
630: .getValue(XmlRowDataConverter.ATTR_LONGVALUE);
631: if (attrValue != null) {
632: try {
633: columnLongValue = Long.parseLong(attrValue);
634: } catch (NumberFormatException e) {
635: LogMgr.logError("XmlDataFileParser.startElement()",
636: "Error converting longvalue", e);
637: }
638: }
639: attrValue = attrs.getValue(XmlRowDataConverter.ATTR_NULL);
640: this .isNull = "true".equals(attrValue);
641: columnDataFile = attrs
642: .getValue(XmlRowDataConverter.ATTR_DATA_FILE);
643: } else {
644: this .chars = null;
645: }
646: }
647:
648: public void endElement(String namespaceURI, String sName,
649: String qName) throws SAXException {
650: if (!this .keepRunning)
651: throw new ParsingInterruptedException();
652: if (qName.equals(this .rowTag)) {
653: if (!this .receiver.shouldProcessNextRow()) {
654: this .receiver.nextRowSkipped();
655: } else {
656: if (!this .ignoreCurrentRow) {
657: try {
658: this .sendRowData();
659: } catch (Exception e) {
660: // don't need to log the error as sendRowData() has already done that.
661: if (this .abortOnError)
662: throw new ParsingInterruptedException();
663: }
664: }
665: }
666: this .ignoreCurrentRow = false;
667: this .currentRowNumber++;
668: } else if (qName.equals(this .columnTag)) {
669: this .buildColumnData();
670: this .currentColIndex++;
671: }
672: this .chars = null;
673: }
674:
675: public void characters(char buf[], int offset, int len)
676: throws SAXException {
677: Thread.yield();
678: if (!this .keepRunning)
679: throw new ParsingInterruptedException();
680: if (chars != null) {
681: this .chars.append(buf, offset, len);
682: }
683: }
684:
685: /** Only implemented to have even more possibilities for cancelling the import */
686: public void ignorableWhitespace(char[] ch, int start, int length)
687: throws SAXException {
688: Thread.yield();
689: if (!this .keepRunning)
690: throw new ParsingInterruptedException();
691: }
692:
693: public void processingInstruction(String target, String data)
694: throws SAXException {
695: Thread.yield();
696: if (!this .keepRunning)
697: throw new ParsingInterruptedException();
698: }
699:
700: public void error(SAXParseException e) throws SAXParseException {
701: String msg = "XML Parse error in line=" + e.getLineNumber()
702: + ",data-row=" + (this .currentRowNumber);
703: LogMgr.logError("XmlDataFileParser.error()", msg, e);
704: this .ignoreCurrentRow = true;
705: }
706:
707: public void fatalError(SAXParseException e)
708: throws SAXParseException {
709: String msg = "Fatal XML parse error in line="
710: + e.getLineNumber() + ",data-row="
711: + (this .currentRowNumber)
712: + "\nRest of file will be ignored!";
713: LogMgr.logError("XmlDataFileParser.fatalError()", msg, e);
714: this .ignoreCurrentRow = true;
715: }
716:
717: // dump warnings too
718: public void warning(SAXParseException err) throws SAXParseException {
719: this .messages.append(ExceptionUtil.getDisplay(err));
720: this .messages.appendNewLine();
721: if (!this .keepRunning)
722: throw err;
723: }
724:
725: /**
726: * Creates the approriate column data object and puts it
727: * into rowData[currentColIndex]
728: * {@link workbench.util.ValueConverter} is not used because
729: * for most of the datatypes we have some special processing here
730: * Date and time can be initialized through the long value in the XML file
731: * Numeric types contain the actual class to be used {@link #createNumericType(String, String)}
732: */
733: private void buildColumnData() throws ParsingInterruptedException {
734: if (this .columnsToImport != null
735: && !this .columnsToImport
736: .contains(this .columns[this .currentColIndex]))
737: return;
738: this .currentRow[this .realColIndex] = null;
739:
740: if (!this .receiver.shouldProcessNextRow())
741: return;
742:
743: // the isNull flag will be set by the startElement method
744: // as that is an attribute of the tag
745: if (this .isNull) {
746: this .realColIndex++;
747: return;
748: }
749:
750: String value = this .chars.toString();
751: if (this .valueModifier != null) {
752: value = this .valueModifier.modifyValue(
753: this .columns[this .realColIndex], value);
754: }
755:
756: int type = this .columns[this .realColIndex].getDataType();
757: try {
758: if (SqlUtil.isCharacterType(type)) {
759: // if clobs are exported as external files, than we'll have a filename in the
760: // attribute (just like with BLOBS)
761: if (this .columnDataFile == null) {
762: this .currentRow[this .realColIndex] = value;
763: } else {
764: String fileDir = this .inputFile.getParent();
765: this .currentRow[this .realColIndex] = new File(
766: fileDir, columnDataFile);
767: }
768: } else if (SqlUtil.isBlobType(type)) {
769: String fileDir = this .inputFile.getParent();
770: this .currentRow[this .realColIndex] = new File(fileDir,
771: columnDataFile);
772: } else if (SqlUtil.isDateType(type)) {
773: // For Date types we don't need the ValueConverter as already we
774: // have a suitable long value that doesn't need parsing
775: java.sql.Date d = new java.sql.Date(
776: this .columnLongValue);
777: if (type == Types.TIMESTAMP) {
778: this .currentRow[this .realColIndex] = new java.sql.Timestamp(
779: d.getTime());
780: } else {
781: this .currentRow[this .realColIndex] = d;
782: }
783: } else {
784: // for all other types we can use the ValueConverter
785: this .currentRow[this .realColIndex] = converter
786: .convertValue(value, type);
787: }
788: } catch (Exception e) {
789: String msg = ResourceMgr.getString("ErrConvertError");
790: msg = StringUtil.replace(msg, "%type%", SqlUtil
791: .getTypeName(this .columns[realColIndex]
792: .getDataType()));
793: msg = StringUtil.replace(msg, "%column%",
794: this .columns[realColIndex].getColumnName());
795: msg = StringUtil.replace(msg, "%error%", e.getMessage());
796: msg = StringUtil.replace(msg, "%value%", value);
797: msg = StringUtil.replace(msg, "%row%", Integer
798: .toString(this .currentRowNumber));
799:
800: this .messages.append(msg);
801: this .messages.appendNewLine();
802:
803: if (this .abortOnError) {
804: LogMgr.logError("XmlDataFileParser.buildColumnData()",
805: msg, e);
806: this .hasErrors = true;
807: throw new ParsingInterruptedException();
808: } else {
809: this .messages.append(ResourceMgr
810: .getString("ErrConvertWarning"));
811: this .hasWarnings = true;
812: LogMgr.logWarning(
813: "XmlDataFileParser.buildColumnData()", msg,
814: null);
815: }
816: }
817:
818: this .realColIndex++;
819: }
820:
821: private TableIdentifier getTargetTable() {
822: TableIdentifier tbl = null;
823: if (this .tableName == null) {
824: tbl = new TableIdentifier(this .tableNameFromFile);
825: } else {
826: tbl = new TableIdentifier(this .tableName);
827: }
828: return tbl;
829: }
830:
831: private void sendTableDefinition() throws SQLException {
832: try {
833: TableIdentifier tbl = getTargetTable();
834:
835: checkTargetColumns();
836: if (this .columnsToImport == null) {
837: this .receiver.setTargetTable(tbl, this .columns);
838: } else {
839: ColumnIdentifier[] cols = new ColumnIdentifier[this .realColCount];
840: int index = 0;
841: for (int i = 0; i < this .colCount; i++) {
842: if (this .columnsToImport.contains(this .columns[i])) {
843: cols[index] = this .columns[i];
844: index++;
845: }
846: }
847: this .receiver.setTargetTable(tbl, cols);
848: }
849: this .currentRow = new Object[this .realColCount];
850: } catch (SQLException e) {
851: this .currentRow = null;
852: this .hasErrors = true;
853: throw e;
854: }
855: }
856:
857: private void sendRowData() throws SAXException, Exception {
858: if (this .receiver != null) {
859: try {
860: this .receiver.processRow(this .currentRow);
861: } catch (Exception e) {
862: LogMgr.logError("XmlDataFileParser.sendRowData()",
863: "Error when sending row data to receiver", e);
864: if (this .abortOnError) {
865: this .hasErrors = true;
866: throw e;
867: }
868: this .hasWarnings = true;
869: if (this .errorHandler != null) {
870: int choice = errorHandler.getActionOnError(
871: this .currentRowNumber + 1, null, null,
872: ExceptionUtil.getDisplay(e, false));
873: if (choice == JobErrorHandler.JOB_ABORT)
874: throw e;
875: if (choice == JobErrorHandler.JOB_IGNORE_ALL) {
876: this .abortOnError = false;
877: }
878: }
879:
880: }
881: }
882: if (!this .keepRunning)
883: throw new ParsingInterruptedException();
884: }
885:
886: public MessageBuffer getMessages() {
887: return this .messages;
888: }
889:
890: private void setUseVerboseFormat(boolean flag) {
891: this .formatKnown = true;
892: this .verboseFormat = flag;
893: if (this .verboseFormat) {
894: rowTag = XmlRowDataConverter.LONG_ROW_TAG;
895: columnTag = XmlRowDataConverter.LONG_COLUMN_TAG;
896: } else {
897: rowTag = XmlRowDataConverter.SHORT_ROW_TAG;
898: columnTag = XmlRowDataConverter.SHORT_COLUMN_TAG;
899: }
900: }
901:
902: public void setErrorHandler(JobErrorHandler handler) {
903: this.errorHandler = handler;
904: }
905:
906: }
|