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