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.AbstractMap;
024: import java.util.Collection;
025: import java.util.Comparator;
026: import java.util.Iterator;
027: import java.util.Map;
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 SortedMap object. Backed by a MapStore object.
058: *
059: * @version $Revision: 1.62 $
060: */
061: public class SortedMap extends AbstractMap implements
062: java.util.SortedMap, SCOMap, 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 SortedMap(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(
125: this .owner.getClass().getName(),
126: ownerSM.getObjectManager()
127: .getClassLoaderResolver());
128: JavaTypeMapping m = ownerTable.getFieldMapping(fmd);
129: if (!(m instanceof MapMapping)) {
130: throw new IncompatibleFieldTypeException(ownerSM,
131: fieldName, java.util.SortedMap.class
132: .getName(), fmd.getTypeName());
133: }
134:
135: this .backingStore = (MapStore) ownerSM
136: .getStoreManager().getStore(clr, fmd,
137: java.util.SortedMap.class);
138: this .valueType = clr.classForName(backingStore
139: .getValueType());
140: }
141:
142: // Set up our delegate, using a suitable comparator
143: Comparator comparator = SCOUtils.getComparator(fmd, ownerSM
144: .getObjectManager().getClassLoaderResolver());
145: if (comparator != null) {
146: this .delegate = new java.util.TreeMap(comparator);
147: } else {
148: this .delegate = new java.util.TreeMap();
149: }
150:
151: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
152: JPOXLogger.PERSISTENCE.debug(SCOUtils
153: .getContainerInfoMessage(ownerSM, fieldName,
154: this , useCache, queued, allowNulls,
155: SCOUtils.useCachedLazyLoading(ownerSM,
156: fieldName)));
157: }
158: }
159: }
160:
161: /**
162: * Method to initialise the SCO from an existing value.
163: * @param o Object to set value using.
164: * @param forInsert Whether the object needs inserting in the datastore with this value
165: * @param forUpdate Whether to update the datastore with this value
166: */
167: public void initialise(Object o, boolean forInsert,
168: boolean forUpdate) {
169: java.util.Map m = (java.util.Map) o;
170: if (m != null) {
171: // Check for the case of serialised maps, and assign StateManagers to any PC keys/values without
172: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
173: .getMetaDataForMember(fieldName);
174: if (SCOUtils.mapHasSerialisedKeysAndValues(fmd)
175: && (fmd.getMap().getKeyClassMetaData() != null || fmd
176: .getMap().getValueClassMetaData() != null)) {
177: ObjectManager om = ownerSM.getObjectManager();
178: Iterator iter = m.entrySet().iterator();
179: while (iter.hasNext()) {
180: Map.Entry entry = (Map.Entry) iter.next();
181: Object key = entry.getKey();
182: Object value = entry.getValue();
183: if (fmd.getMap().getKeyClassMetaData() != null) {
184: StateManager objSM = om.findStateManager(key);
185: if (objSM == null) {
186: objSM = StateManagerFactory
187: .newStateManagerForEmbedded(om,
188: key, false);
189: objSM
190: .addEmbeddedOwner(ownerSM,
191: fieldNumber);
192: }
193: }
194: if (fmd.getMap().getValueClassMetaData() != null) {
195: StateManager objSM = om.findStateManager(value);
196: if (objSM == null) {
197: objSM = StateManagerFactory
198: .newStateManagerForEmbedded(om,
199: value, false);
200: objSM
201: .addEmbeddedOwner(ownerSM,
202: fieldNumber);
203: }
204: }
205: }
206: }
207:
208: if (backingStore != null && useCache && !isCacheLoaded) {
209: // Mark as loaded
210: isCacheLoaded = true;
211: }
212:
213: if (forInsert) {
214: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
215: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
216: "023007", StringUtils.toJVMIDString(ownerSM
217: .getObject()), fieldName, ""
218: + m.size()));
219: }
220: putAll(m);
221: } else if (forUpdate) {
222: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
223: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
224: "023008", StringUtils.toJVMIDString(ownerSM
225: .getObject()), fieldName, ""
226: + m.size()));
227: }
228: clear();
229: putAll(m);
230: } else {
231: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
232: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
233: "023007", StringUtils.toJVMIDString(ownerSM
234: .getObject()), fieldName, ""
235: + m.size()));
236: }
237: delegate.clear();
238: delegate.putAll(m);
239: }
240: }
241: }
242:
243: /**
244: * Method to initialise the SCO for use.
245: */
246: public void initialise() {
247: if (useCache
248: && !SCOUtils.useCachedLazyLoading(ownerSM, fieldName)) {
249: // Load up the container now if not using lazy loading
250: loadFromStore();
251: }
252: }
253:
254: /**
255: * Accessor for the unwrapped value that we are wrapping.
256: * @return The unwrapped value
257: */
258: public Object getValue() {
259: // TODO Cater for delegate not being used
260: return delegate;
261: }
262:
263: /**
264: * Method to effect the load of the data in the SCO.
265: * Used when the SCO supports lazy-loading to tell it to load all now.
266: */
267: public void load() {
268: if (useCache) {
269: loadFromStore();
270: }
271: }
272:
273: /**
274: * Method to load all elements from the "backing store" where appropriate.
275: */
276: protected void loadFromStore() {
277: if (backingStore != null && !isCacheLoaded) {
278: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
279: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("023006",
280: StringUtils.toJVMIDString(ownerSM.getObject()),
281: fieldName));
282: }
283: delegate.clear();
284:
285: // Populate the delegate with the keys/values from the store
286: SCOUtils.populateMapDelegateWithStoreData(delegate,
287: backingStore, ownerSM);
288:
289: isCacheLoaded = true;
290: }
291: }
292:
293: /**
294: * Method to flush the changes to the datastore when operating in queued mode.
295: * Does nothing in "direct" mode.
296: */
297: public void flush() {
298: if (queued) {
299: if (queuedOperations != null) {
300: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
301: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
302: "023005", StringUtils.toJVMIDString(ownerSM
303: .getObject()), fieldName));
304: }
305: Iterator iter = queuedOperations.iterator();
306: while (iter.hasNext()) {
307: QueuedOperation op = (QueuedOperation) iter.next();
308: op.perform(backingStore, ownerSM);
309: }
310:
311: queuedOperations.clear();
312: queuedOperations = null;
313: }
314: }
315: }
316:
317: /**
318: * Convenience method to add a queued operation to the operations we perform at commit.
319: * @param op The operation
320: */
321: protected void addQueuedOperation(QueuedOperation op) {
322: if (queuedOperations == null) {
323: queuedOperations = new java.util.ArrayList();
324: }
325: queuedOperations.add(op);
326: }
327:
328: /**
329: * Method to update an embedded key in this map.
330: * @param key The key
331: * @param fieldNumber Number of field in the key
332: * @param newValue New value for this field
333: */
334: public void updateEmbeddedKey(Object key, int fieldNumber,
335: Object newValue) {
336: if (backingStore != null) {
337: backingStore.updateEmbeddedKey(ownerSM, key, fieldNumber,
338: newValue);
339: }
340: }
341:
342: /**
343: * Method to update an embedded value in this map.
344: * @param value The value
345: * @param fieldNumber Number of field in the value
346: * @param newValue New value for this field
347: */
348: public void updateEmbeddedValue(Object value, int fieldNumber,
349: Object newValue) {
350: if (backingStore != null) {
351: backingStore.updateEmbeddedValue(ownerSM, value,
352: fieldNumber, newValue);
353: }
354: }
355:
356: /**
357: * Accessor for the field name that this SortedMap relates to.
358: * @return The field name
359: **/
360: public String getFieldName() {
361: return fieldName;
362: }
363:
364: /**
365: * Accessor for the owner that this SortedMap relates to.
366: * @return The owner
367: **/
368: public Object getOwner() {
369: return owner;
370: }
371:
372: /**
373: * Method to unset the owner and field details.
374: **/
375: public synchronized void unsetOwner() {
376: if (ownerSM != null) {
377: owner = null;
378: ownerSM = null;
379: fieldName = null;
380: backingStore = null;
381: }
382: }
383:
384: /**
385: * Utility to mark the object as dirty
386: **/
387: public void makeDirty() {
388: // Although we are never really "dirty", the owning object must be
389: // marked dirty so that the proper state change occurs and its
390: // jdoPreStore() gets called properly.
391: if (ownerSM != null) {
392: ownerSM.getObjectManager().getApiAdapter().makeFieldDirty(
393: owner, fieldName);
394: }
395: }
396:
397: /**
398: * Method to return a detached copy of the container.
399: * Recurse sthrough the keys/values so that they are likewise detached.
400: * @param state State for detachment state
401: * @return The detached container
402: */
403: public Object detachCopy(FetchPlanState state) {
404: java.util.Map detached = new java.util.TreeMap();
405: SCOUtils.detachCopyForMap(ownerSM, entrySet(), state, detached);
406: return detached;
407: }
408:
409: /**
410: * Method to return an attached copy of the passed (detached) value. The returned attached copy
411: * is a SCO wrapper. Goes through the existing keys/values in the store for this owner field and
412: * removes ones no longer present, and adds new keys/values. All keys/values in the (detached)
413: * value are attached.
414: * @param value The new (map) value
415: */
416: public void attachCopy(Object value) {
417: java.util.Map m = (java.util.Map) value;
418:
419: // Attach all of the keys/values in the new map
420: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
421: .getMetaDataForMember(fieldName);
422: boolean keysWithoutIdentity = SCOUtils
423: .mapHasKeysWithoutIdentity(fmd);
424: boolean valuesWithoutIdentity = SCOUtils
425: .mapHasValuesWithoutIdentity(fmd);
426:
427: java.util.Map attachedKeysValues = new java.util.TreeMap();
428: SCOUtils.attachCopyForMap(ownerSM, m.entrySet(),
429: attachedKeysValues, keysWithoutIdentity,
430: valuesWithoutIdentity);
431:
432: // Update the attached map with the detached elements
433: SCOUtils.updateMapWithMapKeysValues(ownerSM.getObjectManager()
434: .getApiAdapter(), this , attachedKeysValues);
435: }
436:
437: // ------------------------- Queryable Methods -----------------------------
438:
439: /**
440: * Method to generate a QueryStatement for the SCO.
441: * @return The QueryStatement
442: */
443: public synchronized QueryExpression newQueryStatement() {
444: return newQueryStatement(valueType, null);
445: }
446:
447: /**
448: * Method to return a QueryStatement, using the specified candidate class.
449: * @param candidateClass the candidate class
450: * @param candidateAlias Alias for the candidate
451: * @return The QueryStatement
452: */
453: public synchronized QueryExpression newQueryStatement(
454: Class candidateClass, DatastoreIdentifier candidateAlias) {
455: if (backingStore == null) {
456: throw new QueryUnownedSCOException();
457: }
458:
459: return backingStore.newQueryStatement(ownerSM, candidateClass
460: .getName(), candidateAlias);
461: }
462:
463: /**
464: * Method to return a new result object factory for processing of Query
465: * statements.
466: * @param stmt The Query Statement.
467: * @param ignoreCache Whether to ignore the cache.
468: * @param resultClass Whether to create objects of a particular class
469: * @param useFetchPlan whether to use the fetch plan to retrieve fields in the same query
470: * @return The result object factory.
471: **/
472: public synchronized ResultObjectFactory newResultObjectFactory(
473: QueryExpression stmt, boolean ignoreCache,
474: Class resultClass, boolean useFetchPlan) {
475: if (backingStore == null) {
476: throw new QueryUnownedSCOException();
477: }
478:
479: return backingStore.newResultObjectFactory(ownerSM, stmt,
480: ignoreCache, useFetchPlan);
481: }
482:
483: // ------------------ Implementation of SortedMap methods --------------------
484:
485: /**
486: * Creates and returns a copy of this object.
487: *
488: * <P>Mutable second-class Objects are required to provide a public
489: * clone method in order to allow for copying PersistenceCapable
490: * objects. In contrast to Object.clone(), this method must not throw a
491: * CloneNotSupportedException.
492: * @return The cloned object
493: */
494: public Object clone() {
495: if (useCache) {
496: loadFromStore();
497: }
498:
499: return delegate.clone();
500: }
501:
502: /**
503: * Accessor for the comparator.
504: * @return The comparator
505: */
506: public Comparator comparator() {
507: return delegate.comparator();
508: }
509:
510: /**
511: * Method to return if the map contains this key
512: * @param key The key
513: * @return Whether it is contained
514: **/
515: public boolean containsKey(Object key) {
516: if (useCache && isCacheLoaded) {
517: // If the "delegate" is already loaded, use it
518: return delegate.containsKey(key);
519: } else if (backingStore != null) {
520: return backingStore.containsKey(ownerSM, key);
521: }
522:
523: return delegate.containsKey(key);
524: }
525:
526: /**
527: * Method to return if the map contains this value.
528: * @param value The value
529: * @return Whether it is contained
530: **/
531: public boolean containsValue(Object value) {
532: if (useCache && isCacheLoaded) {
533: // If the "delegate" is already loaded, use it
534: return delegate.containsValue(value);
535: } else if (backingStore != null) {
536: return backingStore.containsValue(ownerSM, value);
537: }
538:
539: return delegate.containsValue(value);
540: }
541:
542: /**
543: * Accessor for the set of entries in the Map.
544: * @return Set of entries
545: **/
546: public java.util.Set entrySet() {
547: if (useCache) {
548: loadFromStore();
549: } else if (backingStore != null) {
550: return new Set(ownerSM, fieldName, false, backingStore
551: .entrySetStore());
552: }
553:
554: return delegate.entrySet();
555: }
556:
557: /**
558: * Method to check the equality of this map, and another.
559: * @param o The map to compare against.
560: * @return Whether they are equal.
561: **/
562: public synchronized boolean equals(Object o) {
563: if (useCache) {
564: loadFromStore();
565: }
566:
567: if (o == this ) {
568: return true;
569: }
570: if (!(o instanceof java.util.Map)) {
571: return false;
572: }
573: java.util.Map m = (java.util.Map) o;
574:
575: return entrySet().equals(m.entrySet());
576: }
577:
578: /**
579: * Accessor for the first key in the sorted map.
580: * @return The first key
581: **/
582: public Object firstKey() {
583: if (useCache && isCacheLoaded) {
584: // If the "delegate" is already loaded, use it
585: return delegate.firstKey();
586: } else if (useCache) {
587: loadFromStore();
588: } else {
589: // Use Iterator to get element
590: java.util.Set keys = keySet();
591: Iterator keysIter = keys.iterator();
592: return keysIter.next();
593: }
594:
595: return delegate.firstKey();
596: }
597:
598: /**
599: * Accessor for the last key in the sorted map.
600: * @return The last key
601: **/
602: public Object lastKey() {
603: if (useCache && isCacheLoaded) {
604: // If the "delegate" is already loaded, use it
605: return delegate.lastKey();
606: } else if (useCache) {
607: loadFromStore();
608: } else {
609: // Use Iterator to get element
610: java.util.Set keys = keySet();
611: Iterator keysIter = keys.iterator();
612: Object last = null;
613: while (keysIter.hasNext()) {
614: last = keysIter.next();
615: }
616: return last;
617: }
618:
619: return delegate.lastKey();
620: }
621:
622: /**
623: * Method to retrieve the head of the map up to the specified key.
624: * @param toKey the key to return up to.
625: * @return The map meeting the input
626: */
627: public java.util.SortedMap headMap(Object toKey) {
628: if (useCache && isCacheLoaded) {
629: // If the "delegate" is already loaded, use it
630: return delegate.headMap(toKey);
631: } else if (useCache) {
632: loadFromStore();
633: } else {
634: // TODO Provide a datastore method to do this
635: throw new JPOXUserException(
636: "JPOX doesn't currently support SortedMap.headMap() when not using cached containers");
637: }
638:
639: return delegate.headMap(toKey);
640: }
641:
642: /**
643: * Method to retrieve the subset of the map between the specified keys.
644: * @param fromKey The start key
645: * @param toKey The end key
646: * @return The map meeting the input
647: */
648: public java.util.SortedMap subMap(Object fromKey, Object toKey) {
649: if (useCache && isCacheLoaded) {
650: // If the "delegate" is already loaded, use it
651: return delegate.subMap(fromKey, toKey);
652: } else if (useCache) {
653: loadFromStore();
654: } else {
655: // TODO Provide a datastore method to do this
656: throw new JPOXUserException(
657: "JPOX doesn't currently support SortedMap.subMap() when not using cached container");
658: }
659:
660: return delegate.subMap(fromKey, toKey);
661: }
662:
663: /**
664: * Method to retrieve the part of the map after the specified key.
665: * @param fromKey The start key
666: * @return The map meeting the input
667: */
668: public java.util.SortedMap tailMap(Object fromKey) {
669: if (useCache && isCacheLoaded) {
670: // If the "delegate" is already loaded, use it
671: return delegate.headMap(fromKey);
672: } else if (useCache) {
673: loadFromStore();
674: } else {
675: // TODO Provide a datastore method to do this
676: throw new JPOXUserException(
677: "JPOX doesn't currently support SortedMap.tailMap() when not using cached containers");
678: }
679:
680: return delegate.headMap(fromKey);
681: }
682:
683: /**
684: * Accessor for the value stored against a key.
685: * @param key The key
686: * @return The value.
687: **/
688: public Object get(Object key) {
689: if (useCache) {
690: loadFromStore();
691: } else if (backingStore != null) {
692: return backingStore.get(ownerSM, key);
693: }
694:
695: return delegate.get(key);
696: }
697:
698: /**
699: * Method to generate a hashcode for this Map.
700: * @return The hashcode.
701: **/
702: public synchronized int hashCode() {
703: if (useCache) {
704: loadFromStore();
705: } else if (backingStore != null) {
706: int h = 0;
707: Iterator i = entrySet().iterator();
708: while (i.hasNext()) {
709: h += i.next().hashCode();
710: }
711:
712: return h;
713: }
714: return delegate.hashCode();
715: }
716:
717: /**
718: * Method to return if the Map is empty.
719: * @return Whether it is empty.
720: **/
721: public boolean isEmpty() {
722: return size() == 0;
723: }
724:
725: /**
726: * Accessor for the set of keys in the Map.
727: * @return Set of keys.
728: **/
729: public java.util.Set keySet() {
730: if (useCache) {
731: loadFromStore();
732: }
733: if (backingStore != null) {
734: return new Set(ownerSM, fieldName, false, backingStore
735: .keySetStore(ownerSM.getObjectManager()
736: .getClassLoaderResolver()));
737: }
738:
739: return delegate.keySet();
740: }
741:
742: /**
743: * Method to return the size of the Map.
744: * @return The size
745: **/
746: public int size() {
747: if (useCache && isCacheLoaded) {
748: // If the "delegate" is already loaded, use it
749: return delegate.size();
750: } else if (backingStore != null) {
751: return backingStore.entrySetStore().size(ownerSM);
752: }
753:
754: return delegate.size();
755: }
756:
757: /**
758: * Accessor for the set of values in the Map.
759: * @return Set of values.
760: **/
761: public Collection values() {
762: if (useCache) {
763: loadFromStore();
764: }
765: if (backingStore != null) {
766: return new Set(ownerSM, fieldName, true, backingStore
767: .valueSetStore(ownerSM.getObjectManager()
768: .getClassLoaderResolver()));
769: }
770:
771: return delegate.values();
772: }
773:
774: // --------------------------- Mutator methods -----------------------------
775:
776: /**
777: * Method to clear the SortedMap.
778: **/
779: public void clear() {
780: makeDirty();
781:
782: if (backingStore != null) {
783: if (queued && !ownerSM.getObjectManager().isFlushing()) {
784: addQueuedOperation(new ClearOperation());
785: } else {
786: backingStore.clear(ownerSM);
787: }
788: }
789: delegate.clear();
790: }
791:
792: /**
793: * Method to add a value against a key to the SortedMap.
794: * @param key The key
795: * @param value The value
796: * @return The previous value for the specified key.
797: **/
798: public Object put(Object key, Object value) {
799: // Reject inappropriate elements
800: if (value == null && !allowNulls) {
801: throw new NullsNotAllowedException(ownerSM, fieldName);
802: }
803:
804: makeDirty();
805:
806: // Dont load the entries since we dont need them here
807:
808: Object oldValue = null;
809: if (backingStore != null) {
810: if (queued && !ownerSM.getObjectManager().isFlushing()) {
811: addQueuedOperation(new PutOperation(key, value));
812: } else {
813: oldValue = backingStore.put(ownerSM, key, value);
814: }
815: }
816: Object delegateOldValue = delegate.put(key, value);
817: if (backingStore == null) {
818: oldValue = delegateOldValue;
819: } else if (queued && !ownerSM.getObjectManager().isFlushing()) {
820: oldValue = delegateOldValue;
821: }
822: return oldValue;
823: }
824:
825: /**
826: * Method to add the specified Map's values under their keys here.
827: * @param m The map
828: **/
829: public void putAll(java.util.Map m) {
830: makeDirty();
831:
832: // Dont load the entries since we dont need them here
833:
834: if (backingStore != null) {
835: if (queued && !ownerSM.getObjectManager().isFlushing()) {
836: Iterator iter = m.entrySet().iterator();
837: while (iter.hasNext()) {
838: Map.Entry entry = (Map.Entry) iter.next();
839: addQueuedOperation(new PutOperation(entry.getKey(),
840: entry.getValue()));
841: }
842: } else {
843: backingStore.putAll(ownerSM, m);
844: }
845: }
846: delegate.putAll(m);
847: }
848:
849: /**
850: * Method to remove the value for a key from the SortedMap.
851: * @param key The key to remove
852: * @return The value that was removed from this key.
853: **/
854: public Object remove(Object key) {
855: makeDirty();
856:
857: // Dont load the entries since we dont need them here
858:
859: Object removed = null;
860: Object delegateRemoved = delegate.remove(key);
861: if (backingStore != null) {
862: if (queued && !ownerSM.getObjectManager().isFlushing()) {
863: addQueuedOperation(new RemoveOperation(key));
864: removed = delegateRemoved;
865: } else {
866: removed = backingStore.remove(ownerSM, key);
867: }
868: } else {
869: removed = delegateRemoved;
870: }
871: return removed;
872: }
873:
874: /**
875: * The writeReplace method is called when ObjectOutputStream is preparing
876: * to write the object to the stream. The ObjectOutputStream checks whether
877: * the class defines the writeReplace method. If the method is defined, the
878: * writeReplace method is called to allow the object to designate its
879: * replacement in the stream. The object returned should be either of the
880: * same type as the object passed in or an object that when read and
881: * resolved will result in an object of a type that is compatible with all
882: * references to the object.
883: *
884: * @return the replaced object
885: * @throws ObjectStreamException
886: */
887: protected Object writeReplace() throws ObjectStreamException {
888: if (useCache) {
889: loadFromStore();
890: return new java.util.TreeMap(delegate);
891: } else {
892: // TODO Cater for non-cached map, load elements in a DB call.
893: return new java.util.TreeMap(delegate);
894: }
895: }
896: }
|