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