Package org.objectweb.speedo.generation.mivisitor

Source Code of org.objectweb.speedo.generation.mivisitor.ORMappingGenerator

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

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;


import org.objectweb.speedo.api.SpeedoException;
import org.objectweb.speedo.api.SpeedoProperties;
import org.objectweb.speedo.generation.lib.AbstractGeneratorComponent;
import org.objectweb.speedo.lib.Personality;
import org.objectweb.speedo.metadata.SpeedoClass;
import org.objectweb.speedo.metadata.SpeedoCollection;
import org.objectweb.speedo.metadata.SpeedoColumn;
import org.objectweb.speedo.metadata.SpeedoCommonField;
import org.objectweb.speedo.metadata.SpeedoDiscriminator;
import org.objectweb.speedo.metadata.SpeedoField;
import org.objectweb.speedo.metadata.SpeedoInheritance;
import org.objectweb.speedo.metadata.SpeedoInheritedField;
import org.objectweb.speedo.metadata.SpeedoJoin;
import org.objectweb.speedo.metadata.SpeedoJoinColumn;
import org.objectweb.speedo.metadata.SpeedoMap;
import org.objectweb.speedo.metadata.SpeedoPackage;
import org.objectweb.speedo.metadata.SpeedoTable;
import org.objectweb.speedo.metadata.SpeedoXMLDescriptor;
import org.objectweb.speedo.naming.api.NamingManager;
import org.objectweb.util.monolog.api.BasicLevel;

/**
* This Speedo Meta information visitor builds and fills the Speedo Meta
* information concerning the OR mapping information. It defines a SpeedoTable,
* SpeedoColumn, SpeedoJoin where it is required.
* This SMI visitor cannot be inclued with other SMI visitor. Indeed it requires
* that all meta information is complete (for all classes). In particular it
* needs the result of the Class analyser visitor. For this reason this SMI
* visitor is a simple GeneratorComponent to use when SMI is complete.
* In addition, due to dependency between classes (relation ship, inheritance)
* the visit of classes must be done in a particular order. For these reasons,
* this Visitor visits the SMI with a help of two internal classes (
* VisitRequired and VisitRemeber). 
*
* @author S.Chassande-Barrioz
*/
public class ORMappingGenerator extends AbstractGeneratorComponent {
    /**
     * This class represents required element to be visited in a persistent
     * class.
     *
     * @author S.Chassande-Barrioz
     */
    public static class VisitRequired {
        /**
         * constant representing a visit of nothing
         */
        public final static VisitRequired NOTHING = new VisitRequired(false, false, null);
        /**
         * constant representing a visit of all elements of a persistent class
         */
      public final static VisitRequired ALL = new VisitRequired(true, true, null);
        /**
         * constant representing a visit of base element of a persistent class
         */
      public final static VisitRequired BASE = new VisitRequired(true, false, null);

        /**
         * indicates if the base must be visited. The base represents:
         * - the table of the persistent class
         * - the identifier of the persistent class
         * - the primitivie fields of the persistent class
         */
        public boolean base = false;
        /**
         * Indicates if all references must be visisted (ClassRef
         * and GenClassRef)
         */
        public boolean references = false;
        /**
         * indicates if the visit of a particular reference field is required.
         * It is often used in case of bi directional relationship with one side
         * of the relation mmped by the other one.
         */
        public SpeedoField refField = null;
       
        /**
         * Builds a new VisitRequired instance including the base elements and
         * a particular reference field.
         * @param refField the field to visit
         */
        public VisitRequired(SpeedoField refField) {
            this.base = true;
            this.references = false;
            this.refField = refField;
        }
        /**
         * Private constructor for static constant only.
         */
        private VisitRequired(boolean base, boolean references, SpeedoField refField) {
            this.base = base;
            this.references = references;
            this.refField = refField;
        }
        /**
         * Prints a string representing the current instance.
         */
        public String toString() {
            if (base) {
                if (references) {
                    return "ALL";
                } else if (refField == null) {
                    return "BASE";
                } else {
                    return "BASE, " + refField.name;
                }
            } else {
                return "NOTHING";
            }
        }
    }   

    /**
     * This class represents the status of visited/treated elements of a
     * persistent class.
     *
     * @author S.Chassande-Barrioz
     */
    public static class VisitRemember {
        /**
         * The persistent class
         */
        private SpeedoClass sc;
        /**
         * indicates if the base elements have been visited:
         * - the table of the persistent class
         * - the identifier of the persistent class
         * - the primitivie fields of the persistent class
         */
        private boolean baseVisited = false;
        /**
         * Indicates if inherited fields have been visited.
         */
        private boolean inheritedFieldsVisited = false;
        /**
         * the list primitive fields of the persistent class
         * The content of the list is SpeedoField instances.
         */
        public List primitiveFields = new ArrayList();
        /**
         * the list reference fields of the persistent class (class reference
         * and generic class reference)
         * The content of the list is SpeedoField instances.
         */
        public List references = new ArrayList();
        /**
         * The list of reference fields already visisted.
         */
        private List visitedReferencesFields = new ArrayList();

        /**
         * Builds a new instance for a persistent class. This constructor fills
         * the list of persistent fields.
         */
        public VisitRemember(SpeedoClass c) {
            sc = c;
            for (Iterator fieldIt = sc.fields.values().iterator(); fieldIt.hasNext();) {
                SpeedoField sf = (SpeedoField) fieldIt.next();
                if (sf.jdoTuple != null) {
                    references.add(sf);
                } else {
                    SpeedoClass rclass = sf.getReferencedClass();
                    if (rclass == null) {
                        primitiveFields.add(sf);
                    } else {
                        references.add(sf);
                    }
                }
            }
        }
        /**
         * Print the status of the class visit
         */
        public String toString() {
            if (baseVisited) {
                int nbref = references.size();
                int visitedref = visitedReferencesFields.size();
                if (nbref == visitedref) {
                    return "ALL";
                } else if (visitedref == 0) {
                    return "BASE";
                } else {
                    StringBuffer sb = new StringBuffer();
                    sb.append("BASE");
                    for (Iterator it = visitedReferencesFields.iterator(); it.hasNext();) {
                        SpeedoField sf = (SpeedoField) it.next();
                        sb.append(", ").append(sf.name);
                    }
                    return sb.toString();
                }
            } else {
                return "NOTHING";
            }
        }
        /**
         * Indicates if some elements specified by the VisitRequired parameter
         * have not been already visited.
         */
        public boolean hasUnvisitedPart(VisitRequired req) {
            if (req.base && !baseVisited) {
                return true;
            }
            if (req.references
                    && references.size() > visitedReferencesFields.size()) {
                return true;
            }
            if (req.refField != null
                    && !visitedReferencesFields.contains(req.refField)) {
                return true;
            }
            if (req.references && !inheritedFieldsVisited) {
                return true;
            }
            return false;
        }
        /**
         * Indicates if the base must be visited according to the parameter
         * and the current status.
         */
        public boolean visitBase(VisitRequired vr) {
            return vr.base && !baseVisited;
        }
        /**
         * Callback method to indicate that the base elements have been visited.
         */
        public void baseVisited() {
            baseVisited = true;
        }
        /**
         * Indicates if one or several reference fields must be visited
         * according to the parameter and the current status.
         */
        public boolean visitReferences(VisitRequired vr) {
            return (vr.references || vr.refField != null)
              && references.size() > visitedReferencesFields.size();
        }
        /**
         * Callback method to indicate that a reference field has been visited.
         */
        public boolean visitReferenceField(SpeedoField sf, VisitRequired vr) {
            return (vr.references || vr.refField == sf)
               && !visitedReferencesFields.contains(sf);
        }
        /**
         * Callback method to indicate that a reference field has been visited.
         */
        public void referenceFieldVisisted(SpeedoField sf) {
            visitedReferencesFields.add(sf);
        }
        /**
         * Indicates if inherited fields must be visited
         * according to the parameter and the current status.
         */
        public boolean visitInheritedFields(VisitRequired vr) {
            return vr.references && !inheritedFieldsVisited;
        }
        /**
         * Callback method to indicate that inherited fields have been visited.
         */
        public void inheritedFieldsVisited() {
            inheritedFieldsVisited = true;
        }
    }

    /**
     * is the map of visited classes (partialy of totaly)
     * key = fully qualified persistent class name
     * value = VisitRemener instance
     */
    private HashMap visitedClasses = new HashMap();
    public final static String LOGGER_NAME =
        SpeedoProperties.LOGGER_NAME + ".generation.orm";
   
    public ORMappingGenerator(Personality p) {
      super(p);
    }
   
    public String getTitle() {
        return "Auto O/R Mapping...";
    }
    protected String getLoggerName() {
        return LOGGER_NAME;
    }

    public boolean init() throws SpeedoException {
        visitedClasses.clear();
    logger = scp.loggerFactory.getLogger(getLoggerName());
    debug = logger != null && logger.isLoggable(BasicLevel.DEBUG);
    return !scp.getXmldescriptor().isEmpty();
    }
    public void process() throws SpeedoException {
        visitedClasses.clear();
        for (Iterator xmlIt = scp.smi.xmlDescriptors.values().iterator(); xmlIt.hasNext();) {
            SpeedoXMLDescriptor xml = (SpeedoXMLDescriptor) xmlIt.next();
            for (Iterator packIt = xml.packages.values().iterator(); packIt.hasNext();) {
                SpeedoPackage sp = (SpeedoPackage) packIt.next();
                for (Iterator classIt = sp.classes.values().iterator(); classIt.hasNext();) {
                    SpeedoClass sc = (SpeedoClass) classIt.next();
                    visitSpeedoClass(sc, VisitRequired.ALL);
                }
            }
        }
        visitedClasses.clear();
    }
    /**
     * Visits a SpeedoClass.
     * @param sc the class to visit
     * @param toVisit
     * @throws SpeedoException
     */
    private void visitSpeedoClass(SpeedoClass sc, VisitRequired toVisit) throws SpeedoException {
        logger.log(BasicLevel.DEBUG, "* visit class '" + sc.getFQName() + "', " + toVisit + ".");
        VisitRemember vr = (VisitRemember) visitedClasses.get(sc);
        if (vr == null) {
            vr = new VisitRemember(sc);
            visitedClasses.put(sc, vr);
        } else if (!vr.hasUnvisitedPart(toVisit)){
            logger.log(BasicLevel.DEBUG, "\t=> already visited: " + vr + ".");
            return;
        }
        if (vr.visitBase(toVisit)) {
            logger.log(BasicLevel.DEBUG, "\tvisit base.");
            //visit parents first
          visitClassParent(sc, toVisit);
          //visit inheritance strategy
          visitClassInheritanceStrategy(sc);
          //visit table of the persistent class
          visitClassTable(sc);
          //visit identity column of the persistent class
          visitIdentityColumn(sc);
            //visit secondary tables (not the join associated)
            if (sc.joinToExtTables != null) {
                for (int i = 0; i < sc.joinToExtTables.length; i++) {
                    SpeedoJoin join = sc.joinToExtTables[i];
                    if (join.mainTable == null) {
                        join.mainTable = sc.mainTable;
                    }
                    if (join.extTable == null) {
                        join.extTable = new SpeedoTable();
                        allocateTableName(sc, join.extTable);
                    }
                }
            }
          //visit primitive Fields first because it can contain the pk fields used
          // for classRef or genClassRef
          //visit primitive fields (including pk fields)
          for (Iterator fieldIt = vr.primitiveFields.iterator(); fieldIt.hasNext();) {
              SpeedoField sf = (SpeedoField) fieldIt.next();
                visitPrimitiveField(sf);
          }
            if (sc.joinToExtTables != null) {
                for (int i = 0; i < sc.joinToExtTables.length; i++) {
                    visitJoinOfSecondaryTable(sc.joinToExtTables[i], sc);
                }
            }
          vr.baseVisited();
        }
        if (vr.visitReferences(toVisit)) {
            logger.log(BasicLevel.DEBUG, "\tvisit reference fields:");
         
          //visit references fields
          for (Iterator fieldIt = vr.references.iterator(); fieldIt.hasNext();) {
              SpeedoField sf = (SpeedoField) fieldIt.next();
              if (vr.visitReferenceField(sf, toVisit)) {
                  if (sf.jdoTuple == null) {
                      visitClassRefField(sf);
                  } else {
                    visitGenClassRefField(sf);
                  }
                vr.referenceFieldVisisted(sf);
              }
          }
        }
        if (vr.visitInheritedFields(toVisit)) {
            visitClassInheritance(sc);
            vr.inheritedFieldsVisited();
        }
        logger.log(BasicLevel.DEBUG, "End of visit class '"
                + sc.getFQName() + "', " + toVisit + ".");
    }
    private void visitJoinOfSecondaryTable(SpeedoJoin join, SpeedoClass sc) throws SpeedoException {
        logger.log(BasicLevel.DEBUG, "\t\tvisit join to the secondary table '" + join.extTable.name + "'.");
        if (join.columns.isEmpty()) {
            //create new JoinColumn to the pk column of the class
            join.columns.addAll(getFKJoinColumn(sc, "FK_", join.extTable));
        } else {
            int size = join.columns.size();
            for (Iterator it = join.columns.iterator(); it.hasNext();) {
                SpeedoJoinColumn jc = (SpeedoJoinColumn) it.next();
                if (jc.targetColumn == null) {
                    if (jc.targetField == null) {
                        if (size == 1) {
                            SpeedoColumn[] cols = getIdColumns(sc);
                            if (cols.length > 1) {
                                throw new SpeedoException("The join to the '"
                                        + join.extTable.name
                                        + "' secondary table has not targetColumn defined and there is several identifier column in the "
                                        + sc.getSourceDesc());
                            }
                            jc.targetColumn = cols[0].name;
                            logger.log(BasicLevel.DEBUG, "\t\tset the target column from the unique PK column: " + jc.targetColumn);
                        } else {
                            throw new SpeedoException("The join columns to the '"
                                    + join.extTable.name
                                    + "' secondary table have not targetColumn defined in the "
                                    + sc.getSourceDesc());
                        }
                    } else {
                        //find the column of the target field
                        SpeedoField sf = sc.getField(jc.targetField);
                        if (sf == null) {
                            throw new SpeedoException("Targeted field '" + jc.targetField + "' in the " + sc.getSourceDesc() + ". It is defined in the join to the secondary table '" + join.extTable.name +"'.");
                        }
                        if (sf.columns == null || sf.columns.length != 1) {
                            throw new SpeedoException("In join column, target field must be a primitive field: " + sf.getSourceDesc() + ". It is defined in the join to the secondary table '" + join.extTable.name +"'.");
                        }
                        jc.targetColumn = sf.columns[0].name;
                    }
                }
            }
        }
    }
   
    /**
     * Visits parent persistent class starting with the root class.
     * @param sc the Speedoclass to visit.
     * @param toVisit
     * @throws SpeedoException
     */
    private void visitClassParent(SpeedoClass sc, VisitRequired toVisit) throws SpeedoException {

        //find all not visited parents
        List parents = new ArrayList();
        SpeedoClass parent = sc.getSuper();
        while (parent != null) {
            VisitRemember vr = (VisitRemember) visitedClasses.get(parent);
            if (vr == null || vr.hasUnvisitedPart(toVisit)) {
                //Add in first
                parents.add(0, parent);
                parent = parent.getSuper();
            } else {
                //if a parent is visited all super parents are visited too
                parent = null;
            }
        }
        //visit parents
        for (Iterator it = parents.iterator(); it.hasNext();) {
            logger.log(BasicLevel.DEBUG, "\tvisit parent of " + sc.getFQName());
            visitSpeedoClass((SpeedoClass) it.next(), toVisit);
        }
    }
    /**
     * Set inheritance strategy when it is not defined.
     */
    private void visitClassInheritanceStrategy(SpeedoClass sc) throws SpeedoException {
        //assign default inheritance strategy
        if (sc.inheritance != null
                && sc.inheritance.superClassName != null
                && sc.inheritance.strategy == SpeedoInheritance.STRATEGY_UNKOWN) {
            if (sc.mainTable == null) {
                //no table defined
                if (sc.inheritance.join == null) {
                    //no join ==> choose filtered
                    sc.inheritance.strategy = SpeedoInheritance.STRATEGY_SUPERCLASS_TABLE;
                } else {
                    //there is a join ==> it means vertical
                    sc.inheritance.strategy = SpeedoInheritance.STRATEGY_NEW_TABLE;
                }
            } else {
                // there is a table. It means horizontal or vertical
                // either there is a join
                sc.inheritance.strategy = SpeedoInheritance.STRATEGY_NEW_TABLE;
            }
        }
    }
    /**
     * Computes the main table according to the inheritance strategy.
     */
    private void visitClassTable(SpeedoClass sc) throws SpeedoException {
        //check the mainTable
        if (sc.mainTable == null) {
            if (sc.inheritance == null || sc.inheritance.superClassName == null) {
                sc.mainTable = new SpeedoTable();
            } else if (sc.inheritance.isFilteredMapping()) {
                //The table is one of the parent
                sc.mainTable = getRootTable(sc.getSuper());
            } else if (sc.inheritance.isHorizontalMapping()) {
                sc.mainTable = new SpeedoTable();
            } else if (sc.inheritance.isVerticalMapping()) {
                SpeedoClass parent = sc.getSuper();
                if (sc.inheritance.join == null) {
                    sc.inheritance.join = new SpeedoJoin();
                }
                SpeedoJoin join = sc.inheritance.join;
                if (join.mainTable == null) {
                    join.mainTable = getRootTable(parent);
                }
                if (sc.mainTable == null) {
                  if (join.extTable == null) {
                      join.extTable = new SpeedoTable();
                  }
                    sc.mainTable = join.extTable;
                } else {
                    join.extTable = sc.mainTable;
                }
            } else if (sc.inheritance.strategy == SpeedoInheritance.STRATEGY_SUBCLASS_TABLE) {
                //allocate a temp table
                sc.mainTable = new SpeedoTable();
            } else {
                throw new SpeedoException("Inheritance case not managed, class: "
                        + sc.getSourceDesc());
            }
        }
        if (sc.mainTable.name == null) {
            allocateTableName(sc, sc.mainTable);
        }
    }
    /**
     * Visit inheritance elements discriminators, inherited fields, join to
     * parent table, ...
     */
    private void visitClassInheritance(SpeedoClass sc) throws SpeedoException {
        if (sc.inheritance == null || sc.inheritance.superClassName == null) {
            return;
        }
        if (sc.inheritance.isFilteredMapping()) {
            if (scp.nmf.getNamingManager(sc).needInheritanceDiscriminator(sc)) {
                SpeedoClass ancestor = sc.getAncestor();
                if (ancestor.inheritance == null || ancestor.inheritance.discriminator == null) {
                    throw new SpeedoException("Filtered inheritance requires discriminator defined at root level: " + ancestor.getSourceDesc());
                }
                if (sc.inheritance.discriminatorValues == null) {
                    throw new SpeedoException("Filtered inheritance requires discriminator values defined for each sub class: " + sc.getSourceDesc());
                }
            }
        } else if (sc.inheritance.isHorizontalMapping()) {
            //maps all field of all parents
            List parents = sc.getParents();
            for (Iterator parentIt = parents.iterator(); parentIt.hasNext();) {
                SpeedoClass parent = (SpeedoClass) parentIt.next();
                for (Iterator fieldIt = parent.fields.values().iterator(); fieldIt.hasNext();) {
                    mapHorizontalInheritedField((SpeedoField) fieldIt.next(), sc);
                }
            }
        } else if (sc.inheritance.isVerticalMapping()) {
            //TODO: join columns between join.mainTable and join.extTable
            //TODO: discriminator
        }
    }
    /**
     * Visit identity column(s) when there is no primary key field
     */
    private void visitIdentityColumn(SpeedoClass sc) throws SpeedoException {
        if (sc.identity.columns != null || sc.getPKFields().size() > 0) {
            return;
        }
        //no pk fields and no column defined in sc.identity
        NamingManager nm = scp.nmf.getNamingManager(sc);
        SpeedoColumn[] cols = nm.getDefaultColumn(sc);
        if (cols == null) {
            throw new SpeedoException("no identity mapping for the class '"
                    + sc.getFQName() + "'.");
        }
        sc.identity.setColumns(Arrays.asList(cols));
    }
    /**
     * Visit primitive field. By default a primitive field is stored in the
     * main table except if a join is specified. The default column name is
     * the field name.
     */
    private void visitPrimitiveField(SpeedoField sf) throws SpeedoException {
        logger.log(BasicLevel.DEBUG, "\t\tvisit field primitive '" + sf.name + "'.");
        if (sf.columns == null) {
            logger.log(BasicLevel.DEBUG, "\t\tcreate new Column.");
            sf.addColumn(new SpeedoColumn());
        }
        SpeedoColumn col = sf.columns[0];
        if (col.table == null) {
          if (sf.join == null) {
              col.table = sf.moClass.mainTable;
          } else {
              col.table = sf.join.extTable;
          }
            logger.log(BasicLevel.DEBUG, "\t\tset column table: " + col.table.name);
        }
        if (col.name == null) {
            col.name = sf.name;
            logger.log(BasicLevel.DEBUG, "\t\tset column name: " + col.name);
        }
    }
    /**
     * Set the mapped-by visit for bidirectional relationship.
     * @param sf is a persistent field.
     */
    private void visitFieldMappedBy(SpeedoField sf) throws SpeedoException {
        if (sf.relationType == SpeedoField.NO_BI_RELATION) {
            logger.log(BasicLevel.DEBUG, "\t\t\tNo bidirectional relation.");
        } else {
            SpeedoField rf = sf.getReverseField();
            boolean sfHasMapping = sf.columns != null || (sf.join != null && !sf.join.columns.isEmpty());
            boolean rfHasMapping = rf.columns != null || (rf.join != null && !rf.join.columns.isEmpty());
            if (sfHasMapping) {
                if (rfHasMapping) {
                    logger.log(BasicLevel.DEBUG, "\t\t\tOR Mapping already defined both side.");
                    sf.mappedByReversefield = false;
                    rf.mappedByReversefield = false;
                } else {
                    logger.log(BasicLevel.DEBUG, "\t\t\tthe field contains an OR Mapping.");
                    sf.mappedByReversefield = false;
                    rf.mappedByReversefield = true;
                }
            } else {
                if (rfHasMapping) {
                    logger.log(BasicLevel.DEBUG, "\t\t\tthe reverse field contains an OR Mapping.");
                    sf.mappedByReversefield = true;
                    rf.mappedByReversefield = false;
                } else {
                    if (sf.relationType == SpeedoField.MANY_ONE_BI_RELATION) {
                        //for MANY_ONE relations, when no mappedBy is specifed, it is
                        // simpler that the collection reference is mapped by simple
                        // reference
                        rf.mappedByReversefield = true;
                        sf.mappedByReversefield = false;
                        logger.log(BasicLevel.DEBUG, "\t\t\tfield is a the MANY side of the relation, then it must contains the OR Mapping.");
                    } else if (sf.relationType == SpeedoField.ONE_MANY_BI_RELATION) {
                        rf.mappedByReversefield = false;
                        sf.mappedByReversefield = true;
                        logger.log(BasicLevel.DEBUG, "\t\t\tfield is a the ONE side of the relation, then it is mapped by the reverse field.");
                    } else {
                        sf.mappedByReversefield = !rf.mappedByReversefield;
                        logger.log(BasicLevel.DEBUG, "\t\t\tfield is " 
                                + (sf.mappedByReversefield ? "" : " NOT")
                                + " mapped by the reverse field.");
                    }
                }
            }
        }
    }
    /**
     * Visit Class reference field. Manages the case of the foreign key is
     * managed by reverse field or not.
     */
    private void visitClassRefField(SpeedoField sf) throws SpeedoException {
        logger.log(BasicLevel.DEBUG, "\t\tvisit field class reference '" + sf.name + "'.");
        visitClassRefFieldExtension(sf);
        visitFieldMappedBy(sf);
        SpeedoClass rclass = sf.getReferencedClass();
        if (sf.mappedByReversefield) {
            SpeedoField rf = sf.getReverseField();
            logger.log(BasicLevel.DEBUG, "\t\tfield '" + sf.name
                    + "' is mapped by reverse field: "
                    + rf.getFQFieldName());;
            visitSpeedoClass(rclass, new VisitRequired(rf));
            computeFieldFromReverse(sf);
        } else {
          SpeedoTable table;
          if (sf.join == null) {
              table = sf.moClass.mainTable;
          } else {
              table = sf.join.extTable;
          }
          if (sf.columns == null) {
              logger.log(BasicLevel.DEBUG, "\t\tfield '" + sf.name
                      + "' requires pk fields of the class "
                      + rclass.getFQName());
            visitSpeedoClass(rclass, VisitRequired.BASE);
              sf.columns = getFKColumn(rclass, sf.name + "_", table);
          } else {
              for (int i = 0; i < sf.columns.length; i++) {
                  sf.columns[i].table = table;
                    computeTargetColumn(sf, i, rclass);
              }
          }
        }
    }
    /**
     * Computes field mapping from its reverse field.
     */
    private void computeFieldFromReverse(SpeedoField sf)throws SpeedoException {
        SpeedoField rField = sf.getReverseField();
        sf.join = new SpeedoJoin();
        sf.join.mainTable = sf.moClass.mainTable;
        if (rField.join == null) {
            //compute the sf.column from the pk column of the referenced class
            sf.join.extTable = rField.moClass.mainTable;
            sf.columns = getFKColumn(rField.moClass, "", sf.join.extTable);
        } else {
            sf.join.extTable = rField.join.extTable;
            //compute the sf.column from the pk column of the referenced class
            sf.columns = new SpeedoColumn[rField.join.columns.size()];
            int i = 0;
            for (Iterator jcolIt = rField.join.columns.iterator(); jcolIt.hasNext();) {
                SpeedoJoinColumn jcol = (SpeedoJoinColumn) jcolIt.next();
                sf.columns[i] = (SpeedoColumn) jcol.column.clone();
                sf.columns[i].targetColumn = jcol.targetColumn;
                sf.columns[i].targetField = jcol.targetField;
                i++;
            }
        }
        //compute the join
        sf.join.columns.clear();
        for (int i = 0; i < rField.columns.length; i++) {
            sf.join.columns.add(new SpeedoJoinColumn(rField.columns[i]));
        }
    }
    /**
     * Gets a list of SpeedoJoinColumn joining the identifier column of
     * a referenced class
     * @param rclass is the referenced
     * @param colPrefix is prefix to the foreign key column name
     * @param table is the table of the foreign key column
     * @return list of SpeedoJoinColumn
     */
    private List getFKJoinColumn(SpeedoClass rclass,
            String colPrefix,
            SpeedoTable table) {
        SpeedoColumn[] fkCols = getFKColumn(rclass, colPrefix, table);
        ArrayList res = new ArrayList(fkCols.length);
        for (int i = 0; i < fkCols.length; i++) {
            res.add(new SpeedoJoinColumn(fkCols[i]));
        }
        return res;
    }
    /**
     * Gets a list of SpeedoColumn joining the identifier column of
     * a referenced class.
     * @param rclass is the referenced
     * @param colPrefix is prefix to the foreign key column name
     * @param table is the table of the foreign key column
     * @return
     */
    private SpeedoColumn[] getFKColumn(SpeedoClass rclass,
            String colPrefix,
            SpeedoTable table) {
        SpeedoColumn[] pkColumns = getIdColumns(rclass);
        SpeedoColumn[] columns = new SpeedoColumn[pkColumns.length];
        for(int i=0; i<pkColumns.length; i++) {
            //create new SpeedoColumn instance targeting pk column
            // with the same description (sql type, length, scale, ...)
            columns[i] = new SpeedoColumn();
            columns[i].targetColumn = pkColumns[i].name;
            columns[i].name = colPrefix + columns[i].targetColumn;
            columns[i].table = table;
            columns[i].sqlType = pkColumns[i].sqlType;
            columns[i].scale = pkColumns[i].scale;
            columns[i].length = pkColumns[i].length;
        }
        return columns;
    }
    /**
     * Gets the identifier column(s) of a persistent class
     * @param sc is a persistent class
     * @return the identifier column(s) of a persistent class
     */
    private SpeedoColumn[] getIdColumns(SpeedoClass sc) {
        Collection pkFields = sc.getPKFields();
        SpeedoColumn[] columns;
        if (pkFields.isEmpty()) {
            //identifier is based on visible persistent field(s)
            columns = new SpeedoColumn[sc.identity.columns.length];
            for(int i=0; i<sc.identity.columns.length; i++) {
                columns[i] = sc.identity.columns[i].column;
            }
        } else {
            //identifier is based on hidden field(s) (ex: data store id)
            int i = 0;
            columns = new SpeedoColumn[pkFields.size()];
            for (Iterator it = pkFields.iterator(); it.hasNext();) {
                SpeedoField pkField = (SpeedoField) it.next();
                columns[i] = pkField.columns[0];
                i++;
            }
        }
        return columns;
    }
    /**
     * Visit GenClassRef field (field referencing a collection or a map of
     * stuff).
     * @param sf is a persistent field referencing a collection, a map of stuff
     * (persistent objects or primitive elements)
     */
    private void visitGenClassRefField(SpeedoField sf) throws SpeedoException {
        logger.log(BasicLevel.DEBUG, "\t\tvisit field generic class reference '" + sf.name + "'.");
        visitGenClassRefFieldExtension(sf);
        visitFieldMappedBy(sf);
        if (sf.mappedByReversefield) {
            SpeedoField rf = sf.getReverseField();
            logger.log(BasicLevel.DEBUG, "\t\tfield '" + sf.name
                    + "' is mapped by reverse field: "
                    + rf.getFQFieldName());
            visitSpeedoClass(sf.getReferencedClass(), new VisitRequired(rf));
            computeFieldFromReverse(sf);
            // do not forget index in case of map indexed by field of the
            // referenced class
            visitGenClassIndex(sf);
            return;
        }
        boolean joinCreated = sf.join == null;
        if (joinCreated) {
            //create the join between mainTable and the genClass table
            sf.join = new SpeedoJoin();
            //sf.moClass.addJoin(sf.join);
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "\t\t\tCreate SpeedoJoin");
            }
        }
        if (sf.join.mainTable == null) {
            sf.join.mainTable = sf.moClass.mainTable;
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "\t\t\tDefine the main table on join: " + sf.join.mainTable.name);
            }
        }
        if (sf.join.extTable == null) {
            //compute the table of the genclass with regards to the relation type
            switch (sf.relationType) {
            case SpeedoField.MANY_MANY_BI_RELATION:
                SpeedoField rfield = sf.getReverseField();
                if (rfield.join != null && rfield.join.extTable != null) {
                    sf.join.extTable = rfield.join.extTable;
                    if (logger.isLoggable(BasicLevel.DEBUG)) {
                        logger.log(BasicLevel.DEBUG, "\t\t\tUse table of reverse field: " + sf.join.extTable.name);
                    }
                } else {
                    sf.join.extTable = new SpeedoTable();
                    sf.join.extTable.name = sf.moClass.name + "_" + sf.name;
                    if (logger.isLoggable(BasicLevel.DEBUG)) {
                        logger.log(BasicLevel.DEBUG, "\t\t\tDefine the join table of the relation: " + sf.join.extTable.name);
                    }
                }
                break;
            case SpeedoField.ONE_MANY_BI_RELATION:
                rfield = sf.getReverseField();
              if (rfield.join != null && rfield.join.extTable != null) {
                  sf.join.extTable = rfield.join.extTable;
                  if (logger.isLoggable(BasicLevel.DEBUG)) {
                      logger.log(BasicLevel.DEBUG, "\t\t\tUse table of reverse field: " + sf.join.extTable.name);
                  }
              } else {
                  sf.join.extTable = rfield.moClass.mainTable;
                  if (logger.isLoggable(BasicLevel.DEBUG)) {
                      logger.log(BasicLevel.DEBUG, "\tUse the table of the reference class: " + sf.join.extTable.name);
                  }
              }
              break;
            default:
              sf.join.extTable = new SpeedoTable();
            sf.join.extTable.name = sf.moClass.name + "_" + sf.name;
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, "\t\t\tDefine the GC table: " + sf.join.extTable.name);
                }
              break;
            }
            sf.join.extTable.join = sf.join;
        }
        if (sf.join.columns.isEmpty()) {
            //Compute the join between the table of the owner class and the
            // table of the genclass
            sf.join.columns.addAll(getFKJoinColumn(sf.moClass, "", sf.join.extTable));
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "\t\t\tDefine the join column: \n" + sf.join.columns);
            }
        } else {
            for (Iterator it = sf.join.columns.iterator(); it.hasNext();) {
                SpeedoJoinColumn jc = (SpeedoJoinColumn) it.next();
                if (jc.targetColumn == null) {
                    computeTargetJoinColumn(sf, jc);
                }
            }
        }
        if (joinCreated && logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "\t\t\tCreated " + sf.join.toString());
        }
        //map the index of the genclass
        visitGenClassIndex(sf);
        //map the element of the genclass
        visitGenClassElement(sf);
    }
    /**
     * Visit genclass index. Indexes can be find for List or Map implementation.
     * @param sf a persistent field.
     */
    private void visitGenClassIndex(SpeedoField sf) throws SpeedoException {
        if (sf.jdoTuple instanceof SpeedoCollection) {
            SpeedoCollection collec = (SpeedoCollection) sf.jdoTuple;
            if (collec.indexColumns == null
                    && List.class.isAssignableFrom(getGCClass(sf))) {
                collec.indexColumns = new SpeedoColumn("idx", sf.join.extTable);
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG,
                            "\t\t\tCreate column for the list index"
                        + collec.indexColumns.toString());
                }
            }
        } else if (sf.jdoTuple instanceof SpeedoMap) {
            SpeedoMap map = (SpeedoMap) sf.jdoTuple;
            if (map.keyColumns == null) {
                map.keyColumns = new SpeedoColumn("idx", sf.join.extTable);
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, "\t\t\tCreate column for the map key"
                        + map.keyColumns.toString());
                }
            }
        }
    }
    /**
     * Visit gen class element. The element can be a primitive element
     * or a reference to a persistent class.
     * @param sf is the SpeedoField meta object representing the genclass
     */
    private void visitGenClassElement(SpeedoField sf) throws SpeedoException {
        SpeedoClass rclass = sf.getReferencedClass();
        if (sf.columns == null) {
          if (rclass == null) {
              //primitive element
              sf.addColumn(new SpeedoColumn("element", sf.join.extTable));
          } else {
              logger.log(BasicLevel.DEBUG, "\t\tfield '" + sf.name
                      + "' requires pk fields of the class "
                      + rclass.getFQName());
                visitSpeedoClass(rclass, VisitRequired.BASE);
              //persistent class ==> classRef
                String prefix = "";
                if (sf.join.extTable != rclass.mainTable) {
                    prefix = "elem_";
                }
                sf.columns = getFKColumn(rclass, prefix, sf.join.extTable);
          }             
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                StringBuffer sb = new StringBuffer();
                sb.append("\t\t\tCreate ").append(sf.columns.length);
                sb.append(" column(s) for the gen class element :\n");
                for (int i = 0; i < sf.columns.length; i++) {
                    sb.append("\t\t-").append(sf.columns[i]).append("\n");
                }
                logger.log(BasicLevel.DEBUG, sb.toString());
            }
        } else {
          if (rclass == null) {
              //primitive element
                if (sf.columns[0].name == null) {
                    sf.columns[0].name = "element";
                }
                if (sf.columns[0].table == null) {
                    sf.columns[0].table = sf.join.extTable;
                }
          } else {
              //persistent class ==> classRef
              SpeedoColumn[] cols = getFKColumn(rclass, "elem_", sf.join.extTable);
              for (int i = 0; i < sf.columns.length; i++) {
                  if (sf.columns[i].name == null) {
                      sf.columns[i].name = cols[i].name;
                  }
                    if (sf.columns[i].targetColumn == null) {
                        computeTargetColumn(sf, i, rclass);
                    }
                  if (sf.columns[i].table == null) {
                      sf.columns[i].table = sf.join.extTable;
                  }
                }
            }
        }
    }
    /**
     * Computes the targetColumn field of the sf.column[colIdx] if it is
     * required.
     * @param sf is the SpeedoCommonField holding the column definition
     * @param colIdx is the index of the column among sf.columns array
     * @param rclass is the referenced persistent class. The target column are
     * the identifier column of the referenced class.
     * @throws SpeedoException
     */
    private void computeTargetColumn(SpeedoCommonField sf,
            int colIdx,
            SpeedoClass rclass) throws SpeedoException {
        if (sf.columns[colIdx].targetColumn != null) {
            return;
        }
        if (sf.columns[colIdx].targetField == null) {
            //both are null
            if (sf.columns.length == 1) {
                //compute the target column from the unique pk field
                try {
                    SpeedoField pkField = rclass.getUniquePKField();
                    sf.columns[colIdx].targetColumn = pkField.columns[0].name;
                } catch (SpeedoException e) {
                    throw new SpeedoException("Bad number of column specified for the " + sf.getSourceDesc(), e);
                }
            } else {
                SpeedoException se = new SpeedoException("Target column is required for the reference " + sf.getSourceDesc());
                logger.log(BasicLevel.ERROR, se.getMessage(), se);
                throw se;
            }
        } else {
            //compute the target column from the target field
            SpeedoField pkField = rclass.getField(sf.columns[colIdx].targetField);
            sf.columns[colIdx].targetColumn = pkField.columns[0].name;
        }
    }
    private void computeTargetJoinColumn(SpeedoCommonField sf,
            SpeedoJoinColumn jc) throws SpeedoException {
        int nbcol = sf.join.columns.size();
        if (jc.targetField == null) {
            //both are null
            if (nbcol == 1) {
                //compute the target column from the unique pk field
                SpeedoField pkField = sf.moClass.getUniquePKField();
                jc.targetColumn = pkField.columns[0].name;
            } else {
                SpeedoException se = new SpeedoException("Target column is required for join column of the reference " + sf.getSourceDesc());
                logger.log(BasicLevel.ERROR, se.getMessage(), se);
                throw se;
            }
        } else {
            //compute the target column from the target field
            SpeedoField pkField = sf.moClass.getField(jc.targetField);
            jc.targetColumn = pkField.columns[0].name;
        }
    }
    /**
     * Get the type of the persistent field.
     * @return the java.lang.Class representing the type of the field.
     */
    private Class getGCClass(SpeedoField sf) throws SpeedoException {
        try {
            return Class.forName(sf.type());
        } catch (ClassNotFoundException e) {
            throw new SpeedoException("Class loading problem: ", e);
        }
    }
    /**
     * Creates a new table name for a SpeedoTable
     * @param sc is the SpeedoClass that has the table
     * @param t is the table without name
     */
    private void allocateTableName(SpeedoClass sc, SpeedoTable t) {
        if (t.name == null) {
          if (sc.mainTable == t) {
              t.name = sc.name.toUpperCase();
          } else {
              for (int i = 0; i < sc.joinToExtTables.length; i++) {
                  if (sc.joinToExtTables[i].extTable == t) {
                      t.name = sc.name.toUpperCase() + "_EXT_" + i;
                      return;
                  }
              }
                t.name = sc.name.toUpperCase();
          }
        }
    }
    /**
     * Builds, if required, the mapping of an inherited field.
     * @param sif
     */
    private void mapHorizontalInheritedField(SpeedoField sf,
            SpeedoClass sc) throws SpeedoException {
        SpeedoInheritedField sif = (SpeedoInheritedField)
        sc.inheritance.remappedInheritedFields.get(sf.getFQFieldName());
        if (sif == null) {
            sif = sc.inheritance.newSpeedoInheritedField(sf);
        }
        if (sf.jdoTuple == null) {
            SpeedoClass rclass = sf.getReferencedClass();
            //primitive field or simple reference to a persistent class
            if (rclass != null && sf.join != null
                    && sf.relationType == SpeedoField.ONE_ONE_BI_RELATION
                    && sf.mappedByReversefield) {
                // the classref belongs a bidirectionnal relationship
                // ONE-ONE. In addition the foreign key is hold by the table
                // of the referenced class
                setJoinNColsFromParent(sif);
            } else {
                if (sif.columns == null) {
                    // ClassRef or primtive element to map localy
                    for (int i = 0; i < sf.columns.length; i++) {
                        //Same column definition but in the table of the class
                        SpeedoColumn col = (SpeedoColumn) sf.columns[i].clone();
                        col.table = sif.moClass.mainTable;
                        sif.addColumn(col);
                    }
                } else {
                    if (rclass != null) {
                        for (int i = 0; i < sif.columns.length; i++) {
                            computeTargetColumn(sif, i, rclass);
                        }
                    }
                }
            }
        } else {
            //reference to a generic class (Collection, Set, Map, ..)
            setJoinNColsFromParent(sif);
            //TODO: support index/key
        }
    }
    private void setJoinNColsFromParent(SpeedoInheritedField sif) {
        SpeedoField sf = sif.inheritedField;
        if (sif.columns == null) {
            //Use the same column for the genclass value
            sif.columns = sf.columns;
        }
        if (sif.join == null) {
            sif.join = new SpeedoJoin();
            sif.join.extTable = sf.join.extTable;
            sif.join.mainTable = sf.moClass.mainTable;
            // Use the same join column name but with the targeted column
            // of the current table. We suppose than the id has the same
            // structure
            sif.join.columns = getFKJoinColumn(sf.moClass, "", sif.join.extTable);
            if (sif.join.columns.size() == 1) {
                ((SpeedoJoinColumn) sif.join.columns.get(0)).column.name =
                    ((SpeedoJoinColumn) sf.join.columns.get(0)).column.name;
            } else {
                for (Iterator it = sf.moClass.getPKFields().iterator(); it.hasNext();) {
                    SpeedoField pkField = (SpeedoField) it.next();
                    SpeedoJoinColumn parentjc = sf.getFKJoinColumn(pkField.columns[0].name);
                    SpeedoJoinColumn newjc = sif.getFKJoinColumn(pkField.columns[0].name);
                    newjc.column.name = parentjc.column.name;
                }
            }
        }
       
    }
   
    /**
     * Get the root table of a persistent class. If the class has not
     * inheritance the root table is the maintable. If the inheritance mapping
     * is vertical, the root table is the one of the parent.
     */
    private SpeedoTable getRootTable(SpeedoClass sc) {
        if (sc.inheritance != null && sc.inheritance.isVerticalMapping()) {
            return sc.inheritance.join.mainTable;
        } else {
            return sc.mainTable;
        }
    }
    /**
     * Visit extensions of field referencing a persistent class. This method
     * converts old Extensions 'source-foreign-keys' and 'target-foreign-keys'
     * to mapping definition in the Speedo meta information.
     * @param sf a persistent field referencing a persistent class.
     * @see SpeedoProperties#SOURCE_FK
     * @see SpeedoProperties#TARGET_FK
     */
    public void visitClassRefFieldExtension(SpeedoField sf) throws SpeedoException {
        SpeedoClass rclass = sf.getReferencedClass();
        String sfk = sf.getExtensionValueByKey(SpeedoProperties.SOURCE_FK);
        String tfk = sf.getExtensionValueByKey(SpeedoProperties.TARGET_FK);
        if (sfk == null && tfk == null) {
            return;
        }
        if (sfk != null && sf.relationType == SpeedoField.ONE_ONE_BI_RELATION) {
            //The relation is mapped by the field having the foreign key
            return;
        }
        if (tfk != null) {
            SpeedoColumn[] cols = getFKColumn(tfk, sf.moClass.mainTable, sf.getReferencedClass());
            if (sf.columns != null && cols.length == sf.columns.length) {
                for (int i = 0; i < cols.length; i++) {
                    sf.columns[i].merge(cols[i]);
                }
            } else {
                sf.columns = cols;
            }
        } else if (sfk != null) { //backward reference
            sf.join = new SpeedoJoin();
            sf.join.mainTable = sf.moClass.mainTable;
            sf.join.extTable = rclass.mainTable;
            //compute the sf.column from the pk column of the referenced class
            Collection pkFields = rclass.getPKFields();
            if (pkFields.isEmpty()) {
                sf.columns = new SpeedoColumn[rclass.identity.columns.length];
                for(int i=0; i<rclass.identity.columns.length; i++) {
                    sf.columns[i] = rclass.identity.columns[i].column;
                }
            } else {
                int i = 0;
                sf.columns = new SpeedoColumn[pkFields.size()];
                for (Iterator it = pkFields.iterator(); it.hasNext();) {
                    SpeedoField pkField = (SpeedoField) it.next();
                    sf.columns[i] = pkField.columns[0];
                    i++;
                }
            }
            //compute the join columns
            sf.join.columns.addAll(getFKJoinColumn(sfk, rclass.mainTable, sf.moClass));
        }
        logger.log(BasicLevel.DEBUG, "Field '" + sf.name
                + "' has deprecated extension(s): "
                + "\n\t-(" + SpeedoProperties.SOURCE_FK + "=" + sfk
                + "\n\t-" + SpeedoProperties.TARGET_FK + "=" + tfk
                + "\nExtensions have been converted:"
                + "\n\t- column:" + sf.printColumns()
                + "\n\t- join:" + sf.join
                );
    }
    /**
     * Visit extensions of field referencing a genclass. This method
     * converts old Extensions 'source-foreign-keys', 'target-foreign-keys'
     * and 'join-table' to mapping definition in the Speedo meta information.
     * @param sf a persistent field referencing a genclass.
     * @see SpeedoProperties#SOURCE_FK
     * @see SpeedoProperties#TARGET_FK
     * @see SpeedoProperties#JOIN_TABLE
     */
    public void visitGenClassRefFieldExtension(SpeedoField sf) throws SpeedoException {
        String sfk = sf.getExtensionValueByKey(SpeedoProperties.SOURCE_FK);
        String tfk = sf.getExtensionValueByKey(SpeedoProperties.TARGET_FK);
        String jt = sf.getExtensionValueByKey(SpeedoProperties.JOIN_TABLE);
        if (sfk == null && tfk == null && jt == null) {
            return;
        }
        if (sfk != null && tfk == null && jt == null
                && sf.relationType == SpeedoField.ONE_MANY_BI_RELATION) {
            //The relation is mapped by the field having the foreign key
            return;
        }
        sf.join = new SpeedoJoin();
        sf.join.mainTable = sf.moClass.mainTable;
        //sf.moClass.addJoin(sf.join);
        if (jt != null) {
            sf.join.extTable = new SpeedoTable();
            sf.join.extTable.name = jt;
        }
        if (tfk != null && (sf.columns == null || sf.columns[0].name == null)) {
            //compute the value column(s)
            sf.columns = getFKColumn(tfk, sf.join.extTable, sf.getReferencedClass());
        }
        if (sfk != null && sf.join.columns.isEmpty()) {
            //compute the join column(s)
            sf.join.columns.addAll(getFKJoinColumn(sfk, sf.join.extTable, sf.moClass));
        }
        logger.log(BasicLevel.DEBUG, "Field '" + sf.name
                + "' has deprecated extension(s): "
                + "\n\t-(" + SpeedoProperties.SOURCE_FK + "=" + sfk
                + "\n\t-" + SpeedoProperties.TARGET_FK + "=" + tfk
                + "\n\t" + SpeedoProperties.JOIN_TABLE + "=" + jt
                + "\nExtensions have been converted:"
                + "\n\t- column:" + sf.printColumns()
                + "\n\t- join:" + sf.join
                );
    }
    /**
     * Gets array of new SpeedoColumn representing a join to a peristent class.
     * @param pk2tfk is the map defining the name of the foreign key column from
     * the primary key column (key=String pkColName/ value=String fkColName)
     * @param table is the table hosting the foreign key column
     * @param rclass is the referenced class having pk column
     */
    private SpeedoColumn[] getFKColumn(String pk2tfk, SpeedoTable table, SpeedoClass rclass) {
        Map map = getPk2Fk(pk2tfk);
        SpeedoColumn[] columns = new SpeedoColumn[map.size()];
        int i = 0;
        for (Iterator it = map.entrySet().iterator(); it.hasNext();) {
            Map.Entry me = (Map.Entry) it.next();
            columns[i] = new SpeedoColumn();
            columns[i].name = (String) me.getValue();
            columns[i].table = table;
            columns[i].targetColumn = (String) me.getKey();
            if (rclass != null) {
                SpeedoColumn pkcol = rclass.getColumn(columns[i].targetColumn, true);
                if (pkcol != null) {
                  columns[i].sqlType = pkcol.sqlType;
                  columns[i].scale = pkcol.scale;
                  columns[i].length = pkcol.length;
                }
            }
            i++;
        }
        return columns;
    }
    /**
     * Gets List of new SpeedoJoinColumn representing a join to a peristent class.
     * @param pk2tfk is the map defining the name of the foreign key column from
     * the primary key column (key=String pkColName/ value=String fkColName)
     * @param table is the table hosting the foreign key column
     * @param rclass is the referenced class having pk column
     */
    private List getFKJoinColumn(String pk2sfk, SpeedoTable table, SpeedoClass rclass) {
        SpeedoColumn[] cols = getFKColumn(pk2sfk, table, rclass);
        ArrayList res = new ArrayList(cols.length);
        for (int i = 0; i < cols.length; i++) {
            res.add(new SpeedoJoinColumn(cols[i]));
        }
        return res;
    }
    /**
     * Converts the value of the extension 'source-foreign-keys' and
     * 'target-foreign-keys' to a map
     * @param fks
     * @return a map (key=String pkColName / Value=String fkColName)
     */
    private Map getPk2Fk(String fks) {
        Map res = new HashMap();
        StringTokenizer st = new StringTokenizer(fks, "=,:;/", false);
        String pk = null;
        while(st.hasMoreTokens()) {
            String tok = st.nextToken();
            tok = tok.trim();
            if (pk == null) {
                pk = tok;
            } else {
                res.put(pk, tok);
                pk = null;
            }
        }
        return res;
    }
}
TOP

Related Classes of org.objectweb.speedo.generation.mivisitor.ORMappingGenerator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.