db-ddlutils-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From to...@apache.org
Subject svn commit: r398681 [1/3] - in /db/ddlutils/trunk/src: java/org/apache/ddlutils/alteration/ java/org/apache/ddlutils/model/ java/org/apache/ddlutils/platform/ java/org/apache/ddlutils/util/ test/org/apache/ddlutils/ test/org/apache/ddlutils/alteration/...
Date Mon, 01 May 2006 20:39:30 GMT
Author: tomdz
Date: Mon May  1 13:39:28 2006
New Revision: 398681

URL: http://svn.apache.org/viewcvs?rev=398681&view=rev
Log:
More implementation of the new alteration algorithm
Added tests for the new alteration algorithm

Added:
    db/ddlutils/trunk/src/test/org/apache/ddlutils/SqlBuilderTest.java
      - copied, changed from r390679, db/ddlutils/trunk/src/test/org/apache/ddlutils/platform/SqlBuilderTest.java
    db/ddlutils/trunk/src/test/org/apache/ddlutils/TestBase.java
    db/ddlutils/trunk/src/test/org/apache/ddlutils/alteration/TestAlterationAlgorithm.java
    db/ddlutils/trunk/src/test/org/apache/ddlutils/platform/TestPlatform.java
Removed:
    db/ddlutils/trunk/src/test/org/apache/ddlutils/platform/SqlBuilderTest.java
Modified:
    db/ddlutils/trunk/src/java/org/apache/ddlutils/alteration/ModelComparator.java
    db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Column.java
    db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Table.java
    db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/SqlBuilder.java
    db/ddlutils/trunk/src/java/org/apache/ddlutils/util/CallbackClosure.java
    db/ddlutils/trunk/src/test/org/apache/ddlutils/TestPlatformBase.java
    db/ddlutils/trunk/src/test/org/apache/ddlutils/alteration/TestModelComparator.java
    db/ddlutils/trunk/src/test/org/apache/ddlutils/platform/TestPlatformImplBase.java

Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/alteration/ModelComparator.java
URL: http://svn.apache.org/viewcvs/db/ddlutils/trunk/src/java/org/apache/ddlutils/alteration/ModelComparator.java?rev=398681&r1=398680&r2=398681&view=diff
==============================================================================
--- db/ddlutils/trunk/src/java/org/apache/ddlutils/alteration/ModelComparator.java (original)
+++ db/ddlutils/trunk/src/java/org/apache/ddlutils/alteration/ModelComparator.java Mon May
 1 13:39:28 2006
@@ -79,7 +79,9 @@
                 changes.add(new AddTableChange(targetTable));
                 for (int fkIdx = 0; fkIdx < targetTable.getForeignKeyCount(); fkIdx++)
                 {
-                    changes.add(new AddForeignKeyChange(sourceTable, targetTable.getForeignKey(fkIdx)));
+                    // we have to use target table's definition here because the
+                    // complete table is new
+                    changes.add(new AddForeignKeyChange(targetTable, targetTable.getForeignKey(fkIdx)));
                 }
             }
             else
@@ -156,7 +158,9 @@
                 {
                     _log.info("Foreign key " + targetFk + " needs to be created for table
" + sourceTable.getName());
                 }
-                changes.add(new AddForeignKeyChange(sourceTable, targetFk));
+                // we have to use the target table here because the foreign key might
+                // reference a new column
+                changes.add(new AddForeignKeyChange(targetTable, targetFk));
             }
         }
 
@@ -185,7 +189,9 @@
                 {
                     _log.info("Index " + targetIndex.getName() + " needs to be created for
table " + sourceTable.getName());
                 }
-                changes.add(new AddIndexChange(sourceTable, targetIndex));
+                // we have to use the target table here because the index might
+                // reference a new column
+                changes.add(new AddIndexChange(targetTable, targetIndex));
             }
         }
 
@@ -217,7 +223,9 @@
             {
                 _log.info("A primary key needs to be added to the table " + sourceTable.getName());
             }
-            changes.add(new AddPrimaryKeyChange(sourceTable, targetPK));
+            // we have to use the target table here because the primary key might
+            // reference a new column
+            changes.add(new AddPrimaryKeyChange(targetTable, targetPK));
         }
         else if ((targetPK.length == 0) && (sourcePK.length > 0))
         {

Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Column.java
URL: http://svn.apache.org/viewcvs/db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Column.java?rev=398681&r1=398680&r2=398681&view=diff
==============================================================================
--- db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Column.java (original)
+++ db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Column.java Mon May  1 13:39:28 2006
@@ -473,7 +473,7 @@
     /**
      * {@inheritDoc}
      */
-    protected Object clone() throws CloneNotSupportedException
+    public Object clone() throws CloneNotSupportedException
     {
         Column result = (Column)super.clone();
 

Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Table.java
URL: http://svn.apache.org/viewcvs/db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Table.java?rev=398681&r1=398680&r2=398681&view=diff
==============================================================================
--- db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Table.java (original)
+++ db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Table.java Mon May  1 13:39:28 2006
@@ -661,7 +661,7 @@
     /**
      * {@inheritDoc}
      */
-    protected Object clone() throws CloneNotSupportedException
+    public Object clone() throws CloneNotSupportedException
     {
         Table result = (Table)super.clone();
 

Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/SqlBuilder.java
URL: http://svn.apache.org/viewcvs/db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/SqlBuilder.java?rev=398681&r1=398680&r2=398681&view=diff
==============================================================================
--- db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/SqlBuilder.java (original)
+++ db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/SqlBuilder.java Mon May  1 13:39:28
2006
@@ -35,6 +35,7 @@
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.ddlutils.DdlUtilsException;
 import org.apache.ddlutils.DynaSqlException;
 import org.apache.ddlutils.Platform;
 import org.apache.ddlutils.PlatformInfo;
@@ -48,6 +49,7 @@
 import org.apache.ddlutils.alteration.ColumnDefaultValueChange;
 import org.apache.ddlutils.alteration.ColumnRequiredChange;
 import org.apache.ddlutils.alteration.ColumnSizeChange;
+import org.apache.ddlutils.alteration.ModelChange;
 import org.apache.ddlutils.alteration.ModelComparator;
 import org.apache.ddlutils.alteration.PrimaryKeyChange;
 import org.apache.ddlutils.alteration.RemoveColumnChange;
@@ -55,6 +57,7 @@
 import org.apache.ddlutils.alteration.RemoveIndexChange;
 import org.apache.ddlutils.alteration.RemovePrimaryKeyChange;
 import org.apache.ddlutils.alteration.RemoveTableChange;
+import org.apache.ddlutils.alteration.TableChange;
 import org.apache.ddlutils.model.Column;
 import org.apache.ddlutils.model.Database;
 import org.apache.ddlutils.model.ForeignKey;
@@ -87,7 +90,7 @@
 public abstract class SqlBuilder
 {
     /** The line separator for in between sql commands. */
-    private static final String LINE_SEPERATOR = System.getProperty("line.separator", "\n");
+    private static final String LINE_SEPARATOR = System.getProperty("line.separator", "\n");
     /** The placeholder for the size value in the native type spec. */
     protected static final String SIZE_PLACEHOLDER = "{0}";
 
@@ -419,7 +422,10 @@
      */
     protected void processChanges(Database currentModel, Database desiredModel, List changes)
throws IOException
     {
-        CallbackClosure callbackClosure = new CallbackClosure(this, "processChange");
+        CallbackClosure callbackClosure = new CallbackClosure(this,
+                                                              "processChange",
+                                                              new Class[] { Database.class,
Database.class, null },
+                                                              new Object[] { currentModel,
desiredModel, null });
 
         // 1st pass: removing external constraints and indices
         applyForSelectedChanges(changes,
@@ -444,7 +450,9 @@
                                                                          ColumnDataTypeChange.class,
                                                                          ColumnSizeChange.class
});
 
-        processTableStructureChanges(CollectionUtils.select(changes, predicate));
+        processTableStructureChanges(currentModel,
+                                     desiredModel,
+                                     CollectionUtils.select(changes, predicate));
 
         // 4th pass: adding tables
         applyForSelectedChanges(changes,
@@ -458,84 +466,322 @@
     }
 
     /**
+     * This is a fall-through callback which generates a warning because a specific
+     * change type wasn't handled.
+     * 
+     * @param currentModel The current database schema
+     * @param desiredModel The desired database schema
+     * @param change       The change object
+     */
+    protected void processChange(Database    currentModel,
+                                 Database    desiredModel,
+                                 ModelChange change) throws IOException
+    {
+        _log.warn("Change of type " + change.getClass() + " was not handled");
+    }
+
+    /**
      * Processes the change representing the removal of a foreign key.
      * 
-     * @param change The change object
+     * @param currentModel The current database schema
+     * @param desiredModel The desired database schema
+     * @param change       The change object
      */
-    protected void processChange(RemoveForeignKeyChange change)
+    protected void processChange(Database               currentModel,
+                                 Database               desiredModel,
+                                 RemoveForeignKeyChange change) throws IOException
     {
-        // TODO: ALTER TABLE DROP FOREIGN KEY
+        writeExternalForeignKeyDropStmt(change.getChangedTable(), change.getForeignKey());
     }
 
     /**
      * Processes the change representing the removal of an index.
      * 
-     * @param change The change object
+     * @param currentModel The current database schema
+     * @param desiredModel The desired database schema
+     * @param change       The change object
      */
-    protected void processChange(RemoveIndexChange change)
+    protected void processChange(Database          currentModel,
+                                 Database          desiredModel,
+                                 RemoveIndexChange change) throws IOException
     {
-        // TODO: DROP INDEX
+        writeExternalIndexDropStmt(change.getChangedTable(), change.getIndex());
     }
 
     /**
      * Processes the change representing the removal of a table.
      * 
-     * @param change The change object
+     * @param currentModel The current database schema
+     * @param desiredModel The desired database schema
+     * @param change       The change object
      */
-    protected void processChange(RemoveTableChange change)
+    protected void processChange(Database          currentModel,
+                                 Database          desiredModel,
+                                 RemoveTableChange change) throws IOException
     {
-        // TODO: DROP TABLE
+        dropTable(change.getChangedTable());
     }
 
     /**
      * Processes the change representing the addition of a table.
      * 
-     * @param change The change object
+     * @param currentModel The current database schema
+     * @param desiredModel The desired database schema
+     * @param change       The change object
      */
-    protected void processChange(AddTableChange change)
-    {
-        // TODO: CREATE TABLE
+    protected void processChange(Database       currentModel,
+                                 Database       desiredModel,
+                                 AddTableChange change) throws IOException
+    {
+        // TODO: where to get the parameters from ?
+        writeTableCreationStmt(desiredModel, change.getNewTable(), null);
+        writeTableCreationStmtEnding(change.getNewTable(), null);
+
+        if (!getPlatformInfo().isPrimaryKeyEmbedded())
+        {
+            writeExternalPrimaryKeysCreateStmt(change.getNewTable(),
+                                               change.getNewTable().getPrimaryKeyColumns());
+        }
+        if (!getPlatformInfo().isIndicesEmbedded())
+        {
+            writeExternalIndicesCreateStmt(change.getNewTable());
+        }
     }
 
     /**
      * Processes the change representing the addition of a foreign key.
      * 
-     * @param change The change object
+     * @param currentModel The current database schema
+     * @param desiredModel The desired database schema
+     * @param change       The change object
      */
-    protected void processChange(AddForeignKeyChange change)
-    {
-        // TODO: ALTER TABLE ADD FOREIGN KEY
+    protected void processChange(Database            currentModel,
+                                 Database            desiredModel,
+                                 AddForeignKeyChange change) throws IOException
+    {
+        writeExternalForeignKeyCreateStmt(desiredModel,
+                                          change.getChangedTable(),
+                                          change.getNewForeignKey());
     }
 
     /**
      * Processes the change representing the addition of an index.
      * 
-     * @param change The change object
+     * @param currentModel The current database schema
+     * @param desiredModel The desired database schema
+     * @param change       The change object
      */
-    protected void processChange(AddIndexChange change)
+    protected void processChange(Database       currentModel,
+                                 Database       desiredModel,
+                                 AddIndexChange change) throws IOException
     {
-        // TODO: CREATE INDEX
+        writeExternalIndexCreateStmt(change.getChangedTable(), change.getNewIndex());
     }
 
     /**
      * Processes the changes to the structure of tables.
      * 
-     * @param changes The change objects
+     * @param currentModel The current database schema
+     * @param desiredModel The desired database schema
+     * @param changes      The change objects
+     */
+    protected void processTableStructureChanges(Database   currentModel,
+                                                Database   desiredModel,
+                                                Collection changes) throws IOException
+    {
+        ListOrderedMap changesPerTable = new ListOrderedMap();
+        boolean        caseSensitive   = getPlatform().isDelimitedIdentifierModeOn();
+
+        // we first sort the changes for the tables
+        // however since the changes might contain source or target tables
+        // we use the names rather than the table objects
+        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)
+        {
+            TableChange change = (TableChange)changeIt.next();
+            String      name   = change.getChangedTable().getName();
+
+            if (!caseSensitive)
+            {
+                name = name.toUpperCase();
+            }
+
+            List changesForTable = (ArrayList)changesPerTable.get(name);
+
+            if (changesForTable == null)
+            {
+                changesForTable = new ArrayList();
+                changesPerTable.put(name, changesForTable);
+            }
+            changesForTable.add(change);
+        }
+        for (Iterator tableChangeIt = changesPerTable.entrySet().iterator(); tableChangeIt.hasNext();)
+        {
+            Map.Entry entry = (Map.Entry)tableChangeIt.next();
+
+            processTableStructureChanges(currentModel,
+                                         desiredModel,
+                                         (String)entry.getKey(),
+                                         (List)entry.getValue());
+        }
+    }
+
+    /**
+     * Processes the changes to the structure of a single table.
+     * 
+     * @param currentModel The current database schema
+     * @param desiredModel The desired database schema
+     * @param tableName    The name of the changed table
+     * @param changes      The change objects for this table
      */
-    protected void processTableStructureChanges(Collection changes)
+    protected void processTableStructureChanges(Database currentModel,
+                                                Database desiredModel,
+                                                String   tableName,
+                                                List     changes) throws IOException
     {
-        // TODO:
-        // * sort the changes according to the affected tables and columns
-        // * for each affected table ...
-        // It might be possible to use the target table directly instead of the
-        // changes, even for datatype changes where we have to create casts in
-        // the INSERT statement (simply compare the native datatypes and create
-        // casts as needed)
-        // Subclasses would then filter through the change collection before (or
-        // after ? -> auto-increment) calling this method in order to use db-specific
-        // statements where possible which might reduce in the number of changes
-        // tables; we however have to take the thus processed changes into account
-        // when creating tables new (i.e. no need for casts when datatype changes)
+        // we might be able to simplify if there is only one change
+        if (changes.size() == 1)
+        {
+            TableChange change = (TableChange)changes.get(0);
+
+            if (change instanceof AddPrimaryKeyChange)
+            {
+                processChange(currentModel, desiredModel, (AddPrimaryKeyChange)change);
+                return;
+            }
+            // TODO: Once named primary keys are supported, the removal can be handled
+            //       here as well (at least for named primary keys)
+        }
+
+        // TODO: where to get the parameters from ?
+        Table sourceTable = currentModel.findTable(tableName, getPlatform().isDelimitedIdentifierModeOn());
+        Table targetTable = desiredModel.findTable(tableName, getPlatform().isDelimitedIdentifierModeOn());
+        Table tempTable   = createTemporaryTable(desiredModel, targetTable, null);
+
+        writeCopyDataStatement(sourceTable, tempTable);
+        dropTable(sourceTable);
+        createTable(desiredModel, targetTable);
+        writeCopyDataStatement(tempTable, targetTable);
+        dropTable(tempTable);
+    }
+
+    /**
+     * Creates a temporary table that corresponds to the given table.
+     * Database-specific implementations may redefine this method if e.g. the
+     * database directly supports temporary tables. The default implementation
+     * simply appends an underscore to the table name and uses that as the
+     * table name.  
+     * 
+     * @param targetModel The target database
+     * @param targetTable The target table
+     * @param parameters  Table creation parameters
+     * @return The temporary table
+     */
+    protected Table createTemporaryTable(Database targetModel, Table targetTable, Map parameters)
throws IOException
+    {
+        Table tempTable = new Table();
+
+        tempTable.setCatalog(targetTable.getCatalog());
+        tempTable.setSchema(targetTable.getSchema());
+        tempTable.setName(targetTable.getName() + "_");
+        tempTable.setType(targetTable.getType());
+        for (int idx = 0; idx < targetTable.getColumnCount(); idx++)
+        {
+            try
+            {
+                tempTable.addColumn((Column)targetTable.getColumn(idx).clone());
+            }
+            catch (CloneNotSupportedException ex)
+            {
+                throw new DdlUtilsException(ex);
+            }
+        }
+
+        writeTableCreationStmt(targetModel, tempTable, parameters);
+        writeTableCreationStmtEnding(tempTable, parameters);
+        return tempTable;
+    }
+
+    /**
+     * Writes a statement that copies the data from the source to the target table. Note
+     * that this copies only those columns that are in both tables.
+     * Database-specific implementations might redefine this method though they usually
+     * it suffices to redefine the {@link #writeCastExpression(Column, Column)} method.
+     * 
+     * @param sourceTable The source table
+     * @param targetTable The target table
+     */
+    protected void writeCopyDataStatement(Table sourceTable, Table targetTable) throws IOException
+    {
+        ListOrderedMap columns = new ListOrderedMap();
+
+        for (int idx = 0; idx < sourceTable.getColumnCount(); idx++)
+        {
+            Column sourceColumn = sourceTable.getColumn(idx);
+            Column targetColumn = targetTable.findColumn(sourceColumn.getName(),
+                                                         getPlatform().isDelimitedIdentifierModeOn());
+
+
+            if (targetColumn != null)
+            {
+                columns.put(sourceColumn, targetColumn);
+            }
+        }
+
+        print("INSERT INTO ");
+        printlnIdentifier(getTableName(targetTable));
+        print(" (");
+        for (Iterator columnIt = columns.keySet().iterator(); columnIt.hasNext();)
+        {
+            printIdentifier(getColumnName((Column)columnIt.next()));
+            if (columnIt.hasNext())
+            {
+                print(",");
+            }
+        }
+        print(") SELECT ");
+        for (Iterator columnsIt = columns.entrySet().iterator(); columnsIt.hasNext();)
+        {
+            Map.Entry entry = (Map.Entry)columnsIt.next();
+
+            writeCastExpression((Column)entry.getKey(),
+                                (Column)entry.getValue());
+            if (columnsIt.hasNext())
+            {
+                print(",");
+            }
+        }
+        print(" FROM ");
+        printlnIdentifier(getTableName(sourceTable));
+        printEndOfStatement();
+    }
+
+    /**
+     * Writes a cast expression that converts the value of the source column to the data
type
+     * of the target column. Per default, simply the name of the source column is written
+     * thereby assuming that any casts happen implicitly.
+     * 
+     * @param sourceColumn The source column
+     * @param targetColumn The target column
+     */
+    protected void writeCastExpression(Column sourceColumn, Column targetColumn) throws IOException
+    {
+        printIdentifier(getColumnName(sourceColumn));
+    }
+    
+    /**
+     * Processes the addition of a primary key to a table. Note that in the default
+     * implementation, this method is called only if this is the only change to the
+     * target table.
+     * 
+     * @param currentModel The current database schema
+     * @param desiredModel The desired database schema
+     * @param change       The change object
+     */
+    protected void processChange(Database            currentModel,
+                                 Database            desiredModel,
+                                 AddPrimaryKeyChange change) throws IOException
+    {
+        writeExternalPrimaryKeysCreateStmt(change.getChangedTable(), change.getPrimaryKeyColumns());
     }
 
     /**
@@ -920,31 +1166,12 @@
      */
     public void createTable(Database database, Table table, Map parameters) throws IOException

     {
-        print("CREATE TABLE ");
-        printlnIdentifier(getTableName(table));
-        println("(");
-
-        writeColumns(table);
-        
-        if (getPlatformInfo().isPrimaryKeyEmbedded())
-        {
-            writeEmbeddedPrimaryKeysStmt(table);
-        }
-        if (getPlatformInfo().isForeignKeysEmbedded())
-        {
-            writeEmbeddedForeignKeysStmt(database, table);
-        }
-        if (getPlatformInfo().isIndicesEmbedded())
-        {
-            writeEmbeddedIndicesStmt(table);
-        }
-        println();
-        print(")");
+        writeTableCreationStmt(database, table, parameters);
         writeTableCreationStmtEnding(table, parameters);
 
         if (!getPlatformInfo().isPrimaryKeyEmbedded())
         {
-            writeExternalPrimaryKeysCreateStmt(table);
+            writeExternalPrimaryKeysCreateStmt(table, table.getPrimaryKeyColumns());
         }
         if (!getPlatformInfo().isIndicesEmbedded())
         {
@@ -1394,6 +1621,37 @@
     }
 
     /** 
+     * Writes the table creation statement without the statement end.
+     *
+     * @param database   The model
+     * @param table      The table
+     * @param parameters Additional platform-specific parameters for the table creation
+     */
+    protected void writeTableCreationStmt(Database database, Table table, Map parameters)
throws IOException
+    {
+        print("CREATE TABLE ");
+        printlnIdentifier(getTableName(table));
+        println("(");
+
+        writeColumns(table);
+        
+        if (getPlatformInfo().isPrimaryKeyEmbedded())
+        {
+            writeEmbeddedPrimaryKeysStmt(table);
+        }
+        if (getPlatformInfo().isForeignKeysEmbedded())
+        {
+            writeEmbeddedForeignKeysStmt(database, table);
+        }
+        if (getPlatformInfo().isIndicesEmbedded())
+        {
+            writeEmbeddedIndicesStmt(table);
+        }
+        println();
+        print(")");
+    }
+    
+    /** 
      * Writes the end of the table creation statement. Per default,
      * only the end of the statement is written, but this can be changed
      * in subclasses.
@@ -1772,12 +2030,11 @@
     /**
      * Writes the primary key constraints of the table as alter table statements.
      * 
-     * @param table The table
+     * @param table             The table
+     * @param primaryKeyColumns The primary key columns 
      */
-    protected void writeExternalPrimaryKeysCreateStmt(Table table) throws IOException
+    protected void writeExternalPrimaryKeysCreateStmt(Table table, Column[] primaryKeyColumns)
throws IOException
     {
-        Column[] primaryKeyColumns = table.getPrimaryKeyColumns();
-
         if ((primaryKeyColumns.length > 0) && shouldGeneratePrimaryKeys(primaryKeyColumns))
         {
             print("ALTER TABLE ");
@@ -1801,16 +2058,6 @@
     protected boolean shouldGeneratePrimaryKeys(Column[] primaryKeyColumns)
     {
         return true;
-/*
-        for (int idx = 0; idx < primaryKeyColumns.length; idx++)
-        {
-            if (!primaryKeyColumns[idx].isAutoIncrement())
-            {
-                return true;
-            }
-        }
-        return false;
-*/
     }
 
     /**
@@ -1991,7 +2238,7 @@
         if (!getPlatformInfo().isAlterTableForDropUsed())
         {
             print(" ON ");
-            print(getTableName(table));
+            printIdentifier(getTableName(table));
         }
         printEndOfStatement();
     }
@@ -2159,7 +2406,7 @@
      */
     protected void println() throws IOException
     {
-        print(LINE_SEPERATOR);
+        print(LINE_SEPARATOR);
     }
 
     /**

Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/util/CallbackClosure.java
URL: http://svn.apache.org/viewcvs/db/ddlutils/trunk/src/java/org/apache/ddlutils/util/CallbackClosure.java?rev=398681&r1=398680&r2=398681&view=diff
==============================================================================
--- db/ddlutils/trunk/src/java/org/apache/ddlutils/util/CallbackClosure.java (original)
+++ db/ddlutils/trunk/src/java/org/apache/ddlutils/util/CallbackClosure.java Mon May  1 13:39:28
2006
@@ -39,35 +39,86 @@
 {
     /** The object on which the callbacks will be invoked. */
     private Object _callee;
+    /** The parameter types. */
+    private Class[] _parameterTypes;
+    /** The parameters. */
+    private Object[] _parameters;
+    /** The position of the callback parameter type. */
+    private int _callbackTypePos = -1;
     /** The cached callbacks. */
     private Map _callbacks = new HashMap();
 
     /**
      * Creates a new closure object.
      * 
-     * @param callee       The object on which the callbacks will be invoked
-     * @param callbackName The name of the callback method
+     * @param callee         The object on which the callbacks will be invoked
+     * @param callbackName   The name of the callback method
+     * @param parameterTypes The parameter types. This array has to contain one <code>null</code>
+     *                       for the type of the object for which the callback is invoked.
+     *                       <code>null</code> or an empty array is regarded
to be the
+     *                       same as an array containing a single <code>null</code>
+     * @param parameters     The actual arguments. The value at the placeholder position
+     *                       will be ignored. Can be <code>null</code> if no
parameter types
+     *                       where given
      */
-    public CallbackClosure(Object callee, String callbackName)
+    public CallbackClosure(Object callee, String callbackName, Class[] parameterTypes, Object[]
parameters)
     {
         _callee = callee;
 
+        if ((parameterTypes == null) || (parameterTypes.length == 0))
+        {
+            _parameterTypes  = new Class[] { null };
+            _parameters      = new Object[] { null };
+            _callbackTypePos = 0;
+        }
+        else
+        {
+            _parameterTypes = new Class[parameterTypes.length];
+            _parameters     = new Object[parameterTypes.length];
+
+            for (int idx = 0; idx < parameterTypes.length; idx++)
+            {
+                if (parameterTypes[idx] == null)
+                {
+                    if (_callbackTypePos >= 0)
+                    {
+                        throw new IllegalArgumentException("The parameter types may contain
null only once");
+                    }
+                    _callbackTypePos = idx;
+                }
+                else
+                {
+                    _parameterTypes[idx] = parameterTypes[idx];
+                    _parameters[idx]     = parameters[idx];
+                }
+            }
+            if (_callbackTypePos < 0)
+            {
+                throw new IllegalArgumentException("The parameter types need to a null placeholder");
+            }
+        }
+        
         Class type = callee.getClass();
 
         // we're caching the callbacks
         do
         {
-            Method[] methods = type.getMethods();
+            Method[] methods = type.getDeclaredMethods();
 
             if (methods != null)
             {
                 for (int idx = 0; idx < methods.length; idx++)
                 {
-                    if (methods[idx].getName().equals(callbackName) &&
-                        (methods[idx].getParameterTypes() != null) &&
-                        (methods[idx].getParameterTypes().length == 1))
+                    Method  method     = methods[idx];
+                    Class[] paramTypes = methods[idx].getParameterTypes();
+
+                    method.setAccessible(true);
+                    if (method.getName().equals(callbackName) && typesMatch(paramTypes))
                     {
-                        _callbacks.put(methods[idx].getParameterTypes()[0], methods[idx]);
+                        if (_callbacks.get(paramTypes[_callbackTypePos]) == null)
+                        {
+                            _callbacks.put(paramTypes[_callbackTypePos], methods[idx]);
+                        }
                     }
                 }
             }
@@ -77,6 +128,28 @@
     }
 
     /**
+     * Checks whether the given method parameter types match the expected ones.
+     * 
+     * @param methodParamTypes The method parameter types
+     * @return <code>true</code> if the parameter types match
+     */
+    private boolean typesMatch(Class[] methodParamTypes)
+    {
+        if ((methodParamTypes == null) || (_parameterTypes.length != methodParamTypes.length))
+        {
+            return false;
+        }
+        for (int idx = 0; idx < _parameterTypes.length; idx++)
+        {
+            if ((idx != _callbackTypePos) && !_parameterTypes[idx].equals(methodParamTypes[idx]))
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+    
+    /**
      * {@inheritDoc}
      */
     public void execute(Object obj) throws DdlUtilsException
@@ -93,7 +166,8 @@
             {
                 try
                 {
-                    callback.invoke(_callee, new Object[] { obj });
+                    _parameters[_callbackTypePos] = obj;
+                    callback.invoke(_callee, _parameters);
                     return;
                 }
                 catch (InvocationTargetException ex)

Copied: db/ddlutils/trunk/src/test/org/apache/ddlutils/SqlBuilderTest.java (from r390679,
db/ddlutils/trunk/src/test/org/apache/ddlutils/platform/SqlBuilderTest.java)
URL: http://svn.apache.org/viewcvs/db/ddlutils/trunk/src/test/org/apache/ddlutils/SqlBuilderTest.java?p2=db/ddlutils/trunk/src/test/org/apache/ddlutils/SqlBuilderTest.java&p1=db/ddlutils/trunk/src/test/org/apache/ddlutils/platform/SqlBuilderTest.java&r1=390679&r2=398681&rev=398681&view=diff
==============================================================================
--- db/ddlutils/trunk/src/test/org/apache/ddlutils/platform/SqlBuilderTest.java (original)
+++ db/ddlutils/trunk/src/test/org/apache/ddlutils/SqlBuilderTest.java Mon May  1 13:39:28
2006
@@ -1,4 +1,4 @@
-package org.apache.ddlutils.platform;
+package org.apache.ddlutils;
 
 /*
  * Copyright 1999-2006 The Apache Software Foundation.
@@ -20,16 +20,17 @@
 import java.util.Map;
 
 import org.apache.ddlutils.Platform;
-import org.apache.ddlutils.TestPlatformBase;
 import org.apache.ddlutils.model.Database;
+import org.apache.ddlutils.platform.SqlBuilder;
+import org.apache.ddlutils.platform.TestPlatform;
 
 /**
- * Test the SqlBuilder (abstract) class.
+ * Test the base SqlBuilder class.
  * 
  * @author Martin van den Bemt
  * @version $Revision: $
  */
-public class SqlBuilderTest extends TestPlatformBase
+public class SqlBuilderTest extends TestBase
 {
     /** The tested model. */
     private static final String TEST_MODEL =
@@ -53,9 +54,10 @@
      */
     public void testUpdateSql()
     {
-        SqlBuilder sqlBuilder = new SqlBuilderImpl(getPlatform());
-        Database   database   = parseDatabaseFromString(TEST_MODEL);
-        Map        map        = new HashMap();
+        TestPlatform platform   = new TestPlatform();
+        SqlBuilder   sqlBuilder = platform.getSqlBuilder();
+        Database     database   = parseDatabaseFromString(TEST_MODEL);
+        Map          map        = new HashMap();
 
         map.put("name", "ddlutils");
         map.put("id", new Integer(0));

Added: db/ddlutils/trunk/src/test/org/apache/ddlutils/TestBase.java
URL: http://svn.apache.org/viewcvs/db/ddlutils/trunk/src/test/org/apache/ddlutils/TestBase.java?rev=398681&view=auto
==============================================================================
--- db/ddlutils/trunk/src/test/org/apache/ddlutils/TestBase.java (added)
+++ db/ddlutils/trunk/src/test/org/apache/ddlutils/TestBase.java Mon May  1 13:39:28 2006
@@ -0,0 +1,126 @@
+package org.apache.ddlutils;
+
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.StringReader;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.ddlutils.io.DatabaseIO;
+import org.apache.ddlutils.model.Database;
+
+/**
+ * Base class for DdlUtils tests.
+ * 
+ * @author Thomas Dudziak
+ * @version $Revision: $
+ */
+public abstract class TestBase extends TestCase
+{
+    /** The log for the tests. */
+    private final Log _log = LogFactory.getLog(getClass());
+
+    /**
+     * Returns the log.
+     * 
+     * @return The log
+     */
+    protected Log getLog()
+    {
+        return _log;
+    }
+
+    /**
+     * Parses the database defined in the given XML definition.
+     * 
+     * @param dbDef
+     *            The database XML definition
+     * @return The database model
+     */
+    protected Database parseDatabaseFromString(String dbDef)
+    {
+        DatabaseIO dbIO = new DatabaseIO();
+        
+        dbIO.setUseInternalDtd(true);
+        dbIO.setValidateXml(false);
+        return dbIO.read(new StringReader(dbDef));
+    }
+
+    /**
+     * Compares the two strings but ignores any whitespace differences. It also
+     * recognizes special delimiter chars.
+     * 
+     * @param expected
+     *            The expected string
+     * @param actual
+     *            The actual string
+     */
+    protected void assertEqualsIgnoringWhitespaces(String expected, String actual)
+    {
+        assertEquals(compressWhitespaces(expected), compressWhitespaces(actual));
+    }
+
+    /**
+     * Compresses the whitespaces in the given string to a single space. Also
+     * recognizes special delimiter chars and removes whitespaces before them.
+     * 
+     * @param original
+     *            The original string
+     * @return The resulting string
+     */
+    private String compressWhitespaces(String original)
+    {
+        StringBuffer result  = new StringBuffer();
+        char         oldChar = ' ';
+        char         curChar;
+
+        for (int idx = 0; idx < original.length(); idx++)
+        {
+            curChar = original.charAt(idx);
+            if (Character.isWhitespace(curChar))
+            {
+                if (oldChar != ' ')
+                {
+                    oldChar = ' ';
+                    result.append(oldChar);
+                }
+            }
+            else
+            {
+                if ((curChar == ',') || (curChar == ';') ||
+                    (curChar == '(') || (curChar == ')'))
+                {
+                    if ((oldChar == ' ') && (result.length() > 0))
+                    {
+                        // we're removing whitespaces before commas/semicolons
+                        result.setLength(result.length() - 1);
+                    }
+                }
+                if ((oldChar == ',') || (oldChar == ';'))
+                {
+                    // we're adding a space after commas/semicolons if necessary
+                    result.append(' ');
+                }
+                result.append(curChar);
+                oldChar = curChar;
+            }
+        }
+        return result.toString();
+    }
+}

Modified: db/ddlutils/trunk/src/test/org/apache/ddlutils/TestPlatformBase.java
URL: http://svn.apache.org/viewcvs/db/ddlutils/trunk/src/test/org/apache/ddlutils/TestPlatformBase.java?rev=398681&r1=398680&r2=398681&view=diff
==============================================================================
--- db/ddlutils/trunk/src/test/org/apache/ddlutils/TestPlatformBase.java (original)
+++ db/ddlutils/trunk/src/test/org/apache/ddlutils/TestPlatformBase.java Mon May  1 13:39:28
2006
@@ -18,14 +18,8 @@
 
 import java.beans.IntrospectionException;
 import java.io.IOException;
-import java.io.StringReader;
 import java.io.StringWriter;
 
-import junit.framework.TestCase;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.ddlutils.io.DatabaseIO;
 import org.apache.ddlutils.model.Database;
 import org.xml.sax.SAXException;
 
@@ -35,7 +29,7 @@
  * @author Thomas Dudziak
  * @version $Revision$
  */
-public abstract class TestPlatformBase extends TestCase
+public abstract class TestPlatformBase extends TestBase
 {
     /** The database schema for testing the column types. */
     public static final String COLUMN_TEST_SCHEMA =
@@ -126,29 +120,17 @@
         "  </table>\n" +
         "</database>";
 
-    /** The log for the tests. */
-    private final Log _log = LogFactory.getLog(getClass());
     /** The tested platform. */
     private Platform _platform;
     /** The writer that the builder of the platform writes to. */
     private StringWriter _writer;
 
     /**
-     * Returns the log.
-     * 
-     * @return The log
-     */
-    protected Log getLog()
-    {
-        return _log;
-    }
-
-    /**
      * {@inheritDoc}
      */
     protected void setUp() throws Exception
     {
-        _writer = new StringWriter();
+        _writer   = new StringWriter();
         _platform = PlatformFactory.createNewPlatformInstance(getDatabaseName());
         _platform.getSqlBuilder().setWriter(_writer);
     }
@@ -159,7 +141,7 @@
     protected void tearDown() throws Exception
     {
         _platform = null;
-        _writer = null;
+        _writer   = null;
     }
 
     /**
@@ -200,25 +182,9 @@
     protected abstract String getDatabaseName();
 
     /**
-     * Parses the database defined in the given XML definition.
-     * 
-     * @param dbDef
-     *            The database XML definition
-     * @return The database model
-     */
-    protected Database parseDatabaseFromString(String dbDef)
-    {
-        DatabaseIO dbIO = new DatabaseIO();
-        
-        dbIO.setUseInternalDtd(true);
-        dbIO.setValidateXml(false);
-        return dbIO.read(new StringReader(dbDef));
-    }
-
-    /**
      * Creates the database creation sql for the given database schema.
      * 
-     * @param schema Th database schema XML
+     * @param schema The database schema XML
      * @return The sql
      */
     protected String createTestDatabase(String schema) throws IntrospectionException, IOException,
SAXException
@@ -229,67 +195,5 @@
         getPlatform().setSqlCommentsOn(false);
         getPlatform().getSqlBuilder().createTables(testDb);
         return getBuilderOutput();
-    }
-
-    /**
-     * Compares the two strings but ignores any whitespace differences. It also
-     * recognizes special delimiter chars.
-     * 
-     * @param expected
-     *            The expected string
-     * @param actual
-     *            The actual string
-     */
-    protected void assertEqualsIgnoringWhitespaces(String expected, String actual)
-    {
-        assertEquals(compressWhitespaces(expected), compressWhitespaces(actual));
-    }
-
-    /**
-     * Compresses the whitespaces in the given string to a single space. Also
-     * recognizes special delimiter chars and removes whitespaces before them.
-     * 
-     * @param original
-     *            The original string
-     * @return The resulting string
-     */
-    private String compressWhitespaces(String original)
-    {
-        StringBuffer result  = new StringBuffer();
-        char         oldChar = ' ';
-        char         curChar;
-
-        for (int idx = 0; idx < original.length(); idx++)
-        {
-            curChar = original.charAt(idx);
-            if (Character.isWhitespace(curChar))
-            {
-                if (oldChar != ' ')
-                {
-                    oldChar = ' ';
-                    result.append(oldChar);
-                }
-            }
-            else
-            {
-                if ((curChar == ',') || (curChar == ';') ||
-                    (curChar == '(') || (curChar == ')'))
-                {
-                    if ((oldChar == ' ') && (result.length() > 0))
-                    {
-                        // we're removing whitespaces before commas/semicolons
-                        result.setLength(result.length() - 1);
-                    }
-                }
-                if ((oldChar == ',') || (oldChar == ';'))
-                {
-                    // we're adding a space after commas/semicolons if necessary
-                    result.append(' ');
-                }
-                result.append(curChar);
-                oldChar = curChar;
-            }
-        }
-        return result.toString();
     }
 }



Mime
View raw message