KickJava   Java API By Example, From Geeks To Geeks.

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


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.security.AccessController JavaDoc;
25 import java.security.PrivilegedActionException JavaDoc;
26 import java.util.*;
27 import oracle.toplink.essentials.exceptions.*;
28 import oracle.toplink.essentials.internal.descriptors.*;
29 import oracle.toplink.essentials.internal.security.PrivilegedAccessHelper;
30 import oracle.toplink.essentials.internal.security.PrivilegedClassForName;
31 import oracle.toplink.essentials.internal.sessions.*;
32 import oracle.toplink.essentials.queryframework.*;
33 import oracle.toplink.essentials.sessions.ObjectCopyingPolicy;
34 import oracle.toplink.essentials.descriptors.DescriptorEventManager;
35 import oracle.toplink.essentials.descriptors.DescriptorQueryManager;
36 import oracle.toplink.essentials.internal.sessions.AbstractRecord;
37 import oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl;
38 import oracle.toplink.essentials.internal.sessions.AbstractSession;
39 import oracle.toplink.essentials.descriptors.ClassDescriptor;
40 import oracle.toplink.essentials.internal.queryframework.JoinedAttributeManager;
41
42 /**
43  * <b>Purpose</b>: Two objects can be considered to be related by aggregation if there is a strict
44  * 1:1 relationship between the objects. This means that if the source (parent)object exists, then
45  * the target (child or owned) object must exist. This class implements the behavior common to the
46  * aggregate object and structure mappings.
47  *
48  * @author Sati
49  * @since TopLink for Java 1.0
50  */

51 public abstract class AggregateMapping extends DatabaseMapping {
52
53     /** Stores a reference class */
54     protected Class JavaDoc referenceClass;
55     protected String JavaDoc referenceClassName;
56
57     /** The descriptor of the reference class */
58     protected ClassDescriptor referenceDescriptor;
59
60     /**
61      * Default constructor.
62      */

63     public AggregateMapping() {
64         super();
65     }
66
67     /**
68      * Make a copy of the sourceQuery for the attribute.
69      */

70     protected DeleteObjectQuery buildAggregateDeleteQuery(DeleteObjectQuery sourceQuery, Object JavaDoc sourceAttributeValue) {
71         DeleteObjectQuery aggregateQuery = new DeleteObjectQuery();
72         buildAggregateModifyQuery(sourceQuery, aggregateQuery, sourceAttributeValue);
73         return aggregateQuery;
74     }
75
76     /**
77      * Initialize the aggregate query with the settings from the source query.
78      */

79     protected void buildAggregateModifyQuery(ObjectLevelModifyQuery sourceQuery, ObjectLevelModifyQuery aggregateQuery, Object JavaDoc sourceAttributeValue) {
80         if (sourceQuery.getSession().isUnitOfWork()) {
81             Object JavaDoc backupAttributeValue = getAttributeValueFromBackupClone(sourceQuery.getBackupClone());
82             if (backupAttributeValue == null) {
83                 backupAttributeValue = getObjectBuilder(sourceAttributeValue, sourceQuery.getSession()).buildNewInstance();
84             }
85             aggregateQuery.setBackupClone(backupAttributeValue);
86         }
87         aggregateQuery.setCascadePolicy(sourceQuery.getCascadePolicy());
88         aggregateQuery.setObject(sourceAttributeValue);
89         aggregateQuery.setTranslationRow(sourceQuery.getTranslationRow());
90         aggregateQuery.setSession(sourceQuery.getSession());
91         aggregateQuery.setProperties(sourceQuery.getProperties());
92     }
93
94     /**
95      * Make a copy of the sourceQuery for the attribute.
96      */

97     protected WriteObjectQuery buildAggregateWriteQuery(WriteObjectQuery sourceQuery, Object JavaDoc sourceAttributeValue) {
98         WriteObjectQuery aggregateQuery = new WriteObjectQuery();
99         buildAggregateModifyQuery(sourceQuery, aggregateQuery, sourceAttributeValue);
100         return aggregateQuery;
101     }
102
103     /**
104      * INTERNAL:
105      * Clone the attribute from the clone and assign it to the backup.
106      */

107     public void buildBackupClone(Object JavaDoc clone, Object JavaDoc backup, UnitOfWorkImpl unitOfWork) {
108         Object JavaDoc attributeValue = getAttributeValueFromObject(clone);
109         setAttributeValueInObject(backup, buildBackupClonePart(attributeValue, unitOfWork));
110     }
111
112     /**
113      * INTERNAL:
114      * Build and return a backup clone of the attribute.
115      */

116     protected Object JavaDoc buildBackupClonePart(Object JavaDoc attributeValue, UnitOfWorkImpl unitOfWork) {
117         if (attributeValue == null) {
118             return null;
119         }
120         return getObjectBuilder(attributeValue, unitOfWork).buildBackupClone(attributeValue, unitOfWork);
121     }
122
123     /**
124      * INTERNAL:
125      * Clone the attribute from the original and assign it to the clone.
126      */

127     public void buildClone(Object JavaDoc original, Object JavaDoc clone, UnitOfWorkImpl unitOfWork, JoinedAttributeManager joinedAttributeManager) {
128         Object JavaDoc attributeValue = getAttributeValueFromObject(original);
129         setAttributeValueInObject(clone, buildClonePart(original, attributeValue, unitOfWork));
130     }
131
132     /**
133      * INTERNAL:
134      * A combination of readFromRowIntoObject and buildClone.
135      * <p>
136      * buildClone assumes the attribute value exists on the original and can
137      * simply be copied.
138      * <p>
139      * readFromRowIntoObject assumes that one is building an original.
140      * <p>
141      * Both of the above assumptions are false in this method, and actually
142      * attempts to do both at the same time.
143      * <p>
144      * Extract value from the row and set the attribute to this value in the
145      * working copy clone.
146      * In order to bypass the shared cache when in transaction a UnitOfWork must
147      * be able to populate working copies directly from the row.
148      */

149     public void buildCloneFromRow(AbstractRecord databaseRow, JoinedAttributeManager joinManager, Object JavaDoc clone, ObjectBuildingQuery sourceQuery, UnitOfWorkImpl unitOfWork, AbstractSession executionSession) {
150         // automatically returns a uow result from scratch that doesn't need cloning
151
Object JavaDoc cloneAttributeValue = valueFromRow(databaseRow, joinManager, sourceQuery, executionSession);
152         setAttributeValueInObject(clone, cloneAttributeValue);
153     }
154
155     /**
156      * INTERNAL:
157      * Build and return a clone of the attribute.
158      */

159     protected Object JavaDoc buildClonePart(Object JavaDoc original, Object JavaDoc attributeValue, UnitOfWorkImpl unitOfWork) {
160         if (attributeValue == null) {
161             return null;
162         }
163         if (unitOfWork.isOriginalNewObject(original)) {
164             unitOfWork.addNewAggregate(attributeValue);
165         }
166
167         // Do not clone for read-only.
168
if (unitOfWork.isClassReadOnly(attributeValue.getClass())) {
169             return attributeValue;
170         }
171
172         ObjectBuilder aggregateObjectBuilder = getObjectBuilder(attributeValue, unitOfWork);
173
174         // bug 2612602 as we are building the working copy make sure that we call to correct clone method.
175
Object JavaDoc clonedAttributeValue = aggregateObjectBuilder.instantiateWorkingCopyClone(attributeValue, unitOfWork);
176         aggregateObjectBuilder.populateAttributesForClone(attributeValue, clonedAttributeValue, unitOfWork, null);
177
178         return clonedAttributeValue;
179     }
180
181     /**
182      * INTERNAL:
183      * Copy of the attribute of the object.
184      * This is NOT used for unit of work but for templatizing an object.
185      */

186     public void buildCopy(Object JavaDoc copy, Object JavaDoc original, ObjectCopyingPolicy policy) {
187         Object JavaDoc attributeValue = getAttributeValueFromObject(original);
188         setAttributeValueInObject(copy, buildCopyOfAttributeValue(attributeValue, policy));
189     }
190
191     /**
192      * Copy of the attribute of the object.
193      * This is NOT used for unit of work but for templatizing an object.
194      */

195     protected Object JavaDoc buildCopyOfAttributeValue(Object JavaDoc attributeValue, ObjectCopyingPolicy policy) {
196         if (attributeValue == null) {
197             return null;
198         }
199         return getObjectBuilder(attributeValue, policy.getSession()).copyObject(attributeValue, policy);
200     }
201
202     /**
203      * INTERNAL:
204      * Build and return a new instance of the specified attribute.
205      * This will be populated by a merge.
206      */

207     protected Object JavaDoc buildNewMergeInstanceOf(Object JavaDoc sourceAttributeValue, AbstractSession session) {
208         return getObjectBuilder(sourceAttributeValue, session).buildNewInstance();
209     }
210
211     /**
212      * INTERNAL:
213      * Cascade perform delete through mappings that require the cascade
214      */

215 // public void cascadePerformDeleteIfRequired(Object object, UnitOfWork uow, IdentityHashtable visitedObjects){
216
//objects referenced by this mapping are not registered as they have
217
// no identity, this is a no-op.
218
// }
219

220     /**
221      * INTERNAL:
222      * Cascade registerNew for Create through mappings that require the cascade
223      */

224 // public void cascadeRegisterNewIfRequired(Object object, UnitOfWork uow, IdentityHashtable visitedObjects){
225
//aggregate objects are not registeres as they have no identity, this is a no-op.
226
// }
227

228     /**
229      * INTERNAL:
230      * Compare the attributes. Return true if they are alike.
231      */

232     protected boolean compareAttributeValues(Object JavaDoc attributeValue1, Object JavaDoc attributeValue2, AbstractSession session) {
233         if ((attributeValue1 == null) && (attributeValue2 == null)) {
234             return true;
235         }
236         if ((attributeValue1 == null) || (attributeValue2 == null)) {
237             return false;
238         }
239         if (attributeValue1.getClass() != attributeValue2.getClass()) {
240             return false;
241         }
242         return getObjectBuilder(attributeValue1, session).compareObjects(attributeValue1, attributeValue2, session);
243     }
244
245     /**
246      * INTERNAL:
247      * Compare the changes between two aggregates.
248      * Return a change record holding the changes.
249      */

250     public ChangeRecord compareForChange(Object JavaDoc clone, Object JavaDoc backup, ObjectChangeSet owner, AbstractSession session) {
251         Object JavaDoc cloneAttribute = getAttributeValueFromObject(clone);
252         Object JavaDoc backupAttribute = null;
253
254         if (!owner.isNew()) {
255             backupAttribute = getAttributeValueFromObject(backup);
256             if ((cloneAttribute == null) && (backupAttribute == null)) {
257                 return null;// no change
258
}
259             if ((cloneAttribute != null) && (backupAttribute != null) && (!cloneAttribute.getClass().equals(backupAttribute.getClass()))) {
260                 backupAttribute = null;
261             }
262         }
263
264         AggregateChangeRecord changeRecord = new AggregateChangeRecord(owner);
265         changeRecord.setAttribute(getAttributeName());
266         changeRecord.setMapping(this);
267
268         if (cloneAttribute == null) {// the attribute was set to null
269
changeRecord.setChangedObject(null);
270             return changeRecord;
271         }
272
273         ObjectBuilder builder = getObjectBuilder(cloneAttribute, session);
274
275         //if the owner is new then the backup will be null, if the owner is new then the aggregate is new
276
//if the backup is null but the owner is not new then this aggregate is new
277
ObjectChangeSet initialChanges = builder.createObjectChangeSet(cloneAttribute, (UnitOfWorkChangeSet)owner.getUOWChangeSet(), (backupAttribute == null), session);
278         ObjectChangeSet changeSet = builder.compareForChange(cloneAttribute, backupAttribute, (UnitOfWorkChangeSet)owner.getUOWChangeSet(), session);
279         if (changeSet == null) {
280             if (initialChanges.isNew()) {
281                 // This happens if original aggregate is of class A, the new aggregate
282
// is of class B (B inherits from A) - and neither A nor B has any mapped attributes.
283
// CR3664
284
changeSet = initialChanges;
285             } else {
286                 return null;// no change
287
}
288         }
289         changeRecord.setChangedObject(changeSet);
290         return changeRecord;
291     }
292
293     /**
294      * INTERNAL:
295      * Compare the attributes belonging to this mapping for the objects.
296      */

297     public boolean compareObjects(Object JavaDoc firstObject, Object JavaDoc secondObject, AbstractSession session) {
298         return compareAttributeValues(getAttributeValueFromObject(firstObject), getAttributeValueFromObject(secondObject), session);
299     }
300
301     /**
302      * INTERNAL:
303      * Convert all the class-name-based settings in this mapping to actual class-based
304      * settings. This method is used when converting a project that has been built
305      * with class names to a project with classes.
306      * @param classLoader
307      */

308     public void convertClassNamesToClasses(ClassLoader JavaDoc classLoader){
309         Class JavaDoc referenceClass = null;
310         try{
311             if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
312                 try {
313                     referenceClass = (Class JavaDoc)AccessController.doPrivileged(new PrivilegedClassForName(getReferenceClassName(), true, classLoader));
314                 } catch (PrivilegedActionException JavaDoc exception) {
315                     throw ValidationException.classNotFoundWhileConvertingClassNames(getReferenceClassName(), exception.getException());
316                 }
317             } else {
318                 referenceClass = oracle.toplink.essentials.internal.security.PrivilegedAccessHelper.getClassForName(getReferenceClassName(), true, classLoader);
319             }
320         } catch (ClassNotFoundException JavaDoc exc){
321             throw ValidationException.classNotFoundWhileConvertingClassNames(getReferenceClassName(), exc);
322         }
323         setReferenceClass(referenceClass);
324     };
325
326     /**
327      * INTERNAL:
328      * Execute a descriptor event for the specified event code.
329      */

330     protected void executeEvent(int eventCode, ObjectLevelModifyQuery query) {
331         ClassDescriptor referenceDescriptor = getReferenceDescriptor(query.getObject(), query.getSession());
332
333         // PERF: Avoid events if no listeners.
334
if (referenceDescriptor.getEventManager().hasAnyEventListeners()) {
335             referenceDescriptor.getEventManager().executeEvent(new oracle.toplink.essentials.descriptors.DescriptorEvent(eventCode, query));
336         }
337     }
338
339     /**
340      * Return the appropriate attribute value.
341      * This method is a hack to allow the aggregate collection
342      * subclass to override....
343      */

344     protected Object JavaDoc getAttributeValueFromBackupClone(Object JavaDoc backupClone) {
345         return getAttributeValueFromObject(backupClone);
346     }
347
348     /**
349      * Convenience method
350      */

351     protected ObjectBuilder getObjectBuilderForClass(Class JavaDoc javaClass, AbstractSession session) {
352         return getReferenceDescriptor(javaClass, session).getObjectBuilder();
353     }
354
355     /**
356      * Convenience method
357      */

358     protected ObjectBuilder getObjectBuilder(Object JavaDoc attributeValue, AbstractSession session) {
359         return getReferenceDescriptor(attributeValue, session).getObjectBuilder();
360     }
361
362     /**
363      * Convenience method
364      */

365     protected DescriptorQueryManager getQueryManager(Object JavaDoc attributeValue, AbstractSession session) {
366         return getReferenceDescriptor(attributeValue, session).getQueryManager();
367     }
368
369     /**
370      * PUBLIC:
371      * Returns the reference class
372      */

373     public Class JavaDoc getReferenceClass() {
374         return referenceClass;
375     }
376
377     /**
378      * INTERNAL:
379      * Used by MW.
380      */

381     public String JavaDoc getReferenceClassName() {
382         if ((referenceClassName == null) && (referenceClass != null)) {
383             referenceClassName = referenceClass.getName();
384         }
385         return referenceClassName;
386     }
387
388     /**
389      * INTERNAL:
390      * Return the referenceDescriptor. This is a descriptor which is associated with the reference class.
391      * NOTE: If you are looking for the descriptor for a specific aggregate object, use
392      * #getReferenceDescriptor(Object). This will ensure you get the right descriptor if the object's
393      * descriptor is part of an inheritance tree.
394      */

395     public ClassDescriptor getReferenceDescriptor() {
396         return referenceDescriptor;
397     }
398
399     /**
400      * INTERNAL:
401      * For inheritance purposes.
402      */

403     protected ClassDescriptor getReferenceDescriptor(Class JavaDoc theClass, AbstractSession session) {
404         if (getReferenceDescriptor().getJavaClass().equals(theClass)) {
405             return getReferenceDescriptor();
406         }
407
408         ClassDescriptor subclassDescriptor = session.getDescriptor(theClass);
409         if (subclassDescriptor == null) {
410             throw DescriptorException.noSubClassMatch(theClass, this);
411         } else {
412             return subclassDescriptor;
413         }
414     }
415
416     /**
417      * Convenience method
418      */

419     protected ClassDescriptor getReferenceDescriptor(Object JavaDoc attributeValue, AbstractSession session) {
420         if (attributeValue == null) {
421             return getReferenceDescriptor();
422         } else {
423             return getReferenceDescriptor(attributeValue.getClass(), session);
424         }
425     }
426
427     /**
428      * INTERNAL:
429      * Initialize the reference descriptor.
430      */

431     public void initialize(AbstractSession session) throws DescriptorException {
432         super.initialize(session);
433
434         if (getReferenceClass() == null) {
435             throw DescriptorException.referenceClassNotSpecified(this);
436         }
437
438         setReferenceDescriptor(session.getDescriptor(getReferenceClass()));
439
440         ClassDescriptor refDescriptor = this.getReferenceDescriptor();
441         if (refDescriptor == null) {
442             session.getIntegrityChecker().handleError(DescriptorException.descriptorIsMissing(getReferenceClass().getName(), this));
443         }
444         if (refDescriptor.isAggregateDescriptor()) {
445             refDescriptor.checkInheritanceTreeAggregateSettings(session, this);
446         } else {
447             session.getIntegrityChecker().handleError(DescriptorException.referenceDescriptorIsNotAggregate(getReferenceClass().getName(), this));
448         }
449     }
450
451     /**
452      * INTERNAL:
453      * Related mapping should implement this method to return true.
454      */

455     public boolean isAggregateMapping() {
456         return true;
457     }
458
459     /**
460      * INTERNAL:
461      * Iterate on the appropriate attribute value.
462      */

463     public void iterate(DescriptorIterator iterator) {
464         iterateOnAttributeValue(iterator, getAttributeValueFromObject(iterator.getVisitedParent()));
465     }
466
467     /**
468      * Iterate on the specified attribute value.
469      */

470     protected void iterateOnAttributeValue(DescriptorIterator iterator, Object JavaDoc attributeValue) {
471         iterator.iterateForAggregateMapping(attributeValue, this, getReferenceDescriptor(attributeValue, iterator.getSession()));
472     }
473
474     /**
475      * Merge the attribute values.
476      */

477     protected void mergeAttributeValue(Object JavaDoc targetAttributeValue, boolean isTargetUnInitialized, Object JavaDoc sourceAttributeValue, MergeManager mergeManager) {
478         // don't merge read-only attributes
479
if (mergeManager.getSession().isClassReadOnly(sourceAttributeValue.getClass())) {
480             return;
481         }
482         if (mergeManager.getSession().isClassReadOnly(targetAttributeValue.getClass())) {
483             return;
484         }
485
486         getObjectBuilder(sourceAttributeValue, mergeManager.getSession()).mergeIntoObject(targetAttributeValue, isTargetUnInitialized, sourceAttributeValue, mergeManager);
487     }
488
489     /**
490      * INTERNAL:
491      * Merge changes from the source to the target object.
492      * With aggregates the merge must cascade to the object changes for the aggregate object
493      * because aggregate objects have no identity outside of themselves.
494      * The actual aggregate object does not need to be replaced, because even if the clone references
495      * another aggregate it appears the same to TopLink
496      */

497     public void mergeChangesIntoObject(Object JavaDoc target, ChangeRecord changeRecord, Object JavaDoc source, MergeManager mergeManager) {
498         ObjectChangeSet aggregateChangeSet = (ObjectChangeSet)((AggregateChangeRecord)changeRecord).getChangedObject();
499         if (aggregateChangeSet == null) {// the change was to set the value to null
500
setAttributeValueInObject(target, null);
501             return;
502         }
503
504         Object JavaDoc sourceAggregate = null;
505         if (source != null) {
506             sourceAggregate = getAttributeValueFromObject(source);
507         }
508         ObjectBuilder objectBuilder = getObjectBuilderForClass(aggregateChangeSet.getClassType(mergeManager.getSession()), mergeManager.getSession());
509         //Bug#4719341 Always obtain aggregate attribute value from the target object regardless of new or not
510
Object JavaDoc targetAggregate = getAttributeValueFromObject(target);
511         if (targetAggregate == null) {
512             targetAggregate = objectBuilder.buildNewInstance();
513         } else {
514             if ((sourceAggregate != null) && (sourceAggregate.getClass() != targetAggregate.getClass())) {
515                 targetAggregate = objectBuilder.buildNewInstance();
516             }
517         }
518         objectBuilder.mergeChangesIntoObject(targetAggregate, aggregateChangeSet, sourceAggregate, mergeManager);
519         setAttributeValueInObject(target, targetAggregate);
520     }
521
522     /**
523      * INTERNAL:
524      * Merge changes from the source to the target object. This merge is only called when a changeSet for the target
525      * does not exist or the target is uninitialized
526      */

527     public void mergeIntoObject(Object JavaDoc target, boolean isTargetUnInitialized, Object JavaDoc source, MergeManager mergeManager) {
528         Object JavaDoc sourceAttributeValue = getAttributeValueFromObject(source);
529         if (sourceAttributeValue == null) {
530             setAttributeValueInObject(target, null);
531             return;
532         }
533
534         Object JavaDoc targetAttributeValue = getAttributeValueFromObject(target);
535         if (targetAttributeValue == null) {
536             // avoid null-pointer/nothing to merge to - create a new instance
537
// (a new clone cannot be used as all changes must be merged)
538
targetAttributeValue = buildNewMergeInstanceOf(sourceAttributeValue, mergeManager.getSession());
539             mergeAttributeValue(targetAttributeValue, true, sourceAttributeValue, mergeManager);
540             // setting new instance so fire event as if set was called by user.
541
// this call will eventually get passed to updateChangeRecord which will
542
//ensure this new aggregates is fully initilized with listeners.
543
this.getDescriptor().getObjectChangePolicy().raiseInternalPropertyChangeEvent(target, getAttributeName(), getAttributeValueFromObject(target), targetAttributeValue);
544             
545         } else {
546             mergeAttributeValue(targetAttributeValue, isTargetUnInitialized, sourceAttributeValue, mergeManager);
547         }
548
549         // allow setter to re-morph any changes...
550
setAttributeValueInObject(target, targetAttributeValue);
551     }
552
553     /**
554      * INTERNAL:
555      * The message is passed to its reference class descriptor.
556      */

557     public void postDelete(DeleteObjectQuery query) throws DatabaseException, OptimisticLockException {
558         if (!isReadOnly()) {
559             postDeleteAttributeValue(query, getAttributeValueFromObject(query.getObject()));
560         }
561     }
562
563     /**
564      * INTERNAL:
565      * The message is passed to its reference class descriptor.
566      */

567     protected void postDeleteAttributeValue(DeleteObjectQuery query, Object JavaDoc attributeValue) throws DatabaseException, OptimisticLockException {
568         if (attributeValue == null) {
569             return;
570         }
571         DeleteObjectQuery aggregateQuery = buildAggregateDeleteQuery(query, attributeValue);
572         getQueryManager(attributeValue, query.getSession()).postDelete(aggregateQuery);
573         executeEvent(DescriptorEventManager.PostDeleteEvent, aggregateQuery);
574     }
575
576     /**
577      * INTERNAL:
578      * The message is passed to its reference class descriptor.
579      */

580     public void postInsert(WriteObjectQuery query) throws DatabaseException, OptimisticLockException {
581         if (!isReadOnly()) {
582             postInsertAttributeValue(query, getAttributeValueFromObject(query.getObject()));
583         }
584     }
585
586     /**
587      * INTERNAL:
588      * The message is passed to its reference class descriptor.
589      */

590     protected void postInsertAttributeValue(WriteObjectQuery query, Object JavaDoc attributeValue) throws DatabaseException, OptimisticLockException {
591         if (attributeValue == null) {
592             return;
593         }
594         WriteObjectQuery aggregateQuery = buildAggregateWriteQuery(query, attributeValue);
595         getQueryManager(attributeValue, query.getSession()).postInsert(aggregateQuery);
596         executeEvent(DescriptorEventManager.PostInsertEvent, aggregateQuery);
597         // aggregates do not actually use a query to write to the database so the post write must be called here
598
executeEvent(DescriptorEventManager.PostWriteEvent, aggregateQuery);
599     }
600
601     /**
602      * INTERNAL:
603      * The message is passed to its reference class descriptor.
604      */

605     public void postUpdate(WriteObjectQuery query) throws DatabaseException, OptimisticLockException {
606         if (!isReadOnly()) {
607             postUpdateAttributeValue(query, getAttributeValueFromObject(query.getObject()));
608         }
609     }
610
611     /**
612      * INTERNAL:
613      * The message is passed to its reference class descriptor.
614      */

615     protected void postUpdateAttributeValue(WriteObjectQuery query, Object JavaDoc attributeValue) throws DatabaseException, OptimisticLockException {
616         if (attributeValue == null) {
617             return;
618         }
619         ObjectChangeSet changeSet = null;
620         UnitOfWorkChangeSet uowChangeSet = null;
621         if (query.getSession().isUnitOfWork() && (((UnitOfWorkImpl)query.getSession()).getUnitOfWorkChangeSet() != null)) {
622             uowChangeSet = (UnitOfWorkChangeSet)((UnitOfWorkImpl)query.getSession()).getUnitOfWorkChangeSet();
623             changeSet = (ObjectChangeSet)uowChangeSet.getObjectChangeSetForClone(attributeValue);
624         }
625         WriteObjectQuery aggregateQuery = buildAggregateWriteQuery(query, attributeValue);
626         aggregateQuery.setObjectChangeSet(changeSet);
627         getQueryManager(attributeValue, query.getSession()).postUpdate(aggregateQuery);
628         executeEvent(DescriptorEventManager.PostUpdateEvent, aggregateQuery);
629         // aggregates do not actually use a query to write to the database so the post write must be called here
630
executeEvent(DescriptorEventManager.PostWriteEvent, aggregateQuery);
631     }
632
633     /**
634      * INTERNAL:
635      * The message is passed to its reference class descriptor.
636      */

637     public void preDelete(DeleteObjectQuery query) throws DatabaseException, OptimisticLockException {
638         if (!isReadOnly()) {
639             preDeleteAttributeValue(query, getAttributeValueFromObject(query.getObject()));
640         }
641     }
642
643     /**
644      * INTERNAL:
645      * The message is passed to its reference class descriptor.
646      */

647     protected void preDeleteAttributeValue(DeleteObjectQuery query, Object JavaDoc attributeValue) throws DatabaseException, OptimisticLockException {
648         if (attributeValue == null) {
649             return;
650         }
651         DeleteObjectQuery aggregateQuery = buildAggregateDeleteQuery(query, attributeValue);
652         executeEvent(DescriptorEventManager.PreDeleteEvent, aggregateQuery);
653         getQueryManager(attributeValue, query.getSession()).preDelete(aggregateQuery);
654     }
655
656     /**
657      * INTERNAL:
658      * The message is passed to its reference class descriptor.
659      */

660     public void preInsert(WriteObjectQuery query) throws DatabaseException, OptimisticLockException {
661         if (!isReadOnly()) {
662             preInsertAttributeValue(query, getAttributeValueFromObject(query.getObject()));
663         }
664     }
665
666     /**
667      * INTERNAL:
668      * The message is passed to its reference class descriptor.
669      */

670     protected void preInsertAttributeValue(WriteObjectQuery query, Object JavaDoc attributeValue) throws DatabaseException, OptimisticLockException {
671         if (attributeValue == null) {
672             return;
673         }
674         WriteObjectQuery aggregateQuery = buildAggregateWriteQuery(query, attributeValue);
675         getQueryManager(attributeValue, query.getSession()).preInsert(aggregateQuery);
676     }
677
678     /**
679      * INTERNAL:
680      * The message is passed to its reference class descriptor.
681      */

682     public void preUpdate(WriteObjectQuery query) throws DatabaseException, OptimisticLockException {
683         if (!isReadOnly()) {
684             preUpdateAttributeValue(query, getAttributeValueFromObject(query.getObject()));
685         }
686     }
687
688     /**
689      * INTERNAL:
690      * The message is passed to its reference class descriptor.
691      */

692     protected void preUpdateAttributeValue(WriteObjectQuery query, Object JavaDoc attributeValue) throws DatabaseException, OptimisticLockException {
693         if (attributeValue == null) {
694             return;
695         }
696         WriteObjectQuery aggregateQuery = buildAggregateWriteQuery(query, attributeValue);
697         ObjectChangeSet changeSet = null;
698         UnitOfWorkChangeSet uowChangeSet = null;
699         if (query.getSession().isUnitOfWork() && (((UnitOfWorkImpl)query.getSession()).getUnitOfWorkChangeSet() != null)) {
700             uowChangeSet = (UnitOfWorkChangeSet)((UnitOfWorkImpl)query.getSession()).getUnitOfWorkChangeSet();
701             changeSet = (ObjectChangeSet)uowChangeSet.getObjectChangeSetForClone(aggregateQuery.getObject());
702         }
703
704         aggregateQuery.setObjectChangeSet(changeSet);
705         // aggregates do not actually use a query to write to the database so the pre-write must be called here
706
if (changeSet == null) {// then we didn't fire events at calculations
707
executeEvent(DescriptorEventManager.PreWriteEvent, aggregateQuery);
708             executeEvent(DescriptorEventManager.PreUpdateEvent, aggregateQuery);
709         }
710         getQueryManager(attributeValue, query.getSession()).preUpdate(aggregateQuery);
711     }
712
713     /**
714      * PUBLIC:
715      * This is a reference class whose instances this mapping will store in the domain objects.
716      */

717     public void setReferenceClass(Class JavaDoc aClass) {
718         referenceClass = aClass;
719     }
720
721     /**
722      * INTERNAL:
723      * Used by MW.
724      */

725     public void setReferenceClassName(String JavaDoc aClassName) {
726         referenceClassName = aClassName;
727     }
728
729     /**
730      * INTERNAL:
731      * Set the referenceDescriptor. This is a descriptor which is associated with
732      * the reference class.
733      */

734     protected void setReferenceDescriptor(ClassDescriptor aDescriptor) {
735         referenceDescriptor = aDescriptor;
736     }
737
738     /**
739      * INTERNAL:
740      * Either create a new change record or update the change record with the new value.
741      * This is used by attribute change tracking. Only call this method if a different aggregate
742      * is being set in the object, if the aggregate itself is being updated then call the
743      * update on the corresponding mapping within the aggregate.
744      */

745     public void updateChangeRecord(Object JavaDoc sourceClone, Object JavaDoc newValue, Object JavaDoc oldValue, ObjectChangeSet objectChangeSet, UnitOfWorkImpl uow) throws DescriptorException {
746         //This method will be called when either the referenced aggregate has
747
//been changed or a component of the referenced aggregate has been changed
748
//this case is determined by the value of the sourceClone
749

750         AggregateChangeRecord changeRecord = (AggregateChangeRecord)objectChangeSet.getChangesForAttributeNamed(this.getAttributeName());
751         if (changeRecord == null){
752             changeRecord = new AggregateChangeRecord(objectChangeSet);
753             changeRecord.setAttribute(this.getAttributeName());
754             changeRecord.setMapping(this);
755             objectChangeSet.addChange(changeRecord);
756         }
757         
758         if ( sourceClone.getClass().equals(objectChangeSet.getClassType(uow)) ) {
759             // event was fired on the parent to the aggregate, the attribute value changed.
760
ClassDescriptor referenceDescriptor = getReferenceDescriptor(newValue, uow);
761             if ( newValue == null ) { // attribute set to null
762
changeRecord.setChangedObject(null);
763                 return;
764             }else{ // attribute set to new aggregate
765
UnitOfWorkChangeSet uowChangeSet = (UnitOfWorkChangeSet)objectChangeSet.getUOWChangeSet();
766                 //force comparison change detection to build changeset.
767
ObjectChangeSet aggregateChangeSet = (ObjectChangeSet)uowChangeSet.getObjectChangeSetForClone(newValue);
768                 if (aggregateChangeSet != null) {
769                     aggregateChangeSet.clear(); // old differences must be thrown away because difference is between old value and new value
770
}
771                 //force comparison change detection to build changeset.
772
changeRecord.setChangedObject(referenceDescriptor.getObjectChangePolicy().createObjectChangeSetThroughComparison(newValue,oldValue, uowChangeSet, (oldValue == null), uow, referenceDescriptor));
773                 referenceDescriptor.getObjectChangePolicy().setChangeSetOnListener((ObjectChangeSet)changeRecord.getChangedObject(), newValue);
774
775             }
776         } else {
777             // not tracked at attribute level, lets force build a changeset then.
778
changeRecord.setChangedObject(referenceDescriptor.getObjectChangePolicy().createObjectChangeSetThroughComparison(sourceClone, null, (UnitOfWorkChangeSet)objectChangeSet.getUOWChangeSet(), true, uow, referenceDescriptor));
779         }
780     }
781     
782     /**
783      * INTERNAL:
784      * Return whether the specified object and all its components have been deleted.
785      */

786     public boolean verifyDelete(Object JavaDoc object, AbstractSession session) throws DatabaseException {
787         return verifyDeleteOfAttributeValue(getAttributeValueFromObject(object), session);
788     }
789
790     /**
791      * INTERNAL:
792      * Return whether the specified object and all its components have been deleted.
793      */

794     protected boolean verifyDeleteOfAttributeValue(Object JavaDoc attributeValue, AbstractSession session) throws DatabaseException {
795         if (attributeValue == null) {
796             return true;
797         }
798         for (Enumeration mappings = getReferenceDescriptor(attributeValue, session).getMappings().elements();
799                  mappings.hasMoreElements();) {
800             DatabaseMapping mapping = (DatabaseMapping)mappings.nextElement();
801             if (!mapping.verifyDelete(attributeValue, session)) {
802                 return false;
803             }
804         }
805         return true;
806     }
807 }
808
Popular Tags