0001: /**********************************************************************
0002: Copyright (c) 2003 Andy Jefferson and others. All rights reserved.
0003: Licensed under the Apache License, Version 2.0 (the "License");
0004: you may not use this file except in compliance with the License.
0005: You may obtain a copy of the License at
0006:
0007: http://www.apache.org/licenses/LICENSE-2.0
0008:
0009: Unless required by applicable law or agreed to in writing, software
0010: distributed under the License is distributed on an "AS IS" BASIS,
0011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0012: See the License for the specific language governing permissions and
0013: limitations under the License.
0014:
0015:
0016: Contributors:
0017: ...
0018: **********************************************************************/package org.jpox.sco;
0019:
0020: import java.io.ObjectStreamException;
0021: import java.util.Collection;
0022: import java.util.Enumeration;
0023: import java.util.Iterator;
0024: import java.util.ListIterator;
0025:
0026: import org.jpox.ClassLoaderResolver;
0027: import org.jpox.ObjectManager;
0028: import org.jpox.StateManager;
0029: import org.jpox.exceptions.JPOXDataStoreException;
0030: import org.jpox.metadata.AbstractMemberMetaData;
0031: import org.jpox.metadata.FieldPersistenceModifier;
0032: import org.jpox.sco.exceptions.IncompatibleFieldTypeException;
0033: import org.jpox.sco.exceptions.NullsNotAllowedException;
0034: import org.jpox.sco.exceptions.QueryUnownedSCOException;
0035: import org.jpox.sco.queued.AddAtOperation;
0036: import org.jpox.sco.queued.AddOperation;
0037: import org.jpox.sco.queued.ClearOperation;
0038: import org.jpox.sco.queued.QueuedOperation;
0039: import org.jpox.sco.queued.RemoveAtOperation;
0040: import org.jpox.sco.queued.RemoveOperation;
0041: import org.jpox.sco.queued.SetOperation;
0042: import org.jpox.state.FetchPlanState;
0043: import org.jpox.state.StateManagerFactory;
0044: import org.jpox.store.DatastoreClass;
0045: import org.jpox.store.DatastoreIdentifier;
0046: import org.jpox.store.expression.QueryExpression;
0047: import org.jpox.store.mapping.CollectionMapping;
0048: import org.jpox.store.mapping.JavaTypeMapping;
0049: import org.jpox.store.query.Queryable;
0050: import org.jpox.store.query.ResultObjectFactory;
0051: import org.jpox.store.scostore.ListStore;
0052: import org.jpox.util.JPOXLogger;
0053: import org.jpox.util.Localiser;
0054: import org.jpox.util.StringUtils;
0055:
0056: /**
0057: * A mutable second-class Vector object.
0058: * This class extends Vector, using that class to contain the current objects, and the backing ListStore
0059: * to be the interface to the datastore. A "backing store" is not present for datastores that dont use
0060: * DatastoreClass, or if the container is serialised or non-persistent.
0061: *
0062: * <H3>Modes of Operation</H3>
0063: * The user can operate the list in 2 modes.
0064: * The <B>cached</B> mode will use an internal cache of the elements (in the "delegate") reading them at
0065: * the first opportunity and then using the cache thereafter.
0066: * The <B>non-cached</B> mode will just go direct to the "backing store" each call.
0067: *
0068: * <H3>Mutators</H3>
0069: * When the "backing store" is present any updates are passed direct to the datastore as well as to the "delegate".
0070: * If the "backing store" isn't present the changes are made to the "delegate" only.
0071: *
0072: * <H3>Accessors</H3>
0073: * When any accessor method is invoked, it typically checks whether the container has been loaded from its
0074: * "backing store" (where present) and does this as necessary. Some methods (<B>size()</B>) just check if
0075: * everything is loaded and use the delegate if possible, otherwise going direct to the datastore.
0076: *
0077: * @version $Revision: 1.99 $
0078: */
0079: public class Vector extends java.util.Vector implements SCOList,
0080: Cloneable, Queryable {
0081: protected static final Localiser LOCALISER = Localiser
0082: .getInstance("org.jpox.Localisation");
0083:
0084: private transient Object owner;
0085: private transient StateManager ownerSM;
0086: private transient String fieldName;
0087: private transient ListStore backingStore;
0088: private transient Class elementType;
0089: private transient boolean allowNulls;
0090:
0091: /** The internal "delegate". */
0092: private java.util.Vector delegate;
0093:
0094: /** Whether to use "delegate" caching. */
0095: protected boolean useCache = true;
0096:
0097: /** Status flag whether the collection is loaded into the cache. */
0098: protected boolean isCacheLoaded = false;
0099:
0100: /** Whether the SCO is in "direct" or "queued" mode. */
0101: boolean queued = false;
0102:
0103: /** Queued operations when using "queued" mode. */
0104: private java.util.ArrayList queuedOperations = null;
0105:
0106: /**
0107: * Constructor, using the StateManager of the "owner" and the field name.
0108: * @param ownerSM The owner StateManager
0109: * @param fieldName The name of the field of the SCO.
0110: **/
0111: public Vector(StateManager ownerSM, String fieldName) {
0112: this .ownerSM = ownerSM;
0113: this .fieldName = fieldName;
0114: this .allowNulls = false;
0115:
0116: // Set up our delegate
0117: this .delegate = new java.util.Vector();
0118:
0119: if (ownerSM != null) {
0120: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
0121: .getMetaDataForMember(fieldName);
0122: owner = ownerSM.getObject();
0123: allowNulls = SCOUtils
0124: .allowNullsInContainer(allowNulls, fmd);
0125: if (ownerSM.getStoreManager().usesDatastoreClass()) {
0126: queued = SCOUtils.useContainerQueueing(ownerSM);
0127: useCache = SCOUtils.useContainerCache(ownerSM,
0128: fieldName);
0129: }
0130:
0131: if (ownerSM.getStoreManager().usesDatastoreClass()
0132: && !SCOUtils.collectionHasSerialisedElements(fmd)
0133: && fmd.getPersistenceModifier() == FieldPersistenceModifier.PERSISTENT) {
0134: ClassLoaderResolver clr = ownerSM.getObjectManager()
0135: .getClassLoaderResolver();
0136: DatastoreClass ownerTable = ownerSM.getStoreManager()
0137: .getDatastoreClass(owner.getClass().getName(),
0138: clr);
0139: JavaTypeMapping m = ownerTable.getFieldMapping(fmd);
0140: if (!(m instanceof CollectionMapping)) {
0141: throw new IncompatibleFieldTypeException(ownerSM,
0142: fieldName,
0143: java.util.Vector.class.getName(), fmd
0144: .getTypeName());
0145: }
0146:
0147: this .backingStore = (ListStore) ownerSM
0148: .getStoreManager().getStore(clr, fmd,
0149: java.util.Vector.class);
0150: this .elementType = clr.classForName(backingStore
0151: .getElementType());
0152: }
0153:
0154: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
0155: JPOXLogger.PERSISTENCE.debug(SCOUtils
0156: .getContainerInfoMessage(ownerSM, fieldName,
0157: this , useCache, queued, allowNulls,
0158: SCOUtils.useCachedLazyLoading(ownerSM,
0159: fieldName)));
0160: }
0161: }
0162: }
0163:
0164: /**
0165: * Method to initialise the SCO from an existing value.
0166: * @param o The object to set from
0167: * @param forInsert Whether the object needs inserting in the datastore with this value
0168: * @param forUpdate Whether to update the datastore with this value
0169: */
0170: public void initialise(Object o, boolean forInsert,
0171: boolean forUpdate) {
0172: Collection c = (Collection) o;
0173: if (c != null) {
0174: // Check for the case of serialised PC elements, and assign StateManagers to the elements without
0175: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
0176: .getMetaDataForMember(fieldName);
0177: if (SCOUtils.collectionHasSerialisedElements(fmd)
0178: && fmd.getCollection().getElementClassMetaData() != null) {
0179: ObjectManager om = ownerSM.getObjectManager();
0180: Iterator iter = c.iterator();
0181: while (iter.hasNext()) {
0182: Object pc = iter.next();
0183: StateManager objSM = om.findStateManager(pc);
0184: if (objSM == null) {
0185: objSM = StateManagerFactory
0186: .newStateManagerForEmbedded(om, pc,
0187: false);
0188: objSM.addEmbeddedOwner(ownerSM, ownerSM
0189: .getClassMetaData()
0190: .getMetaDataForMember(fieldName)
0191: .getAbsoluteFieldNumber());
0192: }
0193: }
0194: }
0195:
0196: if (backingStore != null && useCache && !isCacheLoaded) {
0197: // Mark as loaded
0198: isCacheLoaded = true;
0199: }
0200:
0201: if (forInsert) {
0202: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
0203: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
0204: "023007", StringUtils.toJVMIDString(ownerSM
0205: .getObject()), fieldName, ""
0206: + c.size()));
0207: }
0208: addAll(c);
0209: } else if (forUpdate) {
0210: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
0211: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
0212: "023008", StringUtils.toJVMIDString(ownerSM
0213: .getObject()), fieldName, ""
0214: + c.size()));
0215: }
0216: clear();
0217: addAll(c);
0218: } else {
0219: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
0220: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
0221: "023007", StringUtils.toJVMIDString(ownerSM
0222: .getObject()), fieldName, ""
0223: + c.size()));
0224: }
0225: delegate.clear();
0226: delegate.addAll(c);
0227: }
0228: }
0229: }
0230:
0231: /**
0232: * Method to initialise the SCO for use.
0233: */
0234: public void initialise() {
0235: if (useCache
0236: && !SCOUtils.useCachedLazyLoading(ownerSM, fieldName)) {
0237: // Load up the container now if not using lazy loading
0238: loadFromStore();
0239: }
0240: }
0241:
0242: // ----------------------- Implementation of SCO methods -------------------
0243:
0244: /**
0245: * Accessor for the unwrapped value that we are wrapping.
0246: * @return The unwrapped value
0247: */
0248: public Object getValue() {
0249: // TODO Cater for delegate not being used
0250: return delegate;
0251: }
0252:
0253: /**
0254: * Accessor for the element type.
0255: * @return the element type contained in the collection
0256: */
0257: public Class getElementType() {
0258: return elementType;
0259: }
0260:
0261: /**
0262: * Method to effect the load of the data in the SCO.
0263: * Used when the SCO supports lazy-loading to tell it to load all now.
0264: */
0265: public void load() {
0266: if (useCache) {
0267: loadFromStore();
0268: }
0269: }
0270:
0271: /**
0272: * Method to load all elements from the "backing store" where appropriate.
0273: */
0274: protected void loadFromStore() {
0275: if (backingStore != null && !isCacheLoaded) {
0276: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
0277: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("023006",
0278: StringUtils.toJVMIDString(ownerSM.getObject()),
0279: fieldName));
0280: }
0281: delegate.clear();
0282: Iterator iter = backingStore.iterator(ownerSM);
0283: while (iter.hasNext()) {
0284: delegate.add(iter.next());
0285: }
0286:
0287: isCacheLoaded = true;
0288: }
0289: }
0290:
0291: /**
0292: * Method to flush the changes to the datastore when operating in queued mode.
0293: * Does nothing in "direct" mode.
0294: */
0295: public void flush() {
0296: if (queued) {
0297: if (queuedOperations != null) {
0298: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
0299: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
0300: "023005", StringUtils.toJVMIDString(ownerSM
0301: .getObject()), fieldName));
0302: }
0303: Iterator iter = queuedOperations.iterator();
0304: while (iter.hasNext()) {
0305: QueuedOperation op = (QueuedOperation) iter.next();
0306: op.perform(backingStore, ownerSM);
0307: }
0308:
0309: queuedOperations.clear();
0310: queuedOperations = null;
0311: }
0312: }
0313: }
0314:
0315: /**
0316: * Convenience method to add a queued operation to the operations we perform at commit.
0317: * @param op The operation
0318: */
0319: protected void addQueuedOperation(QueuedOperation op) {
0320: if (queuedOperations == null) {
0321: queuedOperations = new java.util.ArrayList();
0322: }
0323: queuedOperations.add(op);
0324: }
0325:
0326: /**
0327: * Method to update an embedded element in this collection.
0328: * @param element The element
0329: * @param fieldNumber Number of field in the element
0330: * @param value New value for this field
0331: */
0332: public void updateEmbeddedElement(Object element, int fieldNumber,
0333: Object value) {
0334: if (backingStore != null) {
0335: backingStore.updateEmbeddedElement(ownerSM, element,
0336: fieldNumber, value);
0337: }
0338: }
0339:
0340: /**
0341: * Accessor for the field name.
0342: * @return The field name
0343: **/
0344: public String getFieldName() {
0345: return fieldName;
0346: }
0347:
0348: /**
0349: * Accessor for the owner object.
0350: * @return The owner object
0351: **/
0352: public Object getOwner() {
0353: return owner;
0354: }
0355:
0356: /**
0357: * Method to unset the owner and field information.
0358: **/
0359: public synchronized void unsetOwner() {
0360: if (ownerSM != null) {
0361: owner = null;
0362: ownerSM = null;
0363: fieldName = null;
0364: backingStore = null;
0365: }
0366: }
0367:
0368: /**
0369: * Utility to mark the object as dirty
0370: **/
0371: public void makeDirty() {
0372: // Although we are never really "dirty", the owning object must be
0373: // marked dirty so that the proper state change occurs and its
0374: // jdoPreStore() gets called properly.
0375: if (ownerSM != null) {
0376: ownerSM.getObjectManager().getApiAdapter().makeFieldDirty(
0377: owner, fieldName);
0378: }
0379: }
0380:
0381: /**
0382: * Method to return a detached copy of the container.
0383: * Recurse sthrough the elements so that they are likewise detached.
0384: * @param state State for detachment process
0385: * @return The detached container
0386: */
0387: public Object detachCopy(FetchPlanState state) {
0388: java.util.Collection detached = new java.util.Vector();
0389: SCOUtils.detachCopyForCollection(ownerSM, toArray(), state,
0390: detached);
0391: return detached;
0392: }
0393:
0394: /**
0395: * Method to return an attached copy of the passed (detached) value. The returned attached copy
0396: * is a SCO wrapper. Goes through the existing elements in the store for this owner field and
0397: * removes ones no longer present, and adds new elements. All elements in the (detached)
0398: * value are attached.
0399: * @param value The new (collection) value
0400: */
0401: public void attachCopy(Object value) {
0402: java.util.Collection c = (java.util.Collection) value;
0403:
0404: // Attach all of the elements in the new list
0405: AbstractMemberMetaData fmd = ownerSM.getClassMetaData()
0406: .getMetaDataForMember(fieldName);
0407: boolean elementsWithoutIdentity = SCOUtils
0408: .collectionHasElementsWithoutIdentity(fmd);
0409:
0410: java.util.List attachedElements = new java.util.ArrayList(c
0411: .size());
0412: SCOUtils.attachCopyForCollection(ownerSM, c.toArray(),
0413: attachedElements, elementsWithoutIdentity);
0414:
0415: // Update the attached list with the detached elements
0416: SCOUtils.updateListWithListElements(this , attachedElements);
0417: }
0418:
0419: // ------------------------ Query Statement methods ------------------------
0420:
0421: /**
0422: * Method to generate a QueryStatement for the SCO.
0423: * @return The QueryStatement
0424: */
0425: public synchronized QueryExpression newQueryStatement() {
0426: return newQueryStatement(elementType, null);
0427: }
0428:
0429: /**
0430: * Method to return a QueryStatement, using the specified candidate class.
0431: * @param candidateClass the candidate class
0432: * @param candidateAlias Alias for the candidate
0433: * @return The QueryStatement
0434: */
0435: public synchronized QueryExpression newQueryStatement(
0436: Class candidateClass, DatastoreIdentifier candidateAlias) {
0437: if (backingStore == null) {
0438: throw new QueryUnownedSCOException();
0439: }
0440:
0441: return backingStore.newQueryStatement(ownerSM, candidateClass
0442: .getName(), candidateAlias);
0443: }
0444:
0445: /**
0446: * Method to return a ResultObjectFactory for the SCO.
0447: * @param stmt The QueryStatement
0448: * @param ignoreCache Whether to ignore the cache
0449: * @param resultClass Whether to create objects of a particular class
0450: * @param useFetchPlan whether to use the fetch plan to retrieve fields in the same query
0451: * @return The ResultObjectFactory
0452: **/
0453: public synchronized ResultObjectFactory newResultObjectFactory(
0454: QueryExpression stmt, boolean ignoreCache,
0455: Class resultClass, boolean useFetchPlan) {
0456: if (backingStore == null) {
0457: throw new QueryUnownedSCOException();
0458: }
0459:
0460: return backingStore.newResultObjectFactory(ownerSM, stmt,
0461: ignoreCache, useFetchPlan);
0462: }
0463:
0464: // ------------------ Implementation of Vector methods ---------------------
0465:
0466: /**
0467: * Clone operator to return a copy of this object.
0468: * <p>
0469: * Mutable second-class Objects are required to provide a public
0470: * clone method in order to allow for copying PersistenceCapable
0471: * objects. In contrast to Object.clone(), this method must not throw a
0472: * CloneNotSupportedException.
0473: * </p>
0474: * @return The cloned object
0475: */
0476: public Object clone() {
0477: if (useCache) {
0478: loadFromStore();
0479: }
0480:
0481: return delegate.clone();
0482: }
0483:
0484: /**
0485: * Method to return if the list contains this element.
0486: * @param element The element
0487: * @return Whether it is contained
0488: **/
0489: public boolean contains(Object element) {
0490: if (useCache && isCacheLoaded) {
0491: // If the "delegate" is already loaded, use it
0492: return delegate.contains(element);
0493: } else if (backingStore != null) {
0494: return backingStore.contains(ownerSM, element);
0495: }
0496:
0497: return delegate.contains(element);
0498: }
0499:
0500: /**
0501: * Accessor for whether a collection of elements are contained here.
0502: * @param c The collection of elements.
0503: * @return Whether they are contained.
0504: **/
0505: public synchronized boolean containsAll(java.util.Collection c) {
0506: if (useCache) {
0507: loadFromStore();
0508: } else if (backingStore != null) {
0509: java.util.HashSet h = new java.util.HashSet(c);
0510: Iterator iter = iterator();
0511: while (iter.hasNext()) {
0512: h.remove(iter.next());
0513: }
0514:
0515: return h.isEmpty();
0516: }
0517:
0518: return delegate.containsAll(c);
0519: }
0520:
0521: /**
0522: * Method to retrieve an element no.
0523: * @param index The item to retrieve
0524: * @return The element at that position.
0525: **/
0526: public Object elementAt(int index) {
0527: return get(index);
0528: }
0529:
0530: /**
0531: * Equality operator.
0532: * @param o The object to compare against.
0533: * @return Whether this object is the same.
0534: **/
0535: public synchronized boolean equals(Object o) {
0536: if (useCache) {
0537: loadFromStore();
0538: }
0539:
0540: if (o == this ) {
0541: return true;
0542: }
0543:
0544: if (!(o instanceof java.util.List)) {
0545: return false;
0546: }
0547: java.util.List l = (java.util.List) o;
0548: if (l.size() != size()) {
0549: return false;
0550: }
0551: Object[] elements = toArray();
0552: Object[] otherElements = l.toArray();
0553: for (int i = 0; i < elements.length; i++) {
0554: if (!elements[i].equals(otherElements[i])) {
0555: return false;
0556: }
0557: }
0558: return true;
0559: }
0560:
0561: /**
0562: * Method to return the elements of the List as an Enumeration.
0563: * @return The elements
0564: */
0565: public Enumeration elements() {
0566: if (useCache) {
0567: loadFromStore();
0568: return delegate.elements();
0569: } else {
0570: final Iterator iter = new SCOListIterator(this , ownerSM,
0571: delegate, backingStore, useCache, -1);
0572: return new Enumeration() {
0573: public boolean hasMoreElements() {
0574: return iter.hasNext();
0575: }
0576:
0577: public Object nextElement() {
0578: return iter.next();
0579: }
0580: };
0581: }
0582: }
0583:
0584: /**
0585: * Method to return the first element in the Vector.
0586: * @return The first element
0587: */
0588: public Object firstElement() {
0589: return get(0);
0590: }
0591:
0592: /**
0593: * Method to retrieve an element no.
0594: * @param index The item to retrieve
0595: * @return The element at that position.
0596: **/
0597: public Object get(int index) {
0598: if (useCache) {
0599: loadFromStore();
0600: } else if (backingStore != null) {
0601: return backingStore.get(ownerSM, index);
0602: }
0603:
0604: return delegate.get(index);
0605: }
0606:
0607: /**
0608: * Hashcode operator.
0609: * @return The Hash code.
0610: **/
0611: public synchronized int hashCode() {
0612: if (useCache) {
0613: loadFromStore();
0614: }
0615: return delegate.hashCode();
0616: }
0617:
0618: /**
0619: * Method to the position of an element.
0620: * @param element The element.
0621: * @return The position.
0622: **/
0623: public int indexOf(Object element) {
0624: if (useCache) {
0625: loadFromStore();
0626: } else if (backingStore != null) {
0627: return backingStore.indexOf(ownerSM, element);
0628: }
0629:
0630: return delegate.indexOf(element);
0631: }
0632:
0633: /**
0634: * Method to the position of an element.
0635: * @param element The element.
0636: * @param startIndex The start position
0637: * @return The position.
0638: **/
0639: public int indexOf(Object element, int startIndex) {
0640: if (useCache) {
0641: loadFromStore();
0642: } else if (backingStore != null) {
0643: // TODO Currently ignores the "startIndex" but should use it
0644: return backingStore.indexOf(ownerSM, element);
0645: }
0646:
0647: return delegate.indexOf(element, startIndex);
0648: }
0649:
0650: /**
0651: * Accessor for whether the Vector is empty.
0652: * @return Whether it is empty.
0653: **/
0654: public boolean isEmpty() {
0655: return size() == 0;
0656: }
0657:
0658: /**
0659: * Method to retrieve an iterator for the list.
0660: * @return The iterator
0661: **/
0662: public Iterator iterator() {
0663: // Populate the cache if necessary
0664: if (useCache) {
0665: loadFromStore();
0666: }
0667:
0668: return new SCOListIterator(this , ownerSM, delegate,
0669: backingStore, useCache, -1);
0670: }
0671:
0672: /**
0673: * Method to return the last element in the Vector.
0674: * @return The last element
0675: */
0676: public Object lastElement() {
0677: return get(size() - 1);
0678: }
0679:
0680: /**
0681: * Method to retrieve the last position of the element.
0682: * @param element The element
0683: * @return The last position of this element in the List.
0684: **/
0685: public int lastIndexOf(Object element) {
0686: if (useCache) {
0687: loadFromStore();
0688: } else if (backingStore != null) {
0689: return backingStore.lastIndexOf(ownerSM, element);
0690: }
0691:
0692: return delegate.lastIndexOf(element);
0693: }
0694:
0695: /**
0696: * Method to retrieve the last position of the element.
0697: * @param element The element
0698: * @param startIndex The start position
0699: * @return The last position of this element in the List.
0700: **/
0701: public int lastIndexOf(Object element, int startIndex) {
0702: if (useCache) {
0703: loadFromStore();
0704: } else if (backingStore != null) {
0705: // TODO This doesnt start at the "startIndex"
0706: return backingStore.lastIndexOf(ownerSM, element);
0707: }
0708:
0709: return delegate.lastIndexOf(element, startIndex);
0710: }
0711:
0712: /**
0713: * Method to retrieve a List iterator for the list.
0714: * @return The iterator
0715: **/
0716: public ListIterator listIterator() {
0717: // Populate the cache if necessary
0718: if (useCache) {
0719: loadFromStore();
0720: }
0721:
0722: return new SCOListIterator(this , ownerSM, delegate,
0723: backingStore, useCache, -1);
0724: }
0725:
0726: /**
0727: * Method to retrieve a List iterator for the list from the index.
0728: * @param index The start point
0729: * @return The iterator
0730: **/
0731: public ListIterator listIterator(int index) {
0732: // Populate the cache if necessary
0733: if (useCache) {
0734: loadFromStore();
0735: }
0736:
0737: return new SCOListIterator(this , ownerSM, delegate,
0738: backingStore, useCache, index);
0739: }
0740:
0741: /**
0742: * Accessor for the size of the Vector.
0743: * @return The size.
0744: **/
0745: public int size() {
0746: if (useCache && isCacheLoaded) {
0747: // If the "delegate" is already loaded, use it
0748: return delegate.size();
0749: } else if (backingStore != null) {
0750: return backingStore.size(ownerSM);
0751: }
0752:
0753: return delegate.size();
0754: }
0755:
0756: /**
0757: * Accessor for the subList of elements between from and to of the List
0758: * @param from Start index (inclusive)
0759: * @param to End index (exclusive)
0760: * @return The subList
0761: **/
0762: public synchronized java.util.List subList(int from, int to) {
0763: if (useCache) {
0764: loadFromStore();
0765: } else if (backingStore != null) {
0766: return backingStore.subList(ownerSM, from, to);
0767: }
0768:
0769: return delegate.subList(from, to);
0770: }
0771:
0772: /**
0773: * Method to return the list as an array.
0774: * @return The array
0775: **/
0776: public synchronized Object[] toArray() {
0777: if (useCache) {
0778: loadFromStore();
0779: } else if (backingStore != null) {
0780: return SCOUtils.toArray(backingStore, ownerSM);
0781: }
0782: return delegate.toArray();
0783: }
0784:
0785: /**
0786: * Method to return the list as an array.
0787: * @param a The runtime types of the array being defined by this param
0788: * @return The array
0789: **/
0790: public synchronized Object[] toArray(Object a[]) {
0791: if (useCache) {
0792: loadFromStore();
0793: } else if (backingStore != null) {
0794: return SCOUtils.toArray(backingStore, ownerSM, a);
0795: }
0796: return delegate.toArray(a);
0797: }
0798:
0799: // --------------------------- Mutator methods -----------------------------
0800:
0801: /**
0802: * Method to add an element to a position in the Vector.
0803: * @param index The position
0804: * @param element The new element
0805: **/
0806: public void add(int index, Object element) {
0807: // Reject inappropriate elements
0808: if (element == null && !allowNulls) {
0809: throw new NullsNotAllowedException(ownerSM, fieldName);
0810: }
0811:
0812: if (useCache) {
0813: loadFromStore();
0814: }
0815:
0816: if (backingStore != null) {
0817: if (queued && !ownerSM.getObjectManager().isFlushing()) {
0818: addQueuedOperation(new AddAtOperation(index, element));
0819: } else {
0820: try {
0821: backingStore.add(ownerSM, element, index,
0822: (useCache ? delegate.size() : -1));
0823: } catch (JPOXDataStoreException dse) {
0824: JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("023013",
0825: "add", fieldName, dse));
0826: }
0827: }
0828: }
0829:
0830: // Only make it dirty after adding the element(s) to the datastore so we give it time
0831: // to be inserted - otherwise jdoPreStore on this object would have been called before completing the addition
0832: makeDirty();
0833:
0834: delegate.add(index, element);
0835: }
0836:
0837: /**
0838: * Method to add an element to the Vector.
0839: * @param element The new element
0840: * @return Whether it was added ok.
0841: **/
0842: public boolean add(Object element) {
0843: // Reject inappropriate elements
0844: if (element == null && !allowNulls) {
0845: throw new NullsNotAllowedException(ownerSM, fieldName);
0846: }
0847:
0848: if (useCache) {
0849: loadFromStore();
0850: }
0851:
0852: boolean backingSuccess = true;
0853: if (backingStore != null) {
0854: if (queued && !ownerSM.getObjectManager().isFlushing()) {
0855: addQueuedOperation(new AddOperation(element));
0856: } else {
0857: try {
0858: backingStore.add(ownerSM, element,
0859: (useCache ? delegate.size() : -1));
0860: } catch (JPOXDataStoreException dse) {
0861: JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("023013",
0862: "add", fieldName, dse));
0863: backingSuccess = false;
0864: }
0865: }
0866: }
0867:
0868: // Only make it dirty after adding the element(s) to the datastore so we give it time
0869: // to be inserted - otherwise jdoPreStore on this object would have been called before completing the addition
0870: makeDirty();
0871:
0872: boolean delegateSuccess = delegate.add(element);
0873: return (backingStore != null ? backingSuccess : delegateSuccess);
0874: }
0875:
0876: /**
0877: * Method to add a Collection to the Vector.
0878: * @param elements The collection
0879: * @return Whether it was added ok.
0880: **/
0881: public boolean addAll(Collection elements) {
0882: if (useCache) {
0883: loadFromStore();
0884: }
0885:
0886: boolean backingSuccess = true;
0887: if (backingStore != null) {
0888: if (queued && !ownerSM.getObjectManager().isFlushing()) {
0889: Iterator iter = elements.iterator();
0890: while (iter.hasNext()) {
0891: addQueuedOperation(new AddOperation(iter.next()));
0892: }
0893: } else {
0894: try {
0895: backingSuccess = backingStore
0896: .addAll(ownerSM, elements,
0897: (useCache ? delegate.size() : -1));
0898: } catch (JPOXDataStoreException dse) {
0899: JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("023013",
0900: "addAll", fieldName, dse));
0901: backingSuccess = false;
0902: }
0903: }
0904: }
0905:
0906: // Only make it dirty after adding the element(s) to the datastore so we give it time
0907: // to be inserted - otherwise jdoPreStore on this object would have been called before completing the addition
0908: makeDirty();
0909:
0910: boolean delegateSuccess = delegate.addAll(elements);
0911: return (backingStore != null ? backingSuccess : delegateSuccess);
0912: }
0913:
0914: /**
0915: * Method to add a Collection to a position in the Vector.
0916: * @param index Position to insert the collection.
0917: * @param elements The collection
0918: * @return Whether it was added ok.
0919: **/
0920: public boolean addAll(int index, Collection elements) {
0921: if (useCache) {
0922: loadFromStore();
0923: }
0924:
0925: boolean backingSuccess = true;
0926: if (backingStore != null) {
0927: if (queued && !ownerSM.getObjectManager().isFlushing()) {
0928: Iterator iter = elements.iterator();
0929: while (iter.hasNext()) {
0930: addQueuedOperation(new AddOperation(iter.next()));
0931: }
0932: } else {
0933: try {
0934: backingSuccess = backingStore.addAll(ownerSM,
0935: elements, index, (useCache ? delegate
0936: .size() : -1));
0937: } catch (JPOXDataStoreException dse) {
0938: JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("023013",
0939: "addAll", fieldName, dse));
0940: backingSuccess = false;
0941: }
0942: }
0943: }
0944:
0945: // Only make it dirty after adding the element(s) to the datastore so we give it time
0946: // to be inserted - otherwise jdoPreStore on this object would have been called before completing the addition
0947: makeDirty();
0948:
0949: boolean delegateSuccess = delegate.addAll(index, elements);
0950: return (backingStore != null ? backingSuccess : delegateSuccess);
0951: }
0952:
0953: /**
0954: * Method to add an element to the Vector.
0955: * @param element The new element
0956: **/
0957: public void addElement(Object element) {
0958: // This is a historical wrapper to the Collection method
0959: add(element);
0960: }
0961:
0962: /**
0963: * Method to clear the Vector.
0964: **/
0965: public synchronized void clear() {
0966: makeDirty();
0967:
0968: if (backingStore != null) {
0969: if (queued && !ownerSM.getObjectManager().isFlushing()) {
0970: addQueuedOperation(new ClearOperation());
0971: } else {
0972: backingStore.clear(ownerSM);
0973: }
0974: }
0975: delegate.clear();
0976: }
0977:
0978: /**
0979: * Method to remove an element from the Vector.
0980: * @param element The element
0981: * @return Whether the element was removed
0982: **/
0983: public boolean remove(Object element) {
0984: return remove(element, true);
0985: }
0986:
0987: /**
0988: * Method to remove an element from the collection, and observe the flag for whether to allow cascade delete.
0989: * @param element The element
0990: * @param allowCascadeDelete Whether to allow cascade delete
0991: */
0992: public boolean remove(Object element, boolean allowCascadeDelete) {
0993: makeDirty();
0994:
0995: if (useCache) {
0996: loadFromStore();
0997: }
0998:
0999: int size = (useCache ? delegate.size() : -1);
1000: boolean delegateSuccess = delegate.remove(element);
1001:
1002: boolean backingSuccess = true;
1003: if (backingStore != null) {
1004: if (queued && !ownerSM.getObjectManager().isFlushing()) {
1005: backingSuccess = contains(element);
1006: if (backingSuccess) {
1007: addQueuedOperation(new RemoveOperation(element,
1008: allowCascadeDelete));
1009: }
1010: } else {
1011: try {
1012: backingSuccess = backingStore.remove(ownerSM,
1013: element, size, allowCascadeDelete);
1014: } catch (JPOXDataStoreException dse) {
1015: JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("023013",
1016: "remove", fieldName, dse));
1017: backingSuccess = false;
1018: }
1019: }
1020: }
1021:
1022: return (backingStore != null ? backingSuccess : delegateSuccess);
1023: }
1024:
1025: /**
1026: * Method to remove an element from the Vector.
1027: * @param index The element position.
1028: * @return The object that was removed
1029: **/
1030: public Object remove(int index) {
1031: makeDirty();
1032:
1033: if (useCache) {
1034: loadFromStore();
1035: }
1036:
1037: int size = (useCache ? delegate.size() : -1);
1038: Object delegateObject = delegate.remove(index);
1039:
1040: Object backingObject = null;
1041: if (backingStore != null) {
1042: if (queued && !ownerSM.getObjectManager().isFlushing()) {
1043: addQueuedOperation(new RemoveAtOperation(index));
1044: } else {
1045: try {
1046: backingObject = backingStore.remove(ownerSM, index,
1047: size);
1048: } catch (JPOXDataStoreException dse) {
1049: JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("023013",
1050: "remove", fieldName, dse));
1051: backingObject = null;
1052: }
1053: }
1054: }
1055:
1056: return (backingStore != null ? backingObject : delegateObject);
1057: }
1058:
1059: /**
1060: * Method to remove a Collection of elements from the Vector.
1061: * @param elements The collection
1062: * @return Whether it was removed ok.
1063: */
1064: public boolean removeAll(Collection elements) {
1065: makeDirty();
1066:
1067: if (useCache) {
1068: loadFromStore();
1069: }
1070:
1071: int size = (useCache ? delegate.size() : -1);
1072: boolean delegateSuccess = delegate.removeAll(elements);
1073:
1074: boolean backingSuccess = true;
1075: if (backingStore != null) {
1076: if (queued && !ownerSM.getObjectManager().isFlushing()) {
1077: backingSuccess = false;
1078: Iterator iter = elements.iterator();
1079: while (iter.hasNext()) {
1080: Object element = iter.next();
1081: boolean contained = contains(element);
1082: if (contained) {
1083: backingSuccess = true;
1084: addQueuedOperation(new RemoveOperation(iter
1085: .next()));
1086: }
1087: }
1088: } else {
1089: try {
1090: backingSuccess = backingStore.removeAll(ownerSM,
1091: elements, size);
1092: } catch (JPOXDataStoreException dse) {
1093: JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("023013",
1094: "removeAll", fieldName, dse));
1095: backingSuccess = false;
1096: }
1097: }
1098: }
1099:
1100: return (backingStore != null ? backingSuccess : delegateSuccess);
1101: }
1102:
1103: /**
1104: * Method to remove an element from the Vector.
1105: * @param element The element
1106: * @return Whether the element was removed
1107: **/
1108: public boolean removeElement(Object element) {
1109: // This is a historical wrapper to the Collection method
1110: return remove(element);
1111: }
1112:
1113: /**
1114: * Method to remove an element from the Vector.
1115: * @param index The element position.
1116: **/
1117: public void removeElementAt(int index) {
1118: // This is a historical wrapper to the Collection method
1119: remove(index);
1120: }
1121:
1122: /**
1123: * Method to remove all elements from the Vector.
1124: **/
1125: public void removeAllElements() {
1126: clear();
1127: }
1128:
1129: /**
1130: * Method to retain a Collection of elements (and remove all others).
1131: * @param c The collection to retain
1132: * @return Whether they were retained successfully.
1133: **/
1134: public synchronized boolean retainAll(java.util.Collection c) {
1135: makeDirty();
1136:
1137: if (useCache) {
1138: loadFromStore();
1139: }
1140:
1141: boolean modified = false;
1142: Iterator iter = iterator();
1143: while (iter.hasNext()) {
1144: Object element = iter.next();
1145: if (!c.contains(element)) {
1146: iter.remove();
1147: modified = true;
1148: }
1149: }
1150: return modified;
1151: }
1152:
1153: /**
1154: * JPOX wrapper addition that allows turning off of the dependent-field checks
1155: * when doing the position setting. This means that we can prevent the deletion of
1156: * the object that was previously in that position. This particular feature is used
1157: * when attaching a list field and where some elements have changed positions.
1158: * @param index The position
1159: * @param element The new element
1160: * @return The element previously at that position
1161: */
1162: public Object set(int index, Object element,
1163: boolean allowDependentField) {
1164: // Reject inappropriate elements
1165: if (element == null && !allowNulls) {
1166: throw new NullsNotAllowedException(ownerSM, fieldName);
1167: }
1168:
1169: makeDirty();
1170:
1171: if (useCache) {
1172: loadFromStore();
1173: }
1174:
1175: if (backingStore != null) {
1176: if (queued && !ownerSM.getObjectManager().isFlushing()) {
1177: addQueuedOperation(new SetOperation(index, element,
1178: allowDependentField));
1179: } else {
1180: backingStore.set(ownerSM, index, element,
1181: allowDependentField);
1182: }
1183: }
1184: return delegate.set(index, element);
1185: }
1186:
1187: /**
1188: * Method to set the element at a position in the Vector.
1189: * @param index The position
1190: * @param element The new element
1191: * @return The element previously at that position
1192: **/
1193: public Object set(int index, Object element) {
1194: return set(index, element, true);
1195: }
1196:
1197: /**
1198: * Method to set the element at a position in the Vector.
1199: * @param element The new element
1200: * @param index The position
1201: **/
1202: public void setElementAt(Object element, int index) {
1203: // This is a historical wrapper to the Collection method
1204: set(index, element);
1205: }
1206:
1207: /**
1208: * The writeReplace method is called when ObjectOutputStream is preparing
1209: * to write the object to the stream. The ObjectOutputStream checks whether
1210: * the class defines the writeReplace method. If the method is defined, the
1211: * writeReplace method is called to allow the object to designate its
1212: * replacement in the stream. The object returned should be either of the
1213: * same type as the object passed in or an object that when read and
1214: * resolved will result in an object of a type that is compatible with
1215: * all references to the object.
1216: *
1217: * @return the replaced object
1218: * @throws ObjectStreamException
1219: */
1220: protected Object writeReplace() throws ObjectStreamException {
1221: if (useCache) {
1222: loadFromStore();
1223: return new java.util.Vector(delegate);
1224: } else {
1225: // TODO Cater for non-cached collection, load elements in a DB call.
1226: return new java.util.Vector(delegate);
1227: }
1228: }
1229: }
|