KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > oracle > toplink > essentials > mappings > CollectionMapping


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the "License"). You may not use this file except
5  * in compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * glassfish/bootstrap/legal/CDDLv1.0.txt or
9  * https://glassfish.dev.java.net/public/CDDLv1.0.html.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * HEADER in each file and include the License file at
15  * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable,
16  * add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your
18  * own identifying information: Portions Copyright [yyyy]
19  * [name of copyright owner]
20  */

21 // Copyright (c) 1998, 2006, Oracle. All rights reserved.
22
package oracle.toplink.essentials.mappings;
23
24 import java.util.*;
25 import oracle.toplink.essentials.exceptions.*;
26 import oracle.toplink.essentials.indirection.*;
27 import oracle.toplink.essentials.internal.descriptors.*;
28 import oracle.toplink.essentials.internal.helper.*;
29 import oracle.toplink.essentials.internal.identitymaps.*;
30 import oracle.toplink.essentials.internal.indirection.*;
31 import oracle.toplink.essentials.internal.queryframework.*;
32 import oracle.toplink.essentials.internal.sessions.*;
33 import oracle.toplink.essentials.queryframework.*;
34 import oracle.toplink.essentials.sessions.ObjectCopyingPolicy;
35 import oracle.toplink.essentials.expressions.Expression;
36 import oracle.toplink.essentials.expressions.ExpressionBuilder;
37 import oracle.toplink.essentials.internal.sessions.AbstractRecord;
38 import oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl;
39 import oracle.toplink.essentials.internal.sessions.AbstractSession;
40 import oracle.toplink.essentials.descriptors.ClassDescriptor;
41
42 /**
43  * <p><b>Purpose</b>: Abstract class for relationship mappings which store collection of objects
44  *
45  * @author Sati
46  * @since TOPLink/Java 1.0
47  */

48 public abstract class CollectionMapping extends ForeignReferenceMapping implements ContainerMapping {
49     /** Used for delete all in m-m, dc and delete all optimization in 1-m. */
50     protected transient ModifyQuery deleteAllQuery;
51     protected transient boolean hasCustomDeleteAllQuery;
52     protected ContainerPolicy containerPolicy;
53     protected transient boolean hasOrderBy;
54
55     /**
56      * PUBLIC:
57      * Default constructor.
58      */

59     public CollectionMapping() {
60         this.selectionQuery = new ReadAllQuery();
61         this.hasCustomDeleteAllQuery = false;
62         this.containerPolicy = ContainerPolicy.buildPolicyFor(ClassConstants.Vector_class);
63         this.hasOrderBy = false;
64     }
65     
66     /**
67      * PUBLIC:
68      * Provide order support for queryKeyName in ascending order
69      */

70     public void addAscendingOrdering(String JavaDoc queryKeyName) {
71         if (queryKeyName == null) {
72             return;
73         }
74         
75         ((ReadAllQuery)getSelectionQuery()).addAscendingOrdering(queryKeyName);
76     }
77     
78     /**
79      * PUBLIC:
80      * Provide order support for queryKeyName in descending order.
81      */

82     public void addDescendingOrdering(String JavaDoc queryKeyName) {
83         if (queryKeyName == null) {
84             return;
85         }
86         
87         ((ReadAllQuery)getSelectionQuery()).addDescendingOrdering(queryKeyName);
88     }
89     
90     /**
91      * PUBLIC:
92      * Provide order support for queryKeyName in descending or ascending order.
93      * Called from the EJBAnnotationsProcessor when an @OrderBy is found.
94      */

95     public void addOrderBy(String JavaDoc queryKeyName, boolean isDescending) {
96         this.hasOrderBy = true;
97         
98         if (isDescending) {
99             addDescendingOrdering(queryKeyName);
100         } else {
101             addAscendingOrdering(queryKeyName);
102         }
103     }
104     
105     /**
106      * PUBLIC:
107      * Provide order support for queryKeyName in ascending order.
108      * Called from the EJBAnnotationsProcessor when an @OrderBy on an
109      * aggregate is found.
110      */

111     public void addAggregateOrderBy(String JavaDoc aggregateName, String JavaDoc queryKeyName, boolean isDescending) {
112         this.hasOrderBy = true;
113         
114         ReadAllQuery readAllQuery = (ReadAllQuery) getSelectionQuery();
115         ExpressionBuilder builder = readAllQuery.getExpressionBuilder();
116         Expression expression = builder.get(aggregateName).get(queryKeyName).toUpperCase();
117         
118         if (isDescending) {
119             readAllQuery.addOrdering(expression.descending());
120         } else {
121             readAllQuery.addOrdering(expression.ascending());
122         }
123     }
124
125     /**
126      * INTERNAL:
127      * Used during building the backup shallow copy to copy
128      * the vector without re-registering the target objects.
129      */

130     public Object JavaDoc buildBackupCloneForPartObject(Object JavaDoc attributeValue, Object JavaDoc clone, Object JavaDoc backup, UnitOfWorkImpl unitOfWork) {
131         // Check for null
132
if (attributeValue == null) {
133             return getContainerPolicy().containerInstance(1);
134         } else {
135             return getContainerPolicy().cloneFor(attributeValue);
136         }
137     }
138
139     /**
140      * INTERNAL:
141      * Require for cloning, the part must be cloned.
142      * Ignore the objects, use the attribute value.
143      */

144     public Object JavaDoc buildCloneForPartObject(Object JavaDoc attributeValue, Object JavaDoc original, Object JavaDoc clone, UnitOfWorkImpl unitOfWork, boolean isExisting) {
145         ContainerPolicy containerPolicy = getContainerPolicy();
146         if (attributeValue == null) {
147             Object JavaDoc container = containerPolicy.containerInstance(1);
148             return container;
149         }
150         Object JavaDoc clonedAttributeValue = containerPolicy.containerInstance(containerPolicy.sizeFor(attributeValue));
151
152         // I need to synchronize here to prevent the collection from changing while I am cloning it.
153
// This will occur when I am merging into the cache and I am instantiating a UOW valueHolder at the same time
154
// I can not synchronize around the clone, as this will cause deadlocks, so I will need to copy the collection then create the clones
155
// I will use a temporary collection to help speed up the process
156
Object JavaDoc temporaryCollection = null;
157         synchronized (attributeValue) {
158             temporaryCollection = containerPolicy.cloneFor(attributeValue);
159         }
160         for (Object JavaDoc valuesIterator = containerPolicy.iteratorFor(temporaryCollection);
161                  containerPolicy.hasNext(valuesIterator);) {
162             Object JavaDoc cloneValue = buildElementClone(containerPolicy.next(valuesIterator, unitOfWork), unitOfWork, isExisting);
163             containerPolicy.addInto(cloneValue, clonedAttributeValue, unitOfWork);
164         }
165         return clonedAttributeValue;
166     }
167
168     /**
169      * INTERNAL:
170      * Copy of the attribute of the object.
171      * This is NOT used for unit of work but for templatizing an object.
172      */

173     public void buildCopy(Object JavaDoc copy, Object JavaDoc original, ObjectCopyingPolicy policy) {
174         Object JavaDoc attributeValue = getRealCollectionAttributeValueFromObject(original, policy.getSession());
175         Object JavaDoc valuesIterator = getContainerPolicy().iteratorFor(attributeValue);
176         attributeValue = getContainerPolicy().containerInstance(getContainerPolicy().sizeFor(attributeValue));
177         while (getContainerPolicy().hasNext(valuesIterator)) {
178             Object JavaDoc originalValue = getContainerPolicy().next(valuesIterator, policy.getSession());
179             Object JavaDoc copyValue = originalValue;
180             if (policy.shouldCascadeAllParts() || (policy.shouldCascadePrivateParts() && isPrivateOwned())) {
181                 copyValue = policy.getSession().copyObject(originalValue, policy);
182             } else {
183                 // Check for backrefs to copies.
184
copyValue = policy.getCopies().get(originalValue);
185                 if (copyValue == null) {
186                     copyValue = originalValue;
187                 }
188             }
189             getContainerPolicy().addInto(copyValue, attributeValue, policy.getSession());
190         }
191         setRealAttributeValueInObject(copy, attributeValue);
192     }
193
194     /**
195      * INTERNAL:
196      * Clone the element, if necessary.
197      */

198     protected Object JavaDoc buildElementClone(Object JavaDoc element, UnitOfWorkImpl unitOfWork, boolean isExisting) {
199         // optimize registration to knowledge of existence
200
if (isExisting) {
201             return unitOfWork.registerExistingObject(element);
202         } else {// not known whether existing or not
203
return unitOfWork.registerObject(element);
204         }
205     }
206
207     /**
208      * INTERNAL:
209      * Cascade perform delete through mappings that require the cascade
210      */

211     public void cascadePerformRemoveIfRequired(Object JavaDoc object, UnitOfWorkImpl uow, IdentityHashtable visitedObjects){
212         Object JavaDoc cloneAttribute = null;
213         cloneAttribute = getAttributeValueFromObject(object);
214         if ((cloneAttribute == null) || (!this.isCascadeRemove())) {
215             return;
216         }
217
218         ContainerPolicy cp = getContainerPolicy();
219         Object JavaDoc cloneObjectCollection = null;
220         cloneObjectCollection = getRealCollectionAttributeValueFromObject(object, uow);
221         Object JavaDoc cloneIter = cp.iteratorFor(cloneObjectCollection);
222         while (cp.hasNext(cloneIter)) {
223             Object JavaDoc nextObject = cp.next(cloneIter, uow);
224             if (nextObject != null && (! visitedObjects.contains(nextObject)) ){
225                 visitedObjects.put(nextObject, nextObject);
226                 uow.performRemove(nextObject, visitedObjects);
227             }
228         }
229     }
230
231     /**
232      * INTERNAL:
233      * Cascade registerNew for Create through mappings that require the cascade
234      */

235     public void cascadeRegisterNewIfRequired(Object JavaDoc object, UnitOfWorkImpl uow, IdentityHashtable visitedObjects){
236         Object JavaDoc cloneAttribute = null;
237         cloneAttribute = getAttributeValueFromObject(object);
238         if ((cloneAttribute == null) || (!this.isCascadePersist()) || (!getIndirectionPolicy().objectIsInstantiated(cloneAttribute))) {
239             return;
240         }
241
242         ContainerPolicy cp = getContainerPolicy();
243         Object JavaDoc cloneObjectCollection = null;
244         cloneObjectCollection = getRealCollectionAttributeValueFromObject(object, uow);
245         Object JavaDoc cloneIter = cp.iteratorFor(cloneObjectCollection);
246         while (cp.hasNext(cloneIter)) {
247             Object JavaDoc nextObject = cp.next(cloneIter, uow);
248             if (nextObject != null && (! visitedObjects.contains(nextObject)) ){
249                 visitedObjects.put(nextObject, nextObject);
250                 uow.registerNewObjectForPersist(nextObject, visitedObjects);
251             }
252         }
253     }
254     
255     /**
256      * INTERNAL:
257      * Common validation for a collection mapping using a Map class.
258      */

259     private void checkMapClass(Class JavaDoc concreteClass) {
260         // the reference class has to be specified before coming here
261
if (getReferenceClass() == null) {
262             throw DescriptorException.referenceClassNotSpecified(this);
263         }
264         
265         if (! Helper.classImplementsInterface(concreteClass, ClassConstants.Map_Class)) {
266             throw ValidationException.illegalContainerClass(concreteClass);
267         }
268     }
269
270     /**
271      * INTERNAL:
272      * Used by AttributeLevelChangeTracking to update a changeRecord with calculated changes
273      * as apposed to detected changes. If an attribute can not be change tracked it's
274      * changes can be detected through this process.
275      */

276     public void calculateDeferredChanges(ChangeRecord changeRecord, AbstractSession session){
277         CollectionChangeRecord collectionRecord = (CollectionChangeRecord) changeRecord;
278         //clear incase events were fired since the set of the collection
279
// collectionRecord.getAddObjectList().clear();
280
// collectionRecord.getRemoveObjectList().clear();
281
compareCollectionsForChange(collectionRecord.getOriginalCollection(), collectionRecord.getLatestCollection(), collectionRecord, session);
282     }
283
284     /**
285      * INTERNAL:
286      * Cascade the merge to the component object, if appropriate.
287      */

288     public void cascadeMerge(Object JavaDoc sourceElement, MergeManager mergeManager) {
289         if (shouldMergeCascadeParts(mergeManager)) {
290             mergeManager.mergeChanges(mergeManager.getObjectToMerge(sourceElement), null);
291         }
292     }
293
294     /**
295      * INTERNAL:
296      * This method is used to calculate the differences between two collections.
297      * It is passed to the container policy to calculate the changes.
298      */

299     public void compareCollectionsForChange(Object JavaDoc oldCollection, Object JavaDoc newCollection, ChangeRecord changeRecord, AbstractSession session) {
300         getContainerPolicy().compareCollectionsForChange(oldCollection, newCollection, (CollectionChangeRecord) changeRecord, session, getReferenceDescriptor());
301     }
302     
303     /**
304      * INTERNAL:
305      * This method is used to create a change record from comparing two collections
306      * @return prototype.changeset.ChangeRecord
307      */

308     public ChangeRecord compareForChange(Object JavaDoc clone, Object JavaDoc backUp, ObjectChangeSet owner, AbstractSession session) {
309         Object JavaDoc cloneAttribute = null;
310         Object JavaDoc backUpAttribute = null;
311
312         Object JavaDoc backUpObjectCollection = null;
313
314         cloneAttribute = getAttributeValueFromObject(clone);
315
316         if ((cloneAttribute != null) && (!getIndirectionPolicy().objectIsInstantiated(cloneAttribute))) {
317             return null;
318         }
319
320
321         if (!owner.isNew()) {// if the changeSet is for a new object then we must record all off the attributes
322
backUpAttribute = getAttributeValueFromObject(backUp);
323
324             if ((cloneAttribute == null) && (backUpAttribute == null)) {
325                 return null;
326             }
327
328             backUpObjectCollection = getRealCollectionAttributeValueFromObject(backUp, session);
329        }
330
331         Object JavaDoc cloneObjectCollection = null;
332         if (cloneAttribute != null) {
333             cloneObjectCollection = getRealCollectionAttributeValueFromObject(clone, session);
334         } else {
335             cloneObjectCollection = getContainerPolicy().containerInstance(1);
336         }
337
338         CollectionChangeRecord changeRecord = new CollectionChangeRecord(owner);
339         changeRecord.setAttribute(getAttributeName());
340         changeRecord.setMapping(this);
341         compareCollectionsForChange(backUpObjectCollection, cloneObjectCollection, changeRecord, session);
342         if (changeRecord.hasChanges()) {
343             return changeRecord;
344         }
345         return null;
346     }
347
348     /**
349      * INTERNAL:
350      * Compare the attributes belonging to this mapping for the objects.
351      */

352     public boolean compareObjects(Object JavaDoc firstObject, Object JavaDoc secondObject, AbstractSession session) {
353         Object JavaDoc firstObjectCollection = getRealCollectionAttributeValueFromObject(firstObject, session);
354         Object JavaDoc secondObjectCollection = getRealCollectionAttributeValueFromObject(secondObject, session);
355
356         return super.compareObjects(firstObjectCollection, secondObjectCollection, session);
357     }
358
359     /**
360      * INTERNAL:
361      * The memory objects are compared and only the changes are written to the database
362      */

363     protected void compareObjectsAndWrite(Object JavaDoc previousObjects, Object JavaDoc currentObjects, WriteObjectQuery query) throws DatabaseException, OptimisticLockException {
364         ContainerPolicy cp = getContainerPolicy();
365         
366         // If it is for an aggregate collection let it continue so that all of
367
// the correct values are deleted and then re-added This could be
368
// changed to make AggregateCollection changes smarter.
369
if ((query.getObjectChangeSet() != null) && !this.isAggregateCollectionMapping()) {
370             ObjectChangeSet changeSet = query.getObjectChangeSet();
371             CollectionChangeRecord record = (CollectionChangeRecord)changeSet.getChangesForAttributeNamed(this.getAttributeName());
372             
373             if (record != null) {
374                 ObjectChangeSet removedChangeSet = null;
375                 ObjectChangeSet addedChangeSet = null;
376                 UnitOfWorkChangeSet uowChangeSet = (UnitOfWorkChangeSet)changeSet.getUOWChangeSet();
377                 Enumeration removedObjects = record.getRemoveObjectList().elements();
378                 
379                 while (removedObjects.hasMoreElements()) {
380                     removedChangeSet = (ObjectChangeSet)removedObjects.nextElement();
381                     objectRemovedDuringUpdate(query, removedChangeSet.getUnitOfWorkClone());
382                 }
383
384                 Enumeration addedObjects = record.getAddObjectList().elements();
385                 
386                 while (addedObjects.hasMoreElements()) {
387                     addedChangeSet = (ObjectChangeSet)addedObjects.nextElement();
388                     objectAddedDuringUpdate(query, addedChangeSet.getUnitOfWorkClone(), addedChangeSet);
389                 }
390             }
391             
392             return;
393         }
394
395         Hashtable previousObjectsByKey = new Hashtable(cp.sizeFor(previousObjects) + 2);// Read from db or from backup in uow.
396
Hashtable currentObjectsByKey = new Hashtable(cp.sizeFor(currentObjects) + 2);// Current value of object's attribute (clone in uow).
397

398         IdentityHashtable cacheKeysOfCurrentObjects = new IdentityHashtable(cp.sizeFor(currentObjects) + 1);
399
400         // First index the current objects by their primary key.
401
for (Object JavaDoc currentObjectsIter = cp.iteratorFor(currentObjects);
402                  cp.hasNext(currentObjectsIter);) {
403             Object JavaDoc currentObject = cp.next(currentObjectsIter, query.getSession());
404             try {
405                 Vector primaryKey = getReferenceDescriptor().getObjectBuilder().extractPrimaryKeyFromObject(currentObject, query.getSession());
406                 CacheKey key = new CacheKey(primaryKey);
407                 currentObjectsByKey.put(key, currentObject);
408                 cacheKeysOfCurrentObjects.put(currentObject, key);
409             } catch (NullPointerException JavaDoc e) {
410                 // For CR#2646 quietly discard nulls added to a collection mapping.
411
// This try-catch is essentially a null check on currentObject, for
412
// ideally the customer should check for these themselves.
413
if (currentObject != null) {
414                     throw e;
415                 }
416             }
417         }
418
419         // Next index the previous objects (read from db or from backup in uow)
420
// and process the difference to current (optimized in same loop).
421
for (Object JavaDoc previousObjectsIter = cp.iteratorFor(previousObjects);
422                  cp.hasNext(previousObjectsIter);) {
423             Object JavaDoc previousObject = cp.next(previousObjectsIter, query.getSession());
424             Vector primaryKey = getReferenceDescriptor().getObjectBuilder().extractPrimaryKeyFromObject(previousObject, query.getSession());
425             CacheKey key = new CacheKey(primaryKey);
426             previousObjectsByKey.put(key, previousObject);
427
428             // Delete must occur first, incase object with same pk is removed and added,
429
// (technically should not happen, but same applies to unquie constainsts)
430
if (!currentObjectsByKey.containsKey(key)) {
431                 objectRemovedDuringUpdate(query, previousObject);
432             }
433         }
434
435         for (Object JavaDoc currentObjectsIter = cp.iteratorFor(currentObjects);
436                  cp.hasNext(currentObjectsIter);) {
437             Object JavaDoc currentObject = cp.next(currentObjectsIter, query.getSession());
438             try {
439                 CacheKey cacheKey = (CacheKey)cacheKeysOfCurrentObjects.get(currentObject);
440
441                 if (!(previousObjectsByKey.containsKey(cacheKey))) {
442                     objectAddedDuringUpdate(query, currentObject, null);
443                 } else {
444                     objectUnchangedDuringUpdate(query, currentObject, previousObjectsByKey, cacheKey);
445                 }
446             } catch (NullPointerException JavaDoc e) {
447                 // For CR#2646 skip currentObject if it is null.
448
if (currentObject != null) {
449                     throw e;
450                 }
451             }
452         }
453     }
454
455     /**
456      * Compare two objects if their parts are not private owned
457      */

458     protected boolean compareObjectsWithoutPrivateOwned(Object JavaDoc firstCollection, Object JavaDoc secondCollection, AbstractSession session) {
459         ContainerPolicy cp = getContainerPolicy();
460         if (cp.sizeFor(firstCollection) != cp.sizeFor(secondCollection)) {
461             return false;
462         }
463
464         Object JavaDoc firstIter = cp.iteratorFor(firstCollection);
465         Object JavaDoc secondIter = cp.iteratorFor(secondCollection);
466
467         Vector keyValue = new Vector();
468
469         while (cp.hasNext(secondIter)) {
470             Object JavaDoc secondObject = cp.next(secondIter, session);
471             Vector primaryKey = getReferenceDescriptor().getObjectBuilder().extractPrimaryKeyFromObject(secondObject, session);
472             keyValue.addElement(new CacheKey(primaryKey));
473         }
474
475         while (cp.hasNext(firstIter)) {
476             Object JavaDoc firstObject = cp.next(firstIter, session);
477             Vector primaryKey = getReferenceDescriptor().getObjectBuilder().extractPrimaryKeyFromObject(firstObject, session);
478
479             if (!keyValue.contains(new CacheKey(primaryKey))) {
480                 return false;
481             }
482         }
483         return true;
484     }
485
486     /**
487      * Compare two objects if their parts are private owned
488      */

489     protected boolean compareObjectsWithPrivateOwned(Object JavaDoc firstCollection, Object JavaDoc secondCollection, AbstractSession session) {
490         ContainerPolicy cp = getContainerPolicy();
491         if (cp.sizeFor(firstCollection) != cp.sizeFor(secondCollection)) {
492             return false;
493         }
494
495         Object JavaDoc firstIter = cp.iteratorFor(firstCollection);
496         Object JavaDoc secondIter = cp.iteratorFor(secondCollection);
497
498         Hashtable keyValueToObject = new Hashtable(cp.sizeFor(firstCollection) + 2);
499         CacheKey cacheKey;
500
501         while (cp.hasNext(secondIter)) {
502             Object JavaDoc secondObject = cp.next(secondIter, session);
503             Vector primaryKey = getReferenceDescriptor().getObjectBuilder().extractPrimaryKeyFromObject(secondObject, session);
504             keyValueToObject.put(new CacheKey(primaryKey), secondObject);
505         }
506
507         while (cp.hasNext(firstIter)) {
508             Object JavaDoc firstObject = cp.next(firstIter, session);
509             Vector primaryKey = getReferenceDescriptor().getObjectBuilder().extractPrimaryKeyFromObject(firstObject, session);
510             cacheKey = new CacheKey(primaryKey);
511
512             if (keyValueToObject.containsKey(cacheKey)) {
513                 Object JavaDoc object = keyValueToObject.get(cacheKey);
514
515                 if (!session.compareObjects(firstObject, object)) {
516                     return false;
517                 }
518             } else {
519                 return false;
520             }
521         }
522
523         return true;
524     }
525
526     /**
527      * INTERNAL:
528      * Convert all the class-name-based settings in this mapping to actual class-based
529      * settings
530      * This method is implemented by subclasses as necessary.
531      * @param classLoader
532      */

533     public void convertClassNamesToClasses(ClassLoader JavaDoc classLoader){
534         super.convertClassNamesToClasses(classLoader);
535         containerPolicy.convertClassNamesToClasses(classLoader);
536     };
537
538     /**
539      * INTERNAL:
540      * Returns the receiver's containerPolicy.
541      */

542     public ContainerPolicy getContainerPolicy() {
543         return containerPolicy;
544     }
545
546     protected ModifyQuery getDeleteAllQuery() {
547         if (deleteAllQuery == null) {
548             deleteAllQuery = new DataModifyQuery();
549         }
550         return deleteAllQuery;
551     }
552  
553     /**
554      * INTERNAL:
555      * Return the value of an attribute, unwrapping value holders if necessary.
556      * Also check to ensure the collection is a vector.
557      */

558     public Object JavaDoc getRealAttributeValueFromObject(Object JavaDoc object, AbstractSession session) throws DescriptorException {
559         Object JavaDoc value = super.getRealAttributeValueFromObject(object, session);
560         if (value != null) {
561             if (!getContainerPolicy().isValidContainer(value)) {
562                 throw DescriptorException.attributeTypeNotValid(this);
563             }
564         }
565         return value;
566     }
567
568     /**
569      * Convenience method.
570      * Return the value of an attribute, unwrapping value holders if necessary.
571      * If the value is null, build a new container.
572      */

573     public Object JavaDoc getRealCollectionAttributeValueFromObject(Object JavaDoc object, AbstractSession session) throws DescriptorException {
574         Object JavaDoc value = this.getRealAttributeValueFromObject(object, session);
575         if (value == null) {
576             value = this.getContainerPolicy().containerInstance(1);
577         }
578         return value;
579     }
580
581     protected boolean hasCustomDeleteAllQuery() {
582         return hasCustomDeleteAllQuery;
583     }
584
585     /**
586      * INTERNAL:
587      * Return true if ascending or descending ordering has been set on this
588      * mapping via the @OrderBy annotation.
589      */

590     public boolean hasOrderBy() {
591         return hasOrderBy;
592     }
593     
594     /**
595      * INTERNAL:
596      * Initialize the state of mapping.
597      */

598     public void initialize(AbstractSession session) throws DescriptorException {
599         super.initialize(session);
600         setFields(collectFields());
601         getContainerPolicy().prepare(getSelectionQuery(), session);
602
603         // Check that the container policy is correct for the collection type.
604
if ((!usesIndirection()) && (!getAttributeAccessor().getAttributeClass().isAssignableFrom(getContainerPolicy().getContainerClass()))) {
605             throw DescriptorException.incorrectCollectionPolicy(this, getAttributeAccessor().getAttributeClass(), getContainerPolicy().getContainerClass());
606         }
607     }
608
609     /**
610      * INTERNAL:
611      */

612     public boolean isCollectionMapping() {
613         return true;
614     }
615
616     /**
617      * INTERNAL:
618      * Iterate on the specified element.
619      */

620     public void iterateOnElement(DescriptorIterator iterator, Object JavaDoc element) {
621         iterator.iterateReferenceObjectForMapping(element, this);
622     }
623
624     /**
625      * INTERNAL:
626      * Iterate on the attribute value.
627      * The value holder has already been processed.
628      */

629     public void iterateOnRealAttributeValue(DescriptorIterator iterator, Object JavaDoc realAttributeValue) {
630         if (realAttributeValue == null) {
631             return;
632         }
633         ContainerPolicy cp = getContainerPolicy();
634         for (Object JavaDoc iter = cp.iteratorFor(realAttributeValue); cp.hasNext(iter);) {
635             iterateOnElement(iterator, cp.next(iter, iterator.getSession()));
636         }
637     }
638
639     /**
640      * INTERNAL:
641      * Merge changes from the source to the target object. Because this is a
642      * collection mapping, values are added to or removed from the collection
643      * based on the change set.
644      */

645     public void mergeChangesIntoObject(Object JavaDoc target, ChangeRecord chgRecord, Object JavaDoc source, MergeManager mergeManager) {
646         Object JavaDoc valueOfTarget = null;
647         Object JavaDoc valueOfSource = null;
648         AbstractSession parentSession = null;
649         ContainerPolicy containerPolicy = getContainerPolicy();
650         CollectionChangeRecord changeRecord = (CollectionChangeRecord) chgRecord;
651         UnitOfWorkChangeSet uowChangeSet = (UnitOfWorkChangeSet)changeRecord.getOwner().getUOWChangeSet();
652
653         // Collect the changes into a vector. Check to see if the target has an instantiated
654
// collection, if it does then iterate over the changes and merge the collections.
655
if (isAttributeValueInstantiated(target)) {
656             // If it is new will need a new collection.
657
if (changeRecord.getOwner().isNew()) {
658                 valueOfTarget = containerPolicy.containerInstance(changeRecord.getAddObjectList().size());
659             } else {
660                 valueOfTarget = getRealCollectionAttributeValueFromObject(target, mergeManager.getSession());
661             }
662
663             // Remove must happen before add to allow for changes in hash keys.
664
// This is required to return the appropriate object from the parent when unwrapping.
665
if (mergeManager.getSession().isUnitOfWork() && !mergeManager.shouldMergeWorkingCopyIntoBackup()) {
666                 parentSession = ((UnitOfWorkImpl)mergeManager.getSession()).getParent();
667             } else {
668                 parentSession = mergeManager.getSession();
669             }
670             
671             containerPolicy.mergeChanges(changeRecord, valueOfTarget, shouldMergeCascadeParts(mergeManager), mergeManager, parentSession);
672         } else {
673             // The valueholder has not been instantiated
674
if (mergeManager.shouldMergeChangesIntoDistributedCache()) {
675                 return; // do nothing
676
}
677
678             // If I'm not merging on another server then create instance of the collection.
679
valueOfSource = getRealCollectionAttributeValueFromObject(source, mergeManager.getSession());
680             Object JavaDoc iterator = containerPolicy.iteratorFor(valueOfSource);
681             valueOfTarget = containerPolicy.containerInstance(containerPolicy.sizeFor(valueOfSource));
682             
683             while (containerPolicy.hasNext(iterator)) {
684                 // CR2195 - Problem with merging Collection mapping in unit of work and inheritance.
685
Object JavaDoc objectToMerge = containerPolicy.next(iterator, mergeManager.getSession());
686                 
687                 ObjectChangeSet changeSet = (ObjectChangeSet)uowChangeSet.getObjectChangeSetForClone(objectToMerge);
688                 if (shouldMergeCascadeParts(mergeManager) && (valueOfSource != null)) {
689                     mergeManager.mergeChanges(objectToMerge, changeSet);
690                 }
691
692                 // Let the mergemanager get it because I don't have the change for the object.
693
// CR2188 - Problem with merging Collection mapping in unit of work and transparent indirection.
694
containerPolicy.addInto(mergeManager.getTargetVersionOfSourceObject(objectToMerge), valueOfTarget, mergeManager.getSession());
695             }
696         }
697         
698         if (valueOfTarget == null) {
699             valueOfTarget = containerPolicy.containerInstance();
700         }
701         
702         setRealAttributeValueInObject(target, valueOfTarget);
703     }
704
705     /**
706      * INTERNAL:
707      * Merge changes from the source to the target object. This merge is only called when a changeSet for the target
708      * does not exist or the target is uninitialized
709      */

710     public void mergeIntoObject(Object JavaDoc target, boolean isTargetUnInitialized, Object JavaDoc source, MergeManager mergeManager) {
711         if (isTargetUnInitialized) {
712             // This will happen if the target object was removed from the cache before the commit was attempted
713
if (mergeManager.shouldMergeWorkingCopyIntoOriginal() && (!isAttributeValueInstantiated(source))) {
714                 setAttributeValueInObject(target, getIndirectionPolicy().getOriginalIndirectionObject(getAttributeValueFromObject(source), mergeManager.getSession()));
715                 return;
716             }
717         }
718         if (!shouldMergeCascadeReference(mergeManager)) {
719             // This is only going to happen on mergeClone, and we should not attempt to merge the reference
720
return;
721         }
722         if (mergeManager.shouldMergeOriginalIntoWorkingCopy()) {
723             if (!isAttributeValueInstantiated(target)) {
724                 // This will occur when the clone's value has not been instantiated yet and we do not need
725
// the refresh that attribute
726
return;
727             }
728         } else if (!isAttributeValueInstantiated(source)) {
729             // I am merging from a clone into an original. No need to do merge if the attribute was never
730
// modified
731
return;
732         }
733
734         Object JavaDoc valueOfSource = getRealCollectionAttributeValueFromObject(source, mergeManager.getSession());
735
736         // There is a very special case when merging into the shared cache that the original
737
// has been refreshed and now has non-instantiated indirection objects.
738
// Force instantiation is not necessary and can cause problem with JTS drivers.
739
AbstractSession mergeSession = mergeManager.getSession();
740         Object JavaDoc valueOfTarget = getRealCollectionAttributeValueFromObject(target, mergeSession);
741         ContainerPolicy containerPolicy = getContainerPolicy();
742         boolean fireChangeEvents = false;
743         if (! mergeManager.shouldMergeOriginalIntoWorkingCopy()){
744             // if we are copying from original to clone then the source will be
745
// instantiated anyway and we must continue to use the UnitOfWork
746
// valueholder in the case of transparent indirection
747
Object JavaDoc newContainer = containerPolicy.containerInstance(containerPolicy.sizeFor(valueOfSource));
748             valueOfTarget = newContainer;
749         }else{
750             //bug 3953038 - set a new collection in the object until merge completes, this
751
// prevents rel-maint. from adding duplicates.
752
setRealAttributeValueInObject(target, containerPolicy.containerInstance(containerPolicy.sizeFor(valueOfSource)));
753             containerPolicy.clear(valueOfTarget);
754         }
755
756         synchronized(valueOfSource){
757             Object JavaDoc sourceIterator = containerPolicy.iteratorFor(valueOfSource);
758             while (containerPolicy.hasNext(sourceIterator)) {
759                 Object JavaDoc object = containerPolicy.next(sourceIterator, mergeManager.getSession());
760                 if (object == null){
761                     continue; // skip the null
762
}
763                 if (shouldMergeCascadeParts(mergeManager)) {
764                     if ((mergeManager.getSession().isUnitOfWork()) && (((UnitOfWorkImpl)mergeManager.getSession()).getUnitOfWorkChangeSet() != null)) {
765                         // If it is a unit of work, we have to check if I have a change Set fot this object
766
mergeManager.mergeChanges(mergeManager.getObjectToMerge(object), (ObjectChangeSet)((UnitOfWorkImpl)mergeManager.getSession()).getUnitOfWorkChangeSet().getObjectChangeSetForClone(object));
767                     } else {
768                         mergeManager.mergeChanges(mergeManager.getObjectToMerge(object), null);
769                     }
770                 }
771                 object = getReferenceDescriptor().getObjectBuilder().wrapObject(mergeManager.getTargetVersionOfSourceObject(object), mergeManager.getSession());
772                 synchronized (valueOfTarget){
773                     containerPolicy.addInto(object, valueOfTarget, mergeManager.getSession());
774                 }
775             }
776         }
777         // Must re-set variable to allow for set method to re-morph changes if the collection is not being stored directly.
778
setRealAttributeValueInObject(target, valueOfTarget);
779     }
780
781     /**
782      * INTERNAL:
783      * An object was added to the collection during an update, insert it if private.
784      */

785     protected void objectAddedDuringUpdate(ObjectLevelModifyQuery query, Object JavaDoc objectAdded, ObjectChangeSet changeSet) throws DatabaseException, OptimisticLockException {
786         if (!shouldObjectModifyCascadeToParts(query)) {// Called always for M-M
787
return;
788         }
789
790         // Only cascade dependents writes in uow.
791
if (query.shouldCascadeOnlyDependentParts()) {
792             return;
793         }
794
795         // Insert must not be done for uow or cascaded queries and we must cascade to cascade policy.
796
// We should distiguish between insert and write (optimization/paraniod).
797
if (isPrivateOwned()) {
798             InsertObjectQuery insertQuery = new InsertObjectQuery();
799             insertQuery.setObject(objectAdded);
800             insertQuery.setCascadePolicy(query.getCascadePolicy());
801             query.getSession().executeQuery(insertQuery);
802         } else {
803             // Always write for updates, either private or in uow if calling this method.
804
UnitOfWorkChangeSet uowChangeSet = null;
805             if ((changeSet == null) && query.getSession().isUnitOfWork() && (((UnitOfWorkImpl)query.getSession()).getUnitOfWorkChangeSet() != null)) {
806                 uowChangeSet = (UnitOfWorkChangeSet)((UnitOfWorkImpl)query.getSession()).getUnitOfWorkChangeSet();
807                 changeSet = (ObjectChangeSet)uowChangeSet.getObjectChangeSetForClone(query.getObject());
808             }
809             WriteObjectQuery writeQuery = new WriteObjectQuery();
810             writeQuery.setObject(objectAdded);
811             writeQuery.setObjectChangeSet(changeSet);
812             writeQuery.setCascadePolicy(query.getCascadePolicy());
813             query.getSession().executeQuery(writeQuery);
814         }
815     }
816
817     /**
818      * INTERNAL:
819      * An object was removed to the collection during an update, delete it if private.
820      */

821     protected void objectRemovedDuringUpdate(ObjectLevelModifyQuery query, Object JavaDoc objectDeleted) throws DatabaseException, OptimisticLockException {
822         if (isPrivateOwned()) {// Must check ownership for uow and cascading.
823
if (query.shouldCascadeOnlyDependentParts()) {
824                 // If the session is a unit of work
825
if (query.getSession().isUnitOfWork()) {
826                     // ...and the object has not been explictly deleted in the unit of work
827
if (!(((UnitOfWorkImpl)query.getSession()).getDeletedObjects().containsKey(objectDeleted))) {
828                         query.getSession().getCommitManager().addObjectToDelete(objectDeleted);
829                     }
830                 } else {
831                     query.getSession().getCommitManager().addObjectToDelete(objectDeleted);
832                 }
833             } else {
834                 query.getSession().deleteObject(objectDeleted);
835             }
836         }
837     }
838
839     /**
840      * INTERNAL:
841      * An object is still in the collection, update it as it may have changed.
842      */

843     protected void objectUnchangedDuringUpdate(ObjectLevelModifyQuery query, Object JavaDoc object) throws DatabaseException, OptimisticLockException {
844         if (!shouldObjectModifyCascadeToParts(query)) {// Called always for M-M
845
return;
846         }
847
848         // Only cascade dependents writes in uow.
849
if (query.shouldCascadeOnlyDependentParts()) {
850             return;
851         }
852
853         // Always write for updates, either private or in uow if calling this method.
854
WriteObjectQuery writeQuery = new WriteObjectQuery();
855         writeQuery.setObject(object);
856         writeQuery.setCascadePolicy(query.getCascadePolicy());
857         query.getSession().executeQuery(writeQuery);
858     }
859
860     /**
861      * INTERNAL:
862      * copies the non primary key information into the row currently used only in ManyToMany
863      */

864     protected void prepareTranslationRow(AbstractRecord translationRow, Object JavaDoc object, AbstractSession session) {
865         //Do nothing for the generic Collection Mapping
866
}
867
868     /**
869      * INTERNAL:
870      * An object is still in the collection, update it as it may have changed.
871      */

872     protected void objectUnchangedDuringUpdate(ObjectLevelModifyQuery query, Object JavaDoc object, Hashtable backupclones, CacheKey keys) throws DatabaseException, OptimisticLockException {
873         objectUnchangedDuringUpdate(query, object);
874     }
875
876     /**
877      * INTERNAL:
878      * All the privately owned parts are read
879      */

880     protected Object JavaDoc readPrivateOwnedForObject(ObjectLevelModifyQuery modifyQuery) throws DatabaseException {
881         if (modifyQuery.getSession().isUnitOfWork()) {
882             return getRealCollectionAttributeValueFromObject(modifyQuery.getBackupClone(), modifyQuery.getSession());
883         } else {
884             // cr 3819
885
prepareTranslationRow(modifyQuery.getTranslationRow(), modifyQuery.getObject(), modifyQuery.getSession());
886             return modifyQuery.getSession().executeQuery(getSelectionQuery(), modifyQuery.getTranslationRow());
887         }
888     }
889
890     /**
891      * ADVANCED:
892      * Configure the mapping to use a container policy.
893      * The policy manages the access to the collection.
894      */

895     public void setContainerPolicy(ContainerPolicy containerPolicy) {
896         this.containerPolicy = containerPolicy;
897         ((ReadAllQuery)getSelectionQuery()).setContainerPolicy(containerPolicy);
898     }
899
900     /**
901      * PUBLIC:
902      * The default delete all query for mapping can be overridden by specifying the new query.
903      * This query is responsible for doing the deletion required by the mapping,
904      * such as deletion of all the rows from join table for M-M, or optimized delete all of target objects for 1-M.
905      */

906     public void setCustomDeleteAllQuery(ModifyQuery query) {
907         setDeleteAllQuery(query);
908         setHasCustomDeleteAllQuery(true);
909     }
910
911     protected void setDeleteAllQuery(ModifyQuery query) {
912         deleteAllQuery = query;
913     }
914
915     /**
916      * PUBLIC:
917      * Set the receiver's delete all SQL string. This allows the user to override the SQL
918      * generated by TopLink, with there own SQL or procedure call. The arguments are
919      * translated from the fields of the source row, through replacing the field names
920      * marked by '#' with the values for those fields.
921      * This SQL is responsible for doing the deletion required by the mapping,
922      * such as deletion of all the rows from join table for M-M, or optimized delete all of target objects for 1-M.
923      * Example, 'delete from PROJ_EMP where EMP_ID = #EMP_ID'.
924      */

925     public void setDeleteAllSQLString(String JavaDoc sqlString) {
926         DataModifyQuery query = new DataModifyQuery();
927         query.setSQLString(sqlString);
928         setCustomDeleteAllQuery(query);
929     }
930     
931     /**
932      * PUBLIC:
933      * Set the receiver's delete all call. This allows the user to override the SQL
934      * generated by TopLink, with there own SQL or procedure call. The arguments are
935      * translated from the fields of the source row.
936      * This call is responsible for doing the deletion required by the mapping,
937      * such as deletion of all the rows from join table for M-M, or optimized delete all of target objects for 1-M.
938      * Example, 'new SQLCall("delete from PROJ_EMP where EMP_ID = #EMP_ID")'.
939      */

940     public void setDeleteAllCall(Call call) {
941         DataModifyQuery query = new DataModifyQuery();
942         query.setCall(call);
943         setCustomDeleteAllQuery(query);
944     }
945     
946     protected void setHasCustomDeleteAllQuery(boolean bool) {
947         hasCustomDeleteAllQuery = bool;
948     }
949
950     /**
951      * PUBLIC:
952      * Set the name of the session to execute the mapping's queries under.
953      * This can be used by the session broker to override the default session
954      * to be used for the target class.
955      */

956     public void setSessionName(String JavaDoc name) {
957         getDeleteAllQuery().setSessionName(name);
958         getSelectionQuery().setSessionName(name);
959     }
960
961     /**
962      * ADVANCED:
963      * This method is used to have an object add to a collection once the
964      * changeSet is applied. The referenceKey parameter should only be used for
965      * direct Maps.
966      */

967     public void simpleAddToCollectionChangeRecord(Object JavaDoc referenceKey, Object JavaDoc changeSetToAdd, ObjectChangeSet changeSet, AbstractSession session) {
968         CollectionChangeRecord collectionChangeRecord = (CollectionChangeRecord)changeSet.getChangesForAttributeNamed(this.getAttributeName());
969         if (collectionChangeRecord == null) {
970             collectionChangeRecord = new CollectionChangeRecord(changeSet);
971             collectionChangeRecord.setAttribute(getAttributeName());
972             collectionChangeRecord.setMapping(this);
973             collectionChangeRecord.getAddObjectList().put(changeSetToAdd, changeSetToAdd);
974             collectionChangeRecord.getOrderedAddObjects().add(changeSetToAdd);
975             changeSet.addChange(collectionChangeRecord);
976         } else {
977             getContainerPolicy().recordAddToCollectionInChangeRecord((ObjectChangeSet)changeSetToAdd, collectionChangeRecord);
978         }
979         if (referenceKey != null){
980             ((ObjectChangeSet)changeSetToAdd).setNewKey(referenceKey);
981         }
982     }
983
984     /**
985      * ADVANCED:
986      * This method is used to have an object removed from a collection once the
987      * changeSet is applied. The referenceKey parameter should only be used for
988      * direct Maps.
989      */

990     public void simpleRemoveFromCollectionChangeRecord(Object JavaDoc referenceKey, Object JavaDoc changeSetToRemove, ObjectChangeSet changeSet, AbstractSession session) {
991         CollectionChangeRecord collectionChangeRecord = (CollectionChangeRecord)changeSet.getChangesForAttributeNamed(this.getAttributeName());
992         if (collectionChangeRecord == null) {
993             collectionChangeRecord = new CollectionChangeRecord(changeSet);
994             collectionChangeRecord.setAttribute(getAttributeName());
995             collectionChangeRecord.setMapping(this);
996             collectionChangeRecord.getRemoveObjectList().put(changeSetToRemove, changeSetToRemove);
997             changeSet.addChange(collectionChangeRecord);
998         } else {
999             getContainerPolicy().recordRemoveFromCollectionInChangeRecord((ObjectChangeSet)changeSetToRemove, collectionChangeRecord);
1000        }
1001        if (referenceKey != null){
1002            ((ObjectChangeSet)changeSetToRemove).setOldKey(referenceKey);
1003        }
1004    }
1005
1006    /**
1007     * INTERNAL:
1008     * Either create a new change record or update with the new value. This is used
1009     * by attribute change tracking.
1010     * Specifically in a collection mapping this will be called when the customer
1011     * Set a new collection. In this case we will need to mark the change record
1012     * with the new and the old versions of the collection.
1013     * And mark the ObjectChangeSet with the attribute name then when the changes are calculated
1014     * force a compare on the collections to determine changes.
1015     */

1016    public void updateChangeRecord(Object JavaDoc clone, Object JavaDoc newValue, Object JavaDoc oldValue, ObjectChangeSet objectChangeSet, UnitOfWorkImpl uow) {
1017        CollectionChangeRecord collectionChangeRecord = (CollectionChangeRecord)objectChangeSet.getChangesForAttributeNamed(this.getAttributeName());
1018        if (collectionChangeRecord == null) {
1019            collectionChangeRecord = new CollectionChangeRecord(objectChangeSet);
1020            collectionChangeRecord.setAttribute(getAttributeName());
1021            collectionChangeRecord.setMapping(this);
1022            objectChangeSet.addChange(collectionChangeRecord);
1023        }
1024        if (collectionChangeRecord.getOriginalCollection() == null){
1025            collectionChangeRecord.setOriginalCollection(oldValue);
1026        }
1027        collectionChangeRecord.setLatestCollection(newValue);
1028        
1029        objectChangeSet.deferredDetectionRequiredOn(getAttributeName());
1030    }
1031
1032    /**
1033     * PUBLIC:
1034     * Configure the mapping to use an instance of the specified container class
1035     * to hold the target objects.
1036     * <p>The container class must implement (directly or indirectly) the
1037     * <code>java.util.Collection</code> interface.
1038     */

1039    public void useCollectionClass(Class JavaDoc concreteClass) {
1040        ContainerPolicy policy = ContainerPolicy.buildPolicyFor(concreteClass, hasOrderBy());
1041        setContainerPolicy(policy);
1042    }
1043
1044    /**
1045     * PUBLIC:
1046     * Configure the mapping to use an instance of the specified container class
1047     * to hold the target objects.
1048     * <p>The container class must implement (directly or indirectly) the
1049     * <code>java.util.SortedSet</code> interface.
1050     */

1051    public void useSortedSetClass(Class JavaDoc concreteClass, Comparator comparator) {
1052        try {
1053            SortedCollectionContainerPolicy policy = (SortedCollectionContainerPolicy)ContainerPolicy.buildPolicyFor(concreteClass);
1054            policy.setComparator(comparator);
1055            setContainerPolicy(policy);
1056        } catch (ClassCastException JavaDoc e) {
1057            useCollectionClass(concreteClass);
1058        }
1059    }
1060    
1061    /**
1062     * PUBLIC:
1063     * Configure the mapping to use an instance of the specified container
1064     * clas to hold the target objects. The key used to index a value in the
1065     * <code>Map</code> is the value returned by either a call to a
1066     * specified zero-argument method or the value of a field.
1067     * <p> To facilitate resolving the keyName to a method or field,
1068     * the mapping's referenceClass must set before calling this method.
1069     * <p> Note: If the keyName is for a method, that method must be implemented
1070     * by the class (or a superclass) of any value to be inserted into the
1071     * <code>Map</code>.
1072     * <p> The container class must implement (directly or indirectly) the
1073     * <code>java.util.Map</code> interface.
1074     */

1075    public void useMapClass(Class JavaDoc concreteClass, String JavaDoc keyName) {
1076        // the reference class has to be specified before coming here
1077
if (getReferenceClassName() == null) {
1078            throw DescriptorException.referenceClassNotSpecified(this);
1079        }
1080        
1081        ContainerPolicy policy = ContainerPolicy.buildPolicyFor(concreteClass);
1082        policy.setKeyName(keyName, getReferenceClassName());
1083        setContainerPolicy(policy);
1084    }
1085    
1086    /**
1087     * PUBLIC:
1088     * Configure the mapping to use an instance of the specified container
1089     * class to hold the target objects. The key used to index a value in the
1090     * <code>Map</code> is an instance of the composite primary key class.
1091     * <p> To facilitate resolving the primary key class, the mapping's
1092     * referenceClass must set before calling this method.
1093     * <p> The container class must implement (directly or indirectly) the
1094     * <code>java.util.Map</code> interface.
1095     */

1096    public void useMapClass(Class JavaDoc concreteClass) {
1097        useMapClass(concreteClass, null);
1098    }
1099
1100    /**
1101     * PUBLIC:
1102     * If transparent indirection is used, a special collection will be placed in the source
1103     * object's attribute.
1104     * Fetching of the contents of the collection from the database will be delayed
1105     * until absolutely necessary. (Any message sent to the collection will cause
1106     * the contents to be faulted in from the database.)
1107     * This can result in rather significant performance gains, without having to change
1108     * the source object's attribute from Collection (or List or Vector) to
1109     * ValueHolderInterface.
1110     */

1111    public void useTransparentCollection() {
1112        setIndirectionPolicy(new TransparentIndirectionPolicy());
1113        useCollectionClass(ClassConstants.IndirectList_Class);
1114    }
1115    
1116    /**
1117     * PUBLIC:
1118     * If transparent indirection is used, a special collection will be placed in the source
1119     * object's attribute.
1120     * Fetching of the contents of the collection from the database will be delayed
1121     * until absolutely necessary. (Any message sent to the collection will cause
1122     * the contents to be faulted in from the database.)
1123     * This can result in rather significant performance gains, without having to change
1124     * the source object's attribute from Set to
1125     * ValueHolderInterface.
1126     */

1127    public void useTransparentSet() {
1128        setIndirectionPolicy(new TransparentIndirectionPolicy());
1129        useCollectionClass(IndirectSet.class);
1130    }
1131    
1132    /**
1133     * PUBLIC:
1134     * If transparent indirection is used, a special collection will be placed in the source
1135     * object's attribute.
1136     * Fetching of the contents of the collection from the database will be delayed
1137     * until absolutely necessary. (Any message sent to the collection will cause
1138     * the contents to be faulted in from the database.)
1139     * This can result in rather significant performance gains, without having to change
1140     * the source object's attribute from List to
1141     * ValueHolderInterface.
1142     */

1143    public void useTransparentList() {
1144        setIndirectionPolicy(new TransparentIndirectionPolicy());
1145        useCollectionClass(ClassConstants.IndirectList_Class);
1146    }
1147
1148    /**
1149     * PUBLIC:
1150     * If transparent indirection is used, a special map will be placed in the source
1151     * object's attribute.
1152     * Fetching of the contents of the map from the database will be delayed
1153     * until absolutely necessary. (Any message sent to the map will cause
1154     * the contents to be faulted in from the database.)
1155     * This can result in rather significant performance gains, without having to change
1156     * the source object's attribute from Map (or Dictionary or Hashtable) to
1157     * ValueHolderInterface.<p>
1158     * The key used in the Map is the value returned by a call to the zero parameter
1159     * method named methodName. The method should be a zero argument method implemented (or
1160     * inherited) by the value to be inserted into the Map.
1161     */

1162    public void useTransparentMap(String JavaDoc methodName) {
1163        setIndirectionPolicy(new TransparentIndirectionPolicy());
1164        useMapClass(ClassConstants.IndirectMap_Class, methodName);
1165    }
1166
1167    /**
1168     * INTERNAL:
1169     * To validate mappings declaration
1170     */

1171    public void validateBeforeInitialization(AbstractSession session) throws DescriptorException {
1172        super.validateBeforeInitialization(session);
1173
1174        getIndirectionPolicy().validateContainerPolicy(session.getIntegrityChecker());
1175
1176        if (getAttributeAccessor() instanceof InstanceVariableAttributeAccessor) {
1177            Class JavaDoc attributeType = ((InstanceVariableAttributeAccessor)getAttributeAccessor()).getAttributeType();
1178            getIndirectionPolicy().validateDeclaredAttributeTypeForCollection(attributeType, session.getIntegrityChecker());
1179        } else if (getAttributeAccessor() instanceof MethodAttributeAccessor) {
1180            Class JavaDoc returnType = ((MethodAttributeAccessor)getAttributeAccessor()).getGetMethodReturnType();
1181            getIndirectionPolicy().validateGetMethodReturnTypeForCollection(returnType, session.getIntegrityChecker());
1182
1183            Class JavaDoc parameterType = ((MethodAttributeAccessor)getAttributeAccessor()).getSetMethodParameterType();
1184            getIndirectionPolicy().validateSetMethodParameterTypeForCollection(parameterType, session.getIntegrityChecker());
1185        }
1186    }
1187
1188    /**
1189     * INTERNAL:
1190     * Checks if object is deleted from the database or not.
1191     */

1192    public boolean verifyDelete(Object JavaDoc object, AbstractSession session) throws DatabaseException {
1193        // Row is built for translation
1194
if (isReadOnly()) {
1195            return true;
1196        }
1197
1198        if (isPrivateOwned()) {
1199            Object JavaDoc objects = getRealCollectionAttributeValueFromObject(object, session);
1200
1201            ContainerPolicy containerPolicy = getContainerPolicy();
1202            for (Object JavaDoc iter = containerPolicy.iteratorFor(objects); containerPolicy.hasNext(iter);) {
1203                if (!session.verifyDelete(containerPolicy.next(iter, session))) {
1204                    return false;
1205                }
1206            }
1207        }
1208
1209        AbstractRecord row = getDescriptor().getObjectBuilder().buildRowForTranslation(object, session);
1210
1211        //cr 3819 added the line below to fix the translationtable to ensure that it
1212
// contains the required values
1213
prepareTranslationRow(row, object, session);
1214        Object JavaDoc value = session.executeQuery(getSelectionQuery(), row);
1215
1216        return getContainerPolicy().isEmpty(value);
1217    }
1218
1219    /**
1220     * INTERNAL:
1221     * Add a new value and its change set to the collection change record. This is used by
1222     * attribute change tracking.
1223     */

1224    public void addToCollectionChangeRecord(Object JavaDoc newKey, Object JavaDoc newValue, ObjectChangeSet objectChangeSet, UnitOfWorkImpl uow) {
1225        if (newValue != null) {
1226            ClassDescriptor descriptor;
1227            //PERF: Use referenceDescriptor if it does not have inheritance
1228
if (!getReferenceDescriptor().hasInheritance()) {
1229                descriptor = getReferenceDescriptor();
1230            } else {
1231                descriptor = uow.getDescriptor(newValue);
1232            }
1233            newValue = descriptor.getObjectBuilder().unwrapObject(newValue, uow);
1234            ObjectChangeSet newSet = descriptor.getObjectBuilder().createObjectChangeSet(newValue, (UnitOfWorkChangeSet)objectChangeSet.getUOWChangeSet(), uow);
1235            simpleAddToCollectionChangeRecord(newKey, newSet, objectChangeSet, uow);
1236        }
1237    }
1238    
1239    /**
1240     * INTERNAL:
1241     * Return if this mapping supports change tracking.
1242     */

1243    public boolean isChangeTrackingSupported() {
1244        return true;
1245    }
1246
1247    /**
1248     * INTERNAL:
1249     * Remove a value and its change set from the collection change record. This is used by
1250     * attribute change tracking.
1251     */

1252    public void removeFromCollectionChangeRecord(Object JavaDoc newKey, Object JavaDoc newValue, ObjectChangeSet objectChangeSet, UnitOfWorkImpl uow) {
1253        if (newValue != null) {
1254            ClassDescriptor descriptor;
1255
1256            //PERF: Use referenceDescriptor if it does not have inheritance
1257
if (!getReferenceDescriptor().hasInheritance()) {
1258                descriptor = getReferenceDescriptor();
1259            } else {
1260                descriptor = uow.getDescriptor(newValue);
1261            }
1262            newValue = descriptor.getObjectBuilder().unwrapObject(newValue, uow);
1263            ObjectChangeSet newSet = descriptor.getObjectBuilder().createObjectChangeSet(newValue, (UnitOfWorkChangeSet)objectChangeSet.getUOWChangeSet(), uow);
1264            simpleRemoveFromCollectionChangeRecord(newKey, newSet, objectChangeSet, uow);
1265        }
1266    }
1267
1268    /**
1269     * INTERNAL:
1270     * Directly build a change record without comparison
1271     */

1272    public ChangeRecord buildChangeRecord(Object JavaDoc clone, ObjectChangeSet owner, AbstractSession session) {
1273        Object JavaDoc cloneAttribute = null;
1274        cloneAttribute = getAttributeValueFromObject(clone);
1275        if ((cloneAttribute != null) && (!getIndirectionPolicy().objectIsInstantiated(cloneAttribute))) {
1276            return null;
1277        }
1278
1279        // 2612538 - the default size of IdentityHashtable (32) is appropriate
1280
IdentityHashMap cloneKeyValues = new IdentityHashMap();
1281        ContainerPolicy cp = getContainerPolicy();
1282        Object JavaDoc cloneObjectCollection = null;
1283        if (cloneAttribute != null) {
1284            cloneObjectCollection = getRealCollectionAttributeValueFromObject(clone, session);
1285        } else {
1286            cloneObjectCollection = cp.containerInstance(1);
1287        }
1288        Object JavaDoc cloneIter = cp.iteratorFor(cloneObjectCollection);
1289
1290        while (cp.hasNext(cloneIter)) {
1291            Object JavaDoc firstObject = cp.next(cloneIter, session);
1292            if (firstObject != null) {
1293                cloneKeyValues.put(firstObject, firstObject);
1294            }
1295        }
1296
1297        CollectionChangeRecord changeRecord = new CollectionChangeRecord(owner);
1298        changeRecord.setAttribute(getAttributeName());
1299        changeRecord.setMapping(this);
1300        changeRecord.addAdditionChange(cloneKeyValues, (UnitOfWorkChangeSet)owner.getUOWChangeSet(), session);
1301        if (changeRecord.hasChanges()) {
1302            return changeRecord;
1303        }
1304        return null;
1305    }
1306
1307    /**
1308     * INTERNAL:
1309     * Indicates whether valueFromRow should call valueFromRowInternalWithJoin (true)
1310     * or valueFromRowInternal (false)
1311     */

1312    protected boolean shouldUseValueFromRowWithJoin(JoinedAttributeManager joinManager) {
1313        return joinManager.getDataResults_()!=null && super.shouldUseValueFromRowWithJoin(joinManager);
1314    }
1315    
1316    /**
1317     * INTERNAL:
1318     * Return the value of the field from the row or a value holder on the query to obtain the object.
1319     * To get here the mapping's isJoiningSupported() should return true,
1320     * currently that's the case for only 1-m and m-m.
1321     */

1322    protected Object JavaDoc valueFromRowInternalWithJoin(AbstractRecord row, JoinedAttributeManager joinManager, AbstractSession executionSession) throws DatabaseException {
1323        // If the query was using joining, all of the result rows will have been set.
1324
List rows = joinManager.getDataResults_();
1325        Object JavaDoc value = getContainerPolicy().containerInstance();
1326        
1327        // A nested query must be built to pass to the descriptor that looks like the real query execution would,
1328
// these should be cached on the query during prepare.
1329
ObjectLevelReadQuery nestedQuery = null;
1330        if (joinManager.getJoinedMappingQueries_() != null) {
1331            nestedQuery = (ObjectLevelReadQuery) joinManager.getJoinedMappingQueries_().get(this);
1332        } else {
1333            nestedQuery = prepareNestedJoins(joinManager, executionSession);
1334        }
1335        nestedQuery.setSession(executionSession);
1336        //CR #4365 - used to prevent infinite recursion on refresh object cascade all
1337
nestedQuery.setQueryId(joinManager.getBaseQuery().getQueryId());
1338
1339        // Extract the primary key of the source object, to filter only the joined rows for that object.
1340
Vector sourceKey = getDescriptor().getObjectBuilder().extractPrimaryKeyFromRow(row, executionSession);
1341        CacheKey sourceCacheKey = new CacheKey(sourceKey);
1342        
1343        // A set of target cache keys must be maintained to avoid duplicates from multiple 1-m joins.
1344
Set targetCacheKeys = new HashSet();
1345
1346        // For each rows, extract the target row and build the target object and add to the collection.
1347
for (int index = 0; index < rows.size(); index++) {
1348            AbstractRecord sourceRow = (AbstractRecord)rows.get(index);
1349            AbstractRecord targetRow = sourceRow;
1350            
1351            // Row will be set to null if part of another object's join already processed.
1352
if (targetRow != null) {
1353                // CR #... the field for many objects may be in the row,
1354
// so build the subpartion of the row through the computed values in the query,
1355
// this also helps the field indexing match.
1356
targetRow = trimRowForJoin(targetRow, joinManager, executionSession);
1357                AbstractRecord pkRow = trimRowForJoin(sourceRow, new Integer JavaDoc(joinManager.getParentResultIndex()), executionSession);
1358                nestedQuery.setTranslationRow(targetRow);
1359
1360                // Extract the primary key of the row to filter only the joined rows for the source object.
1361
Vector rowSourceKey = getDescriptor().getObjectBuilder().extractPrimaryKeyFromRow(pkRow, executionSession);
1362                if(rowSourceKey != null) {
1363                    CacheKey rowSourceCacheKey = new CacheKey(rowSourceKey);
1364                    
1365                    // Only build/add the object if the join row is for the object.
1366
if (sourceCacheKey.equals(rowSourceCacheKey)) {
1367                        // Partial object queries must select the primary key of the source and related objects.
1368
// If the target joined rows in null (outerjoin) means an empty collection.
1369
Vector targetKey = getReferenceDescriptor().getObjectBuilder().extractPrimaryKeyFromRow(targetRow, executionSession);
1370                        if (targetKey == null) {
1371                            // A null primary key means an empty collection returned as nulls from an outerjoin.
1372
return getIndirectionPolicy().valueFromRow(value);
1373                        }
1374                        CacheKey targetCacheKey = new CacheKey(targetKey);
1375                        
1376                        // Only build/add the taregt object once, skip duplicates from multiple 1-m joins.
1377
if (!targetCacheKeys.contains(targetCacheKey)) {
1378                            targetCacheKeys.add(targetCacheKey);
1379                            Object JavaDoc targetObject = getReferenceDescriptor().getObjectBuilder().buildObject(nestedQuery, targetRow, nestedQuery.getJoinedAttributeManager());
1380                            nestedQuery.setTranslationRow(null);
1381                            getContainerPolicy().addInto(targetObject, value, executionSession);
1382                        }
1383                    }
1384                } else {
1385                    // Clear an empty row
1386
rows.set(index, null);
1387                }
1388            }
1389        }
1390        return getIndirectionPolicy().valueFromRow(value);
1391    }
1392}
1393
Popular Tags