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 LinkedList 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 LinkedList extends java.util.LinkedList implements
042: SCOList, 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.LinkedList 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 LinkedList(StateManager ownerSM, String fieldName) {
061: this .ownerSM = ownerSM;
062: this .fieldName = fieldName;
063: this .allowNulls = false;
064: if (ownerSM != null) {
065: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
066: .getMetaDataForMember(fieldName);
067: this .owner = ownerSM.getObject();
068: this .fieldNumber = fmd.getAbsoluteFieldNumber();
069: allowNulls = SCOUtils
070: .allowNullsInContainer(allowNulls, fmd);
071: }
072: }
073:
074: /**
075: * Method to initialise the SCO from an existing value.
076: * @param o The object to set from
077: * @param forInsert Whether the object needs inserting in the datastore with this value
078: * @param forUpdate Whether to update the datastore with this value
079: */
080: public void initialise(Object o, boolean forInsert,
081: boolean forUpdate) {
082: Collection c = (Collection) o;
083: if (c != null) {
084: delegate = new java.util.LinkedList(c); // Make copy of container rather than using same memory
085: } else {
086: delegate = new java.util.LinkedList();
087: }
088: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
089: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("023003",
090: StringUtils.toJVMIDString(ownerSM.getObject()),
091: fieldName, "" + size(), SCOUtils
092: .getSCOWrapperOptionsMessage(true, false,
093: allowNulls, false)));
094: }
095: }
096:
097: /**
098: * Method to initialise the SCO for use.
099: */
100: public void initialise() {
101: delegate = new java.util.LinkedList();
102: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
103: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("023003",
104: StringUtils.toJVMIDString(ownerSM.getObject()),
105: fieldName, "" + size(), SCOUtils
106: .getSCOWrapperOptionsMessage(true, false,
107: allowNulls, false)));
108: }
109: }
110:
111: // ----------------------- Implementation of SCO methods -------------------
112:
113: /**
114: * Accessor for the unwrapped value that we are wrapping.
115: * @return The unwrapped value
116: */
117: public Object getValue() {
118: return delegate;
119: }
120:
121: /**
122: * Method to effect the load of the data in the SCO.
123: * Used when the SCO supports lazy-loading to tell it to load all now.
124: */
125: public void load() {
126: // Always loaded
127: }
128:
129: /**
130: * Method to flush the changes to the datastore when operating in queued mode.
131: * Does nothing in "direct" mode.
132: */
133: public void flush() {
134: // Never queued
135: }
136:
137: /**
138: * Method to update an embedded element in this collection.
139: * @param element The element
140: * @param fieldNumber Number of field in the element
141: * @param value New value for this field
142: */
143: public void updateEmbeddedElement(Object element, int fieldNumber,
144: Object value) {
145: // Embedded not supported so do nothing
146: }
147:
148: /**
149: * Accessor for the field name.
150: * @return The field name
151: **/
152: public String getFieldName() {
153: return fieldName;
154: }
155:
156: /**
157: * Accessor for the owner object.
158: * @return The owner object
159: **/
160: public Object getOwner() {
161: return owner;
162: }
163:
164: /**
165: * Method to unset the owner and field information.
166: **/
167: public synchronized void unsetOwner() {
168: if (ownerSM != null) {
169: owner = null;
170: ownerSM = null;
171: fieldName = null;
172: }
173: }
174:
175: /**
176: * Utility to mark the object as dirty.
177: **/
178: public void makeDirty() {
179: // Although we are never really "dirty", the owning object must be
180: // marked dirty so that the proper state change occurs and its
181: // jdoPreStore() gets called properly.
182: if (ownerSM != null) {
183: ownerSM.makeDirty(fieldNumber);
184: }
185: }
186:
187: /**
188: * Method to return a detached copy of the container.
189: * Recurse sthrough the elements so that they are likewise detached.
190: * @param state State for detachment process
191: * @return The detached container
192: */
193: public Object detachCopy(FetchPlanState state) {
194: java.util.Collection detached = new java.util.LinkedList();
195: SCOUtils.detachCopyForCollection(ownerSM, toArray(), state,
196: detached);
197: return detached;
198: }
199:
200: /**
201: * Method to return an attached copy of the passed (detached) value. The returned attached copy
202: * is a SCO wrapper. Goes through the existing elements in the store for this owner field and
203: * removes ones no longer present, and adds new elements. All elements in the (detached)
204: * value are attached.
205: * @param value The new (collection) value
206: */
207: public void attachCopy(Object value) {
208: java.util.Collection c = (java.util.Collection) value;
209:
210: // Attach all of the elements in the new list
211: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
212: .getMetaDataForMember(fieldName);
213: boolean elementsWithoutIdentity = SCOUtils
214: .collectionHasElementsWithoutIdentity(fmd);
215:
216: java.util.List attachedElements = new java.util.ArrayList(c
217: .size());
218: SCOUtils.attachCopyForCollection(ownerSM, c.toArray(),
219: attachedElements, elementsWithoutIdentity);
220:
221: // Update the attached list with the detached elements
222: SCOUtils.updateListWithListElements(this , attachedElements);
223: }
224:
225: // ------------------- Implementation of LinkedList methods ----------------
226:
227: /**
228: * Clone operator to return a copy of this object.
229: * <p>
230: * Mutable second-class Objects are required to provide a public
231: * clone method in order to allow for copying PersistenceCapable
232: * objects. In contrast to Object.clone(), this method must not throw a
233: * CloneNotSupportedException.
234: * </p>
235: *
236: * @return The cloned object
237: */
238: public Object clone() {
239: return delegate.clone();
240: }
241:
242: /**
243: * Method to return if the list contains this element.
244: * @param element The element
245: * @return Whether it is contained
246: **/
247: public 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: * Method to retrieve the first element.
280: * @return The first element
281: **/
282: public Object getFirst() {
283: return delegate.getFirst();
284: }
285:
286: /**
287: * Method to retrieve the last element.
288: * @return The last element
289: **/
290: public Object getLast() {
291: return delegate.getLast();
292: }
293:
294: /**
295: * Hashcode operator.
296: * @return The Hash code.
297: **/
298: public synchronized int hashCode() {
299: return delegate.hashCode();
300: }
301:
302: /**
303: * Method to the position of an element.
304: * @param element The element.
305: * @return The position.
306: **/
307: public int indexOf(Object element) {
308: return delegate.indexOf(element);
309: }
310:
311: /**
312: * Accessor for whether the LinkedList is empty.
313: * @return Whether it is empty.
314: **/
315: public boolean isEmpty() {
316: return delegate.isEmpty();
317: }
318:
319: /**
320: * Method to retrieve an iterator for the list.
321: * @return The iterator
322: **/
323: public Iterator iterator() {
324: return new SCOListIterator(this , ownerSM, delegate, null, true,
325: -1);
326: }
327:
328: /**
329: * Method to retrieve a List iterator for the list from the index.
330: * @param index The start point
331: * @return The iterator
332: **/
333: public ListIterator listIterator(int index) {
334: return new SCOListIterator(this , ownerSM, delegate, null, true,
335: index);
336: }
337:
338: /**
339: * Method to retrieve the last position of the element.
340: * @param element The element
341: * @return The last position of this element in the List.
342: **/
343: public int lastIndexOf(Object element) {
344: return delegate.lastIndexOf(element);
345: }
346:
347: /**
348: * Accessor for the size of the LinkedList.
349: * @return The size.
350: **/
351: public int size() {
352: return delegate.size();
353: }
354:
355: /**
356: * Accessor for the subList of elements between from and to of the List
357: * @param from Start index (inclusive)
358: * @param to End index (exclusive)
359: * @return The subList
360: **/
361: public synchronized java.util.List subList(int from, int to) {
362: return delegate.subList(from, to);
363: }
364:
365: /**
366: * Method to return the list as an array.
367: * @return The array
368: **/
369: public synchronized Object[] toArray() {
370: return delegate.toArray();
371: }
372:
373: /**
374: * Method to return the list as an array.
375: * @param a The runtime types of the array being defined by this param
376: * @return The array
377: **/
378: public synchronized Object[] toArray(Object a[]) {
379: return delegate.toArray(a);
380: }
381:
382: /**
383: * Method to add an element to a position in the LinkedList.
384: * @param index The position
385: * @param element The new element
386: **/
387: public void add(int index, Object element) {
388: // Reject inappropriate elements
389: if (element == null && !allowNulls) {
390: throw new NullsNotAllowedException(ownerSM, fieldName);
391: }
392:
393: delegate.add(index, element);
394: makeDirty();
395: }
396:
397: /**
398: * Method to add an element to the LinkedList.
399: * @param element The new element
400: * @return Whether it was added ok.
401: **/
402: public boolean add(Object element) {
403: // Reject inappropriate elements
404: if (element == null && !allowNulls) {
405: throw new NullsNotAllowedException(ownerSM, fieldName);
406: }
407:
408: boolean success = delegate.add(element);
409: if (success) {
410: makeDirty();
411: }
412: return success;
413: }
414:
415: /**
416: * Method to add a Collection to the LinkedList.
417: * @param elements The collection
418: * @return Whether it was added ok.
419: **/
420: public boolean addAll(Collection elements) {
421: boolean success = delegate.addAll(elements);
422: if (success) {
423: makeDirty();
424: }
425: return success;
426: }
427:
428: /**
429: * Method to add a Collection to a position in the LinkedList.
430: * @param index Position to insert the collection.
431: * @param elements The collection
432: * @return Whether it was added ok.
433: **/
434: public boolean addAll(int index, Collection elements) {
435: boolean success = delegate.addAll(index, elements);
436: if (success) {
437: makeDirty();
438: }
439: return success;
440: }
441:
442: /**
443: * Method to add an element as first in the LinkedList.
444: * @param element The new element
445: **/
446: public void addFirst(Object element) {
447: delegate.addFirst(element);
448: makeDirty();
449: }
450:
451: /**
452: * Method to add an element as last in the LinkedList.
453: * @param element The new element
454: **/
455: public void addLast(Object element) {
456: delegate.addLast(element);
457: makeDirty();
458: }
459:
460: /**
461: * Method to clear the LinkedList.
462: **/
463: public synchronized void clear() {
464: delegate.clear();
465: makeDirty();
466: }
467:
468: /**
469: * Method to remove an element from the LinkedList.
470: * @param index The element position.
471: * @return The object that was removed
472: **/
473: public Object remove(int index) {
474: Object obj = delegate.remove(index);
475: makeDirty();
476: return obj;
477: }
478:
479: /**
480: * Method to remove an element from the List
481: * @param element The Element to remove
482: * @return Whether it was removed successfully.
483: **/
484: public synchronized boolean remove(Object element) {
485: return remove(element, true);
486: }
487:
488: /**
489: * Method to remove an element from the List
490: * @param element The Element to remove
491: * @return Whether it was removed successfully.
492: **/
493: public synchronized boolean remove(Object element,
494: boolean allowCascadeDelete) {
495: boolean success = delegate.remove(element);
496: if (success) {
497: makeDirty();
498: }
499: return success;
500: }
501:
502: /**
503: * Method to remove a Collection from the LinkedList.
504: * @param elements The collection
505: * @return Whether it was removed ok.
506: **/
507: public boolean removeAll(Collection elements) {
508: boolean success = delegate.removeAll(elements);
509: if (success) {
510: makeDirty();
511: }
512: return success;
513: }
514:
515: /**
516: * Method to remove the first element from the LinkedList.
517: * @return The object that was removed
518: **/
519: public Object removeFirst() {
520: Object obj = delegate.removeFirst();
521: makeDirty();
522: return obj;
523: }
524:
525: /**
526: * Method to remove the last element from the LinkedList.
527: * @return The object that was removed
528: **/
529: public Object removeLast() {
530: Object obj = delegate.removeLast();
531: makeDirty();
532: return obj;
533: }
534:
535: /**
536: * Method to retain a Collection of elements (and remove all others).
537: * @param c The collection to retain
538: * @return Whether they were retained successfully.
539: **/
540: public synchronized boolean retainAll(java.util.Collection c) {
541: boolean success = delegate.retainAll(c);
542: if (success) {
543: makeDirty();
544: }
545: return success;
546: }
547:
548: /**
549: * JPOX wrapper addition that allows turning off of the dependent-field checks
550: * when doing the position setting. This means that we can prevent the deletion of
551: * the object that was previously in that position. This particular feature is used
552: * when attaching a list field and where some elements have changed positions.
553: * @param index The position
554: * @param element The new element
555: * @return The element previously at that position
556: */
557: public Object set(int index, Object element,
558: boolean allowDependentField) {
559: // Reject inappropriate elements
560: if (element == null && !allowNulls) {
561: throw new NullsNotAllowedException(ownerSM, fieldName);
562: }
563:
564: Object obj = delegate.set(index, element);
565: makeDirty();
566: return obj;
567: }
568:
569: /**
570: * Method to set the element at a position in the LinkedList.
571: * @param index The position
572: * @param element The new element
573: * @return The element previously at that position
574: **/
575: public Object set(int index, Object element) {
576: return set(index, element, true);
577: }
578:
579: /**
580: * The writeReplace method is called when ObjectOutputStream is preparing
581: * to write the object to the stream. The ObjectOutputStream checks whether
582: * the class defines the writeReplace method. If the method is defined, the
583: * writeReplace method is called to allow the object to designate its
584: * replacement in the stream. The object returned should be either of the
585: * same type as the object passed in or an object that when read and
586: * resolved will result in an object of a type that is compatible with
587: * all references to the object.
588: * @return the replaced object
589: * @throws ObjectStreamException
590: */
591: protected Object writeReplace() throws ObjectStreamException {
592: return new java.util.LinkedList(delegate);
593: }
594: }
|