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