/**
* 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;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.objectweb.asm.Type;
import org.objectweb.jorm.api.PException;
import org.objectweb.jorm.metainfo.api.Class;
import org.objectweb.jorm.metainfo.api.ClassMapping;
import org.objectweb.jorm.metainfo.api.ClassProject;
import org.objectweb.jorm.metainfo.api.ClassRef;
import org.objectweb.jorm.metainfo.api.GenClassMapping;
import org.objectweb.jorm.metainfo.api.GenClassRef;
import org.objectweb.jorm.metainfo.api.IdentifierMapping;
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.Reference;
import org.objectweb.jorm.metainfo.api.ReferenceMapping;
import org.objectweb.jorm.metainfo.api.ScalarField;
import org.objectweb.jorm.metainfo.api.TypedElement;
import org.objectweb.jorm.metainfo.lib.MetaInfoPrinter;
import org.objectweb.jorm.type.api.PType;
import org.objectweb.jorm.type.api.PTypeSpace;
import org.objectweb.speedo.api.SpeedoException;
import org.objectweb.speedo.api.SpeedoProperties;
import org.objectweb.speedo.api.UserFieldMapping;
import org.objectweb.speedo.generation.generator.lib.AbstractSpeedoGenerator;
import org.objectweb.speedo.metadata.SpeedoClass;
import org.objectweb.speedo.metadata.SpeedoCollection;
import org.objectweb.speedo.metadata.SpeedoDiscriminator;
import org.objectweb.speedo.metadata.SpeedoElement;
import org.objectweb.speedo.metadata.SpeedoExtension;
import org.objectweb.speedo.metadata.SpeedoField;
import org.objectweb.speedo.metadata.SpeedoInheritedField;
import org.objectweb.speedo.metadata.SpeedoMap;
import org.objectweb.speedo.metadata.SpeedoNoFieldColumn;
import org.objectweb.speedo.naming.api.MIBuilderHelper;
import org.objectweb.speedo.naming.lib.NamingManagerFactory;
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 a builder of jorm meta information. Its entries are the
* following:
* <UL>
* <LI>the jorm meta information manager,</LI>
* <LI>A collection of SpeedoClass object,</LI>
* <LI>the project name,</LI>
* <LI>the mapper name,</LI>
* <LI>a JormMIMappingBuilder instance able to build the mapping part for the
* mapper which the name is specified.</LI>
* </UL>
* @author S.Chassande-Barrioz
*/
public class JormMIBuilder implements MIBuilderHelper, Loggable {
/**
* is the prefix of the fields used to identify a generic class
*/
public final static String GENCLASS_ID_PREFIX = "id_";
/**
* is the prefix of the fields used for the element of a generic class
*/
public final static String GENCLASS_ELM_PREFIX = "elem_";
public final static String DEFAULT_RDB_BUILDER
= "org.objectweb.speedo.generation.jorm.rdb.RdbJORMMapping";
/**
* is the name of the index field in the list (Genclass).
*/
public final static String LIST_INDEX = "idx";
public final static String MAP_INDEX = "idx";
/**
* is the jorm meta information manager iinside which the Class and the
* composite name must be defined.
*/
private Manager manager = null;
private NamingManagerFactory nmf;
private Logger logger = null;
private LoggerFactory loggerFactory = null;
private boolean debug = false;
/**
* Builds a JormMIBuilder without a jorm meta information manager
* and a logger.
*/
public JormMIBuilder() {
}
/**
* Builds a JormMIBuilder with a jorm meta information manager and a logger
* @param manager
*/
public JormMIBuilder(Manager manager, Logger logger) {
this.manager = manager;
this.logger = logger;
}
/**
* Builds a JormMIBuilder with a jorm meta information manager and a logger
* @param manager
*/
public JormMIBuilder(Manager manager, NamingManagerFactory nmf, Logger logger) {
this.manager = manager;
this.logger = logger;
this.nmf = nmf;
}
/**
* retrieves the jorm meta information manager hosting the Class and
* CompositeName instances.
*/
public Manager getManager() {
return manager;
}
/**
* retrieves the jorm meta information manager hosting the Class and
* CompositeName instances.
*/
public void setManager(Manager manager) {
this.manager = manager;
}
public Logger getLogger() {
return logger;
}
public void setLogger(Logger logger) {
this.logger = logger;
}
public void setLoggerFactory(LoggerFactory lf) {
this.loggerFactory = lf;
}
public LoggerFactory getLoggerFactory() {
return loggerFactory;
}
/**
* Creates the jorm meta information for a set of persistent classes. Only
* the generic part will be created.
*
* @param scs is a list of SpeedoClass instances.
* @return a Collection of jorm meta object composed by Class instances and
* CompositeName instances.
*/
public Collection createMI(List scs) throws SpeedoException, PException {
return createMI(scs, null, null);
}
/**
* Creates the jorm meta information for a set of persistent classes.
* @param scs is a list of SpeedoClass instances.
* @param projectName is the project name for which the mapping must
* be defined. If the value is null no mapping will be generated.
* @param mapperName is the mapper name for which the mapping must
* be defined. If the value is null no mapping will be generated.
* @return a Collection of jorm meta object composed by Class instances and
* CompositeName instances.
*/
public Collection createMI(List scs,
String projectName,
String mapperName)
throws SpeedoException, PException {
JormMIMappingBuilder jmimb = null;
String buildercn;
if (mapperName != null) {
if (mapperName.startsWith("rdb")) {
buildercn = DEFAULT_RDB_BUILDER;
} else {
throw new SpeedoException(
"No default JormMIMappingBuilder for the mapper "
+ mapperName);
}
try {
jmimb = (JormMIMappingBuilder)
java.lang.Class.forName(buildercn).newInstance();
} catch (Exception e) {
throw new SpeedoException("Impossible to instanciate a the class "
+ buildercn);
}
if (jmimb instanceof Loggable) {
((Loggable) jmimb).setLogger(logger);
}
}
return createMI(scs, projectName, mapperName, jmimb);
}
/**
* Creates the jorm meta information for a set of persistent classes.
* @param scs is a list of SpeedoClass instances.
* @param projectName is the project name for which the mapping must
* be defined
* @param mapperName is the mapper name for which the mapping must
* be defined
* @param mb is the build of the mapping part of the meta information
* @return a Collection of jorm meta object composed by Class instances and
* CompositeName instances.
*/
public Collection createMI(List scs,
String projectName,
String mapperName,
JormMIMappingBuilder mb)
throws SpeedoException, PException {
if (manager == null) {
throw new SpeedoException(
"the Jorm meta information manager must be assigned");
}
int size = scs.size();
debug = logger.isLoggable(BasicLevel.DEBUG);
ArrayList createdMOs = new ArrayList(size * 2);
SpeedoClass sc;
String mn;
if (mapperName != null) {
// take the mapper name without the sub mapper name
int idx = mapperName.indexOf(".");
mn = (idx != -1 ? mapperName.substring(0, idx) : mapperName);
} else {
mn = null;
}
//Order the list in according to the inheritance order
orderMOsByInheritance(scs);
// Create Class meta objects
for(int i=0; i<size; i++) {
sc = (SpeedoClass) scs.get(i);
createJormClass(sc, projectName, mn, mb, createdMOs);
}
// Fill the meta information with primitive fields
for(int i=0; i<size; i++) {
sc = (SpeedoClass) scs.get(i);
createPrimitiveFields(sc, projectName, mn, mb);
}
// Define the identifier of classes (list with the inheritance order)
for(int i=0; i<size; i++) {
sc = (SpeedoClass) scs.get(i);
createIdentifierNameDef(sc, projectName, mn, mb, createdMOs);
}
// Fill the meta information with reference fields
for(int i=0; i<size; i++) {
sc = (SpeedoClass) scs.get(i);
createReferences(sc, projectName, mn, mb, createdMOs);
}
//define the mapping of inherited field (list with the inheritance order)
for(int i=0; i<size; i++) {
sc = (SpeedoClass) scs.get(i);
mapInheritance(sc, projectName, mn, mb);
}
if (logger.isLoggable(BasicLevel.DEBUG)) {
MetaInfoPrinter mip = new MetaInfoPrinter(manager);
for(int i=0; i<createdMOs.size(); i++) {
mip.print("", (MetaObject) createdMOs.get(i), logger);
}
}
return createdMOs;
}
/**
* Orders the list of SpeedoClass instance according to the inheritance.
* Firstly the ancestor class after its children.
* @param scs is a list of Speedo which has to be ordered.
*/
private void orderMOsByInheritance(List scs) {
List newscs = new ArrayList(scs.size());
if (debug) {
logger.log(BasicLevel.DEBUG, "before ordering scs.size()=" + scs.size());
}
SpeedoClass sc;
for(int i = 0; i<scs.size();) {
sc = (SpeedoClass) scs.get(i);
if (sc.getSuperClassName() == null) {
//Add classes without parent
scs.remove(i);
newscs.add(sc);
if (debug) {
logger.log(BasicLevel.DEBUG, "Class '" + sc.getFQName()
+ "' has no parent");
}
} else {
i++;
}
}
int idx = 0;
while (!scs.isEmpty() && idx < newscs.size()) {
//Take an element in the new list
sc = (SpeedoClass) newscs.get(idx);
if (debug) {
logger.log(BasicLevel.DEBUG, "Find children of the class '" + sc.getFQName() + "'.");
}
//Search its children in the old list
for(int i = 0; i<scs.size();) {
SpeedoClass child = (SpeedoClass) scs.get(i);
if (child.getSuper() == sc) {
//It is a child
scs.remove(i);
newscs.add(child);
if (debug) {
logger.log(BasicLevel.DEBUG, "Class '"
+ child.getFQName() + "' is a child of the class '"
+ sc.getFQName() + "'.");
}
} else {
i++;
}
}
idx ++;
}
if (debug) {
logger.log(BasicLevel.DEBUG, "newscs.size()=" + newscs.size());
}
scs.clear();
scs.addAll(newscs);
}
/**
* Creates the jorm meta information for a persistent class. Only the
* primitive fields are defined in the meta information. To define the
* references of a persistent object the 'createReferences' method must be
* used.
* @param sc is the Speedo meta object describing the class which the jorm
* meta information must be built.
* @param projectName is the project name for which the mapping must
* be defined
* @param mapperName is the mapper name for which the mapping must
* be defined
* @param mb is the build of the mapping part of the meta information
* @param createdMOs is a result paramter. This collection must be fill with
* the created Jorm Meta objects representing a class or a composite name.
* Here the meta object of the class will be added. In addition if the name
* def of the class is based on a composite name then its meta object will
* be added too.
*/
private void createJormClass(SpeedoClass sc,
String projectName,
String mapperName,
JormMIMappingBuilder mb,
Collection createdMOs)
throws SpeedoException, PException {
Class clazz = manager.getClass(sc.getFQName());
if (clazz == null) {
clazz = manager.createClass(sc.getFQName());
createdMOs.add(clazz);
}
if (sc.getSuperClassName() != null) {
SpeedoClass parent = sc.getSpeedoClassFromContext(sc.getSuperClassName());
if (parent == null) {
throw new SpeedoException("No super class '"
+ sc.getSuperClassName() + "'found in the .jdo file: "
+ sc.getXMLFileName());
}
Class parentClazz = manager.getClass(parent.getFQName());
if (parentClazz == null) {
throw new SpeedoException("Internal algorythm problem: parent class must be treated before sub classes");
}
clazz.addSuperClass(parentClazz);
}
sc.jormclass = clazz;
sc.jormclass.setAbstract(sc.isAbstract);
if (projectName != null && mapperName != null) {
ClassProject cp = clazz.getClassProject(projectName);
if (cp == null) {
cp = clazz.createClassProject(projectName);
}
Mapping mapping = cp.getMapping(mapperName);
if (mapping != null) {
//If the mapping is already defined then that means the jorm meta
// information has been already defined.
// => no meta object has been added.
return;
}
mapping = cp.createMapping(mapperName);
mb.createClassMapping(clazz, sc, mapping);
}
}
private void createPrimitiveFields(SpeedoClass sc,
String projectName,
String mapperName,
JormMIMappingBuilder mb)
throws SpeedoException, PException {
Class clazz = sc.jormclass;
ClassMapping cm = clazz.getClassProject(projectName)
.getMapping(mapperName).getClassMapping();
logger.log(BasicLevel.DEBUG, "Generate the Jorm MI for the class "
+ clazz.getFQName());
for (Iterator fieldsIt = sc.fields.values().iterator();fieldsIt.hasNext();) {
SpeedoField sp = (SpeedoField) fieldsIt.next();
createPrimitiveField(sc, clazz, sp, cm, mb);
}
}
private void createPrimitiveField(SpeedoClass sc,
Class clazz,
SpeedoField sp,
ClassMapping cm,
JormMIMappingBuilder mb)throws SpeedoException, PException {
SpeedoExtension se = sp.getExtensionByKey(SpeedoProperties.FIELD_CONVERTER);
PType ptype = null;
String className = null;
if (se != null) {
try {
UserFieldMapping ufm = (UserFieldMapping)
java.lang.Class.forName(se.value).newInstance();
ptype = getPrimitivePType(ufm.getStorageType().getName());
if (ptype == null) {
className = ufm.getStorageType().getName();
}
} catch (Exception e) {
throw new SpeedoException(
"Impossible to instanciate the UserFieldMapping class '"
+ se.value + "' for the field '" + sp.name
+ "' of the class '" + sc.getFQName() + "':", e);
}
} else {
Type type = Type.getType(sp.type);
ptype = getPrimitivePType(type);
if (ptype == null) {
className = type.getClassName();
}
}
if (ptype == null) {
if (isPersistentClass(className, sc.moPackage.name, manager)) {
//JM of a reference field will be defined later.
return;
} else {
logger.log(BasicLevel.INFO, "The field '" + sc.getFQName()
+ "." + sp.name + " is managed as a Serialized field.");
ptype = PTypeSpace.SERIALIZED;
}
}
logger.log(BasicLevel.DEBUG, "primitive field: " + sp.name + " / javatype: " + ptype.getJavaName());
se = sp.getExtensionByKey(SpeedoProperties.SIZE);
int size = PType.NOSIZE;
if (se != null) {
try {
size = Integer.parseInt(se.value);
} catch (NumberFormatException e) {
logger.log(BasicLevel.ERROR, "The specified size for the field '"
+ sp.name + "' of the class '" + sc.getFQName()
+ "' cannot be parsed: " + se.value, e);
}
}
int scale = PType.NOSIZE;
se = sp.getExtensionByKey(SpeedoProperties.SCALE);
if (se != null) {
try {
scale = Integer.parseInt(se.value);
} catch (NumberFormatException e) {
logger.log(BasicLevel.ERROR, "The specified scale for the field '"
+ sp.name + "' of the class '" + sc.getFQName()
+ "' cannot be parsed: " + se.value, e);
}
}
PrimitiveElement pe = clazz.createPrimitiveElement(sp.name, ptype, size, scale);
if (cm != null && mb != null) {
mb.createFieldMapping(pe, sp, cm);
}
}
/**
* Creates the identifier name and its mapping for a persistent class.
* @param sc is the SpeedoClass meta object describing the persistent which
* the identifier has to be defined.
* @param projectName is the jorm project name
* @param mapperName is the mapper name
* @param mb is the builder of the Jorm Mapping meta info
* @param createdMOs is the list of created meta object (Class or
* CompositeName).
* @return a boolean value indicating if the identifier has been created. In
* the inheritance case the identifier is inherited from the parent. If the
* parent identifier is not defined, it is not possible to create the
* identifier of the specified persistent class.
*/
private boolean createIdentifierNameDef(SpeedoClass sc,
String projectName,
String mapperName,
JormMIMappingBuilder mb,
Collection createdMOs)
throws SpeedoException, PException {
ClassMapping cm = sc.jormclass.getClassProject(projectName)
.getMapping(mapperName).getClassMapping();
NameDef classNd = null;
IdentifierMapping im = cm.getIdentifierMapping();
if (im != null) {
classNd = (NameDef) im.getLinkedMO();
if (classNd != null) {
return true;
}
}
if (sc.getSuperClassName() == null) {
classNd = sc.jormclass.createNameDef();
nmf.getNamingManager(sc).defineClassIdentifierNameDef(
classNd, sc.jormclass, sc, cm, this, mb, createdMOs);
if (cm != null) {
cm.createIdentifierMapping(classNd);
}
} else {
SpeedoClass parent = sc.getSpeedoClassFromContext(sc.getSuperClassName());
im = parent.jormclass
.getClassProject(projectName).getMapping(mapperName)
.getClassMapping().getIdentifierMapping();
if (im != null) {
classNd = (NameDef) im.getLinkedMO();
if (classNd != null) {
cm.createIdentifierMapping(classNd);
}
}
}
if (debug) {
logger.log(BasicLevel.DEBUG, "Create the identifier of the class '"
+ sc.getFQName() + "'"
+ (sc.getSuperClassName() == null ? ""
: "(parent class name='" + sc.getSuperClassName() + "')")
+ ", namedef=" + classNd + ".");
}
return classNd != null;
}
/**
* Builds the jorm meta objects representing the references owns by a persitent
* class. It is supposed that if the reference targets a persistent class,
* then the Class meta object and its NameDef are already defined.
* @param sc is the Speedo meta object describing the class which the jorm
* meta information must be built.
* @param projectName is the project name for which the mapping must
* be defined
* @param mapperName is the mapper name for which the mapping must
* be defined
* @param mb is the build of the mapping part of the meta information
* @param createdMOs is a result paramter. This collection must be fill with
* the created Jorm Meta objects representing a class or a composite name.
* Here only the new used composite name will be added.
*/
private void createReferences(SpeedoClass sc,
String projectName,
String mapperName,
JormMIMappingBuilder mb,
Collection createdMOs)
throws SpeedoException, PException {
Class clazz = manager.getClass(sc.getFQName());
logger.log(BasicLevel.DEBUG, "Generate the Jorm MI for the references of class "
+ clazz.getFQName());
Mapping mapping = null;
ClassMapping cm = null;
if (projectName != null && mapperName != null) {
mapping = clazz.getClassProject(projectName)
.getMapping(mapperName);
cm = mapping.getClassMapping();
}
Iterator fieldsIt = sc.fields.values().iterator();
//System.out.println("Manage reference of the class " + sc.getFQName());
while (fieldsIt.hasNext()) {
SpeedoField sp = (SpeedoField) fieldsIt.next();
if (clazz.getTypedElement(sp.name) != null) {
//the primitive element is already managed before(createJormClass)
continue;
}
Type t = Type.getType(sp.type);
String javatype = t.getClassName();
if (!isGenClassRef(javatype)) {
// reference to a class //
//============================================================//
if (debug) {
logger.log(BasicLevel.DEBUG, "Class reference field: "
+ sp.name + " / javatype: " + javatype);
}
// reference to a class
SpeedoClass tsc = sc.moPackage.xmlDescriptor
.getSpeedoClass(javatype, true);
if (tsc == null) {
throw new SpeedoException("No persistent class '"
+ javatype + "' found in the file: "
+ sc.moPackage.xmlDescriptor.xmlFile);
}
Class tclass = manager.getClass(tsc.getFQName());
if (tclass == null) {
manager.createClass(tsc.getFQName());
}
ClassRef cr = clazz.createClassRef(sp.name, tclass);
NameDef refNd = cr.createRefNameDef();
nmf.getNamingManager(tsc)
.defineClassReferenceNameDef(refNd, cr, sp, sc, cm, this, mb);
if ( cm != null) {
cm.createReferenceMapping(sp.name, refNd);
cm.addDependency(tsc.getFQName());
}
} else {
// reference to a generic class //
//============================================================//
String innerType = getInnerType(sp);
if (innerType == null) {
throw new SpeedoException("The inner element type is " +
"required for the multivalued field '" + sp.name
+ "' of the class '" + sc.getFQName()
+ "' in the .jdo file '"
+ sc.moPackage.xmlDescriptor.xmlFile + "'");
}
PType type = getPrimitivePType(innerType);
if (type == null && !isPersistentClass(innerType, sc.moPackage.name, manager)) {
logger.log(BasicLevel.INFO, "The field '" + sc.getFQName()
+ "." + sp.name + " is managed as a Serialized field.");
type = PTypeSpace.SERIALIZED;
}
if (debug) {
logger.log(BasicLevel.DEBUG, "GenClass reference field: "
+ sp.name + " / javatype: " + javatype
+ " / innerType: " + innerType
+ " / ptype=" + (type == null ? null : type.getJavaName()));
}
GenClassRef gcr = clazz.createGenClassRef(sp.name, javatype);
GenClassMapping gcm = null;
ClassRef cr = null;
SpeedoClass tsc = null;
//element of the gen class
if (type != null) {
// gen class of primitive type
gcr.createPrimitiveElement(type, PType.NOSIZE, PType.NOSIZE);
} else if (!isGenClassRef(innerType)) {
// gen class of classref
tsc = sc.moPackage.xmlDescriptor.smi
.getSpeedoClass(innerType, sc.moPackage);
if (tsc == null) {
throw new SpeedoException("The persistent class '"
+ sc.getFQName()
+ "' tries to reference (througth the field '"
+ sp.name
+ "') the class '" + innerType
+ "' not defined in the .jdo file '"
+ sc.moPackage.xmlDescriptor.xmlFile + "'");
}
Class tclass = manager.getClass(tsc.getFQName());
if (tclass == null) {
throw new SpeedoException("The inner element class '"
+ tsc.getFQName() + "' of the multivalued field '"
+ sp.name + "' of the class '" + sc.getFQName()
+ "' in the .jdo file '"
+ sc.moPackage.xmlDescriptor.xmlFile
+ "' has not been found among the persitent classes : "
+ manager.getClasses());
}
cr = gcr.createClassRef(tclass);
} else {
throw new SpeedoException(
"unmanaged the inner-element of the field '"
+ sp.name + "' of the class '" + sc.getFQName()
+ "' : " + innerType);
}
//Map the element of the generic class
if (mapping != null) {
gcm = mb.createGenClassMapping(gcr, sp, mapping);
if (gcr.isPrimitive()) {
mb.createGenClassElementMapping(gcr.getPrimitiveElement(), sp, gcm);
} else {
NameDef elemNd = cr.createRefNameDef();
nmf.getNamingManager(tsc).defineClassReferenceNameDef(elemNd, cr, sp, sc, gcm, this, mb);
//fillNameDef(elemNd, tsc, sc, gcr, gcr, gcm, mb, false, true, true, createdMOs);
gcm.createReferenceMapping(sp.name, elemNd);
cm.addDependency(tsc.getFQName());
}
}
// reference to a generic class from the class
NameDef refNd = gcr.createRefNameDef();
nmf.getNamingManager(sc).defineGenClassReferenceNameDef(refNd, gcr, sp, sc, cm, this, mb);
//fillNameDef(refNd, sc, sc, clazz, gcr, cm, mb, false, false, false, createdMOs);
if (cm != null) {
cm.createReferenceMapping(sp.name, refNd);
}
//identifier of the gen class
NameDef gcidNd = gcr.createIdNameDef();
nmf.getNamingManager(sc).defineGenClassIdentifierNameDef(
gcidNd, gcr, sp, sc, gcm, this, mb);
//fillNameDef(gcidNd, sc, sc, gcr, gcr, gcm, mb, true, true, true, createdMOs);
if (gcm != null) {
gcm.createIdentifierMapping(gcidNd);
}
//index of the gen class
java.lang.Class gcClass;
try {
gcClass = java.lang.Class.forName(javatype);
} catch (ClassNotFoundException e) {
throw new SpeedoException(
"The multivalued field '" + sp.name
+ "' of the class '" + sc.getFQName()
+ "' is not a well known class " + javatype, e);
}
if (isSubType(gcClass, java.util.List.class)) {
PrimitiveElement pe = gcr.createHiddenField(
LIST_INDEX, PTypeSpace.INT, PType.NOSIZE, PType.NOSIZE);
if (gcm != null) {
mb.createGenClassIndexMapping(pe, sp, gcm);
}
gcr.addIndexField(LIST_INDEX);
} else if (isSubType(gcClass, java.util.Map.class)) {
String keyfield = sp.getExtensionValueByKey(SpeedoProperties.KEY_FIELD);
if (keyfield == null) {
keyfield = MAP_INDEX;
}
PrimitiveElement pe = gcr.createHiddenField(
keyfield, getMapKeyPType(sp), PType.NOSIZE, PType.NOSIZE);
if (gcm != null) {
mb.createGenClassIndexMapping(pe, sp, gcm);
}
gcr.addIndexField(pe.getName());
}
}
}
}
private static final String HIDDEN_FIELD_CLASS_NAME = "speedo_class_name";
private void mapInheritance(SpeedoClass sc,
String projectName,
String mapperName,
JormMIMappingBuilder mb)
throws SpeedoException, PException {
ClassMapping cm = sc.jormclass.getClassProject(projectName)
.getMapping(mapperName).getClassMapping();
NameDef nd = (NameDef) cm.getIdentifierMapping().getLinkedMO();
if (sc.inheritance != null && sc.inheritance.discriminator != null) {
SpeedoDiscriminator sd = sc.inheritance.discriminator;
String filter = null;
if (sd.expression != null) {
filter = sd.expression;
} else if (sd.elements.size() == 1) {
SpeedoElement se = (SpeedoElement) sd.elements.get(0);
if (se instanceof SpeedoField) {
filter = ((SpeedoField) se).name;
} else if (se instanceof SpeedoNoFieldColumn) {
filter = HIDDEN_FIELD_CLASS_NAME;
} else {
throw new SpeedoException("Unmanaged filter element: " + se);
}
} else {
throw new SpeedoException(
"Composite inheritance discriminator not yet supported in Speedo");
}
if (filter != null) {
sc.jormclass.setInheritanceFilter(nd, filter);
logger.log(BasicLevel.DEBUG, "Assign filter: " + filter);
}
if (sc.inheritance.discriminator.elements != null
&& sc.inheritance.discriminator.elements.size() == 1) {
SpeedoElement se = (SpeedoElement) sc.inheritance.discriminator.elements.get(0);
if (se instanceof SpeedoNoFieldColumn) {
if (sc.getSuper() == null) {
// The discriminator is a column not mapped to a
// persistent field. then we have to create a constant
// hidden field.
PrimitiveElement pe = sc.jormclass.createHiddenField(
HIDDEN_FIELD_CLASS_NAME, PTypeSpace.STRING, PType.NOSIZE, PType.NOSIZE);
pe.setStatus(PrimitiveElement.CONSTANT_PERSISTENT);
mb.createFieldMapping(pe, (SpeedoNoFieldColumn) se, cm);
}
if (sc.inheritance.discriminator.strategy ==
SpeedoDiscriminator.STRATEGY_CLASS_NAME
&& sc.inheritance.discriminatorValues != null) {
// declare the constant value of the field
sc.jormclass.setConstantValue(HIDDEN_FIELD_CLASS_NAME,
(String) sc.inheritance.discriminatorValues.get(se));
}
}
}
}
if (sc.inheritance != null && sc.inheritance.discriminatorValues != null) {
Object key = sc.inheritance.discriminatorValues.values().iterator().next();
logger.log(BasicLevel.DEBUG, "Assign key: " + key);
sc.jormclass.setInheritanceNamingKey(nd, key);
}
if (sc.getSuperClassName() == null) {
return;
}
SpeedoClass parent = sc.getSpeedoClassFromContext(sc.getSuperClassName());
String im = sc.getExtensionValueByKey(SpeedoProperties.INHERITANCE_MAPPING);
// Map the inherited field if required
if (sc.inheritance.isHorizontalMapping()) {
for (Iterator it = sc.inheritance.remappedInheritedFields.values().iterator(); it.hasNext();) {
SpeedoInheritedField sif = (SpeedoInheritedField) it.next();
if (debug) {
logger.log(BasicLevel.DEBUG, "Map the inherited field '"
+ sif.name + "'.");
}
TypedElement te = sif.inheritedField.moClass.jormclass.getTypedElement(sif.inheritedField.name);
if (te == null) {
throw new SpeedoException("No inherited field '"
+ sif.name + "' found from "
+ sc.getSourceDesc());
} else if (te instanceof PrimitiveElement) {
mb.createFieldMapping((PrimitiveElement) te, sif, cm);
} else if (te instanceof ClassRef) {
ClassRef cr = (ClassRef) te;
//Keep the same namedef than in the parent
//but map the field(s) used in name def on column defined
// in SpeedoInheritedField meta object.
NameDef refNd = (NameDef) cr.getRefNameDef().iterator().next();
cm.createReferenceMapping(sif.inheritedField.name, refNd);
//create PEM for each field used in the NameDef
mb.createClassRefNameDefMapping(cm, refNd, sif);
}
}
} else if (sc.inheritance.isVerticalMapping()) {
//TODO
}
}
private boolean isSubType(java.lang.Class c1, java.lang.Class c2) {
if (c1 == null) {
return false;
}
if (c1 == c2) {
return true;
}
if (c2.isInterface()) {
java.lang.Class[] cs = c1.getInterfaces();
for(int i = 0; i<cs.length; i++) {
if (isSubType(cs[i], c2)) {
return true;
}
}
}
return isSubType(c1.getSuperclass(), c2);
}
private PType getMapKeyPType(SpeedoField sf) throws SpeedoException {
SpeedoMap sm = (SpeedoMap) sf.jdoTuple;
if (sm.keyType == null) {
throw new SpeedoException(
"It is required to define a key-type for the Map field'"
+ sf.name + "' of the class '" + sf.moClass.getFQName() +"' ");
}
PType res = getPrimitivePType((String) sm.keyType);
boolean isPersistClass = isPersistentClass(
(String) sm.keyType, sf.moClass.moPackage.name, manager);
if (res == null && !isPersistClass) {
logger.log(BasicLevel.INFO, "The field '" + sf.moClass.getFQName()
+ "." + sf.name + " is managed as a Serialized field.");
res = PTypeSpace.SERIALIZED;
}
if (res == null) {
throw new SpeedoException("The key-type '" + sm.keyType
+ "' of the Map field'" + sf.name + "' of the class '"
+ sf.moClass.getFQName() +"' is not supported");
}
return res;
}
/**
* Fill the name def of an identifier or a reference
* @param nd is the name def to fill
* @param isIdentifier indicates if the name represents an identifier (true)
* or a reference (false).
* @param isInGenClass indicates if the name is defined in a generic class
* (true) of in a class (false).
* @param ref meta object which the name def must be
* defined. This value is used only in the case of isIdentifier == false and
* isInGenClass == false.
* @param tsc is the speedo meta object representing the referenced class.
* This value is used only in the case of isIdentifier == false.
* @param mo is the jorm meta object hosting the name def and on which the
* eventual hidden field will be created.
* @param hcm if the mapping structure hosting the mapping of the reference.
* This value must be ClassMapping or GenClassMapping instance.
* @param mb is the mapping builder permitting the creation of the mapping
* part.
* @param createdMOs is a result paramter. This collection must be fill with
* the created Jorm Meta objects representing a class or a composite name.
* Here only the new used composite name will be added.
* @throws SpeedoException if the speedo meta information is not completly
* defined.
private void _fillNameDef(NameDef nd,
SpeedoClass tsc,
SpeedoClass ssc,
MetaObject mo,
Reference ref,
CommonClassMapping hcm,
JormMIMappingBuilder mb,
boolean isIdentifier,
boolean isInGenClass,
boolean createField,
Collection createdMOs)
throws SpeedoException, PException {
nmf.getNamingManager(tsc)
.fillNameDef(this,manager, nd, tsc, ssc, mo, ref, hcm,
mb, isIdentifier, isInGenClass, createField, createdMOs);
}
*/
/**
* Calculates the prefix of a field use in a name def.
* @param ref is the meta object of the reference if the namedef is
* used for a reference.
* @param isIdentifier indicates if the namedef is used for an identifier
* (true) or if the namedef is used for a reference (false).
* @param isInGenClass indicates if the namedef is used in a Generic class
* (true) or if the namedef is used in a class (false).
* @return a string value (never null) representing the prefix of a name
* def field.
*/
public String getNameDefFieldPrefix(Reference ref,
boolean isIdentifier,
boolean isInGenClass,
SpeedoField sf) throws SpeedoException {
if (isIdentifier) {
if (isInGenClass
&& sf.relationType == SpeedoField.ONE_MANY_BI_RELATION) {
//In case of ONE-MANY relation ship with Inheritance in the
// MANY side the hidden field name in the GenClass MUST have
// the same name than the hidden field in the referenced class
// Otherwise the alias names do not correspond in the SQL
// request generated by JORM for in inheritance.
return sf.getReverseField().name + '_';
}
return "";
} else {
if (isInGenClass) {
if (sf.relationType == SpeedoField.ONE_MANY_BI_RELATION) {
//In case of ONE-MANY relation ship with Inheritance in the
// MANY side the hidden field name in the GenClass MUST have
// the same name than the hidden field in the referenced class
// Otherwise the alias names do not correspond in the SQL
// request generated by JORM for in inheritance.
return "";
} else {
return GENCLASS_ELM_PREFIX;
}
} else {
if (ref instanceof GenClassRef) {
//The reference of the GCR is mapped over the class identifer
// fields
return "";
} else {
return ref.getName() + '_';
}
}
}
}
/**
* It creates a field of a name def. This field will be hidden
* @param mo is the jorm meta object hosting the field to create
* @param fn is the name of the field to create
* @param type is the type of the field to create
* @param size is the size of the field to create
* @return the jorm meta object representing the namedef field
*/
public ScalarField createNameDefField(MetaObject mo,
String fn,
PType type, int size, int scale)
throws SpeedoException {
ScalarField sf;
if (mo instanceof Class) {
sf = ((Class) mo).createHiddenField(fn, type, size, scale);
} else if (mo instanceof GenClassRef) {
sf = ((GenClassRef) mo).createHiddenField(fn, type, size, scale);
} else {
throw new SpeedoException(
"Impossible to create hidden field on this meta object: " + mo);
}
return sf;
}
/**
* It creates a field of a name def. This field will be hidden
* @param mo is the jorm meta object hosting the field to create
* @param fn is the name of the field to create
* @param type is the type of the field to create
* @return the jorm meta object representing the namedef field
*/
public ScalarField createNameDefField(MetaObject mo,
String fn,
PType type)
throws SpeedoException {
return createNameDefField(mo, fn, type, PType.NOSIZE, PType.NOSIZE);
}
/**
* Retrieves the jorm type matching to primitive type. It converts a Type
* defined in ASM into a PType defined in Jorm. If the type is not a jorm
* primitive type then a null value is returned.
*/
public PType getPrimitivePType(Type t) {
switch (t.getSort()) {
case Type.BOOLEAN:
return PTypeSpace.BOOLEAN;
case Type.CHAR:
return PTypeSpace.CHAR;
case Type.BYTE:
return PTypeSpace.BYTE;
case Type.SHORT:
return PTypeSpace.SHORT;
case Type.INT:
return PTypeSpace.INT;
case Type.FLOAT:
return PTypeSpace.FLOAT;
case Type.LONG:
return PTypeSpace.LONG;
case Type.DOUBLE:
return PTypeSpace.DOUBLE;
case Type.ARRAY:
PType innerType = getPrimitivePType(t.getElementType());
if (innerType !=null) {
if (innerType == PTypeSpace.CHAR) {
return PTypeSpace.CHARARRAY;
} else if (innerType == PTypeSpace.BYTE) {
return PTypeSpace.BYTEARRAY;
} else {
return PTypeSpace.SERIALIZED;
}
}
return null;
case Type.OBJECT:
default:
String cn = t.getClassName();
PType res = getPrimitivePType(cn);
if (res == null && !isPersistentClass(cn, null, manager)) {
return PTypeSpace.SERIALIZED;
} else {
return res;
}
}
}
private PType getPrimitivePType(String cn) {
for (int i = 0; i < PTypeSpace.PREDEFINEDPTYPES.length; i++) {
if (PTypeSpace.PREDEFINEDPTYPES[i] != PTypeSpace.REFTOP
&& PTypeSpace.PREDEFINEDPTYPES[i].getJavaName().equals(cn)) {
return PTypeSpace.PREDEFINEDPTYPES[i];
}
}
if (java.sql.Date.class.getName().equals(cn)) {
return PTypeSpace.DATE;
} else if (java.sql.Time.class.getName().equals(cn)) {
return PTypeSpace.DATE;
} else if (java.sql.Timestamp.class.getName().equals(cn)) {
return PTypeSpace.DATE;
} else if (java.util.Locale.class.getName().equals(cn)) {
return PTypeSpace.STRING;
} else {
return null;
}
}
private boolean isPersistentClass(String cn, String currentPackage, Manager mgr) {
if (isGenClassRef(cn)) {
return true;
}
if (cn.indexOf('.') != -1 || currentPackage == null) {
return mgr.getClass(cn) != null;
} else {
return mgr.getClass(currentPackage + '.' + cn) != null;
}
}
/**
* Indicates if the string representing a java type is a generic class (
* java.util.Collection | java.util.Set | java.util.Map
* @param javatype
* @return
*/
private boolean isGenClassRef(String javatype) {
int i=0;
while(i<AbstractSpeedoGenerator.GC_IMPL .length
&& !AbstractSpeedoGenerator.GC_IMPL[i][0].equals(javatype)) {
i++;
}
return i<AbstractSpeedoGenerator.GC_IMPL .length;
}
/**
* Retrieves the type of a generic class element.
* @param sp is the field which represents the generic class
* @return a String value representing the java type of the element, or null
* if the Speedofield does not represents a generic class.
*/
private String getInnerType(SpeedoField sp) {
if (sp.jdoTuple instanceof SpeedoCollection) {
return (String) ((SpeedoCollection) sp.jdoTuple).elementType;
} else if (sp.jdoTuple instanceof SpeedoMap) {
return (String) ((SpeedoMap) sp.jdoTuple).valueType;
}
return null;
}
/**
* retrieves the start of a pretty error message.
*/
public String getErrorMessage(SpeedoClass sc, MetaObject mo, Reference ref) {
String fqcn;
if (mo == null) {
fqcn = "null";
} else if (mo instanceof Class) {
fqcn = ((Class) mo).getFQName();
} else if (mo instanceof GenClassRef) {
fqcn = ((GenClassRef) mo).getGenClassId();
} else {
fqcn = mo.toString();
}
String res = "Impossible to define an user identifier for the ";
if (ref == null) {
res += "identifier of the class '" + fqcn;
} else {
res += "reference '" + ref.getName()
+ "' from the class '" + fqcn
+ "' to the class '" + sc.getFQName();
}
return res + "': ";
}
public PrimitiveElement getPrimitiveField(MetaObject mo, String name) {
if (mo instanceof Class) {
return (PrimitiveElement) ((Class) mo).getTypedElement(name);
} else if (mo instanceof GenClassRef) {
return ((GenClassRef) mo).getHiddenField(name);
} else {
return null;
}
}
public Manager getManager(MetaObject mo) {
MetaObject current = mo;
while(!(current instanceof Manager)) {
current = current.getParent();
}
return (Manager) current;
}
}