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.Comparator;
022: import java.util.Iterator;
023:
024: import org.jpox.StateManager;
025: import org.jpox.metadata.AbstractMemberMetaData;
026: import org.jpox.sco.SCOCollection;
027: import org.jpox.sco.SCOCollectionIterator;
028: import org.jpox.sco.SCOMtoN;
029: import org.jpox.sco.SCOUtils;
030: import org.jpox.sco.exceptions.NullsNotAllowedException;
031: import org.jpox.state.FetchPlanState;
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 SortedSet 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 SortedSet extends java.util.AbstractSet implements
043: java.util.SortedSet, SCOCollection, SCOMtoN, Cloneable {
044: protected static final Localiser LOCALISER = Localiser
045: .getInstance("org.jpox.Localisation");
046:
047: protected transient Object owner;
048: protected transient StateManager ownerSM;
049: protected transient String fieldName;
050: protected transient int fieldNumber;
051: protected transient boolean allowNulls;
052:
053: /** The internal "delegate". */
054: protected java.util.TreeSet delegate;
055:
056: /**
057: * Constructor, using the StateManager of the "owner" and the field name.
058: * @param ownerSM The owner StateManager
059: * @param fieldName The name of the field of the SCO.
060: */
061: public SortedSet(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: initialiseDelegate();
086: delegate.addAll(c);
087: } else {
088: initialiseDelegate();
089: }
090: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
091: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("023003",
092: StringUtils.toJVMIDString(ownerSM.getObject()),
093: fieldName, "" + size(), SCOUtils
094: .getSCOWrapperOptionsMessage(true, false,
095: allowNulls, false)));
096: }
097: }
098:
099: /**
100: * Method to initialise the SCO for use.
101: */
102: public void initialise() {
103: initialiseDelegate();
104: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
105: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("023003",
106: StringUtils.toJVMIDString(ownerSM.getObject()),
107: fieldName, "" + size(), SCOUtils
108: .getSCOWrapperOptionsMessage(true, false,
109: allowNulls, false)));
110: }
111: }
112:
113: /**
114: * Convenience method to set up the delegate respecting any comparator specified in MetaData.
115: */
116: protected void initialiseDelegate() {
117: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
118: .getMetaDataForMember(fieldName);
119: Comparator comparator = SCOUtils.getComparator(fmd, ownerSM
120: .getObjectManager().getClassLoaderResolver());
121: if (comparator != null) {
122: this .delegate = new java.util.TreeSet(comparator);
123: } else {
124: this .delegate = new java.util.TreeSet();
125: }
126: }
127:
128: // ----------------------- Implementation of SCO methods -------------------
129:
130: /**
131: * Accessor for the unwrapped value that we are wrapping.
132: * @return The unwrapped value
133: */
134: public Object getValue() {
135: return delegate;
136: }
137:
138: /**
139: * Method to effect the load of the data in the SCO.
140: * Used when the SCO supports lazy-loading to tell it to load all now.
141: */
142: public void load() {
143: // Always loaded
144: }
145:
146: /**
147: * Method to flush the changes to the datastore when operating in queued mode.
148: * Does nothing in "direct" mode.
149: */
150: public void flush() {
151: // Never queued
152: }
153:
154: /**
155: * Method to update an embedded element in this collection.
156: * @param element The element
157: * @param fieldNumber Number of field in the element
158: * @param value New value for this field
159: */
160: public void updateEmbeddedElement(Object element, int fieldNumber,
161: Object value) {
162: // Embedded not supported so do nothing
163: }
164:
165: /**
166: * Accessor for the field name.
167: * @return The field name
168: */
169: public String getFieldName() {
170: return fieldName;
171: }
172:
173: /**
174: * Accessor for the owner object.
175: * @return The owner object
176: */
177: public Object getOwner() {
178: return owner;
179: }
180:
181: /**
182: * Method to unset the owner and field information.
183: */
184: public synchronized void unsetOwner() {
185: if (ownerSM != null) {
186: owner = null;
187: ownerSM = null;
188: fieldName = null;
189: }
190: }
191:
192: /**
193: * Utility to mark the object as dirty
194: **/
195: public void makeDirty() {
196: /*
197: * Although we are never really "dirty", the owning object must be
198: * marked dirty so that the proper state change occurs and its
199: * jdoPreStore() gets called properly.
200: */
201: if (ownerSM != null) {
202: ownerSM.makeDirty(fieldNumber);
203: }
204: }
205:
206: /**
207: * Method to return a detached copy of the container.
208: * Recurse sthrough the elements so that they are likewise detached.
209: * @param state State for detachment process
210: * @return The detached container
211: */
212: public Object detachCopy(FetchPlanState state) {
213: java.util.Collection detached = new java.util.TreeSet();
214: SCOUtils.detachCopyForCollection(ownerSM, toArray(), state,
215: detached);
216: return detached;
217: }
218:
219: /**
220: * Method to return an attached copy of the passed (detached) value. The returned attached copy
221: * is a SCO wrapper. Goes through the existing elements in the store for this owner field and
222: * removes ones no longer present, and adds new elements. All elements in the (detached)
223: * value are attached.
224: * @param value The new (collection) value
225: */
226: public void attachCopy(Object value) {
227: java.util.Collection c = (java.util.Collection) value;
228:
229: // Attach all of the elements in the new collection
230: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
231: .getMetaDataForMember(fieldName);
232: boolean elementsWithoutIdentity = SCOUtils
233: .collectionHasElementsWithoutIdentity(fmd);
234:
235: java.util.Collection attachedElements = new java.util.HashSet(c
236: .size());
237: SCOUtils.attachCopyForCollection(ownerSM, c.toArray(),
238: attachedElements, elementsWithoutIdentity);
239:
240: // Update the attached collection with the detached elements
241: SCOUtils.updateCollectionWithCollectionElements(this ,
242: attachedElements);
243: }
244:
245: // ------------------ Implementation of SortedSet methods --------------------
246:
247: /**
248: * Creates and returns a copy of this object.
249: * @return The cloned object
250: */
251: public Object clone() {
252: return delegate.clone();
253: }
254:
255: /**
256: * Accessor for the comparator.
257: * @return The comparator
258: */
259: public Comparator comparator() {
260: return delegate.comparator();
261: }
262:
263: /**
264: * Accessor for whether an element is contained in this Set.
265: * @param element The element
266: * @return Whether it is contained.
267: **/
268: public boolean contains(Object element) {
269: return delegate.contains(element);
270: }
271:
272: /**
273: * Accessor for whether a collection is contained in this Set.
274: * @param c The collection
275: * @return Whether it is contained.
276: **/
277: public synchronized boolean containsAll(java.util.Collection c) {
278: return delegate.containsAll(c);
279: }
280:
281: /**
282: * Equality operator.
283: * @param o The object to compare against.
284: * @return Whether this object is the same.
285: **/
286: public synchronized boolean equals(Object o) {
287: return delegate.equals(o);
288: }
289:
290: /**
291: * Accessor for the first element in the sorted set.
292: * @return The first element
293: **/
294: public Object first() {
295: return delegate.first();
296: }
297:
298: /**
299: * Hashcode operator.
300: * @return The Hash code.
301: **/
302: public synchronized int hashCode() {
303: return delegate.hashCode();
304: }
305:
306: /**
307: * Accessor for whether the SortedSet is empty.
308: * @return Whether it is empty.
309: **/
310: public boolean isEmpty() {
311: return delegate.isEmpty();
312: }
313:
314: /**
315: * Accessor for an iterator for the Set.
316: * @return The iterator
317: **/
318: public Iterator iterator() {
319: return new SCOCollectionIterator(this , ownerSM, delegate, null,
320: true);
321: }
322:
323: /**
324: * Method to retrieve the head elements up to the specified element.
325: * @param toElement the element to return up to.
326: * @return The set of elements meeting the input
327: */
328: public java.util.SortedSet headSet(Object toElement) {
329: return delegate.headSet(toElement);
330: }
331:
332: /**
333: * Method to retrieve the subset of elements between the specified elements.
334: * @param fromElement The start element
335: * @param toElement The end element
336: * @return The set of elements meeting the input
337: */
338: public java.util.SortedSet subSet(Object fromElement,
339: Object toElement) {
340: return delegate.subSet(fromElement, toElement);
341: }
342:
343: /**
344: * Method to retrieve the set of elements after the specified element.
345: * @param fromElement The start element
346: * @return The set of elements meeting the input
347: */
348: public java.util.SortedSet tailSet(Object fromElement) {
349: return delegate.headSet(fromElement);
350: }
351:
352: /**
353: * Accessor for the last element in the sorted set.
354: * @return The last element
355: **/
356: public Object last() {
357: return delegate.last();
358: }
359:
360: /**
361: * Accessor for the size of the SortedSet.
362: * @return The size.
363: **/
364: public int size() {
365: return delegate.size();
366: }
367:
368: /**
369: * Method to return the list as an array.
370: * @return The array
371: **/
372: public Object[] toArray() {
373: return delegate.toArray();
374: }
375:
376: /**
377: * Method to return the list as an array.
378: * @param a The runtime types of the array being defined by this param
379: * @return The array
380: **/
381: public Object[] toArray(Object a[]) {
382: return delegate.toArray(a);
383: }
384:
385: /**
386: * Method to add an element to the SortedSet.
387: * @param element The new element
388: * @return Whether it was added ok.
389: **/
390: public boolean add(Object element) {
391: // Reject inappropriate elements
392: if (element == null && !allowNulls) {
393: throw new NullsNotAllowedException(ownerSM, fieldName);
394: }
395:
396: boolean success = delegate.add(element);
397: if (success) {
398: makeDirty();
399: }
400: return success;
401: }
402:
403: /**
404: * Method to add a collection to the SortedSet.
405: * @param elements The collection
406: * @return Whether it was added ok.
407: **/
408: public boolean addAll(Collection elements) {
409: boolean success = delegate.addAll(elements);
410: if (success) {
411: makeDirty();
412: }
413: return success;
414: }
415:
416: /**
417: * Method to clear the SortedSet
418: **/
419: public void clear() {
420: delegate.clear();
421: makeDirty();
422: }
423:
424: /**
425: * Method to remove an element from the List
426: * @param element The Element to remove
427: * @return Whether it was removed successfully.
428: **/
429: public synchronized boolean remove(Object element) {
430: return remove(element, true);
431: }
432:
433: /**
434: * Method to remove an element from the List
435: * @param element The Element to remove
436: * @return Whether it was removed successfully.
437: **/
438: public synchronized boolean remove(Object element,
439: boolean allowCascadeDelete) {
440: boolean success = delegate.remove(element);
441: if (success) {
442: makeDirty();
443: }
444: return success;
445: }
446:
447: /**
448: * Method to remove all elements from the collection from the SortedSet.
449: * @param elements The collection of elements to remove
450: * @return Whether it was removed ok.
451: **/
452: public boolean removeAll(java.util.Collection elements) {
453: boolean success = delegate.removeAll(elements);
454: if (success) {
455: makeDirty();
456: }
457: return success;
458: }
459:
460: /**
461: * Method to retain a Collection of elements (and remove all others).
462: * @param c The collection to retain
463: * @return Whether they were retained successfully.
464: **/
465: public synchronized boolean retainAll(java.util.Collection c) {
466: boolean success = delegate.retainAll(c);
467: if (success) {
468: makeDirty();
469: }
470: return success;
471: }
472:
473: /**
474: * The writeReplace method is called when ObjectOutputStream is preparing
475: * to write the object to the stream. The ObjectOutputStream checks
476: * whether the class defines the writeReplace method. If the method is
477: * defined, the writeReplace method is called to allow the object to
478: * designate its replacement in the stream. The object returned should be
479: * either of the same type as the object passed in or an object that when
480: * read and resolved will result in an object of a type that is compatible
481: * with all references to the object.
482: * @return the replaced object
483: * @throws ObjectStreamException
484: */
485: protected Object writeReplace() throws ObjectStreamException {
486: return new java.util.TreeSet(delegate);
487: }
488: }
|