/**
* 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();
}
}