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.AbstractList;
021: import java.util.Collection;
022: import java.util.Iterator;
023: import java.util.ListIterator;
024:
025: import org.jpox.StateManager;
026: import org.jpox.metadata.AbstractMemberMetaData;
027: import org.jpox.sco.SCOList;
028: import org.jpox.sco.SCOListIterator;
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 List 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 List extends AbstractList implements SCOList, Cloneable,
043: java.io.Serializable {
044: protected static final Localiser LOCALISER = Localiser
045: .getInstance("org.jpox.Localisation");
046:
047: protected StateManager ownerSM;
048: protected Object owner;
049: protected String fieldName;
050: protected int fieldNumber;
051: protected boolean allowNulls;
052:
053: /** The internal "delegate". */
054: protected java.util.ArrayList 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 List(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: delegate = new java.util.ArrayList(c); // Make copy of container rather than using same memory
086: } else {
087: delegate = new java.util.ArrayList();
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.ArrayList();
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 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: /*
181: * Although we are never really "dirty", the owning object must be
182: * marked dirty so that the proper state change occurs and its
183: * jdoPreStore() gets called properly.
184: */
185: if (ownerSM != null) {
186: ownerSM.makeDirty(fieldNumber);
187: }
188: }
189:
190: /**
191: * Method to return a detached copy of the container.
192: * Recurse sthrough the elements so that they are likewise detached.
193: * @param state State for detachment process
194: * @return The detached container
195: */
196: public Object detachCopy(FetchPlanState state) {
197: java.util.Collection detached = new java.util.ArrayList();
198: SCOUtils.detachCopyForCollection(ownerSM, toArray(), state,
199: detached);
200: return detached;
201: }
202:
203: /**
204: * Method to return an attached copy of the passed (detached) value. The returned attached copy
205: * is a SCO wrapper. Goes through the existing elements in the store for this owner field and
206: * removes ones no longer present, and adds new elements. All elements in the (detached)
207: * value are attached.
208: * @param value The new (collection) value
209: */
210: public void attachCopy(Object value) {
211: java.util.Collection c = (java.util.Collection) value;
212:
213: // Attach all of the elements in the new list
214: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
215: .getMetaDataForMember(fieldName);
216: boolean elementsWithoutIdentity = SCOUtils
217: .collectionHasElementsWithoutIdentity(fmd);
218:
219: java.util.List attachedElements = new java.util.ArrayList(c
220: .size());
221: SCOUtils.attachCopyForCollection(ownerSM, c.toArray(),
222: attachedElements, elementsWithoutIdentity);
223:
224: // Update the attached list with the detached elements
225: SCOUtils.updateListWithListElements(this , attachedElements);
226: }
227:
228: // ------------------- Implementation of List methods ----------------------
229:
230: /**
231: * Creates and returns a copy of this object.
232: * <P>Mutable second-class Objects are required to provide a public
233: * clone method in order to allow for copying PersistenceCapable
234: * objects. In contrast to Object.clone(), this method must not throw a
235: * CloneNotSupportedException.
236: * @return The cloned object
237: */
238: public Object clone() {
239: return delegate.clone();
240: }
241:
242: /**
243: * Accessor for whether an element is contained in the List.
244: * @param element The element
245: * @return Whether the element is contained here
246: **/
247: public synchronized boolean contains(Object element) {
248: return delegate.contains(element);
249: }
250:
251: /**
252: * Accessor for whether a collection of elements are contained here.
253: * @param c The collection of elements.
254: * @return Whether they are contained.
255: **/
256: public synchronized boolean containsAll(java.util.Collection c) {
257: return delegate.containsAll(c);
258: }
259:
260: /**
261: * Equality operator.
262: * @param o The object to compare against.
263: * @return Whether this object is the same.
264: **/
265: public synchronized boolean equals(Object o) {
266: return delegate.equals(o);
267: }
268:
269: /**
270: * Method to retrieve an element no.
271: * @param index The item to retrieve
272: * @return The element at that position.
273: **/
274: public Object get(int index) {
275: return delegate.get(index);
276: }
277:
278: /**
279: * Hashcode operator.
280: * @return The Hash code.
281: **/
282: public synchronized int hashCode() {
283: return delegate.hashCode();
284: }
285:
286: /**
287: * Method to the position of an element.
288: * @param element The element.
289: * @return The position.
290: **/
291: public int indexOf(Object element) {
292: return delegate.indexOf(element);
293: }
294:
295: /**
296: * Accessor for whether the List is empty.
297: * @return Whether it is empty.
298: **/
299: public synchronized boolean isEmpty() {
300: return delegate.isEmpty();
301: }
302:
303: /**
304: * Method to retrieve the last position of the element.
305: * @param element The element
306: * @return The last position of this element in the List.
307: **/
308: public int lastIndexOf(Object element) {
309: return delegate.lastIndexOf(element);
310: }
311:
312: /**
313: * Accessor for an iterator for the List
314: * @return The iterator
315: **/
316: public synchronized Iterator iterator() {
317: return new SCOListIterator(this , ownerSM, delegate, null, true,
318: -1);
319: }
320:
321: /**
322: * Method to retrieve a List iterator for the list.
323: * @return The iterator
324: **/
325: public ListIterator listIterator() {
326: return new SCOListIterator(this , ownerSM, delegate, null, true,
327: -1);
328: }
329:
330: /**
331: * Method to retrieve a List iterator for the list from the index.
332: * @param index The start point
333: * @return The iterator
334: **/
335: public ListIterator listIterator(int index) {
336: return new SCOListIterator(this , ownerSM, delegate, null, true,
337: index);
338: }
339:
340: /**
341: * Accessor for the size of the List
342: * @return The size
343: **/
344: public synchronized int size() {
345: return delegate.size();
346: }
347:
348: /**
349: * Accessor for the subList of elements between from and to of the List
350: * @param from Start index (inclusive)
351: * @param to End index (exclusive)
352: * @return The subList
353: **/
354: public synchronized java.util.List subList(int from, int to) {
355: return delegate.subList(from, to);
356: }
357:
358: /**
359: * Method to return the List as an array.
360: * @return The array
361: **/
362: public synchronized Object[] toArray() {
363: return delegate.toArray();
364: }
365:
366: /**
367: * Method to return the List as an array.
368: * @param a The array to copy to
369: * @return The array
370: **/
371: public synchronized Object[] toArray(Object a[]) {
372: return delegate.toArray(a);
373: }
374:
375: /**
376: * Method to add an element to the List
377: * @param element The element to add
378: * @return Whether it was added successfully.
379: **/
380: public synchronized boolean add(Object element) {
381: // Reject inappropriate elements
382: if (element == null && !allowNulls) {
383: throw new NullsNotAllowedException(ownerSM, fieldName);
384: }
385:
386: boolean success = delegate.add(element);
387: if (success) {
388: makeDirty();
389: }
390: return success;
391: }
392:
393: /**
394: * Method to add an element to the List at a position.
395: * @param element The element to add
396: * @param index The position
397: **/
398: public void add(int index, Object element) {
399: // Reject inappropriate elements
400: if (element == null && !allowNulls) {
401: throw new NullsNotAllowedException(ownerSM, fieldName);
402: }
403:
404: delegate.add(index, element);
405: makeDirty();
406: }
407:
408: /**
409: * Method to add a Collection to the ArrayList.
410: * @param elements The collection
411: * @return Whether it was added ok.
412: */
413: public boolean addAll(Collection elements) {
414: boolean success = delegate.addAll(elements);
415: if (success) {
416: makeDirty();
417: }
418: return success;
419: }
420:
421: /**
422: * Method to add a collection of elements.
423: * @param elements The collection of elements to add.
424: * @param index The position to add them
425: * @return Whether they were added successfully.
426: **/
427: public boolean addAll(int index, Collection elements) {
428: boolean success = delegate.addAll(index, elements);
429: if (success) {
430: makeDirty();
431: }
432: return success;
433: }
434:
435: /**
436: * Method to clear the List
437: **/
438: public synchronized void clear() {
439: delegate.clear();
440: makeDirty();
441: }
442:
443: /**
444: * Method to remove an element from the List
445: * @param element The Element to remove
446: * @return Whether it was removed successfully.
447: **/
448: public synchronized boolean remove(Object element) {
449: return remove(element, true);
450: }
451:
452: /**
453: * Method to remove an element from the List
454: * @param element The Element to remove
455: * @return Whether it was removed successfully.
456: **/
457: public synchronized boolean remove(Object element,
458: boolean allowCascadeDelete) {
459: boolean success = delegate.remove(element);
460: if (success) {
461: makeDirty();
462: }
463: return success;
464: }
465:
466: /**
467: * Method to remove an element from the ArrayList.
468: * @param index The element position.
469: * @return The object that was removed
470: */
471: public Object remove(int index) {
472: Object obj = delegate.remove(index);
473: makeDirty();
474: return obj;
475: }
476:
477: /**
478: * Method to remove a collection of elements from the List.
479: * @param elements Collection of elements to remove
480: * @return Whether it was successful.
481: */
482: public boolean removeAll(Collection elements) {
483: boolean success = delegate.removeAll(elements);
484: if (success) {
485: makeDirty();
486: }
487: return success;
488: }
489:
490: /**
491: * Method to retain a Collection of elements (and remove all others).
492: * @param c The collection to retain
493: * @return Whether they were retained successfully.
494: **/
495: public synchronized boolean retainAll(java.util.Collection c) {
496: boolean success = delegate.retainAll(c);
497: if (success) {
498: makeDirty();
499: }
500: return success;
501: }
502:
503: /**
504: * JPOX wrapper addition that allows turning off of the dependent-field checks
505: * when doing the position setting. This means that we can prevent the deletion of
506: * the object that was previously in that position. This particular feature is used
507: * when attaching a list field and where some elements have changed positions.
508: * @param index The position
509: * @param element The new element
510: * @return The element previously at that position
511: */
512: public Object set(int index, Object element,
513: boolean allowDependentField) {
514: // Reject inappropriate elements
515: if (element == null && !allowNulls) {
516: throw new NullsNotAllowedException(ownerSM, fieldName);
517: }
518:
519: Object obj = delegate.set(index, element);
520: makeDirty();
521: return obj;
522: }
523:
524: /**
525: * Method to set the element at a position in the ArrayList.
526: * @param index The position
527: * @param element The new element
528: * @return The element previously at that position
529: */
530: public Object set(int index, Object element) {
531: return set(index, element, true);
532: }
533:
534: /**
535: * The writeReplace method is called when ObjectOutputStream is preparing
536: * to write the object to the stream. The ObjectOutputStream checks whether
537: * the class defines the writeReplace method. If the method is defined, the
538: * writeReplace method is called to allow the object to designate its
539: * replacement in the stream. The object returned should be either of the
540: * same type as the object passed in or an object that when read and
541: * resolved will result in an object of a type that is compatible with all
542: * references to the object.
543: * @return the replaced object
544: * @throws ObjectStreamException
545: */
546: protected Object writeReplace() throws ObjectStreamException {
547: return new java.util.ArrayList(delegate);
548: }
549: }
|