0001: /**********************************************************************
0002: Copyright (c) 2003 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: Contributors:
0016: ...
0017: **********************************************************************/package org.jpox.sco;
0018:
0019: import java.io.ObjectStreamException;
0020: import java.util.Collection;
0021: import java.util.Iterator;
0022: import java.util.ListIterator;
0023:
0024: import org.jpox.ClassLoaderResolver;
0025: import org.jpox.ObjectManager;
0026: import org.jpox.StateManager;
0027: import org.jpox.exceptions.JPOXDataStoreException;
0028: import org.jpox.metadata.AbstractMemberMetaData;
0029: import org.jpox.metadata.FieldPersistenceModifier;
0030: import org.jpox.sco.exceptions.IncompatibleFieldTypeException;
0031: import org.jpox.sco.exceptions.NullsNotAllowedException;
0032: import org.jpox.sco.exceptions.QueryUnownedSCOException;
0033: import org.jpox.sco.queued.AddAtOperation;
0034: import org.jpox.sco.queued.AddOperation;
0035: import org.jpox.sco.queued.ClearOperation;
0036: import org.jpox.sco.queued.QueuedOperation;
0037: import org.jpox.sco.queued.RemoveAtOperation;
0038: import org.jpox.sco.queued.RemoveOperation;
0039: import org.jpox.sco.queued.SetOperation;
0040: import org.jpox.state.FetchPlanState;
0041: import org.jpox.state.StateManagerFactory;
0042: import org.jpox.store.DatastoreClass;
0043: import org.jpox.store.DatastoreIdentifier;
0044: import org.jpox.store.expression.QueryExpression;
0045: import org.jpox.store.mapping.CollectionMapping;
0046: import org.jpox.store.mapping.JavaTypeMapping;
0047: import org.jpox.store.query.Queryable;
0048: import org.jpox.store.query.ResultObjectFactory;
0049: import org.jpox.store.scostore.ListStore;
0050: import org.jpox.util.JPOXLogger;
0051: import org.jpox.util.Localiser;
0052: import org.jpox.util.StringUtils;
0053:
0054: /**
0055: * A mutable second-class ArrayList object.
0056: * This class extends ArrayList, using that class to contain the current objects, and the backing ListStore
0057: * to be the interface to the datastore. A "backing store" is not present for datastores that dont use
0058: * DatastoreClass, or if the container is serialised or non-persistent.
0059: *
0060: * <H3>Modes of Operation</H3>
0061: * The user can operate the list in 2 modes.
0062: * The <B>cached</B> mode will use an internal cache of the elements (in the "delegate") reading them at
0063: * the first opportunity and then using the cache thereafter.
0064: * The <B>non-cached</B> mode will just go direct to the "backing store" each call.
0065: *
0066: * <H3>Mutators</H3>
0067: * When the "backing store" is present any updates are passed direct to the datastore as well as to the "delegate".
0068: * If the "backing store" isn't present the changes are made to the "delegate" only.
0069: *
0070: * <H3>Accessors</H3>
0071: * When any accessor method is invoked, it typically checks whether the container has been loaded from its
0072: * "backing store" (where present) and does this as necessary. Some methods (<B>size()</B>) just check if
0073: * everything is loaded and use the delegate if possible, otherwise going direct to the datastore.
0074: *
0075: * @version $Revision: 1.95 $
0076: */
0077: public class ArrayList extends java.util.ArrayList implements SCOList,
0078: Cloneable, Queryable {
0079: protected static final Localiser LOCALISER = Localiser
0080: .getInstance("org.jpox.Localisation");
0081:
0082: private transient Object owner;
0083: private transient StateManager ownerSM;
0084: private transient String fieldName;
0085: private transient int fieldNumber;
0086: private transient ListStore backingStore;
0087: private transient Class elementType;
0088:
0089: /** Whether nulls are allowed. */
0090: private transient boolean allowNulls;
0091:
0092: /** The internal "delegate". */
0093: private java.util.ArrayList delegate;
0094:
0095: /** Whether to use "delegate" caching. */
0096: protected boolean useCache = true;
0097:
0098: /** Status flag whether the collection is loaded into the cache. */
0099: protected boolean isCacheLoaded = false;
0100:
0101: /** Whether the SCO is in "direct" or "queued" mode. */
0102: boolean queued = false;
0103:
0104: /** Queued operations when using "queued" mode. */
0105: private java.util.ArrayList queuedOperations = null;
0106:
0107: /**
0108: * Constructor, using the StateManager of the "owner" and the field name.
0109: * @param ownerSM The owner StateManager
0110: * @param fieldName The name of the field of the SCO.
0111: */
0112: public ArrayList(StateManager ownerSM, String fieldName) {
0113: this .fieldName = fieldName;
0114: this .ownerSM = ownerSM;
0115: this .allowNulls = false;
0116:
0117: // Set up our delegate
0118: this .delegate = new java.util.ArrayList();
0119:
0120: if (ownerSM != null) {
0121: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
0122: .getMetaDataForMember(fieldName);
0123: owner = ownerSM.getObject();
0124: fieldNumber = fmd.getAbsoluteFieldNumber();
0125: allowNulls = SCOUtils
0126: .allowNullsInContainer(allowNulls, fmd);
0127: if (ownerSM.getStoreManager().usesDatastoreClass()) {
0128: queued = SCOUtils.useContainerQueueing(ownerSM);
0129: useCache = SCOUtils.useContainerCache(ownerSM,
0130: fieldName);
0131: }
0132:
0133: if (ownerSM.getStoreManager().usesDatastoreClass()
0134: && !SCOUtils.collectionHasSerialisedElements(fmd)
0135: && fmd.getPersistenceModifier() == FieldPersistenceModifier.PERSISTENT) {
0136: ClassLoaderResolver clr = ownerSM.getObjectManager()
0137: .getClassLoaderResolver();
0138: DatastoreClass ownerTable = ownerSM.getStoreManager()
0139: .getDatastoreClass(owner.getClass().getName(),
0140: clr);
0141: JavaTypeMapping m = ownerTable.getFieldMapping(fmd);
0142: if (!(m instanceof CollectionMapping)) {
0143: throw new IncompatibleFieldTypeException(ownerSM,
0144: fieldName, java.util.ArrayList.class
0145: .getName(), fmd.getTypeName());
0146: }
0147:
0148: this .backingStore = (ListStore) ownerSM
0149: .getStoreManager().getStore(clr, fmd,
0150: java.util.ArrayList.class);
0151: this .elementType = clr.classForName(backingStore
0152: .getElementType());
0153: }
0154:
0155: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
0156: JPOXLogger.PERSISTENCE.debug(SCOUtils
0157: .getContainerInfoMessage(ownerSM, fieldName,
0158: this , useCache, queued, allowNulls,
0159: SCOUtils.useCachedLazyLoading(ownerSM,
0160: fieldName)));
0161: }
0162: }
0163: }
0164:
0165: /**
0166: * Method to initialise the SCO from an existing value.
0167: * @param o The object to set from
0168: * @param forInsert Whether the object needs inserting in the datastore with this value
0169: * @param forUpdate Whether to update the SCO in the datastore with this value
0170: */
0171: public void initialise(Object o, boolean forInsert,
0172: boolean forUpdate) {
0173: Collection c = (Collection) o;
0174: if (c != null) {
0175: // Check for the case of serialised PC elements, and assign StateManagers to the elements without
0176: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
0177: .getMetaDataForMember(fieldName);
0178: if (SCOUtils.collectionHasSerialisedElements(fmd)
0179: && fmd.getCollection().getElementClassMetaData() != null) {
0180: ObjectManager om = ownerSM.getObjectManager();
0181: Iterator iter = c.iterator();
0182: while (iter.hasNext()) {
0183: Object pc = iter.next();
0184: StateManager objSM = om.findStateManager(pc);
0185: if (objSM == null) {
0186: objSM = StateManagerFactory
0187: .newStateManagerForEmbedded(om, pc,
0188: false);
0189: objSM.addEmbeddedOwner(ownerSM, fieldNumber);
0190: }
0191: }
0192: }
0193:
0194: if (backingStore != null && useCache && !isCacheLoaded) {
0195: // Mark as loaded so we just use the value
0196: isCacheLoaded = true;
0197: }
0198:
0199: if (forInsert) {
0200: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
0201: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
0202: "023007", StringUtils.toJVMIDString(ownerSM
0203: .getObject()), fieldName, ""
0204: + c.size()));
0205: }
0206: addAll(c);
0207: } else if (forUpdate) {
0208: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
0209: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
0210: "023008", StringUtils.toJVMIDString(ownerSM
0211: .getObject()), fieldName, ""
0212: + c.size()));
0213: }
0214: clear();
0215: addAll(c);
0216: } else {
0217: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
0218: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
0219: "023007", StringUtils.toJVMIDString(ownerSM
0220: .getObject()), fieldName, ""
0221: + c.size()));
0222: }
0223: delegate.clear();
0224: delegate.addAll(c);
0225: }
0226: }
0227: }
0228:
0229: /**
0230: * Method to initialise the SCO for use.
0231: */
0232: public void initialise() {
0233: if (useCache
0234: && !SCOUtils.useCachedLazyLoading(ownerSM, fieldName)) {
0235: // Load up the collection now if not using lazy loading
0236: loadFromStore();
0237: }
0238: }
0239:
0240: // ----------------------- Implementation of SCO methods -------------------
0241:
0242: /**
0243: * Accessor for the unwrapped value that we are wrapping.
0244: * @return The unwrapped value
0245: */
0246: public Object getValue() {
0247: // TODO Cater for delegate not being used
0248: return delegate;
0249: }
0250:
0251: /**
0252: * Accessor for the element type.
0253: * @return the element type contained in the collection
0254: */
0255: public Class getElementType() {
0256: return elementType;
0257: }
0258:
0259: /**
0260: * Method to effect the load of the data in the SCO.
0261: * Used when the SCO supports lazy-loading to tell it to load all now.
0262: */
0263: public void load() {
0264: if (useCache) {
0265: loadFromStore();
0266: }
0267: }
0268:
0269: /**
0270: * Method to load all elements from the "backing store" where appropriate.
0271: */
0272: protected void loadFromStore() {
0273: if (backingStore != null && !isCacheLoaded) {
0274: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
0275: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("023006",
0276: StringUtils.toJVMIDString(ownerSM.getObject()),
0277: fieldName));
0278: }
0279: delegate.clear();
0280: Iterator iter = backingStore.iterator(ownerSM);
0281: while (iter.hasNext()) {
0282: delegate.add(iter.next());
0283: }
0284:
0285: isCacheLoaded = true;
0286: }
0287: }
0288:
0289: /**
0290: * Method to flush the changes to the datastore when operating in queued mode.
0291: * Does nothing in "direct" mode.
0292: */
0293: public void flush() {
0294: if (queued) {
0295: if (queuedOperations != null) {
0296: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
0297: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
0298: "023005", StringUtils.toJVMIDString(ownerSM
0299: .getObject()), fieldName));
0300: }
0301: Iterator iter = queuedOperations.iterator();
0302: while (iter.hasNext()) {
0303: QueuedOperation op = (QueuedOperation) iter.next();
0304: op.perform(backingStore, ownerSM);
0305: }
0306:
0307: queuedOperations.clear();
0308: queuedOperations = null;
0309: }
0310: }
0311: }
0312:
0313: /**
0314: * Convenience method to add a queued operation to the operations we perform at commit.
0315: * @param op The operation
0316: */
0317: protected void addQueuedOperation(QueuedOperation op) {
0318: if (queuedOperations == null) {
0319: queuedOperations = new java.util.ArrayList();
0320: }
0321: queuedOperations.add(op);
0322: }
0323:
0324: /**
0325: * Method to update an embedded element in this list.
0326: * @param element The element
0327: * @param fieldNumber Number of field in the element
0328: * @param value New value for this field
0329: */
0330: public void updateEmbeddedElement(Object element, int fieldNumber,
0331: Object value) {
0332: if (backingStore != null) {
0333: backingStore.updateEmbeddedElement(ownerSM, element,
0334: fieldNumber, value);
0335: }
0336: }
0337:
0338: /**
0339: * Accessor for the field name.
0340: * @return The field name
0341: */
0342: public String getFieldName() {
0343: return fieldName;
0344: }
0345:
0346: /**
0347: * Accessor for the owner object.
0348: * @return The owner object
0349: */
0350: public Object getOwner() {
0351: return owner;
0352: }
0353:
0354: /**
0355: * Method to unset the owner and field information.
0356: */
0357: public synchronized void unsetOwner() {
0358: if (ownerSM != null) {
0359: owner = null;
0360: ownerSM = null;
0361: fieldName = null;
0362: backingStore = null;
0363: }
0364: }
0365:
0366: /**
0367: * Utility to mark the object as dirty
0368: */
0369: public void makeDirty() {
0370: // Although we are never really "dirty", the owning object must be
0371: // marked dirty so that the proper state change occurs and its
0372: // jdoPreStore() gets called properly.
0373: if (ownerSM != null) {
0374: ownerSM.getObjectManager().getApiAdapter().makeFieldDirty(
0375: owner, fieldName);
0376: }
0377: }
0378:
0379: /**
0380: * Method to return a detached copy of the container.
0381: * Recurse sthrough the elements so that they are likewise detached.
0382: * @param state State for detachment process
0383: * @return The detached container
0384: */
0385: public Object detachCopy(FetchPlanState state) {
0386: java.util.Collection detached = new java.util.ArrayList();
0387: SCOUtils.detachCopyForCollection(ownerSM, toArray(), state,
0388: detached);
0389: return detached;
0390: }
0391:
0392: /**
0393: * Method to return an attached copy of the passed (detached) value. The returned attached copy
0394: * is a SCO wrapper. Goes through the existing elements in the store for this owner field and
0395: * removes ones no longer present, and adds new elements. All elements in the (detached)
0396: * value are attached.
0397: * @param value The new (collection) value
0398: */
0399: public void attachCopy(Object value) {
0400: java.util.Collection c = (java.util.Collection) value;
0401:
0402: // Attach all of the elements in the new list
0403: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
0404: .getMetaDataForMember(fieldName);
0405: boolean elementsWithoutIdentity = SCOUtils
0406: .collectionHasElementsWithoutIdentity(fmd);
0407:
0408: java.util.List attachedElements = new java.util.ArrayList(c
0409: .size());
0410: SCOUtils.attachCopyForCollection(ownerSM, c.toArray(),
0411: attachedElements, elementsWithoutIdentity);
0412:
0413: // Update the attached list with the detached elements
0414: SCOUtils.updateListWithListElements(this , attachedElements);
0415: }
0416:
0417: // ------------------------ Query Statement methods ------------------------
0418:
0419: /**
0420: * Method to generate a QueryStatement for the SCO.
0421: * @return The QueryStatement
0422: */
0423: public synchronized QueryExpression newQueryStatement() {
0424: return newQueryStatement(elementType, null);
0425: }
0426:
0427: /**
0428: * Method to return a QueryStatement, using the specified candidate class.
0429: * @param candidateClass The candidate class
0430: * @param candidateAlias Alias for candidate class
0431: * @return The QueryStatement
0432: */
0433: public synchronized QueryExpression newQueryStatement(
0434: Class candidateClass, DatastoreIdentifier candidateAlias) {
0435: if (backingStore == null) {
0436: throw new QueryUnownedSCOException();
0437: }
0438:
0439: return backingStore.newQueryStatement(ownerSM, candidateClass
0440: .getName(), candidateAlias);
0441: }
0442:
0443: /**
0444: * Method to return a ResultObjectFactory for the SCO.
0445: * @param stmt The QueryStatement
0446: * @param ignoreCache Whether to ignore the cache
0447: * @param resultClass Whether to create objects of a particular class
0448: * @param useFetchPlan whether to use the fetch plan to retrieve fields in the same query
0449: * @return The ResultObjectFactory
0450: */
0451: public synchronized ResultObjectFactory newResultObjectFactory(
0452: QueryExpression stmt, boolean ignoreCache,
0453: Class resultClass, boolean useFetchPlan) {
0454: if (backingStore == null) {
0455: throw new QueryUnownedSCOException();
0456: }
0457:
0458: return backingStore.newResultObjectFactory(ownerSM, stmt,
0459: ignoreCache, useFetchPlan);
0460: }
0461:
0462: // ----------------- Implementation of ArrayList methods -------------------
0463:
0464: /**
0465: * Clone operator to return a copy of this object.
0466: * <p>
0467: * Mutable second-class Objects are required to provide a public clone
0468: * method in order to allow for copying
0469: * PersistenceCapable objects. In contrast to Object.clone(), this method
0470: * must not throw a CloneNotSupportedException.
0471: * </p>
0472: * @return The cloned object
0473: */
0474: public Object clone() {
0475: if (useCache) {
0476: loadFromStore();
0477: }
0478:
0479: return delegate.clone();
0480: }
0481:
0482: /**
0483: * Method to return if the list contains this element.
0484: * @param element The element
0485: * @return Whether it is contained
0486: **/
0487: public boolean contains(Object element) {
0488: if (useCache && isCacheLoaded) {
0489: // If the "delegate" is already loaded, use it
0490: return delegate.contains(element);
0491: } else if (backingStore != null) {
0492: return backingStore.contains(ownerSM, element);
0493: }
0494:
0495: return delegate.contains(element);
0496: }
0497:
0498: /**
0499: * Accessor for whether a collection of elements are contained here.
0500: * @param c The collection of elements.
0501: * @return Whether they are contained.
0502: **/
0503: public synchronized boolean containsAll(java.util.Collection c) {
0504: if (useCache) {
0505: loadFromStore();
0506: } else if (backingStore != null) {
0507: java.util.HashSet h = new java.util.HashSet(c);
0508: Iterator iter = iterator();
0509: while (iter.hasNext()) {
0510: h.remove(iter.next());
0511: }
0512:
0513: return h.isEmpty();
0514: }
0515:
0516: return delegate.containsAll(c);
0517: }
0518:
0519: /**
0520: * Equality operator.
0521: * @param o The object to compare against.
0522: * @return Whether this object is the same.
0523: **/
0524: public synchronized boolean equals(Object o) {
0525: if (useCache) {
0526: loadFromStore();
0527: }
0528:
0529: if (o == this ) {
0530: return true;
0531: }
0532: if (!(o instanceof java.util.List)) {
0533: return false;
0534: }
0535: java.util.List l = (java.util.List) o;
0536: if (l.size() != size()) {
0537: return false;
0538: }
0539: Object[] elements = toArray();
0540: Object[] otherElements = l.toArray();
0541: for (int i = 0; i < elements.length; i++) {
0542: if (!elements[i].equals(otherElements[i])) {
0543: return false;
0544: }
0545: }
0546: return true;
0547: }
0548:
0549: /**
0550: * Method to retrieve an element no.
0551: * @param index The item to retrieve
0552: * @return The element at that position.
0553: **/
0554: public Object get(int index) {
0555: if (useCache) {
0556: loadFromStore();
0557: } else if (backingStore != null) {
0558: return backingStore.get(ownerSM, index);
0559: }
0560:
0561: return delegate.get(index);
0562: }
0563:
0564: /**
0565: * Method to the position of an element.
0566: * @param element The element.
0567: * @return The position.
0568: **/
0569: public int indexOf(Object element) {
0570: if (useCache) {
0571: loadFromStore();
0572: } else if (backingStore != null) {
0573: return backingStore.indexOf(ownerSM, element);
0574: }
0575:
0576: return delegate.indexOf(element);
0577: }
0578:
0579: /**
0580: * Accessor for whether the ArrayList is empty.
0581: * @return Whether it is empty.
0582: **/
0583: public boolean isEmpty() {
0584: return size() == 0;
0585: }
0586:
0587: /**
0588: * Method to retrieve an iterator for the list.
0589: * @return The iterator
0590: **/
0591: public Iterator iterator() {
0592: // Populate the cache if necessary
0593: if (useCache) {
0594: loadFromStore();
0595: }
0596:
0597: return new SCOListIterator(this , ownerSM, delegate,
0598: backingStore, useCache, -1);
0599: }
0600:
0601: /**
0602: * Method to retrieve a List iterator for the list.
0603: * @return The iterator
0604: **/
0605: public ListIterator listIterator() {
0606: // Populate the cache if necessary
0607: if (useCache) {
0608: loadFromStore();
0609: }
0610:
0611: return new SCOListIterator(this , ownerSM, delegate,
0612: backingStore, useCache, -1);
0613: }
0614:
0615: /**
0616: * Method to retrieve a List iterator for the list from the index.
0617: * @param index The start point
0618: * @return The iterator
0619: **/
0620: public ListIterator listIterator(int index) {
0621: // Populate the cache if necessary
0622: if (useCache) {
0623: loadFromStore();
0624: }
0625:
0626: return new SCOListIterator(this , ownerSM, delegate,
0627: backingStore, useCache, index);
0628: }
0629:
0630: /**
0631: * Method to retrieve the last position of the element.
0632: * @param element The element
0633: * @return The last position of this element in the List.
0634: **/
0635: public int lastIndexOf(Object element) {
0636: if (useCache) {
0637: loadFromStore();
0638: } else if (backingStore != null) {
0639: return backingStore.lastIndexOf(ownerSM, element);
0640: }
0641:
0642: return delegate.lastIndexOf(element);
0643: }
0644:
0645: /**
0646: * Accessor for the size of the ArrayList.
0647: * @return The size.
0648: **/
0649: public int size() {
0650: if (useCache && isCacheLoaded) {
0651: // If the "delegate" is already loaded, use it
0652: return delegate.size();
0653: } else if (backingStore != null) {
0654: return backingStore.size(ownerSM);
0655: }
0656:
0657: return delegate.size();
0658: }
0659:
0660: /**
0661: * Accessor for the subList of elements between from and to of the List
0662: * @param from Start index (inclusive)
0663: * @param to End index (exclusive)
0664: * @return The subList
0665: **/
0666: public synchronized java.util.List subList(int from, int to) {
0667: if (useCache) {
0668: loadFromStore();
0669: } else if (backingStore != null) {
0670: return backingStore.subList(ownerSM, from, to);
0671: }
0672:
0673: return delegate.subList(from, to);
0674: }
0675:
0676: /**
0677: * Method to return the list as an array.
0678: * @return The array
0679: **/
0680: public synchronized Object[] toArray() {
0681: if (useCache) {
0682: loadFromStore();
0683: } else if (backingStore != null) {
0684: return SCOUtils.toArray(backingStore, ownerSM);
0685: }
0686: return delegate.toArray();
0687: }
0688:
0689: /**
0690: * Method to return the list as an array.
0691: * @param a The runtime types of the array being defined by this param
0692: * @return The array
0693: **/
0694: public synchronized Object[] toArray(Object a[]) {
0695: if (useCache) {
0696: loadFromStore();
0697: } else if (backingStore != null) {
0698: return SCOUtils.toArray(backingStore, ownerSM, a);
0699: }
0700: return delegate.toArray(a);
0701: }
0702:
0703: // ---------------------------- Mutator methods ----------------------------
0704:
0705: /**
0706: * Method to add an element to a position in the ArrayList.
0707: * @param index The position
0708: * @param element The new element
0709: */
0710: public void add(int index, Object element) {
0711: // Reject inappropriate elements
0712: if (element == null && !allowNulls) {
0713: throw new NullsNotAllowedException(ownerSM, fieldName);
0714: }
0715:
0716: if (useCache) {
0717: loadFromStore();
0718: }
0719:
0720: if (backingStore != null) {
0721: if (queued && !ownerSM.getObjectManager().isFlushing()) {
0722: addQueuedOperation(new AddAtOperation(index, element));
0723: } else {
0724: try {
0725: backingStore.add(ownerSM, element, index,
0726: (useCache ? delegate.size() : -1));
0727: } catch (JPOXDataStoreException dse) {
0728: JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("023013",
0729: "add", fieldName, dse));
0730: }
0731: }
0732: }
0733:
0734: // Only make it dirty after adding the element(s) to the datastore so we give it time
0735: // to be inserted - otherwise jdoPreStore on this object would have been called before completing the addition
0736: makeDirty();
0737:
0738: delegate.add(index, element);
0739: }
0740:
0741: /**
0742: * Method to add an element to the ArrayList.
0743: * @param element The new element
0744: * @return Whether it was added ok.
0745: */
0746: public boolean add(Object element) {
0747: // Reject inappropriate elements
0748: if (element == null && !allowNulls) {
0749: throw new NullsNotAllowedException(ownerSM, fieldName);
0750: }
0751:
0752: if (useCache) {
0753: loadFromStore();
0754: }
0755:
0756: boolean backingSuccess = true;
0757: if (backingStore != null) {
0758: if (queued && !ownerSM.getObjectManager().isFlushing()) {
0759: addQueuedOperation(new AddOperation(element));
0760: } else {
0761: try {
0762: backingStore.add(ownerSM, element,
0763: (useCache ? delegate.size() : -1));
0764: } catch (JPOXDataStoreException dse) {
0765: JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("023013",
0766: "add", fieldName, dse));
0767: backingSuccess = false;
0768: }
0769: }
0770: }
0771:
0772: // Only make it dirty after adding the element(s) to the datastore so we give it time
0773: // to be inserted - otherwise jdoPreStore on this object would have been called before completing the addition
0774: makeDirty();
0775:
0776: boolean delegateSuccess = delegate.add(element);
0777: return (backingStore != null ? backingSuccess : delegateSuccess);
0778: }
0779:
0780: /**
0781: * Method to add a Collection to the ArrayList.
0782: * @param elements The collection
0783: * @return Whether it was added ok.
0784: */
0785: public boolean addAll(Collection elements) {
0786: if (useCache) {
0787: loadFromStore();
0788: }
0789:
0790: boolean backingSuccess = true;
0791: if (backingStore != null) {
0792: if (queued && !ownerSM.getObjectManager().isFlushing()) {
0793: Iterator iter = elements.iterator();
0794: while (iter.hasNext()) {
0795: addQueuedOperation(new AddOperation(iter.next()));
0796: }
0797: } else {
0798: try {
0799: backingSuccess = backingStore
0800: .addAll(ownerSM, elements,
0801: (useCache ? delegate.size() : -1));
0802: } catch (JPOXDataStoreException dse) {
0803: JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("023013",
0804: "addAll", fieldName, dse));
0805: backingSuccess = false;
0806: }
0807: }
0808: }
0809:
0810: // Only make it dirty after adding the element(s) to the datastore so we give it time
0811: // to be inserted - otherwise jdoPreStore on this object would have been called before completing the addition
0812: makeDirty();
0813:
0814: boolean delegateSuccess = delegate.addAll(elements);
0815: return (backingStore != null ? backingSuccess : delegateSuccess);
0816: }
0817:
0818: /**
0819: * Method to add a Collection to a position in the ArrayList.
0820: * @param index Position to insert the collection.
0821: * @param elements The collection
0822: * @return Whether it was added ok.
0823: */
0824: public boolean addAll(int index, Collection elements) {
0825: if (useCache) {
0826: loadFromStore();
0827: }
0828:
0829: boolean backingSuccess = true;
0830: if (backingStore != null) {
0831: if (queued && !ownerSM.getObjectManager().isFlushing()) {
0832: Iterator iter = elements.iterator();
0833: while (iter.hasNext()) {
0834: addQueuedOperation(new AddOperation(iter.next()));
0835: }
0836: } else {
0837: try {
0838: backingSuccess = backingStore.addAll(ownerSM,
0839: elements, index, (useCache ? delegate
0840: .size() : -1));
0841: } catch (JPOXDataStoreException dse) {
0842: JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("023013",
0843: "addAll", fieldName, dse));
0844: backingSuccess = false;
0845: }
0846: }
0847: }
0848:
0849: // Only make it dirty after adding the element(s) to the datastore so we give it time
0850: // to be inserted - otherwise jdoPreStore on this object would have been called before completing the addition
0851: makeDirty();
0852:
0853: boolean delegateSuccess = delegate.addAll(index, elements);
0854: return (backingStore != null ? backingSuccess : delegateSuccess);
0855: }
0856:
0857: /**
0858: * Method to clear the ArrayList.
0859: */
0860: public synchronized void clear() {
0861: makeDirty();
0862:
0863: if (backingStore != null) {
0864: if (queued && !ownerSM.getObjectManager().isFlushing()) {
0865: addQueuedOperation(new ClearOperation());
0866: } else {
0867: backingStore.clear(ownerSM);
0868: }
0869: }
0870: delegate.clear();
0871: }
0872:
0873: /**
0874: * Method to remove an element from the List
0875: * @param element The Element to remove
0876: * @return Whether it was removed successfully.
0877: **/
0878: public synchronized boolean remove(Object element) {
0879: return remove(element, true);
0880: }
0881:
0882: /**
0883: * Method to remove an element from the collection, and observe the flag for whether to allow cascade delete.
0884: * @param element The element
0885: * @param allowCascadeDelete Whether to allow cascade delete
0886: */
0887: public boolean remove(Object element, boolean allowCascadeDelete) {
0888: makeDirty();
0889:
0890: if (useCache) {
0891: loadFromStore();
0892: }
0893:
0894: int size = (useCache ? delegate.size() : -1);
0895: boolean delegateSuccess = delegate.remove(element);
0896:
0897: boolean backingSuccess = true;
0898: if (backingStore != null) {
0899: if (queued && !ownerSM.getObjectManager().isFlushing()) {
0900: backingSuccess = contains(element);
0901: if (backingSuccess) {
0902: addQueuedOperation(new RemoveOperation(element,
0903: allowCascadeDelete));
0904: }
0905: } else {
0906: try {
0907: backingSuccess = backingStore.remove(ownerSM,
0908: element, size, allowCascadeDelete);
0909: } catch (JPOXDataStoreException dse) {
0910: JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("023013",
0911: "remove", fieldName, dse));
0912: backingSuccess = false;
0913: }
0914: }
0915: }
0916:
0917: return (backingStore != null ? backingSuccess : delegateSuccess);
0918: }
0919:
0920: /**
0921: * Method to remove an element from the ArrayList.
0922: * @param index The element position.
0923: * @return The object that was removed
0924: */
0925: public synchronized Object remove(int index) {
0926: makeDirty();
0927:
0928: if (useCache) {
0929: loadFromStore();
0930: }
0931:
0932: int size = (useCache ? delegate.size() : -1);
0933: Object delegateObject = delegate.remove(index);
0934:
0935: Object backingObject = null;
0936: if (backingStore != null) {
0937: if (queued && !ownerSM.getObjectManager().isFlushing()) {
0938: addQueuedOperation(new RemoveAtOperation(index));
0939: } else {
0940: try {
0941: backingObject = backingStore.remove(ownerSM, index,
0942: size);
0943: } catch (JPOXDataStoreException dse) {
0944: JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("023013",
0945: "remove", fieldName, dse));
0946: backingObject = null;
0947: }
0948: }
0949: }
0950:
0951: return (backingStore != null ? backingObject : delegateObject);
0952: }
0953:
0954: /**
0955: * Method to remove a collection of elements from the List.
0956: * @param elements Collection of elements to remove
0957: * @return Whether it was successful.
0958: */
0959: public boolean removeAll(Collection elements) {
0960: makeDirty();
0961:
0962: if (useCache) {
0963: loadFromStore();
0964: }
0965:
0966: int size = (useCache ? delegate.size() : -1);
0967: boolean delegateSuccess = delegate.removeAll(elements);
0968:
0969: boolean backingSuccess = true;
0970: if (backingStore != null) {
0971: if (queued && !ownerSM.getObjectManager().isFlushing()) {
0972: backingSuccess = false;
0973: Iterator iter = elements.iterator();
0974: while (iter.hasNext()) {
0975: Object element = iter.next();
0976: boolean contained = contains(element);
0977: if (contained) {
0978: backingSuccess = true;
0979: addQueuedOperation(new RemoveOperation(iter
0980: .next()));
0981: }
0982: }
0983: } else {
0984: try {
0985: backingSuccess = backingStore.removeAll(ownerSM,
0986: elements, size);
0987: } catch (JPOXDataStoreException dse) {
0988: JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("023013",
0989: "removeAll", fieldName, dse));
0990: backingSuccess = false;
0991: }
0992: }
0993: }
0994:
0995: return (backingStore != null ? backingSuccess : delegateSuccess);
0996: }
0997:
0998: /**
0999: * Method to retain a Collection of elements (and remove all others).
1000: * @param c The collection to retain
1001: * @return Whether they were retained successfully.
1002: **/
1003: public synchronized boolean retainAll(java.util.Collection c) {
1004: makeDirty();
1005:
1006: if (useCache) {
1007: loadFromStore();
1008: }
1009:
1010: boolean modified = false;
1011: Iterator iter = iterator();
1012: while (iter.hasNext()) {
1013: Object element = iter.next();
1014: if (!c.contains(element)) {
1015: iter.remove();
1016: modified = true;
1017: }
1018: }
1019: return modified;
1020: }
1021:
1022: /**
1023: * JPOX wrapper addition that allows turning off of the dependent-field checks
1024: * when doing the position setting. This means that we can prevent the deletion of
1025: * the object that was previously in that position. This particular feature is used
1026: * when attaching a list field and where some elements have changed positions.
1027: * @param index The position
1028: * @param element The new element
1029: * @return The element previously at that position
1030: */
1031: public Object set(int index, Object element,
1032: boolean allowDependentField) {
1033: // Reject inappropriate elements
1034: if (element == null && !allowNulls) {
1035: throw new NullsNotAllowedException(ownerSM, fieldName);
1036: }
1037:
1038: makeDirty();
1039:
1040: if (useCache) {
1041: loadFromStore();
1042: }
1043:
1044: if (backingStore != null) {
1045: if (queued && !ownerSM.getObjectManager().isFlushing()) {
1046: addQueuedOperation(new SetOperation(index, element,
1047: allowDependentField));
1048: } else {
1049: backingStore.set(ownerSM, index, element,
1050: allowDependentField);
1051: }
1052: }
1053: return delegate.set(index, element);
1054: }
1055:
1056: /**
1057: * Method to set the element at a position in the ArrayList.
1058: * @param index The position
1059: * @param element The new element
1060: * @return The element previously at that position
1061: */
1062: public Object set(int index, Object element) {
1063: return set(index, element, true);
1064: }
1065:
1066: /**
1067: * The writeReplace method is called when ObjectOutputStream is preparing
1068: * to write the object to the stream. The ObjectOutputStream checks whether
1069: * the class defines the writeReplace method. If the method is defined, the
1070: * writeReplace method is called to allow the object to designate its
1071: * replacement in the stream. The object returned should be either of the
1072: * same type as the object passed in or an object that when read and
1073: * resolved will result in an object of a type that is compatible with all
1074: * references to the object.
1075: *
1076: * @return the replaced object
1077: * @throws ObjectStreamException
1078: */
1079: protected Object writeReplace() throws ObjectStreamException {
1080: if (useCache) {
1081: loadFromStore();
1082: return new java.util.ArrayList(delegate);
1083: } else {
1084: // TODO Cater for non-cached collection, load elements in a DB call.
1085: return new java.util.ArrayList(delegate);
1086: }
1087: }
1088: }
|