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