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 @@
/**
* [EMAIL PROTECTED]
*/
- 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 @@
/**
* [EMAIL PROTECTED]
*/
- 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 [EMAIL PROTECTED]
#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;
+ }
+
+ /**
* [EMAIL PROTECTED]
*/
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;
- }
-
- /**
* [EMAIL PROTECTED]
*/
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();
}
}