0001: /**********************************************************************
0002: Copyright (c) 2004 Andy Jefferson and others. All rights reserved.
0003: Licensed under the Apache License, Version 2.0 (the "License");
0004: you may not use this file except in compliance with the License.
0005: You may obtain a copy of the License at
0006:
0007: http://www.apache.org/licenses/LICENSE-2.0
0008:
0009: Unless required by applicable law or agreed to in writing, software
0010: distributed under the License is distributed on an "AS IS" BASIS,
0011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0012: See the License for the specific language governing permissions and
0013: limitations under the License.
0014:
0015:
0016: Contributors:
0017: 2007 Xuan Baldauf - Make error message "023011" a little bit more verbose
0018: ...
0019: **********************************************************************/package org.jpox.sco;
0020:
0021: import java.util.Collection;
0022: import java.util.Comparator;
0023: import java.util.HashMap;
0024: import java.util.Iterator;
0025: import java.util.List;
0026: import java.util.Map;
0027: import java.util.Set;
0028:
0029: import org.jpox.ClassLoaderResolver;
0030: import org.jpox.ObjectManager;
0031: import org.jpox.ObjectManagerHelper;
0032: import org.jpox.StateManager;
0033: import org.jpox.TypeManager;
0034: import org.jpox.api.ApiAdapter;
0035: import org.jpox.exceptions.JPOXException;
0036: import org.jpox.exceptions.JPOXObjectNotFoundException;
0037: import org.jpox.exceptions.JPOXUserException;
0038: import org.jpox.metadata.AbstractClassMetaData;
0039: import org.jpox.metadata.AbstractMemberMetaData;
0040: import org.jpox.state.FetchPlanState;
0041: import org.jpox.store.FieldValues;
0042: import org.jpox.store.scostore.CollectionStore;
0043: import org.jpox.store.scostore.MapStore;
0044: import org.jpox.store.scostore.SetStore;
0045: import org.jpox.util.ClassUtils;
0046: import org.jpox.util.JPOXLogger;
0047: import org.jpox.util.Localiser;
0048: import org.jpox.util.StringUtils;
0049:
0050: /**
0051: * Collection of utilities for second class wrappers and objects.
0052: *
0053: * @version $Revision: 1.80 $
0054: */
0055: public class SCOUtils {
0056: /** Localiser for messages. */
0057: private static final Localiser LOCALISER = Localiser
0058: .getInstance("org.jpox.Localisation");
0059:
0060: /**
0061: * Method to create a new SCO wrapper for a SCO type.
0062: * The SCO wrapper will be appropriate for the passed value (which represents the instantiated type of the field)
0063: * unless it is null when the wrapper will be appropriate for the declared type of the field.
0064: * While the "instantiated type" and the type of "value" should be the same when value is non-null, there are
0065: * situations where we need to create a List based collection yet have no value so pass in the declaredType
0066: * as Collection, instantiatedType as ArrayList, and value as null.
0067: * @param ownerSM State Manager for the owning object
0068: * @param fmd The Field MetaData for the related field.
0069: * @param declaredType The class of the object
0070: * @param instantiatedType Instantiated type for the field if known
0071: * @param value The value we are wrapping if known
0072: * @param forInsert Whether the SCO needs inserting in the datastore with this value
0073: * @param forUpdate Whether the SCO needs updating in the datastore with this value
0074: * @param replaceField Whether to replace the field with this value
0075: * @return The Second-Class Object
0076: * @throws JPOXUserException if an error occurred when creating the SCO instance
0077: */
0078: public static SCO newSCOInstance(StateManager ownerSM,
0079: AbstractMemberMetaData fmd, Class declaredType,
0080: Class instantiatedType, Object value, boolean forInsert,
0081: boolean forUpdate, boolean replaceField) {
0082: if (!fmd.getType().isAssignableFrom(declaredType)) {
0083: throw new JPOXUserException(LOCALISER.msg("023010",
0084: declaredType.getName(), fmd.getName(), fmd
0085: .getType()));
0086: }
0087:
0088: String typeName = declaredType.getName();
0089: if (instantiatedType != null) {
0090: // Use instantiated type if available
0091: typeName = instantiatedType.getName();
0092: }
0093: if (value != null) {
0094: // If we have a current value, use the actual type to define the wrapper type
0095: typeName = value.getClass().getName();
0096: }
0097:
0098: // Find the SCO wrapper type most suitable
0099: TypeManager typeMgr = fmd.getMetaDataManager().getOMFContext()
0100: .getTypeManager();
0101: boolean fullWrapper = ownerSM.getStoreManager()
0102: .usesDatastoreClass();
0103: Class wrapperType = null;
0104: if (fullWrapper) {
0105: wrapperType = typeMgr.getSecondClassWrapper(typeName);
0106: } else {
0107: wrapperType = typeMgr.getSecondClassWrapperSimple(typeName);
0108: }
0109:
0110: if (wrapperType == null) {
0111: if (value != null && typeMgr.isSecondClassWrapper(typeName)) {
0112: // The passed in value is a wrapper type already, so just return it!
0113: SCO sco = (SCO) value;
0114: if (replaceField) {
0115: // Replace the field with this value
0116: ownerSM.replaceField(fmd.getAbsoluteFieldNumber(),
0117: sco, false);
0118: }
0119: return sco;
0120: } else {
0121: // typeName not supported directly (no SCO wrapper for the precise type)
0122: if (instantiatedType != null) {
0123: // Try the instantiated type
0124: if (fullWrapper) {
0125: wrapperType = typeMgr
0126: .getSecondClassWrapper(instantiatedType
0127: .getName());
0128: } else {
0129: wrapperType = typeMgr
0130: .getSecondClassWrapperSimple(instantiatedType
0131: .getName());
0132: }
0133: }
0134: if (wrapperType == null) {
0135: // Try the declared type
0136: if (fullWrapper) {
0137: wrapperType = typeMgr
0138: .getSecondClassWrapper(declaredType
0139: .getName());
0140: } else {
0141: wrapperType = typeMgr
0142: .getSecondClassWrapperSimple(declaredType
0143: .getName());
0144: }
0145: }
0146: }
0147: }
0148: if (wrapperType == null) {
0149: throw new JPOXUserException(LOCALISER.msg("023011",
0150: declaredType.getName(), StringUtils
0151: .toJVMIDString(value), fmd
0152: .getFullFieldName()));
0153: }
0154:
0155: // Create the SCO wrapper
0156: SCO sco = (SCO) ClassUtils
0157: .newInstance(wrapperType, new Class[] {
0158: org.jpox.StateManager.class, String.class },
0159: new Object[] { ownerSM, fmd.getName() });
0160:
0161: if (replaceField) {
0162: // Replace the field with this value before initialising it
0163: ownerSM.replaceField(fmd.getAbsoluteFieldNumber(), sco,
0164: false);
0165: }
0166:
0167: // Initialise the SCO for use
0168: if (value != null) {
0169: // Apply the existing value
0170: sco.initialise(value, forInsert, forUpdate);
0171: } else {
0172: // Just create it empty and load from the datastore
0173: sco.initialise();
0174: }
0175:
0176: return sco;
0177: }
0178:
0179: /**
0180: * Utility to generate a message representing the SCO container wrapper and its capabilities.
0181: * @param ownerSM StateManager for the owner
0182: * @param fieldName Field with the container
0183: * @param cont The SCOContainer
0184: * @param useCache Whether to use caching of values in the container
0185: * @param queued Whether operations are queued in the wrapper
0186: * @param allowNulls Whether to allow nulls
0187: * @param lazyLoading Whether to use lazy loading in the wrapper
0188: * @return The String
0189: */
0190: public static String getContainerInfoMessage(StateManager ownerSM,
0191: String fieldName, SCOContainer cont, boolean useCache,
0192: boolean queued, boolean allowNulls, boolean lazyLoading) {
0193: String msg = LOCALISER.msg("023004", StringUtils
0194: .toJVMIDString(ownerSM.getObject()), fieldName, cont
0195: .getClass().getName(), "[cache-values=" + useCache
0196: + ", lazy-loading="
0197: + SCOUtils.useCachedLazyLoading(ownerSM, fieldName)
0198: + ", queued-operations=" + queued + ", allow-nulls="
0199: + allowNulls + "]");
0200: return msg;
0201: }
0202:
0203: /**
0204: * Convenience method to generate a message containing the options of this SCO wrapper.
0205: * @param useCache Whether to cache the value in the wrapper (and not go to the datastore)
0206: * @param queued Whether it supports queueing of updates
0207: * @param allowNulls Whether it allows null entries
0208: * @param lazyLoading Whether it is lazy loaded
0209: * @return the message
0210: */
0211: public static String getSCOWrapperOptionsMessage(boolean useCache,
0212: boolean queued, boolean allowNulls, boolean lazyLoading) {
0213: StringBuffer str = new StringBuffer();
0214: if (useCache) {
0215: str.append("cached");
0216: }
0217: if (lazyLoading) {
0218: if (str.length() > 0) {
0219: str.append(",");
0220: }
0221: str.append("lazy-loaded");
0222: }
0223: if (queued) {
0224: if (str.length() > 0) {
0225: str.append(",");
0226: }
0227: str.append("queued");
0228: }
0229: if (allowNulls) {
0230: if (str.length() > 0) {
0231: str.append(",");
0232: }
0233: str.append("allowNulls");
0234: }
0235: return str.toString();
0236: }
0237:
0238: /**
0239: * Utility to return whether or not to allow nulls in the container for the specified field.
0240: * Uses the metadata extension "allow-nulls".
0241: * @param defaultValue Default value for the container
0242: * @param fmd MetaData for the field/property
0243: * @return Whether to allow nulls
0244: */
0245: public static boolean allowNullsInContainer(boolean defaultValue,
0246: AbstractMemberMetaData fmd) {
0247: boolean allow = defaultValue;
0248: if (fmd.hasExtension("allow-nulls")) {
0249: // Override the default "nulls" value with the user specification
0250: String extValue = fmd.getValueForExtension("allow-nulls");
0251: if (extValue.equalsIgnoreCase("true")) {
0252: allow = true;
0253: } else if (extValue.equalsIgnoreCase("false")) {
0254: allow = false;
0255: }
0256: }
0257: return allow;
0258: }
0259:
0260: /**
0261: * Utility to return whether to use queueing of operations in the SCO container.
0262: * @param ownerSM The StateManager for the SCO field
0263: * @return Whether to use queueing
0264: */
0265: public static boolean useContainerQueueing(StateManager ownerSM) {
0266: if (ownerSM.getObjectManager().getTransaction().getOptimistic()) {
0267: return true;
0268: } else {
0269: return ownerSM.getObjectManager().getOMFContext()
0270: .getPersistenceConfiguration()
0271: .getDatastoreTransactionsDelayOperations();
0272: }
0273: }
0274:
0275: /**
0276: * Utility to return whether or not to use the container cache for the
0277: * collection/map for the passed StateManager SCO.
0278: * @param ownerSM The StateManager for the SCO field
0279: * @param fieldName Name of the field.
0280: * @return Whether to use the cache.
0281: */
0282: public static boolean useContainerCache(StateManager ownerSM,
0283: String fieldName) {
0284: if (ownerSM == null) {
0285: return false;
0286: }
0287:
0288: // Get global value for PMF
0289: boolean useCache = ownerSM.getObjectManager().getOMFContext()
0290: .getPersistenceConfiguration().getCacheCollections();
0291:
0292: AbstractMemberMetaData fmd = ownerSM.getMetaDataManager()
0293: .getMetaDataForMember(
0294: ownerSM.getObject().getClass(),
0295: ownerSM.getObjectManager()
0296: .getClassLoaderResolver(), fieldName);
0297: if (fmd.getOrderMetaData() != null
0298: && !fmd.getOrderMetaData().isIndexedList()) {
0299: // "Ordered Lists" have to use caching since most List operations are impossible without indexing
0300: useCache = true;
0301: } else if (fmd.getContainer() != null
0302: && fmd.getContainer().hasExtension("cache")) {
0303: // User has marked the field caching policy
0304: useCache = (new Boolean(fmd.getContainer()
0305: .getValueForExtension("cache"))).booleanValue();
0306: }
0307:
0308: return useCache;
0309: }
0310:
0311: /**
0312: * Accessor for whether the use lazy loading when caching the collection.
0313: * @param ownerSM StateManager of the owning object
0314: * @param fieldName Name of the collection/map field
0315: * @return Whether to use lazy loading when caching the collection
0316: */
0317: public static boolean useCachedLazyLoading(StateManager ownerSM,
0318: String fieldName) {
0319: if (ownerSM == null) {
0320: return false;
0321: }
0322:
0323: boolean lazy = false;
0324:
0325: AbstractClassMetaData cmd = ownerSM.getClassMetaData();
0326: AbstractMemberMetaData fmd = cmd
0327: .getMetaDataForMember(fieldName);
0328: if (ownerSM.getObjectManager().getOMFContext()
0329: .getPersistenceConfiguration()
0330: .getCacheCollectionsLazy() != null) {
0331: // Global setting for PMF
0332: lazy = ownerSM.getObjectManager().getOMFContext()
0333: .getPersistenceConfiguration()
0334: .getCacheCollectionsLazy().booleanValue();
0335: } else if (fmd.getContainer() != null
0336: && fmd.getContainer()
0337: .hasExtension("cache-lazy-loading")) {
0338: // Check if this container has a MetaData value defined
0339: lazy = (new Boolean(fmd.getContainer()
0340: .getValueForExtension("cache-lazy-loading")))
0341: .booleanValue();
0342: } else {
0343: // Check if this SCO is in the current FetchPlan
0344: boolean inFP = false;
0345: int[] fpFields = ownerSM.getObjectManager().getFetchPlan()
0346: .getFetchPlanForClass(cmd)
0347: .getFieldsInActualFetchPlan();
0348: int fieldNo = fmd.getAbsoluteFieldNumber();
0349: if (fpFields != null && fpFields.length > 0) {
0350: for (int i = 0; i < fpFields.length; i++) {
0351: if (fpFields[i] == fieldNo) {
0352: inFP = true;
0353: break;
0354: }
0355: }
0356: }
0357: // Default to lazy loading when not in FetchPlan, and non-lazy when in FetchPlan
0358: lazy = !inFP;
0359: }
0360:
0361: return lazy;
0362: }
0363:
0364: /**
0365: * Convenience method to return if a collection field has elements without their own identity.
0366: * Checks if the elements are embedded in a join table, or in the main table, or serialised.
0367: * @param fmd MetaData for the field
0368: * @return Whether the elements have their own identity or not
0369: */
0370: public static boolean collectionHasElementsWithoutIdentity(
0371: AbstractMemberMetaData fmd) {
0372: boolean elementsWithoutIdentity = false;
0373: if (fmd.isSerialized()) {
0374: // Elements serialised into main table
0375: elementsWithoutIdentity = true;
0376: } else if (fmd.getElementMetaData() != null
0377: && fmd.getElementMetaData().getEmbeddedMetaData() != null
0378: && fmd.getJoinMetaData() != null) {
0379: // Elements embedded in join table using embedded mapping
0380: elementsWithoutIdentity = true;
0381: } else if (fmd.getCollection() != null
0382: && fmd.getCollection().isEmbeddedElement()) {
0383: // Elements are embedded (either serialised, or embedded in join table)
0384: elementsWithoutIdentity = true;
0385: }
0386:
0387: return elementsWithoutIdentity;
0388: }
0389:
0390: /**
0391: * Convenience method to return if a map field has keys without their own identity.
0392: * Checks if the keys are embedded in a join table, or in the main table, or serialised.
0393: * @param fmd MetaData for the field
0394: * @return Whether the keys have their own identity or not
0395: */
0396: public static boolean mapHasKeysWithoutIdentity(
0397: AbstractMemberMetaData fmd) {
0398: boolean keysWithoutIdentity = false;
0399: if (fmd.isSerialized()) {
0400: // Keys (and values) serialised into main table
0401: keysWithoutIdentity = true;
0402: } else if (fmd.getKeyMetaData() != null
0403: && fmd.getKeyMetaData().getEmbeddedMetaData() != null
0404: && fmd.getJoinMetaData() != null) {
0405: // Keys embedded in join table using embedded mapping
0406: keysWithoutIdentity = true;
0407: } else if (fmd.getMap() != null && fmd.getMap().isEmbeddedKey()) {
0408: // Keys are embedded (either serialised, or embedded in join table)
0409: keysWithoutIdentity = true;
0410: }
0411:
0412: return keysWithoutIdentity;
0413: }
0414:
0415: /**
0416: * Convenience method to return if a map field has values without their own identity.
0417: * Checks if the values are embedded in a join table, or in the main table, or serialised.
0418: * @param fmd MetaData for the field
0419: * @return Whether the values have their own identity or not
0420: */
0421: public static boolean mapHasValuesWithoutIdentity(
0422: AbstractMemberMetaData fmd) {
0423: boolean valuesWithoutIdentity = false;
0424: if (fmd.isSerialized()) {
0425: // Values (and keys) serialised into main table
0426: valuesWithoutIdentity = true;
0427: } else if (fmd.getValueMetaData() != null
0428: && fmd.getValueMetaData().getEmbeddedMetaData() != null
0429: && fmd.getJoinMetaData() != null) {
0430: // Values embedded in join table using embedded mapping
0431: valuesWithoutIdentity = true;
0432: } else if (fmd.getMap() != null
0433: && fmd.getMap().isEmbeddedValue()) {
0434: // Values are embedded (either serialised, or embedded in join table)
0435: valuesWithoutIdentity = true;
0436: }
0437:
0438: return valuesWithoutIdentity;
0439: }
0440:
0441: /**
0442: * Convenience method to return if a collection field has the elements serialised into the
0443: * table of the field as a single BLOB.
0444: * @param fmd MetaData for the field
0445: * @return Whether the elements are serialised (either explicitly or implicitly)
0446: */
0447: public static boolean collectionHasSerialisedElements(
0448: AbstractMemberMetaData fmd) {
0449: boolean serialised = fmd.isSerialized();
0450: if (fmd.getCollection() != null
0451: && fmd.getCollection().isEmbeddedElement()
0452: && fmd.getJoinMetaData() == null) {
0453: // Elements are embedded but no join table so we serialise
0454: serialised = true;
0455: }
0456:
0457: return serialised;
0458: }
0459:
0460: /**
0461: * Convenience method to return if an array field has the elements stored into the
0462: * table of the field as a single (BLOB) column.
0463: * @param fmd MetaData for the field
0464: * @return Whether the elements are stored in a single column
0465: */
0466: public static boolean arrayIsStoredInSingleColumn(
0467: AbstractMemberMetaData fmd) {
0468: boolean singleColumn = fmd.isSerialized();
0469: if (!singleColumn && fmd.getArray() != null
0470: && fmd.getJoinMetaData() == null) {
0471: if (fmd.getArray().isEmbeddedElement()) {
0472: // Elements are embedded but no join table so we store in a single column
0473: singleColumn = true;
0474: }
0475:
0476: Class elementClass = fmd.getType().getComponentType();
0477: ApiAdapter api = fmd.getMetaDataManager().getApiAdapter();
0478: if (!elementClass.isInterface()
0479: && !api.isPersistable(elementClass)) {
0480: // Array of non-PC with no join table so store in single column of main table
0481: singleColumn = true;
0482: }
0483: }
0484:
0485: return singleColumn;
0486: }
0487:
0488: /**
0489: * Convenience method to return if a map field has the keys/values serialised into the
0490: * table of the field as a single BLOB.
0491: * @param fmd MetaData for the field
0492: * @return Whether the keys and values are serialised (either explicitly or implicitly)
0493: */
0494: public static boolean mapHasSerialisedKeysAndValues(
0495: AbstractMemberMetaData fmd) {
0496: boolean inverseKeyField = false;
0497: if (fmd.getKeyMetaData() != null
0498: && fmd.getKeyMetaData().getMappedBy() != null) {
0499: inverseKeyField = true;
0500: }
0501: boolean inverseValueField = false;
0502: if (fmd.getValueMetaData() != null
0503: && fmd.getValueMetaData().getMappedBy() != null) {
0504: inverseValueField = true;
0505: }
0506: boolean serialised = fmd.isSerialized();
0507: if (fmd.getMap() != null
0508: && fmd.getJoinMetaData() == null
0509: && (fmd.getMap().isEmbeddedKey() || fmd.getMap()
0510: .isEmbeddedValue()) && !inverseKeyField
0511: && !inverseValueField) {
0512: // Keys/values are embedded but no join table so we serialise the whole map
0513: // Note that we explicitly excluded the JPOX extension 1-N Map with the key stored in the value
0514: serialised = true;
0515: }
0516:
0517: return serialised;
0518: }
0519:
0520: /**
0521: * Convenience method to update a collection to contain the elements in another collection.
0522: * Performs the updates by calling the necessary add(), remove() methods just for the
0523: * elements that have changed. Allows for some elements in one collection being attached
0524: * and some being detached (so having same id, but different state)
0525: * @param api API Adapter
0526: * @param coll The collection to update
0527: * @param newColl The new collection whose elements we need in "coll"
0528: */
0529: public static void updateCollectionWithCollection(ApiAdapter api,
0530: Collection coll, Collection newColl) {
0531: if (coll == null) {
0532: return;
0533: }
0534:
0535: if (newColl == null) {
0536: coll.clear();
0537: return;
0538: }
0539:
0540: // Remove all elements no longer in the Collection
0541: Iterator iter = coll.iterator();
0542: while (iter.hasNext()) {
0543: Object element = iter.next();
0544: if (api.isPersistable(element)) {
0545: Object id = api.getIdForObject(element);
0546:
0547: if (id != null) {
0548: // Element has an id so compare the id
0549: boolean present = false;
0550: Iterator newIter = newColl.iterator();
0551: while (newIter.hasNext()) {
0552: Object newElement = newIter.next();
0553: Object newId = api.getIdForObject(newElement);
0554:
0555: if (id.equals(newId)) {
0556: present = true;
0557: break;
0558: }
0559: }
0560:
0561: if (!present) {
0562: iter.remove();
0563: }
0564: } else {
0565: if (!newColl.contains(element)) {
0566: iter.remove();
0567: }
0568: }
0569: } else {
0570: if (!newColl.contains(element)) {
0571: iter.remove();
0572: }
0573: }
0574: }
0575:
0576: // Add all new elements
0577: Iterator newIter = newColl.iterator();
0578: while (newIter.hasNext()) {
0579: Object newElement = newIter.next();
0580:
0581: if (api.isPersistable(newElement)) {
0582: Object newId = api.getIdForObject(newElement);
0583: if (newId != null) {
0584: boolean present = false;
0585: iter = coll.iterator();
0586: while (iter.hasNext()) {
0587: Object element = iter.next();
0588: Object id = api.getIdForObject(element);
0589: if (newId.equals(id)) {
0590: present = true;
0591: break;
0592: }
0593: }
0594:
0595: if (!present) {
0596: coll.add(newElement);
0597: }
0598: } else {
0599: if (!coll.contains(newElement)) {
0600: coll.add(newElement);
0601: }
0602: }
0603: } else {
0604: if (!coll.contains(newElement)) {
0605: coll.add(newElement);
0606: }
0607: }
0608: }
0609: }
0610:
0611: /**
0612: * Convenience method to update a Store collection to contain the elements in another collection.
0613: * Performs the updates by calling the necessary add(), remove() methods just for the
0614: * elements that have changed. Allows for some elements in one collection being attached
0615: * and some being detached (so having same id, but different state)
0616: * @param store The store to apply changes to
0617: * @param ownerSM StateManager of the owner
0618: * @param newColl The new collection whose elements we need in "coll"
0619: */
0620: public static void updateStoreWithCollection(CollectionStore store,
0621: StateManager ownerSM, Collection newColl) {
0622: if (store == null || ownerSM == null) {
0623: return;
0624: }
0625:
0626: if (newColl == null) {
0627: store.clear(ownerSM);
0628: return;
0629: }
0630:
0631: // Retrieve the current elements from the store
0632: Collection coll = new java.util.HashSet();
0633: ApiAdapter api = ownerSM.getObjectManager().getApiAdapter();
0634: Iterator iter = store.iterator(ownerSM);
0635: while (iter.hasNext()) {
0636: coll.add(iter.next());
0637: }
0638:
0639: // Remove all elements no longer in the store
0640: iter = coll.iterator();
0641: while (iter.hasNext()) {
0642: Object element = iter.next();
0643: if (api.isPersistable(element)) {
0644: Object id = api.getIdForObject(element);
0645:
0646: if (id != null) {
0647: // Element has an id so compare the id
0648: boolean present = false;
0649: Iterator newIter = newColl.iterator();
0650: while (newIter.hasNext()) {
0651: Object newElement = newIter.next();
0652: Object newId = api.getIdForObject(newElement);
0653:
0654: if (id.equals(newId)) {
0655: present = true;
0656: break;
0657: }
0658: }
0659:
0660: if (!present) {
0661: store.remove(ownerSM, element, -1, true);
0662: }
0663: } else {
0664: if (!newColl.contains(element)) {
0665: store.remove(ownerSM, element, -1, true);
0666: }
0667: }
0668: } else {
0669: if (!newColl.contains(element)) {
0670: store.remove(ownerSM, element, -1, true);
0671: }
0672: }
0673: }
0674:
0675: // Add all new elements to the store
0676: Iterator newIter = newColl.iterator();
0677: while (newIter.hasNext()) {
0678: Object newElement = newIter.next();
0679:
0680: if (api.isPersistable(newElement)) {
0681: Object newId = api.getIdForObject(newElement);
0682: if (newId != null) {
0683: boolean present = false;
0684: iter = coll.iterator();
0685: while (iter.hasNext()) {
0686: Object element = iter.next();
0687: Object id = api.getIdForObject(element);
0688: if (newId.equals(id)) {
0689: present = true;
0690: break;
0691: }
0692: }
0693:
0694: if (!present) {
0695: store.add(ownerSM, newElement, -1);
0696: }
0697: } else {
0698: if (!coll.contains(newElement)) {
0699: store.add(ownerSM, newElement, -1);
0700: }
0701: }
0702: } else {
0703: if (!coll.contains(newElement)) {
0704: store.add(ownerSM, newElement, -1);
0705: }
0706: }
0707: }
0708: }
0709:
0710: /**
0711: * Convenience method for use by Collection/Set/HashSet attachCopy methods to
0712: * update the passed (attached) collection using the (attached) elements passed.
0713: * @param coll The current (attached) collection
0714: * @param elements The collection of (attached) elements needed.
0715: * @return If the Collection was updated
0716: */
0717: public static boolean updateCollectionWithCollectionElements(
0718: Collection coll, Collection elements) {
0719: boolean updated = false;
0720:
0721: // Delete any elements that are no longer in the collection
0722: Iterator attachedIter = coll.iterator();
0723: while (attachedIter.hasNext()) {
0724: Object attachedElement = attachedIter.next();
0725: if (!elements.contains(attachedElement)) {
0726: // No longer present so remove it
0727: attachedIter.remove();
0728: updated = true;
0729: }
0730: }
0731:
0732: // Add any new elements
0733: Iterator elementsIter = elements.iterator();
0734: while (elementsIter.hasNext()) {
0735: Object element = elementsIter.next();
0736: if (!coll.contains(element)) {
0737: // Not present so add it
0738: coll.add(element);
0739: updated = true;
0740: }
0741: }
0742: return updated;
0743: }
0744:
0745: /**
0746: * Convenience method for use by List attachCopy methods to update the
0747: * passed (attached) list using the (attached) list elements passed.
0748: * @param list The current (attached) list
0749: * @param elements The list of (attached) elements needed.
0750: * @return If the List was updated
0751: */
0752: public static boolean updateListWithListElements(List list,
0753: List elements) {
0754: boolean updated = false;
0755:
0756: // This method needs to take the existing list and generate a list
0757: // of add/remove/set/clear operations that change the list to the passed
0758: // elements in as efficient a way as possible. The simplest is
0759: // clear() then addAll()!, but if there are many objects and very little
0760: // has changed this would be very inefficient.
0761: // What we do currently is remove all elements no longer present, and then
0762: // add any missing elements, correcting the ordering. This can be non-optimal
0763: // in some situations.
0764: // TODO Optimise the process
0765: // Delete any elements that are no longer in the list
0766: java.util.ArrayList newCopy = new java.util.ArrayList(elements);
0767: Iterator attachedIter = list.iterator();
0768: while (attachedIter.hasNext()) {
0769: Object attachedElement = attachedIter.next();
0770: if (!newCopy.remove(attachedElement)) {
0771: // No longer present, so remove it
0772: attachedIter.remove();
0773: updated = true;
0774: }
0775: }
0776:
0777: // Add any new elements that have been added
0778: java.util.ArrayList oldCopy = new java.util.ArrayList(list);
0779: Iterator elementsIter = elements.iterator();
0780: while (elementsIter.hasNext()) {
0781: Object element = elementsIter.next();
0782: if (!oldCopy.remove(element)) {
0783: // Now present, so add it
0784: list.add(element);
0785: updated = true;
0786: }
0787: }
0788:
0789: // Update position of elements in the list to match the new order
0790: elementsIter = elements.iterator();
0791: int position = 0;
0792: while (elementsIter.hasNext()) {
0793: Object element = elementsIter.next();
0794: Object currentElement = list.get(position);
0795: boolean updatePosition = false;
0796: if ((element == null && currentElement != null)
0797: || (element != null && currentElement == null)) {
0798: // Cater for null elements in the list
0799: updatePosition = true;
0800: } else if (element != null && currentElement != null
0801: && !currentElement.equals(element)) {
0802: updatePosition = true;
0803: }
0804:
0805: if (updatePosition) {
0806: // Update the position, taking care not to have dependent-field deletes taking place
0807: ((SCOList) list).set(position, element, false);
0808: updated = true;
0809: }
0810:
0811: position++;
0812: }
0813:
0814: return updated;
0815: }
0816:
0817: /**
0818: * Convenience method for use by Map attachCopy methods to update the
0819: * passed (attached) map using the (attached) map keys/values passed.
0820: * @param api TODO
0821: * @param map The current (attached) map
0822: * @param keysValues The keys/values required
0823: * @return If the map was updated
0824: */
0825: public static boolean updateMapWithMapKeysValues(ApiAdapter api,
0826: Map map, Map keysValues) {
0827: boolean updated = false;
0828:
0829: // Take a copy of the map so we can call remove() on the map itself
0830: // TODO Change this to use EntrySet in the future.
0831: // EntrySet.iterator().remove() doesn't seem to feed through to the DB at the moment
0832: Map copy = new HashMap(map);
0833:
0834: // Delete any keys that are no longer in the Map
0835: Iterator attachedIter = copy.entrySet().iterator();
0836: while (attachedIter.hasNext()) {
0837: Map.Entry entry = (Map.Entry) attachedIter.next();
0838: Object key = entry.getKey();
0839: if (!keysValues.containsKey(key)) {
0840: map.remove(key);
0841: updated = true;
0842: }
0843: }
0844:
0845: // Add any new keys/values and update any changed values
0846: Iterator keysIter = keysValues.entrySet().iterator();
0847: while (keysIter.hasNext()) {
0848: Map.Entry entry = (Map.Entry) keysIter.next();
0849: Object key = entry.getKey();
0850: Object value = entry.getValue();
0851: if (!map.containsKey(key)) {
0852: // Not present so add it
0853: map.put(key, keysValues.get(key));
0854: updated = true;
0855: } else {
0856: // Update any values
0857: if (api.isPersistable(value)) {
0858: // In case they have changed the PC value for this key
0859: // TODO Check if the PC has changed ?
0860: map.put(key, value);
0861: } else {
0862: Object oldValue = map.get(key);
0863: if ((oldValue == null && value != null)
0864: || (oldValue != null && !oldValue
0865: .equals(value))) {
0866: map.put(key, value);
0867: }
0868: }
0869: }
0870: }
0871:
0872: return updated;
0873: }
0874:
0875: /**
0876: * Convenience method to populate the passed delegate Map with the keys/values from
0877: * the associated Store.
0878: * <P>
0879: * The issue here is that we need to load the keys and values in as few calls as possible.
0880: * The method employed here reads in the keys (if PersistenceCapable), then the values
0881: * (if PersistenceCapable), and then the "entries" (ids of keys and values) so we can
0882: * associate the keys to the values.
0883: * @param delegate The delegate
0884: * @param store The Store
0885: * @param ownerSM State Manager of the owner of the map.
0886: */
0887: public static void populateMapDelegateWithStoreData(Map delegate,
0888: MapStore store, StateManager ownerSM) {
0889: java.util.HashSet keys = new java.util.HashSet();
0890: if (!store.keysAreEmbedded() && !store.keysAreSerialised()) {
0891: // Retrieve the PersistenceCapable keys
0892: SetStore keystore = store.keySetStore(ownerSM
0893: .getObjectManager().getClassLoaderResolver());
0894: Iterator keyIter = keystore.iterator(ownerSM);
0895: while (keyIter.hasNext()) {
0896: keys.add(keyIter.next());
0897: }
0898: }
0899:
0900: java.util.HashSet values = new java.util.HashSet();
0901: if (!store.valuesAreEmbedded() && !store.valuesAreSerialised()) {
0902: // Retrieve the PersistenceCapable values
0903: SetStore valuestore = store.valueSetStore(ownerSM
0904: .getObjectManager().getClassLoaderResolver());
0905: Iterator valueIter = valuestore.iterator(ownerSM);
0906: while (valueIter.hasNext()) {
0907: values.add(valueIter.next());
0908: }
0909: }
0910:
0911: // Retrieve the entries (key-value pairs so we can associate them)
0912: ApiAdapter api = ownerSM.getObjectManager().getApiAdapter();
0913: SetStore entries = store.entrySetStore();
0914: Iterator entryIter = entries.iterator(ownerSM);
0915: while (entryIter.hasNext()) {
0916: Map.Entry entry = (Map.Entry) entryIter.next();
0917: Object key = entry.getKey();
0918: Object value = entry.getValue();
0919:
0920: Object trueKey = null;
0921: if (store.keysAreEmbedded() || store.keysAreSerialised()) {
0922: // The value has been retrieved as part of the entry
0923: trueKey = key;
0924: } else {
0925: // Find the true key (with its fields populated)
0926: // The key in the entry contains just the identity
0927: Object pcKeyId = api.getIdForObject(key);
0928: Iterator keyIter = keys.iterator();
0929: while (keyIter.hasNext()) {
0930: Object obj = keyIter.next();
0931: if (api.getIdForObject(obj).equals(pcKeyId)) {
0932: trueKey = obj;
0933: break;
0934: }
0935: }
0936: }
0937:
0938: Object trueValue = null;
0939: if (store.valuesAreEmbedded()
0940: || store.valuesAreSerialised()) {
0941: // The value has been retrieved as part of the entry
0942: trueValue = value;
0943: } else {
0944: // Find the true value (with its fields populated)
0945: // The value in the entry contains just the identity
0946: Object pcValueId = api.getIdForObject(value);
0947: Iterator valueIter = values.iterator();
0948: while (valueIter.hasNext()) {
0949: Object obj = valueIter.next();
0950: if (api.getIdForObject(obj).equals(pcValueId)) {
0951: trueValue = obj;
0952: }
0953: }
0954: }
0955: delegate.put(trueKey, trueValue);
0956: }
0957: keys.clear();
0958: values.clear();
0959: }
0960:
0961: /**
0962: * Returns <tt>true</tt> if this collection contains the specified
0963: * element. More formally, returns <tt>true</tt> if and only if this
0964: * collection contains at least one element <tt>it</tt> such that
0965: * <tt>(o==null ? it==null : o.equals(it))</tt>.<p>
0966: * <p>
0967: * This implementation iterates over the elements in the collection,
0968: * checking each element in turn for equality with the specified element.
0969: * @param backingStore the Store
0970: * @param sm the StateManager
0971: * @return <tt>true</tt> if this collection contains the specified element.
0972: */
0973: public static Object[] toArray(CollectionStore backingStore,
0974: StateManager sm) {
0975: Object[] result = new Object[backingStore.size(sm)];
0976: Iterator it = backingStore.iterator(sm);
0977: for (int i = 0; it.hasNext(); i++) {
0978: result[i] = it.next();
0979: }
0980: return result;
0981: }
0982:
0983: /**
0984: * Returns an array containing all of the elements in this collection;
0985: *
0986: * @param backingStore the Store
0987: * @param sm the StateManager
0988: * @param a the array into which the elements of the collection are to
0989: * be stored, if it is big enough; otherwise, a new array of the
0990: * same runtime type is allocated for this purpose.
0991: * @return an array containing the elements of the collection.
0992: *
0993: * @throws NullPointerException if the specified array is <tt>null</tt>.
0994: *
0995: * @throws ArrayStoreException if the runtime type of the specified array
0996: * is not a supertype of the runtime type of every element in this
0997: * collection.
0998: */
0999: public static Object[] toArray(CollectionStore backingStore,
1000: StateManager sm, Object a[]) {
1001: int size = backingStore.size(sm);
1002: if (a.length < size) {
1003: a = (Object[]) java.lang.reflect.Array.newInstance(a
1004: .getClass().getComponentType(), size);
1005: }
1006: Iterator it = backingStore.iterator(sm);
1007: for (int i = 0; i < size; i++) {
1008: a[i] = it.next();
1009: }
1010:
1011: if (a.length > size) {
1012: a[size] = null;
1013: }
1014:
1015: return a;
1016: }
1017:
1018: /**
1019: * Convenience method for creating a Comparator using extension metadata tags for the specified field.
1020: * Uses the extension key "comparator-name" (and allows for "comparatorName" - deprecated).
1021: * @param fmd The field that needs the comparator
1022: * @param clr ClassLoader resolver
1023: * @return The Comparator
1024: */
1025: public static Comparator getComparator(AbstractMemberMetaData fmd,
1026: ClassLoaderResolver clr) {
1027: Comparator comparator = null;
1028: String comparatorName = null;
1029: if (fmd.hasMap()
1030: && fmd.getMap().hasExtension("comparator-name")) {
1031: comparatorName = fmd.getMap().getValueForExtension(
1032: "comparator-name");
1033: } else if (fmd.hasCollection()
1034: && fmd.getCollection().hasExtension("comparator-name")) {
1035: comparatorName = fmd.getCollection().getValueForExtension(
1036: "comparator-name");
1037: } else if (fmd.hasMap()
1038: && fmd.getMap().hasExtension("comparatorName")) {
1039: comparatorName = fmd.getMap().getValueForExtension(
1040: "comparatorName");
1041: } else if (fmd.hasCollection()
1042: && fmd.getCollection().hasExtension("comparatorName")) {
1043: comparatorName = fmd.getCollection().getValueForExtension(
1044: "comparatorName");
1045: }
1046:
1047: if (comparatorName != null) {
1048: Class comparatorCls = null;
1049: try {
1050: comparatorCls = clr.classForName(comparatorName);
1051: comparator = (Comparator) ClassUtils.newInstance(
1052: comparatorCls, null, null);
1053: } catch (JPOXException jpe) {
1054: JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("023012", fmd
1055: .getFullFieldName(), comparatorName));
1056: }
1057: }
1058: return comparator;
1059: }
1060:
1061: /**
1062: * Convenience method to refresh fetch plan fields for all elements for a collection field.
1063: * All elements that are PersistenceCapable will be made transient.
1064: * @param ownerSM StateManager for the owning object with the collection
1065: * @param elements The elements in the collection
1066: */
1067: public static void refreshFetchPlanFieldsForCollection(
1068: StateManager ownerSM, Object[] elements) {
1069: ApiAdapter api = ownerSM.getObjectManager().getApiAdapter();
1070: for (int i = 0; i < elements.length; i++) {
1071: if (api.isPersistable(elements[i])) {
1072: ownerSM.getObjectManager().refreshObject(elements[i]);
1073: }
1074: }
1075: }
1076:
1077: /**
1078: * Convenience method to refresh fetch plan fields for all elements for a map field.
1079: * All elements that are PersistenceCapable will be made transient.
1080: * @param ownerSM StateManager for the owning object with the map
1081: * @param entries The entries in the map
1082: */
1083: public static void refreshFetchPlanFieldsForMap(
1084: StateManager ownerSM, Set entries) {
1085: ApiAdapter api = ownerSM.getObjectManager().getApiAdapter();
1086: for (Iterator it = entries.iterator(); it.hasNext();) {
1087: Map.Entry entry = (Map.Entry) it.next();
1088: Object val = entry.getValue();
1089: Object key = entry.getKey();
1090: if (api.isPersistable(key)) {
1091: ownerSM.getObjectManager().refreshObject(key);
1092: }
1093: if (api.isPersistable(val)) {
1094: ownerSM.getObjectManager().refreshObject(val);
1095: }
1096: }
1097: }
1098:
1099: /**
1100: * Convenience method to detach (recursively) all elements for a collection field.
1101: * All elements that are PersistenceCapable will be detached.
1102: * @param ownerSM StateManager for the owning object with the collection
1103: * @param elements The elements in the collection
1104: * @param state FetchPlan state
1105: */
1106: public static void detachForCollection(StateManager ownerSM,
1107: Object[] elements, FetchPlanState state) {
1108: ApiAdapter api = ownerSM.getObjectManager().getApiAdapter();
1109: for (int i = 0; i < elements.length; i++) {
1110: if (api.isPersistable(elements[i])) {
1111: ownerSM.getObjectManager().detachObject(elements[i],
1112: state);
1113: }
1114: }
1115: }
1116:
1117: /**
1118: * Convenience method to detach copies (recursively) of all elements for a collection field.
1119: * All elements that are PersistenceCapable will be detached.
1120: * @param ownerSM StateManager for the owning object with the collection
1121: * @param elements The elements in the collection
1122: * @param state FetchPlan state
1123: * @param detached Collection to add the detached copies to
1124: */
1125: public static void detachCopyForCollection(StateManager ownerSM,
1126: Object[] elements, FetchPlanState state, Collection detached) {
1127: ApiAdapter api = ownerSM.getObjectManager().getApiAdapter();
1128: for (int i = 0; i < elements.length; i++) {
1129: if (elements[i] == null) {
1130: detached.add(null);
1131: } else {
1132: Object object = elements[i];
1133: if (api.isPersistable(object)) {
1134: detached.add(ownerSM.getObjectManager()
1135: .detachObjectCopy(object, state));
1136: } else {
1137: detached.add(object);
1138: }
1139: }
1140: }
1141: }
1142:
1143: /**
1144: * Convenience method to attach (recursively) all elements for a collection field.
1145: * All elements that are PersistenceCapable will be attached.
1146: * @param ownerSM StateManager for the owning object with the collection
1147: * @param elements The elements in the collection
1148: * @param elementsWithoutIdentity Whether the elements have their own identity
1149: */
1150: public static void attachForCollection(StateManager ownerSM,
1151: Object[] elements, boolean elementsWithoutIdentity) {
1152: ApiAdapter api = ownerSM.getObjectManager().getApiAdapter();
1153: for (int i = 0; i < elements.length; i++) {
1154: if (api.isPersistable(elements[i])) {
1155: ownerSM.getObjectManager().attachObject(elements[i],
1156: elementsWithoutIdentity);
1157: }
1158: }
1159: }
1160:
1161: /**
1162: * Method to return an attached copy of the passed (detached) value. The returned attached copy
1163: * is a SCO wrapper. Goes through the existing elements in the store for this owner field and
1164: * removes ones no longer present, and adds new elements. All elements in the (detached)
1165: * value are attached.
1166: * @param ownerSM StateManager for the owning object with the collection
1167: * @param detachedElements The detached elements in the collection
1168: * @param attached Collection to add the attached copies to
1169: * @param elementsWithoutIdentity Whether the elements have their own identity
1170: */
1171: public static void attachCopyForCollection(StateManager ownerSM,
1172: Object[] detachedElements, Collection attached,
1173: boolean elementsWithoutIdentity) {
1174: ApiAdapter api = ownerSM.getObjectManager().getApiAdapter();
1175: for (int i = 0; i < detachedElements.length; i++) {
1176: if (api.isPersistable(detachedElements[i])
1177: && api.isDetachable(detachedElements[i])) {
1178: attached.add(ownerSM.getObjectManager()
1179: .attachObjectCopy(detachedElements[i],
1180: elementsWithoutIdentity));
1181: } else {
1182: attached.add(detachedElements[i]);
1183: }
1184: }
1185: }
1186:
1187: /**
1188: * Convenience method to detach (recursively) all elements for a map field.
1189: * All elements that are PersistenceCapable will be detached.
1190: * @param ownerSM StateManager for the owning object with the map
1191: * @param entries The entries in the map
1192: * @param state FetchPlan state
1193: */
1194: public static void detachForMap(StateManager ownerSM, Set entries,
1195: FetchPlanState state) {
1196: ApiAdapter api = ownerSM.getObjectManager().getApiAdapter();
1197: for (Iterator it = entries.iterator(); it.hasNext();) {
1198: Map.Entry entry = (Map.Entry) it.next();
1199: Object val = entry.getValue();
1200: Object key = entry.getKey();
1201: if (api.isPersistable(key)) {
1202: ownerSM.getObjectManager().detachObject(key, state);
1203: }
1204: if (api.isPersistable(val)) {
1205: ownerSM.getObjectManager().detachObject(val, state);
1206: }
1207: }
1208: }
1209:
1210: /**
1211: * Convenience method to detach copies (recursively) of all elements for a map field.
1212: * All elements that are PersistenceCapable will be detached.
1213: * @param ownerSM StateManager for the owning object with the map
1214: * @param entries The entries in the map
1215: * @param state FetchPlan state
1216: * @param detached Map to add the detached copies to
1217: */
1218: public static void detachCopyForMap(StateManager ownerSM,
1219: Set entries, FetchPlanState state, Map detached) {
1220: ApiAdapter api = ownerSM.getObjectManager().getApiAdapter();
1221: for (Iterator it = entries.iterator(); it.hasNext();) {
1222: Map.Entry entry = (Map.Entry) it.next();
1223: Object val = entry.getValue();
1224: Object key = entry.getKey();
1225: if (api.isPersistable(val)) {
1226: val = ownerSM.getObjectManager().detachObjectCopy(val,
1227: state);
1228: }
1229: if (api.isPersistable(key)) {
1230: key = ownerSM.getObjectManager().detachObjectCopy(key,
1231: state);
1232: }
1233: detached.put(key, val);
1234: }
1235: }
1236:
1237: /**
1238: * Convenience method to attach (recursively) all elements for a map field.
1239: * All elements that are PersistenceCapable will be attached.
1240: * @param ownerSM StateManager for the owning object with the map
1241: * @param entries The entries in the map
1242: * @param keysWithoutIdentity Whether the keys have their own identity
1243: * @param valuesWithoutIdentity Whether the values have their own identity
1244: */
1245: public static void attachForMap(StateManager ownerSM, Set entries,
1246: boolean keysWithoutIdentity, boolean valuesWithoutIdentity) {
1247: ApiAdapter api = ownerSM.getObjectManager().getApiAdapter();
1248: for (Iterator it = entries.iterator(); it.hasNext();) {
1249: Map.Entry entry = (Map.Entry) it.next();
1250: Object val = entry.getValue();
1251: Object key = entry.getKey();
1252: if (api.isPersistable(key)) {
1253: ownerSM.getObjectManager().attachObject(key,
1254: keysWithoutIdentity);
1255: }
1256: if (api.isPersistable(val)) {
1257: ownerSM.getObjectManager().attachObject(val,
1258: valuesWithoutIdentity);
1259: }
1260: }
1261: }
1262:
1263: /**
1264: * Method to return an attached copy of the passed (detached) value. The returned attached copy
1265: * is a SCO wrapper. Goes through the existing elements in the store for this owner field and
1266: * removes ones no longer present, and adds new elements. All elements in the (detached)
1267: * value are attached.
1268: * @param ownerSM StateManager for the owning object with the map
1269: * @param detachedEntries The detached entries in the map
1270: * @param attached Map to add the attached copies to
1271: * @param keysWithoutIdentity Whether the keys have their own identity
1272: * @param valuesWithoutIdentity Whether the values have their own identity
1273: */
1274: public static void attachCopyForMap(StateManager ownerSM,
1275: Set detachedEntries, Map attached,
1276: boolean keysWithoutIdentity, boolean valuesWithoutIdentity) {
1277: Iterator iter = detachedEntries.iterator();
1278: ApiAdapter api = ownerSM.getObjectManager().getApiAdapter();
1279: while (iter.hasNext()) {
1280: Map.Entry entry = (Map.Entry) iter.next();
1281: Object val = entry.getValue();
1282: Object key = entry.getKey();
1283: if (api.isPersistable(val) && api.isDetachable(val)) {
1284: val = ownerSM.getObjectManager().attachObjectCopy(val,
1285: valuesWithoutIdentity);
1286: }
1287: if (api.isPersistable(key) && api.isDetachable(key)) {
1288: key = ownerSM.getObjectManager().attachObjectCopy(key,
1289: keysWithoutIdentity);
1290: }
1291: attached.put(key, val);
1292: }
1293: }
1294:
1295: /**
1296: * Method to check if an object to be stored in a SCO container is already persistent, or is managed
1297: * by a different ObjectManager. If not persistent, this call will persist it. If not yet flushed to the
1298: * datastore this call will flush it.
1299: * @param om ObjectManager that we are using
1300: * @param object The object
1301: * @param fieldValues Values for any fields when persisting (if the object needs persisting)
1302: * @return Whether the object was persisted during this call
1303: */
1304: public static boolean validateObjectForWriting(ObjectManager om,
1305: Object object, FieldValues fieldValues) {
1306: boolean persisted = false;
1307: ApiAdapter api = om.getApiAdapter();
1308: if (api.isPersistable(object)) {
1309: ObjectManager objectOM = ObjectManagerHelper
1310: .getObjectManager(object);
1311: if (objectOM != null && om != objectOM) {
1312: throw new JPOXUserException(LOCALISER.msg("023009",
1313: StringUtils.toJVMIDString(object)), api
1314: .getIdForObject(object));
1315: } else if (!api.isPersistent(object)) {
1316: // Not persistent, so either is detached, or needs persisting for first time
1317: boolean exists = false;
1318: if (api.isDetached(object)) {
1319: if (om.getOMFContext()
1320: .getPersistenceConfiguration()
1321: .getAttachSameDatastore()) {
1322: // Assume that it is detached from this datastore
1323: exists = true;
1324: } else {
1325: // Check if the (attached) object exists in this datastore
1326: try {
1327: Object obj = om.findObject(api
1328: .getIdForObject(object), true,
1329: false, object.getClass().getName());
1330: if (obj != null) {
1331: // PM.getObjectById creates a dummy object to represent this object and automatically
1332: // enlists it in the txn. Evict it to avoid issues with reachability
1333: StateManager objSM = om
1334: .findStateManager(obj);
1335: if (objSM != null) {
1336: om.evictFromTransaction(objSM);
1337: }
1338: }
1339: exists = true;
1340: } catch (JPOXObjectNotFoundException onfe) {
1341: exists = false;
1342: }
1343: }
1344: }
1345: if (!exists) {
1346: // Persist the object
1347: om.persistObjectInternal(object, fieldValues, null,
1348: -1, StateManager.PC);
1349: persisted = true;
1350: }
1351: } else {
1352: // Persistent, but is it flushed to the datastore?
1353: StateManager objectSM = om.findStateManager(object);
1354: if (objectSM.isWaitingToBeFlushedToDatastore()) {
1355: // Newly persistent but still not flushed (e.g in optimistic txn)
1356: // Process any fieldValues
1357: if (fieldValues != null) {
1358: objectSM.loadFieldValues(fieldValues);
1359: }
1360:
1361: // Now flush it
1362: objectSM.flush();
1363: }
1364: }
1365: }
1366: return persisted;
1367: }
1368:
1369: /**
1370: * Method to check if objects to be stored in a SCO container are already persistent, or are managed by
1371: * a different ObjectManager. If not persistent, this call will persist them.
1372: * @param om ObjectManager being used
1373: * @param objects The objects (array, or Collection)
1374: */
1375: public static void validateObjectsForWriting(ObjectManager om,
1376: Object objects) {
1377: if (objects != null) {
1378: if (objects.getClass().isArray()) {
1379: if (!objects.getClass().getComponentType()
1380: .isPrimitive()) {
1381: Object[] obj = ((Object[]) objects);
1382: for (int i = 0; i < obj.length; i++) {
1383: validateObjectForWriting(om, obj[i], null);
1384: }
1385: }
1386: } else if (objects instanceof Collection) {
1387: Collection col = (Collection) objects;
1388: Iterator it = col.iterator();
1389: while (it.hasNext()) {
1390: validateObjectForWriting(om, it.next(), null);
1391: }
1392: }
1393: }
1394: }
1395: }
|