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