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