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.Iterator;
022:
023: import org.jpox.StateManager;
024: import org.jpox.metadata.AbstractMemberMetaData;
025: import org.jpox.sco.SCOCollection;
026: import org.jpox.sco.SCOCollectionIterator;
027: import org.jpox.sco.SCOMtoN;
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 HashSet object.
037: * This is the simplified form that intercepts mutators and marks the field as dirty.
038: *
039: * @version $Revision: 1.3 $
040: */
041: public class HashSet extends java.util.HashSet implements
042: SCOCollection, SCOMtoN, 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.HashSet delegate;
054:
055: /**
056: * Constructor, using the StateManager of the "owner" and the field name.
057: *
058: * @param ownerSM The owner StateManager
059: * @param fieldName The name of the field of the SCO.
060: */
061: public HashSet(StateManager ownerSM, String fieldName) {
062: this .ownerSM = ownerSM;
063: this .fieldName = fieldName;
064: this .allowNulls = false;
065: if (ownerSM != null) {
066: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
067: .getMetaDataForMember(fieldName);
068: owner = ownerSM.getObject();
069: fieldNumber = fmd.getAbsoluteFieldNumber();
070: allowNulls = SCOUtils
071: .allowNullsInContainer(allowNulls, fmd);
072: }
073: }
074:
075: /**
076: * Method to initialise the SCO from an existing value.
077: * @param o The object to set from
078: * @param forInsert Whether the object needs inserting in the datastore with this value
079: * @param forUpdate Whether to update the datastore with this value
080: */
081: public void initialise(Object o, boolean forInsert,
082: boolean forUpdate) {
083: Collection c = (Collection) o;
084: if (c != null) {
085: delegate = new java.util.HashSet(c); // Make copy of the elements rather than using same memory
086: } else {
087: delegate = new java.util.HashSet();
088: }
089: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
090: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("023003",
091: StringUtils.toJVMIDString(ownerSM.getObject()),
092: fieldName, "" + size(), SCOUtils
093: .getSCOWrapperOptionsMessage(true, false,
094: allowNulls, false)));
095: }
096: }
097:
098: /**
099: * Method to initialise the SCO for use.
100: */
101: public void initialise() {
102: delegate = new java.util.HashSet();
103: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
104: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("023003",
105: StringUtils.toJVMIDString(ownerSM.getObject()),
106: fieldName, "" + size(), SCOUtils
107: .getSCOWrapperOptionsMessage(true, false,
108: allowNulls, false)));
109: }
110: }
111:
112: // ----------------------- Implementation of SCO methods -------------------
113:
114: /**
115: * Accessor for the unwrapped value that we are wrapping.
116: * @return The unwrapped value
117: */
118: public Object getValue() {
119: return delegate;
120: }
121:
122: /**
123: * Method to effect the load of the data in the SCO.
124: * Used when the SCO supports lazy-loading to tell it to load all now.
125: */
126: public void load() {
127: // Always loaded
128: }
129:
130: /**
131: * Method to flush the changes to the datastore when operating in queued mode.
132: * Does nothing in "direct" mode.
133: */
134: public void flush() {
135: // Never queued
136: }
137:
138: /**
139: * Method to update an embedded element in this collection.
140: * @param element The element
141: * @param fieldNumber Number of field in the element
142: * @param value New value for this field
143: */
144: public void updateEmbeddedElement(Object element, int fieldNumber,
145: Object value) {
146: // Embedded not supported so nothing to do
147: }
148:
149: /**
150: * Accessor for the field name.
151: * @return The field name
152: */
153: public String getFieldName() {
154: return fieldName;
155: }
156:
157: /**
158: * Accessor for the owner object.
159: * @return The owner object
160: */
161: public Object getOwner() {
162: return owner;
163: }
164:
165: /**
166: * Method to unset the owner and field information.
167: */
168: public synchronized void unsetOwner() {
169: if (ownerSM != null) {
170: owner = null;
171: ownerSM = null;
172: fieldName = null;
173: }
174: }
175:
176: /**
177: * Utility to mark the object as dirty
178: **/
179: public void makeDirty() {
180: /*
181: * Although we are never really "dirty", the owning object must be
182: * marked dirty so that the proper state change occurs and its
183: * jdoPreStore() gets called properly.
184: */
185: if (ownerSM != null) {
186: ownerSM.makeDirty(fieldNumber);
187: }
188: }
189:
190: /**
191: * Method to return a detached copy of the container.
192: * Recurse sthrough the elements so that they are likewise detached.
193: * @param state State for detachment process
194: * @return The detached container
195: */
196: public Object detachCopy(FetchPlanState state) {
197: java.util.Collection detached = new java.util.HashSet();
198: SCOUtils.detachCopyForCollection(ownerSM, toArray(), state,
199: 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 elements in the store for this owner field and
206: * removes ones no longer present, and adds new elements. All elements in the (detached)
207: * value are attached.
208: * @param value The new (collection) value
209: */
210: public void attachCopy(Object value) {
211: java.util.Collection c = (java.util.Collection) value;
212:
213: // Attach all of the elements in the new collection
214: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
215: .getMetaDataForMember(fieldName);
216: boolean elementsWithoutIdentity = SCOUtils
217: .collectionHasElementsWithoutIdentity(fmd);
218:
219: java.util.Collection attachedElements = new java.util.HashSet(c
220: .size());
221: SCOUtils.attachCopyForCollection(ownerSM, c.toArray(),
222: attachedElements, elementsWithoutIdentity);
223:
224: // Update the attached collection with the detached elements
225: SCOUtils.updateCollectionWithCollectionElements(this ,
226: attachedElements);
227: }
228:
229: // ------------------ Implementation of HashSet methods --------------------
230:
231: /**
232: * Creates and returns a copy of this object.
233: * @return The cloned object
234: */
235: public Object clone() {
236: return delegate.clone();
237: }
238:
239: /**
240: * Accessor for whether an element is contained in this Set.
241: * @param element The element
242: * @return Whether it is contained.
243: **/
244: public boolean contains(Object element) {
245: return delegate.contains(element);
246: }
247:
248: /**
249: * Accessor for whether a collection is contained in this Set.
250: * @param c The collection
251: * @return Whether it is contained.
252: **/
253: public synchronized boolean containsAll(java.util.Collection c) {
254: return delegate.containsAll(c);
255: }
256:
257: /**
258: * Equality operator.
259: * @param o The object to compare against.
260: * @return Whether this object is the same.
261: **/
262: public synchronized boolean equals(Object o) {
263: return delegate.equals(o);
264: }
265:
266: /**
267: * Hashcode operator.
268: * @return The Hash code.
269: **/
270: public synchronized int hashCode() {
271: return delegate.hashCode();
272: }
273:
274: /**
275: * Accessor for whether the HashSet is empty.
276: * @return Whether it is empty.
277: **/
278: public boolean isEmpty() {
279: return delegate.isEmpty();
280: }
281:
282: /**
283: * Accessor for an iterator for the Set.
284: * @return The iterator
285: **/
286: public Iterator iterator() {
287: return new SCOCollectionIterator(this , ownerSM, delegate, null,
288: true);
289: }
290:
291: /**
292: * Accessor for the size of the HashSet.
293: * @return The size.
294: **/
295: public int size() {
296: return delegate.size();
297: }
298:
299: /**
300: * Method to return the list as an array.
301: * @return The array
302: **/
303: public Object[] toArray() {
304: return delegate.toArray();
305: }
306:
307: /**
308: * Method to return the list as an array.
309: * @param a The runtime types of the array being defined by this param
310: * @return The array
311: **/
312: public Object[] toArray(Object a[]) {
313: return delegate.toArray(a);
314: }
315:
316: /**
317: * Method to add an element to the HashSet.
318: * @param element The new element
319: * @return Whether it was added ok.
320: **/
321: public boolean add(Object element) {
322: // Reject inappropriate elements
323: if (element == null && !allowNulls) {
324: throw new NullsNotAllowedException(ownerSM, fieldName);
325: }
326:
327: boolean success = delegate.add(element);
328: if (success) {
329: makeDirty();
330: }
331: return success;
332: }
333:
334: /**
335: * Method to add a collection to the HashSet.
336: * @param c The collection
337: * @return Whether it was added ok.
338: **/
339: public boolean addAll(Collection c) {
340: boolean success = delegate.addAll(c);
341: if (success) {
342: makeDirty();
343: }
344: return success;
345: }
346:
347: /**
348: * Method to clear the HashSet
349: **/
350: public void clear() {
351: delegate.clear();
352: makeDirty();
353: }
354:
355: /**
356: * Method to remove an element from the List
357: * @param element The Element to remove
358: * @return Whether it was removed successfully.
359: **/
360: public synchronized boolean remove(Object element) {
361: return remove(element, true);
362: }
363:
364: /**
365: * Method to remove an element from the List
366: * @param element The Element to remove
367: * @return Whether it was removed successfully.
368: **/
369: public synchronized boolean remove(Object element,
370: boolean allowCascadeDelete) {
371: boolean success = delegate.remove(element);
372: if (success) {
373: makeDirty();
374: }
375: return success;
376: }
377:
378: /**
379: * Method to remove all elements from the collection from the HashSet.
380: * @param c The collection of elements to remove
381: * @return Whether it was removed ok.
382: **/
383: public boolean removeAll(java.util.Collection c) {
384: boolean success = delegate.removeAll(c);
385: if (success) {
386: makeDirty();
387: }
388: return success;
389: }
390:
391: /**
392: * Method to retain a Collection of elements (and remove all others).
393: * @param c The collection to retain
394: * @return Whether they were retained successfully.
395: **/
396: public synchronized boolean retainAll(java.util.Collection c) {
397: boolean success = delegate.retainAll(c);
398: if (success) {
399: makeDirty();
400: }
401: return success;
402: }
403:
404: /**
405: * The writeReplace method is called when ObjectOutputStream is preparing
406: * to write the object to the stream. The ObjectOutputStream checks
407: * whether the class defines the writeReplace method. If the method is
408: * defined, the writeReplace method is called to allow the object to
409: * designate its replacement in the stream. The object returned should be
410: * either of the same type as the object passed in or an object that when
411: * read and resolved will result in an object of a type that is compatible
412: * with all references to the object.
413: * @return the replaced object
414: * @throws ObjectStreamException
415: */
416: protected Object writeReplace() throws ObjectStreamException {
417: return new java.util.HashSet(delegate);
418: }
419: }
|