Package com.sun.jdo.spi.persistence.support.sqlstore.model

Source Code of com.sun.jdo.spi.persistence.support.sqlstore.model.ForeignFieldDesc

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2007 Sun Microsystems, Inc. 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.html
* or glassfish/bootstrap/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 glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code.  If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [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.
*/

/*
* ForeignFieldDesc.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.TableElement;
import com.sun.jdo.api.persistence.model.jdo.RelationshipElement;
import com.sun.jdo.api.persistence.support.JDOFatalInternalException;
import com.sun.jdo.spi.persistence.support.sqlstore.SQLStateManager;
import com.sun.jdo.spi.persistence.utility.I18NHelper;
import com.sun.jdo.spi.persistence.utility.logging.Logger;

import java.lang.reflect.Field;
import java.util.ArrayList;

/**
*
*/
public class ForeignFieldDesc extends FieldDesc {

    // Values for deleteAction
    /**
     * When the parent object is deleted from db,
     * delete the relationship object described by this object also.
     */
    public static final int ACT_CASCADE = RelationshipElement.CASCADE_ACTION;

    /**
     * When the parent object is deleted,
     * no action is required for the relationship object described by this object.
     */
    public static final int ACT_NONE = RelationshipElement.NONE_ACTION;

    /** Currently runtime code does not interprete this action. */
    public static final int ACT_NULLIFY = RelationshipElement.NULLIFY_ACTION;

    /** Currently runtime code does not interprete this action. */
    public static final int ACT_RESTRICT = RelationshipElement.RESTRICT_ACTION;

    /** Currently runtime code does not interprete this action. */
    public static final int ACT_AGGREGATE = RelationshipElement.AGGREGATE_ACTION;

    /** Class descriptor for the class of this relationship field. */
    public ClassDesc foreignConfig;

    public int cardinalityLWB;

    public int cardinalityUPB;

    // Takes one of the following values.
    public int deleteAction;

    /** Array of LocalFieldDesc. */
    public ArrayList foreignFields;

    /** Array of ColumnElement. */
    public ArrayList foreignColumns;

    /** Array of LocalFieldDesc. */
    public ArrayList localFields;

    /** Array of ColumnElement. */
    public ArrayList localColumns;

    /** Array of LocalFieldDesc. */
    public ArrayList assocForeignFields;

    /** Array of ColumnElement. */
    public ArrayList assocForeignColumns;

    /** Array of LocalFieldDesc. */
    public ArrayList assocLocalFields;

    /** Array of ColumnElement. */
    public ArrayList assocLocalColumns;

    /**
     * If inverseRelationshipField is not null, it means this field is
     * under managed relationship. Otherwise, this is a one-way relationship.
     */
    private ForeignFieldDesc inverseRelationshipField;

    /**
     * True, if the relationship is mapped to primary key fields
     * <b>on the other relationship side</b>.
     */
    private boolean isMappedToPk;

    ForeignFieldDesc(ClassDesc config) {
        super(config);
    }

    /**
     * Returns true.
     */
    public boolean isRelationshipField() {
        return true;
    }

    /**
     * Returns true, if the relationship is mapped to a join table.
     *
     * @return True, if the relationship is mapped to a join table, false otherwise.
     */
    public boolean useJoinTable() {
        return (assocLocalColumns != null && assocLocalColumns.size() > 0);
    }

    /**
     * Checks the conditions that guarantee, that we have the foreign
     * key on this side.
     *
     * @return True, if this relationship is mapped to foreign keys and the
     * foreign key is on the local side. False otherwise.
     */
    public boolean hasForeignKey() {
        boolean result = false;

        // RESOLVE: Can't check PROP_REF_INTEGRITY_UPDATES for 1 way mappings.
        // See #checkReferentialIntegrityUpdatesForObjectField.
        if (inverseRelationshipField != null) {
            result = cardinalityUPB == 1 && !useJoinTable() &&
                    (sqlProperties & FieldDesc.PROP_REF_INTEGRITY_UPDATES) > 0;
        }
        return result;
    }

    /**
     * Returns true, if the relationship is mapped to primary key fields
     * <b>on the other relationship side</b>.
     *
     * @return True, if the relationship is mapped to primary key fields
     * <b>on the other relationship side</b>, false otherwise.
     * @see #initializeIsMappedToPk
     */
    public boolean isMappedToPk() {
        return isMappedToPk;
    }

    public ArrayList getLocalFields() {
        if (localFields == null) {
            localFields = new ArrayList();
        }

        return localFields;
    }

    public ArrayList getForeignFields() {
        if (foreignFields == null) {
            foreignFields = new ArrayList();
        }

        return foreignFields;
    }

    public ArrayList getAssocLocalFields() {
        // Only create assocLocalFields if there is a corresponding
        // assocLocalColumns to save space.
        if (assocLocalFields == null && assocLocalColumns != null) {
            assocLocalFields = new ArrayList();
        }

        return assocLocalFields;
    }

    public ArrayList getAssocForeignFields() {
        // Only create assocForeignFields if there is a corresponding
        // assocForeignColumns to save space.
        if (assocForeignFields == null && assocForeignColumns != null) {
            assocForeignFields = new ArrayList();
        }

        return assocForeignFields;
    }

    public ForeignFieldDesc getInverseRelationshipField() {
        return inverseRelationshipField;
    }

    /**
     * Constructs the oid of a related instance. If called by {@link
     * SQLStateManager#updateTrackedFields}, the new value for the
     * local field <code>fieldDesc</code> is not yet set and passed as
     * parameter <code>value</code>. If called for navigation by
     * {@link SQLStateManager#populateForeignField}, values of updated
     * local fields must be retrieved from the before image.  In both
     * cases, the actual call to this method is in {@link
     * SQLStateManager#getObjectById}.<p>
     * For tracked field usage, see
     * {@link SQLStateManager#setForeignKey} and
     * {@link SQLStateManager#updateTrackedFields}.
     * For navigation usage, see
     * {@link SQLStateManager#realizeForeignField}.
     *
     * @param sm State manager on the local side.
     * @param fieldDesc Local field being set.
     * @param value New value of the field being set.
     * @return The object id for the related instance. This id is used
     * to lookup the instance from the persistence manager cache. The
     * construced oid is invalid, if one of the oid fields is
     * null. In this case the returned value is null.
     */
    public Object createObjectId(SQLStateManager sm, LocalFieldDesc fieldDesc, Object value) {

        assert isMappedToPk();

        Class oidClass = foreignConfig.getOidClass();
        Object oid = null;

        try {
            oid = oidClass.newInstance();
        } catch (Exception e) {
            throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
                    "core.statemanager.cantnewoid", oidClass.getName()), e); // NOI18N
        }

        Field keyFields[] = foreignConfig.getKeyFields();
        String keyFieldNames[] = foreignConfig.getKeyFieldNames();
        for (int i = 0; i < keyFields.length && oid != null; i++) {
            Field keyField = keyFields[i];

            for (int j = 0; j < foreignFields.size() && oid != null; j++) {
                LocalFieldDesc fa = (LocalFieldDesc) foreignFields.get(j);

                if (fa.getName().compareTo(keyFieldNames[i]) == 0) {
                    LocalFieldDesc la = (LocalFieldDesc) localFields.get(j);
                    Object keyFieldValue = null;

                    if (la == fieldDesc) {
                        keyFieldValue = value;
                    } else if (sm.getSetMaskBit(la.absoluteID) && !sm.getSetMaskBit(absoluteID)) {
                        keyFieldValue = la.getValue(sm.getBeforeImage());
                    } else {
                        keyFieldValue = la.getValue(sm);
                    }

                    if (keyFieldValue != null) {
                        try {
                            // We need to convert the keyFieldValue to the proper type before
                            // setting it.
                            keyField.set(oid, fa.convertValue(keyFieldValue, sm));
                        } catch (IllegalAccessException e) {
                            throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
                                    "core.statemanager.cantsetkeyfield", keyField.getName()), e); // NOI18N
                        }
                    } else {
                        oid = null;
                    }
                }
            }
        }

        return oid;
    }

    //
    // ------------ Initialisation methods ------------
    //

    // TODO: Should be removed, once computeTrackeRelationshipFields is removed.
    private void setInverseRelationshipField(ForeignFieldDesc f) {
        inverseRelationshipField = f;
    }

    void computeTrackedRelationshipFields() {
        // If the field is a ForeignFieldDesc, we only need
        // to compare against other ForeignFieldDesc. The reason
        // is that ForeignFieldDesc implicitly tracks a LocalFieldDesc
        // (foreign key field) via relationship updates.

        ForeignFieldDesc inverseField = getInverseRelationshipField();

        for (int k = 0; k < classDesc.foreignFields.size(); k++) {
            ForeignFieldDesc tf = (ForeignFieldDesc) classDesc.foreignFields.get(k);

            if ((this != tf) && (getType() == tf.getType()) && (compareColumns(this, tf) == true)) {
                if ((inverseField != null) &&
                        (tf.getInverseRelationshipField() == null)) {
                    tf.setInverseRelationshipField(inverseField);
                }

                // Mark the relationship field tracking the foreign key as primary.
                if ((sqlProperties & FieldDesc.PROP_SECONDARY_TRACKED_FIELD) == 0) {
                    sqlProperties |= FieldDesc.PROP_PRIMARY_TRACKED_FIELD;
                }

                if ((tf.sqlProperties & FieldDesc.PROP_PRIMARY_TRACKED_FIELD) == 0) {
                    tf.sqlProperties |= FieldDesc.PROP_SECONDARY_TRACKED_FIELD;
                }

                addTrackedField(tf);
            }
        }
    }

    /**
     * Initializes relationship field information.
     *
     * @param foreignConfig Class descriptor of the foreign class.
     * @param inverseField The inverse relationship field.
     */
    void fixupForeignReference(ClassDesc foreignConfig,
                               ForeignFieldDesc inverseField) {

        registerForeignConfig(foreignConfig, inverseField);
        initializeFieldLists();
        initializeIsMappedToPk();
        addForeignKeyFieldsToDFG();
    }

    /**
     * Registers the relationship information about the foreign class.
     *
     * @param foreignConfig Class descriptor of the foreign class.
     * @param inverseField The inverse relationship field.
     */
    private void registerForeignConfig(ClassDesc foreignConfig,
                                       ForeignFieldDesc inverseField) {
        boolean debug = logger.isLoggable(Logger.FINEST);

        if (debug) {
            Object[] items = new Object[] {classDesc, this, foreignConfig};
            logger.finest("sqlstore.model.classdesc.general", items); // NOI18N
        }

        // Remember the class descriptor for the foreign class.
        this.foreignConfig = foreignConfig;

        if (debug && inverseField != null) {
            logger.finest("sqlstore.model.classdesc.assocrelatedfield", inverseField); //NOI18N
        }

        setInverseRelationshipField(inverseField);
    }

    /**
     * Initialize the field lists based on column list information.
     */
    private void initializeFieldLists() {
        ClassDesc theConfig = classDesc;

        for (int i = 0; i < 4; i++) {
            ArrayList fields = null;
            ArrayList columns = null;

            switch (i) {
                case 0:
                    columns = localColumns;
                    fields = getLocalFields();
                    break;
                case 1:
                    columns = assocLocalColumns;
                    fields = getAssocLocalFields();
                    break;
                case 2:
                    columns = assocForeignColumns;
                    fields = getAssocForeignFields();
                    break;
                case 3:
                    columns = foreignColumns;
                    fields = getForeignFields();
                    theConfig = foreignConfig;
                    break;
            }

            if (columns == null) continue;

            for (int j = 0; j < columns.size(); j++) {
                ColumnElement ce = (ColumnElement) columns.get(j);
                TableElement te = ce.getDeclaringTable();

                if (te == null) {
                    throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
                            "core.configuration.columnnotable")); // NOI18N
                }

                fields.add(theConfig.getLocalFieldDesc(ce));
            }
        }
    }

    /**
     * Checks, if the relationship is mapped to primary key fields
     * <b>on the other relationship side</b> and sets the property
     * <code>isMappedToPk</code>.
     */
    private void initializeIsMappedToPk() {
        int count = foreignFields.size();

        isMappedToPk = !useJoinTable() &&
                foreignConfig.getKeyFields().length == count;

        for (int i = 0; i < count && isMappedToPk; i++) {
            isMappedToPk = ((LocalFieldDesc) foreignFields.get(i)).isKeyField();
        }
    }

    /**
     * Silently adding hidden (local foreign key) fields to the DFG.
     */
    private void addForeignKeyFieldsToDFG() {
        for (int i = 0; i < localFields.size(); i++)  {
            LocalFieldDesc lf = (LocalFieldDesc) localFields.get(i);

            if (lf.absoluteID < 0 && !useJoinTable()) {
                classDesc.getFetchGroup(GROUP_DEFAULT).add(lf);
            }
        }
    }

    /**
     * Determines the relationship side to be updated. Foreign key relationships
     * must always be updated on the side having the foreign key. Jointable
     * relationships can be handled from either side. To have unified
     * dependency management for foreign key _and_ jointable relationships,
     * it's essential that we apply the same rules defining the updated side
     * for foreign key and jointable relationships. We also need to always
     * update the same relationship side.
     */
    void fixupFieldProperties() {
        boolean refIntegrityUpdate = true;

        if (cardinalityUPB > 1) {
            // Collection side
            if (!(refIntegrityUpdate = checkReferentialIntegrityUpdatesForCollectionField())) {
                unsetReferentialIntegrityUpdateProperty();

                // We also unset the IN_CONCURRENCY_CHECK property because we can't
                // detect concurrency violation for changes made to a Collection
                sqlProperties &= ~(FieldDesc.PROP_IN_CONCURRENCY_CHECK);
            }
        } else {
            // Object side
            if (!(refIntegrityUpdate = checkReferentialIntegrityUpdatesForObjectField())) {
                unsetReferentialIntegrityUpdateProperty();

                sqlProperties &= ~(FieldDesc.PROP_IN_CONCURRENCY_CHECK);
            } else if (!useJoinTable()) {
                // This side will write relationship updates to the database.
                // Mark the local fields as part of the foreign key.
                for (int i = 0; i < localFields.size(); i++) {
                    ((LocalFieldDesc)localFields.get(i)).sqlProperties |= FieldDesc.PROP_FOREIGN_KEY_FIELD;
                }
            }
        }

        if (!refIntegrityUpdate) {
            unsetConcurrencyCheckProperty();
        }
    }

    /**
     * Checks, if datastore updates will be scheduled locally or on
     * the opposite relationship side.
     *
     * @return True, if datastore updates for this relationship will
     *   be scheduled locally, false otherwise.
     */
    private boolean checkReferentialIntegrityUpdatesForCollectionField() {
        boolean refIntegrityUpdate;
        ForeignFieldDesc inverseFieldDesc = getInverseRelationshipField();

        if (inverseFieldDesc == null) {
            refIntegrityUpdate = defineUpdatedSideXToM();
        } else {
            if (inverseFieldDesc.cardinalityUPB <= 1) {
                // For 1:N relationships, we always update the relationship side
                // which includes jointables. We indicate this by unsetting the
                // REF_INTEGRITY_UPDATES property which means that integrity updates
                // cannot be done locally.
                refIntegrityUpdate = false;
            } else {
                // For N:M relationships, we choose the updated relationship side
                // depending on the alphabethical order of the related class names.
                // N:M relationships must be mapped to a jointable. As jointable
                // entries can be created from each side, we just define the side.
                refIntegrityUpdate = defineUpdatedSideNToM(inverseFieldDesc);
            }
        }
        return refIntegrityUpdate;
    }

    /**
     * Checks, if datastore updates will be scheduled locally or on
     * the opposite relationship side.
     *
     * @return True, if datastore updates for this relationship will
     *   be scheduled locally, false otherwise.
     */
    private boolean checkReferentialIntegrityUpdatesForObjectField() {
        boolean refIntegrityUpdate;
        ForeignFieldDesc inverseFieldDesc = getInverseRelationshipField();

        if (inverseFieldDesc == null) {
            // Update the local side for one-way relationships.
            refIntegrityUpdate = true;
        } else {
            if (inverseFieldDesc.cardinalityUPB > 1) {
                // For 1:N relationships, we always update the local side
                // which includes jointables. We indicate this by setting the
                // REF_INTEGRITY_UPDATES property which means that integrity updates
                // are done locally.
                refIntegrityUpdate = true;
            } else {
                // For 1:1 relationships, we choose the updated relationship side
                // depending on the side having the foreign key. If the relationship
                // is mapped to primary key fields only, we consider if one side
                // is marked for cascade delete. Otherwise, we choose the updatable
                // side depending on the alphabethical order of the related class names.
                refIntegrityUpdate = defineUpdatedSide1To1(inverseFieldDesc);
            }
        }
        return refIntegrityUpdate;
    }

    /**
     * Defines the updated side for a collection relationship
     * mapped one-way.
     *
     * @return True, if datastore updates for this relationship will
     *   be scheduled locally, false otherwise.
     */
    private boolean defineUpdatedSideXToM() {
        boolean refIntegrityUpdate;
        if (!useJoinTable()) {
            // As this is a foreign key relationship, the other side must
            // be the one side. Foreign key relationships can be updated
            // from either side, even if the inverse side is unknown.
            refIntegrityUpdate = false;
        } else {
            // Update the local side for one-way relationships mapped to jointables.
            refIntegrityUpdate = true;
        }
        return refIntegrityUpdate;
    }

    /**
     * Defines the updated side for many-to-many relationships. As jointable
     * entries can be created from either side, we just define the side. The updated
     * side is chosen based on the alphabethical order of the related class names.
     *
     * @param inverseFieldDesc Inverse relationship field.
     *   Is guaranteed to be not null!
     * @return True, if datastore updates for this relationship will
     *   be scheduled locally, false otherwise.
     */
    private boolean defineUpdatedSideNToM(ForeignFieldDesc inverseFieldDesc) {
        boolean refIntegrityUpdate;
        final boolean updateOtherSide = (inverseFieldDesc.sqlProperties & FieldDesc.PROP_REF_INTEGRITY_UPDATES) > 0;

        if (!updateOtherSide) {
            // The opposite side has already been identified not to be updated.
            refIntegrityUpdate = true;
        } else {
            refIntegrityUpdate = chooseUpdatedSide(inverseFieldDesc);
        }
        return refIntegrityUpdate;
    }

    /**
     * Defines the updated side for one-to-one relationships. The updated side
     * is either defined by:
     *
     * <ol>
     * <li>The relationship side mapped to non-key columns.</li>
     * <li>The relationship side is identified as dependent side, or</li>
     * <li><ul>
     * <li>Mark both sides updatable for foreign key relationships.</li>
     * <li>Choose a side for jointable relationships.</li>
     * </ul></li>
     * </ol>
     *
     * @param inverseFieldDesc Inverse relationship field.
     *   Is guaranteed to be not null!
     * @return True, if datastore updates for this relationship will
     *   be scheduled locally, false otherwise.
     */
    private boolean defineUpdatedSide1To1(ForeignFieldDesc inverseFieldDesc) {
        boolean refIntegrityUpdate;
        final boolean updateOtherSide = (inverseFieldDesc.sqlProperties & FieldDesc.PROP_REF_INTEGRITY_UPDATES) > 0;

        if (!updateOtherSide) {
            // The opposite side has already been identified not to be updated.
            refIntegrityUpdate = true;
        } else if (!useJoinTable()) {
            // Check foreign key constraints and the dependent side.
            refIntegrityUpdate = checkForeignKeysAndDependentSide(inverseFieldDesc);
        } else {
            // Just check the dependent side.
            refIntegrityUpdate = checkDependentSide(inverseFieldDesc);
        }

        if (!refIntegrityUpdate && cardinalityLWB == 1) {
            // Lower bound should not be 1 in this case.
            // We silently set it to 0 for now.
            // RESOLVE: Shall we throw an exception here?
            cardinalityLWB = 0;
        }

        return refIntegrityUpdate;
    }

    /**
     * Checks, if one relationship side isn't mapped to primary key fields
     * (i.e. foreign key side). Based on the assumption, that jointable
     * relationships are always mapped to the primary key columns,
     * this method is called for foreign relationships only.
     *
     * @param inverseFieldDesc Inverse relationship field.
     *   Is guaranteed to be not null!
     * @return True, if datastore updates for this relationship will
     *   be scheduled locally, false otherwise.
     */
    private boolean checkForeignKeysAndDependentSide(ForeignFieldDesc inverseFieldDesc) {
        boolean refIntegrityUpdate;

        // Check the foreign keys.
        if (checkForeignKey(getLocalFields())) {
            // The foreign key is on the local side.
            refIntegrityUpdate = true;
        } else if (checkForeignKey(getForeignFields())) {
            // The foreign key is on the other side.
            refIntegrityUpdate = false;
        } else {
            // The relationship is mapped to primary key columns on either side.
            refIntegrityUpdate = checkDependentSide(inverseFieldDesc);
        }
        return refIntegrityUpdate;
    }

    /**
     * Marks the dependent side as identified by jdo meta-data for update.
     * If no side is marked dependent, the following rules apply:
     *
     * <ul>
     * <li>Foreign key relationships can be handled from both sides,
     * because it must be an 1:1 relationship mapped to primary key
     * fields.</li>
     * <li>Jointable relationships need to be updated from exactly one
     * side to implement unified dependency management. It's important to
     * check the dependency on both relationship sides before the responsible
     * side is chosen.</li>
     * </ul>
     *
     * @param inverseFieldDesc Inverse relationship field.
     *   Is guaranteed to be not null!
     * @return True, if datastore updates for this relationship will
     *   be scheduled locally, false otherwise.
     * @see #isDependentOn
     * @see SQLStateManager#manageDependencyForObjectField
     */
    private boolean checkDependentSide(ForeignFieldDesc inverseFieldDesc) {
        boolean refIntegrityUpdate;

        // Check if meta data identifies the dependent relationship side.
        if (this.isDependentOn(inverseFieldDesc)) {
            // This side is marked dependent and will be updated.
            refIntegrityUpdate = true;
        } else if (inverseFieldDesc.isDependentOn(this)) {
            // This side is marked as primary, the other side will be updated.
            refIntegrityUpdate = false;
        } else {
            if (!useJoinTable()) {
                // No information about the dependent side can be obtained and the
                // relationship is mapped to the primary key fields on both sides.
                // Relationship updates can be done from either side, but only during
                // instance creation/deletion.
                refIntegrityUpdate = true;
            } else {
                // No information about the dependent side can be obtained. If
                // the Employee-Insurance relationship is mapped to a jointable,
                // the dependent side can't be determinated. Identifying the
                // updated side is essential to provide unified dependency
                // management for the database updates, see
                // SQLStateManager#manageDependencyForObjectField
                refIntegrityUpdate = chooseUpdatedSide(inverseFieldDesc);
            }
        }
        return refIntegrityUpdate;
    }

    /**
     * Checks if at least one of the fields in <code>fieldList</code>
     * is updatable. In this case <code>fieldList</code> makes up a
     * foreign key. This is based on the assumption, that key fields
     * referred in relationships must not be updated. Not updatable fields
     * have property REF_INTEGRITY_UPDATES unset.
     *
     * @param fieldList Fields corresponding to datastore columns.
     *   The fields are either a <code>ForeignFieldDesc</code>'s local or foreign fields.
     * @return True, if <code>fieldList</code> is a foreign key, false otherwise.
     */
    private static boolean checkForeignKey(ArrayList fieldList) {
        for (int i = 0; i < fieldList.size(); i++) {
            FieldDesc lf = (FieldDesc) fieldList.get(i);

            if ((lf.sqlProperties & FieldDesc.PROP_REF_INTEGRITY_UPDATES) > 0) {
                // Based on the assumption, that referred key fields
                // have property REF_INTEGRITY_UPDATES unset, at least
                // one of the fields is not part of the key.
                return true;
            }
        }
        return false;
    }

    /**
     * Checks, if <code>this</code> relationship side is dependent
     * on the inverse side <code>inverseFieldDesc</code>. The
     * dependent side can be identified by the following criteria:
     *
     * <ul>
     * <li>This side has cardinalityLWB == 1.</li>
     * <li>The inverse side is marked for cascade delete.</li>
     * </ul>
     *
     * @param inverseFieldDesc Inverse relationship field.
     *   Is guaranteed to be not null!
     * @return true, if <code>this</code> relationship side is dependent
     * on <code>inverseFieldDesc</code>, false otherwise.
     */
    private boolean isDependentOn(ForeignFieldDesc inverseFieldDesc) {
        return (this.cardinalityLWB == 1 ||
                inverseFieldDesc.deleteAction == ForeignFieldDesc.ACT_CASCADE);
    }

    /**
     * Choose the updated relationship side based on the alphabethical
     * order of the related class names. For self relationships, the
     * field names itself are compared, too.
     *
     * @param inverseFieldDesc Inverse relationship field.
     *   Is guaranteed to be not null!
     * @return This method is guaranteed to identify the relationship
     *   side that will not be updated.
     */
    private boolean chooseUpdatedSide(ForeignFieldDesc inverseFieldDesc) {
        int comparison = classDesc.getName().compareTo(foreignConfig.getName());

        if (comparison == 0) {
            comparison = getName().compareTo(inverseFieldDesc.getName());
        }
        return comparison < 0;
    }

    /**
     * Unsets the REF_INTEGRITY_UPDATES property for this relationship field.
     * Datastore updates will be scheduled on the opposite relationship side.
     */
    private void unsetReferentialIntegrityUpdateProperty() {
        if (logger.isLoggable(Logger.FINEST)) {
            logger.finest("sqlstore.model.classdesc.unsetrefintegrityupdate", getName()); // NOI18N
        }

        sqlProperties &= ~(FieldDesc.PROP_REF_INTEGRITY_UPDATES);
    }

    /**
     * Unsets the IN_CONCURRENCY_CHECK property for all the
     * (hidden) local fields involved in this relationship.
     */
    private void unsetConcurrencyCheckProperty() {
        // Copy the field list temporarily.
        ArrayList fieldList = (ArrayList) getLocalFields().clone();

        if (useJoinTable()) {
            fieldList.addAll(getAssocLocalFields());
        }

        for (int j = 0; j < fieldList.size(); j++) {
            FieldDesc lf = (FieldDesc) fieldList.get(j);

            if (lf.absoluteID < 0) {
                if (logger.isLoggable(Logger.FINEST)) {
                    logger.finest("sqlstore.model.classdesc.unsetconcurrencychk", lf.getName()); // NOI18N
                }
                lf.sqlProperties &= ~(FieldDesc.PROP_IN_CONCURRENCY_CHECK);
            }
        }
    }

}
TOP

Related Classes of com.sun.jdo.spi.persistence.support.sqlstore.model.ForeignFieldDesc

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