You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ke...@apache.org on 2011/09/12 16:56:14 UTC

svn commit: r1169782 - in /incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql: sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/ sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/jdbc/ sql-tests-...

Author: kevin
Date: Mon Sep 12 14:56:13 2011
New Revision: 1169782

URL: http://svn.apache.org/viewvc?rev=1169782&view=rev
Log:
ISIS-118: Collections can now handle polymorphic types, including classes.

Added:
    incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/ForeignKeyCollectionMapper.java
      - copied, changed from r1169477, incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/CombinedCollectionMapper.java
    incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/ForeignKeyInChildCollectionMapper.java
      - copied, changed from r1169477, incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/MultiColumnCombinedCollectionMapper.java
    incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/PolymorphicForeignKeyInChildCollectionMapper.java   (with props)
Modified:
    incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/AbstractAutoMapper.java
    incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/CombinedCollectionMapper.java
    incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/MultiColumnCombinedCollectionMapper.java
    incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/jdbc/JdbcAbstractReferenceFieldMapping.java
    incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-tests-common/src/test/java/org/apache/isis/runtimes/dflt/objectstores/sql/PolymorphismTest.java

Modified: incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/AbstractAutoMapper.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/AbstractAutoMapper.java?rev=1169782&r1=1169781&r2=1169782&view=diff
==============================================================================
--- incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/AbstractAutoMapper.java (original)
+++ incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/AbstractAutoMapper.java Mon Sep 12 14:56:13 2011
@@ -82,10 +82,13 @@ public abstract class AbstractAutoMapper
     private void setUpFieldMappers(final FieldMappingLookup lookup, final ObjectMappingLookup objectMapperLookup,
         final String className, final String parameterBase) {
         final IsisConfiguration configParameters = IsisContext.getConfiguration();
-        table = configParameters.getString(parameterBase + "table");
+        table = configParameters.getString(parameterBase + ".table." + className);
         if (table == null) {
-            final String name = "isis_" + className.substring(className.lastIndexOf('.') + 1).toUpperCase();
-            table = Sql.sqlName(name);
+            // final String name = "isis_" + className.substring(className.lastIndexOf('.') + 1);
+            final String name = getTableNameFromSpecification(specification);
+            table = name;
+        } else {
+            table = Sql.tableIdentifier(table);
         }
 
         dbCreatesId = configParameters.getBoolean(parameterBase + "db-ids", false);
@@ -95,11 +98,13 @@ public abstract class AbstractAutoMapper
             // setupSpecifiedMapping(specification, configParameters, parameterBase);
         }
 
-        table = Sql.tableIdentifier(table);
-
         LOG.info("table mapping: " + table + " (" + columnList() + ")");
     }
 
+    protected String getTableNameFromSpecification(ObjectSpecification objectSpecification) {
+        return Sql.tableIdentifier(Sql.sqlName("isis_" + objectSpecification.getShortIdentifier()));
+    }
+
     protected List<ObjectAssociation> fields = new ArrayList<ObjectAssociation>();
 
     protected void getExtraFields(List<ObjectAssociation> fields) {
@@ -126,8 +131,6 @@ public abstract class AbstractAutoMapper
         final ObjectAssociation[] oneToManyProperties = new ObjectAssociation[collectionFieldCount];
         collectionMappers = new CollectionMapper[collectionFieldCount];
         collectionMapperFields = new String[collectionFieldCount];
-        // Properties collectionMappings = configParameters.getPropertiesStrippingPrefix(parameterBase +
-        // "collection");
         final IsisConfiguration subset = IsisContext.getConfiguration().createSubset(parameterBase + ".mapper.");
 
         for (int i = 0, simpleFieldNo = 0, collectionFieldNo = 0; i < fields.size(); i++) {
@@ -137,24 +140,23 @@ public abstract class AbstractAutoMapper
             } else if (field.isOneToManyAssociation()) {
                 oneToManyProperties[collectionFieldNo] = field;
 
-                // TODO: Replace "new CombinedCollectionMapper" with a factory method(?) to allow a different
+                // TODO: Replace "new ForeignKeyCollectionMapper" with a factory method(?) to allow a different
                 // default CollectionMapper
                 final String type = subset.getString(field.getId());
                 if (type == null || type.equals("association-table")) {
                     // collectionMappers[collectionFieldNo] = new AutoCollectionMapper(specification,
                     // oneToManyProperties[collectionFieldNo], lookup);
                     // collectionMappers[collectionFieldNo] = new
-                    // CombinedCollectionMapper(oneToManyProperties[collectionFieldNo], parameterBase, lookup,
+                    // ForeignKeyCollectionMapper(oneToManyProperties[collectionFieldNo], parameterBase, lookup,
                     // objectMapperLookup);
 
                     CollectionMapper collectionMapper = null;
 
                     // Trying to detect recursion, here.
-                    // Let MultiColumnCombinedCollectionMapper find itself when a field is a collection of the current
+                    // Let ForeignKeyInChildCollectionMapper find itself when a field is a collection of the current
                     // field type.
-                    if (this instanceof MultiColumnCombinedCollectionMapper) {
-                        // TODO: Polymorphism - is it sufficient for the collectionMapper to handle the subclasses?
-                        MultiColumnCombinedCollectionMapper mc = (MultiColumnCombinedCollectionMapper) this;
+                    if (this instanceof ForeignKeyInChildCollectionMapper) {
+                        ForeignKeyInChildCollectionMapper mc = (ForeignKeyInChildCollectionMapper) this;
 
                         if (mc.priorField == field) {
                             collectionMapper = mc;
@@ -162,9 +164,17 @@ public abstract class AbstractAutoMapper
                     }
 
                     if (collectionMapper == null) {
-                        collectionMapper =
-                            new MultiColumnCombinedCollectionMapper(oneToManyProperties[collectionFieldNo],
-                                parameterBase, lookup, objectMapperLookup, this, field);
+                        // TODO: Polymorphism - is it sufficient for the collectionMapper to handle the subclasses?
+                        if (field.getSpecification().hasSubclasses()) {
+                            collectionMapper =
+                                new PolymorphicForeignKeyInChildCollectionMapper(
+                                    oneToManyProperties[collectionFieldNo], parameterBase, lookup, objectMapperLookup,
+                                    this, field);
+                        } else {
+                            collectionMapper =
+                                new ForeignKeyInChildCollectionMapper(oneToManyProperties[collectionFieldNo],
+                                    parameterBase, lookup, objectMapperLookup, this, field);
+                        }
                     }
 
                     collectionMappers[collectionFieldNo] = collectionMapper;
@@ -177,7 +187,7 @@ public abstract class AbstractAutoMapper
                         throw new SqlObjectStoreException("Expected property " + property);
                     }
                     /*
-                     * collectionMappers[collectionFieldNo] = new CombinedCollectionMapper(elementType,
+                     * collectionMappers[collectionFieldNo] = new ForeignKeyCollectionMapper(elementType,
                      * oneToManyProperties[collectionFieldNo], parameterBase, lookup, objectMapperLookup);
                      */
                 } else {

Copied: incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/ForeignKeyCollectionMapper.java (from r1169477, incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/CombinedCollectionMapper.java)
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/ForeignKeyCollectionMapper.java?p2=incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/ForeignKeyCollectionMapper.java&p1=incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/CombinedCollectionMapper.java&r1=1169477&r2=1169782&rev=1169782&view=diff
==============================================================================
--- incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/CombinedCollectionMapper.java (original)
+++ incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/ForeignKeyCollectionMapper.java Mon Sep 12 14:56:13 2011
@@ -20,6 +20,7 @@
 package org.apache.isis.runtimes.dflt.objectstores.sql.auto;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 
 import org.apache.log4j.Logger;
@@ -44,8 +45,14 @@ import org.apache.isis.runtimes.dflt.obj
 import org.apache.isis.runtimes.dflt.objectstores.sql.mapping.ObjectReferenceMapping;
 import org.apache.isis.runtimes.dflt.runtime.persistence.PersistorUtil;
 
-public class CombinedCollectionMapper extends AbstractAutoMapper implements CollectionMapper {
-    private static final Logger LOG = Logger.getLogger(CombinedCollectionMapper.class);
+/**
+ * Stores 1-to-many collections by creating a foreign-key column in the table for the incoming objectAssociation class.
+ * This assumes this the class is only ever in 1 collection parent.
+ * 
+ * @version $Rev$ $Date$
+ */
+public class ForeignKeyCollectionMapper extends AbstractAutoMapper implements CollectionMapper {
+    private static final Logger LOG = Logger.getLogger(ForeignKeyCollectionMapper.class);
     private final ObjectAssociation field;
     private final IdMapping idMapping;
     private final VersionMapping versionMapping;
@@ -55,16 +62,13 @@ public class CombinedCollectionMapper ex
     private final ObjectMappingLookup objectMapperLookup2;
 
     private ObjectMapping originalMapping = null;
-    private final boolean isAbstract;
 
-    public CombinedCollectionMapper(final ObjectAssociation objectAssociation, final String parameterBase,
+    public ForeignKeyCollectionMapper(final ObjectAssociation objectAssociation, final String parameterBase,
         final FieldMappingLookup lookup, final ObjectMappingLookup objectMapperLookup) {
         super(objectAssociation.getSpecification().getFullIdentifier(), parameterBase, lookup, objectMapperLookup);
 
         this.field = objectAssociation;
 
-        isAbstract = field.getSpecification().isAbstract();
-
         objectMapperLookup2 = objectMapperLookup;
 
         idMapping = lookup.createIdMapping();
@@ -74,7 +78,7 @@ public class CombinedCollectionMapper ex
         foreignKeyName = Sql.sqlName("fk_" + getColumnName());
 
         foreignKeyName = Sql.identifier(foreignKeyName);
-        foreignKeyMapping = lookup.createMapping(columnName, objectAssociation.getSpecification());
+        foreignKeyMapping = lookup.createMapping(columnName, specification);
     }
 
     protected String determineColumnName(final ObjectAssociation objectAssociation) {
@@ -89,76 +93,67 @@ public class CombinedCollectionMapper ex
         this.columnName = columnName;
     }
 
-    @Override
-    public boolean needsTables(final DatabaseConnector connection) {
-        return isAbstract || !connection.hasColumn(table, foreignKeyName);
+    protected String getForeignKeyName() {
+        return foreignKeyName;
     }
 
     @Override
     public void startup(final DatabaseConnector connector, final FieldMappingLookup lookup) {
         if (originalMapping == null) {
-            originalMapping = objectMapperLookup.getMapping(field.getSpecification(), null);
+            originalMapping = objectMapperLookup.getMapping(specification, null);
         }
         originalMapping.startup(connector, objectMapperLookup2);
         super.startup(connector, lookup);
     }
 
     @Override
+    public boolean needsTables(final DatabaseConnector connection) {
+        return !connection.hasColumn(table, foreignKeyName);
+    }
+
+    @Override
     public void createTables(final DatabaseConnector connection) {
-        if (isAbstract) {
-            return;
-        }
         final StringBuffer sql = new StringBuffer();
         sql.append("alter table ");
         sql.append(table);
         sql.append(" add ");
-        foreignKeyMapping.appendColumnDefinitions(sql);
+        appendColumnDefinitions(sql);
         connection.update(sql.toString());
     }
 
+    protected void appendCollectionUpdateColumnsToNull(StringBuffer sql) {
+        sql.append(foreignKeyName + "=NULL ");
+    }
+
+    protected void appendCollectionWhereValues(final DatabaseConnector connector, final ObjectAdapter parent,
+        final StringBuffer sql) {
+        foreignKeyMapping.appendUpdateValues(connector, sql, parent);
+    }
+
+    protected void appendCollectionUpdateValues(final DatabaseConnector connector, final ObjectAdapter parent,
+        final StringBuffer sql) {
+        appendCollectionWhereValues(connector, parent, sql);
+    }
+
+    protected void appendColumnDefinitions(final StringBuffer sql) {
+        foreignKeyMapping.appendColumnDefinitions(sql);
+    }
+
     @Override
     public void loadInternalCollection(final DatabaseConnector connector, final ObjectAdapter parent,
         final boolean makeResolved) {
 
-        if (isAbstract) { // hasSubClasses, too?
-            // TODO: Polymorphism: loadInternalCollection must load the instance from all the possible child tables.
-            LOG.debug("Is Abstract");
-            return;
-        }
-
         final ObjectAdapter collection = field.get(parent);
         if (collection.getResolveState().canChangeTo(ResolveState.RESOLVING)) {
             LOG.debug("loading internal collection " + field);
             PersistorUtil.start(collection, ResolveState.RESOLVING);
 
-            final StringBuffer sql = new StringBuffer();
-            sql.append("select ");
-            idMapping.appendColumnNames(sql);
+            final List<ObjectAdapter> list = new ArrayList<ObjectAdapter>();
 
-            sql.append(", ");
-            final String columnList = columnList();
-            if (columnList.length() > 0) {
-                sql.append(columnList);
-                sql.append(", ");
-            }
-            sql.append(versionMapping.appendSelectColumns());
-            sql.append(" from ");
-            sql.append(table);
-            sql.append(" where ");
-            foreignKeyMapping.appendUpdateValues(connector, sql, parent);
+            loadCollectionIntoList(connector, parent, makeResolved, list);
 
-            final Results rs = connector.select(sql.toString());
-            final List<ObjectAdapter> list = new ArrayList<ObjectAdapter>();
-            while (rs.next()) {
-                final Oid oid = idMapping.recreateOid(rs, specification);
-                final ObjectAdapter element = getAdapter(specification, oid);
-                loadFields(element, rs, makeResolved);
-                LOG.debug("  element  " + element.getOid());
-                list.add(element);
-            }
             final CollectionFacet collectionFacet = collection.getSpecification().getFacet(CollectionFacet.class);
             collectionFacet.init(collection, list.toArray(new ObjectAdapter[list.size()]));
-            rs.close();
             PersistorUtil.end(collection);
 
             // TODO: Need to finalise this behaviour. At the moment, all collections will get infinitely resolved. I
@@ -174,6 +169,35 @@ public class CombinedCollectionMapper ex
         }
     }
 
+    protected void loadCollectionIntoList(final DatabaseConnector connector, final ObjectAdapter parent,
+        final boolean makeResolved, final List<ObjectAdapter> list) {
+        final StringBuffer sql = new StringBuffer();
+        sql.append("select ");
+        idMapping.appendColumnNames(sql);
+
+        sql.append(", ");
+        final String columnList = columnList();
+        if (columnList.length() > 0) {
+            sql.append(columnList);
+            sql.append(", ");
+        }
+        sql.append(versionMapping.appendSelectColumns());
+        sql.append(" from ");
+        sql.append(table);
+        sql.append(" where ");
+        appendCollectionWhereValues(connector, parent, sql);
+
+        final Results rs = connector.select(sql.toString());
+        while (rs.next()) {
+            final Oid oid = idMapping.recreateOid(rs, specification);
+            final ObjectAdapter element = getAdapter(specification, oid);
+            loadFields(element, rs, makeResolved);
+            LOG.debug("  element  " + element.getOid());
+            list.add(element);
+        }
+        rs.close();
+    }
+
     protected void loadFields(final ObjectAdapter object, final Results rs, final boolean makeResolved) {
         if (object.getResolveState().canChangeTo(ResolveState.RESOLVING)) {
             PersistorUtil.start(object, ResolveState.RESOLVING);
@@ -187,24 +211,30 @@ public class CombinedCollectionMapper ex
         }
     }
 
+    /**
+     * Override this in the Polymorphic case to return just the elements that are appropriate for the subclass currently
+     * being handled.
+     * 
+     * @param collection
+     * @return those elements that ought to be used.
+     */
+    protected Iterator<ObjectAdapter> getElementsForCollectionAsIterator(final ObjectAdapter collection) {
+        final CollectionFacet collectionFacet = collection.getSpecification().getFacet(CollectionFacet.class);
+        final Iterable<ObjectAdapter> elements = collectionFacet.iterable(collection);
+        return elements.iterator();
+    }
+
     @Override
     public void saveInternalCollection(final DatabaseConnector connector, final ObjectAdapter parent) {
         final ObjectAdapter collection = field.get(parent);
         LOG.debug("Saving internal collection " + collection);
 
-        final CollectionFacet collectionFacet = collection.getSpecification().getFacet(CollectionFacet.class);
-        final Iterable<ObjectAdapter> elements = collectionFacet.iterable(collection);
+        final Iterator<ObjectAdapter> elements = getElementsForCollectionAsIterator(collection);
 
         // TODO What is needed to allow a collection update (add/remove) to mark the collection as dirty?
         // checkIfDirty(collection);
 
-        if (elements.iterator().hasNext() == false) {
-            return;
-        }
-
-        if (isAbstract) {
-            // TODO: Polymorphism: saveInternalCollection must save instance into the appropriate child tables.
-            LOG.debug("Is Abstract");
+        if (elements.hasNext() == false) {
             return;
         }
 
@@ -214,9 +244,9 @@ public class CombinedCollectionMapper ex
         sql.append("update ");
         sql.append(table);
         sql.append(" set ");
-        sql.append(foreignKeyName);
-        sql.append(" = NULL where ");
-        foreignKeyMapping.appendUpdateValues(connector, sql, parent);
+        appendCollectionUpdateColumnsToNull(sql);
+        sql.append(" where ");
+        appendCollectionWhereValues(connector, parent, sql);
         connector.update(sql.toString());
 
         // Reinstall collection parent
@@ -225,8 +255,7 @@ public class CombinedCollectionMapper ex
         update.append("update ");
         update.append(table);
         update.append(" set ");
-
-        foreignKeyMapping.appendUpdateValues(connector, update, parent);
+        appendCollectionUpdateValues(connector, parent, update);
         update.append(" where ");
 
         idMapping.appendColumnNames(update);
@@ -234,7 +263,9 @@ public class CombinedCollectionMapper ex
         update.append(" IN (");
 
         int count = 0;
-        for (final ObjectAdapter element : elements) {
+        for (Iterator<ObjectAdapter> iterator = elements; iterator.hasNext();) {
+            ObjectAdapter element = iterator.next();
+
             if (count++ > 0) {
                 update.append(",");
             }

Copied: incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/ForeignKeyInChildCollectionMapper.java (from r1169477, incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/MultiColumnCombinedCollectionMapper.java)
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/ForeignKeyInChildCollectionMapper.java?p2=incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/ForeignKeyInChildCollectionMapper.java&p1=incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/MultiColumnCombinedCollectionMapper.java&r1=1169477&r2=1169782&rev=1169782&view=diff
==============================================================================
--- incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/MultiColumnCombinedCollectionMapper.java (original)
+++ incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/ForeignKeyInChildCollectionMapper.java Mon Sep 12 14:56:13 2011
@@ -38,12 +38,12 @@ import org.apache.isis.runtimes.dflt.obj
  * 
  * @author Kevin
  */
-public class MultiColumnCombinedCollectionMapper extends CombinedCollectionMapper {
-    private static final Logger LOG = Logger.getLogger(CombinedCollectionMapper.class);
+public class ForeignKeyInChildCollectionMapper extends ForeignKeyCollectionMapper {
+    private static final Logger LOG = Logger.getLogger(ForeignKeyCollectionMapper.class);
 
     // TODO: Needs to detect if field has subclasses, and to add FK_* column to *all* non-abstract subclasses.
 
-    public MultiColumnCombinedCollectionMapper(final ObjectAssociation objectAssociation, final String parameterBase,
+    public ForeignKeyInChildCollectionMapper(final ObjectAssociation objectAssociation, final String parameterBase,
         final FieldMappingLookup lookup, final ObjectMappingLookup objectMapperLookup,
         AbstractAutoMapper abstractAutoMapper, ObjectAssociation field) {
         super(objectAssociation, parameterBase, lookup, objectMapperLookup);

Added: incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/PolymorphicForeignKeyInChildCollectionMapper.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/PolymorphicForeignKeyInChildCollectionMapper.java?rev=1169782&view=auto
==============================================================================
--- incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/PolymorphicForeignKeyInChildCollectionMapper.java (added)
+++ incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/PolymorphicForeignKeyInChildCollectionMapper.java Mon Sep 12 14:56:13 2011
@@ -0,0 +1,218 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ */
+
+/**
+ * 
+ */
+package org.apache.isis.runtimes.dflt.objectstores.sql.auto;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.isis.runtimes.dflt.objectstores.sql.DatabaseConnector;
+import org.apache.isis.runtimes.dflt.objectstores.sql.FieldMappingLookup;
+import org.apache.isis.runtimes.dflt.objectstores.sql.ObjectMappingLookup;
+
+/**
+ * Used to map 1-to-many collections by creating, in the child table, 1 column per parent collection. The column is
+ * named by combining the final part of the parent class name and the collection variable name.
+ * 
+ * @author Kevin
+ */
+public class PolymorphicForeignKeyInChildCollectionMapper extends ForeignKeyInChildCollectionMapper {
+
+    private static final Logger LOG = Logger.getLogger(PolymorphicForeignKeyInChildCollectionMapper.class);
+
+    // TODO: Fields have subclasses. Must add FK_* column to *all* non-abstract subclasses.
+    private final ObjectAssociation baseField;
+    private final List<String> tables;
+    private final List<ObjectSpecification> tableSpecifications;
+    // private final String classColumnName;
+
+    // For iterating through the subclasses
+    private ObjectSpecification currentTableSpecification;
+    private Iterator<ObjectAdapter> currentIterator;
+    private List<ObjectAdapter> currentCollection;
+    private int currentIndexStart;
+    private int currentIndex;
+
+    public PolymorphicForeignKeyInChildCollectionMapper(final ObjectAssociation objectAssociation,
+        final String parameterBase, final FieldMappingLookup lookup, final ObjectMappingLookup objectMapperLookup,
+        AbstractAutoMapper abstractAutoMapper, ObjectAssociation field) {
+        super(objectAssociation, parameterBase, lookup, objectMapperLookup, abstractAutoMapper, field);
+
+        this.baseField = objectAssociation;
+        // classColumnName = Sql.identifier(getForeignKeyName() + "_cls");
+
+        tables = new ArrayList<String>();
+        tableSpecifications = new ArrayList<ObjectSpecification>();
+        addSubSpecificationsToTable(specification);
+
+    }
+
+    protected void addSubSpecificationsToTable(ObjectSpecification objectSpecification) {
+        for (ObjectSpecification subSpecification : objectSpecification.subclasses()) {
+            if (subSpecification.isAbstract() == false) {
+                final String tableNameFromSpecification = getTableNameFromSpecification(subSpecification);
+                tables.add(tableNameFromSpecification);
+                tableSpecifications.add(subSpecification);
+            }
+            if (subSpecification.hasSubclasses()) {
+                addSubSpecificationsToTable(subSpecification);
+            }
+        }
+    }
+
+    @Override
+    public boolean needsTables(DatabaseConnector connection) {
+        for (String subTableName : tables) {
+            table = subTableName;
+            if (super.needsTables(connection)) { // || !connection.hasColumn(table, classColumnName)) {
+                // Stop on first table that is needed.
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void createTables(final DatabaseConnector connection) {
+        for (String subTableName : tables) {
+            table = subTableName;
+            if (super.needsTables(connection)) {
+                super.createTables(connection);
+            }
+
+            // if (!connection.hasColumn(table, classColumnName)) {
+            // // I couldn't combine this in one operation with appendColumnDefinitions
+            // final StringBuffer sql = new StringBuffer();
+            // sql.append("alter table ");
+            // sql.append(table);
+            // sql.append(" add ");
+            //
+            // sql.append(classColumnName);
+            // sql.append(" ");
+            // sql.append(JdbcConnector.TYPE_LONG_STRING());
+            //
+            // connection.update(sql.toString());
+            // }
+        }
+    }
+
+    @Override
+    protected void appendCollectionUpdateColumnsToNull(StringBuffer sql) {
+        super.appendCollectionUpdateColumnsToNull(sql);
+        // sql.append("," + classColumnName + "=NULL");
+    }
+
+    @Override
+    protected void appendCollectionUpdateValues(final DatabaseConnector connector, final ObjectAdapter parent,
+        final StringBuffer sql) {
+        super.appendCollectionUpdateValues(connector, parent, sql);
+        // sql.append("," + classColumnName + "=?");
+        // connector.addToQueryValues(currentTableSpecification.getFullIdentifier());
+    }
+
+    @Override
+    protected Iterator<ObjectAdapter> getElementsForCollectionAsIterator(final ObjectAdapter collection) {
+        return currentIterator;
+    }
+
+    @Override
+    public void saveInternalCollection(final DatabaseConnector connector, final ObjectAdapter parent) {
+        final ObjectAdapter collection = baseField.get(parent);
+        LOG.debug("Saving polymorphic internal collection " + collection);
+
+        currentCollection = new ArrayList<ObjectAdapter>();
+        currentIterator = super.getElementsForCollectionAsIterator(collection);
+        for (; currentIterator.hasNext();) {
+            ObjectAdapter item = currentIterator.next();
+            currentCollection.add(item);
+        }
+
+        // TODO: Polymorphism: save instances to appropriate subclass tables
+        for (int i = 0; i < tables.size(); i++) {
+            currentTableSpecification = tableSpecifications.get(i);
+            currentIndex = 0;
+            currentIndexStart = 0;
+
+            currentIterator = new Iterator<ObjectAdapter>() {
+                @Override
+                public boolean hasNext() {
+                    for (int i = currentIndexStart; i < currentCollection.size(); i++) {
+                        ObjectAdapter thisObjectAdapter = currentCollection.get(i);
+                        if (thisObjectAdapter.getSpecification().isOfType(currentTableSpecification)) {
+                            currentIndexStart = currentIndex = i;
+                            return true;
+                        }
+                    }
+                    return false;
+                }
+
+                @Override
+                public ObjectAdapter next() {
+                    currentIndexStart = currentIndex + 1;
+                    return currentCollection.get(currentIndex);
+                }
+
+                @Override
+                public void remove() {
+                }
+            };
+
+            // Provide replacement table and column definitions here
+            table = tables.get(i);
+            super.saveInternalCollection(connector, parent);
+        }
+    }
+
+    @Override
+    public void loadInternalCollection(final DatabaseConnector connector, final ObjectAdapter parent,
+        final boolean makeResolved) {
+        final ObjectAdapter collection = baseField.get(parent);
+        LOG.debug("loading polymorphic internal collection " + collection);
+        // TODO: Polymorphism: load instances from all subclass tables
+        super.loadInternalCollection(connector, parent, makeResolved);
+    }
+
+    @Override
+    protected void loadCollectionIntoList(final DatabaseConnector connector, final ObjectAdapter parent,
+        final boolean makeResolved, final List<ObjectAdapter> superList) {
+        // TODO: Polymorphism: save instances to appropriate subclass tables
+        final List<ObjectAdapter> list = new ArrayList<ObjectAdapter>();
+        for (int i = 0; i < tables.size(); i++) {
+            currentTableSpecification = tableSpecifications.get(i);
+            specification = currentTableSpecification;
+
+            table = tables.get(i);
+            super.loadCollectionIntoList(connector, parent, makeResolved, list);
+
+            superList.addAll(list);
+            list.clear();
+        }
+
+    }
+
+}

Propchange: incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/auto/PolymorphicForeignKeyInChildCollectionMapper.java
------------------------------------------------------------------------------
    svn:executable = *

Modified: incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/jdbc/JdbcAbstractReferenceFieldMapping.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/jdbc/JdbcAbstractReferenceFieldMapping.java?rev=1169782&r1=1169781&r2=1169782&view=diff
==============================================================================
--- incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/jdbc/JdbcAbstractReferenceFieldMapping.java (original)
+++ incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-impl/src/main/java/org/apache/isis/runtimes/dflt/objectstores/sql/jdbc/JdbcAbstractReferenceFieldMapping.java Mon Sep 12 14:56:13 2011
@@ -65,7 +65,7 @@ public class JdbcAbstractReferenceFieldM
         sql.append(", ");
         sql.append(classnameColumn);
         sql.append(" ");
-        sql.append(JdbcConnector.TYPE_STRING());
+        sql.append(JdbcConnector.TYPE_LONG_STRING());
     }
 
     @Override
@@ -73,7 +73,7 @@ public class JdbcAbstractReferenceFieldM
         super.appendCreateColumnDefinitions(sql);
         sql.append(classnameColumn);
         sql.append(" ");
-        sql.append(JdbcConnector.TYPE_STRING());
+        sql.append(JdbcConnector.TYPE_LONG_STRING());
     }
 
     @Override

Modified: incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-tests-common/src/test/java/org/apache/isis/runtimes/dflt/objectstores/sql/PolymorphismTest.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-tests-common/src/test/java/org/apache/isis/runtimes/dflt/objectstores/sql/PolymorphismTest.java?rev=1169782&r1=1169781&r2=1169782&view=diff
==============================================================================
--- incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-tests-common/src/test/java/org/apache/isis/runtimes/dflt/objectstores/sql/PolymorphismTest.java (original)
+++ incubator/isis/trunk/framework/runtimes/dflt/objectstores/sql/sql-tests-common/src/test/java/org/apache/isis/runtimes/dflt/objectstores/sql/PolymorphismTest.java Mon Sep 12 14:56:13 2011
@@ -170,7 +170,7 @@ public class PolymorphismTest extends Sq
 
         List<PolyBaseClass> polyBaseClasses = polyTestClass.getPolyBaseClasses();
         // TODO Polymorphism test temporary disabled
-        // assertEquals(3, polyBaseClasses.size());
+        assertEquals(3, polyBaseClasses.size());
 
     }