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.AbstractCollection;
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 Collection object.
037: * This is the simplified form that intercepts mutators and marks the field as dirty.
038: *
039: * @version $Revision: 1.4 $
040: */
041: public class Collection extends AbstractCollection implements
042: SCOCollection, SCOMtoN, Cloneable, java.io.Serializable {
043: protected static final Localiser LOCALISER = Localiser
044: .getInstance("org.jpox.Localisation");
045:
046: protected Object owner;
047: protected StateManager ownerSM;
048: protected String fieldName;
049: protected int fieldNumber;
050: protected boolean allowNulls;
051:
052: /** The internal "delegate". */
053: protected java.util.Collection delegate;
054:
055: /**
056: * Constructor. Called from CollectionMapping.
057: * @param ownerSM The State Manager for this collection.
058: * @param fieldName Name of the field
059: **/
060: public Collection(StateManager ownerSM, String fieldName) {
061: this .ownerSM = ownerSM;
062: this .fieldName = fieldName;
063: this .allowNulls = false;
064:
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 object in the datastore with this value
080: */
081: public void initialise(Object o, boolean forInsert,
082: boolean forUpdate) {
083: java.util.Collection c = (java.util.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 here so do nothing
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: if (ownerSM != null) {
181: ownerSM.makeDirty(fieldNumber);
182: }
183: }
184:
185: /**
186: * Method to return a detached copy of the container.
187: * Recurses through the elements so that they are likewise detached.
188: * @param state State for detachment process
189: * @return The detached container
190: */
191: public Object detachCopy(FetchPlanState state) {
192: java.util.Collection detached = new java.util.HashSet();
193: SCOUtils.detachCopyForCollection(ownerSM, toArray(), state,
194: detached);
195: return detached;
196: }
197:
198: /**
199: * Method to return an attached copy of the passed (detached) value. The returned attached copy
200: * is a SCO wrapper. Goes through the existing elements in the store for this owner field and
201: * removes ones no longer present, and adds new elements. All elements in the (detached)
202: * value are attached.
203: * @param value The new (collection) value
204: */
205: public void attachCopy(Object value) {
206: java.util.Collection c = (java.util.Collection) value;
207:
208: // Attach all of the elements in the new collection
209: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
210: .getMetaDataForMember(fieldName);
211: boolean elementsWithoutIdentity = SCOUtils
212: .collectionHasElementsWithoutIdentity(fmd);
213:
214: java.util.Collection attachedElements = new java.util.HashSet(c
215: .size());
216: SCOUtils.attachCopyForCollection(ownerSM, c.toArray(),
217: attachedElements, elementsWithoutIdentity);
218:
219: // Update the attached collection with the detached elements
220: SCOUtils.updateCollectionWithCollectionElements(this ,
221: attachedElements);
222: }
223:
224: // ---------------- Implementation of Collection methods -------------------
225:
226: /**
227: * Creates and returns a copy of this object.
228: * <P>
229: * Mutable second-class Objects are required to provide a public
230: * clone method in order to allow for copying PersistenceCapable
231: * objects. In contrast to Object.clone(), this method must not throw a
232: * CloneNotSupportedException.
233: * @return A clone of the object
234: */
235: public Object clone() {
236: return ((java.util.HashSet) delegate).clone();
237: }
238:
239: /**
240: * Accessor for whether an element is contained in the Collection.
241: * @param element The element
242: * @return Whether the element is contained here
243: **/
244: public synchronized boolean contains(Object element) {
245: return delegate.contains(element);
246: }
247:
248: /**
249: * Accessor for whether a collection of elements are contained here.
250: * @param c The collection of elements.
251: * @return Whether they are 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 Collection is empty.
276: * @return Whether it is empty.
277: **/
278: public synchronized boolean isEmpty() {
279: return delegate.isEmpty();
280: }
281:
282: /**
283: * Accessor for an iterator for the Collection.
284: * @return The iterator
285: **/
286: public synchronized Iterator iterator() {
287: return new SCOCollectionIterator(this , ownerSM, delegate, null,
288: true);
289: }
290:
291: /**
292: * Accessor for the size of the Collection.
293: * @return The size
294: **/
295: public synchronized int size() {
296: return delegate.size();
297: }
298:
299: /**
300: * Method to return the Collection as an array.
301: * @return The array
302: **/
303: public synchronized Object[] toArray() {
304: return delegate.toArray();
305: }
306:
307: /**
308: * Method to return the Collection as an array.
309: * @param a The array to write the results to
310: * @return The array
311: **/
312: public synchronized Object[] toArray(Object a[]) {
313: return delegate.toArray(a);
314: }
315:
316: /**
317: * Method to add an element to the Collection.
318: * @param element The element to add
319: * @return Whether it was added successfully.
320: **/
321: public synchronized 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 of elements.
336: * @param c The collection of elements to add.
337: * @return Whether they were added successfully.
338: **/
339: public synchronized boolean addAll(java.util.Collection c) {
340: boolean success = delegate.addAll(c);
341: if (success) {
342: makeDirty();
343: }
344: makeDirty();
345: return success;
346: }
347:
348: /**
349: * Method to clear the Collection.
350: **/
351: public synchronized void clear() {
352: delegate.clear();
353: makeDirty();
354: }
355:
356: /**
357: * Method to remove an element from the List
358: * @param element The Element to remove
359: * @return Whether it was removed successfully.
360: **/
361: public synchronized boolean remove(Object element) {
362: return remove(element, true);
363: }
364:
365: /**
366: * Method to remove an element from the List
367: * @param element The Element to remove
368: * @return Whether it was removed successfully.
369: **/
370: public synchronized boolean remove(Object element,
371: boolean allowCascadeDelete) {
372: boolean success = delegate.remove(element);
373: if (success) {
374: makeDirty();
375: }
376: return success;
377: }
378:
379: /**
380: * Method to remove a Collection of elements.
381: * @param c The collection to remove
382: * @return Whether they were removed successfully.
383: **/
384: public synchronized boolean removeAll(java.util.Collection c) {
385: boolean success = delegate.removeAll(c);
386: if (success) {
387: makeDirty();
388: }
389: return success;
390: }
391:
392: /**
393: * Method to retain a Collection of elements (and remove all others).
394: * @param c The collection to retain
395: * @return Whether they were retained successfully.
396: **/
397: public synchronized boolean retainAll(java.util.Collection c) {
398: boolean success = delegate.retainAll(c);
399: if (success) {
400: makeDirty();
401: }
402: return success;
403: }
404:
405: /**
406: * The writeReplace method is called when ObjectOutputStream is preparing
407: * to write the object to the stream. The ObjectOutputStream checks whether
408: * the class defines the writeReplace method. If the method is defined, the
409: * writeReplace method is called to allow the object to designate its
410: * replacement in the stream. The object returned should be either of the
411: * same type as the object passed in or an object that when read and
412: * resolved will result in an object of a type that is compatible with all
413: * references to the object.
414: * @return the replaced object
415: * @throws ObjectStreamException
416: */
417: protected Object writeReplace() throws ObjectStreamException {
418: return new java.util.HashSet(delegate);
419: }
420: }
|