Package org.objectweb.speedo.generation.jorm.rdb

Source Code of org.objectweb.speedo.generation.jorm.rdb.RdbJORMMapping

/**
* Copyright (C) 2001-2004 France Telecom R&D
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
package org.objectweb.speedo.generation.jorm.rdb;

import java.util.Collection;
import java.util.Iterator;
import java.util.Map;

import org.objectweb.jorm.api.PException;
import org.objectweb.jorm.lib.JormPathHelper;
import org.objectweb.jorm.mapper.rdb.metainfo.RdbClassMapping;
import org.objectweb.jorm.mapper.rdb.metainfo.RdbClassMultiMapping;
import org.objectweb.jorm.mapper.rdb.metainfo.RdbExternalTable;
import org.objectweb.jorm.mapper.rdb.metainfo.RdbGenClassMapping;
import org.objectweb.jorm.mapper.rdb.metainfo.RdbJoin;
import org.objectweb.jorm.mapper.rdb.metainfo.RdbPrimitiveElementMapping;
import org.objectweb.jorm.mapper.rdb.metainfo.RdbTable;
import org.objectweb.jorm.metainfo.api.Class;
import org.objectweb.jorm.metainfo.api.ClassMapping;
import org.objectweb.jorm.metainfo.api.GenClassMapping;
import org.objectweb.jorm.metainfo.api.GenClassRef;
import org.objectweb.jorm.metainfo.api.Manager;
import org.objectweb.jorm.metainfo.api.Mapping;
import org.objectweb.jorm.metainfo.api.MetaObject;
import org.objectweb.jorm.metainfo.api.NameDef;
import org.objectweb.jorm.metainfo.api.PrimitiveElement;
import org.objectweb.jorm.metainfo.api.PrimitiveElementMapping;
import org.objectweb.speedo.api.SpeedoException;
import org.objectweb.speedo.api.SpeedoProperties;
import org.objectweb.speedo.generation.jorm.JormMIMappingBuilder;
import org.objectweb.speedo.metadata.SpeedoClass;
import org.objectweb.speedo.metadata.SpeedoCollection;
import org.objectweb.speedo.metadata.SpeedoColumn;
import org.objectweb.speedo.metadata.SpeedoCommonField;
import org.objectweb.speedo.metadata.SpeedoDiscriminator;
import org.objectweb.speedo.metadata.SpeedoElement;
import org.objectweb.speedo.metadata.SpeedoField;
import org.objectweb.speedo.metadata.SpeedoIdentity;
import org.objectweb.speedo.metadata.SpeedoInheritedField;
import org.objectweb.speedo.metadata.SpeedoJoinColumn;
import org.objectweb.speedo.metadata.SpeedoMap;
import org.objectweb.speedo.metadata.SpeedoNoFieldColumn;
import org.objectweb.speedo.metadata.SpeedoTable;
import org.objectweb.speedo.naming.api.MIBuilderHelper;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Loggable;
import org.objectweb.util.monolog.api.Logger;
import org.objectweb.util.monolog.api.LoggerFactory;

/**
* This class is an implementation of the JormMIMappingBuilder for the mapper
* rdb and its sub mappers. It defines the O/R mapping of persistent classes.
* It supports the mapping a of a class into several tables (multi table class
* mapping).
*
* @author S.Chassande-Barrioz
*/
public class RdbJORMMapping
  implements JormMIMappingBuilder, SpeedoProperties, Loggable {

    private Logger logger;

    private boolean debug = false;

    // IMPLEMENTATION OF THE JormMIMappingBuilder INTERFACE //
    //------------------------------------------------------//

    /**
     * Defines the mapping the a class. It creates the table(s) where the
     * class is stored. When there are several tables (multitable mapping) or
     * in case of vertical inheritance, this method bulds the join between
     * the main table and the secondary table.
     * @param clazz is the JORM meta object representing the persistent class.
     * @param sc is the Speedo meta object representing the persistent class.
     * @param mapping is the JORM Meta object associated to the persistent class
     * @return the JORM meta object of mapping corresponding to the class.
     */
    public ClassMapping createClassMapping(
            Class clazz,
            SpeedoClass sc,
            Mapping mapping) throws PException, SpeedoException {
        //build ClassMapping instance
        RdbClassMultiMapping rcm =
            new RdbClassMultiMapping("multi-table", clazz, mapping);
        mapping.setClassMapping(rcm);

        //Build JORM meta objects for the main table
        if (sc.mainTable != null) {
            if (debug) {
                logger.log(BasicLevel.DEBUG, "define main table '"
                        + sc.mainTable.name + "' of the class '"
                        + sc.getFQName() + "'.");
            }
            rcm.createRdbTable(sc.mainTable.name);
        }
        //Build JORM meta objects for the secondary (external) tables
        if (sc.joinToExtTables != null) {
            for (int i = 0; i < sc.joinToExtTables.length; i++) {
                RdbExternalTable extTable = rcm
                        .createRdbExternalTable(sc.joinToExtTables[i].extTable.name);
                RdbJoin join = extTable.createRdbJoin("_" + i);
                if (debug) {
                    logger.log(BasicLevel.DEBUG, "define external table '"
                            + sc.joinToExtTables[i].extTable.name
                            + "' for the class '" + sc.getFQName() + "'.");
                }
                for (Iterator it = sc.joinToExtTables[i].columns.iterator(); it
                        .hasNext();) {
                    SpeedoJoinColumn jc = (SpeedoJoinColumn) it.next();
                    join.addJoinColumnNames(jc.targetColumn,
                            jc.column.name);
                    if (debug) {
                        logger.log(BasicLevel.DEBUG,
                                "\tdefine join between columns '"
                                        + jc.column.name + "' and '"
                                        + jc.column.targetColumn + "'.");
                    }
                }
            }
        }

        //manage inheritance
        if (sc.inheritance != null && sc.inheritance.superClassName != null) {
            String ruleName = null;
            if (sc.inheritance.isFilteredMapping()) {
                ruleName = RdbClassMapping.MAP_NEW_FIELDS_TO_EXTENDED_STRUCTURES;
            } else if (sc.inheritance.isHorizontalMapping()) {
                ruleName = RdbClassMapping.REMAP_FIELDS_TO_NEW_STRUCTURES;
            } else if (sc.inheritance.isVerticalMapping()) {
                ruleName = RdbClassMapping.MAP_NEW_FIELDS_TO_ADDED_STRUCTURES;
                //TODO: link the main table to the one of the parent
            }
            SpeedoClass parent = sc.getSpeedoClassFromContext(sc
                    .getSuperClassName());
            rcm.createParentClassMapping(ruleName, sc.jormclass
                    .getSuperClass(parent.getFQName()));
            //Add inter dependencies between the parent and its child
            if (debug) {
                logger.log(BasicLevel.DEBUG, "Add dependencies between "
                        + parent.getFQName() + " and " + sc.getFQName());
            }
            rcm.addDependency(parent.getFQName());
            getClassMapping(mapping, parent.jormclass).addDependency(
                    sc.getFQName());
        }
        return rcm;
    }

    /**
     * Maps the identifier fields.
     * @param cm is the JORM meta object representing the mapping of a
     * persistent class.
     * @param nd is the JORM meta object representing the name definition of the
     * persistent class.
     * @param sc is the Speedo meta object representing the persistent class
     * @param mibh is a helper for the building of the JORM meta information.
     */
    public void createClassIdentifierNameDefMapping(
            ClassMapping cm,
            NameDef nd,
            SpeedoClass sc,
            MIBuilderHelper mibh) throws PException, SpeedoException {
        Class clazz = (Class) nd.getParent();
        RdbClassMapping rcm = (RdbClassMapping) cm;
        if (nd.isFieldName()) {
            //The identifier of the class is based on a single field
            PrimitiveElement pe = (PrimitiveElement) clazz.getTypedElement(nd
                    .getFieldName());
            if (pe == null) {
                throw new SpeedoException(
                        "impossible to map the fields of the namedef of the metaobject '"
                                + JormPathHelper.getPath(clazz) + "'.");
            }
            SpeedoField sf = sc.getField(pe.getName());
            if (sf == null) {
                // The single field used as identifier is not a visible
                // persistent field
                if (sc.identity.columns != null
                        && sc.identity.columns.length == 1
                        && sc.identity.columns[0] != null
                        && sc.identity.columns[0].column != null
                        && sc.identity.columns[0].column.name != null) {
                    // the column name of the hidden identity field has been
                    // specified
                    createFieldMapping(pe,
                            sc.identity.columns[0].column.name,
                            sc.identity.columns[0].column.sqlType,
                            rcm.getMainRdbTable(),
                            null);
                } else {
                    // No column name specified
                    createFieldMapping(pe, (SpeedoCommonField) null, cm);
                }
            }
        } else {
            // The identifier of the persistent class is based on several
            // persistent field.
            Collection c = nd.getNameRef().getProjection().values();
            int i = 0;
            for (Iterator it = c.iterator(); it.hasNext();) {
                PrimitiveElement pe = (PrimitiveElement) clazz
                        .getTypedElement((String) it.next());
                if (pe == null) {
                    throw new SpeedoException(
                            "impossible to map the fields of the namedef of the metaobject '"
                                    + JormPathHelper.getPath(clazz) + "'.");
                }
                //find the corresponding Speedo meta object.
                SpeedoField sf = sc.getField(pe.getName());
                if (sf == null) {
                    SpeedoNoFieldColumn col = getIdentityColumn(sc.identity, pe
                            .getName());
                    if (col != null) {
                        //column name is specified for the field
                        createFieldMapping(pe, col.column.name,
                                col.column.sqlType, rcm.getMainRdbTable(),
                                null);
                    } else {
                        // no column found
                        createFieldMapping(pe, (SpeedoCommonField) null, cm);
                    }
                }
                i++;
            }
        }
    }

    /**
     * Finds the column among identity column, corresponding to a field name
     * @param identity is the identity meta object of a class
     * @param pkField is the name of the expected field.
     */
    private SpeedoNoFieldColumn getIdentityColumn(SpeedoIdentity identity,
            String pkField) {
        if (identity.columns == null) {
            return null;
        }
        for (int i = 0; i < identity.columns.length; i++) {
            if (identity.columns[i].column != null
                    && pkField.equals(identity.columns[i].column.targetField)) {
                return identity.columns[i];
            }
        }
        return null;
    }

    /**
     * Creates a PEM (PrimitiveElementMapping) for a PrimitiveElement. The PEM
     * is created only if the column does not already exist in the table
     * @param pe is the primitive element to map
     * @param colName is the column name where the PE has to be mapped
     * @param colType is the column type where the PE has to be mapped
     * @param t is the table where to map the PE.
     * @param join is the JORM meta object representing the join to the
     * secondary/external table to specify when the column is in a secondary /
     * external table. If the column is in the main table, this parameter must
     * be null.
     * @return the created or existing PEM corresponding to the specified
     * column name.
     */
    private RdbPrimitiveElementMapping createFieldMapping(PrimitiveElement pe,
            String colName,
            String colType,
            RdbTable t,
            RdbJoin join) throws PException, SpeedoException {
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "The Primitive field " + pe.getName()
                    + " is mappeded over the column (name=" + colName
                    + ", sql type=" + colType + ")");
        }
        RdbPrimitiveElementMapping pem = (RdbPrimitiveElementMapping)
          t.getPrimitiveElementMappingByCol(colName);
        if (pem == null) {
            pem = t.createPrimitiveElementMapping(pe, colName, colType, false);
        }
        if (join != null) {
            pem.bindPrimitiveElement(join, pe);
        }
        return pem;
    }

    /**
     * Creates a PEM (PrimitiveElementMapping) for a PrimitiveElement. The PEM
     * is created only if the column does not already exist in the table
     * @param pe is the primitive element to map
     * @param sf is the persistent field holding column definition
     * @param cm is the RdbClassMapping correponding to the persistent class
     * owning the field. It permits to find tha table where the field is mapped.
     * @return the created or existing PEM corresponding to the specified
     * column name.
     */
    private PrimitiveElementMapping createFieldMapping(PrimitiveElement pe,
            SpeedoCommonField sf,
            ClassMapping cm) throws PException, SpeedoException {
        String colName;
        String colType;
        RdbClassMultiMapping rcm = (RdbClassMultiMapping) cm;
        RdbTable t;
        RdbJoin join = null;
        if (sf == null) {
            //auto compute column name
            colName = pe.getName();
            colType = null;
            //get the main table as default
            t = rcm.getMainRdbTable();
        } else {
            //find the column corresponding to the primitive field
            SpeedoColumn[] cols = sf.columns;
            if (cols == null || cols.length == 0) {
                throw new SpeedoException("No column defined for the "
                        + sf.getSourceDesc());
            }
            if (cols.length > 1) {
                throw new SpeedoException("More than one column for the "
                        + sf.getSourceDesc());
            }
            SpeedoColumn col = cols[0];
            colName = col.name;
            colType = col.sqlType;
            //Find the table (JORM Meta object)
            if (sf.join == null) {
                t = rcm.getMainRdbTable();
            } else {
                t = rcm.getRdbExternalTable(sf.join.extTable.name);
                int idx = sf.moClass.getJoinIndex(sf.join);
                if (idx != -1) {
                    join = ((RdbExternalTable) t).getRdbJoin("_" + idx);
                }
            }
        }
        return createFieldMapping(pe, colName, colType, t, join);
    }

    /**
     * Creates a PEM (PrimitiveElementMapping) for a PrimitiveElement. The PEM
     * is created only if the column does not already exist in the table
     * @param pe is the primitive element to map
     * @param sf is the persistent field holding column definition
     * @param cm is the RdbClassMapping correponding to the persistent class
     * owning the field. It permits to find tha table where the field is mapped.
     * @return the created or existing PEM corresponding to the specified
     * column name.
     */
    public PrimitiveElementMapping createFieldMapping(
            PrimitiveElement pe,
            SpeedoField sf,
            ClassMapping cm) throws PException, SpeedoException {
        return createFieldMapping(pe, (SpeedoCommonField) sf, cm);
    }

    /**
     * Creates a PEM (PrimitiveElementMapping) for a PrimitiveElement. The PEM
     * is created only if the column does not already exist in the table
     * @param pe is the primitive element to map
     * @param sif is the persistent field holding column definition
     * @param cm is the RdbClassMapping correponding to the persistent class
     * owning the field. It permits to find tha table where the field is mapped.
     * @return the created or existing PEM corresponding to the specified
     * column name.
     */
    public PrimitiveElementMapping createFieldMapping(
            PrimitiveElement pe,
            SpeedoInheritedField sif,
            ClassMapping cm) throws PException, SpeedoException {
        return createFieldMapping(pe, (SpeedoCommonField) sif, cm);
    }

    /**
     * Creates a PEM (PrimitiveElementMapping) for a PrimitiveElement. The PEM
     * is created only if the column does not already exist in the table
     * @param pe is the primitive element to map
     * @param sif is the persistent field holding column definition
     * @param cm is the RdbClassMapping correponding to the persistent class
     * owning the field. It permits to find tha table where the field is mapped.
     * @return the created or existing PEM corresponding to the specified
     * column name.
     */
    public PrimitiveElementMapping createFieldMapping(
            PrimitiveElement pe,
            SpeedoNoFieldColumn snfc,
            ClassMapping cm) throws PException, SpeedoException {
       
        return createFieldMapping(pe, snfc.column.name, snfc.column.sqlType,
                ((RdbClassMultiMapping) cm).getMainRdbTable(), null);
    }

    /**
     * Creates the mapping of the name def (JORM meta object) corresponding to
     * a reference to a persistent class from a persistent class.
     * According to the mapping information hold by the SpeedoField, the
     * Class Reference is mapped in the main table or in an external table.
     * The external can be the table of the targeted class in case of One-One
     * relationship.
     * As the implementation of this method is a bit complex, it has been divided
     * in two local methods:
     * - createLocalClassRefNameDefMapping
     * - createExternalClassRefNameDefMapping
     *
     * @param cm is the MappingStructure which will host the mapping of the
     * reference
     * @param nd is the namedef corresponding to the reference
     * @param sf is the Speedo meta object representing the persistent field.
     * referencing a class.
     * @see #createLocalClassRefNameDefMapping(RdbClassMultiMapping, Class, SpeedoCommonField, NameDef, NameDef, SpeedoClass)
     * @see #createExternalClassRefNameDefMapping(RdbClassMultiMapping, Class, SpeedoCommonField, NameDef, RdbClassMultiMapping, NameDef, SpeedoClass)
     */
    public void createClassRefNameDefMapping(
            ClassMapping cm,
            NameDef nd,
            SpeedoCommonField sf) throws PException, SpeedoException {
        RdbClassMultiMapping rcm = (RdbClassMultiMapping) cm;
        SpeedoClass tclass = null;
        if (sf instanceof SpeedoField) {
            tclass = ((SpeedoField) sf).getReferencedClass();
        } else if (sf instanceof SpeedoInheritedField) {
            tclass = ((SpeedoInheritedField) sf).inheritedField.getReferencedClass();
        }
        RdbClassMultiMapping trcm = (RdbClassMultiMapping) tclass.jormclass
                .getClassProject(cm.getProjectName()).getMapping(
                        cm.getMapperName()).getClassMapping();
        NameDef tnd = (NameDef) trcm.getIdentifierMapping().getLinkedMO();
        Class jclass = sf.moClass.jormclass;
        if (sf.join == null) {
            createLocalClassRefNameDefMapping(rcm, jclass, sf, nd, tnd, tclass);
        } else {
            createExternalClassRefNameDefMapping(rcm, jclass, sf, nd, trcm, tnd, tclass);
        }
        //add the dependency
        trcm.addDependency(sf.moClass.getFQName());
    }

    /**
     * Creates the mapping of the name def (JORM meta object) corresponding to
     * a reference to a persistent class from a persistent class. The class
     * reference is mapped into a local foreign key.
     *
     * @param rcm is the MappingStructure which will host the mapping of the
     * reference
     * @param sf is the Speedo meta object representing the persistent field.
     * referencing a class.
     * @param nd is the namedef corresponding to the reference
     * @param tnd is the namedef corresponding to the referenced class
     * (identifier)
     * @param tclass is the referenced class.
     * @see createClassRefNameDefMapping
     */
    private void createLocalClassRefNameDefMapping(
            RdbClassMultiMapping rcm,
            Class jclass,
            SpeedoCommonField sf,
            NameDef nd,
            NameDef tnd,
            SpeedoClass tclass) throws PException, SpeedoException {
        RdbTable table = rcm.getRdbTable();
        //Map the name def of the reference over new local columns
        if (nd.isFieldName()) {
            PrimitiveElement pe = (PrimitiveElement) jclass
                    .getTypedElement(nd.getFieldName());
            RdbPrimitiveElementMapping pem = (RdbPrimitiveElementMapping) table
                    .getPrimitiveElementMappingByCol(sf.columns[0].name);
            if (pem == null) {
                // create it
                table.createPrimitiveElementMapping(pe,
                        sf.columns[0].name, sf.columns[0].sqlType,
                        false);
            } else {
                //it already exists
                //remove the old hidden field
                ((Class) pe.getParent()).removeTypedElement(pe
                        .getName());
                //Use the existing one in the name def
                pe = (PrimitiveElement) pem.getLinkedMO();
                nd.setFieldName(pe.getName());
                if (pem.getType() == null) {
                    pem.setType(sf.columns[0].sqlType);
                }
            }
        } else {
            Map tndproj = tnd.getNameRef().getProjection();
            Iterator it = nd.getNameRef().getProjection().entrySet()
                    .iterator();
            while (it.hasNext()) {
                Map.Entry me = (Map.Entry) it.next();
                //find the field in the class used in by this name def
                PrimitiveElement pe = (PrimitiveElement) jclass
                        .getTypedElement((String) me.getValue());
                //find the pk column
                RdbPrimitiveElementMapping tpem = getPEMOfField(tclass,
                        (Mapping) rcm.getParent(),
                        (String) tndproj.get(me.getKey()));
                //find the fk column name
                SpeedoColumn fkCol = sf.getFKColumn(tpem.getName());
                //check if the fk column name is already used
                RdbPrimitiveElementMapping pem = (RdbPrimitiveElementMapping) table
                        .getPrimitiveElementMappingByCol(fkCol.name);
                if (pem == null) {
                    // create it
                    table.createPrimitiveElementMapping(pe, fkCol.name,
                            fkCol.sqlType, false);
                } else {
                    //it already exists
                    //remove the old hidden field
                    ((Class) pe.getParent()).removeTypedElement(pe.getName());
                    //Use the existing one in the name def
                    pe = (PrimitiveElement) pem.getLinkedMO();
                    me.setValue(pe.getName());
                    if (pem.getType() == null) {
                        pem.setType(sf.columns[0].sqlType);
                    }
                }
            }
        }
    }

    /**
     * Creates the mapping of the name def (JORM meta object) corresponding to
     * a reference to a persistent class from a persistent class. The class
     * reference is mapped into an external table. This external table can be
     * the table of the referenced class (backward foreign key) in case of
     * One-One relationship.
     *
     * @param rcm is the MappingStructure which will host the mapping of the
     * reference
     * @param trcm is the MappingStructure hosting the referenced class.
     * @param sf is the Speedo meta object representing the persistent field.
     * referencing a class.
     * @param nd is the namedef corresponding to the reference
     * @param tnd is the namedef corresponding to the referenced class
     * (identifier)
     * @param tclass is the referenced class.
     * @see createClassRefNameDefMapping
     */
    private void createExternalClassRefNameDefMapping(
            RdbClassMultiMapping rcm,
            Class jclass,
            SpeedoCommonField sf,
            NameDef nd,
            RdbClassMultiMapping trcm,
            NameDef tnd,
            SpeedoClass tclass) throws PException, SpeedoException {
        RdbExternalTable exttable = rcm.createRdbExternalTable(sf.join.extTable.name);
        if (sf.join.extTable.name.equals(tclass.mainTable.name)) {
            // The external table is the table of the targeted class
            exttable.setColocated(true);
            trcm.getRdbTable().setColocated(true);
            trcm.getRdbTable().setColocatedMaster(true);
            trcm.addDependency(sf.moClass.getFQName());
            rcm.addDependency(tclass.getFQName());
            //optimisation: if there is a reverse field then we suppose
            // that the user will do the coherence.
            if (sf instanceof SpeedoField) {
                exttable.setReadOnly(((SpeedoField) sf).isCoherentReverseField);
            } else if (sf instanceof SpeedoInheritedField) {
                exttable.setReadOnly(((SpeedoInheritedField) sf).inheritedField.isCoherentReverseField);
            }
            exttable.setColocated(true);
            exttable.setColocatedMaster(false);
        }

        //Define the join to the external table
        RdbJoin j = exttable.createRdbJoin(sf.name);
        for (Iterator fkColIt = sf.join.columns.iterator(); fkColIt
                .hasNext();) {
            SpeedoJoinColumn jc = (SpeedoJoinColumn) fkColIt.next();
            j.addJoinColumnNames(jc.targetColumn, jc.column.name);
        }
        // map the name def over pk field of the referenced class
        if (tnd.isFieldName()) {
            RdbPrimitiveElementMapping pem = (RdbPrimitiveElementMapping) trcm
                    .getPrimitiveElementMapping(tnd.getFieldName(), true);
            PrimitiveElement pe = (PrimitiveElement) jclass
                    .getTypedElement(nd.getFieldName());
            exttable.createPrimitiveElementMapping(pe, pem.getName(),
                    null, false, j);
        } else {
            Map tclassndproj = tnd.getNameRef().getProjection();
            Iterator it = nd.getNameRef().getProjection().entrySet()
                    .iterator();
            while (it.hasNext()) {
                Map.Entry me = (Map.Entry) it.next();
                PrimitiveElement pe = (PrimitiveElement) jclass
                        .getTypedElement((String) me.getValue());
                RdbPrimitiveElementMapping pem = (RdbPrimitiveElementMapping)
                  trcm.getPrimitiveElementMapping(
                          (String) tclassndproj.get(me.getKey()), true);
                exttable.createPrimitiveElementMapping(pe, pem.getName(), null, false, j);
            }
        }
    }

    /**
     * Creates the mapping of the name def (JORM meta object) corresponding to
     * a reference to a persistent class from a generic persistent class.
     * The class reference is always stored in into the table of the genric
     * class. But it is important to check if the column used for the class
     * reference can be reused for index or generic class identifier.
     * @param gcm is the MappingStructure which will host the mapping of the
     * reference
     * @param nd is the namedef corresponding to the reference
     * @param sf is the Speedo meta object representing the persistent field
     * referencing a generic class.
     */
    public void createClassRefNameDefMapping(GenClassMapping gcm,
            NameDef nd,
            SpeedoField sf) throws PException, SpeedoException {
        RdbGenClassMapping rgcm = (RdbGenClassMapping) gcm;
        //Find the table (JORM Meta object)
        RdbTable table = rgcm.getRdbTable();
        GenClassRef gcr = (GenClassRef) gcm.getLinkedMO();
        if (nd.isFieldName()) {
            createPEMInGC(gcr.getHiddenField(nd.getFieldName()),
                    sf.columns[0], table, nd, null);
        } else {
            SpeedoClass tclass = sf.getReferencedClass();
            RdbClassMultiMapping trcm = getClassMapping(
                    (Mapping) gcm.getParent(), tclass.jormclass);
            NameDef tnd = (NameDef) trcm.getIdentifierMapping().getLinkedMO();
            Map tndproj = tnd.getNameRef().getProjection();
            Iterator it = nd.getNameRef().getProjection().entrySet()
                    .iterator();
            while (it.hasNext()) {
                Map.Entry me = (Map.Entry) it.next();
                //find the field in the genclass used in by this name def
                PrimitiveElement pe = gcr.getHiddenField((String) me.getValue());
                //find the pk column
                RdbPrimitiveElementMapping tpem = getPEMOfField(tclass,
                        (Mapping) gcm.getParent(),
                        (String) tndproj.get(me.getKey()));
                //find the fk column name
                SpeedoColumn fkCol = sf.getFKColumn(tpem.getName());
                createPEMInGC(pe, fkCol, table, nd, (String) me.getKey());
            }
        }
    }

    /**
     * It creates the mapping of a primitive field (element of the generic
     * class).
     * @param pe is the Jorm meta object representing a primitive field
     * @param gcm is the MappingStructure which will host the mapping of the
     * field
     * @param sf is the Speedo meta object representing the persistent field
     * referencing a generic class.
     * @return a PrimitiveElementMapping corresponding to the given primitive
     * field.
     * @throws PException if it is not possible to build the mapping of the
     * primitive field.
     */
    public PrimitiveElementMapping createGenClassElementMapping(
            PrimitiveElement pe,
            SpeedoField sf,
            GenClassMapping gcm) throws PException, SpeedoException {
        //find the column corresponding to the generic class element
        SpeedoColumn[] cols = sf.columns;
        if (cols == null || cols.length == 0) {
            throw new SpeedoException(
                    "No column defined for the element of the "
                            + sf.getSourceDesc());
        }
        if (cols.length > 1) {
            throw new SpeedoException(
                    "More than one column for the element of the "
                            + sf.getSourceDesc());
        }
        return createFieldMapping(pe, cols[0].name, cols[0].sqlType,
                ((RdbGenClassMapping) gcm).getRdbTable(), null);
    }

    /**
     * It creates the mapping of a primitive field used as index in the generic
     * class.
     * @param pe is the Jorm meta object representing a primitive field
     * @param gcm is the MappingStructure which will host the mapping of the
     * field
     * @param sf is the Speedo meta object representing the persistent field
     * referencing a generic class.
     * @return a PrimitiveElementMapping corresponding to the given primitive
     * field.
     * @throws PException if it is not possible to build the mapping of the
     * primitive field.
     */
    public PrimitiveElementMapping createGenClassIndexMapping(
            PrimitiveElement pe,
            SpeedoField sf,
            GenClassMapping gcm) throws PException, SpeedoException {
        SpeedoColumn col = null;
        if (sf.jdoTuple instanceof SpeedoCollection) {
            col = ((SpeedoCollection) sf.jdoTuple).indexColumns;
        } else if (sf.jdoTuple instanceof SpeedoMap) {
            col = ((SpeedoMap) sf.jdoTuple).keyColumns;
        }
        return createFieldMapping(pe, col.name, col.sqlType,
                ((RdbGenClassMapping) gcm).getRdbTable(), null);
    }

    /**
     * Creates the mapping of the name def (JORM meta object) corresponding to
     * the identifier of a persistent generic class (collection, map, ...).
     * @param gcm is the MappingStructure which will host the mapping of the
     * generic class
     * @param nd is the namedef corresponding to the identifier of the
     * generic class
     * @param sf is the Speedo meta object representing the persistent field
     * referencing a generic class.
     */
    public void createGenClassIdentifierNameDefMapping(GenClassMapping gcm,
            NameDef nd, SpeedoField sf, MIBuilderHelper mibh)
            throws PException, SpeedoException {
        GenClassRef gcr = (GenClassRef) nd.getParent();
        RdbGenClassMapping rgcm = (RdbGenClassMapping) gcm;
        RdbTable table = rgcm.getRdbTable();
        if (nd.isFieldName()) {
            PrimitiveElement pe = gcr.getHiddenField(nd.getFieldName());
            SpeedoJoinColumn col = (SpeedoJoinColumn) sf.join.columns.get(0);
            createPEMInGC(pe, col.column, table, nd, null);
        } else {
            //get the ClassMapping of class owning the generic class
            RdbClassMultiMapping rcmOwner = (RdbClassMultiMapping) sf.moClass.jormclass
                    .getClassProject(rgcm.getProjectName()).getMapping(
                            rgcm.getMapperName()).getClassMapping();
            //get the namedef identifier of the generic class owner
            NameDef ndIdOwner = (NameDef) rcmOwner.getIdentifierMapping()
                    .getLinkedMO();
            //:compute the prefix for genclass field(s) used as identifier
            String prefix = mibh.getNameDefFieldPrefix(gcr, true, true, sf);
            Map classNdProj = ndIdOwner.getNameRef().getProjection();
            Iterator it = nd.getNameRef().getProjection().entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry me = (Map.Entry) it.next();
                String compositeFieldName = (String) me.getValue();
                //get the primitive element corresponding in the Generic class
                PrimitiveElement peInGC =
                        gcr.getHiddenField(compositeFieldName);
                String classIdFieldName = (String) classNdProj.get(me.getKey());
                SpeedoField pkfield = (SpeedoField) sf.moClass.getField(classIdFieldName);
                SpeedoColumn col = sf.getJoinColumn(pkfield);
                createPEMInGC(peInGC, col, table, nd, (String) me.getKey());
            }
        }
    }

    /**
     * Creates a primitive element mapping in a table if no column with the
     * same name alrready exists. The PEM to create corresponds to a field
     * used in a name def. It can be the name def of the generic class
     * identifier, or the name def of a classref in a generic class.
     * If the column already exists in the table, that means two hidden fields
     * are mapped over the same column. In this case the specified primitive
     * element is removed from the GenClassRef. And its usage is replaced by
     * the existing one.
     * @param pe is a hidden field of a generic class
     * @param col is the column descriptor (Speedo meta object)
     * @param table is the table hosting the PEM.
     * @param nd is the name def using the PE.
     * @param compositeFieldName is the field name in a composite name in case
     * of the name def is based on a cmposite name. Otherwise this parameter is
     * not used.
     * @throws PException
     */
    private void createPEMInGC(PrimitiveElement pe,
            SpeedoColumn col,
            RdbTable table,
            NameDef nd,
            String compositeFieldName) throws PException {
        if (col.name == null) {
            // default mapping
            col.name = pe.getName();
        }
        //check if the column name is already used
        RdbPrimitiveElementMapping pem = (RdbPrimitiveElementMapping)
            table.getPrimitiveElementMappingByCol(col.name);
        if (pem == null) {
            // create it
            table.createPrimitiveElementMapping(
                    pe, col.name, col.sqlType, !col.allowNull);
        } else {
            //it exists then assign the real primitive element to the
            // the name def
            ((GenClassRef) pe.getParent()).removeTypedElement(pe.getName());
            pe = (PrimitiveElement) pem.getLinkedMO();
            if (nd.isFieldName()) {
                nd.setFieldName(pe.getName());
            } else {
                nd.getNameRef().getProjection().put(
                        compositeFieldName, pe.getName());
            }
            if (pem.getType() == null) {
                pem.setType(col.sqlType);
            }
        }
    }

    /**
     * It builds a GenClassMapping, assignes it to the mapping and builds
     * mapping structure for the class (RdbTable, directory name, ...).
     * @param gcr is the Jorm meta object representing the gen class which the
     * GenClassMapping must be built.
     * @param mapping is the Mapping instance which will host the GenClassMapping.
     * @param sf is the SpeedoField corresponding to the generic class.
     * @return the GenClassMapping instance built by the method (never null).
     * @throws PException if it is not possible to build the GenClassMapping
     */
    public GenClassMapping createGenClassMapping(
            GenClassRef gcr,
            SpeedoField sf,
            Mapping mapping) throws PException, SpeedoException {
        RdbGenClassMapping rgcm = new RdbGenClassMapping("to-table", gcr,
                mapping);
        mapping.addGenClassMapping(gcr.getGenClassId(), rgcm);
        RdbTable t = null;
        if (gcr.isPrimitive()) {
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "The GCR field " + gcr.getName()
                        + " is mappeded over a dedicated table: "
                        + sf.join.extTable.name);
            }
            //Create the RdbTable for the Generic class
            rgcm.createRdbTable(sf.join.extTable.name);
        } else if (gcr.isClassRef()) {
            SpeedoField reverseField = sf.getReverseField();
            SpeedoClass tclass = sf.moClass.getSpeedoClassFromContext(
                    gcr.getClassRef().getMOClass().getFQName());
            Class targetClassJMO = tclass.jormclass;
            mapping.getClassMapping().addDependency(targetClassJMO.getFQName());
            switch (sf.relationType) {
            case SpeedoField.MANY_MANY_BI_RELATION:
                //Create the join table for the Generic class
                t = rgcm.createRdbTable(sf.join.extTable.name);
                if (reverseField.mappedByReversefield
                        || (reverseField.join != null
                                && reverseField.join.extTable != null
                                && t.getName().equalsIgnoreCase(reverseField.join.extTable.name))) {
                    //The MANY-MANY relation is mapped over the same join table
                  if (sf.name.compareToIgnoreCase(reverseField.name) < 0) {
                      // master side is not randomly choosen 
                      t.setColocatedMaster(true);
                  } else {
                      //write are done only on master side. The slave side
                      // reads only the generic class
                      t.setReadOnly(true);
                      t.setColocated(true);
                  }
                }
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, "Many-Many relation,"
                            + " join table: " + t.getName()
                            + (t.isColocated()?", colocated":"")
                            + (t.isColocatedMaster()?", master":"")
                            + (t.isReadOnly()?", readonly":"")
                            );
                }
                break;
            case SpeedoField.ONE_MANY_BI_RELATION:
                reverseField = sf.getReverseField();
              RdbClassMultiMapping trcm = (RdbClassMultiMapping) getClassMapping(
                    mapping, targetClassJMO);
              if (sf.mappedByReversefield) {
                  RdbTable tTable;
                  SpeedoTable table;
                  if (reverseField.join != null) {
                      table = reverseField.join.extTable;
                      tTable = trcm.getRdbExternalTable(table.name);
                  } else {
                      table = reverseField.moClass.mainTable;
                      tTable = trcm.getRdbTable();
                  }
                  t = rgcm.createRdbTable(table.name);
                  tTable.setColocated(true);
                    t.setColocated(true);
                  tTable.setColocatedMaster(true);
                    t.setColocatedMaster(false);
                  trcm.addDependency(sf.moClass.getFQName());
                  if (sf.isCoherentReverseField) {
                      //optimisation: the reverse field assumes the coherence.
                      // Then there is no need to write the generic class.
                      t.setReadOnly(true);
                  }
              } else if (sf.join != null) {
                  t = rgcm.createRdbTable(sf.join.extTable.name);
              }
                if (t != null && logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, "One-Many relation,"
                            + " join table: " + t.getName()
                            + (t.isColocated()?", colocated":"")
                            + (t.isColocatedMaster()?", master":"")
                            + (t.isReadOnly()?", readonly":"")
                            );
                }
                break;
            default:
                //Create the RdbTable for the Generic class
                rgcm.createRdbTable(sf.join.extTable.name);
              //add the dependency
              trcm = (RdbClassMultiMapping) getClassMapping(mapping, targetClassJMO);
              trcm.addDependency(sf.moClass.getFQName());
              if (t != null && logger.isLoggable(BasicLevel.DEBUG)) {
                  logger.log(BasicLevel.DEBUG, "GCR field,"
                        + " join table: " + sf.join.extTable.name);
              }
                break;
            }
        }
        return rgcm;
    }

    public void createGenClassRefNameDefMapping(ClassMapping cm,
            NameDef nd,
            SpeedoCommonField sf) throws PException, SpeedoException {
        // TODO: support mapping of GCR on field which does not correspond to
        // identifier field of the GC owner (polymorphid).
    }

    /**
     * Find the PEM corresponding to a persistent field.
     * @param tsc is
     * @param map
     * @param fieldName
     * @return
     * @throws PException
     */
    private RdbPrimitiveElementMapping getPEMOfField(
            SpeedoClass sc,
            Mapping map,
            String fieldName) throws PException {
        SpeedoClass current = sc;
        RdbPrimitiveElementMapping pem = null;
        while(pem == null) {
          RdbClassMultiMapping trcm = getClassMapping(map, current.jormclass);
          pem = (RdbPrimitiveElementMapping)
            trcm.getPrimitiveElementMapping(fieldName, true);
            current = sc.getSuper();
        }
        if (pem == null) {
            throw new PException("No mapping found for the field '"
                    + fieldName + "' for the class '" + sc.getFQName()
                    + "' or its parent.");
        }
        return pem;
    }

    // IMPLEMENTATION OF THE Loggable INTERFACE //
    //------------------------------------------//

    public Logger getLogger() {
        return logger;
    }

    public LoggerFactory getLoggerFactory() {
        return null;
    }

    public void setLogger(Logger logger) {
        this.logger = logger;
        debug = logger != null && logger.isLoggable(BasicLevel.DEBUG);
    }

    public void setLoggerFactory(LoggerFactory loggerFactory) {
        setLogger(loggerFactory.getLogger(SpeedoProperties.LOGGER_NAME
                + ".generation.jorm.mimappingbuilder"));
    }

    private Manager getManager(MetaObject mo) {
        MetaObject m = mo;
        while (m != null && !(m instanceof Manager)) {
            m = m.getParent();
        }
        return (Manager) m;
    }

    private RdbClassMultiMapping getClassMapping(Mapping map, Class clazz) {
        return (RdbClassMultiMapping) clazz
            .getClassProject(map.getClassMapping().getProjectName())
                .getMapping(map.getMapperName()).getClassMapping();
    }
}
TOP

Related Classes of org.objectweb.speedo.generation.jorm.rdb.RdbJORMMapping

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.