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