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