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 LinkedHashSet 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 LinkedHashSet extends java.util.LinkedHashSet 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.LinkedHashSet delegate;
054:
055: /**
056: * Constructor, using the StateManager of the "owner" and the field name.
057: * @param ownerSM The owner StateManager
058: * @param fieldName The name of the field of the SCO.
059: */
060: public LinkedHashSet(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 The object to set from
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: Collection c = (Collection) o;
083: if (c != null) {
084: delegate = new java.util.LinkedHashSet(c); // Make copy of container rather than using same memory
085: } else {
086: delegate = new java.util.LinkedHashSet();
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.LinkedHashSet();
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: // ----------------------- Implementation of SCO methods -------------------
112:
113: /**
114: * Accessor for the unwrapped value that we are wrapping.
115: * @return The unwrapped value
116: */
117: public Object getValue() {
118: return delegate;
119: }
120:
121: /**
122: * Method to effect the load of the data in the SCO.
123: * Used when the SCO supports lazy-loading to tell it to load all now.
124: */
125: public void load() {
126: // Always loaded
127: }
128:
129: /**
130: * Method to flush the changes to the datastore when operating in queued mode.
131: * Does nothing in "direct" mode.
132: */
133: public void flush() {
134: // Never queued
135: }
136:
137: /**
138: * Method to update an embedded element in this collection.
139: * @param element The element
140: * @param fieldNumber Number of field in the element
141: * @param value New value for this field
142: */
143: public void updateEmbeddedElement(Object element, int fieldNumber,
144: Object value) {
145: // Embedded not supported so do nothing
146: }
147:
148: /**
149: * Accessor for the field name.
150: * @return The field name
151: */
152: public String getFieldName() {
153: return fieldName;
154: }
155:
156: /**
157: * Accessor for the owner object.
158: * @return The owner object
159: */
160: public Object getOwner() {
161: return owner;
162: }
163:
164: /**
165: * Method to unset the owner and field information.
166: */
167: public synchronized void unsetOwner() {
168: if (ownerSM != null) {
169: owner = null;
170: ownerSM = null;
171: fieldName = null;
172: }
173: }
174:
175: /**
176: * Utility to mark the object as dirty
177: **/
178: public void makeDirty() {
179: /*
180: * Although we are never really "dirty", the owning object must be
181: * marked dirty so that the proper state change occurs and its
182: * jdoPreStore() gets called properly.
183: */
184: if (ownerSM != null) {
185: ownerSM.makeDirty(fieldNumber);
186: }
187: }
188:
189: /**
190: * Method to return a detached copy of the container.
191: * Recurse sthrough the elements so that they are likewise detached.
192: * @param state State for detachment process
193: * @return The detached container
194: */
195: public Object detachCopy(FetchPlanState state) {
196: java.util.Collection detached = new java.util.LinkedHashSet();
197: SCOUtils.detachCopyForCollection(ownerSM, toArray(), state,
198: detached);
199: return detached;
200: }
201:
202: /**
203: * Method to return an attached copy of the passed (detached) value. The returned attached copy
204: * is a SCO wrapper. Goes through the existing elements in the store for this owner field and
205: * removes ones no longer present, and adds new elements. All elements in the (detached)
206: * value are attached.
207: * @param value The new (collection) value
208: */
209: public void attachCopy(Object value) {
210: java.util.Collection c = (java.util.Collection) value;
211:
212: // Attach all of the elements in the new collection
213: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
214: .getMetaDataForMember(fieldName);
215: boolean elementsWithoutIdentity = SCOUtils
216: .collectionHasElementsWithoutIdentity(fmd);
217:
218: java.util.Collection attachedElements = new java.util.HashSet(c
219: .size());
220: SCOUtils.attachCopyForCollection(ownerSM, c.toArray(),
221: attachedElements, elementsWithoutIdentity);
222:
223: // Update the attached collection with the detached elements
224: SCOUtils.updateCollectionWithCollectionElements(this ,
225: attachedElements);
226: }
227:
228: // ------------------ Implementation of LinkedHashSet methods --------------------
229:
230: /**
231: * Creates and returns a copy of this object.
232: * @return The cloned object
233: */
234: public Object clone() {
235: return delegate.clone();
236: }
237:
238: /**
239: * Accessor for whether an element is contained in this Set.
240: * @param element The element
241: * @return Whether it is contained.
242: **/
243: public boolean contains(Object element) {
244: return delegate.contains(element);
245: }
246:
247: /**
248: * Accessor for whether a collection is contained in this Set.
249: * @param c The collection
250: * @return Whether it is contained.
251: **/
252: public synchronized boolean containsAll(java.util.Collection c) {
253: return delegate.containsAll(c);
254: }
255:
256: /**
257: * Equality operator.
258: * @param o The object to compare against.
259: * @return Whether this object is the same.
260: **/
261: public synchronized boolean equals(Object o) {
262: return delegate.equals(o);
263: }
264:
265: /**
266: * Hashcode operator.
267: * @return The Hash code.
268: **/
269: public synchronized int hashCode() {
270: return delegate.hashCode();
271: }
272:
273: /**
274: * Accessor for whether the LinkedHashSet is empty.
275: * @return Whether it is empty.
276: **/
277: public boolean isEmpty() {
278: return delegate.isEmpty();
279: }
280:
281: /**
282: * Accessor for an iterator for the Set.
283: * @return The iterator
284: **/
285: public Iterator iterator() {
286: return new SCOCollectionIterator(this , ownerSM, delegate, null,
287: true);
288: }
289:
290: /**
291: * Accessor for the size of the LinkedHashSet.
292: * @return The size.
293: **/
294: public int size() {
295: return delegate.size();
296: }
297:
298: /**
299: * Method to return the list as an array.
300: * @return The array
301: **/
302: public Object[] toArray() {
303: return delegate.toArray();
304: }
305:
306: /**
307: * Method to return the list as an array.
308: * @param a The runtime types of the array being defined by this param
309: * @return The array
310: **/
311: public Object[] toArray(Object a[]) {
312: return delegate.toArray(a);
313: }
314:
315: // ------------------------------ Mutator methods --------------------------
316:
317: /**
318: * Method to add an element to the LinkedHashSet.
319: * @param element The new element
320: * @return Whether it was added ok.
321: **/
322: public boolean add(Object element) {
323: // Reject inappropriate elements
324: if (element == null && !allowNulls) {
325: throw new NullsNotAllowedException(ownerSM, fieldName);
326: }
327:
328: boolean success = delegate.add(element);
329: if (success) {
330: makeDirty();
331: }
332: return success;
333: }
334:
335: /**
336: * Method to add a collection to the LinkedHashSet.
337: * @param elements The collection
338: * @return Whether it was added ok.
339: **/
340: public boolean addAll(Collection elements) {
341: boolean success = delegate.addAll(elements);
342: if (success) {
343: makeDirty();
344: }
345: return success;
346: }
347:
348: /**
349: * Method to clear the LinkedHashSet
350: **/
351: public 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 all elements from the collection from the LinkedHashSet.
381: * @param elements The collection of elements to remove
382: * @return Whether it was removed ok.
383: **/
384: public boolean removeAll(java.util.Collection elements) {
385: boolean success = delegate.removeAll(elements);
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
408: * whether the class defines the writeReplace method. If the method is
409: * defined, the writeReplace method is called to allow the object to
410: * designate its replacement in the stream. The object returned should be
411: * either of the same type as the object passed in or an object that when
412: * read and resolved will result in an object of a type that is compatible
413: * with all references to the object.
414: * @return the replaced object
415: * @throws ObjectStreamException
416: */
417: protected Object writeReplace() throws ObjectStreamException {
418: return new java.util.LinkedHashSet(delegate);
419: }
420: }
|