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