All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.jdo.api.persistence.mapping.ejb.MappingFile Maven / Gradle / Ivy

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
// Portion Copyright [2018-2019] Payara Foundation and/or affiliates

/*
 * MappingFile.java
 *
 * Created on February 1, 2002, 9:47 PM
 */

package com.sun.jdo.api.persistence.mapping.ejb;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.File;

import java.util.Collection;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.ResourceBundle;
import java.util.Iterator;
import java.io.IOException;

import org.netbeans.modules.dbschema.*;

import com.sun.jdo.api.persistence.mapping.ejb.beans.*;
import org.glassfish.common.util.StringHelper;
import com.sun.jdo.api.persistence.model.*;
import com.sun.jdo.api.persistence.model.mapping.*;
import com.sun.jdo.api.persistence.model.mapping.impl.*;
import com.sun.jdo.api.persistence.model.jdo.*;
import com.sun.jdo.api.persistence.model.jdo.impl.*;
import com.sun.jdo.spi.persistence.utility.logging.Logger;
import org.glassfish.persistence.common.I18NHelper;

import org.netbeans.modules.schema2beans.Schema2BeansException;

/** This class supports the conversion between the iAS mapping file
 * format and the object used to represent that mapping to support
 * the iAS EJBC process and iAS CMP run-time.
 *
 * @author vbk
 * @version 1.0
 */
public class MappingFile {

    private static final String JAVA_TYPE_SET = "java.util.Set"; //NOI18N
    private static final String JAVA_TYPE_COLLECTION = "java.util.Collection"; //NOI18N
    private static final List types = new ArrayList<>();

    /** Definitive location for a  mapping file in an ejb-jar file. */
    public static final String DEFAULT_LOCATION_IN_EJB_JAR;

    /** The logger */
    private static final Logger logger =
        LogHelperMappingConversion.getLogger();

    /**
     * I18N message handler
     */
    private final static ResourceBundle messages = I18NHelper.loadBundle(
            MappingFile.class);

    static {
        types.add(JAVA_TYPE_SET);
        types.add(JAVA_TYPE_COLLECTION);

        DEFAULT_LOCATION_IN_EJB_JAR = new StringBuilder(I18NHelper.getMessage(
            messages, "CONST_IAS_MAPPING_FILE_LOC")). //NOI18N
            append(File.separator).
            append(I18NHelper.getMessage(
            messages, "CONST_IAS_MAPPING_FILE")).toString(); //NOI18N
    }

    private Map inverseRelationships = null;
    private Map namedGroups = null;
    private int groupCount = MappingFieldElement.GROUP_INDEPENDENT;

    private ClassLoader classLoader = null;

    private static int MINIMUM_PRECISION = 19;

    /**
     * An object which implements ConversionHelper interface to
     * access data from other available sources to support the mapping
     * generation.
     */
    private ConversionHelper helper = null;

    private HashMap loadedSchema = new HashMap();

    /** Creates new MappingFile */
    public  MappingFile() {
        classLoader = null;
    }

    /** Creates new MappingFile */
    public  MappingFile(ClassLoader cl) {
        this();
        classLoader = cl;
    }

    /** Convert an SunCmpMapping object into the equivelent collection of
     * MappingClassElement objects
     * @param content An SunCmpMapping object that describes a mapping
     * for one or more beans
     * @param helper An object which implements ConversionHelper interface to
     * access data from other available sources to support the mapping
     * generation
     * @return A Collection of MappingClassElement objects
     * @throws DBException
     * @throws ModelException
     * @throws ConversionException
     */
    public Map intoMappingClasses(SunCmpMappings content,
        ConversionHelper helper)
        throws DBException, ModelException, ConversionException {
        Map mces = new java.util.HashMap();
        this.helper = helper;
        boolean ensureValidation = helper.ensureValidation();

        for (int i = 0; i < content.sizeSunCmpMapping(); i++) {
            SunCmpMapping beanSet = content.getSunCmpMapping(i);
            inverseRelationships = new HashMap();
            namedGroups = new HashMap();
            String schema = beanSet.getSchema();

            if (helper.generateFields()) {

                // sweep through the mappings to complete them
                completeCmrMappings(beanSet);
            }

            // process bean mapping and fields mapping
            for (int k = 0; k < beanSet.sizeEntityMapping(); k++) {
                EntityMapping beanMapping = beanSet.getEntityMapping(k);
                MappingClassElement aTpMapping = null;
                if (ensureValidation) {
                    aTpMapping = mapFieldsOfBean(beanMapping, schema);
                }
                else {
                    try {
                        aTpMapping = mapFieldsOfBean(beanMapping, schema);
                    }
                    catch (ConversionException t) {
                        if (logger.isLoggable(Logger.FINE))
                            logger.fine(
                                I18NHelper.getMessage(
                                messages,
                                "MESSAGE_CONV_EXC", //NOI18N
                                t.toString()));
                    }
                }

                mces.put(beanMapping.getEjbName(), aTpMapping);
            }
        }
        return mces;
    }

    /** Convert an SunCmpMapping object into the equivelent collection of
     * MappingClassElement objects
     * @param content An InputStream that contains an xml data stream that
     * conforms to the sun-cmp-mapping.dtd
     * @param helper An object which implements ConversionHelper interface to
     * access data from other available sources to support the mapping
     * generation
     * @return A Collection of MappingClassElement objects
     * @throws DBException
     * @throws ModelException
     * @throws ConversionException
     */
    public Map intoMappingClasses(InputStream content, ConversionHelper helper)
        throws DBException, ModelException, ConversionException {
        SunCmpMappings foo = null;
        try {
            foo = SunCmpMappings.createGraph(content);
        }
        catch (Schema2BeansException t) {
            if (helper.ensureValidation()) {
                throw new ConversionException(
                    I18NHelper.getMessage(messages,
                    "XML_ERROR_IN_MAPPING_FILE", //NOI18N
                    DEFAULT_LOCATION_IN_EJB_JAR));
            }
            foo = SunCmpMappings.createGraph();
        }

        return intoMappingClasses(foo, helper);
    }

    /** Creates an SunCmpMapping object from a Collection of
     * MappingClassElement objects
     * @param dest The output for processing
     * @param mappingClasses The Collection of MappingClassElements
     * @param helper An object which implements ConversionHelper interface to
     * access data from other available sources to support the mapping
     * generation
     * @throws IOException Thrown if there is a problem
     * sending the data out on dest.
     * @throws Schema2BeansException
     */
    public void fromMappingClasses(OutputStream dest, Map mappingClasses,
        ConversionHelper helper)
        throws IOException, Schema2BeansException {
        SunCmpMappings tmp = fromMappingClasses(mappingClasses, helper);
        tmp.write(dest);
    }

    /** Creates an SunCmpMapping object from a Collection of
     * MappingClassElement objects
     * @param mappingClasses The Collection of MappingClassElements
     * @param helper An object which implements ConversionHelper interface to
     * access data from other available sources to support the mapping
     * generation.
     * @return The SunCmpMapping object that is equivelent to the
     * collection of MappingClassElements.
     * throws Schema2BeansException
     */
    public SunCmpMappings fromMappingClasses(Map mappingClasses,
        ConversionHelper helper) throws Schema2BeansException {
        Iterator keyIter = mappingClasses.keySet().iterator();
        Map mapOfMapping = new java.util.HashMap();
        while (keyIter.hasNext()) {
            String ejbName = (String) keyIter.next();
            MappingClassElement mce = (MappingClassElement)
                mappingClasses.get(ejbName);
            EntityMapping beanMapping = new EntityMapping();

            if (null != mce) {
                setConsistency(mce, beanMapping);

                String schemaName = mce.getDatabaseRoot();
                SunCmpMapping aMapping = (SunCmpMapping) mapOfMapping.get(
                    schemaName);
                if (null == aMapping) {
                    aMapping = new SunCmpMapping();
                    aMapping.setSchema(schemaName);
                    mapOfMapping.put(schemaName, aMapping);
                }
                List tables = mce.getTables();
                MappingTableElement primary = null;
                if (tables.size() > 0) {
                    primary = (MappingTableElement) tables.get(0);
                    beanMapping.setTableName(primary.getName());
                }
                beanMapping.setEjbName(ejbName);
                if (null != primary) {
                    List refKeys = primary.getReferencingKeys();
                    for (int i = 0; refKeys != null && i < refKeys.size(); i++) {
                        SecondaryTable sT = new SecondaryTable();
                        MappingReferenceKeyElement mrke =
                            (MappingReferenceKeyElement) refKeys.get(i);
                        MappingTableElement mte = mrke.getTable();
                        if (null != mte) {
                            sT.setTableName(mte.getName());
                            List cpnames = mrke.getColumnPairNames();
                            boolean hasPairs = false;
                            for (int j = 0; cpnames != null &&
                                j < cpnames.size(); j++) {
                                List token =
                                    StringHelper.separatedListToArray((String)cpnames.get(j),";"); //NOI18N
                                ColumnPair cp = new ColumnPair();
                                Iterator iter = token.iterator();
                                while (iter.hasNext()) {
                                    String columnName = (String)iter.next();
                                    cp.addColumnName(columnName);
                                }
                                sT.addColumnPair(cp);
                                hasPairs = true;
                            }
                            if (hasPairs)
                                beanMapping.addSecondaryTable(sT);
                            else
                                if (logger.isLoggable(Logger.FINE))
                                    logger.fine(
                                        I18NHelper.getMessage(
                                        messages,
                                        "WARN_ILLEGAL_PAIR", //NOI18N
                                        new Object [] {ejbName, mte.getName(), cpnames}));
                        }
                        else {
                            if (logger.isLoggable(Logger.FINE))
                                logger.fine(
                                    I18NHelper.getMessage(
                                    messages,
                                    "WARN_MISSING_TABLE", //NOI18N
                                    new Object [] {ejbName, primary.getName()}));
                        }
                    }
                }
                else {
                    if (logger.isLoggable(Logger.FINE))
                        logger.fine(
                            I18NHelper.getMessage(
                            messages,
                            "WARN_NO_PRIMARY", //NOI18N
                            ejbName));
                }

                // transform the field mappings
                PersistenceClassElement pce = null;
                PersistenceFieldElement pfields[] = null;
                if (mce instanceof MappingClassElementImpl) {
                    MappingClassElementImpl mcei =
                        (MappingClassElementImpl) mce;
                    pce = mcei.getPersistenceElement();
                    pfields = pce.getFields();
                }
                int len = 0;
                if (null != pfields)
                    len = pfields.length;
                for (int i = 0; i < len; i++) {
                    PersistenceFieldElement pfield = pfields[i];
                    String fieldName = pfield.getName();
                    if (helper.isGeneratedField(ejbName, fieldName)) {
                        continue;
                    }
                    if (pfield instanceof RelationshipElement) {
                        MappingRelationshipElement mre =
                            (MappingRelationshipElement) mce.getField(fieldName);
                        MappingFieldElement mfe = mre;
                        CmrFieldMapping cfm = new CmrFieldMapping();
                        cfm.setCmrFieldName(fieldName);
                        List cols = null;
                        if (null != mfe) {
                            cols = mfe.getColumns();
                            int fgVal = mfe.getFetchGroup();
                            setFetchedWith(cfm, fgVal);
                        }
                        for (int j = 0; null != cols && j < cols.size(); j++) {
                            String cpstring = (String) cols.get(j);
                            int slen = cpstring.indexOf(';');
                            ColumnPair cp = new ColumnPair();
                            cp.addColumnName(cpstring.substring(0,slen));
                            cp.addColumnName(cpstring.substring(slen+1));
                            cfm.addColumnPair(cp);
                        }
                        if (null != mre)
                            cols = mre.getAssociatedColumns();
                        for (int j = 0; null != cols && j < cols.size(); j++) {
                            String cpstring = (String) cols.get(j);
                            int slen = cpstring.indexOf(';');
                            ColumnPair cp = new ColumnPair();
                            cp.addColumnName(cpstring.substring(0,slen));
                            cp.addColumnName(cpstring.substring(slen+1));
                            cfm.addColumnPair(cp);
                        }
                        beanMapping.addCmrFieldMapping(cfm);
                    }
                    else {
                        MappingFieldElement mfe = mce.getField(fieldName);
                        CmpFieldMapping cfm = new CmpFieldMapping();
                        cfm.setFieldName(fieldName);
                        List cols = null;
                        if (null != mfe) {
                            cols = mfe.getColumns();
                            for (int j = 0; null != cols &&
                                j < cols.size(); j++) {
                                cfm.addColumnName((String)cols.get(j));
                            }
                            int fgVal = mfe.getFetchGroup();
                            setFetchedWith(cfm,fgVal);
                        }
                        beanMapping.addCmpFieldMapping(cfm);
                    }
                }
                aMapping.addEntityMapping(beanMapping);
            }
        }
        SunCmpMappings retVal = null;
        retVal = new SunCmpMappings();
        Iterator mapOfMappingIter = mapOfMapping.values().iterator();
        while (mapOfMappingIter.hasNext()) {
            SunCmpMapping aVal = (SunCmpMapping) mapOfMappingIter.next();
            retVal.addSunCmpMapping(aVal);
        }
        return retVal;
    }

    /** Set fetchgroup in schema2beans FetchedWith bean
     * @param cfm An object that represents HasFetchedWith object
     * in schema2beans
     * @param fgVal integer that represents fetch group value
     */
    private void setFetchedWith(HasFetchedWith cfm, int fgVal) {
        FetchedWith fw = new FetchedWith();
        if (fgVal <= MappingFieldElement.GROUP_INDEPENDENT) {
            String key = "IndependentFetchGroup"+fgVal; //NOI18N
            fw.setNamedGroup(key);
        }
        else if (fgVal == MappingFieldElement.GROUP_DEFAULT) {
            fw.setDefault(true);
        }
        else if (fgVal > MappingFieldElement.GROUP_DEFAULT) {
            fw.setLevel(fgVal-1);
        }
        else if (fgVal == MappingFieldElement.GROUP_NONE) {
            fw.setNone(true);
        }
        cfm.setFetchedWith(fw);
    }

    /** Convert a bean's cmp-field-mapping and cmr-field-mapping elements into
     * mapping model objects.
     *
     * The method can be called in two distinct cases:
     *
     * 1. At deployment time. The mapping must be complete enough to create a
     * valid mapping model, in order to support execution.
     *
     * 2. During mapping development.  The mapping may be incomplete, but the
     * model objects that are created need to be as consistent as possible.
     * The absence of data should not be fatal, if possible.
     *
     * @param mapping The schema2beans object that represents the mapping
     * for a particular bean.
     * @param schemaArg The name of the schema that the bean is mapped against.
     * @return The MappingClassElement that corresponds to the
     * schema2beans object.
     * @throws DBException
     * @throws ModelException
     * @throws ConversionException
     */
    private MappingClassElement mapFieldsOfBean(EntityMapping mapping,
                                                String schemaArg)
        throws DBException, ModelException, ConversionException {

        String beanName = mapping.getEjbName();
        MappingClassElement mce = null;
        List tablesOfBean = new ArrayList();
        Map knownTables = new HashMap();

        if (logger.isLoggable(Logger.FINE))
            logger.fine(
                I18NHelper.getMessage(
                messages,
                "MESSAGE_START_BEAN", //NOI18N
                beanName));

        String jdoClassName = helper.getMappedClassName(beanName);
        if (logger.isLoggable(Logger.FINE))
            logger.fine(
                I18NHelper.getMessage(
                messages,
                "MESSAGE_JDOCLASS_NAME", //NOI18N
                beanName, jdoClassName));

        if (null == jdoClassName) {
            throw new ConversionException(
                I18NHelper.getMessage(
                messages,
                "ERR_INVALID_CLASS", //NOI18N
                beanName));
        }

        // create the mapping class element and its children
        PersistenceClassElementImpl persistElImpl =
            new PersistenceClassElementImpl(jdoClassName);
        persistElImpl.setKeyClass(jdoClassName+".Oid"); //NOI18N
        mce = new MappingClassElementImpl(
            new PersistenceClassElement(persistElImpl));

        SchemaElement schema = null;

        // Assign the schema
        if (!StringHelper.isEmpty(schemaArg))
            schema = setDatabaseRoot(mce, schemaArg,
                helper.ensureValidation());

        // Map the table information
        // Ensure the bean is mapped to a primary table.
        if (!StringHelper.isEmpty(mapping.getTableName())) {

            mapPrimaryTable(mapping, mce,
                schema, knownTables, tablesOfBean);
            mapSecondaryTables(mapping, mce,
                schema, knownTables, tablesOfBean);
        }

        ColumnElement candidatePK = null;

        // map the simple fields.
        candidatePK = mapNonRelationshipFields(mapping, mce,
            beanName, schema, knownTables);

        createMappingForUnknownPKClass(mapping, mce,
            beanName, candidatePK);

        createMappingForConsistency(mapping, mce,
            beanName, knownTables);

        // map the relationship fields.
        mapRelationshipFields(mapping, mce,
            beanName, schema, knownTables, tablesOfBean);

        // map any unmapped fields.
        mapUnmappedFields(mce, beanName);

        return mce;
    }

    /** Create field mapping for unknown primary key
     * @param mapping The schema2beans object that represents the mapping
     * for a particular bean.
     * @param mce Mapping class element
     * @param beanName Bean name
     * @param candidatePK A ColumnElement object which is a candidate column for
     * mapping to primary key field if it is unknown primary key
     * @throws ModelException
     * @throws ConversionException
     */
    private void createMappingForUnknownPKClass(EntityMapping mapping,
                                                MappingClassElement mce,
                                                String beanName,
                                                ColumnElement candidatePK)
        throws ModelException, ConversionException {
        String primaryTableName = mapping.getTableName();

        if (helper.generateFields()
            && helper.applyDefaultUnknownPKClassStrategy(beanName)) {
            if (null != candidatePK) {
                String fieldName = helper.getGeneratedPKFieldName();

                // Fix later. Since mapping and persistence classes in the
                // cache are only skeletons and not the one we want,
                // put pce in a map at pce creation time for persistence
                // class look up later to avoid a cast
                // to MappingClassElementImpl.
                PersistenceFieldElement pfe = createPersistenceField(mce,
                    fieldName);
                pfe.setKey(true);
                MappingFieldElement mfe = createMappingField(mce, fieldName,
                    candidatePK);
            }
            else {
                // There is no column which meets primary key criteria.
                // Report error.
                throw new ConversionException(
                    I18NHelper.getMessage(
                    messages,
                    "WARN_NO_PKCOLUMN", //NOI18N
                    primaryTableName));
            }
        }
    }

    /** Load the consistency information. if it is version consistency, create
     * field mapping for version columns.
     * @param mapping The schema2beans object that represents the mapping
     * for a particular bean.
     * @param mce Mapping class element
     * @param beanName Bean name
     * @param knownTables A Map which contains primary and secondary tables
     * for beans in the set. Keys: table names Values: TableElement objects
     * @throws ModelException
     * @throws DBException
     * @throws ConversionException
     */
    private void createMappingForConsistency(EntityMapping mapping,
                                             MappingClassElement mce,
                                             String beanName,
                                             Map knownTables)
        throws ModelException, DBException, ConversionException {

        // load the consistency information
        List versionColumns = loadConsistency(mapping, mce);

        // If the consistency level is version consistency, the version field
        // is always created, independent of the ConversionHelper's value for
        // generateFields.  This is because a generated version field is
        // needed to hold the version column information.
        if (versionColumns != null) {

            String primaryTableName = mapping.getTableName();

            // For 8.1 release, we only support one version column per bean
            if (versionColumns.size() > 1) {
                throw new ConversionException(
                    I18NHelper.getMessage(
                    messages,
                    "ERR_INVALID_VERSION_COLUMNS")); //NOI18N
            }
            String versionColumn = (String)versionColumns.get(0);

            String sourceTableName = getTableName(versionColumn,
                    primaryTableName);

            // we do not support version column from secondary table
            // in 8.1 release
            if (!sourceTableName.equals(primaryTableName)) {
                throw new ConversionException(
                    I18NHelper.getMessage(
                    messages,
                    "WARN_VERSION_COLUMN_INVALID_TABLE", //NOI18N
                    primaryTableName, beanName, versionColumn));
            }

            TableElement sourceTableEl = (TableElement) knownTables.get(
                    sourceTableName);
            String versionColumnName = getColumnName(versionColumn);
            ColumnElement versionCol = getColumnElement(sourceTableEl,
                    DBIdentifier.create(versionColumnName), helper);

            if (null != versionCol) {

                // Since 8.1 release only support one version column per bean,
                // use prefix as the version  field name
                String fieldName = helper.getGeneratedVersionFieldNamePrefix();
                PersistenceFieldElement pfe = createPersistenceField(mce,
                    fieldName);
                MappingFieldElement mfe = createMappingField(mce, fieldName,
                    versionCol);
                mfe.setVersion(true);
            }
            else {
                // There is no version column.
                // Report error.
                throw new ConversionException(
                    I18NHelper.getMessage(
                    messages,
                    "WARN_VERSION_COLUMN_MISSING", //NOI18N
                    primaryTableName, beanName));
            }
        }
    }

    /** Map simple cmp field to MappingFieldElement
     * @param mapping The schema2beans object that represents the mapping
     * for a particular bean.
     * @param mce Mapping class element
     * @param beanName Bean name
     * @param schema dbschema information for all beans
     * @param knownTables A Map which contains primary and secondary tables
     * for beans in the set. Keys: table names Values: TableElement objects
     * @return candidate primary key column for unknown
     * primary key class mapping, if applicable
     * @throws DBException
     * @throws ModelException
     * @throws ConversionException
     */
    private ColumnElement mapNonRelationshipFields(EntityMapping mapping,
                                                   MappingClassElement mce,
                                                   String beanName,
                                                   SchemaElement schema,
                                                   Map knownTables)
        throws DBException, ModelException, ConversionException {

        CmpFieldMapping [] mapOfFields = mapping.getCmpFieldMapping();
        String primaryTableName = mapping.getTableName();
        ColumnElement candidatePK = null;

        // get candidate column only used for unknown primary key
        if (helper.generateFields()
            && helper.applyDefaultUnknownPKClassStrategy(beanName)) {

            candidatePK = getCandidatePK(schema, primaryTableName);
        }

        for (int i = 0; i < mapOfFields.length; i++) {
            CmpFieldMapping mapForOneField = mapOfFields[i];

            String fieldName = mapForOneField.getFieldName();
            if (!validateField(mce, beanName, fieldName,
                helper.ensureValidation())) {
                if (logger.isLoggable(Logger.FINE))
                    logger.fine(
                        I18NHelper.getMessage(
                        messages,
                        "WARN_INVALID_FIELD", //NOI18N
                        beanName, fieldName));

                continue;
            }

            String columnNames[] = mapForOneField.getColumnName();
            MappingFieldElement mfe = createUnmappedField(mce, beanName,
                fieldName);
            boolean fieldMappedToABlob = false;

            for (int j = 0; j < columnNames.length; j++) {
                String sourceTableName = getTableName(columnNames[j],
                    primaryTableName);
                if (null == sourceTableName) {
                    throw new ConversionException(
                        I18NHelper.getMessage(
                        messages,
                        "ERR_UNDEFINED_TABLE")); //NOI18N
                }

                String sourceColumnName = getColumnName(columnNames[j]);

                // get the table element
                TableElement sourceTableEl = getTableElement(sourceTableName,
                    knownTables, schema);
                ColumnElement aCol = getColumnElement(sourceTableEl,
                    DBIdentifier.create(sourceColumnName), helper);
                if (logger.isLoggable(Logger.FINE))
                    logger.fine(
                        I18NHelper.getMessage(
                        messages,
                        "MESSAGE_ADD_COLUMN", //NOI18N
                        new Object [] {aCol, fieldName}));

                // If candidatePK is mapped to a field, then it can not
                // treated as a primary key for unknown primary key
                if (helper.generateFields()
                    && helper.applyDefaultUnknownPKClassStrategy(beanName)) {
                    if (candidatePK != null && candidatePK.equals(aCol)) {
                        candidatePK = null;
                    }
                }

                fieldMappedToABlob |= aCol.isBlobType();
                mfe.addColumn(aCol);
            }

            FetchedWith fw = mapForOneField.getFetchedWith();
            setFetchGroup(fw, mfe, beanName, fieldMappedToABlob);
            mce.addField(mfe);
        }

        return candidatePK;
    }

    /**
     * Converts entity mapping relationship information from sun-cmp-mappings.xml
     * into JDO mapping file information.
     *
     * @param mapping The schema2beans object that represents the mapping
     * for a particular bean.
     * @param mce Mapping class element being populated corresponding to the bean.
     * @param beanName Bean name.
     * @param knownTables A Map which contains primary and secondary tables
     * for beans in the set. Keys: table names Values: TableElement objects
     * @param tablesOfBean contains primary table and
     * secondary tables of the bean beanName.
     * @throws ModelException
     * @throws DBException
     * @throws ConversionException
     */
    private void mapRelationshipFields(EntityMapping mapping,
                                       MappingClassElement mce,
                                       String beanName,
                                       SchemaElement schema,
                                       Map knownTables,
                                       List tablesOfBean)
        throws ModelException, DBException, ConversionException {

        String primaryTableName = mapping.getTableName();
        CmrFieldMapping mapOfRelations[] = mapping.getCmrFieldMapping();
        PersistenceClassElement pce = ((MappingClassElementImpl)mce).getPersistenceElement();

        for (int i = 0; mapOfRelations != null && i < mapOfRelations.length;
                i++) {
            CmrFieldMapping aRelation = mapOfRelations[i];
            String fieldName = aRelation.getCmrFieldName();

            if (!validateField(mce, beanName, fieldName, helper.ensureValidation())) {
                if (logger.isLoggable(Logger.FINE))
                    logger.fine(
                        I18NHelper.getMessage(
                        messages,
                        "WARN_INVALID_CMRFIELD", //NOI18N
                        beanName, fieldName));
                continue;
            }

            RelationshipElement rel = new RelationshipElement(
                new RelationshipElementImpl(fieldName), pce);
            MappingRelationshipElement mre =
                new MappingRelationshipElementImpl(fieldName, mce);

            // Register the inverse RelationshipElement
            registerInverseRelationshipElement(rel, beanName, fieldName);

            final ColumnPair pairs[] = aRelation.getColumnPair();
            final String relationId = mapping.getEjbName() + "_Relationship_" + i; //NOI18N
            Collection primaryTableColumns = convertToColumnPairElements(
                    pairs,
                    primaryTableName, schema, knownTables, tablesOfBean,
                    relationId,
                    mre);

            setUpperBound(rel, beanName, fieldName);
            setCascadeDeleteAction(rel, beanName, fieldName);
            setLowerBound(rel,
                    primaryTableColumns,
                    primaryTableName,schema, knownTables,
                    beanName, fieldName);
            setFetchGroup(aRelation.getFetchedWith(), mre, beanName, false);

            pce.addField(rel);
            mce.addField(mre);
        }
    }

    /** Map unmapped field to mapping field object.
     * @param mce Mapping class element
     * @param beanName Bean name
     * @throws ModelException
     */
    private void mapUnmappedFields(MappingClassElement mce, String beanName)
        throws ModelException {

        Object[] fields = helper.getFields(beanName);
        if (!helper.ensureValidation()) {
            for (int i = 0; i < fields.length; i++) {
                String fieldName = (String) fields[i];
                MappingFieldElement mfe = mce.getField(fieldName);

                if (null == mfe) {

                    // this field needs a mapping created for it...
                    mfe = createUnmappedField(mce, beanName, fieldName);
                    mce.addField(mfe);
                }
            }
        }
    }

    /** Set consistency from MappingClassElement into schema2beans
     * Consistency bean.
     * @param mce An object that represents the mapping object
     * @param beanMapping The schema2beans object that represents the mapping
     * for a particular bean.
     */
    private void setConsistency(MappingClassElement mce,
        EntityMapping beanMapping) {
        int consistency = mce.getConsistencyLevel();
        if (MappingClassElement.NONE_CONSISTENCY != consistency) {
            Consistency c = new Consistency();
            if (MappingClassElement.LOCK_WHEN_MODIFIED_CHECK_ALL_AT_COMMIT_CONSISTENCY == consistency) {
                c.setLockWhenModified(true);
                c.setCheckAllAtCommit(true);
            }
            if (MappingClassElement.LOCK_WHEN_MODIFIED_CONSISTENCY == consistency)
                c.setLockWhenModified(true);
            if (MappingClassElement.CHECK_ALL_AT_COMMIT_CONSISTENCY == consistency)
                c.setCheckAllAtCommit(true);
            if (MappingClassElement.LOCK_WHEN_LOADED_CONSISTENCY  == consistency)
                c.setLockWhenLoaded(true);
            if (MappingClassElement.CHECK_MODIFIED_AT_COMMIT_CONSISTENCY == consistency)
                c.setCheckModifiedAtCommit(true);
            if (MappingClassElement.VERSION_CONSISTENCY == consistency) {
                CheckVersionOfAccessedInstances versionIns =
                    new CheckVersionOfAccessedInstances();
                Iterator iter = mce.getVersionFields().iterator();
                while (iter.hasNext()) {
                    List columnNames = ((MappingFieldElement)iter.next()).
                        getColumns();

                    // vesion field only allow to map to one column
                    if (columnNames != null && columnNames.size() > 0)
                        versionIns.addColumnName((String)columnNames.get(0));
                }
                c.setCheckVersionOfAccessedInstances(versionIns);
            }
            beanMapping.setConsistency(c);
        }
    }

    /** Load consistency from schema2beans into MappingClassElement
     * @param mapping The schema2beans object that represents the mapping
     * for a particular bean.
     * @param mce An object that represents mapping object
     * @return a list of version column names if applicable; return null
     * otherwise.
     * @throws ModelException
     * @throws ConversionException
     */
    private List loadConsistency(EntityMapping mapping, MappingClassElement mce)
        throws ModelException, ConversionException {
        Consistency c = mapping.getConsistency();
        if (null == c) {
            mce.setConsistencyLevel(MappingClassElement.NONE_CONSISTENCY);
        }
        else {
            CheckVersionOfAccessedInstances versionIns =
                c.getCheckVersionOfAccessedInstances();

            if (c.isCheckModifiedAtCommit())
                mce.setConsistencyLevel(
                    MappingClassElement.CHECK_MODIFIED_AT_COMMIT_CONSISTENCY);
            else if(c.isLockWhenLoaded())
                mce.setConsistencyLevel(
                    MappingClassElement.LOCK_WHEN_LOADED_CONSISTENCY);
            else if(c.isCheckAllAtCommit())
                mce.setConsistencyLevel(
                    MappingClassElement.CHECK_ALL_AT_COMMIT_CONSISTENCY);
            else if(c.isLockWhenModified())
                mce.setConsistencyLevel(
                    MappingClassElement.LOCK_WHEN_MODIFIED_CONSISTENCY);
            else if(c.isLockWhenModified() && c.isCheckAllAtCommit())
                mce.setConsistencyLevel(MappingClassElement.
                    LOCK_WHEN_MODIFIED_CHECK_ALL_AT_COMMIT_CONSISTENCY);
            else if(c.isNone())
                mce.setConsistencyLevel(MappingClassElement.NONE_CONSISTENCY);
            else if (versionIns != null) {
                mce.setConsistencyLevel(MappingClassElement.VERSION_CONSISTENCY);
                List versionColumns = new ArrayList();
                for (int i = 0; i < versionIns.sizeColumnName(); i++) {
                    versionColumns.add(versionIns.getColumnName(i));
                }
                return versionColumns;
            }
            else {
                throw new ConversionException(
                    I18NHelper.getMessage(
                    messages,
                    "ERR_INVALID_CONSISTENCY_VALUE", mce)); //NOI18N
            }
        }
        return null;
    }

    /** Map the primary table of the bean.
     * @param mapping The schema2beans object that represents the mapping
     * for a particular bean.
     * @param mce Mapping class element
     * @param schema dbschema information for all beans
     * @param knownTables A Map which contains primary and secondary tables
     * for beans in the set. Keys: table names Values: TableElement objects
     * @param tablesOfBean contains primary table and
     * secondary tables of the bean corresponding to the mapping
     * @throws DBException
     * @throws ModelException
     * @throws ConversionException
     */
    private void mapPrimaryTable(EntityMapping mapping,
                                  MappingClassElement mce,
                                  SchemaElement schema,
                                  Map knownTables,
                                  List tablesOfBean)
        throws DBException, ModelException, ConversionException {

        String primaryTableName = mapping.getTableName();
        TableElement primTabEl = getTableElement(primaryTableName, knownTables, schema);

        mce.addTable(primTabEl);
        tablesOfBean.add(primaryTableName);
    }

    /** Get the candidate pk column for unknown primary key.
     * @param schema dbschema information for all beans
     * @param primaryTableName Primary table name
     * @return candidate pk column which will be used for unknown primary key,
     * if applicable
     * @throws DBException
     * @throws ConversionException
     */
    private ColumnElement getCandidatePK(SchemaElement schema,
                                         String primaryTableName)
        throws DBException, ConversionException {
        ColumnElement candidatePK = null;

        TableElement primTabEl = getTableElement(schema,
            DBIdentifier.create(primaryTableName), helper);
        // Check if the candidatePK is really satisfying primary key
        // criteria. It will be used only for unknown primary key.
        UniqueKeyElement uke = primTabEl.getPrimaryKey();

        if (null != uke) {
            ColumnElement cols[] = uke.getColumns();
            if (null != cols && 1 == cols.length) {
                candidatePK = cols[0];
                if (logger.isLoggable(Logger.FINE))
                    logger.fine(
                        I18NHelper.getMessage(
                        messages,
                        "MESSAGE_CANDIDATE_PK", //NOI18N
                        candidatePK.getName()));

                Integer pre = candidatePK.getPrecision();
                if (null != candidatePK && !candidatePK.isNumericType()) {
                    candidatePK = null;
                }
                if (null != candidatePK && (null != pre) && pre.intValue() < MINIMUM_PRECISION) {
                    candidatePK = null;
                }
            }
        }
        return candidatePK;
    }

    /**
     * Converts column pair information from sun-cmp-mappings.xml into
     * column pair elements from the JDO mapping definition.
     *
     * @param pairs ColumnPair information from sun-cmp-mappings.xml.
     * @param primaryTableName Name of the bean's primary table.
     * @param schema dbschema information for all beans.
     * @param knownTables A Map which contains primary and secondary tables
     * for beans in the set. Keys: table names Values: TableElement objects
     * @param tablesOfBean contains primary table and
     * secondary tables of the bean corresponding to the mre's
     * declaring class.
     * @param relationId Relationship id used to name the ColumnPairElements.
     * @param mre Mapping relationship element (== JDO information).
     * @return Collection of column elements, including all columns from the
     * current cmr definition that are part of the primary table.
     * @throws DBException
     * @throws ModelException
     * @throws ConversionException
     */
    private Collection convertToColumnPairElements(ColumnPair pairs[],
                                                   String primaryTableName,
                                                   SchemaElement schema,
                                                   Map knownTables,
                                                   List tablesOfBean,
                                                   String relationId,
                                                   MappingRelationshipElement mre)
            throws DBException, ModelException, ConversionException {

        Collection primaryTableColumns = new ArrayList();
        boolean isJoin = false;

        for (int i = 0; null != pairs && i < pairs.length; i++) {
            ColumnPair pair = pairs[i];
            ColumnPairElement cpe = new ColumnPairElement();
            boolean localSet = false;

            cpe.setName(DBIdentifier.create(relationId + "_ColumnPair_" + i)); //NOI18N

            for (int j = 0; j < 2; j++) {
                String columnName = pair.getColumnName(j);
                String sourceTableName = getTableName(columnName,
                    primaryTableName);
                String sourceColumnName = getColumnName(columnName);
                TableElement sourceTableEl = getTableElement(sourceTableName,
                    knownTables, schema);
                ColumnElement ce = getColumnElement(sourceTableEl,
                    DBIdentifier.create(sourceColumnName), helper);
                ce.toString();

                // tablesOfBean stores the primary table and the secondary
                // tables for the bean.
                // It can be used for checking join table if sourceTableName
                // is not in the tablesOfBean.
                // If it is join table, should use addLocalColumn instead of
                // addColumn.
                if (j == 0) {
                    if (tablesOfBean.contains(sourceTableName)) {
                        localSet = true;

                        // Remember local columns for lower bound determination.
                        primaryTableColumns.add(ce);
                    }
                    else {

                        // join table
                        isJoin = true;
                        localSet = false;
                    }
                }

                if (cpe.getLocalColumn() == null) {
                    cpe.setLocalColumn(ce);
                }
                else {
                    cpe.setReferencedColumn(ce);
                }
            }

            if (localSet) {
                if (!isJoin) {
                    mre.addColumn(cpe);
                }
                else {
                    mre.addLocalColumn(cpe);
                }
            }
            else if (isJoin) {
                mre.addAssociatedColumn(cpe);
            }
        }

        return primaryTableColumns;
    }

    /**
     * If a non-collection relationship field is mapped to a
     * non-nullable column, the lower bound of the relationship might
     * be set to 1. To set the lower bound, we have to determine the
     * dependent side of the relationship. We set the lower bound to 1
     * based on the following rules:
     *
     * 
    *
  • If the non-nullable column is not part of the primary key.
  • *
  • If the local side has got a foreign key.
  • *
  • If the local columns are a real subset of the primary key.
  • *
  • If the user specified the local side for cascade delete.
  • *
* * @param rel JDO relationship information being constructed. * @param primaryTableColumns Collection of all columns from the * current cmr definition that are part of the primary table. * @param primaryTableName Name of the bean's primary table. * @param knownTables A Map which contains primary and secondary tables * for beans in the set. Keys: table names Values: TableElement objects * @param schema dbschema information for all beans. * @param beanName Bean name. * @param fieldName Relationship field name. * @throws ModelException * @throws DBException * @throws ConversionException */ private void setLowerBound(RelationshipElement rel, Collection primaryTableColumns, String primaryTableName, SchemaElement schema, Map knownTables, String beanName, String fieldName) throws ModelException, DBException, ConversionException { rel.setLowerBound(0); if (logger.isLoggable(Logger.FINE)) logger.fine( I18NHelper.getMessage( messages, "MESSAGE_LWB_NULL", //NOI18N beanName, fieldName)); if (1 == rel.getUpperBound() && null != primaryTableName) { boolean isPartOfPrimaryKey = false; TableElement primaryTable = getTableElement(primaryTableName, knownTables, schema); UniqueKeyElement pk = primaryTable.getPrimaryKey(); ForeignKeyElement fks[] = primaryTable.getForeignKeys(); Iterator iter = primaryTableColumns.iterator(); while (iter.hasNext() && 0 == rel.getLowerBound()) { ColumnElement ce = (ColumnElement) iter.next(); if (!ce.isNullable()) { isPartOfPrimaryKey |= isPartOfPrimaryKey(ce, pk); if (!isPartOfPrimaryKey) { // We suppose that all primary key columns are non-nullable. // If the non-nullable column is not part of the primary key, // this is the dependent side. rel.setLowerBound(1); if (logger.isLoggable(Logger.FINE)) logger.fine( I18NHelper.getMessage( messages, "MESSAGE_LWB_NOPK", //NOI18N beanName, fieldName)); } // Check the foreign key constraint else if (isPartOfForeignKey(ce, fks)) { // If the non-nullable column is part of the foreign key, // this is the dependent side. rel.setLowerBound(1); if (logger.isLoggable(Logger.FINE)) logger.fine( I18NHelper.getMessage( messages, "MESSAGE_LWB_FK", //NOI18N beanName, fieldName)); } } } if (0 == rel.getLowerBound() && isPartOfPrimaryKey) { // The lower bound is still unset and all local columns // are part of the primary key. if (primaryTableColumns.size() < pk.getColumns().length) { // The local columns are a real subset of the primary key. // ==> This must be the dependent side. rel.setLowerBound(1); if (logger.isLoggable(Logger.FINE)) logger.fine( I18NHelper.getMessage( messages, "MESSAGE_LWB_PKSUBSET", //NOI18N beanName, fieldName)); } else if (isCascadeDelete(beanName, fieldName)) { // This relationship side is marked as dependent side by the user. rel.setLowerBound(1); if (logger.isLoggable(Logger.FINE)) logger.fine( I18NHelper.getMessage( messages, "MESSAGE_LWB_CASCADE", //NOI18N beanName, fieldName)); } else { if (logger.isLoggable(Logger.FINE)) logger.fine( I18NHelper.getMessage( messages, "MESSAGE_LWB_NODEPENDENT", //NOI18N beanName, fieldName)); } } } } /** * Looks up the table element for tableName in the * table element cache knownTables. If the table * element is not found, the table name is looked up in the * database schema and registered in the cache. * * @param tableName Table name. * @param knownTables A Map which contains primary and secondary tables * for beans in the set. Keys: table names Values: TableElement objects * @param schema dbschema information for all beans. * @return Table element for table tableName. * @exception DBException * @exception ConversionException */ private TableElement getTableElement(String tableName, Map knownTables, SchemaElement schema) throws DBException, ConversionException { TableElement te = (TableElement) knownTables.get(tableName); if (null == te) { te = getTableElement(schema, DBIdentifier.create(tableName), helper); knownTables.put(tableName, te); } return te; } /** * Checks, if the column ce is part of the * primary key pk. * RESOLVE: Method isPrimaryKeyColumn in ModelValidator * implements similar functionality. * * @param ce Column element for the column to be tested. * @param pk Primary key element. The column to be tested * must be defined in the same table. * @return True, if the column is part of the primary key, * false otherwise. * @see com.sun.jdo.api.persistence.model.util.ModelValidator */ private boolean isPartOfPrimaryKey(ColumnElement ce, UniqueKeyElement pk) { return null != pk && ce.equals(pk.getColumn(ce.getName())); } /** * Checks, if the column ce is part of one * of the foreign key constraints defined in fks. * RESOLVE: Method matchesFK in ModelValidator implements similar * functionality. * * @param ce Column element for the column to be tested. * @param fks Array of foreign key elements. The column to be * tested must be defined in the same table. * @return True, if the column is part of one of the foreign keys, * false otherwise. * @see com.sun.jdo.api.persistence.model.util.ModelValidator */ private boolean isPartOfForeignKey(ColumnElement ce, ForeignKeyElement[] fks) { // RESOLVE: Column ce might be included in multiple foreign // keys. The foreign key check applies to ALL relationships // mapped to column ce. How can we find out, that a foreign // key matches exactly the relationship being checked here? if (fks != null) { for (int index = 0; index < fks.length; index++) { if (ce.equals(fks[index].getColumn(ce.getName()))) { // The current ce is part of the foreign key. return true; } } } return false; } /** * Returns the cascade delete setting for the current relationship side. * The ConversionHelper interface provides cascade delete information * for the related side only. * * @param beanName Bean name. * @param fieldName Relationship field name. * @return True, if the current relationship side is marked for cascade delete, * false otherwise. */ private boolean isCascadeDelete(String beanName, String fieldName) { final String beanInField = helper.getRelationshipFieldContent(beanName, fieldName); final String inverseField = helper.getInverseFieldName(beanName, fieldName); return (null != beanInField && null != inverseField) ? helper.relatedObjectsAreDeleted(beanInField, inverseField) : false; } /** * Registers relationship element rel in the cache * mapping field names to inverse relationship elements. The * inverse bean- and field names for the registration are * determined with the conversion helper. Returns the * relationship element for the inverse field, if this field has * been already processed, null otherwise. * * @param rel JDO relationship information being constructed. * @param beanName Bean name. * @param fieldName Relationship field name. * @return The relationship element for the inverse field, if this field * has been already processed, null otherwise. * @throws ModelException */ private RelationshipElement registerInverseRelationshipElement( RelationshipElement rel, String beanName, String fieldName) throws ModelException { String key = beanName + "." + fieldName; //NOI18N RelationshipElement inverse = (RelationshipElement) inverseRelationships.get(key); if (null == inverse) { final String beanInField = helper.getRelationshipFieldContent( beanName, fieldName); final String inverseField = helper.getInverseFieldName(beanName, fieldName); if (null != beanInField && null != inverseField) { key = beanInField + "." + inverseField; //NOI18N inverseRelationships.put(key, rel); } } else { rel.changeInverseRelationship(inverse); inverse.changeInverseRelationship(rel); } return inverse; } /** * Sets the upper bound for relationship element rel * depending on the upper bound information from the deployment * descriptor and defines the element class for collection * relationship fields. * * @param rel JDO relationship information being constructed. * @param beanName Bean name. * @param fieldName Relationship field name. * @throws ModelException * @throws ConversionException */ private void setUpperBound(RelationshipElement rel, String beanName, String fieldName) throws ModelException, ConversionException { String beanInField = helper.getRelationshipFieldContent(beanName, fieldName); String classInJdoField = helper.getMappedClassName(beanInField); String multiplicity = helper.getMultiplicity(beanName, fieldName); // Set the upper bound. if (multiplicity.equals(helper.MANY)) { rel.setUpperBound(Integer.MAX_VALUE); rel.setElementClass(classInJdoField); String collectionClass = helper.getRelationshipFieldType(beanName, fieldName); if (types.contains(collectionClass)) { rel.setCollectionClass(collectionClass); } else { rel.setCollectionClass(null); if (logger.isLoggable(Logger.WARNING)) logger.warning( I18NHelper.getMessage( messages, "WARN_INVALID_RELATIONSHIP_FIELDTYPE", //NOI18N beanName, fieldName, collectionClass)); } } else if (multiplicity.equals(helper.ONE)) { rel.setUpperBound(1); // Fix later. This code should be removed because in one side // setElementClass should not be called. // This line of code is for bug 4665051 which is plugin bug. // It is likely that bug 4665051 indicates that there is code // in the plugin which depends on the element class being set // for a one side relationship. rel.setElementClass(classInJdoField); } else { throw new ConversionException( I18NHelper.getMessage( messages, "ERR_BAD_MULTIPLICTY", //NOI18N multiplicity, rel.getName())); } } /** * Sets the cascade delete option for relationship element * rel depending on the information from the * deployment descriptor. While the deployment descriptor * specifies cascade delete option on the dependent side, JDO * specifies it on the primary side. For this reason, the * information must be "switched". * * @param rel JDO relationship information being constructed. * @param beanName Bean name. * @param fieldName Relationship field name. * @throws ModelException */ private void setCascadeDeleteAction(RelationshipElement rel, String beanName, String fieldName) throws ModelException { if (helper.relatedObjectsAreDeleted(beanName, fieldName)) { if (logger.isLoggable(Logger.FINE)) logger.fine( I18NHelper.getMessage( messages, "MESSAGE_REL_OBJ_DEL", //NOI18N beanName, fieldName)); rel.setDeleteAction(RelationshipElement.CASCADE_ACTION); } } private SchemaElement setDatabaseRoot(MappingClassElement foo, String schemaElementValue, boolean strict) throws ModelException, DBException, ConversionException { SchemaElement bar = null; if (null != classLoader) { if (loadedSchema.get(schemaElementValue) == null) { SchemaElement.removeFromCache(schemaElementValue); loadedSchema.put(schemaElementValue, schemaElementValue); } bar = SchemaElement.forName(schemaElementValue,classLoader); } else bar = SchemaElement.forName(schemaElementValue); if (strict) { if (bar == null) { // Prepare for a schema related error throw new ConversionException( I18NHelper.getMessage( messages, "ERR_CANNOT_FIND_SCHEMA", //NOI18N new Object [] {schemaElementValue, classLoader})); } } else { if (null == bar) { // conjure up a schema element and set its name... // need to create a dummy for invalid mappings because the // mapping model setter methods don't accept strings even // though that is what they store. bar = new SchemaElement(); DBIdentifier n = DBIdentifier.create(schemaElementValue); n.setFullName(schemaElementValue); bar.setName(n); } } foo.setDatabaseRoot(bar); return bar; } private String getTableName(String columnName, String defaultName) { String retVal = defaultName; int len = columnName.lastIndexOf('.'); if (len > 0) retVal = columnName.substring(0,len); return retVal; } private String getColumnName(String columnName) { String retVal = columnName; int len = columnName.lastIndexOf('.'); if (len > 0) retVal = columnName.substring(len+1); return retVal; } /** Map the secondary tables of the bean. * @param mapping The schema2beans object that represents the mapping * for a particular bean. * @param mce Mapping class element * @param schema dbschema information for all beans * @param knownTables A Map which contains primary and secondary tables * for beans in the set. Keys: table names Values: TableElement objects * @param tablesOfBean contains primary table and * secondary tables of the bean corresponding to the mapping * @throws ModelException * @throws DBException * @throws ConversionException */ private void mapSecondaryTables(EntityMapping mapping, MappingClassElement mce, SchemaElement schema, Map knownTables, List tablesOfBean) throws ModelException, DBException, ConversionException { SecondaryTable [] tableList = mapping.getSecondaryTable(); List tl = mce.getTables(); if (null != tl && tl.size() > 0 && null != tl.get(0)) { MappingTableElement primary = (MappingTableElement) tl.get(0); for (int i = 0; null != tableList && i < tableList.length; i++) { String tn = tableList[i].getTableName(); if (StringHelper.isEmpty(tn)) continue; TableElement te = getTableElement(schema, DBIdentifier.create(tn.trim()), helper); ColumnPair pairs[] = tableList[i].getColumnPair(); int len = 0; if (null != pairs) len = pairs.length; if (0 == len) { if (logger.isLoggable(Logger.WARNING)) logger.warning( I18NHelper.getMessage( messages, "WARN_NO_PAIRS", //NOI18N new Object [] {mce, tn})); continue; } MappingReferenceKeyElement mrke = mce.addSecondaryTable( primary,te); for (int j = 0; null != pairs && j < pairs.length; j++) { ColumnPairElement cpe = new ColumnPairElement(); DBIdentifier dbId = DBIdentifier.create("SecondaryTable"+j); //NOI18N cpe.setName(dbId); ColumnPair pair = pairs[j]; for (int k = 0; k < 2; k++) { String nameOne = pair.getColumnName(k); String sourceTableName = getTableName( nameOne.trim(), primary.getName().toString()); String sourceColumnName = getColumnName(nameOne); dbId = DBIdentifier.create(sourceTableName); TableElement sourceTableEl = getTableElement(schema, dbId, helper); dbId = DBIdentifier.create(sourceColumnName); ColumnElement ce = getColumnElement(sourceTableEl, dbId, helper); if (k == 0) cpe.setLocalColumn(ce); else cpe.setReferencedColumn(ce); } mrke.addColumnPair(cpe); } knownTables.put(tn,te); tablesOfBean.add(tn); } } else throw new ConversionException( I18NHelper.getMessage( messages, "WARN_NOT_MAPPED_TO_PRIMARY", //NOI18N mce.getName())); } private boolean validateField(MappingClassElement mce, String beanName, String fieldName, boolean throwEx) throws ConversionException { MappingFieldElement mfe = mce.getField(fieldName); if (null != mfe) { if (throwEx) throw new ConversionException( I18NHelper.getMessage( messages, "ERR_FIELD_MAPPED_TWICE", //NOI18N beanName, fieldName)); else return false; } if (!helper.hasField(beanName,fieldName)) { if (throwEx) throw new ConversionException( I18NHelper.getMessage( messages, "ERR_INVALID_FIELD", //NOI18N beanName, fieldName)); else return false; } return true; } // loop through the mappings to create a hash from bean name to em objects private Map getBean2EntityMappingMap(SunCmpMapping beanSet) { Map retVal = new HashMap(); EntityMapping [] entityMappingsInSet = beanSet.getEntityMapping(); int len = 0; if (null != entityMappingsInSet) len = entityMappingsInSet.length; for (int k = 0; k < len; k++) { EntityMapping anEntityMapping = entityMappingsInSet[k]; String beanName = anEntityMapping.getEjbName(); beanName.trim().charAt(0); retVal.put(beanName,anEntityMapping); } return retVal; } // for each cmr field in the mapping // determine is the inverse is a generated field // create the mapping data for this generated field private boolean completeCmrMappings(SunCmpMapping beanSet) throws ConversionException { // loop through the mappings to create a hash from bean name to em objects Map beanName2EntityMapping = getBean2EntityMappingMap(beanSet); Iterator emIter = beanName2EntityMapping.values().iterator(); boolean retVal = false; String errorMsg = I18NHelper.getMessage( messages, "ERR_BAD_CONVERSION_HELPER"); //NOI18N while (emIter.hasNext()) { EntityMapping anEM = (EntityMapping) emIter.next(); String beanName = anEM.getEjbName(); String pt = anEM.getTableName(); CmrFieldMapping[] cmrsInEM = anEM.getCmrFieldMapping(); int len = 0; if (null != cmrsInEM && !StringHelper.isEmpty(beanName)) len = cmrsInEM.length; for (int i = 0; i < len; i++) { String fieldName = cmrsInEM[i].getCmrFieldName(); if (!helper.hasField(beanName, fieldName)) { throw new ConversionException(I18NHelper.getMessage( messages, "WARN_INVALID_CMRFIELD", //NOI18N beanName, fieldName)); } fieldName.trim().charAt(0); String otherField = helper.getInverseFieldName(beanName, fieldName); if (otherField == null) { throw new ConversionException(errorMsg); } String otherBean = helper.getRelationshipFieldContent(beanName, fieldName); if (otherBean == null) { throw new ConversionException(errorMsg); } if (helper.isGeneratedRelationship(otherBean,otherField)) { retVal = true; String otherBeanName = helper.getRelationshipFieldContent( beanName, fieldName); otherBeanName.trim().charAt(0); EntityMapping otherEM = (EntityMapping) beanName2EntityMapping.get( otherBeanName); CmrFieldMapping inverseMapping = new CmrFieldMapping(); inverseMapping.setCmrFieldName(otherField); inverseMapping.setColumnPair( reverseCPArray(cmrsInEM[i].getColumnPair(), pt, beanName, fieldName)); otherEM.addCmrFieldMapping(inverseMapping); } } } return retVal; } private ColumnPair[] reverseCPArray(ColumnPair[] cpa, String primeTable, String beanName, String fieldName) throws ConversionException { int len = (cpa == null) ? 0 : cpa.length; if (len == 0) { throw new ConversionException( I18NHelper.getMessage( messages, "ERR_COLUMN_PAIR_MISSING", //NOI18N beanName, fieldName)); } ColumnPair [] retVal = new ColumnPair[len]; for (int index = 0; index < len; index++) { retVal[index] = new ColumnPair(); retVal[index].addColumnName( qualify(primeTable,cpa[index].getColumnName(1))); retVal[index].addColumnName( qualify(primeTable,cpa[index].getColumnName(0))); } return retVal; } private String qualify(String tn, String cn) { int tmp = cn.indexOf('.'); String retVal = cn; if (-1 == tmp) retVal = tn + "." + cn; // NOI18N return retVal; } private TableElement getTableElement(SchemaElement schema, DBIdentifier dbId, ConversionHelper helper) throws DBException, ConversionException { TableElement retVal = ((schema != null) ? schema.getTable(dbId) : null); if (null == retVal && !helper.ensureValidation()) { // Need to create a dummy for invalid mappings because // the mapping model setter methods don't accept // strings even though that is what they store. // Create the table and add it to the knownTables list // for later retVal = new TableElement(); retVal.setName(dbId); retVal.setDeclaringSchema(schema); org.netbeans.modules.dbschema.UniqueKeyElement tkey = new org.netbeans.modules.dbschema.UniqueKeyElement(); ColumnElement fakeKeyCol = new ColumnElement(); fakeKeyCol.setName(DBIdentifier.create(retVal.getName().getName()+ "."+"fookeyng")); //NOI18N // Type numeric=2 fakeKeyCol.setType(2); fakeKeyCol.setPrecision(Integer.valueOf(MINIMUM_PRECISION)); tkey.setPrimaryKey(true); tkey.addColumn(fakeKeyCol); retVal.addColumn(fakeKeyCol); retVal.addKey(tkey); } if (retVal == null) { throw new ConversionException( I18NHelper.getMessage( messages, "ERR_INVALID_TABLE", //NOI18N new Object [] {dbId.getName(), schema})); } return retVal; } private ColumnElement getColumnElement(TableElement sourceTableEl, DBIdentifier sourceColumnName, ConversionHelper helper) throws DBException, ConversionException { ColumnElement aCol = sourceTableEl.getColumn(sourceColumnName); if (null == aCol && !helper.ensureValidation()) { aCol = new ColumnElement(); aCol.setName(DBIdentifier.create(sourceTableEl.getName().toString()+"."+sourceColumnName.toString())); // NOI18N aCol.setDeclaringTable(sourceTableEl); aCol.setNullable(true); } if (aCol == null) { throw new ConversionException( I18NHelper.getMessage( messages, "ERR_INVALID_COLUMN", //NOI18N new Object [] {sourceColumnName, sourceTableEl})); } return aCol; } private MappingFieldElement createUnmappedField(MappingClassElement mce, String beanName, String fieldName) throws ModelException { PersistenceClassElement pce = ((MappingClassElementImpl)mce). getPersistenceElement(); PersistenceFieldElementImpl pfei = new PersistenceFieldElementImpl(fieldName); PersistenceFieldElement pfe = new PersistenceFieldElement(pfei, pce); pfe.setKey(helper.isKey(beanName,fieldName,false)); pce.addField(pfe); MappingFieldElement mfe = new MappingFieldElementImpl(fieldName, mce); return mfe; } /** * Set fetch group level for mapping field and mapping relationship * element. If there is fetch level information, then set mapping field * element with the level. If there is no fetch group level information, * set mapping field element to GROUP_NONE if it is mapping relationship * element or blob type field otherwise set to GROUP_DEFAULT. * @param fw An object having fetch group level information * @param mfe An mapping field or mapping relationship element to be set * fetch group level * @param fieldMappedToABlob boolean type to indicate that field is blob * type or not * @throws ModelException * @throws ConversionException */ private void setFetchGroup(FetchedWith fw, MappingFieldElement mfe, String beanName, boolean fieldMappedToABlob) throws ModelException, ConversionException { if (null != fw) { boolean tryLevel = false; int level = 0; try { level = fw.getLevel(); tryLevel = true; } catch (RuntimeException e) { // If there is no level set, schema2beans throws // RuntimeException. // Need to do more investigation on why throws this exception. // it is very likely that there is no level set, which would // throw an exception here.. Which we are going to ignore } if (tryLevel) { if (level < 1) { throw new ConversionException( I18NHelper.getMessage( messages, "ERR_INVALID_FG_LEVEL", //NOI18N beanName, mfe.getName(), ""+level)); //NOI18N } mfe.setFetchGroup(level+1); } String ig = fw.getNamedGroup(); if (null != ig) { Integer fgval = (Integer) namedGroups.get(ig); if (null == fgval) { fgval = new Integer(groupCount--); namedGroups.put(ig,fgval); } mfe.setFetchGroup(fgval.intValue()); } if (fw.isNone()) mfe.setFetchGroup(MappingFieldElement.GROUP_NONE); if (fw.isDefault()) mfe.setFetchGroup(MappingFieldElement.GROUP_DEFAULT); } else { if (mfe instanceof MappingRelationshipElement) mfe.setFetchGroup(MappingFieldElement.GROUP_NONE); else { if (fieldMappedToABlob) mfe.setFetchGroup(MappingFieldElement.GROUP_NONE); else mfe.setFetchGroup(MappingFieldElement.GROUP_DEFAULT); } } } private PersistenceFieldElement createPersistenceField( MappingClassElement mce, String fieldName) throws ModelException { PersistenceClassElement pce = ((MappingClassElementImpl)mce).getPersistenceElement(); PersistenceFieldElementImpl pfei = new PersistenceFieldElementImpl(fieldName); PersistenceFieldElement pfe = new PersistenceFieldElement(pfei, pce); pce.addField(pfe); return pfe; } private MappingFieldElement createMappingField(MappingClassElement mce, String fieldName, ColumnElement col) throws ModelException { MappingFieldElement mfe = new MappingFieldElementImpl(fieldName, mce); mce.addField(mfe); if (col != null) mfe.addColumn(col); return mfe; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy