alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Glassfish example source code file (ClassDesc.java)

This example Glassfish source code file (ClassDesc.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Glassfish tags/keywords

arraylist, arraylist, concurrency, fielddesc, fielddesc, foreignfielddesc, localfielddesc, localfielddesc, noi18n, noi18n, object, reflection, string, tabledesc, updatequeryplan, util

The Glassfish ClassDesc.java source code

/*
 * 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.
 */

/*
 * ClassDesc.java
 *
 * Created on March 3, 2000
 *
 */

package com.sun.jdo.spi.persistence.support.sqlstore.model;

import org.netbeans.modules.dbschema.ColumnElement;
import org.netbeans.modules.dbschema.ColumnPairElement;
import org.netbeans.modules.dbschema.TableElement;
import com.sun.jdo.api.persistence.model.Model;
import com.sun.jdo.api.persistence.model.jdo.ConcurrencyGroupElement;
import com.sun.jdo.api.persistence.model.jdo.PersistenceClassElement;
import com.sun.jdo.api.persistence.model.jdo.PersistenceFieldElement;
import com.sun.jdo.api.persistence.model.jdo.RelationshipElement;
import com.sun.jdo.api.persistence.model.mapping.MappingClassElement;
import com.sun.jdo.api.persistence.model.mapping.MappingFieldElement;
import com.sun.jdo.api.persistence.model.mapping.MappingRelationshipElement;
import com.sun.jdo.api.persistence.model.mapping.impl.*;
import com.sun.jdo.api.persistence.support.*;
import com.sun.jdo.spi.persistence.support.sqlstore.*;
import com.sun.jdo.spi.persistence.support.sqlstore.sql.RetrieveDescImpl;
import com.sun.jdo.spi.persistence.support.sqlstore.sql.UpdateObjectDescImpl;
import com.sun.jdo.spi.persistence.support.sqlstore.sql.concurrency.*;
import com.sun.jdo.spi.persistence.support.sqlstore.sql.generator.UpdateQueryPlan;
import com.sun.jdo.spi.persistence.utility.StringHelper;
import com.sun.jdo.spi.persistence.utility.logging.Logger;
import org.glassfish.persistence.common.I18NHelper;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.*;

/**
 *
 */
public class ClassDesc
        implements com.sun.jdo.spi.persistence.support.sqlstore.PersistenceConfig {

    private ArrayList fetchGroups;

    private int maxHierarchicalGroupID;

    /**
     * Used for batched update check. Set to true if this class has at least one
     * local field that does not belong to DFG
     */
    private boolean hasLocalNonDFGField;

    /** Contains all local and foreign fields. */
    public ArrayList fields;

    /** Contains all hidden fields. */
    public ArrayList hiddenFields;

    /** Contains all relationship fields. */
    public ArrayList foreignFields;

    /** Contains the fields used for version consistency validation. */
    private LocalFieldDesc[] versionFields;

    private ArrayList tables;

    private Class pcClass;

    private Class oidClass;

    public int maxFields;

    public int maxVisibleFields;

    public int maxHiddenFields;

    private Concurrency optimisticConcurrency;

    private Concurrency checkDirtyConcurrency;

    private Concurrency databaseConcurrency;

    private Concurrency explicitConcurrency;

    private MappingClassElementImpl mdConfig;

    private ClassLoader classLoader;

    private Constructor constructor;

    private PersistenceClassElement pcElement;

    private PersistenceFieldElement[] persistentFields;

    private Field keyFields[];
    private String keyFieldNames[];
    private LocalFieldDesc[] keyFieldDescs;

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

    /** RetrieveDescriptor cache for navigation and reloading. */
    private final Map retrieveDescCache = new HashMap();

    /**
     * RetrieveDescriptor cache for navigation queries. This cache
     * holds foreign RetrieveDescriptors constrained by the relationship
     * key values.
     */
    private final Map foreignRetrieveDescCache = new HashMap();

    /** Retrieve descriptor for version consistency verification. */
    private RetrieveDesc retrieveDescForVerification;
    private Object retrieveDescForVerificationSynchObj = new Object();

    /** UpdateQueryPlan cache. */
    private final Map updateQueryPlanCache = new HashMap();

    /** UpdateQueryPlan for insert. */
    private UpdateQueryPlan updateQueryPlanForInsert;
    private Object updateQueryPlanForInsertSynchObj = new Object();

    /** UpdateQueryPlan for delete. */
    private UpdateQueryPlan updateQueryPlanForDelete;
    private Object updateQueryPlanForDeleteSynchObj = new Object();

    /** I18N message handler. */
    private final static ResourceBundle messages = I18NHelper.loadBundle(
            "com.sun.jdo.spi.persistence.support.sqlstore.Bundle", // NOI18N
            ClassDesc.class.getClassLoader());

    /** PC Constructor signature. */
    private static final Class[] sigSM = new Class[]{StateManager.class};

    public ClassDesc(MappingClassElement mdConfig, Class pcClass) {

        this.mdConfig = (MappingClassElementImpl) mdConfig;
        pcElement = this.mdConfig.getPersistenceElement();
        this.pcClass = pcClass;
        classLoader = pcClass.getClassLoader();

        try {
            constructor = pcClass.getConstructor(sigSM);
        } catch (Exception e) {
            // Persistence capable classes must provide this constructor
            throw new JDOFatalUserException(I18NHelper.getMessage(messages,
                    "jdo.persistencemanagerimpl.assertpersistencecapable.error", // NOI18N
                    pcClass.getName()), e);
        }

        fields = new ArrayList();
        foreignFields = new ArrayList();
        tables = new ArrayList();
        fetchGroups = new ArrayList();
    }

    public String toString() {
        return getName();
    }

    /**
     * Creates a new instance of <code>ClassDesc for the given
     * <code>pcClass.
     * @param pcClass The persistence capable class.
     * @return A new instance of ClassDesc.
     */
    static ClassDesc newInstance(Class pcClass) {
        Model model = Model.RUNTIME;
        String className = pcClass.getName();
        ClassLoader classLoader = pcClass.getClassLoader();
        ClassDesc rc = null;

        try {
            MappingClassElement mdConfig =
               model.getMappingClass(className, classLoader);

            // Validate the model information for this class.
            validateModel(model, className, classLoader);

            rc = new ClassDesc(mdConfig, pcClass);

        } catch (JDOException e) {
            throw e;
        } catch (IllegalArgumentException e) {
            throw new JDOFatalUserException(I18NHelper.getMessage(messages,
                "core.configuration.loadfailed.class", className), e); // NOI18N
        } catch (Exception e) {
            throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
                "core.configuration.loadfailed.class", className), e); // NOI18N
        }

        return rc;
    }

    /**
     * Validate the mapping for the given class. If the result
     * collection returned by validate() is not empty, it means
     * it failed the test. After logging all the exceptions,
     * throw a JDOUserException and inform the user to go back
     * to the mapping tool to fix up the mapping file. The
     * JDOUserException contains the validation messages aswell.
     *
     * @param model Runtime model.
     * @param className Persistence capable class' name.
     * @param classLoader Persistence capable class' loader.
     */
    static private void validateModel(Model model,
                                      String className,
                                      ClassLoader classLoader) {
        Collection c = null;

        if (!(c = model.validate(className, classLoader, null)).isEmpty()) {
            Iterator iter = c.iterator();
            StringBuffer validationMsgs = new StringBuffer();

            while (iter.hasNext()) {
                Exception ex = (Exception) iter.next();
                String validationMsg = ex.getLocalizedMessage();

                logger.fine(I18NHelper.getMessage(messages,
                    "core.configuration.validationproblem", // NOI18N
                    className, validationMsg));
                validationMsgs.append(validationMsg).append('\n'); // NOI18N
            }
            throw new JDOFatalUserException(I18NHelper.getMessage(messages,
                "core.configuration.validationfailed", // NOI18N
                className, validationMsgs.toString()));
        }
    }

    public void initialize(ConfigCache cache) {
        boolean debug = logger.isLoggable();
        if (debug) {
            logger.fine("sqlstore.model.classdesc.persistconfiginit", mdConfig); // NOI18N
        }

        loadOidClass();
        initializeFields();
        computeTrackedPrimitiveFields();

        initializeTables();
        initializeJoinTables();
        initializeVersionFields();
        initializeConcurrency();

        initializeKeyFields();
        initializeFetchGroups();

        fixupForeignReferences(cache);
        fixupFieldProperties();
        computeTrackedRelationshipFields();
        cleanupTrackedFields();

        // All fields should be initialized at this point. Now calculate
        // the total number of hidden fields and fields.
        if (hiddenFields != null) {
            maxHiddenFields = hiddenFields.size();
        }
        maxFields = maxVisibleFields + maxHiddenFields;

        if (debug) {
            logger.fine("sqlstore.model.classdesc.persistconfiginit.exit"); // NOI18N
        }
    }

    private void loadOidClass() {

        if (oidClass != null) return;

        String keyClassName = pcElement.getKeyClass();
        String suffix = keyClassName.substring(keyClassName.length() - 4);

        // First check whether the key class name ends with ".oid".
        // If so, we need to convert it '.' to '$' because is it
        // an inner class.
        if (suffix.compareToIgnoreCase(".oid") == 0) { // NOI18N
            StringBuffer buf = new StringBuffer(keyClassName);

            buf.setCharAt(buf.length() - 4, '$');
            keyClassName = buf.toString();
        }

        try {
            oidClass = Class.forName(keyClassName, true, classLoader);
        } catch (Throwable e) {
            throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
                    "core.configuration.cantloadclass", keyClassName)); // NOI18N
        }

        if (logger.isLoggable()) {
            logger.fine("sqlstore.model.classdesc.loadedclass", oidClass); // NOI18N
        }
    }

    /**
     * This method maps all the visible fields. It has the side-effect of computing
     * the value for maxVisibleFields.
     */
    private void initializeFields() {
        ArrayList concurrencyGroups = new ArrayList();
        persistentFields = pcElement.getFields();

        for (int i = 0; i < persistentFields.length; i++) {
            PersistenceFieldElement pcf = persistentFields[i];
            MappingFieldElementImpl mdf = (MappingFieldElementImpl) mdConfig.getField(pcf.getName());

            if (mdf == null) {
                throw new JDOFatalUserException(I18NHelper.getMessage(messages,
                        "core.configuration.fieldnotmapped", // NOI18N
                        pcf.getName(), pcElement.getName()));
            }

            FieldDesc f;

            if (!(mdf instanceof MappingRelationshipElement)) {
                f = createLocalField(mdf);
            } else {
                f = createForeignField((RelationshipElement) pcf, (MappingRelationshipElementImpl) mdf);
            }

            initializeFetchAndConcurrencyGroup(f, pcf, mdf, concurrencyGroups);

            if (mdf.isReadOnly()) {
                f.sqlProperties |= FieldDesc.PROP_READ_ONLY;
            }

            try {
                f.setupDesc(pcClass, pcf.getName());
            } catch (JDOException e) {
                throw e;
            } catch (Exception e) {
                throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
                "core.configuration.loadfailed.field", // NOI18N
                pcf.getName(), pcElement.getName()), e);
            }

            f.absoluteID = pcf.getFieldNumber();

            addField(f);

            if (logger.isLoggable(Logger.FINEST)) {
                Object[] items = new Object[] {f.getName(),new Integer(f.absoluteID)};
                logger.finest("sqlstore.model.classdesc.fieldinfo", items); // NOI18N
            }
        }

        this.maxVisibleFields = fields.size();
    }

    /**
     * Creates an instance of <code>LocalFieldDesc.

     * @param mdf Input <code>MappingFieldElementImpl
     */
    private LocalFieldDesc createLocalField(MappingFieldElementImpl mdf) {
        ArrayList columnDesc = mdf.getColumnObjects();

        // Make sure this field is properly mapped.
        if ((columnDesc == null) || (columnDesc.size() == 0)) {
            throw new JDOFatalUserException(I18NHelper.getMessage(messages,
                    "core.configuration.fieldnotmapped", // NOI18N
                    mdf.getName(), pcElement.getName()));
        }
        return new LocalFieldDesc(this, columnDesc);
    }

    /**
     * Creates an instance of <code>LocalFieldDesc for column
     * that corresponds to a hidden field. Adds the newly created field to
     * <code>hiddenFields and declaredHiddenFields.
     *
     * @param column The input column.
     * @return New instance of <code>LocalFieldDesc
     */
    private LocalFieldDesc createLocalHiddenField(ColumnElement column) {
        ArrayList columnDesc = new ArrayList();
        columnDesc.add(column);
        LocalFieldDesc lf = new LocalFieldDesc(this, columnDesc);

        if (hiddenFields == null) {
            hiddenFields = new ArrayList();
        }
        hiddenFields.add(lf);

        // AbsouluteID for hidden fields must be < 0.
        lf.absoluteID = -hiddenFields.size();

        if (logger.isLoggable(Logger.FINEST)) {
            Object[] items = new Object[] {pcClass,lf.getName(),column.getName().getFullName()};
            logger.finest("sqlstore.model.classdesc.getlocalfielddesc", items); // NOI18N
        }

        return lf;
    }

    private FieldDesc createForeignField(RelationshipElement fpcf,
                                         MappingRelationshipElementImpl fmdf) {

        ForeignFieldDesc ff = new ForeignFieldDesc(this);

        addForeignField(ff);

        ff.cardinalityLWB = fpcf.getLowerBound();
        ff.cardinalityUPB = fpcf.getUpperBound();
        ff.deleteAction = fpcf.getDeleteAction();

        initializeColumnLists(ff, fmdf);

        // This is a workaround to make sure that that cardinalityUPB is not <= 1
        // if the field is of type collection.
        if (Model.RUNTIME.isCollection(fpcf.getCollectionClass()) &&
                (ff.cardinalityUPB <= 1)) {
            ff.cardinalityUPB = Integer.MAX_VALUE;
        }

        if (ff.cardinalityUPB > 1) {
            try {
                ff.setComponentType(Class.forName(fpcf.getElementClass(), true, classLoader));
            } catch (Throwable e) {
              logger.log(Logger.WARNING, "sqlstore.exception.log", e);
            }
        }

        return ff;
    }

    private void initializeColumnLists(ForeignFieldDesc ff, MappingRelationshipElementImpl fmdf) {
        ArrayList assocPairs = fmdf.getAssociatedColumnObjects();
        ArrayList pairs = fmdf.getColumnObjects();
        ArrayList localColumns = new ArrayList();
        ArrayList foreignColumns = new ArrayList();

        // We need to go through each local column and extract the foreign column.
        if ((assocPairs == null) || (assocPairs.size() == 0)) {
            for (int i = 0; i < pairs.size(); i++) {
                ColumnPairElement fce = (ColumnPairElement) pairs.get(i);
                localColumns.add(fce.getLocalColumn());
                foreignColumns.add(fce.getReferencedColumn());
            }

            ff.localColumns = localColumns;
            ff.foreignColumns = foreignColumns;
        } else {
            ArrayList assocLocalColumns = new ArrayList();
            ArrayList assocForeignColumns = new ArrayList();

            for (int i = 0; i < pairs.size(); i++) {
                ColumnPairElement alc = (ColumnPairElement) pairs.get(i);
                localColumns.add(alc.getLocalColumn());
                assocLocalColumns.add(alc.getReferencedColumn());
            }

            for (int i = 0; i < assocPairs.size(); i++) {
                ColumnPairElement afc = (ColumnPairElement) assocPairs.get(i);
                assocForeignColumns.add(afc.getLocalColumn());
                foreignColumns.add(afc.getReferencedColumn());
            }

            ff.localColumns = localColumns;
            ff.assocLocalColumns = assocLocalColumns;
            ff.assocForeignColumns = assocForeignColumns;
            ff.foreignColumns = foreignColumns;
        }
    }

    private void initializeFetchAndConcurrencyGroup(FieldDesc f,
                                                    PersistenceFieldElement pcf,
                                                    MappingFieldElementImpl mdf,
                                                    ArrayList concurrencyGroups) {
        f.fetchGroup = mdf.getFetchGroup();

        // RESOLVE: For now, we can only handle one concurrency group per field
        // MBO:
        // I can call method getConcurrencyGroups w/o exception using the latest mapping files.
        // Please note the mapping files do not include any concurrency group info,
        // thus getConcurrencyGroups returns an empty array.
        ConcurrencyGroupElement cgroups[] = pcf.getConcurrencyGroups();
        //ConcurrencyGroupElement cgroups[] = null;

        if ((cgroups == null) || (cgroups.length == 0)) {
            if (f.fetchGroup == FieldDesc.GROUP_DEFAULT) {
                f.concurrencyGroup = f.fetchGroup;
            }
        } else {
            ConcurrencyGroupElement cge = cgroups[0];
            int index = 0;

            if ((index = concurrencyGroups.indexOf(cge)) == -1) {
                index = concurrencyGroups.size();
                concurrencyGroups.add(cge);
            }

            f.concurrencyGroup = index;
        }
    }

    private void createSecondaryTableKey(TableDesc table, MappingReferenceKeyElementImpl mappingSecondaryKey) {

        ColumnPairElement pairs[] = mappingSecondaryKey.getColumnPairs();
        KeyDesc referencingKey = new KeyDesc();
        KeyDesc referencedKey = new KeyDesc();
        TableDesc secondaryTable = findTableDesc(((MappingTableElementImpl) mappingSecondaryKey.getTable()).getTableObject());

        for (int i = 0; i < pairs.length; i++) {
            ColumnPairElement pair = pairs[i];

            ColumnElement lc = pair.getLocalColumn();
            ColumnElement fc = pair.getReferencedColumn();

            referencingKey.addColumn(lc);

            FieldDesc lf = getLocalFieldDesc(lc);
            referencingKey.addField(lf);

            // We need to force field for the referencing key to be in the DFG
            // so it will always be loaded. This is to facilitate updating
            // secondary tables that requires this field to be loaded
            // for constraint purposes.
            lf.fetchGroup = FieldDesc.GROUP_DEFAULT;

            referencedKey.addColumn(fc);
            referencedKey.addField(getLocalFieldDesc(fc));
        }

        table.addSecondaryTableKey(new ReferenceKeyDesc(secondaryTable, referencingKey, referencedKey));
        secondaryTable.setPrimaryTableKey(new ReferenceKeyDesc(table, referencedKey, referencingKey));
    }

    /**
     * This method maps all the tables.
     */
    private void initializeTables() {
        ArrayList mdTables = mdConfig.getTables();

        createTables(mdTables);
        processSecondaryTables(mdTables);
    }

    private void createTables(ArrayList mdTables) {
        for (int i = 0; i < mdTables.size(); i++) {
            MappingTableElementImpl mdt = (MappingTableElementImpl) mdTables.get(i);
            TableDesc t = new TableDesc(mdt.getTableObject());

            ArrayList keys = mdt.getKeyObjects();
            KeyDesc key = new KeyDesc();
            t.setKey(key);
            key.addColumns(keys);

            for (int j = 0; j < keys.size(); j++) {
                ColumnElement c = (ColumnElement) keys.get(j);

                if (c != null) {
                    key.addField(getLocalFieldDesc(c));
                }
            }

            addTableDesc(t);
        }
    }

    /**
     * Validity checks on secondary tables:
     *	1) Ensure that every secondary table has a TableDesc
     *	2) Every referencing key is the same length as the table's key
     * Build the referencing keys.
     * NOTE: This method assumes that the entries of <code>mdTables
     * and <code>tables are sorted in the same order.
     */
    private void processSecondaryTables(ArrayList mdTables) {

        for (int i = 0; i < tables.size(); i++) {
            MappingTableElementImpl mdt = (MappingTableElementImpl) mdTables.get(i);
            TableDesc t = (TableDesc) tables.get(i);
            ArrayList secondaryKeys = mdt.getReferencingKeys();

            for (int j = 0; j < secondaryKeys.size(); j++) {
                MappingReferenceKeyElementImpl mappingSecondaryKey = (MappingReferenceKeyElementImpl) secondaryKeys.get(j);
                createSecondaryTableKey(t, mappingSecondaryKey);
            }
        }
    }

    private void initializeJoinTables() {
        Iterator iter = foreignFields.iterator();

        while (iter.hasNext()) {
            ForeignFieldDesc ff = (ForeignFieldDesc) iter.next();

            if (ff.useJoinTable()) {
                TableElement joinTable = ((ColumnElement) ff.assocLocalColumns.get(0)).getDeclaringTable();
                TableDesc joinTableDesc = findTableDesc(joinTable);

                if (joinTableDesc == null) {
                    joinTableDesc = new TableDesc(joinTable);

                    // Mark this table as a join table
                    joinTableDesc.setJoinTable(true);
                    addTableDesc(joinTableDesc);
                }
              }
        }
    }

    /**
     * Returns a list of fields in fetchGroup <code>groupID.
     *
     * @param groupID Fetch group id.
     * @return List of fields in fetchGroup <code>groupID. The list
     *  for <code>FieldDesc.GROUP_NONE is empty.
     * @see #initializeFetchGroups
     */
    public ArrayList getFetchGroup(int groupID) {
        int index = 0;

        if (groupID >= FieldDesc.GROUP_NONE) {
            index = groupID;
        } else if (groupID < FieldDesc.GROUP_NONE) {
            index = -groupID + maxHierarchicalGroupID;
        }

        for (int i = fetchGroups.size(); i <= index; i++) {
            fetchGroups.add(null);
        }

        ArrayList group = (ArrayList) fetchGroups.get(index);

        if (group == null) {
            group = new ArrayList();
            fetchGroups.set(index, group);
        }

        return group;
    }

    private void addField(FieldDesc f) {
        fields.add(f);
    }

    private void addForeignField(ForeignFieldDesc f) {
        foreignFields.add(f);
    }

    /**
     * Compute the fetch group lists for the declared fields.
     * A fetch group lists the fields sharing the same value for
     * <code>FieldDesc.fetchGroup.
     * Note: Fields that aren't in any fetch group must not be added
     * to <code>fetchGroups[FieldDesc.GROUP_NONE].
     *
     * @see #getFetchGroup
     */
    private void initializeFetchGroups() {

        for (int i = 0; i < 2; i++) {
            ArrayList theFields = null;

            if (i == 0) {
                theFields = fields;
            } else {
                // It is possible to have hidden fields in the DFG.
                if ((theFields = hiddenFields) == null)
                    continue;
            }

            for (int j = 0; j < theFields.size(); j++) {
                FieldDesc f = (FieldDesc) theFields.get(j);

                // Do not add the field to the fetch group for GROUP_NONE.
                if (f.fetchGroup > FieldDesc.GROUP_NONE) {
                    getFetchGroup(f.fetchGroup).add(f);
                }

                // Check declared visible fields only.
                if (i == 0 && !f.isRelationshipField()
                        && f.fetchGroup != FieldDesc.GROUP_DEFAULT) {
                    hasLocalNonDFGField = true;
                }
            }
        }

        this.maxHierarchicalGroupID = fetchGroups.size() - 1;

        for (int i = 0; i < fields.size(); i++) {
            FieldDesc f = (FieldDesc) fields.get(i);

            // Do not add the field to the fetch group for GROUP_NONE.
            if (f.fetchGroup < FieldDesc.GROUP_NONE) {
                getFetchGroup(f.fetchGroup).add(f);
            }
        }
    }

    private void initializeConcurrency() {
        optimisticConcurrency = new ConcurrencyOptVerify();
        optimisticConcurrency.configPersistence(this);
        checkDirtyConcurrency = new ConcurrencyCheckDirty();
        checkDirtyConcurrency.configPersistence(this);
        databaseConcurrency = new ConcurrencyDBNative();
        databaseConcurrency.configPersistence(this);
        explicitConcurrency = new ConcurrencyDBExplicit();
        explicitConcurrency.configPersistence(this);
    }

    private void initializeKeyFields() {
        boolean debug = logger.isLoggable(Logger.FINEST);
        if (oidClass == null)
            return;

        keyFields = oidClass.getFields();
        keyFieldNames = new String[keyFields.length];
        keyFieldDescs = new LocalFieldDesc[keyFields.length];

        if (debug) {
            logger.finest("sqlstore.model.classdesc.createsqldesc", oidClass); // NOI18N
        }

        for (int i = 0; i < keyFields.length; i++) {
            Field kf = keyFields[i];
            String name = kf.getName();
            keyFieldNames[i] = name;

            if (name.equals("serialVersionUID")) { // NOI18N
                continue;
            }

            LocalFieldDesc f = getLocalFieldDesc(name);

            if (f != null) {
                if (debug) {
                    logger.finest("sqlstore.model.classdesc.pkfield", f.getName()); // NOI18N
                }

                // The fetch group for pk fields should always be DFG.
                f.fetchGroup = FieldDesc.GROUP_DEFAULT;
                f.sqlProperties &= ~(FieldDesc.PROP_REF_INTEGRITY_UPDATES);
                f.sqlProperties &= ~(FieldDesc.PROP_IN_CONCURRENCY_CHECK);
                f.sqlProperties |= FieldDesc.PROP_PRIMARY_KEY_FIELD;

                keyFieldDescs[i] = f;
            } else {
                throw new JDOFatalUserException(I18NHelper.getMessage(messages,
                        "core.configuration.noneexistentpkfield", // NOI18N
                        name, oidClass.getName(), pcClass.getName()));
            }
        }
    }

    /**
     * Initialize the list of field descriptors of version consistency fields.
     * The names of the version fields are obtained from
     * {@link MappingClassElement#getVersionFields}.
     */
    private void initializeVersionFields() {
        int size = mdConfig.getVersionFields().size();
        Iterator versionFieldIterator = mdConfig.getVersionFields().iterator();
        versionFields = new LocalFieldDesc[size];

        for (int i = 0; i < size; i++) {
            MappingFieldElement mdField = (MappingFieldElement) versionFieldIterator.next();
            LocalFieldDesc f = (LocalFieldDesc) getField(mdField.getName());

            if (f != null) {
                if (logger.isLoggable()) {
                    logger.finest("sqlstore.model.classdesc.vcfield", f.getName()); // NOI18N
                }

                versionFields[i] = f;
                registerVersionFieldWithTable(f);

                // The fetch group for version fields should always be DFG.
                f.fetchGroup = FieldDesc.GROUP_DEFAULT;
                f.sqlProperties &= ~(FieldDesc.PROP_REF_INTEGRITY_UPDATES);
                f.sqlProperties |= FieldDesc.PROP_VERSION_FIELD;
            } else {
                throw new JDOFatalUserException(I18NHelper.getMessage(messages,
                        "core.configuration.noneexistentvcfield", // NOI18N
                        mdField.getName(), pcClass.getName()));
            }
        }
    }

    /**
     * Registers the version field <cod>versionField with the
     * corresponding table.
     *
     * @param versionField Field used in version consistency check.
     */
    private void registerVersionFieldWithTable(LocalFieldDesc versionField) {
        // Version field must be mapped to exactly one column.
        ColumnElement ce =  (ColumnElement) versionField.getColumnElements().next();
        Iterator iter = tables.iterator();

        while (iter.hasNext()) {
            TableDesc table = (TableDesc) iter.next();

            if (!table.isJoinTable()) {
                if (ce.getDeclaringTable() == table.getTableElement()) {
                    table.setVersionField(versionField);
                    break;
                }
            }
        }
    }

    /**
     * Computes all the primitive tracked fields.
     * Primitive fields track each other if they are mapped to same columns.
     * One of them is made the primary tracked field as per precedence rules
     * on the field types. This field is used to bind values to columns while
     * updating the database.
     */
    private void computeTrackedPrimitiveFields() {
        // Compute the list of primitive fields to track for each primitive field.
        for (int i = 0; i < fields.size(); i++) {
            FieldDesc f = (FieldDesc) fields.get(i);

            if (!f.isRelationshipField()) {
                LocalFieldDesc lf = (LocalFieldDesc) f;

                lf.computeTrackedPrimitiveFields();
                lf.computePrimaryTrackedPrimitiveField();
            }
        }
    }

    /**
     * Computes the tracked relationship fields. Relationships are tracked in
     * the following way:
     * <ul>
     * <li>A relationship field tracks a local field, if it's mappped
     * to the same columns. The relationship will be updated when the
     * local field is set.</li>
     * <li>Relationship fields track each other if they are mapped to
     * the same columns. The first field in the field order is marked
     * as primary tracked field. If the foreign key columns are
     * explicitly mapped, this field also tracks the local foreign key
     * field. The foreign key columns are explicitly mapped in
     * overlapping pk/fk situations. The other relationship fields
     * mapped to the same columns are updated when the primary tracked
     * relationship field is set.</li>
     * </ul>
     * @see FieldDesc#PROP_PRIMARY_TRACKED_FIELD
     * @see FieldDesc#PROP_SECONDARY_TRACKED_FIELD
     */
    private void computeTrackedRelationshipFields() {
        // Compute the list of fields to track for each field.
        for (int i = 0; i < 2; i++) {
            ArrayList theFields = null;

            // We first check all the visible fields and then the hidden fields.
            if (i == 0) {
                theFields = this.fields;
            } else {
                if ((theFields = this.hiddenFields) == null) {
                    continue;
                }
            }

            for (int j = 0; j < theFields.size(); j++) {
                FieldDesc f = (FieldDesc) theFields.get(j);

                f.computeTrackedRelationshipFields();
            }
        }
    }

    /**
     * Remove redundant ForeignFieldDescs from each LocalFieldDesc's tracked field list.
     */
    private void cleanupTrackedFields() {

        for (int i = 0; i < fields.size(); i++) {
            FieldDesc f = (FieldDesc) fields.get(i);

            if (f instanceof LocalFieldDesc) {
                ((LocalFieldDesc) f).cleanupTrackedFields();
            }
        }
    }

    /**
     *  The fixupForeignReferences method finds all the references to foreign
     *  classes in this configuration. It then builds the appropriate run-time
     *  information (ForeignFieldDesc) for the foreign reference from the
     *  meta-data.
     */
    private void fixupForeignReferences(ConfigCache cache) {

        for (int i = 0; i < foreignFields.size(); i++) {
            ForeignFieldDesc ff = (ForeignFieldDesc) foreignFields.get(i);
            Class classType = null;

            if ((classType = ff.getComponentType()) == null) {
                classType = ff.getType();
            }

            ClassDesc foreignConfig = (ClassDesc) cache.getPersistenceConfig(classType);

            if (foreignConfig == null) continue;

            // Look up the inverse relationship field name if there is any.
            String irName = pcElement.getRelationship(ff.getName()).getInverseRelationshipName();
            ForeignFieldDesc inverseField = null;

            if (irName != null) {
                inverseField = (ForeignFieldDesc) foreignConfig.getField(irName);
            }

            ff.fixupForeignReference(foreignConfig, inverseField);
        }
    }

    private void fixupFieldProperties() {

        for (int i = 0; i < foreignFields.size(); i++) {
            ForeignFieldDesc ff = (ForeignFieldDesc) foreignFields.get(i);
            ff.fixupFieldProperties();
        }
    }

    LocalFieldDesc getLocalFieldDesc(ColumnElement column) {
        LocalFieldDesc result;

        for (int i = 0; i < 2; i++) {
            ArrayList theFields = null;

            if (i == 0) {
                theFields = fields;
            } else {
                theFields = hiddenFields;
            }

            if (theFields != null) {
                for (int j = 0; j < theFields.size(); j++) {
                    FieldDesc f = (FieldDesc) theFields.get(j);

                    if (f instanceof LocalFieldDesc) {
                        result = (LocalFieldDesc) f;

                        for (int k = 0; k < result.columnDescs.size(); k++) {
                            ColumnElement c = (ColumnElement) result.columnDescs.get(k);

                            // if (c.equals(column))
                            if (c.getName().getFullName().compareTo(column.getName().getFullName()) == 0) {
                                // If f is a tracked field and it is not the primary, we continue
                                // searching.
                                if ((f.getTrackedFields() != null) &&
                                        ((f.sqlProperties & FieldDesc.PROP_PRIMARY_TRACKED_FIELD) == 0)) {
                                    continue;
                                }

                                return result;
                            }
                        }
                    }
                }
            }
        }

        // If we didn't find the field associated with the column, we need to
        // create a hidden field and add it to the hiddenFields list.
        result = createLocalHiddenField(column);

        return result;
    }

    /**
     * Returns the local field descriptor for the field <code>name.
     *
     * @param name Field name.
     * @return Local field descriptor for the field <code>name.
     * @throws JDOFatalInternalException if the field is not defined for this class.
     */
    public LocalFieldDesc getLocalFieldDesc(String name) {
        FieldDesc desc = getField(name);

        if (desc == null) {
            throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
                        "core.generic.unknownfield", // NOI18N
                        name, getName()));
        }

        if (!(desc instanceof LocalFieldDesc)) {
            throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
                        "core.generic.notinstanceof", // NOI18N
                        desc.getClass().getName(), "LocalFieldDesc")); // NOI18N
        }

        return ((LocalFieldDesc) desc);
    }

    public TableDesc findTableDesc(TableElement mdTable) {
        for (int i = 0; i < tables.size(); i++) {
            TableDesc t = (TableDesc) tables.get(i);

            if (t.getTableElement().equals(mdTable)) {
                return t;
            }
        }

        return null;
    }

    private void addTableDesc(TableDesc t) {
        // setConsistencyLevel of this table before adding it to our list
        if (!t.isJoinTable()) {
            // JoinTables represent relationships instead of "real" objects,
            // they should never have a special consistencyLevel.
            t.setConsistencyLevel(mdConfig.getConsistencyLevel());
        }
        tables.add(t);
    }

    public int getTableIndex(TableDesc tableDesc) {
        return tables.indexOf(tableDesc);
    }

    public FieldDesc getField(String name) {
        for (int i = 0; i < fields.size(); i++) {
            FieldDesc f = (FieldDesc) fields.get(i);

            if ((f != null) && (f.getName().compareTo(name) == 0)) {
                return f;
            }
        }

        if (hiddenFields != null) {
            for (int i = 0; i < hiddenFields.size(); i++) {
                FieldDesc f = (FieldDesc) hiddenFields.get(i);

                if (f.getName().compareTo(name) == 0) {
                    return f;
                }
            }
        }

        return null;
    }

    public FieldDesc getField(int index) {
        if (index >= 0) {
            return (FieldDesc) fields.get(index);
        } else {
            return (FieldDesc) hiddenFields.get(-(index + 1));
        }
    }

    public Constructor getConstructor() {
        return constructor;
    }

    public Class getPersistenceCapableClass() {
        return pcClass;
    }

    public Class getOidClass() {
        return oidClass;
    }

    public String getName() {
        return pcClass.getName();
    }

    public Iterator getTables() {
        return tables.iterator();
    }

    public TableDesc getPrimaryTable() {
        return (TableDesc) tables.get(0);
    }

    public boolean isNavigable() {
        return mdConfig.isNavigable();
    }

    public boolean hasVersionConsistency() {
        return mdConfig.getConsistencyLevel() == MappingClassElement.VERSION_CONSISTENCY;
    }

    public LocalFieldDesc[] getVersionFields() {
        return versionFields;
    }

    public Concurrency getConcurrency(boolean optimistic) {
        // Following algo is used to determine which concurrency to return:
        // consistency level specified in model(represented by the variable consistencyLevel),
        // takes precedence over
        // concurrency specified by transaction (represented by parameter 'optimistic' to this method)

        Concurrency concurrency = null;
        int consistencyLevel = mdConfig.getConsistencyLevel();

        if (consistencyLevel == MappingClassElement.NONE_CONSISTENCY) {
            // No consistency level specified in model
            if (optimistic) {
                concurrency = (Concurrency) optimisticConcurrency.clone();
            } else {
                concurrency = (Concurrency) databaseConcurrency.clone();
            }

        } else {
            // currently, we would not try to interprete consistencyLevel as bitmap
            // When we implment consistencyLevel like 2+, 3+, we would have to interprete
            // consistencyLevel as bitmap and implement some changes in class hierarchy
            // starting  with interface Concurrency
            switch(consistencyLevel) {

                // We would never reach this code because we are in the else part
                // case MappingClassElement.NONE_CONSISTENCY :
                //	concurrency = (Concurrency) databaseConcurrency.clone();
                //	break;

            case MappingClassElement.CHECK_MODIFIED_AT_COMMIT_CONSISTENCY :
                concurrency = (Concurrency) checkDirtyConcurrency.clone();
                break;

            case MappingClassElement.LOCK_WHEN_LOADED_CONSISTENCY :
                // This consistency level is implemeted inside SelectStatement and
                // not by any object implementing Concurrency
                concurrency = (Concurrency) explicitConcurrency.clone();
                break;

            case MappingClassElement.VERSION_CONSISTENCY :
                // This consistency level is implemented inside ClassDesc and
                // UpdateQueryPlan and not by any object implementing Concurrency.
                // Selects are handled in ClassDesc, updates/deletes in
                // UpdateQueryPlan.
                concurrency = (Concurrency) databaseConcurrency.clone();
                break;

            // **Note**
            // Plese change text for the exception thrown in default below when
            // we start supporting new consistency levels. The text lists currently supported
            // consistency level
            default :
                throw new JDOUnsupportedOptionException(I18NHelper.getMessage(messages,
                    "core.configuration.unsupportedconsistencylevel", pcClass));// NOI18N
            }
        }

        return concurrency;
    }

    /**
     * Determines whether this classDesc has the
     * CHECK_MODIFIED_AT_COMMIT_CONSISTENCY level.
     * @return <code>true if this has the
     * CHECK_MODIFIED_AT_COMMIT_CONSISTENCY level;
     * <code>false otherwise.
     */
    public boolean hasModifiedCheckAtCommitConsistency() {
        return(mdConfig.getConsistencyLevel() ==
                MappingClassElement.CHECK_MODIFIED_AT_COMMIT_CONSISTENCY);
    }

    public boolean isPKField(int index) {
        return persistentFields[index].isKey();
    }

    /**
     * Return the key fields as array of java.lang.reflect.Field instances.
     * @return The key fields as array of java.lang.reflect.Field instances.
     */
    public Field[] getKeyFields() {
        return keyFields;
    }

    /**
     * Returns the key field names as array of String.
     * @return The key field names as array of String.
     */
    public String[] getKeyFieldNames() {
        return keyFieldNames;
    }

    /**
     * Returns the descriptors for key fields as array of LocalFieldDesc.
     * @return The descriptors for key fields as array of LocalFieldDesc.
     */
    public LocalFieldDesc[] getKeyFieldDescs() {
        return keyFieldDescs;
    }

    /**
     * Returns a new <code>SQLStateManager instance,
     * initialized with passed store manager and this instance of
     * the runtime class model.
     *
     * @param store Store manager, an instance of SQLStoreManager.
     * @return A new <code>SQLStateManager instance.
     */
    public SQLStateManager newStateManagerInstance(PersistenceStore store) {
        return new SQLStateManager(store, this);
    }

    /**
     * Returns a RetrieveDescriptor which represent a SQL query selecting a pc
     * instance by pk-fields. Please note that the RDs are cached, so the method
     * first checks the cache. If there is no corresponding RetrieveDescriptor
     * in the cache, it creates a new one and stores it in the cache. If the
     * additionalField is not null, the method retrieves the field indicated by
     * it along with the query. Fetch group fields will be added when the query
     * plan is build.
     *
     * Note, the reason to introduce the RetrieveDesc cache in ClassDesc and not
     * in the store manager is, that we can have the cache per class, where
     * the store manager could only provide one big cache for all pc classes.
     *
     * @param additionalField The field to be retrieved in addition to the
     * DFG fields.
     * @param store The store manager.
     * @return A RetrieveDescriptor selecting a pc instance by pk-fields.
     * @see #getRetrieveDescForFKQuery
     */
    public RetrieveDesc getRetrieveDescForPKQuery(FieldDesc additionalField, PersistenceStore store) {
        RetrieveDescImpl rd = null;
        String cacheKey = generateRDCacheKey(additionalField);

        synchronized (retrieveDescCache) {
            // Cache lookup.
            rd = (RetrieveDescImpl) retrieveDescCache.get(cacheKey);
            // Generate a new RD if there isn't one be found in the cache.
            if (rd == null) {
                rd = (RetrieveDescImpl) store.getRetrieveDesc(pcClass);
                if (additionalField != null) {
                    RetrieveDesc frd = null;
                    String name = additionalField.getName();
                    // If the additionalField is not null, we will retrieve
                    // the field indicated by it along with the query.
                    if (additionalField instanceof ForeignFieldDesc) {
                        Class additionalClass = ((ForeignFieldDesc) additionalField).
                                foreignConfig.getPersistenceCapableClass();

                        frd = store.getRetrieveDesc(additionalClass);
                    }

                    rd.addPrefetchedField(name, frd);
                }

                addPKConstraints(rd);
                // Cache fillup.
                retrieveDescCache.put(cacheKey, rd);
            }
        }

        return rd;
    }

    /**
     * Returns a RetrieveDescriptor which represent a SQL query selecting pc
     * instances by the relationship key. The relationship key is taken from
     * the foreign field <code>foreignField and used as query constraint.
     * Please note that the RDs are cached, so the method first checks the cache.
     * If there is no corresponding RetrieveDescriptor in the cache, it creates
     * a new one and stores it in the cache. FetchGroup fields will be added
     * when the query plan is build, see <code>SelectQueryPlan#processFetchGroups.
     *
     * Note, the reason to introduce the RetrieveDesc cache in ClassDesc and not
     * in the store manager is, that we can have the cache per class, where
     * the store manager could only provide one big cache for all pc classes.
     *
     * @param foreignField The relationship field to be retrieved.
     *  Following is true for this field.
     * <ul>
     *  <li> It is part of an independent fetch group with only one field in the fetch group.
     *       <p>Or
     *       <P>Not part of any fetch group.
     *  </li>
     *  <li>It is not mapped to a join table. 
     * </ul>
     * @param store The store manager.
     * @return A RetrieveDescriptor selecting pc instance(s) corresponding to
     * the foreign field
     * @see #getRetrieveDescForPKQuery
     */
    public RetrieveDesc getRetrieveDescForFKQuery(ForeignFieldDesc foreignField, PersistenceStore store) {
        RetrieveDescImpl rd = null;
        String cacheKey = generateRDCacheKey(foreignField);

        synchronized (foreignRetrieveDescCache) {
            // Cache lookup.
            rd = (RetrieveDescImpl) foreignRetrieveDescCache.get(cacheKey);
            // Generate a new RD if there isn't one be found in the cache.
            if (rd == null) {
                rd = (RetrieveDescImpl) store.getRetrieveDesc(foreignField.foreignConfig.getPersistenceCapableClass());

                addFKConstraints(rd, foreignField);
                // Cache fillup.
                foreignRetrieveDescCache.put(cacheKey, rd);
            }
        }

        return rd;
    }

    /**
     * Gets RetrieveDescriptor(rd) for verifying a VC instance. The returned rd
     * is set up to expect constraints for pk followed by constraints for version
     * fields.
     * @param store
     * @return Instance of retrieve Descriptor for verifying a VC instance.
     */
    public RetrieveDesc getRetrieveDescForVerificationQuery(PersistenceStore store) {

        assert hasVersionConsistency();

        synchronized(retrieveDescForVerificationSynchObj) {
            if (retrieveDescForVerification == null) {
                RetrieveDescImpl rd = (RetrieveDescImpl) store.getRetrieveDesc(pcClass);

                int index = addPKConstraints(rd);
                rd.addParameterConstraints(versionFields, index);
                rd.setOption(RetrieveDescImpl.OPT_VERIFY);

                retrieveDescForVerification = rd;
            }
        }
        return retrieveDescForVerification;
    }

    /**
     * Generate for each keyField 'keyfield = ?' in the where clause
     * with InputParamValues for later binding of the actual query parameters.
     */
    private int addPKConstraints(RetrieveDescImpl rd) {
        // PK Constraints are always added first hence startIndex == 0
        rd.addParameterConstraints(keyFieldDescs, 0);

        return keyFieldDescs.length;
    }

    /**
     * Generate the condition on the relationship key in the where clause
     * with InputParamValues for later binding of the actual query parameters.
     */
    private void addFKConstraints(RetrieveDescImpl rd, ForeignFieldDesc foreignField) {
        for (int i = 0; i < foreignField.foreignFields.size(); i++) {
            LocalFieldDesc fff = (LocalFieldDesc) foreignField.foreignFields.get(i);
            rd.addParameterConstraint(fff, i);
        }
    }

    /**
     * Generates the key for a RetrieveDescriptor for the cache lookup.
     * The key has one of two forms:
     * <li>
     * Fully qualified classname of the pcclass, if additionalField is null.
     * </li>
     * <li>
     * Fully qualified classname of the pcclass + '/' + additionalFieldName +
     * '/' + additionalFieldID, otherwise.
     * </li>
     * @param additionalField The field to be retrieved in addition to the
     * DFG fields.
     * @return The generated cache key as a String.
     */
    private String generateRDCacheKey(FieldDesc additionalField)
    {
        StringBuffer key = new StringBuffer();

        key.append(pcClass.getName());

        if (additionalField != null) {
            // using '/' as separator between class and fieldname
            key.append('/');
            key.append(additionalField.getName());
            key.append('/');
            key.append(additionalField.absoluteID);
        }

        return key.toString();
    }

    /**
     * Returns true, if this class has got local fields not in the default
     * fetch group. Because UpdateQueryPlans for updates are cached depending
     * on the set of updated fields {@link #getUpdateQueryPlanForUpdate}, we
     * might need to compare the updated fields btw. two instances when
     * batching is enabled.
     *
     * @return True, if this class has got local fields not in the default
     * fetch group.
     * @see SQLStateManager#requiresImmediateFlush
     */
    public boolean hasLocalNonDFGFields() {
        // All instances with modified DFG fields can be batched with
        // the same statement. If there's a primitive field outside
        // the DFG, we need to compare modified fields to check if the
        // same statement can be used.
        return hasLocalNonDFGField;
    }

    /**
     * Retrieves the update query plan for the specified descriptor.
     * @param desc the descriptor
     * @param store the store manager
     * @return Instance of update query plan for the specified descriptor.
     */
    public UpdateQueryPlan getUpdateQueryPlan(
            UpdateObjectDescImpl desc, SQLStoreManager store)
    {
        switch (desc.getUpdateAction()) {
            case ActionDesc.LOG_CREATE:
                return getUpdateQueryPlanForInsert(desc, store);
            case ActionDesc.LOG_DESTROY:
                return getUpdateQueryPlanForDelete(desc, store);
            case ActionDesc.LOG_UPDATE:
                return getUpdateQueryPlanForUpdate(desc, store);
            default:
                // TBD: error message + I18N
                // error unknown action
                return null;
        }
    }

    /** */
    private UpdateQueryPlan getUpdateQueryPlanForInsert(
            UpdateObjectDescImpl desc, SQLStoreManager store)
    {
        synchronized(updateQueryPlanForInsertSynchObj) {
            if (updateQueryPlanForInsert == null) {
                updateQueryPlanForInsert = buildQueryPlan(store, desc);
            }
        }
        return updateQueryPlanForInsert;
    }

    /** */
    private UpdateQueryPlan getUpdateQueryPlanForDelete(
            UpdateObjectDescImpl desc, SQLStoreManager store)
    {
        synchronized(updateQueryPlanForDeleteSynchObj) {
            if (updateQueryPlanForDelete == null) {
                updateQueryPlanForDelete = buildQueryPlan(store, desc);
            }
        }
        return updateQueryPlanForDelete;
    }

    /** */
    private UpdateQueryPlan getUpdateQueryPlanForUpdate(
            UpdateObjectDescImpl desc, SQLStoreManager store)
    {
        String key = getSortedFieldNumbers(desc.getUpdatedFields());
        UpdateQueryPlan plan;
        synchronized(updateQueryPlanCache) {
            plan = (UpdateQueryPlan)updateQueryPlanCache.get(key);
            if (plan == null) {
                plan = buildQueryPlan(store, desc);
                updateQueryPlanCache.put(key, plan);
            }
        }
        return plan;
    }

    /**
     * Builds and initializes a new query plan based on the information
     * passed with the <code>desc parameter. The returned plan
     * and its related data structures will be readonly.
     *
     * @param store Store manager
     * @param desc Update information, including the update action and
     * modified fields as appropriate.
     *
     * @return A new query plan. The returned plan will be readonly.
     */
    private UpdateQueryPlan buildQueryPlan(SQLStoreManager store,
                                           UpdateObjectDescImpl desc) {
        UpdateQueryPlan plan;

        plan = new UpdateQueryPlan(desc, store);
        plan.build(true);

        // Initialize the text for all statements. After this point,
        // the plan and its related data structures will be readonly.
        plan.getStatements();

        return plan;
    }

    /**
     * The methods returns the string representation of the sorted field numbers
     * of the FieldDescs.
     * The key is the string representation of the sorted field number list
     * of updated fields.
     * @param fields the list of FieldDescs
     * @return the sorted field number string
     */
    private String getSortedFieldNumbers(List fields)
    {
        // Use the array of field numbers of the updated fields as the key
        int size = fields.size();
        int [] fieldNos = new int[size];
        for (int i = 0; i < size; i++) {
            FieldDesc f = (FieldDesc)fields.get(i);
            fieldNos[i] = f.absoluteID;
        }
        Arrays.sort(fieldNos);
        return StringHelper.intArrayToSeparatedList(fieldNos, ","); //NOI18N
    }

}

Other Glassfish examples (source code examples)

Here is a short list of links related to this Glassfish ClassDesc.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.