001: /**********************************************************************
002: Copyright (c) 2007 Andy Jeffersonand 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.AbstractSet;
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.store.scostore.SetStore;
032: import org.jpox.util.JPOXLogger;
033: import org.jpox.util.Localiser;
034: import org.jpox.util.StringUtils;
035:
036: /**
037: * A mutable second-class Set object.
038: * This is the simplified form that intercepts mutators and marks the field as dirty.
039: *
040: * @version $Revision: 1.3 $
041: */
042: public class Set extends AbstractSet implements SCOCollection, SCOMtoN,
043: Cloneable, java.io.Serializable {
044: protected static final Localiser LOCALISER = Localiser
045: .getInstance("org.jpox.Localisation");
046:
047: protected Object owner;
048: protected StateManager ownerSM;
049: protected String fieldName;
050: protected int fieldNumber;
051: protected boolean allowNulls;
052:
053: /** The internal "delegate". */
054: protected java.util.Collection delegate;
055:
056: /**
057: * Constructor.
058: * @param ownerSM The State Manager for this set.
059: * @param fieldName Name of the field
060: */
061: public Set(StateManager ownerSM, String fieldName) {
062: this (ownerSM, fieldName, false, null);
063: }
064:
065: /**
066: * Constructor allowing the specification of the backing store to be used.
067: * @param ownerSM State Manager for the owning object
068: * @param fieldName Name of the field
069: * @param allowsNulls Whether nulls are allowed
070: * @param backingStore The backing store
071: */
072: public Set(StateManager ownerSM, String fieldName,
073: boolean allowsNulls, SetStore backingStore) {
074: this .ownerSM = ownerSM;
075: this .fieldName = fieldName;
076: this .allowNulls = allowsNulls;
077: if (ownerSM != null) {
078: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
079: .getMetaDataForMember(fieldName);
080: owner = ownerSM.getObject();
081: fieldNumber = fmd.getAbsoluteFieldNumber();
082: allowNulls = SCOUtils
083: .allowNullsInContainer(allowNulls, fmd);
084: }
085: }
086:
087: /**
088: * Method to initialise the SCO from an existing value.
089: * @param o The object to set from
090: * @param forInsert Whether the object needs inserting in the datastore with this value
091: * @param forUpdate Whether to update the datastore with this value
092: */
093: public void initialise(Object o, boolean forInsert,
094: boolean forUpdate) {
095: java.util.Collection c = (java.util.Collection) o;
096: if (c != null) {
097: delegate = new java.util.HashSet(c); // Make copy of container rather than using same memory
098: } else {
099: delegate = new java.util.HashSet();
100: }
101: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
102: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("023003",
103: StringUtils.toJVMIDString(ownerSM.getObject()),
104: fieldName, "" + size(), SCOUtils
105: .getSCOWrapperOptionsMessage(true, false,
106: allowNulls, false)));
107: }
108: }
109:
110: /**
111: * Method to initialise the SCO for use.
112: */
113: public void initialise() {
114: delegate = new java.util.HashSet();
115: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
116: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("023003",
117: StringUtils.toJVMIDString(ownerSM.getObject()),
118: fieldName, "" + size(), SCOUtils
119: .getSCOWrapperOptionsMessage(true, false,
120: allowNulls, false)));
121: }
122: }
123:
124: // ----------------------- Implementation of SCO methods -------------------
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 element in this collection.
152: * @param element The element
153: * @param fieldNumber Number of field in the element
154: * @param value New value for this field
155: */
156: public void updateEmbeddedElement(Object element, int fieldNumber,
157: Object value) {
158: // Embedded not supported so do nothing
159: }
160:
161: /**
162: * Accessor for the field name.
163: * @return The field name
164: */
165: public String getFieldName() {
166: return fieldName;
167: }
168:
169: /**
170: * Accessor for the owner object.
171: * @return The owner object
172: */
173: public Object getOwner() {
174: return owner;
175: }
176:
177: /**
178: * Method to unset the owner and field information.
179: */
180: public synchronized void unsetOwner() {
181: if (ownerSM != null) {
182: owner = null;
183: ownerSM = null;
184: fieldName = null;
185: }
186: }
187:
188: /**
189: * Utility to mark the object as dirty
190: **/
191: public void makeDirty() {
192: /*
193: * Although we are never really "dirty", the owning object must be
194: * marked dirty so that the proper state change occurs and its
195: * jdoPreStore() gets called properly.
196: */
197: if (ownerSM != null) {
198: ownerSM.makeDirty(fieldNumber);
199: }
200: }
201:
202: /**
203: * Method to return a detached copy of the container.
204: * Recurses through the elements so that they are likewise detached.
205: * @param state State for detachment process
206: * @return The detached container
207: */
208: public Object detachCopy(FetchPlanState state) {
209: java.util.Collection detached = new java.util.HashSet();
210: SCOUtils.detachCopyForCollection(ownerSM, toArray(), state,
211: detached);
212: return detached;
213: }
214:
215: /**
216: * Method to return an attached copy of the passed (detached) value. The returned attached copy
217: * is a SCO wrapper. Goes through the existing elements in the store for this owner field and
218: * removes ones no longer present, and adds new elements. All elements in the (detached)
219: * value are attached.
220: * @param value The new (collection) value
221: */
222: public void attachCopy(Object value) {
223: java.util.Collection c = (java.util.Collection) value;
224:
225: // Attach all of the elements in the new collection
226: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
227: .getMetaDataForMember(fieldName);
228: boolean elementsWithoutIdentity = SCOUtils
229: .collectionHasElementsWithoutIdentity(fmd);
230:
231: java.util.Collection attachedElements = new java.util.HashSet(c
232: .size());
233: SCOUtils.attachCopyForCollection(ownerSM, c.toArray(),
234: attachedElements, elementsWithoutIdentity);
235:
236: // Update the attached collection with the detached elements
237: SCOUtils.updateCollectionWithCollectionElements(this ,
238: attachedElements);
239: }
240:
241: // ---------------- Implementation of Collection methods -------------------
242:
243: /**
244: * Creates and returns a copy of this object.
245: * <P>
246: * Mutable second-class Objects are required to provide a public
247: * clone method in order to allow for copying PersistenceCapable
248: * objects. In contrast to Object.clone(), this method must not throw a
249: * CloneNotSupportedException.
250: * @return A clone of the object
251: */
252: public Object clone() {
253: return ((java.util.HashSet) delegate).clone();
254: }
255:
256: /**
257: * Accessor for whether an element is contained in the Collection.
258: * @param element The element
259: * @return Whether the element is contained here
260: **/
261: public synchronized boolean contains(Object element) {
262: boolean success = delegate.contains(element);
263: makeDirty();
264: return success;
265: }
266:
267: /**
268: * Accessor for whether a collection of elements are contained here.
269: * @param c The collection of elements.
270: * @return Whether they are contained.
271: **/
272: public synchronized boolean containsAll(java.util.Collection c) {
273: return delegate.containsAll(c);
274: }
275:
276: /**
277: * Equality operator.
278: * @param o The object to compare against.
279: * @return Whether this object is the same.
280: **/
281: public synchronized boolean equals(Object o) {
282: return delegate.equals(o);
283: }
284:
285: /**
286: * Hashcode operator.
287: * @return The Hash code.
288: **/
289: public synchronized int hashCode() {
290: return delegate.hashCode();
291: }
292:
293: /**
294: * Accessor for whether the Collection is empty.
295: * @return Whether it is empty.
296: **/
297: public synchronized boolean isEmpty() {
298: return delegate.isEmpty();
299: }
300:
301: /**
302: * Accessor for an iterator for the Collection.
303: * @return The iterator
304: **/
305: public synchronized Iterator iterator() {
306: return new SCOCollectionIterator(this , ownerSM, delegate, null,
307: true);
308: }
309:
310: /**
311: * Accessor for the size of the Collection.
312: * @return The size
313: **/
314: public synchronized int size() {
315: return delegate.size();
316: }
317:
318: /**
319: * Method to return the Collection as an array.
320: * @return The array
321: **/
322: public synchronized Object[] toArray() {
323: return delegate.toArray();
324: }
325:
326: /**
327: * Method to return the Collection as an array.
328: * @param a The array to write the results to
329: * @return The array
330: **/
331: public synchronized Object[] toArray(Object a[]) {
332: return delegate.toArray(a);
333: }
334:
335: /**
336: * Method to add an element to the Collection.
337: * @param element The element to add
338: * @return Whether it was added successfully.
339: **/
340: public synchronized boolean add(Object element) {
341: // Reject inappropriate elements
342: if (element == null && !allowNulls) {
343: throw new NullsNotAllowedException(ownerSM, fieldName);
344: }
345:
346: boolean success = delegate.add(element);
347: if (success) {
348: makeDirty();
349: }
350: return success;
351: }
352:
353: /**
354: * Method to add a collection of elements.
355: * @param c The collection of elements to add.
356: * @return Whether they were added successfully.
357: **/
358: public synchronized boolean addAll(java.util.Collection c) {
359: boolean success = delegate.addAll(c);
360: if (success) {
361: makeDirty();
362: }
363: return success;
364: }
365:
366: /**
367: * Method to clear the Collection.
368: **/
369: public synchronized void clear() {
370: delegate.clear();
371: makeDirty();
372: }
373:
374: /**
375: * Method to remove an element from the List
376: * @param element The Element to remove
377: * @return Whether it was removed successfully.
378: **/
379: public synchronized boolean remove(Object element) {
380: return remove(element, true);
381: }
382:
383: /**
384: * Method to remove an element from the List
385: * @param element The Element to remove
386: * @return Whether it was removed successfully.
387: **/
388: public synchronized boolean remove(Object element,
389: boolean allowCascadeDelete) {
390: boolean success = delegate.remove(element);
391: if (success) {
392: makeDirty();
393: }
394: return success;
395: }
396:
397: /**
398: * Method to remove a Collection of elements.
399: * @param c The collection to remove
400: * @return Whether they were removed successfully.
401: **/
402: public synchronized boolean removeAll(java.util.Collection c) {
403: boolean success = delegate.removeAll(c);
404: if (success) {
405: makeDirty();
406: }
407: return success;
408: }
409:
410: /**
411: * Method to retain a Collection of elements (and remove all others).
412: * @param c The collection to retain
413: * @return Whether they were retained successfully.
414: **/
415: public synchronized boolean retainAll(java.util.Collection c) {
416: boolean success = delegate.retainAll(c);
417: if (success) {
418: makeDirty();
419: }
420: return success;
421: }
422:
423: /**
424: * The writeReplace method is called when ObjectOutputStream is preparing
425: * to write the object to the stream. The ObjectOutputStream checks whether
426: * the class defines the writeReplace method. If the method is defined, the
427: * writeReplace method is called to allow the object to designate its
428: * replacement in the stream. The object returned should be either of the
429: * same type as the object passed in or an object that when read and
430: * resolved will result in an object of a type that is compatible with all
431: * references to the object.
432: * @return the replaced object
433: * @throws ObjectStreamException
434: */
435: protected Object writeReplace() throws ObjectStreamException {
436: return new java.util.HashSet(delegate);
437: }
438: }
|