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: import java.util.SortedSet;
024:
025: import org.jpox.StateManager;
026: import org.jpox.metadata.AbstractMemberMetaData;
027: import org.jpox.sco.SCOCollection;
028: import org.jpox.sco.SCOCollectionIterator;
029: import org.jpox.sco.SCOMtoN;
030: import org.jpox.sco.SCOUtils;
031: import org.jpox.sco.exceptions.NullsNotAllowedException;
032: import org.jpox.state.FetchPlanState;
033: import org.jpox.util.JPOXLogger;
034: import org.jpox.util.Localiser;
035: import org.jpox.util.StringUtils;
036:
037: /**
038: * A mutable second-class TreeSet object.
039: * This is the simplified form that intercepts mutators and marks the field as dirty.
040: *
041: * @version $Revision: 1.3 $
042: */
043: public class TreeSet extends java.util.TreeSet implements
044: SCOCollection, SCOMtoN, Cloneable {
045: protected static final Localiser LOCALISER = Localiser
046: .getInstance("org.jpox.Localisation");
047:
048: protected transient Object owner;
049: protected transient StateManager ownerSM;
050: protected transient String fieldName;
051: protected transient int fieldNumber;
052: protected transient boolean allowNulls;
053:
054: /** The internal "delegate". */
055: protected java.util.TreeSet delegate;
056:
057: /**
058: * Constructor, using the StateManager of the "owner" and the field name.
059: * @param ownerSM The owner StateManager
060: * @param fieldName The name of the field of the SCO.
061: */
062: public TreeSet(StateManager ownerSM, String fieldName) {
063: this .ownerSM = ownerSM;
064: this .fieldName = fieldName;
065: this .allowNulls = false;
066: if (ownerSM != null) {
067: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
068: .getMetaDataForMember(fieldName);
069: owner = ownerSM.getObject();
070: fieldNumber = fmd.getAbsoluteFieldNumber();
071: allowNulls = SCOUtils
072: .allowNullsInContainer(allowNulls, fmd);
073: }
074: }
075:
076: /**
077: * Method to initialise the SCO from an existing value.
078: * @param o The object to set from
079: * @param forInsert Whether the object needs inserting in the datastore with this value
080: * @param forUpdate Whether to update the datastore with this value
081: */
082: public void initialise(Object o, boolean forInsert,
083: boolean forUpdate) {
084: Collection c = (Collection) o;
085: if (c != null) {
086: initialiseDelegate();
087: delegate.addAll(c);
088: } else {
089: initialiseDelegate();
090: }
091: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
092: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("023003",
093: StringUtils.toJVMIDString(ownerSM.getObject()),
094: fieldName, "" + size(), SCOUtils
095: .getSCOWrapperOptionsMessage(true, false,
096: allowNulls, false)));
097: }
098: }
099:
100: /**
101: * Method to initialise the SCO for use.
102: */
103: public void initialise() {
104: initialiseDelegate();
105: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
106: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("023003",
107: StringUtils.toJVMIDString(ownerSM.getObject()),
108: fieldName, "" + size(), SCOUtils
109: .getSCOWrapperOptionsMessage(true, false,
110: allowNulls, false)));
111: }
112: }
113:
114: /**
115: * Convenience method to set up the delegate respecting any comparator specified in MetaData.
116: */
117: protected void initialiseDelegate() {
118: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
119: .getMetaDataForMember(fieldName);
120: Comparator comparator = SCOUtils.getComparator(fmd, ownerSM
121: .getObjectManager().getClassLoaderResolver());
122: if (comparator != null) {
123: this .delegate = new java.util.TreeSet(comparator);
124: } else {
125: this .delegate = new java.util.TreeSet();
126: }
127: }
128:
129: // ----------------------- Implementation of SCO methods -------------------
130:
131: /**
132: * Accessor for the unwrapped value that we are wrapping.
133: * @return The unwrapped value
134: */
135: public Object getValue() {
136: return delegate;
137: }
138:
139: /**
140: * Method to effect the load of the data in the SCO.
141: * Used when the SCO supports lazy-loading to tell it to load all now.
142: */
143: public void load() {
144: // Always loaded
145: }
146:
147: /**
148: * Method to flush the changes to the datastore when operating in queued mode.
149: * Does nothing in "direct" mode.
150: */
151: public void flush() {
152: // Never queued
153: }
154:
155: /**
156: * Method to update an embedded element in this collection.
157: * @param element The element
158: * @param fieldNumber Number of field in the element
159: * @param value New value for this field
160: */
161: public void updateEmbeddedElement(Object element, int fieldNumber,
162: Object value) {
163: // Embedded not supported so do nothing
164: }
165:
166: /**
167: * Accessor for the field name.
168: * @return The field name
169: */
170: public String getFieldName() {
171: return fieldName;
172: }
173:
174: /**
175: * Accessor for the owner object.
176: * @return The owner object
177: */
178: public Object getOwner() {
179: return owner;
180: }
181:
182: /**
183: * Method to unset the owner and field information.
184: */
185: public synchronized void unsetOwner() {
186: if (ownerSM != null) {
187: owner = null;
188: ownerSM = null;
189: fieldName = null;
190: }
191: }
192:
193: /**
194: * Utility to mark the object as dirty
195: **/
196: public void makeDirty() {
197: /*
198: * Although we are never really "dirty", the owning object must be
199: * marked dirty so that the proper state change occurs and its
200: * jdoPreStore() gets called properly.
201: */
202: if (ownerSM != null) {
203: ownerSM.makeDirty(fieldNumber);
204: }
205: }
206:
207: /**
208: * Method to return a detached copy of the container.
209: * Recurse sthrough the elements so that they are likewise detached.
210: * @param state State for detachment process
211: * @return The detached container
212: */
213: public Object detachCopy(FetchPlanState state) {
214: java.util.Collection detached = new java.util.TreeSet();
215: SCOUtils.detachCopyForCollection(ownerSM, toArray(), state,
216: detached);
217: return detached;
218: }
219:
220: /**
221: * Method to return an attached copy of the passed (detached) value. The returned attached copy
222: * is a SCO wrapper. Goes through the existing elements in the store for this owner field and
223: * removes ones no longer present, and adds new elements. All elements in the (detached)
224: * value are attached.
225: * @param value The new (collection) value
226: */
227: public void attachCopy(Object value) {
228: java.util.Collection c = (java.util.Collection) value;
229:
230: // Attach all of the elements in the new collection
231: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
232: .getMetaDataForMember(fieldName);
233: boolean elementsWithoutIdentity = SCOUtils
234: .collectionHasElementsWithoutIdentity(fmd);
235:
236: java.util.Collection attachedElements = new java.util.HashSet(c
237: .size());
238: SCOUtils.attachCopyForCollection(ownerSM, c.toArray(),
239: attachedElements, elementsWithoutIdentity);
240:
241: // Update the attached collection with the detached elements
242: SCOUtils.updateCollectionWithCollectionElements(this ,
243: attachedElements);
244: }
245:
246: // ------------------ Implementation of TreeSet methods --------------------
247:
248: /**
249: * Creates and returns a copy of this object.
250: * @return The cloned object
251: */
252: public Object clone() {
253: return delegate.clone();
254: }
255:
256: /**
257: * Accessor for the comparator.
258: * @return The comparator
259: */
260: public Comparator comparator() {
261: return delegate.comparator();
262: }
263:
264: /**
265: * Accessor for whether an element is contained in this Set.
266: * @param element The element
267: * @return Whether it is contained.
268: **/
269: public boolean contains(Object element) {
270: return delegate.contains(element);
271: }
272:
273: /**
274: * Accessor for whether a collection is contained in this Set.
275: * @param c The collection
276: * @return Whether it is contained.
277: **/
278: public synchronized boolean containsAll(java.util.Collection c) {
279: return delegate.containsAll(c);
280: }
281:
282: /**
283: * Equality operator.
284: * @param o The object to compare against.
285: * @return Whether this object is the same.
286: **/
287: public synchronized boolean equals(Object o) {
288: return delegate.equals(o);
289: }
290:
291: /**
292: * Accessor for the first element in the sorted set.
293: * @return The first element
294: **/
295: public Object first() {
296: return delegate.first();
297: }
298:
299: /**
300: * Hashcode operator.
301: * @return The Hash code.
302: **/
303: public synchronized int hashCode() {
304: return delegate.hashCode();
305: }
306:
307: /**
308: * Accessor for whether the TreeSet is empty.
309: * @return Whether it is empty.
310: **/
311: public boolean isEmpty() {
312: return delegate.isEmpty();
313: }
314:
315: /**
316: * Accessor for an iterator for the Set.
317: * @return The iterator
318: **/
319: public Iterator iterator() {
320: return new SCOCollectionIterator(this , ownerSM, delegate, null,
321: true);
322: }
323:
324: /**
325: * Method to retrieve the head elements up to the specified element.
326: * @param toElement the element to return up to.
327: * @return The set of elements meeting the input
328: */
329: public SortedSet headSet(Object toElement) {
330: return delegate.headSet(toElement);
331: }
332:
333: /**
334: * Method to retrieve the subset of elements between the specified elements.
335: * @param fromElement The start element
336: * @param toElement The end element
337: * @return The set of elements meeting the input
338: */
339: public SortedSet subSet(Object fromElement, 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 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 TreeSet.
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 TreeSet.
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 TreeSet.
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 TreeSet
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 TreeSet.
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: *
483: * @return the replaced object
484: * @throws ObjectStreamException
485: */
486: protected Object writeReplace() throws ObjectStreamException {
487: return new java.util.TreeSet(delegate);
488: }
489: }
|