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: import java.util.ListIterator;
023:
024: import org.jpox.StateManager;
025: import org.jpox.metadata.AbstractMemberMetaData;
026: import org.jpox.sco.SCOList;
027: import org.jpox.sco.SCOListIterator;
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 ArrayList 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 ArrayList extends java.util.ArrayList implements SCOList,
042: 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.ArrayList 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 ArrayList(StateManager ownerSM, String fieldName) {
061: this .fieldName = fieldName;
062: this .ownerSM = ownerSM;
063: this .allowNulls = false;
064:
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 SCO in 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 the elements 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: // TODO Cater for delegate not being used
120: return delegate;
121: }
122:
123: /**
124: * Method to effect the load of the data in the SCO.
125: * Used when the SCO supports lazy-loading to tell it to load all now.
126: */
127: public void load() {
128: // Always loaded
129: }
130:
131: /**
132: * Method to flush the changes to the datastore when operating in queued mode.
133: * Does nothing in simple form here.
134: */
135: public void flush() {
136: // Never queued
137: }
138:
139: /**
140: * Method to update an embedded element in this list.
141: * @param element The element
142: * @param fieldNumber Number of field in the element
143: * @param value New value for this field
144: */
145: public void updateEmbeddedElement(Object element, int fieldNumber,
146: Object value) {
147: // Embedded not supported here so do nothing
148: }
149:
150: /**
151: * Accessor for the field name.
152: * @return The field name
153: */
154: public String getFieldName() {
155: return fieldName;
156: }
157:
158: /**
159: * Accessor for the owner object.
160: * @return The owner object
161: */
162: public Object getOwner() {
163: return owner;
164: }
165:
166: /**
167: * Method to unset the owner and field information.
168: */
169: public synchronized void unsetOwner() {
170: if (ownerSM != null) {
171: owner = null;
172: ownerSM = null;
173: fieldName = null;
174: }
175: }
176:
177: /**
178: * Utility to mark the object as dirty
179: */
180: public void makeDirty() {
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: 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.ArrayList();
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 list
213: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
214: .getMetaDataForMember(fieldName);
215: boolean elementsWithoutIdentity = SCOUtils
216: .collectionHasElementsWithoutIdentity(fmd);
217:
218: java.util.List attachedElements = new java.util.ArrayList(c
219: .size());
220: SCOUtils.attachCopyForCollection(ownerSM, c.toArray(),
221: attachedElements, elementsWithoutIdentity);
222:
223: // Update the attached list with the detached elements
224: SCOUtils.updateListWithListElements(this , attachedElements);
225: }
226:
227: // ----------------- Implementation of ArrayList methods -------------------
228:
229: /**
230: * Clone operator to return a copy of this object.
231: * <p>
232: * Mutable second-class Objects are required to provide a public clone
233: * method in order to allow for copying
234: * PersistenceCapable objects. In contrast to Object.clone(), this method
235: * must not throw a CloneNotSupportedException.
236: * </p>
237: * @return The cloned object
238: */
239: public Object clone() {
240: return delegate.clone();
241: }
242:
243: /**
244: * Method to return if the list contains this element.
245: * @param element The element
246: * @return Whether it is contained
247: **/
248: public boolean contains(Object element) {
249: return delegate.contains(element);
250: }
251:
252: /**
253: * Accessor for whether a collection of elements are contained here.
254: * @param c The collection of elements.
255: * @return Whether they are contained.
256: **/
257: public synchronized boolean containsAll(java.util.Collection c) {
258: return delegate.containsAll(c);
259: }
260:
261: /**
262: * Equality operator.
263: * @param o The object to compare against.
264: * @return Whether this object is the same.
265: **/
266: public synchronized boolean equals(Object o) {
267: return delegate.equals(o);
268: }
269:
270: /**
271: * Method to retrieve an element no.
272: * @param index The item to retrieve
273: * @return The element at that position.
274: **/
275: public Object get(int index) {
276: return delegate.get(index);
277: }
278:
279: /**
280: * Method to the position of an element.
281: * @param element The element.
282: * @return The position.
283: **/
284: public int indexOf(Object element) {
285: return delegate.indexOf(element);
286: }
287:
288: /**
289: * Accessor for whether the ArrayList is empty.
290: * @return Whether it is empty.
291: **/
292: public boolean isEmpty() {
293: return delegate.isEmpty();
294: }
295:
296: /**
297: * Method to retrieve an iterator for the list.
298: * @return The iterator
299: **/
300: public Iterator iterator() {
301: return new SCOListIterator(this , ownerSM, delegate, null, true,
302: -1);
303: }
304:
305: /**
306: * Method to retrieve a List iterator for the list.
307: * @return The iterator
308: **/
309: public ListIterator listIterator() {
310: return new SCOListIterator(this , ownerSM, delegate, null, true,
311: -1);
312: }
313:
314: /**
315: * Method to retrieve a List iterator for the list from the index.
316: * @param index The start point
317: * @return The iterator
318: **/
319: public ListIterator listIterator(int index) {
320: return new SCOListIterator(this , ownerSM, delegate, null, true,
321: index);
322: }
323:
324: /**
325: * Method to retrieve the last position of the element.
326: * @param element The element
327: * @return The last position of this element in the List.
328: **/
329: public int lastIndexOf(Object element) {
330: return delegate.lastIndexOf(element);
331: }
332:
333: /**
334: * Accessor for the size of the ArrayList.
335: * @return The size.
336: **/
337: public int size() {
338: return delegate.size();
339: }
340:
341: /**
342: * Accessor for the subList of elements between from and to of the List
343: * @param from Start index (inclusive)
344: * @param to End index (exclusive)
345: * @return The subList
346: **/
347: public synchronized java.util.List subList(int from, int to) {
348: return delegate.subList(from, to);
349: }
350:
351: /**
352: * Method to return the list as an array.
353: * @return The array
354: **/
355: public synchronized Object[] toArray() {
356: return delegate.toArray();
357: }
358:
359: /**
360: * Method to return the list as an array.
361: * @param a The runtime types of the array being defined by this param
362: * @return The array
363: **/
364: public synchronized Object[] toArray(Object a[]) {
365: return delegate.toArray(a);
366: }
367:
368: /**
369: * Method to add an element to a position in the ArrayList.
370: * @param index The position
371: * @param element The new element
372: */
373: public void add(int index, Object element) {
374: // Reject inappropriate elements
375: if (element == null && !allowNulls) {
376: throw new NullsNotAllowedException(ownerSM, fieldName);
377: }
378:
379: delegate.add(index, element);
380: makeDirty();
381: }
382:
383: /**
384: * Method to add an element to the ArrayList.
385: * @param element The new element
386: * @return Whether it was added ok.
387: */
388: public boolean add(Object element) {
389: // Reject inappropriate elements
390: if (element == null && !allowNulls) {
391: throw new NullsNotAllowedException(ownerSM, fieldName);
392: }
393:
394: boolean success = delegate.add(element);
395: if (success) {
396: makeDirty();
397: }
398: return success;
399: }
400:
401: /**
402: * Method to add a Collection to the ArrayList.
403: * @param elements The collection
404: * @return Whether it was added ok.
405: */
406: public boolean addAll(Collection elements) {
407: boolean success = delegate.addAll(elements);
408: if (success) {
409: makeDirty();
410: }
411: return success;
412: }
413:
414: /**
415: * Method to add a Collection to a position in the ArrayList.
416: * @param index Position to insert the collection.
417: * @param elements The collection
418: * @return Whether it was added ok.
419: */
420: public boolean addAll(int index, Collection elements) {
421: boolean success = delegate.addAll(index, elements);
422: if (success) {
423: makeDirty();
424: }
425: return success;
426: }
427:
428: /**
429: * Method to clear the ArrayList.
430: */
431: public synchronized void clear() {
432: delegate.clear();
433: makeDirty();
434: }
435:
436: /**
437: * Method to remove an element from the List
438: * @param element The Element to remove
439: * @return Whether it was removed successfully.
440: **/
441: public synchronized boolean remove(Object element) {
442: return remove(element, true);
443: }
444:
445: /**
446: * Method to remove an element from the List
447: * @param element The Element to remove
448: * @return Whether it was removed successfully.
449: **/
450: public synchronized boolean remove(Object element,
451: boolean allowCascadeDelete) {
452: boolean success = delegate.remove(element);
453: if (success) {
454: makeDirty();
455: }
456: return success;
457: }
458:
459: /**
460: * Method to remove an element from the ArrayList.
461: * @param index The element position.
462: * @return The object that was removed
463: */
464: public synchronized Object remove(int index) {
465: Object obj = delegate.remove(index);
466: makeDirty();
467: return obj;
468: }
469:
470: /**
471: * Method to remove a collection of elements from the List.
472: * @param elements Collection of elements to remove
473: * @return Whether it was successful.
474: */
475: public boolean removeAll(Collection elements) {
476: boolean success = delegate.removeAll(elements);
477: if (success) {
478: makeDirty();
479: }
480: return success;
481: }
482:
483: /**
484: * Method to retain a Collection of elements (and remove all others).
485: * @param c The collection to retain
486: * @return Whether they were retained successfully.
487: **/
488: public synchronized boolean retainAll(java.util.Collection c) {
489: boolean success = delegate.retainAll(c);
490: if (success) {
491: makeDirty();
492: }
493: return success;
494: }
495:
496: /**
497: * JPOX wrapper addition that allows turning off of the dependent-field checks
498: * when doing the position setting. This means that we can prevent the deletion of
499: * the object that was previously in that position. This particular feature is used
500: * when attaching a list field and where some elements have changed positions.
501: * @param index The position
502: * @param element The new element
503: * @return The element previously at that position
504: */
505: public Object set(int index, Object element,
506: boolean allowDependentField) {
507: // Reject inappropriate elements
508: if (element == null && !allowNulls) {
509: throw new NullsNotAllowedException(ownerSM, fieldName);
510: }
511:
512: Object obj = delegate.set(index, element);
513: makeDirty();
514: return obj;
515: }
516:
517: /**
518: * Method to set the element at a position in the ArrayList.
519: * @param index The position
520: * @param element The new element
521: * @return The element previously at that position
522: */
523: public Object set(int index, Object element) {
524: return set(index, element, true);
525: }
526:
527: /**
528: * The writeReplace method is called when ObjectOutputStream is preparing
529: * to write the object to the stream. The ObjectOutputStream checks whether
530: * the class defines the writeReplace method. If the method is defined, the
531: * writeReplace method is called to allow the object to designate its
532: * replacement in the stream. The object returned should be either of the
533: * same type as the object passed in or an object that when read and
534: * resolved will result in an object of a type that is compatible with all
535: * references to the object.
536: * @return the replaced object
537: * @throws ObjectStreamException
538: */
539: protected Object writeReplace() throws ObjectStreamException {
540: return new java.util.ArrayList(delegate);
541: }
542: }
|