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.mivisitor;
0018:
0019: import java.util.ArrayList;
0020: import java.util.Arrays;
0021: import java.util.Collection;
0022: import java.util.Collections;
0023: import java.util.HashMap;
0024: import java.util.Iterator;
0025: import java.util.List;
0026: import java.util.Map;
0027: import java.util.StringTokenizer;
0028:
0029: import org.objectweb.speedo.api.SpeedoException;
0030: import org.objectweb.speedo.api.SpeedoProperties;
0031: import org.objectweb.speedo.generation.lib.AbstractGeneratorComponent;
0032: import org.objectweb.speedo.lib.Personality;
0033: import org.objectweb.speedo.metadata.SpeedoClass;
0034: import org.objectweb.speedo.metadata.SpeedoCollection;
0035: import org.objectweb.speedo.metadata.SpeedoColumn;
0036: import org.objectweb.speedo.metadata.SpeedoCommonField;
0037: import org.objectweb.speedo.metadata.SpeedoDiscriminator;
0038: import org.objectweb.speedo.metadata.SpeedoField;
0039: import org.objectweb.speedo.metadata.SpeedoInheritance;
0040: import org.objectweb.speedo.metadata.SpeedoInheritedField;
0041: import org.objectweb.speedo.metadata.SpeedoJoin;
0042: import org.objectweb.speedo.metadata.SpeedoJoinColumn;
0043: import org.objectweb.speedo.metadata.SpeedoMap;
0044: import org.objectweb.speedo.metadata.SpeedoPackage;
0045: import org.objectweb.speedo.metadata.SpeedoTable;
0046: import org.objectweb.speedo.metadata.SpeedoXMLDescriptor;
0047: import org.objectweb.speedo.naming.api.NamingManager;
0048: import org.objectweb.util.monolog.api.BasicLevel;
0049:
0050: /**
0051: * This Speedo Meta information visitor builds and fills the Speedo Meta
0052: * information concerning the OR mapping information. It defines a SpeedoTable,
0053: * SpeedoColumn, SpeedoJoin where it is required.
0054: * This SMI visitor cannot be inclued with other SMI visitor. Indeed it requires
0055: * that all meta information is complete (for all classes). In particular it
0056: * needs the result of the Class analyser visitor. For this reason this SMI
0057: * visitor is a simple GeneratorComponent to use when SMI is complete.
0058: * In addition, due to dependency between classes (relation ship, inheritance)
0059: * the visit of classes must be done in a particular order. For these reasons,
0060: * this Visitor visits the SMI with a help of two internal classes (
0061: * VisitRequired and VisitRemeber).
0062: *
0063: * @author S.Chassande-Barrioz
0064: */
0065: public class ORMappingGenerator extends AbstractGeneratorComponent {
0066: /**
0067: * This class represents required element to be visited in a persistent
0068: * class.
0069: *
0070: * @author S.Chassande-Barrioz
0071: */
0072: public static class VisitRequired {
0073: /**
0074: * constant representing a visit of nothing
0075: */
0076: public final static VisitRequired NOTHING = new VisitRequired(
0077: false, false, null);
0078: /**
0079: * constant representing a visit of all elements of a persistent class
0080: */
0081: public final static VisitRequired ALL = new VisitRequired(true,
0082: true, null);
0083: /**
0084: * constant representing a visit of base element of a persistent class
0085: */
0086: public final static VisitRequired BASE = new VisitRequired(
0087: true, false, null);
0088:
0089: /**
0090: * indicates if the base must be visited. The base represents:
0091: * - the table of the persistent class
0092: * - the identifier of the persistent class
0093: * - the primitivie fields of the persistent class
0094: */
0095: public boolean base = false;
0096: /**
0097: * Indicates if all references must be visisted (ClassRef
0098: * and GenClassRef)
0099: */
0100: public boolean references = false;
0101: /**
0102: * indicates if the visit of a particular reference field is required.
0103: * It is often used in case of bi directional relationship with one side
0104: * of the relation mmped by the other one.
0105: */
0106: public SpeedoField refField = null;
0107:
0108: /**
0109: * Builds a new VisitRequired instance including the base elements and
0110: * a particular reference field.
0111: * @param refField the field to visit
0112: */
0113: public VisitRequired(SpeedoField refField) {
0114: this .base = true;
0115: this .references = false;
0116: this .refField = refField;
0117: }
0118:
0119: /**
0120: * Private constructor for static constant only.
0121: */
0122: private VisitRequired(boolean base, boolean references,
0123: SpeedoField refField) {
0124: this .base = base;
0125: this .references = references;
0126: this .refField = refField;
0127: }
0128:
0129: /**
0130: * Prints a string representing the current instance.
0131: */
0132: public String toString() {
0133: if (base) {
0134: if (references) {
0135: return "ALL";
0136: } else if (refField == null) {
0137: return "BASE";
0138: } else {
0139: return "BASE, " + refField.name;
0140: }
0141: } else {
0142: return "NOTHING";
0143: }
0144: }
0145: }
0146:
0147: /**
0148: * This class represents the status of visited/treated elements of a
0149: * persistent class.
0150: *
0151: * @author S.Chassande-Barrioz
0152: */
0153: public static class VisitRemember {
0154: /**
0155: * The persistent class
0156: */
0157: private SpeedoClass sc;
0158: /**
0159: * indicates if the base elements have been visited:
0160: * - the table of the persistent class
0161: * - the identifier of the persistent class
0162: * - the primitivie fields of the persistent class
0163: */
0164: private boolean baseVisited = false;
0165: /**
0166: * Indicates if inherited fields have been visited.
0167: */
0168: private boolean inheritedFieldsVisited = false;
0169: /**
0170: * the list primitive fields of the persistent class
0171: * The content of the list is SpeedoField instances.
0172: */
0173: public List primitiveFields = new ArrayList();
0174: /**
0175: * the list reference fields of the persistent class (class reference
0176: * and generic class reference)
0177: * The content of the list is SpeedoField instances.
0178: */
0179: public List references = new ArrayList();
0180: /**
0181: * The list of reference fields already visisted.
0182: */
0183: private List visitedReferencesFields = new ArrayList();
0184:
0185: /**
0186: * Builds a new instance for a persistent class. This constructor fills
0187: * the list of persistent fields.
0188: */
0189: public VisitRemember(SpeedoClass c) {
0190: sc = c;
0191: for (Iterator fieldIt = sc.fields.values().iterator(); fieldIt
0192: .hasNext();) {
0193: SpeedoField sf = (SpeedoField) fieldIt.next();
0194: if (sf.jdoTuple != null) {
0195: references.add(sf);
0196: } else {
0197: SpeedoClass rclass = sf.getReferencedClass();
0198: if (rclass == null) {
0199: primitiveFields.add(sf);
0200: } else {
0201: references.add(sf);
0202: }
0203: }
0204: }
0205: }
0206:
0207: /**
0208: * Print the status of the class visit
0209: */
0210: public String toString() {
0211: if (baseVisited) {
0212: int nbref = references.size();
0213: int visitedref = visitedReferencesFields.size();
0214: if (nbref == visitedref) {
0215: return "ALL";
0216: } else if (visitedref == 0) {
0217: return "BASE";
0218: } else {
0219: StringBuffer sb = new StringBuffer();
0220: sb.append("BASE");
0221: for (Iterator it = visitedReferencesFields
0222: .iterator(); it.hasNext();) {
0223: SpeedoField sf = (SpeedoField) it.next();
0224: sb.append(", ").append(sf.name);
0225: }
0226: return sb.toString();
0227: }
0228: } else {
0229: return "NOTHING";
0230: }
0231: }
0232:
0233: /**
0234: * Indicates if some elements specified by the VisitRequired parameter
0235: * have not been already visited.
0236: */
0237: public boolean hasUnvisitedPart(VisitRequired req) {
0238: if (req.base && !baseVisited) {
0239: return true;
0240: }
0241: if (req.references
0242: && references.size() > visitedReferencesFields
0243: .size()) {
0244: return true;
0245: }
0246: if (req.refField != null
0247: && !visitedReferencesFields.contains(req.refField)) {
0248: return true;
0249: }
0250: if (req.references && !inheritedFieldsVisited) {
0251: return true;
0252: }
0253: return false;
0254: }
0255:
0256: /**
0257: * Indicates if the base must be visited according to the parameter
0258: * and the current status.
0259: */
0260: public boolean visitBase(VisitRequired vr) {
0261: return vr.base && !baseVisited;
0262: }
0263:
0264: /**
0265: * Callback method to indicate that the base elements have been visited.
0266: */
0267: public void baseVisited() {
0268: baseVisited = true;
0269: }
0270:
0271: /**
0272: * Indicates if one or several reference fields must be visited
0273: * according to the parameter and the current status.
0274: */
0275: public boolean visitReferences(VisitRequired vr) {
0276: return (vr.references || vr.refField != null)
0277: && references.size() > visitedReferencesFields
0278: .size();
0279: }
0280:
0281: /**
0282: * Callback method to indicate that a reference field has been visited.
0283: */
0284: public boolean visitReferenceField(SpeedoField sf,
0285: VisitRequired vr) {
0286: return (vr.references || vr.refField == sf)
0287: && !visitedReferencesFields.contains(sf);
0288: }
0289:
0290: /**
0291: * Callback method to indicate that a reference field has been visited.
0292: */
0293: public void referenceFieldVisisted(SpeedoField sf) {
0294: visitedReferencesFields.add(sf);
0295: }
0296:
0297: /**
0298: * Indicates if inherited fields must be visited
0299: * according to the parameter and the current status.
0300: */
0301: public boolean visitInheritedFields(VisitRequired vr) {
0302: return vr.references && !inheritedFieldsVisited;
0303: }
0304:
0305: /**
0306: * Callback method to indicate that inherited fields have been visited.
0307: */
0308: public void inheritedFieldsVisited() {
0309: inheritedFieldsVisited = true;
0310: }
0311: }
0312:
0313: /**
0314: * is the map of visited classes (partialy of totaly)
0315: * key = fully qualified persistent class name
0316: * value = VisitRemener instance
0317: */
0318: private HashMap visitedClasses = new HashMap();
0319: public final static String LOGGER_NAME = SpeedoProperties.LOGGER_NAME
0320: + ".generation.orm";
0321:
0322: public ORMappingGenerator(Personality p) {
0323: super (p);
0324: }
0325:
0326: public String getTitle() {
0327: return "Auto O/R Mapping...";
0328: }
0329:
0330: protected String getLoggerName() {
0331: return LOGGER_NAME;
0332: }
0333:
0334: public boolean init() throws SpeedoException {
0335: visitedClasses.clear();
0336: logger = scp.loggerFactory.getLogger(getLoggerName());
0337: debug = logger != null && logger.isLoggable(BasicLevel.DEBUG);
0338: return !scp.getXmldescriptor().isEmpty();
0339: }
0340:
0341: public void process() throws SpeedoException {
0342: visitedClasses.clear();
0343: for (Iterator xmlIt = scp.smi.xmlDescriptors.values()
0344: .iterator(); xmlIt.hasNext();) {
0345: SpeedoXMLDescriptor xml = (SpeedoXMLDescriptor) xmlIt
0346: .next();
0347: for (Iterator packIt = xml.packages.values().iterator(); packIt
0348: .hasNext();) {
0349: SpeedoPackage sp = (SpeedoPackage) packIt.next();
0350: for (Iterator classIt = sp.classes.values().iterator(); classIt
0351: .hasNext();) {
0352: SpeedoClass sc = (SpeedoClass) classIt.next();
0353: visitSpeedoClass(sc, VisitRequired.ALL);
0354: }
0355: }
0356: }
0357: visitedClasses.clear();
0358: }
0359:
0360: /**
0361: * Visits a SpeedoClass.
0362: * @param sc the class to visit
0363: * @param toVisit
0364: * @throws SpeedoException
0365: */
0366: private void visitSpeedoClass(SpeedoClass sc, VisitRequired toVisit)
0367: throws SpeedoException {
0368: logger.log(BasicLevel.DEBUG, "* visit class '" + sc.getFQName()
0369: + "', " + toVisit + ".");
0370: VisitRemember vr = (VisitRemember) visitedClasses.get(sc);
0371: if (vr == null) {
0372: vr = new VisitRemember(sc);
0373: visitedClasses.put(sc, vr);
0374: } else if (!vr.hasUnvisitedPart(toVisit)) {
0375: logger.log(BasicLevel.DEBUG, "\t=> already visited: " + vr
0376: + ".");
0377: return;
0378: }
0379: if (vr.visitBase(toVisit)) {
0380: logger.log(BasicLevel.DEBUG, "\tvisit base.");
0381: //visit parents first
0382: visitClassParent(sc, toVisit);
0383: //visit inheritance strategy
0384: visitClassInheritanceStrategy(sc);
0385: //visit table of the persistent class
0386: visitClassTable(sc);
0387: //visit identity column of the persistent class
0388: visitIdentityColumn(sc);
0389: //visit secondary tables (not the join associated)
0390: if (sc.joinToExtTables != null) {
0391: for (int i = 0; i < sc.joinToExtTables.length; i++) {
0392: SpeedoJoin join = sc.joinToExtTables[i];
0393: if (join.mainTable == null) {
0394: join.mainTable = sc.mainTable;
0395: }
0396: if (join.extTable == null) {
0397: join.extTable = new SpeedoTable();
0398: allocateTableName(sc, join.extTable);
0399: }
0400: }
0401: }
0402: //visit primitive Fields first because it can contain the pk fields used
0403: // for classRef or genClassRef
0404: //visit primitive fields (including pk fields)
0405: for (Iterator fieldIt = vr.primitiveFields.iterator(); fieldIt
0406: .hasNext();) {
0407: SpeedoField sf = (SpeedoField) fieldIt.next();
0408: visitPrimitiveField(sf);
0409: }
0410: if (sc.joinToExtTables != null) {
0411: for (int i = 0; i < sc.joinToExtTables.length; i++) {
0412: visitJoinOfSecondaryTable(sc.joinToExtTables[i], sc);
0413: }
0414: }
0415: vr.baseVisited();
0416: }
0417: if (vr.visitReferences(toVisit)) {
0418: logger.log(BasicLevel.DEBUG, "\tvisit reference fields:");
0419:
0420: //visit references fields
0421: for (Iterator fieldIt = vr.references.iterator(); fieldIt
0422: .hasNext();) {
0423: SpeedoField sf = (SpeedoField) fieldIt.next();
0424: if (vr.visitReferenceField(sf, toVisit)) {
0425: if (sf.jdoTuple == null) {
0426: visitClassRefField(sf);
0427: } else {
0428: visitGenClassRefField(sf);
0429: }
0430: vr.referenceFieldVisisted(sf);
0431: }
0432: }
0433: }
0434: if (vr.visitInheritedFields(toVisit)) {
0435: visitClassInheritance(sc);
0436: vr.inheritedFieldsVisited();
0437: }
0438: logger.log(BasicLevel.DEBUG, "End of visit class '"
0439: + sc.getFQName() + "', " + toVisit + ".");
0440: }
0441:
0442: private void visitJoinOfSecondaryTable(SpeedoJoin join,
0443: SpeedoClass sc) throws SpeedoException {
0444: logger.log(BasicLevel.DEBUG,
0445: "\t\tvisit join to the secondary table '"
0446: + join.extTable.name + "'.");
0447: if (join.columns.isEmpty()) {
0448: //create new JoinColumn to the pk column of the class
0449: join.columns.addAll(getFKJoinColumn(sc, "FK_",
0450: join.extTable));
0451: } else {
0452: int size = join.columns.size();
0453: for (Iterator it = join.columns.iterator(); it.hasNext();) {
0454: SpeedoJoinColumn jc = (SpeedoJoinColumn) it.next();
0455: if (jc.targetColumn == null) {
0456: if (jc.targetField == null) {
0457: if (size == 1) {
0458: SpeedoColumn[] cols = getIdColumns(sc);
0459: if (cols.length > 1) {
0460: throw new SpeedoException(
0461: "The join to the '"
0462: + join.extTable.name
0463: + "' secondary table has not targetColumn defined and there is several identifier column in the "
0464: + sc.getSourceDesc());
0465: }
0466: jc.targetColumn = cols[0].name;
0467: logger.log(BasicLevel.DEBUG,
0468: "\t\tset the target column from the unique PK column: "
0469: + jc.targetColumn);
0470: } else {
0471: throw new SpeedoException(
0472: "The join columns to the '"
0473: + join.extTable.name
0474: + "' secondary table have not targetColumn defined in the "
0475: + sc.getSourceDesc());
0476: }
0477: } else {
0478: //find the column of the target field
0479: SpeedoField sf = sc.getField(jc.targetField);
0480: if (sf == null) {
0481: throw new SpeedoException(
0482: "Targeted field '"
0483: + jc.targetField
0484: + "' in the "
0485: + sc.getSourceDesc()
0486: + ". It is defined in the join to the secondary table '"
0487: + join.extTable.name + "'.");
0488: }
0489: if (sf.columns == null
0490: || sf.columns.length != 1) {
0491: throw new SpeedoException(
0492: "In join column, target field must be a primitive field: "
0493: + sf.getSourceDesc()
0494: + ". It is defined in the join to the secondary table '"
0495: + join.extTable.name + "'.");
0496: }
0497: jc.targetColumn = sf.columns[0].name;
0498: }
0499: }
0500: }
0501: }
0502: }
0503:
0504: /**
0505: * Visits parent persistent class starting with the root class.
0506: * @param sc the Speedoclass to visit.
0507: * @param toVisit
0508: * @throws SpeedoException
0509: */
0510: private void visitClassParent(SpeedoClass sc, VisitRequired toVisit)
0511: throws SpeedoException {
0512:
0513: //find all not visited parents
0514: List parents = new ArrayList();
0515: SpeedoClass parent = sc.getSuper();
0516: while (parent != null) {
0517: VisitRemember vr = (VisitRemember) visitedClasses
0518: .get(parent);
0519: if (vr == null || vr.hasUnvisitedPart(toVisit)) {
0520: //Add in first
0521: parents.add(0, parent);
0522: parent = parent.getSuper();
0523: } else {
0524: //if a parent is visited all super parents are visited too
0525: parent = null;
0526: }
0527: }
0528: //visit parents
0529: for (Iterator it = parents.iterator(); it.hasNext();) {
0530: logger.log(BasicLevel.DEBUG, "\tvisit parent of "
0531: + sc.getFQName());
0532: visitSpeedoClass((SpeedoClass) it.next(), toVisit);
0533: }
0534: }
0535:
0536: /**
0537: * Set inheritance strategy when it is not defined.
0538: */
0539: private void visitClassInheritanceStrategy(SpeedoClass sc)
0540: throws SpeedoException {
0541: //assign default inheritance strategy
0542: if (sc.inheritance != null
0543: && sc.inheritance.super ClassName != null
0544: && sc.inheritance.strategy == SpeedoInheritance.STRATEGY_UNKOWN) {
0545: if (sc.mainTable == null) {
0546: //no table defined
0547: if (sc.inheritance.join == null) {
0548: //no join ==> choose filtered
0549: sc.inheritance.strategy = SpeedoInheritance.STRATEGY_SUPERCLASS_TABLE;
0550: } else {
0551: //there is a join ==> it means vertical
0552: sc.inheritance.strategy = SpeedoInheritance.STRATEGY_NEW_TABLE;
0553: }
0554: } else {
0555: // there is a table. It means horizontal or vertical
0556: // either there is a join
0557: sc.inheritance.strategy = SpeedoInheritance.STRATEGY_NEW_TABLE;
0558: }
0559: }
0560: }
0561:
0562: /**
0563: * Computes the main table according to the inheritance strategy.
0564: */
0565: private void visitClassTable(SpeedoClass sc) throws SpeedoException {
0566: //check the mainTable
0567: if (sc.mainTable == null) {
0568: if (sc.inheritance == null
0569: || sc.inheritance.super ClassName == null) {
0570: sc.mainTable = new SpeedoTable();
0571: } else if (sc.inheritance.isFilteredMapping()) {
0572: //The table is one of the parent
0573: sc.mainTable = getRootTable(sc.getSuper());
0574: } else if (sc.inheritance.isHorizontalMapping()) {
0575: sc.mainTable = new SpeedoTable();
0576: } else if (sc.inheritance.isVerticalMapping()) {
0577: SpeedoClass parent = sc.getSuper();
0578: if (sc.inheritance.join == null) {
0579: sc.inheritance.join = new SpeedoJoin();
0580: }
0581: SpeedoJoin join = sc.inheritance.join;
0582: if (join.mainTable == null) {
0583: join.mainTable = getRootTable(parent);
0584: }
0585: if (sc.mainTable == null) {
0586: if (join.extTable == null) {
0587: join.extTable = new SpeedoTable();
0588: }
0589: sc.mainTable = join.extTable;
0590: } else {
0591: join.extTable = sc.mainTable;
0592: }
0593: } else if (sc.inheritance.strategy == SpeedoInheritance.STRATEGY_SUBCLASS_TABLE) {
0594: //allocate a temp table
0595: sc.mainTable = new SpeedoTable();
0596: } else {
0597: throw new SpeedoException(
0598: "Inheritance case not managed, class: "
0599: + sc.getSourceDesc());
0600: }
0601: }
0602: if (sc.mainTable.name == null) {
0603: allocateTableName(sc, sc.mainTable);
0604: }
0605: }
0606:
0607: /**
0608: * Visit inheritance elements discriminators, inherited fields, join to
0609: * parent table, ...
0610: */
0611: private void visitClassInheritance(SpeedoClass sc)
0612: throws SpeedoException {
0613: if (sc.inheritance == null
0614: || sc.inheritance.super ClassName == null) {
0615: return;
0616: }
0617: if (sc.inheritance.isFilteredMapping()) {
0618: if (scp.nmf.getNamingManager(sc)
0619: .needInheritanceDiscriminator(sc)) {
0620: SpeedoClass ancestor = sc.getAncestor();
0621: if (ancestor.inheritance == null
0622: || ancestor.inheritance.discriminator == null) {
0623: throw new SpeedoException(
0624: "Filtered inheritance requires discriminator defined at root level: "
0625: + ancestor.getSourceDesc());
0626: }
0627: if (sc.inheritance.discriminatorValues == null) {
0628: throw new SpeedoException(
0629: "Filtered inheritance requires discriminator values defined for each sub class: "
0630: + sc.getSourceDesc());
0631: }
0632: }
0633: } else if (sc.inheritance.isHorizontalMapping()) {
0634: //maps all field of all parents
0635: List parents = sc.getParents();
0636: for (Iterator parentIt = parents.iterator(); parentIt
0637: .hasNext();) {
0638: SpeedoClass parent = (SpeedoClass) parentIt.next();
0639: for (Iterator fieldIt = parent.fields.values()
0640: .iterator(); fieldIt.hasNext();) {
0641: mapHorizontalInheritedField((SpeedoField) fieldIt
0642: .next(), sc);
0643: }
0644: }
0645: } else if (sc.inheritance.isVerticalMapping()) {
0646: //TODO: join columns between join.mainTable and join.extTable
0647: //TODO: discriminator
0648: }
0649: }
0650:
0651: /**
0652: * Visit identity column(s) when there is no primary key field
0653: */
0654: private void visitIdentityColumn(SpeedoClass sc)
0655: throws SpeedoException {
0656: if (sc.identity.columns != null || sc.getPKFields().size() > 0) {
0657: return;
0658: }
0659: //no pk fields and no column defined in sc.identity
0660: NamingManager nm = scp.nmf.getNamingManager(sc);
0661: SpeedoColumn[] cols = nm.getDefaultColumn(sc);
0662: if (cols == null) {
0663: throw new SpeedoException(
0664: "no identity mapping for the class '"
0665: + sc.getFQName() + "'.");
0666: }
0667: sc.identity.setColumns(Arrays.asList(cols));
0668: }
0669:
0670: /**
0671: * Visit primitive field. By default a primitive field is stored in the
0672: * main table except if a join is specified. The default column name is
0673: * the field name.
0674: */
0675: private void visitPrimitiveField(SpeedoField sf)
0676: throws SpeedoException {
0677: logger.log(BasicLevel.DEBUG, "\t\tvisit field primitive '"
0678: + sf.name + "'.");
0679: if (sf.columns == null) {
0680: logger.log(BasicLevel.DEBUG, "\t\tcreate new Column.");
0681: sf.addColumn(new SpeedoColumn());
0682: }
0683: SpeedoColumn col = sf.columns[0];
0684: if (col.table == null) {
0685: if (sf.join == null) {
0686: col.table = sf.moClass.mainTable;
0687: } else {
0688: col.table = sf.join.extTable;
0689: }
0690: logger.log(BasicLevel.DEBUG, "\t\tset column table: "
0691: + col.table.name);
0692: }
0693: if (col.name == null) {
0694: col.name = sf.name;
0695: logger.log(BasicLevel.DEBUG, "\t\tset column name: "
0696: + col.name);
0697: }
0698: }
0699:
0700: /**
0701: * Set the mapped-by visit for bidirectional relationship.
0702: * @param sf is a persistent field.
0703: */
0704: private void visitFieldMappedBy(SpeedoField sf)
0705: throws SpeedoException {
0706: if (sf.relationType == SpeedoField.NO_BI_RELATION) {
0707: logger.log(BasicLevel.DEBUG,
0708: "\t\t\tNo bidirectional relation.");
0709: } else {
0710: SpeedoField rf = sf.getReverseField();
0711: boolean sfHasMapping = sf.columns != null
0712: || (sf.join != null && !sf.join.columns.isEmpty());
0713: boolean rfHasMapping = rf.columns != null
0714: || (rf.join != null && !rf.join.columns.isEmpty());
0715: if (sfHasMapping) {
0716: if (rfHasMapping) {
0717: logger
0718: .log(BasicLevel.DEBUG,
0719: "\t\t\tOR Mapping already defined both side.");
0720: sf.mappedByReversefield = false;
0721: rf.mappedByReversefield = false;
0722: } else {
0723: logger.log(BasicLevel.DEBUG,
0724: "\t\t\tthe field contains an OR Mapping.");
0725: sf.mappedByReversefield = false;
0726: rf.mappedByReversefield = true;
0727: }
0728: } else {
0729: if (rfHasMapping) {
0730: logger
0731: .log(BasicLevel.DEBUG,
0732: "\t\t\tthe reverse field contains an OR Mapping.");
0733: sf.mappedByReversefield = true;
0734: rf.mappedByReversefield = false;
0735: } else {
0736: if (sf.relationType == SpeedoField.MANY_ONE_BI_RELATION) {
0737: //for MANY_ONE relations, when no mappedBy is specifed, it is
0738: // simpler that the collection reference is mapped by simple
0739: // reference
0740: rf.mappedByReversefield = true;
0741: sf.mappedByReversefield = false;
0742: logger
0743: .log(
0744: BasicLevel.DEBUG,
0745: "\t\t\tfield is a the MANY side of the relation, then it must contains the OR Mapping.");
0746: } else if (sf.relationType == SpeedoField.ONE_MANY_BI_RELATION) {
0747: rf.mappedByReversefield = false;
0748: sf.mappedByReversefield = true;
0749: logger
0750: .log(
0751: BasicLevel.DEBUG,
0752: "\t\t\tfield is a the ONE side of the relation, then it is mapped by the reverse field.");
0753: } else {
0754: sf.mappedByReversefield = !rf.mappedByReversefield;
0755: logger.log(BasicLevel.DEBUG, "\t\t\tfield is "
0756: + (sf.mappedByReversefield ? ""
0757: : " NOT")
0758: + " mapped by the reverse field.");
0759: }
0760: }
0761: }
0762: }
0763: }
0764:
0765: /**
0766: * Visit Class reference field. Manages the case of the foreign key is
0767: * managed by reverse field or not.
0768: */
0769: private void visitClassRefField(SpeedoField sf)
0770: throws SpeedoException {
0771: logger.log(BasicLevel.DEBUG,
0772: "\t\tvisit field class reference '" + sf.name + "'.");
0773: visitClassRefFieldExtension(sf);
0774: visitFieldMappedBy(sf);
0775: SpeedoClass rclass = sf.getReferencedClass();
0776: if (sf.mappedByReversefield) {
0777: SpeedoField rf = sf.getReverseField();
0778: logger.log(BasicLevel.DEBUG, "\t\tfield '" + sf.name
0779: + "' is mapped by reverse field: "
0780: + rf.getFQFieldName());
0781: ;
0782: visitSpeedoClass(rclass, new VisitRequired(rf));
0783: computeFieldFromReverse(sf);
0784: } else {
0785: SpeedoTable table;
0786: if (sf.join == null) {
0787: table = sf.moClass.mainTable;
0788: } else {
0789: table = sf.join.extTable;
0790: }
0791: if (sf.columns == null) {
0792: logger.log(BasicLevel.DEBUG, "\t\tfield '" + sf.name
0793: + "' requires pk fields of the class "
0794: + rclass.getFQName());
0795: visitSpeedoClass(rclass, VisitRequired.BASE);
0796: sf.columns = getFKColumn(rclass, sf.name + "_", table);
0797: } else {
0798: for (int i = 0; i < sf.columns.length; i++) {
0799: sf.columns[i].table = table;
0800: computeTargetColumn(sf, i, rclass);
0801: }
0802: }
0803: }
0804: }
0805:
0806: /**
0807: * Computes field mapping from its reverse field.
0808: */
0809: private void computeFieldFromReverse(SpeedoField sf)
0810: throws SpeedoException {
0811: SpeedoField rField = sf.getReverseField();
0812: sf.join = new SpeedoJoin();
0813: sf.join.mainTable = sf.moClass.mainTable;
0814: if (rField.join == null) {
0815: //compute the sf.column from the pk column of the referenced class
0816: sf.join.extTable = rField.moClass.mainTable;
0817: sf.columns = getFKColumn(rField.moClass, "",
0818: sf.join.extTable);
0819: } else {
0820: sf.join.extTable = rField.join.extTable;
0821: //compute the sf.column from the pk column of the referenced class
0822: sf.columns = new SpeedoColumn[rField.join.columns.size()];
0823: int i = 0;
0824: for (Iterator jcolIt = rField.join.columns.iterator(); jcolIt
0825: .hasNext();) {
0826: SpeedoJoinColumn jcol = (SpeedoJoinColumn) jcolIt
0827: .next();
0828: sf.columns[i] = (SpeedoColumn) jcol.column.clone();
0829: sf.columns[i].targetColumn = jcol.targetColumn;
0830: sf.columns[i].targetField = jcol.targetField;
0831: i++;
0832: }
0833: }
0834: //compute the join
0835: sf.join.columns.clear();
0836: for (int i = 0; i < rField.columns.length; i++) {
0837: sf.join.columns
0838: .add(new SpeedoJoinColumn(rField.columns[i]));
0839: }
0840: }
0841:
0842: /**
0843: * Gets a list of SpeedoJoinColumn joining the identifier column of
0844: * a referenced class
0845: * @param rclass is the referenced
0846: * @param colPrefix is prefix to the foreign key column name
0847: * @param table is the table of the foreign key column
0848: * @return list of SpeedoJoinColumn
0849: */
0850: private List getFKJoinColumn(SpeedoClass rclass, String colPrefix,
0851: SpeedoTable table) {
0852: SpeedoColumn[] fkCols = getFKColumn(rclass, colPrefix, table);
0853: ArrayList res = new ArrayList(fkCols.length);
0854: for (int i = 0; i < fkCols.length; i++) {
0855: res.add(new SpeedoJoinColumn(fkCols[i]));
0856: }
0857: return res;
0858: }
0859:
0860: /**
0861: * Gets a list of SpeedoColumn joining the identifier column of
0862: * a referenced class.
0863: * @param rclass is the referenced
0864: * @param colPrefix is prefix to the foreign key column name
0865: * @param table is the table of the foreign key column
0866: * @return
0867: */
0868: private SpeedoColumn[] getFKColumn(SpeedoClass rclass,
0869: String colPrefix, SpeedoTable table) {
0870: SpeedoColumn[] pkColumns = getIdColumns(rclass);
0871: SpeedoColumn[] columns = new SpeedoColumn[pkColumns.length];
0872: for (int i = 0; i < pkColumns.length; i++) {
0873: //create new SpeedoColumn instance targeting pk column
0874: // with the same description (sql type, length, scale, ...)
0875: columns[i] = new SpeedoColumn();
0876: columns[i].targetColumn = pkColumns[i].name;
0877: columns[i].name = colPrefix + columns[i].targetColumn;
0878: columns[i].table = table;
0879: columns[i].sqlType = pkColumns[i].sqlType;
0880: columns[i].scale = pkColumns[i].scale;
0881: columns[i].length = pkColumns[i].length;
0882: }
0883: return columns;
0884: }
0885:
0886: /**
0887: * Gets the identifier column(s) of a persistent class
0888: * @param sc is a persistent class
0889: * @return the identifier column(s) of a persistent class
0890: */
0891: private SpeedoColumn[] getIdColumns(SpeedoClass sc) {
0892: Collection pkFields = sc.getPKFields();
0893: SpeedoColumn[] columns;
0894: if (pkFields.isEmpty()) {
0895: //identifier is based on visible persistent field(s)
0896: columns = new SpeedoColumn[sc.identity.columns.length];
0897: for (int i = 0; i < sc.identity.columns.length; i++) {
0898: columns[i] = sc.identity.columns[i].column;
0899: }
0900: } else {
0901: //identifier is based on hidden field(s) (ex: data store id)
0902: int i = 0;
0903: columns = new SpeedoColumn[pkFields.size()];
0904: for (Iterator it = pkFields.iterator(); it.hasNext();) {
0905: SpeedoField pkField = (SpeedoField) it.next();
0906: columns[i] = pkField.columns[0];
0907: i++;
0908: }
0909: }
0910: return columns;
0911: }
0912:
0913: /**
0914: * Visit GenClassRef field (field referencing a collection or a map of
0915: * stuff).
0916: * @param sf is a persistent field referencing a collection, a map of stuff
0917: * (persistent objects or primitive elements)
0918: */
0919: private void visitGenClassRefField(SpeedoField sf)
0920: throws SpeedoException {
0921: logger.log(BasicLevel.DEBUG,
0922: "\t\tvisit field generic class reference '" + sf.name
0923: + "'.");
0924: visitGenClassRefFieldExtension(sf);
0925: visitFieldMappedBy(sf);
0926: if (sf.mappedByReversefield) {
0927: SpeedoField rf = sf.getReverseField();
0928: logger.log(BasicLevel.DEBUG, "\t\tfield '" + sf.name
0929: + "' is mapped by reverse field: "
0930: + rf.getFQFieldName());
0931: visitSpeedoClass(sf.getReferencedClass(),
0932: new VisitRequired(rf));
0933: computeFieldFromReverse(sf);
0934: // do not forget index in case of map indexed by field of the
0935: // referenced class
0936: visitGenClassIndex(sf);
0937: return;
0938: }
0939: boolean joinCreated = sf.join == null;
0940: if (joinCreated) {
0941: //create the join between mainTable and the genClass table
0942: sf.join = new SpeedoJoin();
0943: //sf.moClass.addJoin(sf.join);
0944: if (logger.isLoggable(BasicLevel.DEBUG)) {
0945: logger.log(BasicLevel.DEBUG, "\t\t\tCreate SpeedoJoin");
0946: }
0947: }
0948: if (sf.join.mainTable == null) {
0949: sf.join.mainTable = sf.moClass.mainTable;
0950: if (logger.isLoggable(BasicLevel.DEBUG)) {
0951: logger.log(BasicLevel.DEBUG,
0952: "\t\t\tDefine the main table on join: "
0953: + sf.join.mainTable.name);
0954: }
0955: }
0956: if (sf.join.extTable == null) {
0957: //compute the table of the genclass with regards to the relation type
0958: switch (sf.relationType) {
0959: case SpeedoField.MANY_MANY_BI_RELATION:
0960: SpeedoField rfield = sf.getReverseField();
0961: if (rfield.join != null && rfield.join.extTable != null) {
0962: sf.join.extTable = rfield.join.extTable;
0963: if (logger.isLoggable(BasicLevel.DEBUG)) {
0964: logger.log(BasicLevel.DEBUG,
0965: "\t\t\tUse table of reverse field: "
0966: + sf.join.extTable.name);
0967: }
0968: } else {
0969: sf.join.extTable = new SpeedoTable();
0970: sf.join.extTable.name = sf.moClass.name + "_"
0971: + sf.name;
0972: if (logger.isLoggable(BasicLevel.DEBUG)) {
0973: logger.log(BasicLevel.DEBUG,
0974: "\t\t\tDefine the join table of the relation: "
0975: + sf.join.extTable.name);
0976: }
0977: }
0978: break;
0979: case SpeedoField.ONE_MANY_BI_RELATION:
0980: rfield = sf.getReverseField();
0981: if (rfield.join != null && rfield.join.extTable != null) {
0982: sf.join.extTable = rfield.join.extTable;
0983: if (logger.isLoggable(BasicLevel.DEBUG)) {
0984: logger.log(BasicLevel.DEBUG,
0985: "\t\t\tUse table of reverse field: "
0986: + sf.join.extTable.name);
0987: }
0988: } else {
0989: sf.join.extTable = rfield.moClass.mainTable;
0990: if (logger.isLoggable(BasicLevel.DEBUG)) {
0991: logger.log(BasicLevel.DEBUG,
0992: "\tUse the table of the reference class: "
0993: + sf.join.extTable.name);
0994: }
0995: }
0996: break;
0997: default:
0998: sf.join.extTable = new SpeedoTable();
0999: sf.join.extTable.name = sf.moClass.name + "_" + sf.name;
1000: if (logger.isLoggable(BasicLevel.DEBUG)) {
1001: logger.log(BasicLevel.DEBUG,
1002: "\t\t\tDefine the GC table: "
1003: + sf.join.extTable.name);
1004: }
1005: break;
1006: }
1007: sf.join.extTable.join = sf.join;
1008: }
1009: if (sf.join.columns.isEmpty()) {
1010: //Compute the join between the table of the owner class and the
1011: // table of the genclass
1012: sf.join.columns.addAll(getFKJoinColumn(sf.moClass, "",
1013: sf.join.extTable));
1014: if (logger.isLoggable(BasicLevel.DEBUG)) {
1015: logger.log(BasicLevel.DEBUG,
1016: "\t\t\tDefine the join column: \n"
1017: + sf.join.columns);
1018: }
1019: } else {
1020: for (Iterator it = sf.join.columns.iterator(); it.hasNext();) {
1021: SpeedoJoinColumn jc = (SpeedoJoinColumn) it.next();
1022: if (jc.targetColumn == null) {
1023: computeTargetJoinColumn(sf, jc);
1024: }
1025: }
1026: }
1027: if (joinCreated && logger.isLoggable(BasicLevel.DEBUG)) {
1028: logger.log(BasicLevel.DEBUG, "\t\t\tCreated "
1029: + sf.join.toString());
1030: }
1031: //map the index of the genclass
1032: visitGenClassIndex(sf);
1033: //map the element of the genclass
1034: visitGenClassElement(sf);
1035: }
1036:
1037: /**
1038: * Visit genclass index. Indexes can be find for List or Map implementation.
1039: * @param sf a persistent field.
1040: */
1041: private void visitGenClassIndex(SpeedoField sf)
1042: throws SpeedoException {
1043: if (sf.jdoTuple instanceof SpeedoCollection) {
1044: SpeedoCollection collec = (SpeedoCollection) sf.jdoTuple;
1045: if (collec.indexColumns == null
1046: && List.class.isAssignableFrom(getGCClass(sf))) {
1047: collec.indexColumns = new SpeedoColumn("idx",
1048: sf.join.extTable);
1049: if (logger.isLoggable(BasicLevel.DEBUG)) {
1050: logger.log(BasicLevel.DEBUG,
1051: "\t\t\tCreate column for the list index"
1052: + collec.indexColumns.toString());
1053: }
1054: }
1055: } else if (sf.jdoTuple instanceof SpeedoMap) {
1056: SpeedoMap map = (SpeedoMap) sf.jdoTuple;
1057: if (map.keyColumns == null) {
1058: map.keyColumns = new SpeedoColumn("idx",
1059: sf.join.extTable);
1060: if (logger.isLoggable(BasicLevel.DEBUG)) {
1061: logger.log(BasicLevel.DEBUG,
1062: "\t\t\tCreate column for the map key"
1063: + map.keyColumns.toString());
1064: }
1065: }
1066: }
1067: }
1068:
1069: /**
1070: * Visit gen class element. The element can be a primitive element
1071: * or a reference to a persistent class.
1072: * @param sf is the SpeedoField meta object representing the genclass
1073: */
1074: private void visitGenClassElement(SpeedoField sf)
1075: throws SpeedoException {
1076: SpeedoClass rclass = sf.getReferencedClass();
1077: if (sf.columns == null) {
1078: if (rclass == null) {
1079: //primitive element
1080: sf.addColumn(new SpeedoColumn("element",
1081: sf.join.extTable));
1082: } else {
1083: logger.log(BasicLevel.DEBUG, "\t\tfield '" + sf.name
1084: + "' requires pk fields of the class "
1085: + rclass.getFQName());
1086: visitSpeedoClass(rclass, VisitRequired.BASE);
1087: //persistent class ==> classRef
1088: String prefix = "";
1089: if (sf.join.extTable != rclass.mainTable) {
1090: prefix = "elem_";
1091: }
1092: sf.columns = getFKColumn(rclass, prefix,
1093: sf.join.extTable);
1094: }
1095: if (logger.isLoggable(BasicLevel.DEBUG)) {
1096: StringBuffer sb = new StringBuffer();
1097: sb.append("\t\t\tCreate ").append(sf.columns.length);
1098: sb.append(" column(s) for the gen class element :\n");
1099: for (int i = 0; i < sf.columns.length; i++) {
1100: sb.append("\t\t-").append(sf.columns[i]).append(
1101: "\n");
1102: }
1103: logger.log(BasicLevel.DEBUG, sb.toString());
1104: }
1105: } else {
1106: if (rclass == null) {
1107: //primitive element
1108: if (sf.columns[0].name == null) {
1109: sf.columns[0].name = "element";
1110: }
1111: if (sf.columns[0].table == null) {
1112: sf.columns[0].table = sf.join.extTable;
1113: }
1114: } else {
1115: //persistent class ==> classRef
1116: SpeedoColumn[] cols = getFKColumn(rclass, "elem_",
1117: sf.join.extTable);
1118: for (int i = 0; i < sf.columns.length; i++) {
1119: if (sf.columns[i].name == null) {
1120: sf.columns[i].name = cols[i].name;
1121: }
1122: if (sf.columns[i].targetColumn == null) {
1123: computeTargetColumn(sf, i, rclass);
1124: }
1125: if (sf.columns[i].table == null) {
1126: sf.columns[i].table = sf.join.extTable;
1127: }
1128: }
1129: }
1130: }
1131: }
1132:
1133: /**
1134: * Computes the targetColumn field of the sf.column[colIdx] if it is
1135: * required.
1136: * @param sf is the SpeedoCommonField holding the column definition
1137: * @param colIdx is the index of the column among sf.columns array
1138: * @param rclass is the referenced persistent class. The target column are
1139: * the identifier column of the referenced class.
1140: * @throws SpeedoException
1141: */
1142: private void computeTargetColumn(SpeedoCommonField sf, int colIdx,
1143: SpeedoClass rclass) throws SpeedoException {
1144: if (sf.columns[colIdx].targetColumn != null) {
1145: return;
1146: }
1147: if (sf.columns[colIdx].targetField == null) {
1148: //both are null
1149: if (sf.columns.length == 1) {
1150: //compute the target column from the unique pk field
1151: try {
1152: SpeedoField pkField = rclass.getUniquePKField();
1153: sf.columns[colIdx].targetColumn = pkField.columns[0].name;
1154: } catch (SpeedoException e) {
1155: throw new SpeedoException(
1156: "Bad number of column specified for the "
1157: + sf.getSourceDesc(), e);
1158: }
1159: } else {
1160: SpeedoException se = new SpeedoException(
1161: "Target column is required for the reference "
1162: + sf.getSourceDesc());
1163: logger.log(BasicLevel.ERROR, se.getMessage(), se);
1164: throw se;
1165: }
1166: } else {
1167: //compute the target column from the target field
1168: SpeedoField pkField = rclass
1169: .getField(sf.columns[colIdx].targetField);
1170: sf.columns[colIdx].targetColumn = pkField.columns[0].name;
1171: }
1172: }
1173:
1174: private void computeTargetJoinColumn(SpeedoCommonField sf,
1175: SpeedoJoinColumn jc) throws SpeedoException {
1176: int nbcol = sf.join.columns.size();
1177: if (jc.targetField == null) {
1178: //both are null
1179: if (nbcol == 1) {
1180: //compute the target column from the unique pk field
1181: SpeedoField pkField = sf.moClass.getUniquePKField();
1182: jc.targetColumn = pkField.columns[0].name;
1183: } else {
1184: SpeedoException se = new SpeedoException(
1185: "Target column is required for join column of the reference "
1186: + sf.getSourceDesc());
1187: logger.log(BasicLevel.ERROR, se.getMessage(), se);
1188: throw se;
1189: }
1190: } else {
1191: //compute the target column from the target field
1192: SpeedoField pkField = sf.moClass.getField(jc.targetField);
1193: jc.targetColumn = pkField.columns[0].name;
1194: }
1195: }
1196:
1197: /**
1198: * Get the type of the persistent field.
1199: * @return the java.lang.Class representing the type of the field.
1200: */
1201: private Class getGCClass(SpeedoField sf) throws SpeedoException {
1202: try {
1203: return Class.forName(sf.type());
1204: } catch (ClassNotFoundException e) {
1205: throw new SpeedoException("Class loading problem: ", e);
1206: }
1207: }
1208:
1209: /**
1210: * Creates a new table name for a SpeedoTable
1211: * @param sc is the SpeedoClass that has the table
1212: * @param t is the table without name
1213: */
1214: private void allocateTableName(SpeedoClass sc, SpeedoTable t) {
1215: if (t.name == null) {
1216: if (sc.mainTable == t) {
1217: t.name = sc.name.toUpperCase();
1218: } else {
1219: for (int i = 0; i < sc.joinToExtTables.length; i++) {
1220: if (sc.joinToExtTables[i].extTable == t) {
1221: t.name = sc.name.toUpperCase() + "_EXT_" + i;
1222: return;
1223: }
1224: }
1225: t.name = sc.name.toUpperCase();
1226: }
1227: }
1228: }
1229:
1230: /**
1231: * Builds, if required, the mapping of an inherited field.
1232: * @param sif
1233: */
1234: private void mapHorizontalInheritedField(SpeedoField sf,
1235: SpeedoClass sc) throws SpeedoException {
1236: SpeedoInheritedField sif = (SpeedoInheritedField) sc.inheritance.remappedInheritedFields
1237: .get(sf.getFQFieldName());
1238: if (sif == null) {
1239: sif = sc.inheritance.newSpeedoInheritedField(sf);
1240: }
1241: if (sf.jdoTuple == null) {
1242: SpeedoClass rclass = sf.getReferencedClass();
1243: //primitive field or simple reference to a persistent class
1244: if (rclass != null
1245: && sf.join != null
1246: && sf.relationType == SpeedoField.ONE_ONE_BI_RELATION
1247: && sf.mappedByReversefield) {
1248: // the classref belongs a bidirectionnal relationship
1249: // ONE-ONE. In addition the foreign key is hold by the table
1250: // of the referenced class
1251: setJoinNColsFromParent(sif);
1252: } else {
1253: if (sif.columns == null) {
1254: // ClassRef or primtive element to map localy
1255: for (int i = 0; i < sf.columns.length; i++) {
1256: //Same column definition but in the table of the class
1257: SpeedoColumn col = (SpeedoColumn) sf.columns[i]
1258: .clone();
1259: col.table = sif.moClass.mainTable;
1260: sif.addColumn(col);
1261: }
1262: } else {
1263: if (rclass != null) {
1264: for (int i = 0; i < sif.columns.length; i++) {
1265: computeTargetColumn(sif, i, rclass);
1266: }
1267: }
1268: }
1269: }
1270: } else {
1271: //reference to a generic class (Collection, Set, Map, ..)
1272: setJoinNColsFromParent(sif);
1273: //TODO: support index/key
1274: }
1275: }
1276:
1277: private void setJoinNColsFromParent(SpeedoInheritedField sif) {
1278: SpeedoField sf = sif.inheritedField;
1279: if (sif.columns == null) {
1280: //Use the same column for the genclass value
1281: sif.columns = sf.columns;
1282: }
1283: if (sif.join == null) {
1284: sif.join = new SpeedoJoin();
1285: sif.join.extTable = sf.join.extTable;
1286: sif.join.mainTable = sf.moClass.mainTable;
1287: // Use the same join column name but with the targeted column
1288: // of the current table. We suppose than the id has the same
1289: // structure
1290: sif.join.columns = getFKJoinColumn(sf.moClass, "",
1291: sif.join.extTable);
1292: if (sif.join.columns.size() == 1) {
1293: ((SpeedoJoinColumn) sif.join.columns.get(0)).column.name = ((SpeedoJoinColumn) sf.join.columns
1294: .get(0)).column.name;
1295: } else {
1296: for (Iterator it = sf.moClass.getPKFields().iterator(); it
1297: .hasNext();) {
1298: SpeedoField pkField = (SpeedoField) it.next();
1299: SpeedoJoinColumn parentjc = sf
1300: .getFKJoinColumn(pkField.columns[0].name);
1301: SpeedoJoinColumn newjc = sif
1302: .getFKJoinColumn(pkField.columns[0].name);
1303: newjc.column.name = parentjc.column.name;
1304: }
1305: }
1306: }
1307:
1308: }
1309:
1310: /**
1311: * Get the root table of a persistent class. If the class has not
1312: * inheritance the root table is the maintable. If the inheritance mapping
1313: * is vertical, the root table is the one of the parent.
1314: */
1315: private SpeedoTable getRootTable(SpeedoClass sc) {
1316: if (sc.inheritance != null
1317: && sc.inheritance.isVerticalMapping()) {
1318: return sc.inheritance.join.mainTable;
1319: } else {
1320: return sc.mainTable;
1321: }
1322: }
1323:
1324: /**
1325: * Visit extensions of field referencing a persistent class. This method
1326: * converts old Extensions 'source-foreign-keys' and 'target-foreign-keys'
1327: * to mapping definition in the Speedo meta information.
1328: * @param sf a persistent field referencing a persistent class.
1329: * @see SpeedoProperties#SOURCE_FK
1330: * @see SpeedoProperties#TARGET_FK
1331: */
1332: public void visitClassRefFieldExtension(SpeedoField sf)
1333: throws SpeedoException {
1334: SpeedoClass rclass = sf.getReferencedClass();
1335: String sfk = sf
1336: .getExtensionValueByKey(SpeedoProperties.SOURCE_FK);
1337: String tfk = sf
1338: .getExtensionValueByKey(SpeedoProperties.TARGET_FK);
1339: if (sfk == null && tfk == null) {
1340: return;
1341: }
1342: if (sfk != null
1343: && sf.relationType == SpeedoField.ONE_ONE_BI_RELATION) {
1344: //The relation is mapped by the field having the foreign key
1345: return;
1346: }
1347: if (tfk != null) {
1348: SpeedoColumn[] cols = getFKColumn(tfk,
1349: sf.moClass.mainTable, sf.getReferencedClass());
1350: if (sf.columns != null && cols.length == sf.columns.length) {
1351: for (int i = 0; i < cols.length; i++) {
1352: sf.columns[i].merge(cols[i]);
1353: }
1354: } else {
1355: sf.columns = cols;
1356: }
1357: } else if (sfk != null) { //backward reference
1358: sf.join = new SpeedoJoin();
1359: sf.join.mainTable = sf.moClass.mainTable;
1360: sf.join.extTable = rclass.mainTable;
1361: //compute the sf.column from the pk column of the referenced class
1362: Collection pkFields = rclass.getPKFields();
1363: if (pkFields.isEmpty()) {
1364: sf.columns = new SpeedoColumn[rclass.identity.columns.length];
1365: for (int i = 0; i < rclass.identity.columns.length; i++) {
1366: sf.columns[i] = rclass.identity.columns[i].column;
1367: }
1368: } else {
1369: int i = 0;
1370: sf.columns = new SpeedoColumn[pkFields.size()];
1371: for (Iterator it = pkFields.iterator(); it.hasNext();) {
1372: SpeedoField pkField = (SpeedoField) it.next();
1373: sf.columns[i] = pkField.columns[0];
1374: i++;
1375: }
1376: }
1377: //compute the join columns
1378: sf.join.columns.addAll(getFKJoinColumn(sfk,
1379: rclass.mainTable, sf.moClass));
1380: }
1381: logger.log(BasicLevel.DEBUG, "Field '" + sf.name
1382: + "' has deprecated extension(s): " + "\n\t-("
1383: + SpeedoProperties.SOURCE_FK + "=" + sfk + "\n\t-"
1384: + SpeedoProperties.TARGET_FK + "=" + tfk
1385: + "\nExtensions have been converted:" + "\n\t- column:"
1386: + sf.printColumns() + "\n\t- join:" + sf.join);
1387: }
1388:
1389: /**
1390: * Visit extensions of field referencing a genclass. This method
1391: * converts old Extensions 'source-foreign-keys', 'target-foreign-keys'
1392: * and 'join-table' to mapping definition in the Speedo meta information.
1393: * @param sf a persistent field referencing a genclass.
1394: * @see SpeedoProperties#SOURCE_FK
1395: * @see SpeedoProperties#TARGET_FK
1396: * @see SpeedoProperties#JOIN_TABLE
1397: */
1398: public void visitGenClassRefFieldExtension(SpeedoField sf)
1399: throws SpeedoException {
1400: String sfk = sf
1401: .getExtensionValueByKey(SpeedoProperties.SOURCE_FK);
1402: String tfk = sf
1403: .getExtensionValueByKey(SpeedoProperties.TARGET_FK);
1404: String jt = sf
1405: .getExtensionValueByKey(SpeedoProperties.JOIN_TABLE);
1406: if (sfk == null && tfk == null && jt == null) {
1407: return;
1408: }
1409: if (sfk != null && tfk == null && jt == null
1410: && sf.relationType == SpeedoField.ONE_MANY_BI_RELATION) {
1411: //The relation is mapped by the field having the foreign key
1412: return;
1413: }
1414: sf.join = new SpeedoJoin();
1415: sf.join.mainTable = sf.moClass.mainTable;
1416: //sf.moClass.addJoin(sf.join);
1417: if (jt != null) {
1418: sf.join.extTable = new SpeedoTable();
1419: sf.join.extTable.name = jt;
1420: }
1421: if (tfk != null
1422: && (sf.columns == null || sf.columns[0].name == null)) {
1423: //compute the value column(s)
1424: sf.columns = getFKColumn(tfk, sf.join.extTable, sf
1425: .getReferencedClass());
1426: }
1427: if (sfk != null && sf.join.columns.isEmpty()) {
1428: //compute the join column(s)
1429: sf.join.columns.addAll(getFKJoinColumn(sfk,
1430: sf.join.extTable, sf.moClass));
1431: }
1432: logger.log(BasicLevel.DEBUG, "Field '" + sf.name
1433: + "' has deprecated extension(s): " + "\n\t-("
1434: + SpeedoProperties.SOURCE_FK + "=" + sfk + "\n\t-"
1435: + SpeedoProperties.TARGET_FK + "=" + tfk + "\n\t"
1436: + SpeedoProperties.JOIN_TABLE + "=" + jt
1437: + "\nExtensions have been converted:" + "\n\t- column:"
1438: + sf.printColumns() + "\n\t- join:" + sf.join);
1439: }
1440:
1441: /**
1442: * Gets array of new SpeedoColumn representing a join to a peristent class.
1443: * @param pk2tfk is the map defining the name of the foreign key column from
1444: * the primary key column (key=String pkColName/ value=String fkColName)
1445: * @param table is the table hosting the foreign key column
1446: * @param rclass is the referenced class having pk column
1447: */
1448: private SpeedoColumn[] getFKColumn(String pk2tfk,
1449: SpeedoTable table, SpeedoClass rclass) {
1450: Map map = getPk2Fk(pk2tfk);
1451: SpeedoColumn[] columns = new SpeedoColumn[map.size()];
1452: int i = 0;
1453: for (Iterator it = map.entrySet().iterator(); it.hasNext();) {
1454: Map.Entry me = (Map.Entry) it.next();
1455: columns[i] = new SpeedoColumn();
1456: columns[i].name = (String) me.getValue();
1457: columns[i].table = table;
1458: columns[i].targetColumn = (String) me.getKey();
1459: if (rclass != null) {
1460: SpeedoColumn pkcol = rclass.getColumn(
1461: columns[i].targetColumn, true);
1462: if (pkcol != null) {
1463: columns[i].sqlType = pkcol.sqlType;
1464: columns[i].scale = pkcol.scale;
1465: columns[i].length = pkcol.length;
1466: }
1467: }
1468: i++;
1469: }
1470: return columns;
1471: }
1472:
1473: /**
1474: * Gets List of new SpeedoJoinColumn representing a join to a peristent class.
1475: * @param pk2tfk is the map defining the name of the foreign key column from
1476: * the primary key column (key=String pkColName/ value=String fkColName)
1477: * @param table is the table hosting the foreign key column
1478: * @param rclass is the referenced class having pk column
1479: */
1480: private List getFKJoinColumn(String pk2sfk, SpeedoTable table,
1481: SpeedoClass rclass) {
1482: SpeedoColumn[] cols = getFKColumn(pk2sfk, table, rclass);
1483: ArrayList res = new ArrayList(cols.length);
1484: for (int i = 0; i < cols.length; i++) {
1485: res.add(new SpeedoJoinColumn(cols[i]));
1486: }
1487: return res;
1488: }
1489:
1490: /**
1491: * Converts the value of the extension 'source-foreign-keys' and
1492: * 'target-foreign-keys' to a map
1493: * @param fks
1494: * @return a map (key=String pkColName / Value=String fkColName)
1495: */
1496: private Map getPk2Fk(String fks) {
1497: Map res = new HashMap();
1498: StringTokenizer st = new StringTokenizer(fks, "=,:;/", false);
1499: String pk = null;
1500: while (st.hasMoreTokens()) {
1501: String tok = st.nextToken();
1502: tok = tok.trim();
1503: if (pk == null) {
1504: pk = tok;
1505: } else {
1506: res.put(pk, tok);
1507: pk = null;
1508: }
1509: }
1510: return res;
1511: }
1512: }
|