Coverage Report - org.kuali.core.db.torque.KualiTorqueSchemaDumpTask
 
Classes in this File Line Coverage Branch Coverage Complexity
KualiTorqueSchemaDumpTask
0%
0/328
0%
0/122
2.6
 
 1  
 package org.kuali.core.db.torque;
 2  
 
 3  
 import static org.kuali.db.jdbc.JDBCUtils.closeQuietly;
 4  
 
 5  
 import java.io.File;
 6  
 import java.io.FileOutputStream;
 7  
 import java.io.PrintWriter;
 8  
 import java.io.Writer;
 9  
 import java.sql.Connection;
 10  
 import java.sql.DatabaseMetaData;
 11  
 import java.sql.ResultSet;
 12  
 import java.sql.SQLException;
 13  
 import java.sql.Types;
 14  
 import java.util.ArrayList;
 15  
 import java.util.HashMap;
 16  
 import java.util.List;
 17  
 import java.util.Map;
 18  
 
 19  
 import org.apache.commons.io.FilenameUtils;
 20  
 import org.apache.commons.io.IOUtils;
 21  
 import org.apache.commons.lang.StringUtils;
 22  
 import org.apache.tools.ant.BuildException;
 23  
 import org.apache.tools.ant.Project;
 24  
 import org.apache.torque.engine.database.model.TypeMap;
 25  
 import org.apache.torque.engine.platform.Platform;
 26  
 import org.apache.torque.engine.platform.PlatformFactory;
 27  
 import org.apache.xerces.dom.DocumentImpl;
 28  
 import org.apache.xerces.dom.DocumentTypeImpl;
 29  
 import org.apache.xml.serialize.Method;
 30  
 import org.apache.xml.serialize.OutputFormat;
 31  
 import org.apache.xml.serialize.XMLSerializer;
 32  
 import org.kuali.core.db.torque.pojo.Column;
 33  
 import org.kuali.core.db.torque.pojo.ForeignKey;
 34  
 import org.kuali.core.db.torque.pojo.Index;
 35  
 import org.kuali.core.db.torque.pojo.Reference;
 36  
 import org.w3c.dom.Element;
 37  
 
 38  0
 public class KualiTorqueSchemaDumpTask extends DumpTask {
 39  
 
 40  0
     boolean processTables = true;
 41  0
     boolean processViews = true;
 42  0
     boolean processSequences = true;
 43  
 
 44  
     /**
 45  
      * The file XML will be written to
 46  
      */
 47  
     File schemaXMLFile;
 48  
 
 49  
     /**
 50  
      * DOM document produced.
 51  
      */
 52  
     DocumentImpl doc;
 53  
 
 54  
     /**
 55  
      * The document root element.
 56  
      */
 57  
     Element databaseNode;
 58  
 
 59  
     /**
 60  
      * Map of columns that have primary keys.
 61  
      */
 62  
     Map<String, String> primaryKeys;
 63  
 
 64  
     @Override
 65  
     protected void showConfiguration() {
 66  0
         super.showConfiguration();
 67  0
         log("Exporting to: " + schemaXMLFile.getAbsolutePath());
 68  0
     }
 69  
 
 70  
     protected String getSystemId() {
 71  0
         if (antCompatibilityMode) {
 72  0
             return "database.dtd";
 73  
         } else {
 74  0
             return ImpexDTDResolver.DTD_LOCATION;
 75  
         }
 76  
     }
 77  
 
 78  
     protected DocumentImpl getDocumentImpl() {
 79  0
         DocumentTypeImpl docType = new DocumentTypeImpl(null, "database", null, getSystemId());
 80  0
         DocumentImpl doc = new DocumentImpl(docType);
 81  0
         doc.appendChild(doc.createComment(" " + getComment() + " "));
 82  0
         return doc;
 83  
     }
 84  
 
 85  
     /**
 86  
      * Execute the task
 87  
      */
 88  
     @Override
 89  
     public void execute() throws BuildException {
 90  
         try {
 91  0
             log("--------------------------------------");
 92  0
             log("Impex - Schema Export");
 93  0
             log("--------------------------------------");
 94  0
             log("Loading platform for " + getTargetDatabase());
 95  0
             Platform platform = PlatformFactory.getPlatformFor(targetDatabase);
 96  0
             updateConfiguration(platform);
 97  0
             showConfiguration();
 98  0
             doc = getDocumentImpl();
 99  0
             generateXML(platform);
 100  0
             serialize();
 101  0
         } catch (Exception e) {
 102  0
             throw new BuildException(e);
 103  0
         }
 104  0
         log("Impex - Schema Export finished");
 105  0
     }
 106  
 
 107  
     protected void serialize() throws BuildException {
 108  0
         Writer out = null;
 109  
         try {
 110  0
             File file = new File(FilenameUtils.getFullPath(getSchemaXMLFile().getCanonicalPath()));
 111  0
             file.mkdirs();
 112  0
             out = new PrintWriter(new FileOutputStream(getSchemaXMLFile()));
 113  0
             OutputFormat format = new OutputFormat(Method.XML, getEncoding(), true);
 114  0
             XMLSerializer xmlSerializer = new XMLSerializer(out, format);
 115  0
             xmlSerializer.serialize(doc);
 116  0
         } catch (Exception e) {
 117  0
             throw new BuildException("Error serializing", e);
 118  
         } finally {
 119  0
             IOUtils.closeQuietly(out);
 120  0
         }
 121  0
     }
 122  
 
 123  
     protected Map<String, String> getPrimaryKeys(final Platform platform, final DatabaseMetaData dbMetaData,
 124  
             final String curTable) throws SQLException {
 125  0
         List<String> primKeys = platform.getPrimaryKeys(dbMetaData, schema, curTable);
 126  
 
 127  
         // Set the primary keys.
 128  0
         Map<String, String> primaryKeys = new HashMap<String, String>();
 129  0
         for (int k = 0; k < primKeys.size(); k++) {
 130  0
             String curPrimaryKey = primKeys.get(k);
 131  0
             primaryKeys.put(curPrimaryKey, curPrimaryKey);
 132  
         }
 133  0
         return primaryKeys;
 134  
     }
 135  
 
 136  
     protected Element getColumnElement(final Column col, final String curTable) {
 137  0
         String name = col.getName();
 138  0
         Integer type = col.getSqlType();
 139  0
         int size = col.getSize();
 140  0
         int scale = col.getDecimalDigits();
 141  
 
 142  0
         Integer nullType = col.getNullType();
 143  0
         String defValue = col.getDefValue();
 144  
 
 145  0
         Element column = doc.createElement("column");
 146  0
         column.setAttribute("name", name);
 147  
 
 148  0
         column.setAttribute("type", TypeMap.getTorqueType(type).getName());
 149  
 
 150  0
         if (size > 0
 151  
                 && (type.intValue() == Types.CHAR || type.intValue() == Types.VARCHAR
 152  
                         || type.intValue() == Types.LONGVARCHAR || type.intValue() == Types.DECIMAL || type.intValue() == Types.NUMERIC)) {
 153  0
             column.setAttribute("size", String.valueOf(size));
 154  
         }
 155  
 
 156  0
         if (scale > 0 && (type.intValue() == Types.DECIMAL || type.intValue() == Types.NUMERIC)) {
 157  0
             column.setAttribute("scale", String.valueOf(scale));
 158  
         }
 159  
 
 160  0
         if (primaryKeys.containsKey(name)) {
 161  0
             column.setAttribute("primaryKey", "true");
 162  
             // JHK: protect MySQL from excessively long column in the PK
 163  
             // System.out.println( curTable + "." + name + " / " + size );
 164  0
             if (column.getAttribute("size") != null && size > 765) {
 165  0
                 log("updating column " + curTable + "." + name + " length from " + size + " to 255");
 166  0
                 column.setAttribute("size", "255");
 167  
             }
 168  
         } else {
 169  0
             if (nullType.intValue() == DatabaseMetaData.columnNoNulls) {
 170  0
                 column.setAttribute("required", "true");
 171  
             }
 172  
         }
 173  
 
 174  0
         if (StringUtils.isNotBlank(defValue)) {
 175  0
             defValue = getDefaultValue(defValue);
 176  0
             column.setAttribute("default", defValue);
 177  
         }
 178  0
         return column;
 179  
     }
 180  
 
 181  
     protected String getDefaultValue(String defValue) {
 182  0
         if (StringUtils.isBlank(defValue)) {
 183  0
             return null;
 184  
         }
 185  0
         defValue = defValue.trim();
 186  
         // trim out parens & quotes out of def value.
 187  
         // makes sense for MSSQL. not sure about others.
 188  0
         if (defValue.startsWith("(") && defValue.endsWith(")")) {
 189  0
             defValue = defValue.substring(1, defValue.length() - 1);
 190  
         }
 191  
 
 192  0
         if (defValue.startsWith("'") && defValue.endsWith("'")) {
 193  0
             defValue = defValue.substring(1, defValue.length() - 1);
 194  
         }
 195  0
         if (defValue.equals("NULL")) {
 196  0
             defValue = "";
 197  
         }
 198  0
         return defValue;
 199  
     }
 200  
 
 201  
     protected void processColumns(final DatabaseMetaData dbMetaData, final String curTable, final Element table)
 202  
             throws SQLException {
 203  0
         List<Column> columns = getColumns(dbMetaData, curTable);
 204  0
         for (Column column : columns) {
 205  0
             Element columnElement = getColumnElement(column, curTable);
 206  0
             table.appendChild(columnElement);
 207  0
         }
 208  0
     }
 209  
 
 210  
     protected void processForeignKeys(final DatabaseMetaData dbMetaData, final String curTable, final Element table)
 211  
             throws SQLException {
 212  0
         Map<String, ForeignKey> foreignKeys = getForeignKeys(dbMetaData, curTable);
 213  
 
 214  
         // Foreign keys for this table.
 215  0
         for (String fkName : foreignKeys.keySet()) {
 216  0
             Element fk = getForeignKeyElement(fkName, foreignKeys);
 217  0
             table.appendChild(fk);
 218  0
         }
 219  0
     }
 220  
 
 221  
     protected Element getForeignKeyElement(final String fkName, final Map<String, ForeignKey> foreignKeys) {
 222  0
         Element fk = doc.createElement("foreign-key");
 223  0
         fk.setAttribute("name", fkName);
 224  0
         ForeignKey forKey = foreignKeys.get(fkName);
 225  0
         String foreignKeyTable = forKey.getRefTableName();
 226  0
         List<Reference> refs = forKey.getReferences();
 227  0
         fk.setAttribute("foreignTable", foreignKeyTable);
 228  0
         String onDelete = forKey.getOnDelete();
 229  
         // gmcgrego - just adding onDelete if it's cascade so as not to affect kfs behavior
 230  0
         if (onDelete == "cascade") {
 231  0
             fk.setAttribute("onDelete", onDelete);
 232  
         }
 233  0
         for (Reference refData : refs) {
 234  0
             Element ref = doc.createElement("reference");
 235  0
             ref.setAttribute("local", refData.getLocalColumn());
 236  0
             ref.setAttribute("foreign", refData.getForeignColumn());
 237  0
             fk.appendChild(ref);
 238  0
         }
 239  0
         return fk;
 240  
     }
 241  
 
 242  
     protected void processIndexes(final DatabaseMetaData dbMetaData, final String curTable, final Element table)
 243  
             throws SQLException {
 244  0
         for (Index idx : getIndexes(dbMetaData, curTable)) {
 245  0
             String tagName = idx.isUnique() ? "unique" : "index";
 246  0
             Element index = doc.createElement(tagName);
 247  0
             index.setAttribute("name", idx.getName());
 248  0
             for (String colName : idx.getColumns()) {
 249  0
                 Element col = doc.createElement(tagName + "-column");
 250  0
                 col.setAttribute("name", colName);
 251  0
                 index.appendChild(col);
 252  0
             }
 253  0
             table.appendChild(index);
 254  0
         }
 255  0
     }
 256  
 
 257  
     protected void processTable(final String curTable, final Platform platform, final DatabaseMetaData dbMetaData)
 258  
             throws SQLException {
 259  0
         long start = System.currentTimeMillis();
 260  
 
 261  0
         Element table = doc.createElement("table");
 262  0
         table.setAttribute("name", curTable);
 263  
 
 264  
         // Setup the primary keys.
 265  0
         primaryKeys = getPrimaryKeys(platform, dbMetaData, curTable);
 266  
 
 267  
         // Process columns
 268  0
         processColumns(dbMetaData, curTable, table);
 269  
 
 270  
         // Process foreign keys
 271  0
         processForeignKeys(dbMetaData, curTable, table);
 272  
 
 273  
         // Process indexes
 274  0
         processIndexes(dbMetaData, curTable, table);
 275  
 
 276  
         // Add this table to the XML
 277  0
         databaseNode.appendChild(table);
 278  
 
 279  0
         log(utils.pad("Processed " + curTable, System.currentTimeMillis() - start));
 280  0
     }
 281  
 
 282  
     protected void processTables(final Platform platform, final DatabaseMetaData dbMetaData) throws SQLException {
 283  0
         if (!processTables) {
 284  0
             return;
 285  
         }
 286  
 
 287  0
         List<String> tableList = platform.getTableNames(dbMetaData, schema);
 288  0
         log("Found " + tableList.size() + " tables");
 289  0
         StringFilter filterer = new StringFilter(includePatterns, excludePatterns);
 290  0
         filterer.filter(tableList.iterator());
 291  0
         log("Processing " + tableList.size() + " tables after filtering is applied");
 292  
 
 293  0
         for (String curTable : tableList) {
 294  0
             processTable(curTable, platform, dbMetaData);
 295  
         }
 296  0
     }
 297  
 
 298  
     protected void processViews(final Platform platform, final DatabaseMetaData dbMetaData) throws SQLException {
 299  0
         if (!processViews) {
 300  0
             return;
 301  
         }
 302  0
         List<String> viewNames = getViewNames(dbMetaData);
 303  0
         for (String viewName : viewNames) {
 304  0
             Element view = doc.createElement("view");
 305  0
             view.setAttribute("name", viewName);
 306  
             /**
 307  
              * <view name="" viewdefinition="" />
 308  
              */
 309  0
             String definition = platform.getViewDefinition(dbMetaData.getConnection(), schema, viewName);
 310  0
             definition = definition.replaceAll("\0", "");
 311  0
             view.setAttribute("viewdefinition", definition);
 312  0
             databaseNode.appendChild(view);
 313  0
         }
 314  0
     }
 315  
 
 316  
     protected void processSequences(final Platform platform, final DatabaseMetaData dbMetaData) throws SQLException {
 317  0
         if (!processSequences) {
 318  0
             return;
 319  
         }
 320  0
         List<String> sequenceNames = getSequenceNames(dbMetaData);
 321  0
         for (String sequenceName : sequenceNames) {
 322  0
             Element sequence = doc.createElement("sequence");
 323  0
             sequence.setAttribute("name", sequenceName);
 324  
             /*
 325  
              * <view name="" nextval="" />
 326  
              */
 327  0
             Long nextVal = platform.getSequenceNextVal(dbMetaData.getConnection(), schema, sequenceName);
 328  0
             sequence.setAttribute("nextval", nextVal.toString());
 329  
 
 330  0
             databaseNode.appendChild(sequence);
 331  0
         }
 332  0
         doc.appendChild(databaseNode);
 333  0
     }
 334  
 
 335  
     protected String getName() {
 336  0
         return artifactId;
 337  
     }
 338  
 
 339  
     /**
 340  
      * Generates an XML database schema from JDBC metadata.
 341  
      * 
 342  
      * @throws Exception
 343  
      *             a generic exception.
 344  
      */
 345  
     protected void generateXML(final Platform platform) throws Exception {
 346  
 
 347  0
         Connection connection = null;
 348  
         try {
 349  
             // Attempt to connect to a database.
 350  0
             connection = getConnection();
 351  
 
 352  
             // Get the database Metadata.
 353  0
             DatabaseMetaData dbMetaData = connection.getMetaData();
 354  
 
 355  0
             databaseNode = doc.createElement("database");
 356  0
             databaseNode.setAttribute("name", getName());
 357  
             // JHK added naming method
 358  0
             databaseNode.setAttribute("defaultJavaNamingMethod", "nochange");
 359  
 
 360  0
             processTables(platform, dbMetaData);
 361  0
             processViews(platform, dbMetaData);
 362  0
             processSequences(platform, dbMetaData);
 363  
         } finally {
 364  0
             closeQuietly(connection);
 365  0
         }
 366  0
     }
 367  
 
 368  
     public List<String> getViewNames(final DatabaseMetaData dbMeta) throws SQLException {
 369  0
         log("Getting view list...");
 370  0
         List<String> tables = new ArrayList<String>();
 371  0
         ResultSet tableNames = null;
 372  
         // these are the entity types we want from the database
 373  0
         String[] types = { "VIEW" }; // JHK: removed views from list
 374  
         try {
 375  0
             tableNames = dbMeta.getTables(null, schema, null, types);
 376  0
             while (tableNames.next()) {
 377  0
                 String name = tableNames.getString(3);
 378  0
                 tables.add(name);
 379  0
             }
 380  
         } finally {
 381  0
             if (tableNames != null) {
 382  0
                 tableNames.close();
 383  
             }
 384  
         }
 385  0
         log("Found " + tables.size() + " views.");
 386  0
         return tables;
 387  
     }
 388  
 
 389  
     public boolean isSequence(final String sequenceName) {
 390  0
         return sequenceName.toUpperCase().startsWith("SEQ_") || sequenceName.toUpperCase().startsWith("SEQUENCE_")
 391  
                 || sequenceName.toUpperCase().endsWith("_SEQ") || sequenceName.toUpperCase().endsWith("_SEQUENCE")
 392  
                 || sequenceName.toUpperCase().endsWith("_ID") || sequenceName.toUpperCase().endsWith("_S");
 393  
     }
 394  
 
 395  
     public List<String> getSequenceNames(final DatabaseMetaData dbMeta) throws SQLException {
 396  0
         log("Getting sequence list...");
 397  0
         List<String> tables = new ArrayList<String>();
 398  0
         ResultSet tableNames = null;
 399  
         // these are the entity types we want from the database
 400  0
         String[] types = { "TABLE", "SEQUENCE" }; // JHK: removed views from list
 401  
         try {
 402  0
             tableNames = dbMeta.getTables(null, schema, null, types);
 403  0
             while (tableNames.next()) {
 404  0
                 String name = tableNames.getString(3);
 405  0
                 if (isSequence(name)) {
 406  0
                     tables.add(name);
 407  
                 }
 408  0
             }
 409  
         } finally {
 410  0
             if (tableNames != null) {
 411  0
                 tableNames.close();
 412  
             }
 413  
         }
 414  0
         log("Found " + tables.size() + " sequences.");
 415  0
         return tables;
 416  
     }
 417  
 
 418  
     // for ( int i = 1; i <= tableNames.getMetaData().getColumnCount(); i++ ) {
 419  
     // System.out.print( tableNames.getMetaData().getColumnName( i ) + "," );
 420  
     // }
 421  
     // System.out.println();
 422  
     // for ( int i = 1; i <= tableNames.getMetaData().getColumnCount(); i++ ) {
 423  
     // System.out.print( tableNames.getString( i ) + "," );
 424  
     // }
 425  
     // System.out.println();
 426  
 
 427  
     /**
 428  
      * Retrieves all the column names and types for a given table from JDBC metadata. It returns a List of Lists. Each
 429  
      * element of the returned List is a List with: element 0 => a String object for the column name. element 1 => an
 430  
      * Integer object for the column type. element 2 => size of the column. element 3 => null type.
 431  
      * 
 432  
      * @param dbMeta
 433  
      *            JDBC metadata.
 434  
      * @param tableName
 435  
      *            Table from which to retrieve column information.
 436  
      * @return The list of columns in <code>tableName</code>.
 437  
      * @throws SQLException
 438  
      */
 439  
     protected List<Column> getColumns(final DatabaseMetaData dbMeta, final String tableName) throws SQLException {
 440  0
         List<Column> columns = new ArrayList<Column>();
 441  0
         ResultSet columnSet = null;
 442  
         try {
 443  0
             columnSet = dbMeta.getColumns(null, schema, tableName, null);
 444  0
             while (columnSet.next()) {
 445  0
                 String name = columnSet.getString(4);
 446  0
                 Integer sqlType = new Integer(columnSet.getString(5));
 447  0
                 Integer size = new Integer(columnSet.getInt(7));
 448  0
                 Integer decimalDigits = new Integer(columnSet.getInt(9));
 449  0
                 Integer nullType = new Integer(columnSet.getInt(11));
 450  0
                 String defValue = columnSet.getString(13);
 451  
 
 452  0
                 Column col = new Column();
 453  0
                 col.setName(name);
 454  0
                 col.setSqlType(sqlType);
 455  0
                 col.setSize(size);
 456  0
                 col.setNullType(nullType);
 457  0
                 col.setDefValue(defValue);
 458  0
                 col.setDecimalDigits(decimalDigits);
 459  0
                 columns.add(col);
 460  0
             }
 461  
         } finally {
 462  0
             closeQuietly(columnSet);
 463  0
         }
 464  0
         return columns;
 465  
     }
 466  
 
 467  
     protected String getOnDelete(final ResultSet foreignKeys) throws SQLException {
 468  0
         int deleteRule = foreignKeys.getInt(11);
 469  0
         String onDelete = "none";
 470  0
         if (deleteRule == DatabaseMetaData.importedKeyCascade) {
 471  0
             onDelete = "cascade";
 472  0
         } else if (deleteRule == DatabaseMetaData.importedKeyRestrict) {
 473  0
             onDelete = "restrict";
 474  0
         } else if (deleteRule == DatabaseMetaData.importedKeySetNull) {
 475  0
             onDelete = "setnull";
 476  
         }
 477  0
         return onDelete;
 478  
     }
 479  
 
 480  
     protected String getForeignKeyName(final ResultSet foreignKeys, final String refTableName) throws SQLException {
 481  0
         String fkName = foreignKeys.getString(12);
 482  
         // if FK has no name - make it up (use tablename instead)
 483  0
         if (fkName == null) {
 484  0
             fkName = refTableName;
 485  
         }
 486  0
         return fkName;
 487  
     }
 488  
 
 489  
     protected ForeignKey getNewKualiForeignKey(final String refTableName, final String onDelete) {
 490  0
         ForeignKey fk = new ForeignKey();
 491  0
         fk.setRefTableName(refTableName); // referenced table name
 492  0
         fk.setReferences(new ArrayList<Reference>());
 493  0
         fk.setOnDelete(onDelete);
 494  0
         return fk;
 495  
     }
 496  
 
 497  
     protected void addForeignKey(final Map<String, ForeignKey> fks, final String fkName, final String refTableName,
 498  
             final String onDelete, final ResultSet foreignKeys) throws SQLException {
 499  0
         ForeignKey fk = fks.get(fkName);
 500  0
         if (fk == null) {
 501  0
             fk = getNewKualiForeignKey(refTableName, onDelete);
 502  0
             fks.put(fkName, fk);
 503  
         }
 504  0
         List<Reference> references = fk.getReferences();
 505  0
         Reference reference = new Reference();
 506  0
         reference.setLocalColumn(foreignKeys.getString(8)); // local column
 507  0
         reference.setForeignColumn(foreignKeys.getString(4)); // foreign column
 508  0
         references.add(reference);
 509  0
     }
 510  
 
 511  
     /**
 512  
      * Retrieves a list of foreign key columns for a given table.
 513  
      * 
 514  
      * @param dbMeta
 515  
      *            JDBC metadata.
 516  
      * @param tableName
 517  
      *            Table from which to retrieve FK information.
 518  
      * @return A list of foreign keys in <code>tableName</code>.
 519  
      * @throws SQLException
 520  
      */
 521  
     protected Map<String, ForeignKey> getForeignKeys(final DatabaseMetaData dbMeta, final String tableName)
 522  
             throws SQLException {
 523  0
         Map<String, ForeignKey> fks = new HashMap<String, ForeignKey>();
 524  0
         ResultSet foreignKeys = null;
 525  
         try {
 526  0
             foreignKeys = dbMeta.getImportedKeys(null, schema, tableName);
 527  0
             while (foreignKeys.next()) {
 528  0
                 String refTableName = foreignKeys.getString(3);
 529  0
                 String fkName = getForeignKeyName(foreignKeys, refTableName);
 530  0
                 String onDelete = getOnDelete(foreignKeys);
 531  0
                 addForeignKey(fks, fkName, refTableName, onDelete, foreignKeys);
 532  0
             }
 533  0
         } catch (SQLException e) {
 534  
             // this seems to be happening in some db drivers (sybase)
 535  
             // when retrieving foreign keys from views.
 536  0
             log("Could not read foreign keys for Table " + tableName + " : " + e.getMessage(), Project.MSG_WARN);
 537  
         } finally {
 538  0
             closeQuietly(foreignKeys);
 539  0
         }
 540  0
         return fks;
 541  
     }
 542  
 
 543  
     protected String getPrimaryKeyName(final String tableName, final DatabaseMetaData dbMeta) throws SQLException {
 544  0
         ResultSet pkInfo = null;
 545  
         try {
 546  0
             pkInfo = dbMeta.getPrimaryKeys(null, schema, tableName);
 547  0
             if (pkInfo.next()) {
 548  0
                 return pkInfo.getString("PK_NAME");
 549  
             }
 550  0
         } catch (SQLException e) {
 551  0
             log("Could not locate primary key info for " + tableName + " : " + e.getMessage(), Project.MSG_WARN);
 552  
         } finally {
 553  0
             closeQuietly(pkInfo);
 554  0
         }
 555  0
         return null;
 556  
     }
 557  
 
 558  
     protected Index getTableIndex(final ResultSet indexInfo, final String pkName) throws SQLException {
 559  0
         Index index = new Index();
 560  0
         index.setName(indexInfo.getString("INDEX_NAME"));
 561  0
         index.setUnique(!indexInfo.getBoolean("NON_UNIQUE"));
 562  0
         return index;
 563  
     }
 564  
 
 565  
     protected void addIndexIfNotPK(final Index index, final String pkName, final List<Index> indexes) {
 566  
         // if has the same name as the PK, don't add it to the index list
 567  0
         if (pkName == null || !pkName.equals(index.getName())) {
 568  0
             indexes.add(index);
 569  0
             log("Added " + index.getName() + " to index list", Project.MSG_DEBUG);
 570  
         } else {
 571  0
             log("Skipping PK: " + index.getName(), Project.MSG_DEBUG);
 572  
         }
 573  0
     }
 574  
 
 575  
     public List<Index> getIndexes(final DatabaseMetaData dbMeta, final String tableName) throws SQLException {
 576  0
         List<Index> indexes = new ArrayList<Index>();
 577  
 
 578  
         // need to ensure that the PK is not returned as an index
 579  0
         String pkName = getPrimaryKeyName(tableName, dbMeta);
 580  
 
 581  0
         ResultSet indexInfo = null;
 582  
         try {
 583  0
             indexInfo = dbMeta.getIndexInfo(null, schema, tableName, false, true);
 584  0
             Index currIndex = null;
 585  0
             while (indexInfo.next()) {
 586  
 
 587  
                 // Extract the name of the index
 588  0
                 String name = indexInfo.getString("INDEX_NAME");
 589  0
                 if (name == null) {
 590  
                     // If there is no name, we are done
 591  0
                     continue;
 592  
                 }
 593  
 
 594  
                 // If this is the first time we are assigning a value to currIndex, OR
 595  
                 // we have scrolled to the next row in the result set and are now on a
 596  
                 // new index, we need to add to our list of indexes
 597  0
                 if (currIndex == null || !name.equals(currIndex.getName())) {
 598  
                     // Get a new TableIndex object
 599  0
                     currIndex = getTableIndex(indexInfo, pkName);
 600  
                     // Add this index to the list if it is not the primary key index
 601  
                     // The PK is handled elsewhere
 602  0
                     addIndexIfNotPK(currIndex, pkName, indexes);
 603  
                 }
 604  
 
 605  
                 // Add column information to the current index
 606  0
                 currIndex.getColumns().add(indexInfo.getString("COLUMN_NAME"));
 607  0
             }
 608  0
         } catch (SQLException e) {
 609  0
             log("Could not read indexes for Table " + tableName + " : " + e.getMessage(), Project.MSG_WARN);
 610  
         } finally {
 611  0
             closeQuietly(indexInfo);
 612  0
         }
 613  0
         return indexes;
 614  
     }
 615  
 
 616  
     public DocumentImpl getDoc() {
 617  0
         return doc;
 618  
     }
 619  
 
 620  
     public void setDoc(final DocumentImpl doc) {
 621  0
         this.doc = doc;
 622  0
     }
 623  
 
 624  
     public Element getDatabaseNode() {
 625  0
         return databaseNode;
 626  
     }
 627  
 
 628  
     public void setDatabaseNode(final Element databaseNode) {
 629  0
         this.databaseNode = databaseNode;
 630  0
     }
 631  
 
 632  
     public Map<String, String> getPrimaryKeys() {
 633  0
         return primaryKeys;
 634  
     }
 635  
 
 636  
     public void setPrimaryKeys(final Map<String, String> primaryKeys) {
 637  0
         this.primaryKeys = primaryKeys;
 638  0
     }
 639  
 
 640  
     public boolean isProcessTables() {
 641  0
         return processTables;
 642  
     }
 643  
 
 644  
     public boolean isProcessViews() {
 645  0
         return processViews;
 646  
     }
 647  
 
 648  
     public boolean isProcessSequences() {
 649  0
         return processSequences;
 650  
     }
 651  
 
 652  
     public File getSchemaXMLFile() {
 653  0
         return schemaXMLFile;
 654  
     }
 655  
 
 656  
     public void setSchemaXMLFile(final File schemaXMLFile) {
 657  0
         this.schemaXMLFile = schemaXMLFile;
 658  0
     }
 659  
 
 660  
     public void setProcessTables(final boolean processTables) {
 661  0
         this.processTables = processTables;
 662  0
     }
 663  
 
 664  
     public void setProcessViews(final boolean processViews) {
 665  0
         this.processViews = processViews;
 666  0
     }
 667  
 
 668  
     public void setProcessSequences(final boolean processSequences) {
 669  0
         this.processSequences = processSequences;
 670  0
     }
 671  
 }