001: /**********************************************************************
002: Copyright (c) 2004 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: 2004 Andy Jefferson - added Queryable implementation
018: 2004 Andy Jefferson - added caching capability
019: ...
020: **********************************************************************/package org.jpox.sco;
021:
022: import java.io.ObjectStreamException;
023: import java.util.Collection;
024: import java.util.Comparator;
025: import java.util.Iterator;
026: import java.util.Map;
027: import java.util.SortedMap;
028:
029: import org.jpox.ClassLoaderResolver;
030: import org.jpox.ObjectManager;
031: import org.jpox.StateManager;
032: import org.jpox.exceptions.JPOXUserException;
033: import org.jpox.metadata.AbstractMemberMetaData;
034: import org.jpox.metadata.FieldPersistenceModifier;
035: import org.jpox.sco.exceptions.IncompatibleFieldTypeException;
036: import org.jpox.sco.exceptions.NullsNotAllowedException;
037: import org.jpox.sco.exceptions.QueryUnownedSCOException;
038: import org.jpox.sco.queued.ClearOperation;
039: import org.jpox.sco.queued.PutOperation;
040: import org.jpox.sco.queued.QueuedOperation;
041: import org.jpox.sco.queued.RemoveOperation;
042: import org.jpox.state.FetchPlanState;
043: import org.jpox.state.StateManagerFactory;
044: import org.jpox.store.DatastoreClass;
045: import org.jpox.store.DatastoreIdentifier;
046: import org.jpox.store.expression.QueryExpression;
047: import org.jpox.store.mapping.JavaTypeMapping;
048: import org.jpox.store.mapping.MapMapping;
049: import org.jpox.store.query.Queryable;
050: import org.jpox.store.query.ResultObjectFactory;
051: import org.jpox.store.scostore.MapStore;
052: import org.jpox.util.JPOXLogger;
053: import org.jpox.util.Localiser;
054: import org.jpox.util.StringUtils;
055:
056: /**
057: * A mutable second-class TreeMap object. Backed by a MapStore object.
058: *
059: * @version $Revision: 1.62 $
060: */
061: public class TreeMap extends java.util.TreeMap implements SCOMap,
062: Cloneable, Queryable {
063: protected static final Localiser LOCALISER = Localiser
064: .getInstance("org.jpox.Localisation");
065:
066: private transient Object owner;
067: private transient StateManager ownerSM;
068: private transient String fieldName;
069: private transient int fieldNumber;
070: private transient Class valueType;
071: private transient boolean allowNulls;
072:
073: /** The "backing store" */
074: protected MapStore backingStore;
075:
076: /** The internal "delegate". */
077: protected java.util.TreeMap delegate;
078:
079: /** Whether to use "delegate" caching. */
080: protected boolean useCache = true;
081:
082: /** Status flag whether the map is loaded into the cache. */
083: protected boolean isCacheLoaded = false;
084:
085: /** Whether the SCO is in "direct" or "queued" mode. */
086: boolean queued = false;
087:
088: /** Queued operations when using "queued" mode. */
089: private java.util.ArrayList queuedOperations = null;
090:
091: /**
092: * Constructor
093: * @param ownerSM the owner StateManager
094: * @param fieldName the field name
095: */
096: public TreeMap(StateManager ownerSM, String fieldName) {
097: this .ownerSM = ownerSM;
098: this .fieldName = fieldName;
099: this .allowNulls = false;
100:
101: if (ownerSM == null) {
102: this .delegate = new java.util.TreeMap();
103: }
104:
105: if (ownerSM != null) {
106: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
107: .getMetaDataForMember(fieldName);
108: owner = ownerSM.getObject();
109: fieldNumber = fmd.getAbsoluteFieldNumber();
110: allowNulls = SCOUtils
111: .allowNullsInContainer(allowNulls, fmd);
112: if (ownerSM.getStoreManager().usesDatastoreClass()) {
113: queued = SCOUtils.useContainerQueueing(ownerSM);
114: useCache = SCOUtils.useContainerCache(ownerSM,
115: fieldName);
116: }
117:
118: if (ownerSM.getStoreManager().usesDatastoreClass()
119: && !SCOUtils.mapHasSerialisedKeysAndValues(fmd)
120: && fmd.getPersistenceModifier() == FieldPersistenceModifier.PERSISTENT) {
121: ClassLoaderResolver clr = ownerSM.getObjectManager()
122: .getClassLoaderResolver();
123: DatastoreClass ownerTable = ownerSM.getStoreManager()
124: .getDatastoreClass(owner.getClass().getName(),
125: clr);
126: JavaTypeMapping m = ownerTable.getFieldMapping(fmd);
127: if (!(m instanceof MapMapping)) {
128: throw new IncompatibleFieldTypeException(ownerSM,
129: fieldName, java.util.TreeMap.class
130: .getName(), fmd.getTypeName());
131: }
132:
133: this .backingStore = (MapStore) ownerSM
134: .getStoreManager().getStore(clr, fmd,
135: java.util.TreeMap.class);
136: this .valueType = clr.classForName(backingStore
137: .getValueType());
138: }
139:
140: // Set up our delegate, using a comparator
141: Comparator comparator = SCOUtils.getComparator(fmd, ownerSM
142: .getObjectManager().getClassLoaderResolver());
143: if (comparator != null) {
144: this .delegate = new java.util.TreeMap(comparator);
145: } else {
146: this .delegate = new java.util.TreeMap();
147: }
148:
149: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
150: JPOXLogger.PERSISTENCE.debug(SCOUtils
151: .getContainerInfoMessage(ownerSM, fieldName,
152: this , useCache, queued, allowNulls,
153: SCOUtils.useCachedLazyLoading(ownerSM,
154: fieldName)));
155: }
156: }
157: }
158:
159: /**
160: * Method to initialise the SCO from an existing value.
161: * @param o Object to set value using.
162: * @param forInsert Whether the object needs inserting in the datastore with this value
163: * @param forUpdate Whether to update the datastore with this value
164: */
165: public void initialise(Object o, boolean forInsert,
166: boolean forUpdate) {
167: java.util.Map m = (java.util.Map) o;
168: if (m != null) {
169: // Check for the case of serialised maps, and assign StateManagers to any PC keys/values without
170: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
171: .getMetaDataForMember(fieldName);
172: if (SCOUtils.mapHasSerialisedKeysAndValues(fmd)
173: && (fmd.getMap().getKeyClassMetaData() != null || fmd
174: .getMap().getValueClassMetaData() != null)) {
175: ObjectManager om = ownerSM.getObjectManager();
176: Iterator iter = m.entrySet().iterator();
177: while (iter.hasNext()) {
178: Map.Entry entry = (Map.Entry) iter.next();
179: Object key = entry.getKey();
180: Object value = entry.getValue();
181: if (fmd.getMap().getKeyClassMetaData() != null) {
182: StateManager objSM = om.findStateManager(key);
183: if (objSM == null) {
184: objSM = StateManagerFactory
185: .newStateManagerForEmbedded(om,
186: key, false);
187: objSM
188: .addEmbeddedOwner(ownerSM,
189: fieldNumber);
190: }
191: }
192: if (fmd.getMap().getValueClassMetaData() != null) {
193: StateManager objSM = om.findStateManager(value);
194: if (objSM == null) {
195: objSM = StateManagerFactory
196: .newStateManagerForEmbedded(om,
197: value, false);
198: objSM
199: .addEmbeddedOwner(ownerSM,
200: fieldNumber);
201: }
202: }
203: }
204: }
205:
206: if (backingStore != null && useCache && !isCacheLoaded) {
207: // Mark as loaded
208: isCacheLoaded = true;
209: }
210:
211: if (forInsert) {
212: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
213: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
214: "023007", StringUtils.toJVMIDString(ownerSM
215: .getObject()), fieldName, ""
216: + m.size()));
217: }
218: putAll(m);
219: } else if (forUpdate) {
220: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
221: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
222: "023008", StringUtils.toJVMIDString(ownerSM
223: .getObject()), fieldName, ""
224: + m.size()));
225: }
226: clear();
227: putAll(m);
228: } else {
229: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
230: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
231: "023007", StringUtils.toJVMIDString(ownerSM
232: .getObject()), fieldName, ""
233: + m.size()));
234: }
235: delegate.clear();
236: delegate.putAll(m);
237: }
238: }
239: }
240:
241: /**
242: * Method to initialise the SCO for use.
243: */
244: public void initialise() {
245: if (useCache
246: && !SCOUtils.useCachedLazyLoading(ownerSM, fieldName)) {
247: // Load up the container now if not using lazy loading
248: loadFromStore();
249: }
250: }
251:
252: /**
253: * Accessor for the unwrapped value that we are wrapping.
254: * @return The unwrapped value
255: */
256: public Object getValue() {
257: // TODO Cater for delegate not being used
258: return delegate;
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:
283: // Populate the delegate with the keys/values from the store
284: SCOUtils.populateMapDelegateWithStoreData(delegate,
285: backingStore, ownerSM);
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 key in this map.
328: * @param key The key
329: * @param fieldNumber Number of field in the key
330: * @param newValue New value for this field
331: */
332: public void updateEmbeddedKey(Object key, int fieldNumber,
333: Object newValue) {
334: if (backingStore != null) {
335: backingStore.updateEmbeddedKey(ownerSM, key, fieldNumber,
336: newValue);
337: }
338: }
339:
340: /**
341: * Method to update an embedded value in this map.
342: * @param value The value
343: * @param fieldNumber Number of field in the value
344: * @param newValue New value for this field
345: */
346: public void updateEmbeddedValue(Object value, int fieldNumber,
347: Object newValue) {
348: if (backingStore != null) {
349: backingStore.updateEmbeddedValue(ownerSM, value,
350: fieldNumber, newValue);
351: }
352: }
353:
354: /**
355: * Accessor for the field name that this TreeMap relates to.
356: * @return The field name
357: **/
358: public String getFieldName() {
359: return fieldName;
360: }
361:
362: /**
363: * Accessor for the owner that this TreeMap relates to.
364: * @return The owner
365: **/
366: public Object getOwner() {
367: return owner;
368: }
369:
370: /**
371: * Method to unset the owner and field details.
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: // Although we are never really "dirty", the owning object must be
387: // marked dirty so that the proper state change occurs and its
388: // jdoPreStore() gets called properly.
389: if (ownerSM != null) {
390: ownerSM.getObjectManager().getApiAdapter().makeFieldDirty(
391: owner, fieldName);
392: }
393: }
394:
395: /**
396: * Method to return a detached copy of the container.
397: * Recurse sthrough the keys/values so that they are likewise detached.
398: * @param state State for detachment process
399: * @return The detached container
400: */
401: public Object detachCopy(FetchPlanState state) {
402: java.util.Map detached = new java.util.TreeMap();
403: SCOUtils.detachCopyForMap(ownerSM, entrySet(), state, detached);
404: return detached;
405: }
406:
407: /**
408: * Method to return an attached copy of the passed (detached) value. The returned attached copy
409: * is a SCO wrapper. Goes through the existing keys/values in the store for this owner field and
410: * removes ones no longer present, and adds new keys/values. All keys/values in the (detached)
411: * value are attached.
412: * @param value The new (map) value
413: */
414: public void attachCopy(Object value) {
415: java.util.Map m = (java.util.Map) value;
416:
417: // Attach all of the keys/values in the new map
418: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
419: .getMetaDataForMember(fieldName);
420: boolean keysWithoutIdentity = SCOUtils
421: .mapHasKeysWithoutIdentity(fmd);
422: boolean valuesWithoutIdentity = SCOUtils
423: .mapHasValuesWithoutIdentity(fmd);
424:
425: java.util.Map attachedKeysValues = new java.util.TreeMap();
426: SCOUtils.attachCopyForMap(ownerSM, m.entrySet(),
427: attachedKeysValues, keysWithoutIdentity,
428: valuesWithoutIdentity);
429:
430: // Update the attached map with the detached elements
431: SCOUtils.updateMapWithMapKeysValues(ownerSM.getObjectManager()
432: .getApiAdapter(), this , attachedKeysValues);
433: }
434:
435: // ------------------------- Queryable Methods -----------------------------
436:
437: /**
438: * Method to generate a QueryStatement for the SCO.
439: * @return The QueryStatement
440: */
441: public synchronized QueryExpression newQueryStatement() {
442: return newQueryStatement(valueType, null);
443: }
444:
445: /**
446: * Method to return a QueryStatement, using the specified candidate class.
447: * @param candidateClass the candidate class
448: * @param candidateAlias Alias for the candidate
449: * @return The QueryStatement
450: */
451: public synchronized QueryExpression newQueryStatement(
452: Class candidateClass, DatastoreIdentifier candidateAlias) {
453: if (backingStore == null) {
454: throw new QueryUnownedSCOException();
455: }
456:
457: return backingStore.newQueryStatement(ownerSM, candidateClass
458: .getName(), candidateAlias);
459: }
460:
461: /**
462: * Method to return a new result object factory for processing of Query
463: * statements.
464: * @param stmt The Query Statement.
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 result object factory.
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 TreeMap methods --------------------
482:
483: /**
484: * Creates and returns a copy of this object.
485: *
486: * <P>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 The cloned object
491: */
492: public Object clone() {
493: if (useCache) {
494: loadFromStore();
495: }
496:
497: return delegate.clone();
498: }
499:
500: /**
501: * Accessor for the comparator.
502: * @return The comparator
503: */
504: public Comparator comparator() {
505: return delegate.comparator();
506: }
507:
508: /**
509: * Method to return if the map contains this key
510: * @param key The key
511: * @return Whether it is contained
512: **/
513: public boolean containsKey(Object key) {
514: if (useCache && isCacheLoaded) {
515: // If the "delegate" is already loaded, use it
516: return delegate.containsKey(key);
517: } else if (backingStore != null) {
518: return backingStore.containsKey(ownerSM, key);
519: }
520:
521: return delegate.containsKey(key);
522: }
523:
524: /**
525: * Method to return if the map contains this value.
526: * @param value The value
527: * @return Whether it is contained
528: **/
529: public boolean containsValue(Object value) {
530: if (useCache && isCacheLoaded) {
531: // If the "delegate" is already loaded, use it
532: return delegate.containsValue(value);
533: } else if (backingStore != null) {
534: return backingStore.containsValue(ownerSM, value);
535: }
536:
537: return delegate.containsValue(value);
538: }
539:
540: /**
541: * Accessor for the set of entries in the Map.
542: * @return Set of entries
543: **/
544: public java.util.Set entrySet() {
545: if (useCache) {
546: loadFromStore();
547: } else if (backingStore != null) {
548: return new Set(ownerSM, fieldName, false, backingStore
549: .entrySetStore());
550: }
551:
552: return delegate.entrySet();
553: }
554:
555: /**
556: * Method to check the equality of this map, and another.
557: * @param o The map to compare against.
558: * @return Whether they are equal.
559: **/
560: public synchronized boolean equals(Object o) {
561: if (useCache) {
562: loadFromStore();
563: }
564:
565: if (o == this ) {
566: return true;
567: }
568: if (!(o instanceof java.util.Map)) {
569: return false;
570: }
571: java.util.Map m = (java.util.Map) o;
572:
573: return entrySet().equals(m.entrySet());
574: }
575:
576: /**
577: * Accessor for the first key in the sorted map.
578: * @return The first key
579: **/
580: public Object firstKey() {
581: if (useCache && isCacheLoaded) {
582: // If the "delegate" is already loaded, use it
583: return delegate.firstKey();
584: } else if (useCache) {
585: loadFromStore();
586: } else {
587: // Use Iterator to get element
588: java.util.Set keys = keySet();
589: Iterator keysIter = keys.iterator();
590: return keysIter.next();
591: }
592:
593: return delegate.firstKey();
594: }
595:
596: /**
597: * Accessor for the last key in the sorted map.
598: * @return The last key
599: **/
600: public Object lastKey() {
601: if (useCache && isCacheLoaded) {
602: // If the "delegate" is already loaded, use it
603: return delegate.lastKey();
604: } else if (useCache) {
605: loadFromStore();
606: } else {
607: // Use Iterator to get element
608: java.util.Set keys = keySet();
609: Iterator keysIter = keys.iterator();
610: Object last = null;
611: while (keysIter.hasNext()) {
612: last = keysIter.next();
613: }
614: return last;
615: }
616:
617: return delegate.lastKey();
618: }
619:
620: /**
621: * Method to retrieve the head of the map up to the specified key.
622: * @param toKey the key to return up to.
623: * @return The map meeting the input
624: */
625: public SortedMap headMap(Object toKey) {
626: if (useCache && isCacheLoaded) {
627: // If the "delegate" is already loaded, use it
628: return delegate.headMap(toKey);
629: } else if (useCache) {
630: loadFromStore();
631: } else {
632: // TODO Provide a datastore method to do this
633: throw new JPOXUserException(
634: "JPOX doesn't currently support TreeMap.headMap() when not using cached containers");
635: }
636:
637: return delegate.headMap(toKey);
638: }
639:
640: /**
641: * Method to retrieve the subset of the map between the specified keys.
642: * @param fromKey The start key
643: * @param toKey The end key
644: * @return The map meeting the input
645: */
646: public SortedMap subMap(Object fromKey, Object toKey) {
647: if (useCache && isCacheLoaded) {
648: // If the "delegate" is already loaded, use it
649: return delegate.subMap(fromKey, toKey);
650: } else if (useCache) {
651: loadFromStore();
652: } else {
653: // TODO Provide a datastore method to do this
654: throw new JPOXUserException(
655: "JPOX doesn't currently support TreeMap.subMap() when not using cached container");
656: }
657:
658: return delegate.subMap(fromKey, toKey);
659: }
660:
661: /**
662: * Method to retrieve the part of the map after the specified key.
663: * @param fromKey The start key
664: * @return The map meeting the input
665: */
666: public SortedMap tailMap(Object fromKey) {
667: if (useCache && isCacheLoaded) {
668: // If the "delegate" is already loaded, use it
669: return delegate.headMap(fromKey);
670: } else if (useCache) {
671: loadFromStore();
672: } else {
673: // TODO Provide a datastore method to do this
674: throw new JPOXUserException(
675: "JPOX doesn't currently support TreeMap.tailMap() when not using cached containers");
676: }
677:
678: return delegate.headMap(fromKey);
679: }
680:
681: /**
682: * Accessor for the value stored against a key.
683: * @param key The key
684: * @return The value.
685: **/
686: public Object get(Object key) {
687: if (useCache) {
688: loadFromStore();
689: } else if (backingStore != null) {
690: return backingStore.get(ownerSM, key);
691: }
692:
693: return delegate.get(key);
694: }
695:
696: /**
697: * Method to generate a hashcode for this Map.
698: * @return The hashcode.
699: **/
700: public synchronized int hashCode() {
701: if (useCache) {
702: loadFromStore();
703: } else if (backingStore != null) {
704: int h = 0;
705: Iterator i = entrySet().iterator();
706: while (i.hasNext()) {
707: h += i.next().hashCode();
708: }
709:
710: return h;
711: }
712: return delegate.hashCode();
713: }
714:
715: /**
716: * Method to return if the Map is empty.
717: * @return Whether it is empty.
718: **/
719: public boolean isEmpty() {
720: return size() == 0;
721: }
722:
723: /**
724: * Accessor for the set of keys in the Map.
725: * @return Set of keys.
726: **/
727: public java.util.Set keySet() {
728: if (useCache) {
729: loadFromStore();
730: }
731: if (backingStore != null) {
732: return new Set(ownerSM, fieldName, false, backingStore
733: .keySetStore(ownerSM.getObjectManager()
734: .getClassLoaderResolver()));
735: }
736:
737: return delegate.keySet();
738: }
739:
740: /**
741: * Method to return the size of the Map.
742: * @return The size
743: **/
744: public int size() {
745: if (useCache && isCacheLoaded) {
746: // If the "delegate" is already loaded, use it
747: return delegate.size();
748: } else if (backingStore != null) {
749: return backingStore.entrySetStore().size(ownerSM);
750: }
751:
752: return delegate.size();
753: }
754:
755: /**
756: * Accessor for the set of values in the Map.
757: * @return Set of values.
758: **/
759: public Collection values() {
760: if (useCache) {
761: loadFromStore();
762: }
763: if (backingStore != null) {
764: return new Set(ownerSM, fieldName, true, backingStore
765: .valueSetStore(ownerSM.getObjectManager()
766: .getClassLoaderResolver()));
767: }
768:
769: return delegate.values();
770: }
771:
772: // --------------------------- Mutator methods -----------------------------
773:
774: /**
775: * Method to clear the TreeMap.
776: **/
777: public void clear() {
778: makeDirty();
779:
780: if (backingStore != null) {
781: if (queued && !ownerSM.getObjectManager().isFlushing()) {
782: addQueuedOperation(new ClearOperation());
783: } else {
784: backingStore.clear(ownerSM);
785: }
786: }
787: delegate.clear();
788: }
789:
790: /**
791: * Method to add a value against a key to the TreeMap.
792: * @param key The key
793: * @param value The value
794: * @return The previous value for the specified key.
795: **/
796: public Object put(Object key, Object value) {
797: // Reject inappropriate elements
798: if (value == null && !allowNulls) {
799: throw new NullsNotAllowedException(ownerSM, fieldName);
800: }
801:
802: makeDirty();
803:
804: // Dont load the entries since we dont need them here
805:
806: Object oldValue = null;
807: if (backingStore != null) {
808: if (queued && !ownerSM.getObjectManager().isFlushing()) {
809: addQueuedOperation(new PutOperation(key, value));
810: } else {
811: oldValue = backingStore.put(ownerSM, key, value);
812: }
813: }
814: Object delegateOldValue = delegate.put(key, value);
815: if (backingStore == null) {
816: oldValue = delegateOldValue;
817: } else if (queued && !ownerSM.getObjectManager().isFlushing()) {
818: oldValue = delegateOldValue;
819: }
820: return oldValue;
821: }
822:
823: /**
824: * Method to add the specified Map's values under their keys here.
825: * @param m The map
826: **/
827: public void putAll(java.util.Map m) {
828: makeDirty();
829:
830: // Dont load the entries since we dont need them here
831:
832: if (backingStore != null) {
833: if (queued && !ownerSM.getObjectManager().isFlushing()) {
834: Iterator iter = m.entrySet().iterator();
835: while (iter.hasNext()) {
836: Map.Entry entry = (Map.Entry) iter.next();
837: addQueuedOperation(new PutOperation(entry.getKey(),
838: entry.getValue()));
839: }
840: } else {
841: backingStore.putAll(ownerSM, m);
842: }
843: }
844: delegate.putAll(m);
845: }
846:
847: /**
848: * Method to remove the value for a key from the TreeMap.
849: * @param key The key to remove
850: * @return The value that was removed from this key.
851: **/
852: public Object remove(Object key) {
853: makeDirty();
854:
855: // Dont load the entries since we dont need them here
856:
857: Object removed = null;
858: Object delegateRemoved = delegate.remove(key);
859: if (backingStore != null) {
860: if (queued && !ownerSM.getObjectManager().isFlushing()) {
861: addQueuedOperation(new RemoveOperation(key));
862: removed = delegateRemoved;
863: } else {
864: removed = backingStore.remove(ownerSM, key);
865: }
866: } else {
867: removed = delegateRemoved;
868: }
869: return removed;
870: }
871:
872: /**
873: * The writeReplace method is called when ObjectOutputStream is preparing
874: * to write the object to the stream. The ObjectOutputStream checks whether
875: * the class defines the writeReplace method. If the method is defined, the
876: * writeReplace method is called to allow the object to designate its
877: * replacement in the stream. The object returned should be either of the
878: * same type as the object passed in or an object that when read and
879: * resolved will result in an object of a type that is compatible with all
880: * references to the object.
881: *
882: * @return the replaced object
883: * @throws ObjectStreamException
884: */
885: protected Object writeReplace() throws ObjectStreamException {
886: if (useCache) {
887: loadFromStore();
888: return new java.util.TreeMap(delegate);
889: } else {
890: // TODO Cater for non-cached map, load elements in a DB call.
891: return new java.util.TreeMap(delegate);
892: }
893: }
894: }
|