1 /*
2 * PROJECT : DAR Runtime and Tools
3 * COPYRIGHT : Copyright (C) 1999-2004 tim.stephenson@enableit.org
4 * LICENSE : GNU LESSER GENERAL PUBLIC LICENSE
5 * Version 2.1, February 1999
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21 package org.enableit.db.darrt;
22
23 import java.io.File;
24 import java.io.FileWriter;
25 import java.sql.Connection;
26 import java.sql.DatabaseMetaData;
27 import java.sql.ResultSet;
28 import java.text.SimpleDateFormat;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.Date;
32 import java.util.Iterator;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.StringTokenizer;
36 import java.util.TreeMap;
37
38 import javax.xml.parsers.DocumentBuilder;
39 import javax.xml.parsers.DocumentBuilderFactory;
40 import javax.xml.parsers.ParserConfigurationException;
41
42 import org.apache.log4j.Category;
43 import org.enableit.db.ConnFactory;
44 import org.enableit.db.DBException;
45 import org.enableit.db.DBUtils;
46 import org.enableit.db.SqlType;
47 import org.enableit.db.beans.Database;
48 import org.enableit.db.beans.Provider;
49 import org.enableit.db.beans.ProviderExt;
50 import org.enableit.db.beans.Schema;
51 import org.enableit.db.beans.Table;
52 import org.exolab.castor.xml.Marshaller;
53 import org.exolab.castor.xml.Unmarshaller;
54 import org.w3c.dom.Document;
55 import org.w3c.dom.Element;
56
57
58 /***
59 * Export database schemas as XML.
60 *
61 * <p>Note on ASA interpretation of DatabaseMetaData object:
62 * SchemaConstants.SCHEMA = users and groups.</p>
63 */
64 public class SchemaExporter extends AbstractSchemaHandler {
65 public static final String COL_TYPE = "col-type";
66 public static final String NULLABLE = "null";
67 public static final String PK = "primary-key";
68 public static final String FK = "foreign-key";
69 public static final String FK_TABLE = "fk-table-name";
70 public static final String FK_DATA_COLUMN = "data-column";
71 public static final String FK_DATA_COL_NAME = "col-name";
72 public static final String FK_DISPLAY_COLUMN = "display-column";
73 public static final String FK_DISPLAY_COL_NAME = "col-name";
74
75 /***
76 * Define a static Category instance for logging.
77 */
78 private static Category logger = Category.getInstance(SchemaExporter.class);
79 private String catalog;
80
81 /***
82 * The name of the database schema to be exported.
83 * <p>See JDBC documentation for definition of 'schema'. Actual implmentation
84 * varies from one RDBMS to the next.</p>
85 */
86 private String targetSchema;
87
88 /***
89 * File name for the exported database definition.
90 * <p>NB: MUST not contain characters that are illegal in file names.</p>
91 */
92 private String fileName;
93
94 /***
95 * The table types to export.
96 * <p>The available table types vary from one RDBMS to another,
97 * but typically includes TABLE and VIEW. Default is <em>TABLE</em></p>
98 */
99 private String[] tableTypes = { "TABLE" };
100 private boolean uniqueIndexOnly = false;
101 private boolean approxIndexStats = false;
102 private Provider provider;
103
104 /***
105 * Responsible for exporting VIEW definitions.
106 */
107 private ViewExporter viewExporter = new ViewExporter();
108
109 /***
110 * Formats date info within the schema export meta data.
111 */
112 private SimpleDateFormat formatter;
113
114 /***
115 * Default Constructor.
116 */
117 public SchemaExporter() {
118 formatter = new SimpleDateFormat();
119 }
120
121 /***
122 * The name of the file to write schema to if <code>exportToFile</code>
123 * invoked.
124 * <p>If not specified the file will be named according to the logical
125 * name contained in the <code>Provider</code> parameter.</p>
126 * @param fileName to export to.
127 */
128 public void setFileName(String fileName) {
129 logger.info("METHOD_ENTRY: setFileName: " + fileName);
130
131 this.fileName = fileName;
132
133 logger.info("METHOD_EXIT: setFileName");
134 }
135
136 /***
137 * Algorhythm is as follows:
138 * <ol>
139 * <li>If set, use the filename provided.</li>
140 * <li>If set, use the name of the provider.</li>
141 * <li>Use data source name or URL within <code>Provider</code>
142 * to infer a name that should be unique in most cases.</li>
143 * </ol>
144 * @return fileName To write to.
145 */
146 public String getFileName(Provider provider) {
147 logger.info("METHOD_ENTRY: getFileName, provider: "
148 + ProviderExt.toString(provider));
149
150 String retVal = fileName;
151
152 if (retVal == null) {
153 retVal = provider.getName();
154 }
155
156 if (retVal == null) {
157 String dsName = provider.getJdbc2DatasourceName();
158
159 if (dsName != null) {
160 if (dsName.indexOf("/") != -1) {
161 retVal = dsName.substring(dsName.lastIndexOf("/") + 1);
162 } else {
163 retVal = dsName;
164 }
165 } else {
166 retVal = provider.getUrl();
167 retVal = retVal.replace(':', '_');
168 retVal = retVal.replace('/', '_');
169 }
170
171 if (!retVal.endsWith(".xml")) {
172 retVal += ".xml";
173 }
174 }
175
176 logger.info("METHOD_EXIT: getFileName, fileName=" + retVal);
177
178 return retVal;
179 }
180
181 /***
182 * The name of the schema to be exported.
183 * <p>
184 * If no schema is specified all will be exported.
185 */
186 public void setSchemaName(String targetSchema) {
187 logger.info("METHOD_ENTRY: setSchemaName: " + targetSchema);
188
189 this.targetSchema = targetSchema;
190
191 logger.info("METHOD_EXIT: setSchemaName");
192 }
193
194 /***
195 * Set the table types to export.
196 */
197 public void setTableTypes(String tableTypes) {
198 StringTokenizer st = new StringTokenizer(tableTypes, ",");
199 String[] tableTypesArray = new String[st.countTokens()];
200 int i = 0;
201
202 while (st.hasMoreTokens()) {
203 tableTypesArray[i++] = st.nextToken().trim();
204 }
205
206 setTableTypes(tableTypesArray);
207 }
208
209 /***
210 * An array of table types to be exported.
211 * <p>
212 * Table types include such things as 'TABLE' and 'VIEW',
213 * though the exact list depends
214 * on the database vendor.
215 * <p>
216 * If no table types are specified all will be exported.
217 */
218 public void setTableTypes(String[] tableTypes) {
219 if (tableTypes == null) {
220 this.tableTypes = null;
221 } else {
222 /*
223 * The objective here is to set the local array to the
224 * parameter array but to exclude VIEW
225 */
226 this.tableTypes = new String[tableTypes.length - 1];
227 logger.debug("No. of table types =" + tableTypes.length);
228
229 int j = 0;
230
231 try {
232 for (int i = 0; i < tableTypes.length; i++) {
233 if (!SchemaConstants.VIEW.equalsIgnoreCase(tableTypes[i])) {
234 this.tableTypes[j++] = tableTypes[i];
235 }
236 }
237 } catch (ArrayIndexOutOfBoundsException e) {
238 this.tableTypes = tableTypes;
239 }
240
241 logger.info("tableTypes=" + Arrays.asList(this.tableTypes));
242 }
243
244 logger.info("METHOD_EXIT: setTableTypes");
245 }
246
247 /***
248 * Export the schema identified by the <code>Provider</code> parameter.
249 * @param provider Encapsulation of the Data Source information.
250 */
251 public Document export(Provider provider)
252 throws DBException {
253 logger.info("METHOD_ENTRY: export(Provider)");
254
255 this.provider = provider;
256
257 Document doc = null;
258 Connection conn = null;
259
260 try {
261 conn = ConnFactory.getConnection(provider);
262
263 if (conn == null) {
264 throw new DBException("Null connection when connecting "
265 + "to database");
266 }
267
268 doc = export(conn);
269 } catch (DBException e) {
270 throw e;
271 } catch (Exception e) {
272 logger.error(e.getMessage(), e);
273 throw new DBException(e.getClass().getName() + ":" + e.getMessage(),
274 e);
275 } finally {
276 try {
277 conn.close();
278 } catch (Exception e) {
279 ;
280 }
281
282 conn = null;
283 }
284
285 logger.info("METHOD_EXIT: export(Provider)");
286
287 return doc;
288 }
289
290 /***
291 * Export the schema identified by the provided connection parameters.
292 */
293 public Document export(String driver, String url, String userid,
294 String password)
295 throws DBException {
296 logger.info("METHOD_ENTRY: export: " + driver + "," + url + ","
297 + userid + "," + password);
298
299 Document doc = null;
300
301 try {
302 // Set the provider to avoid NPE in output
303 ProviderExt provider = new ProviderExt();
304
305 provider.setConnectionProperties(driver, url, userid, password);
306 doc = export(provider);
307 } catch (DBException e) {
308 throw e;
309 } catch (Exception e) {
310 logger.error(e.getMessage());
311 throw new DBException(e.getClass().getName() + ":" + e.getMessage(),
312 e);
313 }
314
315 logger.info("METHOD_EXIT: export");
316
317 return doc;
318 }
319
320 /***
321 * Export the schema identified by the provided connection parameters.
322 */
323 public Document export(String dataSourceName)
324 throws DBException {
325 logger.info("METHOD_ENTRY: export(String): dataSourceName="
326 + dataSourceName);
327
328 Document doc = null;
329
330 try {
331 // Set the provider to avoid NPE in output
332 ProviderExt provider = new ProviderExt();
333
334 provider.setJdbc2DatasourceName(dataSourceName);
335 doc = export(provider);
336 } catch (DBException e) {
337 throw e;
338 } catch (Exception e) {
339 logger.error(e.getMessage(), e);
340 throw new DBException(e.getClass().getName() + ":" + e.getMessage());
341 }
342
343 logger.info("METHOD_EXIT: export(String)");
344
345 return doc;
346 }
347
348 /***
349 * Export the schema at the database already connected to.
350 */
351 private Document export(Connection conn)
352 throws DBException {
353 logger.info("METHOD_ENTRY: export(Connection)");
354
355 Document doc = null;
356
357 // Construct an xml doc
358 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
359
360 try {
361 DocumentBuilder docBuilder = factory.newDocumentBuilder();
362
363 doc = docBuilder.newDocument();
364 } catch (ParserConfigurationException e) {
365 logger.error(e.getMessage(), e);
366 throw new DBException(e.getMessage(), e);
367 }
368
369 // get schema items to place in doc
370 try {
371 Element databaseTag = doc.createElement(SchemaConstants.ROOT_ELEMENT);
372 DatabaseMetaData dmd = conn.getMetaData();
373
374 /*
375 * General database provider information
376 */
377 Element providerTag = doc.createElement(SchemaConstants.PROVIDER);
378
379 Element nameTag = doc.createElement(SchemaConstants.NAME);
380
381 nameTag.appendChild(doc.createTextNode(dmd.getDatabaseProductName()));
382 providerTag.appendChild(nameTag);
383
384 Element productNameTag = doc.createElement(SchemaConstants.PRODUCT_NAME);
385
386 productNameTag.appendChild(doc.createTextNode(
387 dmd.getDatabaseProductName()));
388 providerTag.appendChild(productNameTag);
389
390 Element productVsnTag = doc.createElement(SchemaConstants.VSN);
391
392 productVsnTag.appendChild(doc.createTextNode(
393 dmd.getDatabaseProductVersion()));
394 providerTag.appendChild(productVsnTag);
395
396 // Database driver info
397 Element driverTag = doc.createElement(SchemaConstants.DRIVER);
398 Element driverNameTag = doc.createElement(SchemaConstants.NAME);
399
400 driverNameTag.appendChild(doc.createTextNode(dmd.getDriverName()));
401 driverTag.appendChild(driverNameTag);
402
403 Element driverVsnTag = doc.createElement(SchemaConstants.VSN);
404
405 driverVsnTag.appendChild(doc.createTextNode(dmd.getDriverVersion()));
406 driverTag.appendChild(driverVsnTag);
407
408 Element driverClassNameTag = doc.createElement(SchemaConstants.CLASS_NAME);
409
410 driverClassNameTag.appendChild(doc.createTextNode((provider
411 .getDriver() == null) ? ""
412 : provider.getDriver().getClassName()));
413 driverTag.appendChild(driverClassNameTag);
414
415 Element urlTag = doc.createElement(SchemaConstants.URL_STRING);
416
417 urlTag.appendChild(doc.createTextNode(dmd.getURL()));
418 driverTag.appendChild(urlTag);
419
420 Element usernameTag = doc.createElement(SchemaConstants.USERNAME);
421
422 usernameTag.appendChild(doc.createTextNode(dmd.getUserName()));
423 driverTag.appendChild(usernameTag);
424
425 Element passwordTag = doc.createElement(SchemaConstants.PASSWORD);
426 String password = (provider.getPassword() == null) ? ""
427 : provider
428 .getPassword();
429
430 passwordTag.appendChild(doc.createTextNode(password));
431 driverTag.appendChild(passwordTag);
432
433 providerTag.appendChild(driverTag);
434
435 Element creationDateTag = doc.createElement(SchemaConstants.CREATION_DATE);
436
437 creationDateTag.appendChild(doc.createTextNode(formatter.format(
438 new Date())));
439 providerTag.appendChild(creationDateTag);
440
441 databaseTag.appendChild(providerTag);
442
443 /*
444 * Database metadata information (not sure if this is useful yet)
445 */
446 Element metadataTag = doc.createElement(SchemaConstants.METADATA);
447 Element catalogsTag = doc.createElement(SchemaConstants.CATALOGS);
448 ResultSet catalogs = dmd.getCatalogs();
449
450 while (catalogs.next()) {
451 //logger.debug("TABLE TYPE: " + tableTypes.getString("TABLE_TYPE"));
452 Element catalogNameTag = doc.createElement(SchemaConstants.NAME);
453
454 catalogNameTag.appendChild(doc.createTextNode(
455 catalogs.getString("TABLE_CAT").trim()));
456 catalogsTag.appendChild(catalogNameTag);
457 }
458
459 catalogs.close();
460 metadataTag.appendChild(catalogsTag);
461
462 Element tableTypesTag = doc.createElement(SchemaConstants.TABLE_TYPES);
463 ResultSet tableTypes = dmd.getTableTypes();
464
465 while (tableTypes.next()) {
466 //logger.debug("TABLE TYPE: " + tableTypes.getString("TABLE_TYPE"));
467 Element tableTypeNameTag = doc.createElement(SchemaConstants.NAME);
468
469 tableTypeNameTag.appendChild(doc.createTextNode(
470 tableTypes.getString("TABLE_TYPE").trim()));
471 tableTypesTag.appendChild(tableTypeNameTag);
472 }
473
474 metadataTag.appendChild(tableTypesTag);
475
476 Element catalogTermTag = doc.createElement(SchemaConstants.CATALOG_TERM);
477
478 catalogTermTag.appendChild(doc.createTextNode(dmd.getCatalogTerm()));
479 metadataTag.appendChild(catalogTermTag);
480
481 Element schemaTermTag = doc.createElement(SchemaConstants.SCHEMA_TERM);
482
483 schemaTermTag.appendChild(doc.createTextNode(dmd.getSchemaTerm()));
484 metadataTag.appendChild(schemaTermTag);
485
486 if (getDebug()) {
487 databaseTag.appendChild(metadataTag);
488 }
489
490 /*
491 * Get the schema data (including the table data)
492 */
493 ResultSet schemas = dmd.getSchemas();
494 ArrayList schList = (ArrayList) DBUtils.convertResultToList(schemas);
495
496 for (Iterator i = schList.iterator(); i.hasNext();) {
497 Element schemaTag = doc.createElement(SchemaConstants.SCHEMA);
498 TreeMap row = (TreeMap) i.next();
499 String schemaName = (String) row.get("TABLE_SCHEM");
500
501 logger.debug("Found schema named: " + schemaName);
502
503 // If no target schema was specified, or if this is the one specified - export it
504 if ((targetSchema == null)
505 || schemaName.equalsIgnoreCase(targetSchema)) {
506 logger.debug("Exporting schema named: " + schemaName);
507
508 Element schemaNameTag = doc.createElement(SchemaConstants.NAME);
509
510 schemaNameTag.appendChild(doc.createTextNode(schemaName));
511 schemaTag.appendChild(schemaNameTag);
512
513 if (this.tableTypes.length > 0) {
514 addTables(dmd, doc, schemaName, schemaTag,
515 this.tableTypes);
516 }
517
518 String[] viewType = new String[1];
519
520 viewType[0] = "VIEW"; // TODO is this constant for all dbms?
521 viewExporter.setProvider(provider);
522 addTables(dmd, doc, schemaName, schemaTag, viewType);
523 databaseTag.appendChild(schemaTag);
524 } else {
525 logger.debug("Ignoring schema named: " + schemaName);
526 }
527 }
528
529 doc.appendChild(databaseTag);
530 } catch (Exception e) {
531 // Things to catch : array out of bounds from no column results found
532 logger.error(e.getMessage(), e);
533 }
534
535 logger.info("METHOD_EXIT: export(Connection)");
536
537 return doc;
538 }
539
540 /***
541 * Extract table metadata adding appropriate tags.
542 */
543 private void addTables(DatabaseMetaData dmd, Document doc,
544 String schemaName, Element schemaTag, String[] tableTypes)
545 throws SchemaHandlingException {
546 logger.info("METHOD_ENTRY: addTables, schemaName: " + schemaName);
547
548 try {
549 ResultSet tables = dmd.getTables(null, schemaName,
550 getTablePattern(), tableTypes);
551
552 while (tables.next()) {
553 String tableName = tables.getString("TABLE_NAME");
554 String tableType = tables.getString("TABLE_TYPE");
555
556 /*
557 * The table portion
558 */
559 Element tableTag = null;
560
561 if (SchemaConstants.VIEW.equalsIgnoreCase(tableType)) {
562 tableTag = doc.createElement(SchemaConstants.VIEW);
563 } else {
564 tableTag = doc.createElement(SchemaConstants.TABLE);
565 }
566
567 Element nameTag = doc.createElement(SchemaConstants.NAME);
568
569 nameTag.appendChild(doc.createTextNode(tableName));
570 tableTag.appendChild(nameTag);
571
572 Element typeTag = doc.createElement(SchemaConstants.TYPE);
573
574 typeTag.appendChild(doc.createTextNode(tableType));
575 tableTag.appendChild(typeTag);
576
577 // Capture Primary Key Name
578 ResultSet pk = dmd.getPrimaryKeys(catalog, schemaName, tableName);
579 ArrayList pkList = new ArrayList();
580
581 while (pk.next()) {
582 logger.debug("Primary Key List: " + pkList.toString());
583
584 String pkValue = pk.getString("COLUMN_NAME");
585
586 pkList.add(pkValue);
587 }
588
589 pk.close();
590
591 if (pkList.size() == 0) {
592 logger.warn("No primary key was found for " + tableName);
593
594 //throw new DBException(msg) ;
595 }
596
597 // All columns' data
598 ResultSet columns = dmd.getColumns(catalog, schemaName,
599 tableName, null);
600 ArrayList colList = (ArrayList) DBUtils.convertResultToList(columns);
601
602 columns.close();
603
604 ResultSet exKeys = dmd.getExportedKeys(catalog, schemaName,
605 tableName);
606
607 //ArrayList fKeyList = (ArrayList)DBUtils.convertResultToList(exKeys) ;
608 ArrayList exKeyList = new ArrayList();
609
610 while (exKeys.next()) {
611 exKeyList.add(exKeys.getString("PKCOLUMN_NAME"));
612 }
613
614 exKeys.close();
615
616 // TODO: Nothing done with the exported keys yet...
617 //pks.add(((TreeMap)colList.get(0)).get("COLUMN_NAME")) ;
618 // All foreign keys' data
619 ResultSet fKeys = dmd.getImportedKeys(catalog, schemaName,
620 tableName);
621 ArrayList fKeyList = (ArrayList) DBUtils.convertResultToList(fKeys);
622
623 //logger.debug("FKs found=" + fKeyList);
624 fKeys.close();
625
626 // For each list element (represents a table column) ...
627 Element columnTag = null;
628
629 for (Iterator i = colList.iterator(); i.hasNext();) {
630 TreeMap tableCol = (TreeMap) i.next();
631
632 // ... for each column construct column element
633 columnTag = doc.createElement(SchemaConstants.COLUMN);
634
635 // get column name & description
636 String colName = (String) tableCol.get("COLUMN_NAME");
637 Element colDescTag = doc.createElement(SchemaConstants.DESCRIPTION);
638
639 colDescTag.appendChild(doc.createTextNode(
640 (String) tableCol.get("REMARKS")));
641
642 Element columnNameTag = doc.createElement(SchemaConstants.COL_NAME);
643
644 columnNameTag.appendChild(doc.createTextNode(colName));
645 columnTag.appendChild(colDescTag);
646 columnTag.appendChild(columnNameTag);
647
648 // get column datatype
649 // defined as short in JDBC doco, Oracle returns BigDecimal, Sybase an Integer
650 Number typeObj = (Number) tableCol.get("DATA_TYPE");
651 int type = ((typeObj == null) ? Integer.MIN_VALUE
652 : typeObj.intValue());
653 Number scaleObj = (Number) tableCol.get("COLUMN_SIZE");
654 int scale = ((scaleObj == null) ? Integer.MIN_VALUE
655 : scaleObj.intValue());
656 Number precisionObj = (Number) tableCol.get(
657 "DECIMAL_DIGITS");
658 int precision = ((precisionObj == null) ? Integer.MIN_VALUE
659 : precisionObj
660 .intValue());
661 String colType = new SqlType(type, scale, precision)
662 .toString();
663 Element columnTypeTag = doc.createElement(COL_TYPE);
664
665 columnTypeTag.appendChild(doc.createTextNode(colType));
666 columnTag.appendChild(columnTypeTag);
667
668 String colDefault = (String) tableCol.get("COLUMN_DEF");
669
670 if (colDefault != null) {
671 logger.debug("Column " + colName + " has default: "
672 + colDefault);
673
674 // Translate to generic name for recognised defaults
675 DDLGenerator dbms = DDLFactory.getInstance().getGenerator(provider) ;
676 colDefault = dbms.getGenericDefault(colDefault) ;
677
678 Element colDefaultTag = doc.createElement(SchemaConstants.DEFAULT);
679
680 colDefaultTag.appendChild(doc.createTextNode(colDefault));
681 columnTag.appendChild(colDefaultTag);
682 }
683
684 // get column null / not null
685 // assume the worst (includes don't know case)
686 boolean isNullable = false;
687
688 logger.debug("Nullable: "
689 + tableCol.get("IS_NULLABLE").toString());
690
691 if (((String) tableCol.get("IS_NULLABLE")).equals("YES")) {
692 isNullable = true;
693 }
694
695 Element nullableTag = doc.createElement(NULLABLE);
696
697 nullableTag.appendChild(doc.createTextNode(String.valueOf(
698 isNullable)));
699 columnTag.appendChild(nullableTag);
700
701 // determine if column is primary key
702 if (pkList.contains(colName)) {
703 Element pkTag = doc.createElement(SchemaConstants.PK);
704
705 pkTag.appendChild(doc.createTextNode("true"));
706 columnTag.appendChild(pkTag);
707 }
708
709 // determine if column is a foreign key
710 if (fKeyList.size() > 1) {
711 logger.warn("More than one foreign key for " + colName) ;
712 logger.warn("... all but 1st will be ignored:" + fKeyList) ;
713 }
714 //for (Iterator j = fKeyList.iterator(); j.hasNext();) {
715 if (fKeyList.size() > 0) {
716 TreeMap key = (TreeMap) fKeyList.get(0);
717 String fKeyName = (String) key.get("FKCOLUMN_NAME");
718
719 if ((fKeyName != null) && fKeyName.equals(colName)) {
720 logger.debug("Found foreign key for column: "
721 + fKeyName);
722
723 Element fkTag = doc.createElement(FK);
724 Element fkTableTag = doc.createElement(FK_TABLE);
725 String fkTableName = (String) key.get(
726 "PKTABLE_NAME");
727
728 fkTableTag.appendChild(doc.createTextNode(
729 fkTableName));
730 fkTag.appendChild(fkTableTag);
731
732 // foreign key data col (the actual foreign key)
733 Element fkDataColTag = doc.createElement(FK_DATA_COLUMN);
734 String pkName = (String) key.get("PKCOLUMN_NAME");
735 Element fkDataColDescTag = doc.createElement(SchemaConstants.DESCRIPTION);
736
737 fkDataColDescTag.appendChild(doc.createTextNode(
738 (String) key.get("REMARKS")));
739 fkDataColTag.appendChild(fkDataColDescTag);
740
741 Element fkDataColNameTag = doc.createElement(SchemaConstants.FK_DATA_COL_NAME);
742
743 fkDataColNameTag.appendChild(doc.createTextNode(
744 pkName));
745
746 // fk display column
747 Element fkDisplayColTag = doc.createElement(FK_DISPLAY_COLUMN);
748 Element fkDisplayColNameTag = doc.createElement(SchemaConstants.FK_DISPLAY_COL_NAME);
749
750 // try to guess display name ...
751 ResultSet fkRS = dmd.getColumns(catalog,
752 schemaName, fkTableName, null);
753 List fkList = DBUtils.convertResultToList(fkRS);
754 //System.out.println("FKs for " + colName + ": " + fkList);
755
756 fkRS.close();
757 fkRS = null;
758
759 String displayName = null;
760
761 logger.debug("Looking for display col for: "
762 + fkTableName + "." + pkName);
763
764 for (Iterator k = fkList.iterator(); k.hasNext();) {
765 Map fk = (Map) k.next();
766 String fkColName = (String) fk.get(
767 "COLUMN_NAME");
768
769 logger.debug("... examining " + fkColName);
770
771 if (fkColName.equalsIgnoreCase(fkTableName
772 + "name")
773 || fkColName.equalsIgnoreCase(fkTableName
774 + "_name")) {
775 displayName = fkColName;
776 logger.debug("... found: " + fkColName);
777
778 break;
779 }
780 }
781
782 if (displayName == null) {
783 for (Iterator k = fkList.iterator();
784 k.hasNext();) {
785 Map fk = (Map) k.next();
786 String fkColName = (String) fk.get(
787 "COLUMN_NAME");
788
789 logger.debug("... examining " + fkColName);
790
791 if (fkColName.equalsIgnoreCase(fkTableName
792 + "desc")
793 || fkColName.equalsIgnoreCase(fkTableName
794 + "_desc")) {
795 displayName = fkColName;
796 logger.debug("... found: " + fkColName);
797
798 break;
799 }
800 }
801 }
802
803 // ... fall back on data column's name
804 if (displayName == null) {
805 displayName = pkName;
806 }
807
808 fkDisplayColNameTag.appendChild(doc.createTextNode(
809 displayName));
810 fkDataColTag.appendChild(fkDataColNameTag);
811
812 Element fkDisplayColDescTag = doc.createElement(SchemaConstants.DESCRIPTION);
813
814 fkDisplayColDescTag.appendChild(doc.createTextNode(
815 (String) key.get("REMARKS")));
816 fkDisplayColTag.appendChild(fkDisplayColDescTag);
817 fkDisplayColTag.appendChild(fkDisplayColNameTag);
818 fkTag.appendChild(fkDataColTag);
819 fkTag.appendChild(fkDisplayColTag);
820
821 columnTag.appendChild(fkTag);
822 }
823 }
824
825 tableTag.appendChild(columnTag);
826 }
827
828 if (SchemaConstants.VIEW.equalsIgnoreCase(tableType)) {
829 // For views find, their definition
830 Element definitionTag = doc.createElement(SchemaConstants.DEFINITION);
831 String definition = viewExporter.export(dmd.getConnection(),
832 tableName);
833
834 logger.debug("definition=" + definition);
835 definitionTag.appendChild(doc.createTextNode(definition));
836 tableTag.appendChild(definitionTag);
837 } else {
838 // Get Index info - 1st null indicates drop catalog from criteria
839 ResultSet indexes = dmd.getIndexInfo(null, schemaName,
840 tableName, uniqueIndexOnly, approxIndexStats);
841 int indexCount = 0;
842 String lastIndexName = null;
843 Element indexTag = null;
844
845 while (indexes.next()) {
846 switch (indexes.getInt("TYPE")) {
847 case DatabaseMetaData.tableIndexStatistic:
848
849 // do nothing
850 break;
851 default:
852
853 String indexName = indexes.getString("INDEX_NAME");
854
855 if ((lastIndexName == null)
856 || !indexName.equals(lastIndexName)) {
857 // Create new index
858 if ((indexTag != null)
859 && (lastIndexName != null)) {
860 tableTag.appendChild(indexTag);
861 }
862
863 indexCount++;
864 indexTag = doc.createElement(SchemaConstants.INDEX);
865
866 Element indexNameTag = doc.createElement(SchemaConstants.NAME);
867
868 indexNameTag.appendChild(doc.createTextNode(
869 String.valueOf(indexName)));
870 indexTag.appendChild(indexNameTag);
871
872 Element indexTypeTag = doc.createElement(SchemaConstants.TYPE);
873
874 indexTypeTag.appendChild(doc.createTextNode(
875 String.valueOf(indexes.getInt("TYPE"))));
876 indexTag.appendChild(indexTypeTag);
877
878 Element indexUniqueTag = doc.createElement(SchemaConstants.INDEX_UNIQUE);
879
880 // This is horrible - the jdk1.3.1 javadoc says the column should be called 'NON-UNIQUE'
881 String unique = "UNKNOWN";
882
883 if ((provider != null)
884 && (provider.getDriver() != null)
885 && (provider.getDriver().getName() != null)) {
886 if (provider.getDriver().getName().indexOf("sybase") > 0) {
887 unique = (indexes.getBoolean(
888 "TABLE_NAME") ? "UNIQUE"
889 : "NON-UNIQUE");
890 } else if (provider.getDriver().getName()
891 .indexOf("microsoft") > 0) {
892 unique = (indexes.getBoolean(
893 "TABLE_NAME") ? "UNIQUE"
894 : "NON-UNIQUE");
895 } else if (provider.getDriver().getName()
896 .indexOf("oracle") > 0) {
897 unique = (indexes.getBoolean(
898 "TABLE_NAME") ? "UNIQUE"
899 : "NON-UNIQUE");
900 }
901 }
902
903 indexUniqueTag.appendChild(doc.createTextNode(
904 unique));
905 indexTag.appendChild(indexUniqueTag);
906 }
907
908 // Add index column details
909 Element colNameTag = doc.createElement(SchemaConstants.COL_NAME);
910
911 colNameTag.appendChild(doc.createTextNode(
912 indexes.getString("COLUMN_NAME")));
913 indexTag.appendChild(colNameTag);
914
915 lastIndexName = indexName;
916 }
917 }
918
919 if (indexTag != null) {
920 tableTag.appendChild(indexTag);
921 }
922
923 indexCount++;
924
925 logger.warn("Index count for table " + tableName + "="
926 + indexCount);
927 indexes.close();
928 }
929
930 schemaTag.appendChild(tableTag);
931 }
932
933 // end while have tables
934 } catch (Exception e) {
935 logger.error("Error whilst trying to process schema named: "
936 + schemaName, e);
937 throw new SchemaHandlingException(e.getMessage());
938 }
939
940 logger.info("METHOD_EXIT: addTables");
941 }
942
943 /***
944 * Export the schema identified by the <code>Provider</code> parameter
945 * to a file.
946 * <p>To control where the file is written call <code>setOperDir</code>
947 * first.</p>
948 * @param provider Encapsulation of the Data Source information.
949 * @param outputMode Controls the number of files output.
950 * Enumerated in <code>SchemaConstants</code>. One of <code>
951 * OM_SINGLE_FILE, OM_SINGLE_FILE_AND_FILE_PER_TABLE,
952 * OM_FILE_PER_TABLE.</code>
953 */
954 public Document exportToFile(Provider provider, int outputMode)
955 throws DBException {
956 logger.info("METHOD_ENTRY: exportToFile(Provider)");
957
958 Document doc = null;
959
960 try {
961 File schemaFile = new File(getOperDir(), getFileName(provider));
962 Database database = null;
963
964 doc = export(provider);
965
966 FileWriter fw = null;
967
968 logger.debug("Output mode is:" + outputMode);
969
970 switch (outputMode) {
971 case SchemaConstants.OM_SINGLE_FILE:
972
973 // deliberately omit break ;
974 case SchemaConstants.OM_SINGLE_FILE_AND_FILE_PER_TABLE:
975
976 if (!getOperDir().exists()) {
977 getOperDir().mkdirs();
978 }
979
980 logger.warn("Writing single schema file to: " + schemaFile);
981 database = (Database) Unmarshaller.unmarshal(Database.class, doc);
982 fw = new FileWriter(schemaFile);
983 Marshaller.marshal(database, fw);
984
985 // TODO: replace Castor with Axis, because of pretty printing
986 //DOM2Writer.serializeAsXML(doc, fw, false, true) ;
987 fw.close();
988 fw = null; // encourage garbage collection
989
990 if (outputMode == SchemaConstants.OM_SINGLE_FILE) {
991 break; // otherwise continue with file per table as well
992 }
993
994 // else deliberately omit break ;
995 case SchemaConstants.OM_FILE_PER_TABLE:
996 database = (Database) Unmarshaller.unmarshal(Database.class, doc);
997
998 File outputDir = new File(getOperDir(),
999 getFileName(provider).substring(0,
1000 getFileName(provider).indexOf(".xml")));
1001
1002 logger.warn("Writing schema file per table in dir: "
1003 + outputDir);
1004
1005 if (!outputDir.exists()) {
1006 outputDir.mkdirs();
1007 }
1008
1009 /* TODO: This sort of works but leads to funny whitespace (not 'pretty')
1010 NodeList tables = doc.getElementsByTagName("table") ;
1011 for (int j = 0; j < tables.getLength() ; j++) {
1012 String name = null;
1013 Node table = tables.item(j) ;
1014 NodeList tableChildren = table.getChildNodes() ;
1015 for (int k = 0 ; k < tableChildren.getLength() ; k++) {
1016 Node tableChild = tableChildren.item(k) ;
1017 if ("name".equals(tableChild.getNodeName())) {
1018 name = tableChild.getFirstChild().getNodeValue() ;
1019 break ;
1020 }
1021 }
1022 File file = new File(outputDir, name + ".xml") ;
1023 fw = new FileWriter(file) ;
1024 DOM2Writer.serializeAsXML(doc, fw, false, true) ; // true => pretty
1025 fw.close() ;
1026 fw = null; // encourage garbage collection
1027 }*/
1028 for (int j = 0; j < database.getSchemaCount(); j++) {
1029 Schema schema = database.getSchema(j);
1030
1031 for (int i = 0; i < schema.getTableCount(); i++) {
1032 Table table = schema.getTable(i);
1033 File file = new File(outputDir, table.getName()
1034 + ".xml");
1035
1036 fw = new FileWriter(file);
1037 Marshaller.marshal(table, fw);
1038 fw.close();
1039 fw = null; // encourage garbage collection
1040 }
1041 }
1042
1043 break;
1044 default:
1045 logger.error("Unsupported output mode: " + outputMode);
1046 }
1047 } catch (DBException e) {
1048 throw e;
1049 } catch (Exception e) {
1050 logger.error(e.getMessage(), e);
1051 throw new DBException(e.getClass().getName() + ":" + e.getMessage(),
1052 e);
1053 }
1054
1055 logger.info("METHOD_EXIT: exportToFile(Provider)");
1056
1057 return doc;
1058 }
1059
1060 /***
1061 *
1062 *
1063 * @param args
1064 * args[0] - tableName to turn into XML representation
1065 */
1066 public static void main(String[] args) {
1067 try {
1068 SchemaExporter transformer = new SchemaExporter();
1069
1070 transformer.export(args[0], args[1], args[2], args[3]);
1071 } catch (Exception e) {
1072 System.out.println("Failed: " + e.getMessage());
1073 }
1074 }
1075
1076 /***
1077 * Returns the directory name classes should be stored in based on the
1078 * Java package name received
1079 */
1080 private String getPackageDir(String javaPackage) {
1081 StringBuffer dir = new StringBuffer();
1082 StringTokenizer st = new StringTokenizer(javaPackage, ".");
1083
1084 while (st.hasMoreTokens()) {
1085 dir.append(st.nextToken());
1086 dir.append("/");
1087 }
1088
1089 // remove final '/' and return
1090 return dir.substring(0, dir.length() - 1);
1091 }
1092 }
This page was automatically generated by Maven