001: /**********************************************************************
002: Copyright (c) 2007 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: Contributors:
016: ...
017: **********************************************************************/package org.jpox.sco.simple;
018:
019: import java.io.ObjectStreamException;
020: import java.util.Collection;
021: import java.util.Comparator;
022: import java.util.SortedMap;
023:
024: import org.jpox.StateManager;
025: import org.jpox.metadata.AbstractMemberMetaData;
026: import org.jpox.sco.SCOMap;
027: import org.jpox.sco.SCOUtils;
028: import org.jpox.sco.exceptions.NullsNotAllowedException;
029: import org.jpox.state.FetchPlanState;
030: import org.jpox.util.JPOXLogger;
031: import org.jpox.util.Localiser;
032: import org.jpox.util.StringUtils;
033:
034: /**
035: * A mutable second-class TreeMap object.
036: * This is the simplified form that intercepts mutators and marks the field as dirty.
037: *
038: * @version $Revision: 1.2 $
039: */
040: public class TreeMap extends java.util.TreeMap implements SCOMap,
041: Cloneable {
042: protected static final Localiser LOCALISER = Localiser
043: .getInstance("org.jpox.Localisation");
044:
045: protected transient Object owner;
046: protected transient StateManager ownerSM;
047: protected transient String fieldName;
048: protected transient int fieldNumber;
049: protected transient boolean allowNulls;
050:
051: /** The internal "delegate". */
052: protected java.util.TreeMap delegate;
053:
054: /**
055: * Constructor
056: * @param ownerSM the owner StateManager
057: * @param fieldName the field name
058: */
059: public TreeMap(StateManager ownerSM, String fieldName) {
060: this .ownerSM = ownerSM;
061: this .fieldName = fieldName;
062: this .allowNulls = false;
063: if (ownerSM != null) {
064: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
065: .getMetaDataForMember(fieldName);
066: owner = ownerSM.getObject();
067: fieldNumber = fmd.getAbsoluteFieldNumber();
068: allowNulls = SCOUtils
069: .allowNullsInContainer(allowNulls, fmd);
070: }
071: }
072:
073: /**
074: * Method to initialise the SCO from an existing value.
075: * @param o Object to set value using.
076: * @param forInsert Whether the object needs inserting in the datastore with this value
077: * @param forUpdate Whether to update the datastore with this value
078: */
079: public void initialise(Object o, boolean forInsert,
080: boolean forUpdate) {
081: java.util.Map m = (java.util.Map) o;
082: if (m != null) {
083: initialiseDelegate();
084: delegate.putAll(m);
085: } else {
086: initialiseDelegate();
087: }
088: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
089: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("023003",
090: StringUtils.toJVMIDString(ownerSM.getObject()),
091: fieldName, "" + size(), SCOUtils
092: .getSCOWrapperOptionsMessage(true, false,
093: allowNulls, false)));
094: }
095: }
096:
097: /**
098: * Method to initialise the SCO for use.
099: */
100: public void initialise() {
101: initialiseDelegate();
102: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
103: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("023003",
104: StringUtils.toJVMIDString(ownerSM.getObject()),
105: fieldName, "" + size(), SCOUtils
106: .getSCOWrapperOptionsMessage(true, false,
107: allowNulls, false)));
108: }
109: }
110:
111: /**
112: * Convenience method to set up the delegate respecting any comparator specified in MetaData.
113: */
114: protected void initialiseDelegate() {
115: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
116: .getMetaDataForMember(fieldName);
117: Comparator comparator = SCOUtils.getComparator(fmd, ownerSM
118: .getObjectManager().getClassLoaderResolver());
119: if (comparator != null) {
120: this .delegate = new java.util.TreeMap(comparator);
121: } else {
122: this .delegate = new java.util.TreeMap();
123: }
124: }
125:
126: /**
127: * Accessor for the unwrapped value that we are wrapping.
128: * @return The unwrapped value
129: */
130: public Object getValue() {
131: return delegate;
132: }
133:
134: /**
135: * Method to effect the load of the data in the SCO.
136: * Used when the SCO supports lazy-loading to tell it to load all now.
137: */
138: public void load() {
139: // Always loaded
140: }
141:
142: /**
143: * Method to flush the changes to the datastore when operating in queued mode.
144: * Does nothing in "direct" mode.
145: */
146: public void flush() {
147: // Never queued
148: }
149:
150: /**
151: * Method to update an embedded key in this map.
152: * @param key The key
153: * @param fieldNumber Number of field in the key
154: * @param newValue New value for this field
155: */
156: public void updateEmbeddedKey(Object key, int fieldNumber,
157: Object newValue) {
158: // Embedded not supported so do nothing
159: }
160:
161: /**
162: * Method to update an embedded value in this map.
163: * @param value The value
164: * @param fieldNumber Number of field in the value
165: * @param newValue New value for this field
166: */
167: public void updateEmbeddedValue(Object value, int fieldNumber,
168: Object newValue) {
169: // Embedded not supported so do nothing
170: }
171:
172: /**
173: * Accessor for the field name that this TreeMap relates to.
174: * @return The field name
175: **/
176: public String getFieldName() {
177: return fieldName;
178: }
179:
180: /**
181: * Accessor for the owner that this TreeMap relates to.
182: * @return The owner
183: **/
184: public Object getOwner() {
185: return owner;
186: }
187:
188: /**
189: * Method to unset the owner and field details.
190: **/
191: public synchronized void unsetOwner() {
192: if (ownerSM != null) {
193: owner = null;
194: ownerSM = null;
195: fieldName = null;
196: }
197: }
198:
199: /**
200: * Utility to mark the object as dirty
201: **/
202: public void makeDirty() {
203: // Although we are never really "dirty", the owning object must be
204: // marked dirty so that the proper state change occurs and its
205: // jdoPreStore() gets called properly.
206: if (ownerSM != null) {
207: ownerSM.makeDirty(fieldNumber);
208: }
209: }
210:
211: /**
212: * Method to return a detached copy of the container.
213: * Recurse sthrough the keys/values so that they are likewise detached.
214: * @param state State for detachment process
215: * @return The detached container
216: */
217: public Object detachCopy(FetchPlanState state) {
218: java.util.Map detached = new java.util.TreeMap();
219: SCOUtils.detachCopyForMap(ownerSM, entrySet(), state, detached);
220: return detached;
221: }
222:
223: /**
224: * Method to return an attached copy of the passed (detached) value. The returned attached copy
225: * is a SCO wrapper. Goes through the existing keys/values in the store for this owner field and
226: * removes ones no longer present, and adds new keys/values. All keys/values in the (detached)
227: * value are attached.
228: * @param value The new (map) value
229: */
230: public void attachCopy(Object value) {
231: java.util.Map m = (java.util.Map) value;
232:
233: // Attach all of the keys/values in the new map
234: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
235: .getMetaDataForMember(fieldName);
236: boolean keysWithoutIdentity = SCOUtils
237: .mapHasKeysWithoutIdentity(fmd);
238: boolean valuesWithoutIdentity = SCOUtils
239: .mapHasValuesWithoutIdentity(fmd);
240:
241: java.util.Map attachedKeysValues = new java.util.TreeMap();
242: SCOUtils.attachCopyForMap(ownerSM, m.entrySet(),
243: attachedKeysValues, keysWithoutIdentity,
244: valuesWithoutIdentity);
245:
246: // Update the attached map with the detached elements
247: SCOUtils.updateMapWithMapKeysValues(ownerSM.getObjectManager()
248: .getApiAdapter(), this , attachedKeysValues);
249: }
250:
251: // ------------------ Implementation of TreeMap methods --------------------
252:
253: /**
254: * Creates and returns a copy of this object.
255: *
256: * <P>Mutable second-class Objects are required to provide a public
257: * clone method in order to allow for copying PersistenceCapable
258: * objects. In contrast to Object.clone(), this method must not throw a
259: * CloneNotSupportedException.
260: * @return The cloned object
261: */
262: public Object clone() {
263: return delegate.clone();
264: }
265:
266: /**
267: * Accessor for the comparator.
268: * @return The comparator
269: */
270: public Comparator comparator() {
271: return delegate.comparator();
272: }
273:
274: /**
275: * Method to return if the map contains this key
276: * @param key The key
277: * @return Whether it is contained
278: **/
279: public boolean containsKey(Object key) {
280: return delegate.containsKey(key);
281: }
282:
283: /**
284: * Method to return if the map contains this value.
285: * @param value The value
286: * @return Whether it is contained
287: **/
288: public boolean containsValue(Object value) {
289: return delegate.containsValue(value);
290: }
291:
292: /**
293: * Accessor for the set of entries in the Map.
294: * @return Set of entries
295: **/
296: public java.util.Set entrySet() {
297: return delegate.entrySet();
298: }
299:
300: /**
301: * Method to check the equality of this map, and another.
302: * @param o The map to compare against.
303: * @return Whether they are equal.
304: **/
305: public synchronized boolean equals(Object o) {
306: return delegate.equals(o);
307: }
308:
309: /**
310: * Accessor for the first key in the sorted map.
311: * @return The first key
312: **/
313: public Object firstKey() {
314: return delegate.firstKey();
315: }
316:
317: /**
318: * Accessor for the last key in the sorted map.
319: * @return The last key
320: **/
321: public Object lastKey() {
322: return delegate.lastKey();
323: }
324:
325: /**
326: * Method to retrieve the head of the map up to the specified key.
327: * @param toKey the key to return up to.
328: * @return The map meeting the input
329: */
330: public SortedMap headMap(Object toKey) {
331: return delegate.headMap(toKey);
332: }
333:
334: /**
335: * Method to retrieve the subset of the map between the specified keys.
336: * @param fromKey The start key
337: * @param toKey The end key
338: * @return The map meeting the input
339: */
340: public SortedMap subMap(Object fromKey, Object toKey) {
341: return delegate.subMap(fromKey, toKey);
342: }
343:
344: /**
345: * Method to retrieve the part of the map after the specified key.
346: * @param fromKey The start key
347: * @return The map meeting the input
348: */
349: public SortedMap tailMap(Object fromKey) {
350: return delegate.headMap(fromKey);
351: }
352:
353: /**
354: * Accessor for the value stored against a key.
355: * @param key The key
356: * @return The value.
357: **/
358: public Object get(Object key) {
359: return delegate.get(key);
360: }
361:
362: /**
363: * Method to generate a hashcode for this Map.
364: * @return The hashcode.
365: **/
366: public synchronized int hashCode() {
367: return delegate.hashCode();
368: }
369:
370: /**
371: * Method to return if the Map is empty.
372: * @return Whether it is empty.
373: **/
374: public boolean isEmpty() {
375: return delegate.isEmpty();
376: }
377:
378: /**
379: * Accessor for the set of keys in the Map.
380: * @return Set of keys.
381: **/
382: public java.util.Set keySet() {
383: return delegate.keySet();
384: }
385:
386: /**
387: * Method to return the size of the Map.
388: * @return The size
389: **/
390: public int size() {
391: return delegate.size();
392: }
393:
394: /**
395: * Accessor for the set of values in the Map.
396: * @return Set of values.
397: **/
398: public Collection values() {
399: return delegate.values();
400: }
401:
402: /**
403: * Method to clear the TreeMap.
404: **/
405: public void clear() {
406: delegate.clear();
407: makeDirty();
408: }
409:
410: /**
411: * Method to add a value against a key to the TreeMap.
412: * @param key The key
413: * @param value The value
414: * @return The previous value for the specified key.
415: **/
416: public Object put(Object key, Object value) {
417: // Reject inappropriate elements
418: if (value == null && !allowNulls) {
419: throw new NullsNotAllowedException(ownerSM, fieldName);
420: }
421:
422: Object oldValue = delegate.put(key, value);
423: makeDirty();
424: return oldValue;
425: }
426:
427: /**
428: * Method to add the specified Map's values under their keys here.
429: * @param m The map
430: **/
431: public void putAll(java.util.Map m) {
432: delegate.putAll(m);
433: makeDirty();
434: }
435:
436: /**
437: * Method to remove the value for a key from the TreeMap.
438: * @param key The key to remove
439: * @return The value that was removed from this key.
440: **/
441: public Object remove(Object key) {
442: Object removed = delegate.remove(key);
443: makeDirty();
444: return removed;
445: }
446:
447: /**
448: * The writeReplace method is called when ObjectOutputStream is preparing
449: * to write the object to the stream. The ObjectOutputStream checks whether
450: * the class defines the writeReplace method. If the method is defined, the
451: * writeReplace method is called to allow the object to designate its
452: * replacement in the stream. The object returned should be either of the
453: * same type as the object passed in or an object that when read and
454: * resolved will result in an object of a type that is compatible with all
455: * references to the object.
456: * @return the replaced object
457: * @throws ObjectStreamException
458: */
459: protected Object writeReplace() throws ObjectStreamException {
460: return new java.util.TreeMap(delegate);
461: }
462: }
|