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