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