Package org.jpox.store.rdbms.table

Source Code of org.jpox.store.rdbms.table.ColumnCreator

/**********************************************************************
Copyright (c) 2004 Erik Bengtson and others. All rights reserved.
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.

Contributors:
2004 Andy Jefferson - Updated to use CorrespondentColumnsMapping to match up column specs.
2004 Andy Jefferson - changed class.forName to use ClassLoaderResolver.classForName
2005 Andy Jefferson - fix for use of "subclass-table" owners.
    ...
**********************************************************************/
package org.jpox.store.rdbms.table;

import org.jpox.ClassLoaderResolver;
import org.jpox.exceptions.JPOXUserException;
import org.jpox.metadata.AbstractClassMetaData;
import org.jpox.metadata.AbstractMemberMetaData;
import org.jpox.metadata.ColumnMetaData;
import org.jpox.metadata.ColumnMetaDataContainer;
import org.jpox.metadata.FieldRole;
import org.jpox.metadata.InheritanceStrategy;
import org.jpox.metadata.MetaDataUtils;
import org.jpox.store.exceptions.NoTableManagedException;
import org.jpox.store.mapped.DatastoreClass;
import org.jpox.store.mapped.DatastoreContainerObject;
import org.jpox.store.mapped.DatastoreField;
import org.jpox.store.mapped.DatastoreIdentifier;
import org.jpox.store.mapped.IdentifierFactory;
import org.jpox.store.mapped.MappedStoreManager;
import org.jpox.store.mapped.mapping.CorrespondentColumnsMapper;
import org.jpox.store.mapped.mapping.InterfaceMapping;
import org.jpox.store.mapped.mapping.JavaTypeMapping;
import org.jpox.store.mapped.mapping.PersistenceCapableMapping;
import org.jpox.store.mapped.mapping.ReferenceMapping;
import org.jpox.store.mapped.mapping.SerialisedPCMapping;
import org.jpox.store.mapped.mapping.SerialisedReferenceMapping;
import org.jpox.store.rdbms.Column;
import org.jpox.store.rdbms.RDBMSManager;
import org.jpox.store.rdbms.exceptions.DuplicateColumnNameException;
import org.jpox.store.rdbms.sqlidentifier.RDBMSIdentifierFactory;
import org.jpox.util.JPOXLogger;
import org.jpox.util.Localiser;

/**
* Helper class to create columns.
* Used for generating columns for reference (Object, interface) fields, and for join
* table fields.
*
* @version $Revision: 1.93 $
*/
public final class ColumnCreator
{
    /** Localiser for messages. */
    protected static final Localiser LOCALISER = Localiser.getInstance("org.jpox.store.rdbms.Localisation",
        RDBMSManager.class.getClassLoader());

    /**
     * Private constructor to prevent instantiation.
     */
    private ColumnCreator()
    {
        // private constructor
    }

    /**
     * Convenience method to add the column for an index mapping.
     * @param mapping The mapping
     * @param storeMgr Manager for the store
     * @param clr ClassLoaderResolver
     * @param table Table where we create the column
     * @param colmd The column MetaData
     * @param pk Whether this column is (part of) the PK.
     * @return The added column
     */
    public static DatastoreField createIndexColumn(JavaTypeMapping mapping,
                                             MappedStoreManager storeMgr,
                                             ClassLoaderResolver clr,
                                             DatastoreContainerObject table,
                                             ColumnMetaData colmd,
                                             boolean pk)
    {
        DatastoreIdentifier identifier = null;
        if (colmd != null && colmd.getName() != null)
        {
            // User defined name
            identifier = storeMgr.getIdentifierFactory().newDatastoreFieldIdentifier(colmd.getName());
        }
        else
        {
            // No name so generate one
            identifier = ((RDBMSIdentifierFactory)storeMgr.getIdentifierFactory()).newAdapterIndexFieldIdentifier();
        }

        DatastoreField column = table.addDatastoreField(mapping.getType(), identifier, mapping, colmd);
        storeMgr.getMappingManager().createDatastoreMapping(mapping, storeMgr, column, mapping.getJavaType().getName());
        if (pk)
        {
            column.setAsPrimaryKey();
        }

        return column;
    }

    /**
     * Method to create the required columns (and mapping if necessary) for a field
     * @param javaType The java type of the field
     * @param fmd Metadata for the field
     * @param columnMetaData MetaData defining the columns
     * @param storeMgr Store Manager
     * @param table The table to add the mapping to
     * @param primaryKey Whether this field is the PK
     * @param nullable Whether this field is to be nullable
     * @param serialised Whether the field is serialised
     * @param embedded Whether the field is embedded
     * @param fieldRole The role of this field (if any)
     * @param clr ClassLoader resolver
     * @return The java type mapping for this field
     */
    public static JavaTypeMapping createColumnsForJoinTables(Class javaType,
                                                AbstractMemberMetaData fmd,
                                                ColumnMetaData[] columnMetaData,
                                                MappedStoreManager storeMgr,
                                                DatastoreContainerObject table,
                                                boolean primaryKey,
                                                boolean nullable,
                                                boolean serialised,
                                                boolean embedded,
                                                int fieldRole,
                                                ClassLoaderResolver clr)
    {
        return createColumnsForField(javaType, null, table, storeMgr, fmd, primaryKey, nullable, serialised, embedded,
            fieldRole, columnMetaData, clr, false);
    }

    /**
     * Create columns for a field that uses subclass-table inheritance and where
     * we want to have a FK for each subclass with its own table.
     * @param mapping the mapping for the field
     * @param table the Table which will hold the columns
     * @param fmd MetaData for the field
     * @param clr The ClassLoaderResolver
     */
    public static void createColumnsForFieldUsingSubclassTable(JavaTypeMapping mapping,
            DatastoreContainerObject table,
            AbstractMemberMetaData fmd,
            ClassLoaderResolver clr)
    {
        MappedStoreManager storeMgr = table.getStoreManager();
        AbstractClassMetaData refCmd = storeMgr.getOMFContext().getMetaDataManager().getMetaDataForClass(fmd.getType(), clr);
        if (refCmd.getInheritanceMetaData().getStrategyValue() != InheritanceStrategy.SUBCLASS_TABLE)
        {
            throw new JPOXUserException(LOCALISER.msg("020185", fmd.getFullFieldName()));
        }
        AbstractClassMetaData[] subclassCmds = storeMgr.getClassesManagingTableForClass(refCmd, clr);

        boolean pk = false;
        if (subclassCmds.length > 1)
        {
            pk = false;
        }
        boolean nullable = true;
        if (subclassCmds.length > 1)
        {
            nullable = true;
        }

        int colPos = 0;
        for (int i=0; i<subclassCmds.length; i++)
        {
            Class type = clr.classForName(subclassCmds[i].getFullClassName());
            DatastoreClass dc = storeMgr.getDatastoreClass(subclassCmds[i].getFullClassName(), clr);
            JavaTypeMapping m = dc.getIDMapping();

            // obtain the column names for this type
            // TODO Fix this. The <column> elements have no way of being ordered to match the subclasses
            ColumnMetaData[] columnMetaDataForType = null;
            if (fmd.getColumnMetaData() != null && fmd.getColumnMetaData().length > 0)
            {
                if (fmd.getColumnMetaData().length < colPos+m.getNumberOfDatastoreFields())
                {
                    throw new JPOXUserException(LOCALISER.msg("020186",
                        fmd.getFullFieldName(), "" + fmd.getColumnMetaData().length,
                        "" + (colPos + m.getNumberOfDatastoreFields())));
                }
                columnMetaDataForType = new ColumnMetaData[m.getNumberOfDatastoreFields()];
                System.arraycopy(fmd.getColumnMetaData(), colPos, columnMetaDataForType, 0, columnMetaDataForType.length);
                colPos += columnMetaDataForType.length;
            }

            // Create the FK columns for this subclass
            createColumnsForField(type, mapping, table, storeMgr, fmd, pk, nullable, false, false, FieldRole.ROLE_FIELD,
                columnMetaDataForType, clr, true);

            if (JPOXLogger.DATASTORE.isInfoEnabled())
            {
                JPOXLogger.DATASTORE.info(LOCALISER.msg("020187", type, fmd.getName()));
            }
        }
    }

    /**
     * Create columns for reference (Object/interface) fields.
     * @param mapping the mapping for the field
     * @param table the Table which will hold the columns
     * @param fmd MetaData for the field
     * @param clr The ClassLoaderResolver
     * @param embedded Whether the field is embedded
     */
    public static void createColumnsForFieldUsingReference(JavaTypeMapping mapping,
            DatastoreContainerObject table,
            AbstractMemberMetaData fmd,
            ClassLoaderResolver clr,
            boolean embedded)
    {
        createColumnsForReferenceField(mapping, table, fmd, false, true, false, embedded, FieldRole.ROLE_FIELD,
            fmd.getColumnMetaData(), clr);
    }

    /**
     * Create columns for reference (Interface/Object) fields.
     * This call createColumnsForField for each implementation class of the reference.
     * @param mapping the InterfaceMapping/ObjectMapping
     * @param table the Table which will hold the columns
     * @param fmd the Field
     */
    private static void createColumnsForReferenceField(
            JavaTypeMapping mapping,
            DatastoreContainerObject table,
            AbstractMemberMetaData fmd,
            boolean isPrimaryKey,
            boolean isNullable,
            boolean serialised,
            boolean embedded,
            int fieldRole,
            ColumnMetaData[] columnMetaData,
            ClassLoaderResolver clr)
    {
        String[] implTypes = MetaDataUtils.getInstance().getImplementationNamesForReferenceField(fmd, fieldRole, clr);

        // Extract the type of the interface/Object field
        String fieldTypeName = fmd.getTypeName();
        if (fmd.getFieldTypes() != null && fmd.getFieldTypes().length == 1)
        {
            // "field-type" specified
            fieldTypeName = fmd.getFieldTypes()[0];
        }
        if (fmd.hasCollection())
        {
            fieldTypeName = fmd.getCollection().getElementType();
        }
        else if (fmd.hasArray())
        {
            fieldTypeName = fmd.getArray().getElementType();
        }
        else if (fmd.hasMap())
        {
            if (fieldRole == FieldRole.ROLE_MAP_KEY)
            {
                fieldTypeName = fmd.getMap().getKeyType();
            }
            else if (fieldRole == FieldRole.ROLE_MAP_VALUE)
            {
                fieldTypeName = fmd.getMap().getValueType();
            }
        }
        MappedStoreManager storeMgr = table.getStoreManager();
        boolean isPersistentInterfaceField = storeMgr.getOMFContext().getMetaDataManager().isPersistentInterface(fieldTypeName);

        // Set the PK and nullability of column(s) for the implementations (based on the number of impls etc)
        boolean pk = isPrimaryKey;
        boolean nullable = isNullable;
        if (implTypes.length > 1)
        {
            pk = false; // Cannot be part of PK if more than 1 implementation
        }
        if (implTypes.length > 1 && !pk)
        {
            nullable = true; // Must be nullable if more than 1 impl (since only 1 impl can have value at a time)
        }

        int colPos = 0;
        Class[] implTypeClasses = new Class[implTypes.length];
        for (int i=0; i<implTypes.length; i++)
        {
            String implName = implTypes[i];

            Class type = clr.classForName(implName);
            if (type == null)
            {
                throw new JPOXUserException(LOCALISER.msg("020189",
                    fmd.getTypeName(), implName));
            }
            else if (type.isInterface())
            {
                throw new JPOXUserException(LOCALISER.msg("020190",
                    fmd.getFullFieldName(), fmd.getTypeName(), implName));
            }
            implTypeClasses[i] = type;

            // Check if this implementation needs columns (or if its inheritance tree is already catered for in previous impls)
            boolean columnsNeeded = true;
            for (int j=0;j<i;j++)
            {
                if (type.isAssignableFrom(implTypeClasses[j]) || implTypeClasses[j].isAssignableFrom(type))
                {
                    // This implementation is part of the same inheritance tree as another implementation already processed
                    // so we already have its FK present
                    columnsNeeded = false;
                    break;
                }
            }

            if (isPersistentInterfaceField &&
                !storeMgr.getOMFContext().getMetaDataManager().isPersistentInterfaceImplementation(fieldTypeName, implName))
            {
                // We have a "persistent-interface" field yet this is not a JPOX-generated implementation so ignore it
                // It is arguable if we should allow the real implementations of this interface here, but the JDO2 TCK doesn't
                // make that assumption so we don't either
                columnsNeeded = false;
            }

            if (columnsNeeded)
            {
                // Get the mapping for this implementation
                JavaTypeMapping m;
                if (storeMgr.getOMFContext().getTypeManager().isSupportedType(type.getName()))
                {
                    m = storeMgr.getDatastoreAdapter().getMapping(type, storeMgr, serialised, embedded,
                        fmd.getFullFieldName());
                }
                else
                {
                    try
                    {
                        DatastoreClass dc = storeMgr.getDatastoreClass(type.getName(), clr);
                        m = dc.getIDMapping();
                    }
                    catch (NoTableManagedException ex)
                    {
                        // TODO Localise this message
                        throw new JPOXUserException("Cannot define columns for " + fmd.getFullFieldName() + " due to " + ex.getMessage(), ex);
                    }
                }

                ColumnMetaData[] columnMetaDataForType = null;
                if (columnMetaData != null && columnMetaData.length > 0)
                {
                    if (columnMetaData.length < colPos+m.getNumberOfDatastoreFields())
                    {
                        throw new JPOXUserException(LOCALISER.msg("020186",
                            fmd.getFullFieldName(), "" + columnMetaData.length,
                            "" + (colPos + m.getNumberOfDatastoreFields())));
                    }
                    columnMetaDataForType = new ColumnMetaData[m.getNumberOfDatastoreFields()];
                    System.arraycopy(columnMetaData, colPos, columnMetaDataForType, 0, columnMetaDataForType.length);
                    colPos += columnMetaDataForType.length;
                }

                // Create the FK column(s) for this implementation
                createColumnsForField(type, mapping, table, storeMgr, fmd, pk, nullable, serialised, embedded,
                    fieldRole, columnMetaDataForType, clr, true);

                if( JPOXLogger.DATASTORE.isInfoEnabled() )
                {
                    JPOXLogger.DATASTORE.info(LOCALISER.msg("020188", type,
                        fmd.getName()));
                }
            }
        }
    }

    /**
     * Method to create the column(s) for a field in either a join table or for a reference field.
     * @param javaType The java type of the field being stored
     * @param mapping The JavaTypeMapping (if existing, otherwise created and returned by this method)
     * @param table The table to insert the columns into (join table, or primary table (if ref field))
     * @param storeMgr Manager for the store
     * @param fmd MetaData for the field (or null if a collection field)
     * @param isPrimaryKey Whether to create the columns as part of the PK
     * @param isNullable Whether the columns should be nullable
     * @param serialised Whether the field is serialised
     * @param embedded Whether the field is embedded
     * @param fieldRole The role of the field (when part of a join table)
     * @param columnMetaData MetaData for the column(s)
     * @param clr ClassLoader resolver
     * @param isReferenceField Whether this field is part of a reference field
     * @return The JavaTypeMapping for the table
     */
    private static JavaTypeMapping createColumnsForField(
            Class javaType,
            JavaTypeMapping mapping,
            DatastoreContainerObject table,
            MappedStoreManager storeMgr,
            AbstractMemberMetaData fmd,
            boolean isPrimaryKey,
            boolean isNullable,
            boolean serialised,
            boolean embedded,
            int fieldRole,
            ColumnMetaData[] columnMetaData,
            ClassLoaderResolver clr,
            boolean isReferenceField)
    {
        if (mapping == null)
        {
            // No mapping so create it for the passed java type
            mapping = storeMgr.getDatastoreAdapter().getMapping(javaType, storeMgr, serialised,
                embedded, fmd != null ? fmd.getFullFieldName() : null);
        }

        if (mapping instanceof InterfaceMapping && fmd != null && fmd.hasExtension("implementation-classes"))
        {
            // Store the implementation-classes with the mapping
            ((InterfaceMapping) mapping).setImplementationClasses(fmd.getValueForExtension("implementation-classes"));
        }

        IdentifierFactory idFactory = storeMgr.getIdentifierFactory();
        if (storeMgr.getOMFContext().getTypeManager().isReferenceType(javaType) && !serialised && !embedded)
        {
            // Reference mapping
            // Create column(s) for the reference field (typically one column per implementation type)
            createColumnsForReferenceField(mapping, table, fmd, isPrimaryKey, isNullable, serialised, embedded,
                fieldRole, columnMetaData, clr);
        }
        else if (mapping instanceof ReferenceMapping ||
                mapping instanceof SerialisedPCMapping ||
                mapping instanceof SerialisedReferenceMapping ||
                mapping instanceof PersistenceCapableMapping)
        /*else if (!storeMgr.getOMFContext().getTypeManager().isSupportedType(javaType.getName()))*/
        {
            // PC mapping
            JavaTypeMapping container = mapping;
            if (mapping instanceof ReferenceMapping)
            {
                // Interface/Object has child mappings for each implementation
                container = storeMgr.getDatastoreAdapter().getMapping(javaType, storeMgr, serialised, embedded, fmd != null ? fmd.getFullFieldName() : null);
                ((ReferenceMapping) mapping).addJavaTypeMapping(container);
            }

            if (container instanceof SerialisedPCMapping || container instanceof SerialisedReferenceMapping)
            {
                // Serialised column will be added by mapping constructor, so just return it
                return mapping;
            }

            // Get the table that we want our column to be a FK to
            // This could be the owner table, element table, key table, value table etc
            DatastoreClass destinationTable = storeMgr.getDatastoreClass(javaType.getName(), clr);
            if (destinationTable == null)
            {
                // Maybe the owner hasn't got its own table (e.g "subclass-table" inheritance strategy)
                AbstractClassMetaData ownerCmd = storeMgr.getOMFContext().getMetaDataManager().getMetaDataForClass(javaType, clr);
                AbstractClassMetaData[] ownerCmds = storeMgr.getClassesManagingTableForClass(ownerCmd, clr);
                if (ownerCmds == null || ownerCmds.length == 0)
                {
                    throw new JPOXUserException(LOCALISER.msg("057023", javaType.getName())).setFatal();
                }
                // Use the first one since they should all have the same id column(s)
                destinationTable = storeMgr.getDatastoreClass(ownerCmds[0].getFullClassName(), clr);
            }
            if (destinationTable != null)
            {
                // Foreign-Key to the destination table
                JavaTypeMapping m = destinationTable.getIDMapping();

                // For each datastore mapping, add a column.
                ColumnMetaDataContainer columnContainer = null;
                if (columnMetaData != null && columnMetaData.length > 0)
                {
                    columnContainer = (ColumnMetaDataContainer)columnMetaData[0].getParent();
                }
                CorrespondentColumnsMapper correspondentColumnsMapping =
                    new CorrespondentColumnsMapper(columnContainer, columnMetaData, m, true);
                for (int i=0; i<m.getNumberOfDatastoreFields(); i++)
                {
                    JavaTypeMapping refDatastoreMapping =
                        storeMgr.getDatastoreAdapter().getMapping(m.getDataStoreMapping(i).getJavaTypeMapping().getJavaType(), storeMgr);
                    ColumnMetaData colmd = correspondentColumnsMapping.getColumnMetaDataByIdentifier(
                        m.getDataStoreMapping(i).getDatastoreField().getIdentifier());
                    try
                    {
                        DatastoreIdentifier identifier = null;
                        if (colmd.getName() == null)
                        {
                            // User hasn't provided a name, so we use default naming
                            if (isReferenceField)
                            {
                                // Create reference identifier
                                identifier = idFactory.newReferenceFieldIdentifier(fmd,
                                    storeMgr.getOMFContext().getMetaDataManager().getMetaDataForClass(javaType, clr),
                                    m.getDataStoreMapping(i).getDatastoreField().getIdentifier(),
                                    storeMgr.getOMFContext().getTypeManager().isDefaultEmbeddedType(javaType), fieldRole);
                            }
                            else
                            {
                                // Create join table identifier (FK using destination table identifier)
                                AbstractMemberMetaData[] relatedMmds = fmd.getRelatedMemberMetaData(clr);
                                // TODO Cater for more than 1 related field
                                identifier = ((RDBMSIdentifierFactory)idFactory).newJoinTableFieldIdentifier(fmd,
                                    relatedMmds != null ? relatedMmds[0] : null,
                                    m.getDataStoreMapping(i).getDatastoreField().getIdentifier(),
                                    storeMgr.getOMFContext().getTypeManager().isDefaultEmbeddedType(javaType), fieldRole);
                            }
                        }
                        else
                        {
                            // User defined name, so we use that.
                            identifier = idFactory.newDatastoreFieldIdentifier(colmd.getName());
                        }

                        DatastoreField column = table.addDatastoreField(javaType.getName(), identifier, refDatastoreMapping, colmd);
                        ((Column) m.getDataStoreMapping(i).getDatastoreField()).copyConfigurationTo(column);
                        if (isPrimaryKey)
                        {
                            column.setAsPrimaryKey();
                        }
                        if (isNullable)
                        {
                            column.setNullable();
                        }

                        storeMgr.getMappingManager().createDatastoreMapping(refDatastoreMapping, storeMgr, column,
                            m.getDataStoreMapping(i).getJavaTypeMapping().getJavaTypeForDatastoreMapping(i));
                    }
                    catch (DuplicateColumnNameException ex)
                    {
                      throw new JPOXUserException("Cannot create column for field "+fmd.getFullFieldName()+" column metadata "+colmd,ex);
                    }
                    ((PersistenceCapableMapping) container).addJavaTypeMapping(refDatastoreMapping);
                }
            }
        }
        else
        {
            // Non-PC mapping
            // Add column for the field
            DatastoreField column = null;
            ColumnMetaData colmd = null;
            if (columnMetaData != null && columnMetaData.length > 0)
            {
                colmd = columnMetaData[0];
            }

            DatastoreIdentifier identifier = null;
            if (colmd != null && colmd.getName() != null)
            {
                // User specified name
                identifier = idFactory.newDatastoreFieldIdentifier(colmd.getName());
            }
            else
            {
                // No user-supplied name so generate one
                identifier = ((RDBMSIdentifierFactory)idFactory).newJoinTableFieldIdentifier(fmd, null, null,
                    storeMgr.getOMFContext().getTypeManager().isDefaultEmbeddedType(javaType), fieldRole);
            }
            column = table.addDatastoreField(javaType.getName(), identifier, mapping, colmd);
            storeMgr.getMappingManager().createDatastoreMapping(mapping, storeMgr, column,
                mapping.getJavaTypeForDatastoreMapping(0));

            if (isNullable)
            {
                column.setNullable();
            }
        }

        return mapping;
    }
}
TOP

Related Classes of org.jpox.store.rdbms.table.ColumnCreator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.