0001: /**
0002: * Copyright (C) 2001-2004 France Telecom R&D
0003: *
0004: * This library is free software; you can redistribute it and/or
0005: * modify it under the terms of the GNU Lesser General Public
0006: * License as published by the Free Software Foundation; either
0007: * version 2 of the License, or (at your option) any later version.
0008: *
0009: * This library is distributed in the hope that it will be useful,
0010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0012: * Lesser General Public License for more details.
0013: *
0014: * You should have received a copy of the GNU Lesser General Public
0015: * License along with this library; if not, write to the Free Software
0016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0017: */package org.objectweb.speedo.generation.jorm;
0018:
0019: import java.util.ArrayList;
0020: import java.util.Collection;
0021: import java.util.Iterator;
0022: import java.util.List;
0023:
0024: import org.objectweb.asm.Type;
0025: import org.objectweb.jorm.api.PException;
0026: import org.objectweb.jorm.metainfo.api.Class;
0027: import org.objectweb.jorm.metainfo.api.ClassMapping;
0028: import org.objectweb.jorm.metainfo.api.ClassProject;
0029: import org.objectweb.jorm.metainfo.api.ClassRef;
0030: import org.objectweb.jorm.metainfo.api.GenClassMapping;
0031: import org.objectweb.jorm.metainfo.api.GenClassRef;
0032: import org.objectweb.jorm.metainfo.api.IdentifierMapping;
0033: import org.objectweb.jorm.metainfo.api.Manager;
0034: import org.objectweb.jorm.metainfo.api.Mapping;
0035: import org.objectweb.jorm.metainfo.api.MetaObject;
0036: import org.objectweb.jorm.metainfo.api.NameDef;
0037: import org.objectweb.jorm.metainfo.api.PrimitiveElement;
0038: import org.objectweb.jorm.metainfo.api.Reference;
0039: import org.objectweb.jorm.metainfo.api.ReferenceMapping;
0040: import org.objectweb.jorm.metainfo.api.ScalarField;
0041: import org.objectweb.jorm.metainfo.api.TypedElement;
0042: import org.objectweb.jorm.metainfo.lib.MetaInfoPrinter;
0043: import org.objectweb.jorm.type.api.PType;
0044: import org.objectweb.jorm.type.api.PTypeSpace;
0045: import org.objectweb.speedo.api.SpeedoException;
0046: import org.objectweb.speedo.api.SpeedoProperties;
0047: import org.objectweb.speedo.api.UserFieldMapping;
0048: import org.objectweb.speedo.generation.generator.lib.AbstractSpeedoGenerator;
0049: import org.objectweb.speedo.metadata.SpeedoClass;
0050: import org.objectweb.speedo.metadata.SpeedoCollection;
0051: import org.objectweb.speedo.metadata.SpeedoDiscriminator;
0052: import org.objectweb.speedo.metadata.SpeedoElement;
0053: import org.objectweb.speedo.metadata.SpeedoExtension;
0054: import org.objectweb.speedo.metadata.SpeedoField;
0055: import org.objectweb.speedo.metadata.SpeedoInheritedField;
0056: import org.objectweb.speedo.metadata.SpeedoMap;
0057: import org.objectweb.speedo.metadata.SpeedoNoFieldColumn;
0058: import org.objectweb.speedo.naming.api.MIBuilderHelper;
0059: import org.objectweb.speedo.naming.lib.NamingManagerFactory;
0060: import org.objectweb.util.monolog.api.BasicLevel;
0061: import org.objectweb.util.monolog.api.Loggable;
0062: import org.objectweb.util.monolog.api.Logger;
0063: import org.objectweb.util.monolog.api.LoggerFactory;
0064:
0065: /**
0066: * This class is a builder of jorm meta information. Its entries are the
0067: * following:
0068: * <UL>
0069: * <LI>the jorm meta information manager,</LI>
0070: * <LI>A collection of SpeedoClass object,</LI>
0071: * <LI>the project name,</LI>
0072: * <LI>the mapper name,</LI>
0073: * <LI>a JormMIMappingBuilder instance able to build the mapping part for the
0074: * mapper which the name is specified.</LI>
0075: * </UL>
0076: * @author S.Chassande-Barrioz
0077: */
0078: public class JormMIBuilder implements MIBuilderHelper, Loggable {
0079:
0080: /**
0081: * is the prefix of the fields used to identify a generic class
0082: */
0083: public final static String GENCLASS_ID_PREFIX = "id_";
0084:
0085: /**
0086: * is the prefix of the fields used for the element of a generic class
0087: */
0088: public final static String GENCLASS_ELM_PREFIX = "elem_";
0089:
0090: public final static String DEFAULT_RDB_BUILDER = "org.objectweb.speedo.generation.jorm.rdb.RdbJORMMapping";
0091:
0092: /**
0093: * is the name of the index field in the list (Genclass).
0094: */
0095: public final static String LIST_INDEX = "idx";
0096: public final static String MAP_INDEX = "idx";
0097:
0098: /**
0099: * is the jorm meta information manager iinside which the Class and the
0100: * composite name must be defined.
0101: */
0102: private Manager manager = null;
0103: private NamingManagerFactory nmf;
0104:
0105: private Logger logger = null;
0106: private LoggerFactory loggerFactory = null;
0107: private boolean debug = false;
0108:
0109: /**
0110: * Builds a JormMIBuilder without a jorm meta information manager
0111: * and a logger.
0112: */
0113: public JormMIBuilder() {
0114: }
0115:
0116: /**
0117: * Builds a JormMIBuilder with a jorm meta information manager and a logger
0118: * @param manager
0119: */
0120: public JormMIBuilder(Manager manager, Logger logger) {
0121: this .manager = manager;
0122: this .logger = logger;
0123: }
0124:
0125: /**
0126: * Builds a JormMIBuilder with a jorm meta information manager and a logger
0127: * @param manager
0128: */
0129: public JormMIBuilder(Manager manager, NamingManagerFactory nmf,
0130: Logger logger) {
0131: this .manager = manager;
0132: this .logger = logger;
0133: this .nmf = nmf;
0134: }
0135:
0136: /**
0137: * retrieves the jorm meta information manager hosting the Class and
0138: * CompositeName instances.
0139: */
0140: public Manager getManager() {
0141: return manager;
0142: }
0143:
0144: /**
0145: * retrieves the jorm meta information manager hosting the Class and
0146: * CompositeName instances.
0147: */
0148: public void setManager(Manager manager) {
0149: this .manager = manager;
0150: }
0151:
0152: public Logger getLogger() {
0153: return logger;
0154: }
0155:
0156: public void setLogger(Logger logger) {
0157: this .logger = logger;
0158: }
0159:
0160: public void setLoggerFactory(LoggerFactory lf) {
0161: this .loggerFactory = lf;
0162: }
0163:
0164: public LoggerFactory getLoggerFactory() {
0165: return loggerFactory;
0166: }
0167:
0168: /**
0169: * Creates the jorm meta information for a set of persistent classes. Only
0170: * the generic part will be created.
0171: *
0172: * @param scs is a list of SpeedoClass instances.
0173: * @return a Collection of jorm meta object composed by Class instances and
0174: * CompositeName instances.
0175: */
0176: public Collection createMI(List scs) throws SpeedoException,
0177: PException {
0178: return createMI(scs, null, null);
0179: }
0180:
0181: /**
0182: * Creates the jorm meta information for a set of persistent classes.
0183: * @param scs is a list of SpeedoClass instances.
0184: * @param projectName is the project name for which the mapping must
0185: * be defined. If the value is null no mapping will be generated.
0186: * @param mapperName is the mapper name for which the mapping must
0187: * be defined. If the value is null no mapping will be generated.
0188: * @return a Collection of jorm meta object composed by Class instances and
0189: * CompositeName instances.
0190: */
0191: public Collection createMI(List scs, String projectName,
0192: String mapperName) throws SpeedoException, PException {
0193: JormMIMappingBuilder jmimb = null;
0194: String buildercn;
0195: if (mapperName != null) {
0196: if (mapperName.startsWith("rdb")) {
0197: buildercn = DEFAULT_RDB_BUILDER;
0198: } else {
0199: throw new SpeedoException(
0200: "No default JormMIMappingBuilder for the mapper "
0201: + mapperName);
0202: }
0203: try {
0204: jmimb = (JormMIMappingBuilder) java.lang.Class.forName(
0205: buildercn).newInstance();
0206: } catch (Exception e) {
0207: throw new SpeedoException(
0208: "Impossible to instanciate a the class "
0209: + buildercn);
0210: }
0211: if (jmimb instanceof Loggable) {
0212: ((Loggable) jmimb).setLogger(logger);
0213: }
0214: }
0215: return createMI(scs, projectName, mapperName, jmimb);
0216: }
0217:
0218: /**
0219: * Creates the jorm meta information for a set of persistent classes.
0220: * @param scs is a list of SpeedoClass instances.
0221: * @param projectName is the project name for which the mapping must
0222: * be defined
0223: * @param mapperName is the mapper name for which the mapping must
0224: * be defined
0225: * @param mb is the build of the mapping part of the meta information
0226: * @return a Collection of jorm meta object composed by Class instances and
0227: * CompositeName instances.
0228: */
0229: public Collection createMI(List scs, String projectName,
0230: String mapperName, JormMIMappingBuilder mb)
0231: throws SpeedoException, PException {
0232: if (manager == null) {
0233: throw new SpeedoException(
0234: "the Jorm meta information manager must be assigned");
0235: }
0236: int size = scs.size();
0237: debug = logger.isLoggable(BasicLevel.DEBUG);
0238: ArrayList createdMOs = new ArrayList(size * 2);
0239: SpeedoClass sc;
0240: String mn;
0241: if (mapperName != null) {
0242: // take the mapper name without the sub mapper name
0243: int idx = mapperName.indexOf(".");
0244: mn = (idx != -1 ? mapperName.substring(0, idx) : mapperName);
0245: } else {
0246: mn = null;
0247: }
0248:
0249: //Order the list in according to the inheritance order
0250: orderMOsByInheritance(scs);
0251: // Create Class meta objects
0252: for (int i = 0; i < size; i++) {
0253: sc = (SpeedoClass) scs.get(i);
0254: createJormClass(sc, projectName, mn, mb, createdMOs);
0255: }
0256: // Fill the meta information with primitive fields
0257: for (int i = 0; i < size; i++) {
0258: sc = (SpeedoClass) scs.get(i);
0259: createPrimitiveFields(sc, projectName, mn, mb);
0260: }
0261: // Define the identifier of classes (list with the inheritance order)
0262: for (int i = 0; i < size; i++) {
0263: sc = (SpeedoClass) scs.get(i);
0264: createIdentifierNameDef(sc, projectName, mn, mb, createdMOs);
0265: }
0266: // Fill the meta information with reference fields
0267: for (int i = 0; i < size; i++) {
0268: sc = (SpeedoClass) scs.get(i);
0269: createReferences(sc, projectName, mn, mb, createdMOs);
0270: }
0271: //define the mapping of inherited field (list with the inheritance order)
0272: for (int i = 0; i < size; i++) {
0273: sc = (SpeedoClass) scs.get(i);
0274: mapInheritance(sc, projectName, mn, mb);
0275: }
0276:
0277: if (logger.isLoggable(BasicLevel.DEBUG)) {
0278: MetaInfoPrinter mip = new MetaInfoPrinter(manager);
0279: for (int i = 0; i < createdMOs.size(); i++) {
0280: mip.print("", (MetaObject) createdMOs.get(i), logger);
0281: }
0282: }
0283: return createdMOs;
0284: }
0285:
0286: /**
0287: * Orders the list of SpeedoClass instance according to the inheritance.
0288: * Firstly the ancestor class after its children.
0289: * @param scs is a list of Speedo which has to be ordered.
0290: */
0291: private void orderMOsByInheritance(List scs) {
0292: List newscs = new ArrayList(scs.size());
0293: if (debug) {
0294: logger.log(BasicLevel.DEBUG, "before ordering scs.size()="
0295: + scs.size());
0296: }
0297: SpeedoClass sc;
0298: for (int i = 0; i < scs.size();) {
0299: sc = (SpeedoClass) scs.get(i);
0300: if (sc.getSuperClassName() == null) {
0301: //Add classes without parent
0302: scs.remove(i);
0303: newscs.add(sc);
0304: if (debug) {
0305: logger.log(BasicLevel.DEBUG, "Class '"
0306: + sc.getFQName() + "' has no parent");
0307: }
0308: } else {
0309: i++;
0310: }
0311: }
0312: int idx = 0;
0313: while (!scs.isEmpty() && idx < newscs.size()) {
0314: //Take an element in the new list
0315: sc = (SpeedoClass) newscs.get(idx);
0316: if (debug) {
0317: logger.log(BasicLevel.DEBUG,
0318: "Find children of the class '" + sc.getFQName()
0319: + "'.");
0320: }
0321: //Search its children in the old list
0322: for (int i = 0; i < scs.size();) {
0323: SpeedoClass child = (SpeedoClass) scs.get(i);
0324: if (child.getSuper() == sc) {
0325: //It is a child
0326: scs.remove(i);
0327: newscs.add(child);
0328: if (debug) {
0329: logger.log(BasicLevel.DEBUG, "Class '"
0330: + child.getFQName()
0331: + "' is a child of the class '"
0332: + sc.getFQName() + "'.");
0333: }
0334: } else {
0335: i++;
0336: }
0337: }
0338: idx++;
0339: }
0340: if (debug) {
0341: logger.log(BasicLevel.DEBUG, "newscs.size()="
0342: + newscs.size());
0343: }
0344: scs.clear();
0345: scs.addAll(newscs);
0346: }
0347:
0348: /**
0349: * Creates the jorm meta information for a persistent class. Only the
0350: * primitive fields are defined in the meta information. To define the
0351: * references of a persistent object the 'createReferences' method must be
0352: * used.
0353: * @param sc is the Speedo meta object describing the class which the jorm
0354: * meta information must be built.
0355: * @param projectName is the project name for which the mapping must
0356: * be defined
0357: * @param mapperName is the mapper name for which the mapping must
0358: * be defined
0359: * @param mb is the build of the mapping part of the meta information
0360: * @param createdMOs is a result paramter. This collection must be fill with
0361: * the created Jorm Meta objects representing a class or a composite name.
0362: * Here the meta object of the class will be added. In addition if the name
0363: * def of the class is based on a composite name then its meta object will
0364: * be added too.
0365: */
0366: private void createJormClass(SpeedoClass sc, String projectName,
0367: String mapperName, JormMIMappingBuilder mb,
0368: Collection createdMOs) throws SpeedoException, PException {
0369: Class clazz = manager.getClass(sc.getFQName());
0370: if (clazz == null) {
0371: clazz = manager.createClass(sc.getFQName());
0372: createdMOs.add(clazz);
0373: }
0374: if (sc.getSuperClassName() != null) {
0375: SpeedoClass parent = sc.getSpeedoClassFromContext(sc
0376: .getSuperClassName());
0377: if (parent == null) {
0378: throw new SpeedoException("No super class '"
0379: + sc.getSuperClassName()
0380: + "'found in the .jdo file: "
0381: + sc.getXMLFileName());
0382: }
0383: Class parentClazz = manager.getClass(parent.getFQName());
0384: if (parentClazz == null) {
0385: throw new SpeedoException(
0386: "Internal algorythm problem: parent class must be treated before sub classes");
0387: }
0388: clazz.addSuperClass(parentClazz);
0389: }
0390: sc.jormclass = clazz;
0391: sc.jormclass.setAbstract(sc.isAbstract);
0392: if (projectName != null && mapperName != null) {
0393: ClassProject cp = clazz.getClassProject(projectName);
0394: if (cp == null) {
0395: cp = clazz.createClassProject(projectName);
0396: }
0397: Mapping mapping = cp.getMapping(mapperName);
0398: if (mapping != null) {
0399: //If the mapping is already defined then that means the jorm meta
0400: // information has been already defined.
0401: // => no meta object has been added.
0402: return;
0403: }
0404: mapping = cp.createMapping(mapperName);
0405: mb.createClassMapping(clazz, sc, mapping);
0406: }
0407: }
0408:
0409: private void createPrimitiveFields(SpeedoClass sc,
0410: String projectName, String mapperName,
0411: JormMIMappingBuilder mb) throws SpeedoException, PException {
0412: Class clazz = sc.jormclass;
0413: ClassMapping cm = clazz.getClassProject(projectName)
0414: .getMapping(mapperName).getClassMapping();
0415: logger.log(BasicLevel.DEBUG,
0416: "Generate the Jorm MI for the class "
0417: + clazz.getFQName());
0418: for (Iterator fieldsIt = sc.fields.values().iterator(); fieldsIt
0419: .hasNext();) {
0420: SpeedoField sp = (SpeedoField) fieldsIt.next();
0421: createPrimitiveField(sc, clazz, sp, cm, mb);
0422: }
0423: }
0424:
0425: private void createPrimitiveField(SpeedoClass sc, Class clazz,
0426: SpeedoField sp, ClassMapping cm, JormMIMappingBuilder mb)
0427: throws SpeedoException, PException {
0428: SpeedoExtension se = sp
0429: .getExtensionByKey(SpeedoProperties.FIELD_CONVERTER);
0430: PType ptype = null;
0431: String className = null;
0432: if (se != null) {
0433: try {
0434: UserFieldMapping ufm = (UserFieldMapping) java.lang.Class
0435: .forName(se.value).newInstance();
0436: ptype = getPrimitivePType(ufm.getStorageType()
0437: .getName());
0438: if (ptype == null) {
0439: className = ufm.getStorageType().getName();
0440: }
0441: } catch (Exception e) {
0442: throw new SpeedoException(
0443: "Impossible to instanciate the UserFieldMapping class '"
0444: + se.value + "' for the field '"
0445: + sp.name + "' of the class '"
0446: + sc.getFQName() + "':", e);
0447: }
0448: } else {
0449: Type type = Type.getType(sp.type);
0450: ptype = getPrimitivePType(type);
0451: if (ptype == null) {
0452: className = type.getClassName();
0453: }
0454: }
0455: if (ptype == null) {
0456: if (isPersistentClass(className, sc.moPackage.name, manager)) {
0457: //JM of a reference field will be defined later.
0458: return;
0459: } else {
0460: logger.log(BasicLevel.INFO, "The field '"
0461: + sc.getFQName() + "." + sp.name
0462: + " is managed as a Serialized field.");
0463: ptype = PTypeSpace.SERIALIZED;
0464: }
0465: }
0466: logger.log(BasicLevel.DEBUG, "primitive field: " + sp.name
0467: + " / javatype: " + ptype.getJavaName());
0468: se = sp.getExtensionByKey(SpeedoProperties.SIZE);
0469: int size = PType.NOSIZE;
0470: if (se != null) {
0471: try {
0472: size = Integer.parseInt(se.value);
0473: } catch (NumberFormatException e) {
0474: logger.log(BasicLevel.ERROR,
0475: "The specified size for the field '" + sp.name
0476: + "' of the class '" + sc.getFQName()
0477: + "' cannot be parsed: " + se.value, e);
0478: }
0479: }
0480: int scale = PType.NOSIZE;
0481: se = sp.getExtensionByKey(SpeedoProperties.SCALE);
0482: if (se != null) {
0483: try {
0484: scale = Integer.parseInt(se.value);
0485: } catch (NumberFormatException e) {
0486: logger.log(BasicLevel.ERROR,
0487: "The specified scale for the field '" + sp.name
0488: + "' of the class '" + sc.getFQName()
0489: + "' cannot be parsed: " + se.value, e);
0490: }
0491: }
0492: PrimitiveElement pe = clazz.createPrimitiveElement(sp.name,
0493: ptype, size, scale);
0494: if (cm != null && mb != null) {
0495: mb.createFieldMapping(pe, sp, cm);
0496: }
0497: }
0498:
0499: /**
0500: * Creates the identifier name and its mapping for a persistent class.
0501: * @param sc is the SpeedoClass meta object describing the persistent which
0502: * the identifier has to be defined.
0503: * @param projectName is the jorm project name
0504: * @param mapperName is the mapper name
0505: * @param mb is the builder of the Jorm Mapping meta info
0506: * @param createdMOs is the list of created meta object (Class or
0507: * CompositeName).
0508: * @return a boolean value indicating if the identifier has been created. In
0509: * the inheritance case the identifier is inherited from the parent. If the
0510: * parent identifier is not defined, it is not possible to create the
0511: * identifier of the specified persistent class.
0512: */
0513: private boolean createIdentifierNameDef(SpeedoClass sc,
0514: String projectName, String mapperName,
0515: JormMIMappingBuilder mb, Collection createdMOs)
0516: throws SpeedoException, PException {
0517: ClassMapping cm = sc.jormclass.getClassProject(projectName)
0518: .getMapping(mapperName).getClassMapping();
0519: NameDef classNd = null;
0520: IdentifierMapping im = cm.getIdentifierMapping();
0521: if (im != null) {
0522: classNd = (NameDef) im.getLinkedMO();
0523: if (classNd != null) {
0524: return true;
0525: }
0526: }
0527: if (sc.getSuperClassName() == null) {
0528: classNd = sc.jormclass.createNameDef();
0529: nmf.getNamingManager(sc)
0530: .defineClassIdentifierNameDef(classNd,
0531: sc.jormclass, sc, cm, this , mb, createdMOs);
0532: if (cm != null) {
0533: cm.createIdentifierMapping(classNd);
0534: }
0535: } else {
0536: SpeedoClass parent = sc.getSpeedoClassFromContext(sc
0537: .getSuperClassName());
0538: im = parent.jormclass.getClassProject(projectName)
0539: .getMapping(mapperName).getClassMapping()
0540: .getIdentifierMapping();
0541: if (im != null) {
0542: classNd = (NameDef) im.getLinkedMO();
0543: if (classNd != null) {
0544: cm.createIdentifierMapping(classNd);
0545: }
0546: }
0547: }
0548: if (debug) {
0549: logger.log(BasicLevel.DEBUG,
0550: "Create the identifier of the class '"
0551: + sc.getFQName()
0552: + "'"
0553: + (sc.getSuperClassName() == null ? ""
0554: : "(parent class name='"
0555: + sc.getSuperClassName()
0556: + "')") + ", namedef="
0557: + classNd + ".");
0558: }
0559: return classNd != null;
0560: }
0561:
0562: /**
0563: * Builds the jorm meta objects representing the references owns by a persitent
0564: * class. It is supposed that if the reference targets a persistent class,
0565: * then the Class meta object and its NameDef are already defined.
0566: * @param sc is the Speedo meta object describing the class which the jorm
0567: * meta information must be built.
0568: * @param projectName is the project name for which the mapping must
0569: * be defined
0570: * @param mapperName is the mapper name for which the mapping must
0571: * be defined
0572: * @param mb is the build of the mapping part of the meta information
0573: * @param createdMOs is a result paramter. This collection must be fill with
0574: * the created Jorm Meta objects representing a class or a composite name.
0575: * Here only the new used composite name will be added.
0576: */
0577: private void createReferences(SpeedoClass sc, String projectName,
0578: String mapperName, JormMIMappingBuilder mb,
0579: Collection createdMOs) throws SpeedoException, PException {
0580: Class clazz = manager.getClass(sc.getFQName());
0581: logger.log(BasicLevel.DEBUG,
0582: "Generate the Jorm MI for the references of class "
0583: + clazz.getFQName());
0584: Mapping mapping = null;
0585: ClassMapping cm = null;
0586: if (projectName != null && mapperName != null) {
0587: mapping = clazz.getClassProject(projectName).getMapping(
0588: mapperName);
0589: cm = mapping.getClassMapping();
0590: }
0591: Iterator fieldsIt = sc.fields.values().iterator();
0592: //System.out.println("Manage reference of the class " + sc.getFQName());
0593: while (fieldsIt.hasNext()) {
0594: SpeedoField sp = (SpeedoField) fieldsIt.next();
0595: if (clazz.getTypedElement(sp.name) != null) {
0596: //the primitive element is already managed before(createJormClass)
0597: continue;
0598: }
0599: Type t = Type.getType(sp.type);
0600: String javatype = t.getClassName();
0601: if (!isGenClassRef(javatype)) {
0602: // reference to a class //
0603: //============================================================//
0604: if (debug) {
0605: logger.log(BasicLevel.DEBUG,
0606: "Class reference field: " + sp.name
0607: + " / javatype: " + javatype);
0608: }
0609: // reference to a class
0610: SpeedoClass tsc = sc.moPackage.xmlDescriptor
0611: .getSpeedoClass(javatype, true);
0612: if (tsc == null) {
0613: throw new SpeedoException("No persistent class '"
0614: + javatype + "' found in the file: "
0615: + sc.moPackage.xmlDescriptor.xmlFile);
0616: }
0617: Class tclass = manager.getClass(tsc.getFQName());
0618: if (tclass == null) {
0619: manager.createClass(tsc.getFQName());
0620: }
0621: ClassRef cr = clazz.createClassRef(sp.name, tclass);
0622: NameDef refNd = cr.createRefNameDef();
0623: nmf.getNamingManager(tsc).defineClassReferenceNameDef(
0624: refNd, cr, sp, sc, cm, this , mb);
0625: if (cm != null) {
0626: cm.createReferenceMapping(sp.name, refNd);
0627: cm.addDependency(tsc.getFQName());
0628: }
0629:
0630: } else {
0631: // reference to a generic class //
0632: //============================================================//
0633: String innerType = getInnerType(sp);
0634: if (innerType == null) {
0635: throw new SpeedoException(
0636: "The inner element type is "
0637: + "required for the multivalued field '"
0638: + sp.name
0639: + "' of the class '"
0640: + sc.getFQName()
0641: + "' in the .jdo file '"
0642: + sc.moPackage.xmlDescriptor.xmlFile
0643: + "'");
0644: }
0645: PType type = getPrimitivePType(innerType);
0646: if (type == null
0647: && !isPersistentClass(innerType,
0648: sc.moPackage.name, manager)) {
0649: logger.log(BasicLevel.INFO, "The field '"
0650: + sc.getFQName() + "." + sp.name
0651: + " is managed as a Serialized field.");
0652: type = PTypeSpace.SERIALIZED;
0653: }
0654: if (debug) {
0655: logger.log(BasicLevel.DEBUG,
0656: "GenClass reference field: "
0657: + sp.name
0658: + " / javatype: "
0659: + javatype
0660: + " / innerType: "
0661: + innerType
0662: + " / ptype="
0663: + (type == null ? null : type
0664: .getJavaName()));
0665: }
0666: GenClassRef gcr = clazz.createGenClassRef(sp.name,
0667: javatype);
0668: GenClassMapping gcm = null;
0669: ClassRef cr = null;
0670: SpeedoClass tsc = null;
0671:
0672: //element of the gen class
0673: if (type != null) {
0674: // gen class of primitive type
0675: gcr.createPrimitiveElement(type, PType.NOSIZE,
0676: PType.NOSIZE);
0677: } else if (!isGenClassRef(innerType)) {
0678: // gen class of classref
0679: tsc = sc.moPackage.xmlDescriptor.smi
0680: .getSpeedoClass(innerType, sc.moPackage);
0681: if (tsc == null) {
0682: throw new SpeedoException(
0683: "The persistent class '"
0684: + sc.getFQName()
0685: + "' tries to reference (througth the field '"
0686: + sp.name
0687: + "') the class '"
0688: + innerType
0689: + "' not defined in the .jdo file '"
0690: + sc.moPackage.xmlDescriptor.xmlFile
0691: + "'");
0692: }
0693: Class tclass = manager.getClass(tsc.getFQName());
0694: if (tclass == null) {
0695: throw new SpeedoException(
0696: "The inner element class '"
0697: + tsc.getFQName()
0698: + "' of the multivalued field '"
0699: + sp.name
0700: + "' of the class '"
0701: + sc.getFQName()
0702: + "' in the .jdo file '"
0703: + sc.moPackage.xmlDescriptor.xmlFile
0704: + "' has not been found among the persitent classes : "
0705: + manager.getClasses());
0706: }
0707: cr = gcr.createClassRef(tclass);
0708: } else {
0709: throw new SpeedoException(
0710: "unmanaged the inner-element of the field '"
0711: + sp.name + "' of the class '"
0712: + sc.getFQName() + "' : "
0713: + innerType);
0714: }
0715: //Map the element of the generic class
0716: if (mapping != null) {
0717: gcm = mb.createGenClassMapping(gcr, sp, mapping);
0718: if (gcr.isPrimitive()) {
0719: mb.createGenClassElementMapping(gcr
0720: .getPrimitiveElement(), sp, gcm);
0721: } else {
0722: NameDef elemNd = cr.createRefNameDef();
0723: nmf.getNamingManager(tsc)
0724: .defineClassReferenceNameDef(elemNd,
0725: cr, sp, sc, gcm, this , mb);
0726: //fillNameDef(elemNd, tsc, sc, gcr, gcr, gcm, mb, false, true, true, createdMOs);
0727: gcm.createReferenceMapping(sp.name, elemNd);
0728: cm.addDependency(tsc.getFQName());
0729: }
0730: }
0731:
0732: // reference to a generic class from the class
0733: NameDef refNd = gcr.createRefNameDef();
0734: nmf.getNamingManager(sc)
0735: .defineGenClassReferenceNameDef(refNd, gcr, sp,
0736: sc, cm, this , mb);
0737: //fillNameDef(refNd, sc, sc, clazz, gcr, cm, mb, false, false, false, createdMOs);
0738: if (cm != null) {
0739: cm.createReferenceMapping(sp.name, refNd);
0740: }
0741:
0742: //identifier of the gen class
0743: NameDef gcidNd = gcr.createIdNameDef();
0744: nmf.getNamingManager(sc)
0745: .defineGenClassIdentifierNameDef(gcidNd, gcr,
0746: sp, sc, gcm, this , mb);
0747: //fillNameDef(gcidNd, sc, sc, gcr, gcr, gcm, mb, true, true, true, createdMOs);
0748: if (gcm != null) {
0749: gcm.createIdentifierMapping(gcidNd);
0750: }
0751:
0752: //index of the gen class
0753: java.lang.Class gcClass;
0754: try {
0755: gcClass = java.lang.Class.forName(javatype);
0756: } catch (ClassNotFoundException e) {
0757: throw new SpeedoException(
0758: "The multivalued field '" + sp.name
0759: + "' of the class '"
0760: + sc.getFQName()
0761: + "' is not a well known class "
0762: + javatype, e);
0763: }
0764: if (isSubType(gcClass, java.util.List.class)) {
0765: PrimitiveElement pe = gcr.createHiddenField(
0766: LIST_INDEX, PTypeSpace.INT, PType.NOSIZE,
0767: PType.NOSIZE);
0768: if (gcm != null) {
0769: mb.createGenClassIndexMapping(pe, sp, gcm);
0770: }
0771: gcr.addIndexField(LIST_INDEX);
0772: } else if (isSubType(gcClass, java.util.Map.class)) {
0773: String keyfield = sp
0774: .getExtensionValueByKey(SpeedoProperties.KEY_FIELD);
0775: if (keyfield == null) {
0776: keyfield = MAP_INDEX;
0777: }
0778: PrimitiveElement pe = gcr.createHiddenField(
0779: keyfield, getMapKeyPType(sp), PType.NOSIZE,
0780: PType.NOSIZE);
0781: if (gcm != null) {
0782: mb.createGenClassIndexMapping(pe, sp, gcm);
0783: }
0784: gcr.addIndexField(pe.getName());
0785: }
0786: }
0787: }
0788:
0789: }
0790:
0791: private static final String HIDDEN_FIELD_CLASS_NAME = "speedo_class_name";
0792:
0793: private void mapInheritance(SpeedoClass sc, String projectName,
0794: String mapperName, JormMIMappingBuilder mb)
0795: throws SpeedoException, PException {
0796: ClassMapping cm = sc.jormclass.getClassProject(projectName)
0797: .getMapping(mapperName).getClassMapping();
0798: NameDef nd = (NameDef) cm.getIdentifierMapping().getLinkedMO();
0799:
0800: if (sc.inheritance != null
0801: && sc.inheritance.discriminator != null) {
0802: SpeedoDiscriminator sd = sc.inheritance.discriminator;
0803: String filter = null;
0804: if (sd.expression != null) {
0805: filter = sd.expression;
0806: } else if (sd.elements.size() == 1) {
0807: SpeedoElement se = (SpeedoElement) sd.elements.get(0);
0808: if (se instanceof SpeedoField) {
0809: filter = ((SpeedoField) se).name;
0810: } else if (se instanceof SpeedoNoFieldColumn) {
0811: filter = HIDDEN_FIELD_CLASS_NAME;
0812: } else {
0813: throw new SpeedoException(
0814: "Unmanaged filter element: " + se);
0815: }
0816: } else {
0817: throw new SpeedoException(
0818: "Composite inheritance discriminator not yet supported in Speedo");
0819: }
0820: if (filter != null) {
0821: sc.jormclass.setInheritanceFilter(nd, filter);
0822: logger
0823: .log(BasicLevel.DEBUG, "Assign filter: "
0824: + filter);
0825: }
0826: if (sc.inheritance.discriminator.elements != null
0827: && sc.inheritance.discriminator.elements.size() == 1) {
0828: SpeedoElement se = (SpeedoElement) sc.inheritance.discriminator.elements
0829: .get(0);
0830: if (se instanceof SpeedoNoFieldColumn) {
0831: if (sc.getSuper() == null) {
0832: // The discriminator is a column not mapped to a
0833: // persistent field. then we have to create a constant
0834: // hidden field.
0835: PrimitiveElement pe = sc.jormclass
0836: .createHiddenField(
0837: HIDDEN_FIELD_CLASS_NAME,
0838: PTypeSpace.STRING,
0839: PType.NOSIZE, PType.NOSIZE);
0840: pe
0841: .setStatus(PrimitiveElement.CONSTANT_PERSISTENT);
0842: mb.createFieldMapping(pe,
0843: (SpeedoNoFieldColumn) se, cm);
0844: }
0845: if (sc.inheritance.discriminator.strategy == SpeedoDiscriminator.STRATEGY_CLASS_NAME
0846: && sc.inheritance.discriminatorValues != null) {
0847: // declare the constant value of the field
0848: sc.jormclass
0849: .setConstantValue(
0850: HIDDEN_FIELD_CLASS_NAME,
0851: (String) sc.inheritance.discriminatorValues
0852: .get(se));
0853: }
0854: }
0855: }
0856: }
0857: if (sc.inheritance != null
0858: && sc.inheritance.discriminatorValues != null) {
0859: Object key = sc.inheritance.discriminatorValues.values()
0860: .iterator().next();
0861: logger.log(BasicLevel.DEBUG, "Assign key: " + key);
0862: sc.jormclass.setInheritanceNamingKey(nd, key);
0863: }
0864:
0865: if (sc.getSuperClassName() == null) {
0866: return;
0867: }
0868: SpeedoClass parent = sc.getSpeedoClassFromContext(sc
0869: .getSuperClassName());
0870: String im = sc
0871: .getExtensionValueByKey(SpeedoProperties.INHERITANCE_MAPPING);
0872:
0873: // Map the inherited field if required
0874: if (sc.inheritance.isHorizontalMapping()) {
0875: for (Iterator it = sc.inheritance.remappedInheritedFields
0876: .values().iterator(); it.hasNext();) {
0877: SpeedoInheritedField sif = (SpeedoInheritedField) it
0878: .next();
0879: if (debug) {
0880: logger.log(BasicLevel.DEBUG,
0881: "Map the inherited field '" + sif.name
0882: + "'.");
0883: }
0884: TypedElement te = sif.inheritedField.moClass.jormclass
0885: .getTypedElement(sif.inheritedField.name);
0886: if (te == null) {
0887: throw new SpeedoException("No inherited field '"
0888: + sif.name + "' found from "
0889: + sc.getSourceDesc());
0890: } else if (te instanceof PrimitiveElement) {
0891: mb.createFieldMapping((PrimitiveElement) te, sif,
0892: cm);
0893: } else if (te instanceof ClassRef) {
0894: ClassRef cr = (ClassRef) te;
0895: //Keep the same namedef than in the parent
0896: //but map the field(s) used in name def on column defined
0897: // in SpeedoInheritedField meta object.
0898: NameDef refNd = (NameDef) cr.getRefNameDef()
0899: .iterator().next();
0900: cm.createReferenceMapping(sif.inheritedField.name,
0901: refNd);
0902: //create PEM for each field used in the NameDef
0903: mb.createClassRefNameDefMapping(cm, refNd, sif);
0904: }
0905: }
0906: } else if (sc.inheritance.isVerticalMapping()) {
0907: //TODO
0908: }
0909: }
0910:
0911: private boolean isSubType(java.lang.Class c1, java.lang.Class c2) {
0912: if (c1 == null) {
0913: return false;
0914: }
0915: if (c1 == c2) {
0916: return true;
0917: }
0918: if (c2.isInterface()) {
0919: java.lang.Class[] cs = c1.getInterfaces();
0920: for (int i = 0; i < cs.length; i++) {
0921: if (isSubType(cs[i], c2)) {
0922: return true;
0923: }
0924: }
0925: }
0926: return isSubType(c1.getSuperclass(), c2);
0927: }
0928:
0929: private PType getMapKeyPType(SpeedoField sf) throws SpeedoException {
0930: SpeedoMap sm = (SpeedoMap) sf.jdoTuple;
0931: if (sm.keyType == null) {
0932: throw new SpeedoException(
0933: "It is required to define a key-type for the Map field'"
0934: + sf.name + "' of the class '"
0935: + sf.moClass.getFQName() + "' ");
0936: }
0937: PType res = getPrimitivePType((String) sm.keyType);
0938: boolean isPersistClass = isPersistentClass((String) sm.keyType,
0939: sf.moClass.moPackage.name, manager);
0940: if (res == null && !isPersistClass) {
0941: logger.log(BasicLevel.INFO, "The field '"
0942: + sf.moClass.getFQName() + "." + sf.name
0943: + " is managed as a Serialized field.");
0944: res = PTypeSpace.SERIALIZED;
0945: }
0946: if (res == null) {
0947: throw new SpeedoException("The key-type '" + sm.keyType
0948: + "' of the Map field'" + sf.name
0949: + "' of the class '" + sf.moClass.getFQName()
0950: + "' is not supported");
0951: }
0952: return res;
0953: }
0954:
0955: /**
0956: * Fill the name def of an identifier or a reference
0957: * @param nd is the name def to fill
0958: * @param isIdentifier indicates if the name represents an identifier (true)
0959: * or a reference (false).
0960: * @param isInGenClass indicates if the name is defined in a generic class
0961: * (true) of in a class (false).
0962: * @param ref meta object which the name def must be
0963: * defined. This value is used only in the case of isIdentifier == false and
0964: * isInGenClass == false.
0965: * @param tsc is the speedo meta object representing the referenced class.
0966: * This value is used only in the case of isIdentifier == false.
0967: * @param mo is the jorm meta object hosting the name def and on which the
0968: * eventual hidden field will be created.
0969: * @param hcm if the mapping structure hosting the mapping of the reference.
0970: * This value must be ClassMapping or GenClassMapping instance.
0971: * @param mb is the mapping builder permitting the creation of the mapping
0972: * part.
0973: * @param createdMOs is a result paramter. This collection must be fill with
0974: * the created Jorm Meta objects representing a class or a composite name.
0975: * Here only the new used composite name will be added.
0976: * @throws SpeedoException if the speedo meta information is not completly
0977: * defined.
0978: private void _fillNameDef(NameDef nd,
0979: SpeedoClass tsc,
0980: SpeedoClass ssc,
0981: MetaObject mo,
0982: Reference ref,
0983: CommonClassMapping hcm,
0984: JormMIMappingBuilder mb,
0985: boolean isIdentifier,
0986: boolean isInGenClass,
0987: boolean createField,
0988: Collection createdMOs)
0989: throws SpeedoException, PException {
0990: nmf.getNamingManager(tsc)
0991: .fillNameDef(this,manager, nd, tsc, ssc, mo, ref, hcm,
0992: mb, isIdentifier, isInGenClass, createField, createdMOs);
0993: }
0994: */
0995:
0996: /**
0997: * Calculates the prefix of a field use in a name def.
0998: * @param ref is the meta object of the reference if the namedef is
0999: * used for a reference.
1000: * @param isIdentifier indicates if the namedef is used for an identifier
1001: * (true) or if the namedef is used for a reference (false).
1002: * @param isInGenClass indicates if the namedef is used in a Generic class
1003: * (true) or if the namedef is used in a class (false).
1004: * @return a string value (never null) representing the prefix of a name
1005: * def field.
1006: */
1007: public String getNameDefFieldPrefix(Reference ref,
1008: boolean isIdentifier, boolean isInGenClass, SpeedoField sf)
1009: throws SpeedoException {
1010: if (isIdentifier) {
1011: if (isInGenClass
1012: && sf.relationType == SpeedoField.ONE_MANY_BI_RELATION) {
1013: //In case of ONE-MANY relation ship with Inheritance in the
1014: // MANY side the hidden field name in the GenClass MUST have
1015: // the same name than the hidden field in the referenced class
1016: // Otherwise the alias names do not correspond in the SQL
1017: // request generated by JORM for in inheritance.
1018: return sf.getReverseField().name + '_';
1019: }
1020: return "";
1021: } else {
1022: if (isInGenClass) {
1023: if (sf.relationType == SpeedoField.ONE_MANY_BI_RELATION) {
1024: //In case of ONE-MANY relation ship with Inheritance in the
1025: // MANY side the hidden field name in the GenClass MUST have
1026: // the same name than the hidden field in the referenced class
1027: // Otherwise the alias names do not correspond in the SQL
1028: // request generated by JORM for in inheritance.
1029: return "";
1030: } else {
1031: return GENCLASS_ELM_PREFIX;
1032: }
1033: } else {
1034: if (ref instanceof GenClassRef) {
1035: //The reference of the GCR is mapped over the class identifer
1036: // fields
1037: return "";
1038: } else {
1039: return ref.getName() + '_';
1040: }
1041: }
1042: }
1043: }
1044:
1045: /**
1046: * It creates a field of a name def. This field will be hidden
1047: * @param mo is the jorm meta object hosting the field to create
1048: * @param fn is the name of the field to create
1049: * @param type is the type of the field to create
1050: * @param size is the size of the field to create
1051: * @return the jorm meta object representing the namedef field
1052: */
1053: public ScalarField createNameDefField(MetaObject mo, String fn,
1054: PType type, int size, int scale) throws SpeedoException {
1055: ScalarField sf;
1056: if (mo instanceof Class) {
1057: sf = ((Class) mo).createHiddenField(fn, type, size, scale);
1058: } else if (mo instanceof GenClassRef) {
1059: sf = ((GenClassRef) mo).createHiddenField(fn, type, size,
1060: scale);
1061: } else {
1062: throw new SpeedoException(
1063: "Impossible to create hidden field on this meta object: "
1064: + mo);
1065: }
1066: return sf;
1067: }
1068:
1069: /**
1070: * It creates a field of a name def. This field will be hidden
1071: * @param mo is the jorm meta object hosting the field to create
1072: * @param fn is the name of the field to create
1073: * @param type is the type of the field to create
1074: * @return the jorm meta object representing the namedef field
1075: */
1076: public ScalarField createNameDefField(MetaObject mo, String fn,
1077: PType type) throws SpeedoException {
1078: return createNameDefField(mo, fn, type, PType.NOSIZE,
1079: PType.NOSIZE);
1080: }
1081:
1082: /**
1083: * Retrieves the jorm type matching to primitive type. It converts a Type
1084: * defined in ASM into a PType defined in Jorm. If the type is not a jorm
1085: * primitive type then a null value is returned.
1086: */
1087: public PType getPrimitivePType(Type t) {
1088: switch (t.getSort()) {
1089: case Type.BOOLEAN:
1090: return PTypeSpace.BOOLEAN;
1091: case Type.CHAR:
1092: return PTypeSpace.CHAR;
1093: case Type.BYTE:
1094: return PTypeSpace.BYTE;
1095: case Type.SHORT:
1096: return PTypeSpace.SHORT;
1097: case Type.INT:
1098: return PTypeSpace.INT;
1099: case Type.FLOAT:
1100: return PTypeSpace.FLOAT;
1101: case Type.LONG:
1102: return PTypeSpace.LONG;
1103: case Type.DOUBLE:
1104: return PTypeSpace.DOUBLE;
1105: case Type.ARRAY:
1106: PType innerType = getPrimitivePType(t.getElementType());
1107: if (innerType != null) {
1108: if (innerType == PTypeSpace.CHAR) {
1109: return PTypeSpace.CHARARRAY;
1110: } else if (innerType == PTypeSpace.BYTE) {
1111: return PTypeSpace.BYTEARRAY;
1112: } else {
1113: return PTypeSpace.SERIALIZED;
1114: }
1115: }
1116: return null;
1117: case Type.OBJECT:
1118: default:
1119: String cn = t.getClassName();
1120: PType res = getPrimitivePType(cn);
1121: if (res == null && !isPersistentClass(cn, null, manager)) {
1122: return PTypeSpace.SERIALIZED;
1123: } else {
1124: return res;
1125: }
1126: }
1127: }
1128:
1129: private PType getPrimitivePType(String cn) {
1130: for (int i = 0; i < PTypeSpace.PREDEFINEDPTYPES.length; i++) {
1131: if (PTypeSpace.PREDEFINEDPTYPES[i] != PTypeSpace.REFTOP
1132: && PTypeSpace.PREDEFINEDPTYPES[i].getJavaName()
1133: .equals(cn)) {
1134: return PTypeSpace.PREDEFINEDPTYPES[i];
1135: }
1136: }
1137: if (java.sql.Date.class.getName().equals(cn)) {
1138: return PTypeSpace.DATE;
1139: } else if (java.sql.Time.class.getName().equals(cn)) {
1140: return PTypeSpace.DATE;
1141: } else if (java.sql.Timestamp.class.getName().equals(cn)) {
1142: return PTypeSpace.DATE;
1143: } else if (java.util.Locale.class.getName().equals(cn)) {
1144: return PTypeSpace.STRING;
1145: } else {
1146: return null;
1147: }
1148: }
1149:
1150: private boolean isPersistentClass(String cn, String currentPackage,
1151: Manager mgr) {
1152: if (isGenClassRef(cn)) {
1153: return true;
1154: }
1155: if (cn.indexOf('.') != -1 || currentPackage == null) {
1156: return mgr.getClass(cn) != null;
1157: } else {
1158: return mgr.getClass(currentPackage + '.' + cn) != null;
1159: }
1160: }
1161:
1162: /**
1163: * Indicates if the string representing a java type is a generic class (
1164: * java.util.Collection | java.util.Set | java.util.Map
1165: * @param javatype
1166: * @return
1167: */
1168: private boolean isGenClassRef(String javatype) {
1169: int i = 0;
1170: while (i < AbstractSpeedoGenerator.GC_IMPL.length
1171: && !AbstractSpeedoGenerator.GC_IMPL[i][0]
1172: .equals(javatype)) {
1173: i++;
1174: }
1175: return i < AbstractSpeedoGenerator.GC_IMPL.length;
1176: }
1177:
1178: /**
1179: * Retrieves the type of a generic class element.
1180: * @param sp is the field which represents the generic class
1181: * @return a String value representing the java type of the element, or null
1182: * if the Speedofield does not represents a generic class.
1183: */
1184: private String getInnerType(SpeedoField sp) {
1185: if (sp.jdoTuple instanceof SpeedoCollection) {
1186: return (String) ((SpeedoCollection) sp.jdoTuple).elementType;
1187: } else if (sp.jdoTuple instanceof SpeedoMap) {
1188: return (String) ((SpeedoMap) sp.jdoTuple).valueType;
1189: }
1190: return null;
1191: }
1192:
1193: /**
1194: * retrieves the start of a pretty error message.
1195: */
1196: public String getErrorMessage(SpeedoClass sc, MetaObject mo,
1197: Reference ref) {
1198: String fqcn;
1199: if (mo == null) {
1200: fqcn = "null";
1201: } else if (mo instanceof Class) {
1202: fqcn = ((Class) mo).getFQName();
1203: } else if (mo instanceof GenClassRef) {
1204: fqcn = ((GenClassRef) mo).getGenClassId();
1205: } else {
1206: fqcn = mo.toString();
1207: }
1208: String res = "Impossible to define an user identifier for the ";
1209: if (ref == null) {
1210: res += "identifier of the class '" + fqcn;
1211: } else {
1212: res += "reference '" + ref.getName() + "' from the class '"
1213: + fqcn + "' to the class '" + sc.getFQName();
1214: }
1215: return res + "': ";
1216: }
1217:
1218: public PrimitiveElement getPrimitiveField(MetaObject mo, String name) {
1219: if (mo instanceof Class) {
1220: return (PrimitiveElement) ((Class) mo)
1221: .getTypedElement(name);
1222: } else if (mo instanceof GenClassRef) {
1223: return ((GenClassRef) mo).getHiddenField(name);
1224: } else {
1225: return null;
1226: }
1227: }
1228:
1229: public Manager getManager(MetaObject mo) {
1230: MetaObject current = mo;
1231: while (!(current instanceof Manager)) {
1232: current = current.getParent();
1233: }
1234: return (Manager) current;
1235: }
1236: }
|