001: /**********************************************************************
002: Copyright (c) 2006 Andy Jefferson and others. All rights reserved.
003: Licensed under the Apache License, Version 2.0 (the "License");
004: you may not use this file except in compliance with the License.
005: You may obtain a copy of the License at
006:
007: http://www.apache.org/licenses/LICENSE-2.0
008:
009: Unless required by applicable law or agreed to in writing, software
010: distributed under the License is distributed on an "AS IS" BASIS,
011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: See the License for the specific language governing permissions and
013: limitations under the License.
014:
015: Contributors:
016: ...
017: **********************************************************************/package org.jpox.sco;
018:
019: import java.io.ObjectStreamException;
020: import java.util.AbstractQueue;
021: import java.util.Iterator;
022:
023: import org.jpox.ClassLoaderResolver;
024: import org.jpox.ObjectManager;
025: import org.jpox.StateManager;
026: import org.jpox.exceptions.JPOXDataStoreException;
027: import org.jpox.metadata.AbstractMemberMetaData;
028: import org.jpox.metadata.FieldPersistenceModifier;
029: import org.jpox.sco.exceptions.IncompatibleFieldTypeException;
030: import org.jpox.sco.exceptions.NullsNotAllowedException;
031: import org.jpox.sco.exceptions.QueryUnownedSCOException;
032: import org.jpox.sco.queued.AddOperation;
033: import org.jpox.sco.queued.ClearOperation;
034: import org.jpox.sco.queued.QueuedOperation;
035: import org.jpox.sco.queued.RemoveAtOperation;
036: import org.jpox.sco.queued.RemoveOperation;
037: import org.jpox.state.FetchPlanState;
038: import org.jpox.state.StateManagerFactory;
039: import org.jpox.store.DatastoreClass;
040: import org.jpox.store.DatastoreIdentifier;
041: import org.jpox.store.expression.QueryExpression;
042: import org.jpox.store.mapping.CollectionMapping;
043: import org.jpox.store.mapping.JavaTypeMapping;
044: import org.jpox.store.query.Queryable;
045: import org.jpox.store.query.ResultObjectFactory;
046: import org.jpox.store.scostore.ListStore;
047: import org.jpox.util.JPOXLogger;
048: import org.jpox.util.Localiser;
049: import org.jpox.util.StringUtils;
050:
051: /**
052: * A mutable second-class Queue object.
053: * This class extends AbstractQueue, using that class to contain the current objects, and the backing CollectionStore
054: * to be the interface to the datastore. A "backing store" is not present for datastores that dont use
055: * DatastoreClass, or if the container is serialised or non-persistent.
056: *
057: * <H3>Modes of Operation</H3>
058: * The user can operate the list in 2 modes.
059: * The <B>cached</B> mode will use an internal cache of the elements (in the "delegate") reading them at
060: * the first opportunity and then using the cache thereafter.
061: * The <B>non-cached</B> mode will just go direct to the "backing store" each call.
062: *
063: * <H3>Mutators</H3>
064: * When the "backing store" is present any updates are passed direct to the datastore as well as to the "delegate".
065: * If the "backing store" isn't present the changes are made to the "delegate" only.
066: *
067: * <H3>Accessors</H3>
068: * When any accessor method is invoked, it typically checks whether the container has been loaded from its
069: * "backing store" (where present) and does this as necessary. Some methods (<B>size()</B>) just check if
070: * everything is loaded and use the delegate if possible, otherwise going direct to the datastore.
071: *
072: * @version $Revision: 1.32 $
073: */
074: public class Queue extends AbstractQueue implements SCOCollection,
075: SCOMtoN, Cloneable, Queryable, java.io.Serializable {
076: /** Localiser for messages. */
077: private static final Localiser LOCALISER = Localiser.getInstance(
078: "org.jpox.Localisation", PriorityQueue.class
079: .getClassLoader());
080:
081: protected Object owner;
082: protected StateManager ownerSM;
083: protected String fieldName;
084: protected int fieldNumber;
085: protected Class elementType;
086: protected boolean allowNulls;
087:
088: /** The "backing store" (for use when not serialised). */
089: protected ListStore backingStore; // Really need a List since the Queue needs ordering
090:
091: /** The internal "delegate". */
092: protected java.util.Queue delegate;
093:
094: /** Whether to use "delegate" caching. */
095: protected boolean useCache = true;
096:
097: /** Status flag whether the collection is loaded into the cache. */
098: protected boolean isCacheLoaded = false;
099:
100: /** Whether the SCO is in "direct" or "queued" mode. */
101: boolean queued = false;
102:
103: /** Queued operations when using "queued" mode. */
104: private java.util.ArrayList queuedOperations = null;
105:
106: /**
107: * Constructor.
108: * @param ownerSM The State Manager for this set.
109: * @param fieldName Name of the field
110: **/
111: public Queue(StateManager ownerSM, String fieldName) {
112: this .ownerSM = ownerSM;
113: this .fieldName = fieldName;
114: this .allowNulls = false;
115:
116: // Set up our delegate
117: this .delegate = new java.util.PriorityQueue();
118:
119: if (ownerSM != null) {
120: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
121: .getMetaDataForMember(fieldName);
122: owner = ownerSM.getObject();
123: fieldNumber = fmd.getAbsoluteFieldNumber();
124: allowNulls = SCOUtils
125: .allowNullsInContainer(allowNulls, fmd);
126: if (ownerSM.getStoreManager().usesDatastoreClass()) {
127: queued = SCOUtils.useContainerQueueing(ownerSM);
128: useCache = SCOUtils.useContainerCache(ownerSM,
129: fieldName);
130: }
131:
132: if (ownerSM.getStoreManager().usesDatastoreClass()
133: && !SCOUtils.collectionHasSerialisedElements(fmd)
134: && fmd.getPersistenceModifier() == FieldPersistenceModifier.PERSISTENT) {
135: ClassLoaderResolver clr = ownerSM.getObjectManager()
136: .getClassLoaderResolver();
137: DatastoreClass ownerTable = ownerSM.getStoreManager()
138: .getDatastoreClass(owner.getClass().getName(),
139: clr);
140: JavaTypeMapping m = ownerTable.getFieldMapping(fmd);
141: if (!(m instanceof CollectionMapping)) {
142: throw new IncompatibleFieldTypeException(ownerSM,
143: fieldName, java.util.List.class.getName(),
144: fmd.getTypeName());
145: }
146:
147: this .backingStore = (ListStore) ownerSM
148: .getStoreManager().getStore(clr, fmd,
149: java.util.Queue.class);
150: this .elementType = clr.classForName(this .backingStore
151: .getElementType());
152: }
153:
154: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
155: JPOXLogger.PERSISTENCE.debug(SCOUtils
156: .getContainerInfoMessage(ownerSM, fieldName,
157: this , useCache, queued, allowNulls,
158: SCOUtils.useCachedLazyLoading(ownerSM,
159: fieldName)));
160: }
161: }
162: }
163:
164: /**
165: * Method to initialise the SCO from an existing value.
166: * @param o The object to set from
167: * @param forInsert Whether the object needs inserting in the datastore with this value
168: * @param forUpdate Whether to update the datastore with this value
169: */
170: public void initialise(Object o, boolean forInsert,
171: boolean forUpdate) {
172: java.util.Collection c = (java.util.Collection) o;
173: if (c != null) {
174: // Check for the case of serialised PC elements, and assign StateManagers to the elements without
175: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
176: .getMetaDataForMember(fieldName);
177: if (SCOUtils.collectionHasSerialisedElements(fmd)
178: && fmd.getCollection().getElementClassMetaData() != null) {
179: ObjectManager om = ownerSM.getObjectManager();
180: Iterator iter = c.iterator();
181: while (iter.hasNext()) {
182: Object pc = iter.next();
183: StateManager objSM = om.findStateManager(pc);
184: if (objSM == null) {
185: objSM = StateManagerFactory
186: .newStateManagerForEmbedded(om, pc,
187: false);
188: objSM.addEmbeddedOwner(ownerSM, fieldNumber);
189: }
190: }
191: }
192:
193: if (backingStore != null && useCache && !isCacheLoaded) {
194: // Mark as loaded
195: isCacheLoaded = true;
196: }
197:
198: if (forInsert) {
199: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
200: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
201: "023007", StringUtils.toJVMIDString(ownerSM
202: .getObject()), fieldName, ""
203: + c.size()));
204: }
205: addAll(c);
206: } else if (forUpdate) {
207: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
208: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
209: "023008", StringUtils.toJVMIDString(ownerSM
210: .getObject()), fieldName, ""
211: + c.size()));
212: }
213: clear();
214: addAll(c);
215: } else {
216: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
217: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
218: "023007", StringUtils.toJVMIDString(ownerSM
219: .getObject()), fieldName, ""
220: + c.size()));
221: }
222: delegate.clear();
223: delegate.addAll(c);
224: }
225: }
226: }
227:
228: /**
229: * Method to initialise the SCO for use.
230: */
231: public void initialise() {
232: if (useCache
233: && !SCOUtils.useCachedLazyLoading(ownerSM, fieldName)) {
234: // Load up the container now if not using lazy loading
235: loadFromStore();
236: }
237: }
238:
239: // ----------------------- Implementation of SCO methods -------------------
240:
241: /**
242: * Accessor for the unwrapped value that we are wrapping.
243: * @return The unwrapped value
244: */
245: public Object getValue() {
246: // TODO Cater for delegate not being used
247: return delegate;
248: }
249:
250: /**
251: * Accessor for the element type.
252: * @return the element type contained in the collection
253: */
254: public Class getElementType() {
255: return elementType;
256: }
257:
258: /**
259: * Method to effect the load of the data in the SCO.
260: * Used when the SCO supports lazy-loading to tell it to load all now.
261: */
262: public void load() {
263: if (useCache) {
264: loadFromStore();
265: }
266: }
267:
268: /**
269: * Method to load all elements from the "backing store" where appropriate.
270: */
271: protected void loadFromStore() {
272: if (backingStore != null && !isCacheLoaded) {
273: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
274: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("023006",
275: StringUtils.toJVMIDString(ownerSM.getObject()),
276: fieldName));
277: }
278: delegate.clear();
279: Iterator iter = backingStore.iterator(ownerSM);
280: while (iter.hasNext()) {
281: delegate.add(iter.next());
282: }
283:
284: isCacheLoaded = true;
285: }
286: }
287:
288: /**
289: * Method to flush the changes to the datastore when operating in queued mode.
290: * Does nothing in "direct" mode.
291: */
292: public void flush() {
293: if (queued) {
294: if (queuedOperations != null) {
295: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
296: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
297: "023005", StringUtils.toJVMIDString(ownerSM
298: .getObject()), fieldName));
299: }
300: Iterator iter = queuedOperations.iterator();
301: while (iter.hasNext()) {
302: QueuedOperation op = (QueuedOperation) iter.next();
303: op.perform(backingStore, ownerSM);
304: }
305:
306: queuedOperations.clear();
307: queuedOperations = null;
308: }
309: }
310: }
311:
312: /**
313: * Convenience method to add a queued operation to the operations we perform at commit.
314: * @param op The operation
315: */
316: protected void addQueuedOperation(QueuedOperation op) {
317: if (queuedOperations == null) {
318: queuedOperations = new java.util.ArrayList();
319: }
320: queuedOperations.add(op);
321: }
322:
323: /**
324: * Method to update an embedded element in this collection.
325: * @param element The element
326: * @param fieldNumber Number of field in the element
327: * @param value New value for this field
328: */
329: public void updateEmbeddedElement(Object element, int fieldNumber,
330: Object value) {
331: if (backingStore != null) {
332: backingStore.updateEmbeddedElement(ownerSM, element,
333: fieldNumber, value);
334: }
335: }
336:
337: /**
338: * Accessor for the field name.
339: * @return The field name
340: */
341: public String getFieldName() {
342: return fieldName;
343: }
344:
345: /**
346: * Accessor for the owner object.
347: * @return The owner object
348: */
349: public Object getOwner() {
350: return owner;
351: }
352:
353: /**
354: * Method to unset the owner and field information.
355: */
356: public synchronized void unsetOwner() {
357: if (ownerSM != null) {
358: owner = null;
359: ownerSM = null;
360: fieldName = null;
361: backingStore = null;
362: }
363: }
364:
365: /**
366: * Utility to mark the object as dirty
367: **/
368: public void makeDirty() {
369: /*
370: * Although we are never really "dirty", the owning object must be
371: * marked dirty so that the proper state change occurs and its
372: * jdoPreStore() gets called properly.
373: */
374: if (ownerSM != null) {
375: ownerSM.getObjectManager().getApiAdapter().makeFieldDirty(
376: owner, fieldName);
377: }
378: }
379:
380: /**
381: * Method to return a detached copy of the container.
382: * Recurses through the elements so that they are likewise detached.
383: * @param state State for detachment process
384: * @return The detached container
385: */
386: public Object detachCopy(FetchPlanState state) {
387: java.util.Collection detached = new java.util.PriorityQueue();
388: SCOUtils.detachCopyForCollection(ownerSM, toArray(), state,
389: detached);
390: return detached;
391: }
392:
393: /**
394: * Method to return an attached copy of the passed (detached) value. The returned attached copy
395: * is a SCO wrapper. Goes through the existing elements in the store for this owner field and
396: * removes ones no longer present, and adds new elements. All elements in the (detached)
397: * value are attached.
398: * @param value The new (collection) value
399: */
400: public void attachCopy(Object value) {
401: java.util.Collection c = (java.util.Collection) value;
402:
403: // Attach all of the elements in the new collection
404: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
405: .getMetaDataForMember(fieldName);
406: boolean elementsWithoutIdentity = SCOUtils
407: .collectionHasElementsWithoutIdentity(fmd);
408:
409: java.util.Collection attachedElements = new java.util.HashSet(c
410: .size());
411: SCOUtils.attachCopyForCollection(ownerSM, c.toArray(),
412: attachedElements, elementsWithoutIdentity);
413:
414: // Update the attached collection with the detached elements
415: SCOUtils.updateCollectionWithCollectionElements(this ,
416: attachedElements);
417: }
418:
419: // ------------------------ Query Statement methods ------------------------
420:
421: /**
422: * Method to generate a QueryStatement for the SCO.
423: * @return The QueryStatement
424: */
425: public synchronized QueryExpression newQueryStatement() {
426: return newQueryStatement(elementType, null);
427: }
428:
429: /**
430: * Method to return a QueryStatement, using the specified candidate class.
431: * @param candidateClass the candidate class
432: * @param candidateAlias Alias for the candidate
433: * @return The QueryStatement
434: */
435: public synchronized QueryExpression newQueryStatement(
436: Class candidateClass, DatastoreIdentifier candidateAlias) {
437: if (backingStore == null) {
438: throw new QueryUnownedSCOException();
439: }
440:
441: return backingStore.newQueryStatement(ownerSM, candidateClass
442: .getName(), candidateAlias);
443: }
444:
445: /**
446: * Method to return a ResultObjectFactory for the SCO.
447: * @param stmt The QueryStatement
448: * @param ignoreCache Whether to ignore the cache
449: * @param resultClass Whether to create objects of a particular class
450: * @param useFetchPlan whether to use the fetch plan to retrieve fields in the same query
451: * @return The ResultObjectFactory
452: */
453: public synchronized ResultObjectFactory newResultObjectFactory(
454: QueryExpression stmt, boolean ignoreCache,
455: Class resultClass, boolean useFetchPlan) {
456: if (backingStore == null) {
457: throw new QueryUnownedSCOException();
458: }
459:
460: return backingStore.newResultObjectFactory(ownerSM, stmt,
461: ignoreCache, useFetchPlan);
462: }
463:
464: // ---------------- Implementation of Queue methods -------------------
465:
466: /**
467: * Creates and returns a copy of this object.
468: * <P>
469: * Mutable second-class Objects are required to provide a public
470: * clone method in order to allow for copying PersistenceCapable
471: * objects. In contrast to Object.clone(), this method must not throw a
472: * CloneNotSupportedException.
473: * @return A clone of the object
474: */
475: public Object clone() {
476: if (useCache) {
477: loadFromStore();
478: }
479:
480: // return ((java.util.PriorityQueue)delegate).clone();
481: // TODO Implement cloning
482: return null;
483: }
484:
485: /**
486: * Accessor for whether an element is contained in the Collection.
487: * @param element The element
488: * @return Whether the element is contained here
489: **/
490: public synchronized boolean contains(Object element) {
491: if (useCache && isCacheLoaded) {
492: // If the "delegate" is already loaded, use it
493: return delegate.contains(element);
494: } else if (backingStore != null) {
495: return backingStore.contains(ownerSM, element);
496: }
497:
498: return delegate.contains(element);
499: }
500:
501: /**
502: * Accessor for whether a collection of elements are contained here.
503: * @param c The collection of elements.
504: * @return Whether they are contained.
505: **/
506: public synchronized boolean containsAll(java.util.Collection c) {
507: if (useCache) {
508: loadFromStore();
509: } else if (backingStore != null) {
510: java.util.HashSet h = new java.util.HashSet(c);
511: Iterator iter = iterator();
512: while (iter.hasNext()) {
513: h.remove(iter.next());
514: }
515:
516: return h.isEmpty();
517: }
518:
519: return delegate.containsAll(c);
520: }
521:
522: /**
523: * Equality operator.
524: * @param o The object to compare against.
525: * @return Whether this object is the same.
526: **/
527: public synchronized boolean equals(Object o) {
528: if (useCache) {
529: loadFromStore();
530: }
531:
532: if (o == this ) {
533: return true;
534: }
535: if (!(o instanceof java.util.Set)) {
536: return false;
537: }
538: java.util.Set c = (java.util.Set) o;
539:
540: return c.size() == size() && containsAll(c);
541: }
542:
543: /**
544: * Hashcode operator.
545: * @return The Hash code.
546: **/
547: public synchronized int hashCode() {
548: if (useCache) {
549: loadFromStore();
550: }
551: return delegate.hashCode();
552: }
553:
554: /**
555: * Accessor for whether the Collection is empty.
556: * @return Whether it is empty.
557: **/
558: public synchronized boolean isEmpty() {
559: return (size() == 0);
560: }
561:
562: /**
563: * Accessor for an iterator for the Collection.
564: * @return The iterator
565: **/
566: public synchronized Iterator iterator() {
567: // Populate the cache if necessary
568: if (useCache) {
569: loadFromStore();
570: }
571: return new SCOCollectionIterator(this , ownerSM, delegate,
572: backingStore, useCache);
573: }
574:
575: /**
576: * Method to peek at the next element in the Queue.
577: * @return The element
578: **/
579: public synchronized Object peek() {
580: if (useCache) {
581: loadFromStore();
582: } else if (backingStore != null) {
583: return backingStore.get(ownerSM, 0);
584: }
585:
586: return delegate.peek();
587: }
588:
589: /**
590: * Accessor for the size of the Collection.
591: * @return The size
592: **/
593: public synchronized int size() {
594: if (useCache && isCacheLoaded) {
595: // If the "delegate" is already loaded, use it
596: return delegate.size();
597: } else if (backingStore != null) {
598: return backingStore.size(ownerSM);
599: }
600:
601: return delegate.size();
602: }
603:
604: /**
605: * Method to return the Collection as an array.
606: * @return The array
607: **/
608: public synchronized Object[] toArray() {
609: if (useCache) {
610: loadFromStore();
611: } else if (backingStore != null) {
612: return SCOUtils.toArray(backingStore, ownerSM);
613: }
614: return delegate.toArray();
615: }
616:
617: /**
618: * Method to return the Collection as an array.
619: * @param a The array to write the results to
620: * @return The array
621: **/
622: public synchronized Object[] toArray(Object a[]) {
623: if (useCache) {
624: loadFromStore();
625: } else if (backingStore != null) {
626: return SCOUtils.toArray(backingStore, ownerSM, a);
627: }
628: return delegate.toArray(a);
629: }
630:
631: /**
632: * Method to return the Collection as a String.
633: * @return The string form
634: **/
635: public String toString() {
636: StringBuffer s = new StringBuffer("[");
637: int i = 0;
638: Iterator iter = iterator();
639: while (iter.hasNext()) {
640: if (i > 0) {
641: s.append(',');
642: }
643: s.append(iter.next());
644: i++;
645: }
646: s.append("]");
647:
648: return s.toString();
649: }
650:
651: // ----------------------------- Mutator methods ---------------------------
652:
653: /**
654: * Method to add an element to the Collection.
655: * @param element The element to add
656: * @return Whether it was added successfully.
657: **/
658: public synchronized boolean add(Object element) {
659: // Reject inappropriate elements
660: if (element == null && !allowNulls) {
661: throw new NullsNotAllowedException(ownerSM, fieldName);
662: }
663:
664: if (useCache) {
665: loadFromStore();
666: }
667:
668: boolean backingSuccess = true;
669: if (backingStore != null) {
670: if (queued && !ownerSM.getObjectManager().isFlushing()) {
671: addQueuedOperation(new AddOperation(element));
672: } else {
673: try {
674: backingStore.add(ownerSM, element,
675: (useCache ? delegate.size() : -1));
676: } catch (JPOXDataStoreException dse) {
677: JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("023013",
678: "add", fieldName, dse));
679: backingSuccess = false;
680: }
681: }
682: }
683:
684: // Only make it dirty after adding the field to the datastore so we give it time
685: // to be inserted - otherwise jdoPreStore on this object would have been called before completing the addition
686: makeDirty();
687:
688: boolean delegateSuccess = delegate.add(element);
689: return (backingStore != null ? backingSuccess : delegateSuccess);
690: }
691:
692: /**
693: * Method to add a collection of elements.
694: * @param elements The collection of elements to add.
695: * @return Whether they were added successfully.
696: **/
697: public synchronized boolean addAll(java.util.Collection elements) {
698: if (useCache) {
699: loadFromStore();
700: }
701:
702: boolean backingSuccess = true;
703: if (backingStore != null) {
704: if (queued && !ownerSM.getObjectManager().isFlushing()) {
705: Iterator iter = elements.iterator();
706: while (iter.hasNext()) {
707: addQueuedOperation(new AddOperation(iter.next()));
708: }
709: } else {
710: try {
711: backingSuccess = backingStore
712: .addAll(ownerSM, elements,
713: (useCache ? delegate.size() : -1));
714: } catch (JPOXDataStoreException dse) {
715: JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("023013",
716: "addAll", fieldName, dse));
717: backingSuccess = false;
718: }
719: }
720: }
721:
722: // Only make it dirty after adding the field to the datastore so we give it time
723: // to be inserted - otherwise jdoPreStore on this object would have been called before completing the addition
724: makeDirty();
725:
726: boolean delegateSuccess = delegate.addAll(elements);
727: return (backingStore != null ? backingSuccess : delegateSuccess);
728: }
729:
730: /**
731: * Method to clear the Collection.
732: **/
733: public synchronized void clear() {
734: makeDirty();
735:
736: if (backingStore != null) {
737: if (queued && !ownerSM.getObjectManager().isFlushing()) {
738: addQueuedOperation(new ClearOperation());
739: } else {
740: backingStore.clear(ownerSM);
741: }
742: }
743: delegate.clear();
744: }
745:
746: /**
747: * Method to offer an element to the Queue.
748: * @param element The element to offer
749: * @return Whether it was added successfully.
750: **/
751: public synchronized boolean offer(Object element) {
752: return add(element);
753: }
754:
755: /**
756: * Method to poll the next element in the Queue.
757: * @return The element (now removed)
758: **/
759: public synchronized Object poll() {
760: makeDirty();
761:
762: if (useCache) {
763: loadFromStore();
764: }
765:
766: int size = (useCache ? delegate.size() : -1);
767: Object delegateObject = delegate.poll();
768:
769: Object backingObject = null;
770: if (backingStore != null) {
771: if (queued && !ownerSM.getObjectManager().isFlushing()) {
772: addQueuedOperation(new RemoveAtOperation(0));
773: } else {
774: try {
775: backingObject = backingStore.remove(ownerSM, 0,
776: size);
777: } catch (JPOXDataStoreException dse) {
778: backingObject = null;
779: }
780: }
781: }
782:
783: return (backingStore != null ? backingObject : delegateObject);
784: }
785:
786: /**
787: * Method to remove an element from the Collection.
788: * @param element The Element to remove
789: * @return Whether it was removed successfully.
790: **/
791: public synchronized boolean remove(Object element) {
792: return remove(element, true);
793: }
794:
795: /**
796: * Method to remove an element from the collection, and observe the flag for whether to allow cascade delete.
797: * @param element The element
798: * @param allowCascadeDelete Whether to allow cascade delete
799: */
800: public boolean remove(Object element, boolean allowCascadeDelete) {
801: makeDirty();
802:
803: if (useCache) {
804: loadFromStore();
805: }
806:
807: int size = (useCache ? delegate.size() : -1);
808: boolean delegateSuccess = delegate.remove(element);
809:
810: boolean backingSuccess = true;
811: if (backingStore != null) {
812: if (queued && !ownerSM.getObjectManager().isFlushing()) {
813: backingSuccess = contains(element);
814: if (backingSuccess) {
815: addQueuedOperation(new RemoveOperation(element,
816: allowCascadeDelete));
817: }
818: } else {
819: try {
820: backingSuccess = backingStore.remove(ownerSM,
821: element, size, allowCascadeDelete);
822: } catch (JPOXDataStoreException dse) {
823: JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("023013",
824: "remove", fieldName, dse));
825: backingSuccess = false;
826: }
827: }
828: }
829:
830: return (backingStore != null ? backingSuccess : delegateSuccess);
831: }
832:
833: /**
834: * Method to remove a Collection of elements.
835: * @param elements The collection to remove
836: * @return Whether they were removed successfully.
837: **/
838: public synchronized boolean removeAll(java.util.Collection elements) {
839: makeDirty();
840:
841: if (useCache) {
842: loadFromStore();
843: }
844:
845: int size = (useCache ? delegate.size() : -1);
846: boolean delegateSuccess = delegate.removeAll(elements);
847:
848: boolean backingSuccess = true;
849: if (backingStore != null) {
850: if (queued && !ownerSM.getObjectManager().isFlushing()) {
851: backingSuccess = false;
852: Iterator iter = elements.iterator();
853: while (iter.hasNext()) {
854: Object element = iter.next();
855: boolean contained = contains(element);
856: if (contained) {
857: backingSuccess = true;
858: addQueuedOperation(new RemoveOperation(iter
859: .next()));
860: }
861: }
862: } else {
863: try {
864: backingSuccess = backingStore.removeAll(ownerSM,
865: elements, size);
866: } catch (JPOXDataStoreException dse) {
867: JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("023013",
868: "removeAll", fieldName, dse));
869: backingSuccess = false;
870: }
871: }
872: }
873:
874: return (backingStore != null ? backingSuccess : delegateSuccess);
875: }
876:
877: /**
878: * Method to retain a Collection of elements (and remove all others).
879: * @param c The collection to retain
880: * @return Whether they were retained successfully.
881: **/
882: public synchronized boolean retainAll(java.util.Collection c) {
883: makeDirty();
884:
885: if (useCache) {
886: loadFromStore();
887: }
888:
889: boolean modified = false;
890: Iterator iter = iterator();
891: while (iter.hasNext()) {
892: Object element = iter.next();
893: if (!c.contains(element)) {
894: iter.remove();
895: modified = true;
896: }
897: }
898: return modified;
899: }
900:
901: /**
902: * The writeReplace method is called when ObjectOutputStream is preparing
903: * to write the object to the stream. The ObjectOutputStream checks whether
904: * the class defines the writeReplace method. If the method is defined, the
905: * writeReplace method is called to allow the object to designate its
906: * replacement in the stream. The object returned should be either of the
907: * same type as the object passed in or an object that when read and
908: * resolved will result in an object of a type that is compatible with all
909: * references to the object.
910: * @return the replaced object
911: * @throws ObjectStreamException
912: */
913: protected Object writeReplace() throws ObjectStreamException {
914: if (useCache) {
915: loadFromStore();
916: return new java.util.PriorityQueue(delegate);
917: } else {
918: // TODO Cater for non-cached collection, load elements in a DB call.
919: return new java.util.PriorityQueue(delegate);
920: }
921: }
922: }
|