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