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.AbstractQueue;
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 Queue 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 Queue extends AbstractQueue implements SCOCollection,
043: SCOMtoN, Cloneable, java.io.Serializable {
044: protected static final Localiser LOCALISER = Localiser.getInstance(
045: "org.jpox.Localisation", PriorityQueue.class
046: .getClassLoader());
047:
048: protected Object owner;
049: protected StateManager ownerSM;
050: protected String fieldName;
051: protected int fieldNumber;
052: protected boolean allowNulls;
053:
054: /** The internal "delegate". */
055: protected java.util.Queue delegate;
056:
057: /**
058: * Constructor.
059: * @param ownerSM The State Manager for this set.
060: * @param fieldName Name of the field
061: **/
062: public Queue(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: java.util.Collection c = (java.util.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.PriorityQueue(5, comparator);
124: } else {
125: this .delegate = new java.util.PriorityQueue();
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: * Recurses through 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.PriorityQueue();
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 Queue methods -------------------
247:
248: /**
249: * Creates and returns a copy of this object.
250: * <P>
251: * Mutable second-class Objects are required to provide a public
252: * clone method in order to allow for copying PersistenceCapable
253: * objects. In contrast to Object.clone(), this method must not throw a
254: * CloneNotSupportedException.
255: * @return A clone of the object
256: */
257: public Object clone() {
258: // return ((java.util.PriorityQueue)delegate).clone();
259: // TODO Implement cloning
260: return null;
261: }
262:
263: /**
264: * Accessor for whether an element is contained in the Collection.
265: * @param element The element
266: * @return Whether the element is contained here
267: **/
268: public synchronized boolean contains(Object element) {
269: return delegate.contains(element);
270: }
271:
272: /**
273: * Accessor for whether a collection of elements are contained here.
274: * @param c The collection of elements.
275: * @return Whether they are 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: * Hashcode operator.
292: * @return The Hash code.
293: **/
294: public synchronized int hashCode() {
295: return delegate.hashCode();
296: }
297:
298: /**
299: * Accessor for whether the Collection is empty.
300: * @return Whether it is empty.
301: **/
302: public synchronized boolean isEmpty() {
303: return delegate.isEmpty();
304: }
305:
306: /**
307: * Accessor for an iterator for the Collection.
308: * @return The iterator
309: **/
310: public synchronized Iterator iterator() {
311: return new SCOCollectionIterator(this , ownerSM, delegate, null,
312: true);
313: }
314:
315: /**
316: * Method to peek at the next element in the Queue.
317: * @return The element
318: **/
319: public synchronized Object peek() {
320: return delegate.peek();
321: }
322:
323: /**
324: * Accessor for the size of the Collection.
325: * @return The size
326: **/
327: public synchronized int size() {
328: return delegate.size();
329: }
330:
331: /**
332: * Method to return the Collection as an array.
333: * @return The array
334: **/
335: public synchronized Object[] toArray() {
336: return delegate.toArray();
337: }
338:
339: /**
340: * Method to return the Collection as an array.
341: * @param a The array to write the results to
342: * @return The array
343: **/
344: public synchronized Object[] toArray(Object a[]) {
345: return delegate.toArray(a);
346: }
347:
348: /**
349: * Method to add an element to the Collection.
350: * @param element The element to add
351: * @return Whether it was added successfully.
352: **/
353: public synchronized boolean add(Object element) {
354: // Reject inappropriate elements
355: if (element == null && !allowNulls) {
356: throw new NullsNotAllowedException(ownerSM, fieldName);
357: }
358:
359: boolean success = delegate.add(element);
360: if (success) {
361: makeDirty();
362: }
363: return success;
364: }
365:
366: /**
367: * Method to add a collection of elements.
368: * @param elements The collection of elements to add.
369: * @return Whether they were added successfully.
370: **/
371: public synchronized boolean addAll(java.util.Collection elements) {
372: boolean success = delegate.addAll(elements);
373: if (success) {
374: makeDirty();
375: }
376: return success;
377: }
378:
379: /**
380: * Method to clear the Collection.
381: **/
382: public synchronized void clear() {
383: delegate.clear();
384: makeDirty();
385: }
386:
387: /**
388: * Method to offer an element to the Queue.
389: * @param element The element to offer
390: * @return Whether it was added successfully.
391: **/
392: public synchronized boolean offer(Object element) {
393: boolean success = delegate.offer(element);
394: if (success) {
395: makeDirty();
396: }
397: return success;
398: }
399:
400: /**
401: * Method to poll the next element in the Queue.
402: * @return The element (now removed)
403: **/
404: public synchronized Object poll() {
405: Object obj = delegate.poll();
406: makeDirty();
407: return obj;
408: }
409:
410: /**
411: * Method to remove an element from the List
412: * @param element The Element to remove
413: * @return Whether it was removed successfully.
414: **/
415: public synchronized boolean remove(Object element) {
416: return remove(element, true);
417: }
418:
419: /**
420: * Method to remove an element from the List
421: * @param element The Element to remove
422: * @return Whether it was removed successfully.
423: **/
424: public synchronized boolean remove(Object element,
425: boolean allowCascadeDelete) {
426: boolean success = delegate.remove(element);
427: if (success) {
428: makeDirty();
429: }
430: return success;
431: }
432:
433: /**
434: * Method to remove a Collection of elements.
435: * @param elements The collection to remove
436: * @return Whether they were removed successfully.
437: **/
438: public synchronized boolean removeAll(java.util.Collection elements) {
439: boolean success = delegate.removeAll(elements);
440: if (success) {
441: makeDirty();
442: }
443: return success;
444: }
445:
446: /**
447: * Method to retain a Collection of elements (and remove all others).
448: * @param c The collection to retain
449: * @return Whether they were retained successfully.
450: **/
451: public synchronized boolean retainAll(java.util.Collection c) {
452: boolean success = delegate.retainAll(c);
453: if (success) {
454: makeDirty();
455: }
456: return success;
457: }
458:
459: /**
460: * The writeReplace method is called when ObjectOutputStream is preparing
461: * to write the object to the stream. The ObjectOutputStream checks whether
462: * the class defines the writeReplace method. If the method is defined, the
463: * writeReplace method is called to allow the object to designate its
464: * replacement in the stream. The object returned should be either of the
465: * same type as the object passed in or an object that when read and
466: * resolved will result in an object of a type that is compatible with all
467: * references to the object.
468: * @return the replaced object
469: * @throws ObjectStreamException
470: */
471: protected Object writeReplace() throws ObjectStreamException {
472: return new java.util.PriorityQueue(delegate);
473: }
474: }
|