KickJava   Java API By Example, From Geeks To Geeks.

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


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.expressions.*;
27 import oracle.toplink.essentials.internal.helper.*;
28 import oracle.toplink.essentials.internal.queryframework.JoinedAttributeManager;
29 import oracle.toplink.essentials.internal.sessions.*;
30 import oracle.toplink.essentials.internal.descriptors.ObjectBuilder;
31 import oracle.toplink.essentials.sessions.DatabaseRecord;
32 import oracle.toplink.essentials.queryframework.*;
33 import oracle.toplink.essentials.internal.sessions.AbstractRecord;
34 import oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl;
35 import oracle.toplink.essentials.internal.sessions.AbstractSession;
36 import oracle.toplink.essentials.descriptors.ClassDescriptor;
37
38 /**
39  * <p><b>Purpose</b>:Two objects can be considered to be related by aggregation if there is a strict
40  * 1:1 relationship between the objects. This means that if the source (parent)
41  * object exists, then the target (child or owned) object must exist.
42  *
43  * In TopLink, it also means the data for the owned object is in the same table as
44  * the parent.
45  *
46  * @author Sati
47  * @since TOPLink/Java 1.0
48  */

49 public class AggregateObjectMapping extends AggregateMapping implements RelationalMapping {
50
51     /**
52      * If <em>all</em> the fields in the database row for the aggregate object are NULL,
53      * then, by default, TopLink will place a null in the appropriate source object
54      * (as opposed to an aggregate object filled with nulls).
55      * To change this behavior, set the value of this variable to false. Then TopLink
56      * will build a new instance of the aggregate object that is filled with nulls
57      * and place it in the source object.
58      */

59     protected boolean isNullAllowed;
60
61     /** Map the name of a field in the aggregate descriptor to a field in the source table. */
62     protected transient Map aggregateToSourceFieldNames;
63
64     /**
65      * Default constructor.
66      */

67     public AggregateObjectMapping() {
68         aggregateToSourceFieldNames = new HashMap(5);
69         isNullAllowed = true;
70     }
71
72     /**
73      * INTERNAL:
74      */

75     public boolean isRelationalMapping() {
76         return true;
77     }
78
79     /**
80      * PUBLIC:
81      * Add a field name translation that maps from a field name in the
82      * source table to a field name in the aggregate descriptor.
83      */

84     public void addFieldNameTranslation(String JavaDoc sourceFieldName, String JavaDoc aggregateFieldName) {
85         String JavaDoc unQualifiedAggregateFieldName = aggregateFieldName.substring(aggregateFieldName.lastIndexOf('.') + 1);// -1 is returned for no ".".
86
getAggregateToSourceFieldNames().put(unQualifiedAggregateFieldName, sourceFieldName);
87     }
88
89     /**
90      * INTERNAL:
91      * Return whether all the aggregate fields in the specified
92      * row are NULL.
93      */

94     protected boolean allAggregateFieldsAreNull(AbstractRecord databaseRow) {
95         for (Enumeration fieldsEnum = getReferenceFields().elements();
96                  fieldsEnum.hasMoreElements();) {
97             DatabaseField field = (DatabaseField)fieldsEnum.nextElement();
98             Object JavaDoc value = databaseRow.get(field);
99             if (value != null) {
100                 return false;
101             }
102         }
103         return true;
104     }
105
106     /**
107      * PUBLIC:
108      * If <em>all</em> the fields in the database row for the aggregate object are NULL,
109      * then, by default, TopLink will place a null in the appropriate source object
110      * (as opposed to an aggregate object filled with nulls). This behavior can be
111      * explicitly set by calling #allowNull().
112      * To change this behavior, call #dontAllowNull(). Then TopLink
113      * will build a new instance of the aggregate object that is filled with nulls
114      * and place it in the source object.
115      * In either situation, when writing, TopLink will place a NULL in all the
116      * fields in the database row for the aggregate object.
117      */

118     public void allowNull() {
119         setIsNullAllowed(true);
120     }
121
122     /**
123      * INTERNAL:
124      * Return whether the query's backup object has an attribute
125      * value of null.
126      */

127     protected boolean backupAttributeValueIsNull(WriteObjectQuery query) {
128         if (query.getSession().isUnitOfWork()) {
129             Object JavaDoc backupAttributeValue = getAttributeValueFromObject(query.getBackupClone());
130             if (backupAttributeValue == null) {
131                 return true;
132             }
133         }
134         return false;
135     }
136
137     /**
138      * INTERNAL:
139      * Build and return an aggregate object from the specified row.
140      * If a null value is allowed and
141      * all the appropriate fields in the row are NULL, return a null.
142      * Otherwise, simply create a new aggregate object and return it.
143      */

144     protected Object JavaDoc buildAggregateFromRow(AbstractRecord databaseRow, Object JavaDoc targetObject, JoinedAttributeManager joinManager, boolean buildShallowOriginal, AbstractSession session) throws DatabaseException {
145         // check for all NULLs
146
if (isNullAllowed() && allAggregateFieldsAreNull(databaseRow)) {
147             return null;
148         }
149
150         // If refreshing, maintain object identity;
151
// otherwise construct a new aggregate object.
152
Object JavaDoc aggregate = null;
153         ClassDescriptor descriptor = getReferenceDescriptor();
154         boolean refreshing = true;
155         if (descriptor.hasInheritance()) {
156             Class JavaDoc newAggregateClass = descriptor.getInheritancePolicy().classFromRow(databaseRow, session);
157             descriptor = getReferenceDescriptor(newAggregateClass, session);
158
159             if (joinManager.getBaseQuery().shouldRefreshIdentityMapResult()) {
160                 aggregate = getMatchingAttributeValueFromObject(databaseRow, targetObject, session, descriptor);
161                 if ((aggregate != null) && (aggregate.getClass() != newAggregateClass)) {
162                     // if the class has changed out from underneath us, we cannot preserve object identity
163
// build a new instance of the *new* class
164
aggregate = descriptor.getObjectBuilder().buildNewInstance();
165                     refreshing = false;
166                 }
167             }
168         } else {
169             if (joinManager.getBaseQuery().shouldRefreshIdentityMapResult()) {
170                 aggregate = getMatchingAttributeValueFromObject(databaseRow, targetObject, session, descriptor);
171             }
172         }
173
174         if (aggregate == null) {
175             aggregate = descriptor.getObjectBuilder().buildNewInstance();
176             refreshing = false;
177         }
178
179         ObjectBuildingQuery nestedQuery = joinManager.getBaseQuery();
180         nestedQuery.setSession(session); //ensure the correct session is set on the query.
181
if (joinManager.getBaseQuery().isObjectLevelReadQuery()){
182             if (joinManager.isAttributeJoined(getDescriptor(), getAttributeName()) ){
183                 // A nested query must be built to pass to the descriptor that looks like the real query execution would.
184
nestedQuery = (ObjectLevelReadQuery)((ObjectLevelReadQuery)joinManager.getBaseQuery()).deepClone();
185                 // Must cascade the nested partial/join expression and filter the nested ones.
186
((ObjectLevelReadQuery)nestedQuery).getJoinedAttributeManager().setJoinedAttributeExpressions_(extractNestedExpressions(joinManager.getJoinedAttributeExpressions(), joinManager.getBaseExpressionBuilder(), false));
187                 nestedQuery.setDescriptor(descriptor);
188             }
189         }
190         if (buildShallowOriginal) {
191             descriptor.getObjectBuilder().buildAttributesIntoShallowObject(aggregate, databaseRow, nestedQuery);
192         } else if (session.isUnitOfWork()) {
193             descriptor.getObjectBuilder().buildAttributesIntoWorkingCopyClone(aggregate, nestedQuery, joinManager, databaseRow, (UnitOfWorkImpl)session, refreshing);
194         } else {
195             descriptor.getObjectBuilder().buildAttributesIntoObject(aggregate, databaseRow, nestedQuery, joinManager, refreshing);
196         }
197         return aggregate;
198     }
199
200     /**
201      * INTERNAL:
202      * Build and return a database row with all the reference
203      * fields set to nulls.
204      */

205     protected AbstractRecord buildNullReferenceRow() {
206         AbstractRecord result = new DatabaseRecord(getReferenceFields().size());
207         for (Enumeration stream = getReferenceFields().elements(); stream.hasMoreElements();) {
208             result.put((DatabaseField)stream.nextElement(), null);
209         }
210         return result;
211     }
212
213     /**
214      * INTERNAL:
215      * Used to allow object level comparisons.
216      * In the case of an Aggregate which has no primary key must do an attribute
217      * by attribute comparison.
218      */

219     public Expression buildObjectJoinExpression(Expression expression, Object JavaDoc value, AbstractSession session) {
220         Expression attributeByAttributeComparison = null;
221         Expression join = null;
222         Object JavaDoc attributeValue = null;
223
224         // value need not be unwrapped as it is an aggregate, nor should it
225
// influence a call to getReferenceDescriptor.
226
ClassDescriptor referenceDescriptor = getReferenceDescriptor();
227         if ((value != null) && !referenceDescriptor.getJavaClass().isInstance(value)) {
228             throw QueryException.incorrectClassForObjectComparison(expression, value, this);
229         }
230         Enumeration mappings = referenceDescriptor.getMappings().elements();
231         for (; mappings.hasMoreElements();) {
232             DatabaseMapping mapping = (DatabaseMapping)mappings.nextElement();
233             if (value == null) {
234                 attributeValue = null;
235             } else {
236                 attributeValue = mapping.getAttributeValueFromObject(value);
237             }
238             join = expression.get(mapping.getAttributeName()).equal(attributeValue);
239             if (attributeByAttributeComparison == null) {
240                 attributeByAttributeComparison = join;
241             } else {
242                 attributeByAttributeComparison = attributeByAttributeComparison.and(join);
243             }
244         }
245         return attributeByAttributeComparison;
246     }
247
248     /**
249      * INTERNAL:
250      * Used to allow object level comparisons.
251      */

252     public Expression buildObjectJoinExpression(Expression expression, Expression argument, AbstractSession session) {
253         Expression attributeByAttributeComparison = null;
254
255         //Enumeration mappingsEnum = getSourceToTargetKeyFields().elements();
256
Enumeration mappingsEnum = getReferenceDescriptor().getMappings().elements();
257         for (; mappingsEnum.hasMoreElements();) {
258             DatabaseMapping mapping = (DatabaseMapping)mappingsEnum.nextElement();
259             String JavaDoc attributeName = mapping.getAttributeName();
260             Expression join = expression.get(attributeName).equal(argument.get(attributeName));
261             if (attributeByAttributeComparison == null) {
262                 attributeByAttributeComparison = join;
263             } else {
264                 attributeByAttributeComparison = attributeByAttributeComparison.and(join);
265             }
266         }
267         return attributeByAttributeComparison;
268     }
269
270     /**
271      * INTERNAL:
272      * Build and return a database row built with the values from
273      * the specified attribute value.
274      */

275     protected AbstractRecord buildRowFromAggregate(Object JavaDoc object, Object JavaDoc attributeValue, AbstractSession session) throws DescriptorException {
276         return buildRowFromAggregate(object, attributeValue, session, false);
277     }
278
279     /**
280      * INTERNAL:
281      * Build and return a database row built with the values from
282      * the specified attribute value.
283      */

284     protected AbstractRecord buildRowFromAggregate(Object JavaDoc object, Object JavaDoc attributeValue, AbstractSession session, boolean forceWriteOfReadOnlyClasses) throws DescriptorException {
285         if (attributeValue == null) {
286             if (isNullAllowed()) {
287                 return buildNullReferenceRow();
288             } else {
289                 throw DescriptorException.nullForNonNullAggregate(object, this);
290             }
291         } else {
292             if ((!forceWriteOfReadOnlyClasses) && (session.isClassReadOnly(attributeValue.getClass()))) {
293                 return new DatabaseRecord(1);
294             } else {
295                 return getObjectBuilder(attributeValue, session).buildRow(attributeValue, session);
296             }
297         }
298     }
299
300     /**
301      * INTERNAL:
302      * Build and return a database row built with the values from
303      * the specified attribute value.
304      */

305     protected AbstractRecord buildRowFromAggregateWithChangeRecord(ChangeRecord changeRecord, ObjectChangeSet objectChangeSet, AbstractSession session) throws DescriptorException {
306         return buildRowFromAggregateWithChangeRecord(changeRecord, objectChangeSet, session, false);
307     }
308
309     /**
310      * INTERNAL:
311      * Build and return a database row built with the values from
312      * the specified attribute value.
313      */

314     protected AbstractRecord buildRowFromAggregateWithChangeRecord(ChangeRecord changeRecord, ObjectChangeSet objectChangeSet, AbstractSession session, boolean forceWriteOfReadOnlyClasses) throws DescriptorException {
315         if (objectChangeSet == null) {
316             if (isNullAllowed()) {
317                 return buildNullReferenceRow();
318             } else {
319                 Object JavaDoc object = ((ObjectChangeSet)changeRecord.getOwner()).getUnitOfWorkClone();
320                 throw DescriptorException.nullForNonNullAggregate(object, this);
321             }
322         } else {
323             if ((!forceWriteOfReadOnlyClasses) && (session.isClassReadOnly(objectChangeSet.getClassType(session)))) {
324                 return new DatabaseRecord(1);
325             } else {
326                 return getReferenceDescriptor(objectChangeSet.getClassType(session), session).getObjectBuilder().buildRowWithChangeSet(objectChangeSet, session);
327             }
328         }
329     }
330
331     /**
332      * INTERNAL:
333      * Build and return a database row built with the changed values from
334      * the specified attribute value.
335      */

336     protected AbstractRecord buildRowFromAggregateForUpdate(WriteObjectQuery query, Object JavaDoc attributeValue) throws DescriptorException {
337         if (attributeValue == null) {
338             if (isNullAllowed()) {
339                 if (backupAttributeValueIsNull(query)) {
340                     return new DatabaseRecord(1);// both attributes are null - no update required
341
} else {
342                     return buildNullReferenceRow();
343                 }
344             } else {
345                 throw DescriptorException.nullForNonNullAggregate(query.getObject(), this);
346             }
347         } else if ((query.getBackupClone() != null) && ((getMatchingBackupAttributeValue(query, attributeValue) == null) || !(attributeValue.getClass().equals(getMatchingBackupAttributeValue(query, attributeValue).getClass())))) {
348             return getObjectBuilder(attributeValue, query.getSession()).buildRow(attributeValue, query.getSession());
349         } else {
350             if (query.getSession().isClassReadOnly(attributeValue.getClass())) {
351                 return new DatabaseRecord(1);
352             }
353             WriteObjectQuery clonedQuery = (WriteObjectQuery)query.clone();
354             clonedQuery.setObject(attributeValue);
355             if (query.getSession().isUnitOfWork()) {
356                 Object JavaDoc backupAttributeValue = getMatchingBackupAttributeValue(query, attributeValue);
357                 if (backupAttributeValue == null) {
358                     backupAttributeValue = getObjectBuilder(attributeValue, query.getSession()).buildNewInstance();
359                 }
360                 clonedQuery.setBackupClone(backupAttributeValue);
361             }
362             return getObjectBuilder(attributeValue, query.getSession()).buildRowForUpdate(clonedQuery);
363         }
364     }
365
366     /**
367      * INTERNAL:
368      * Clone the attribute from the original and assign it to the clone.
369      */

370     public void buildClone(Object JavaDoc original, Object JavaDoc clone, UnitOfWorkImpl unitOfWork, JoinedAttributeManager joinedAttributeManager) {
371         Object JavaDoc attributeValue = getAttributeValueFromObject(original);
372         Object JavaDoc aggregateClone = buildClonePart(original, attributeValue, unitOfWork);
373
374         if (aggregateClone != null) {
375             ClassDescriptor descriptor = getReferenceDescriptor(aggregateClone, unitOfWork);
376             descriptor.getObjectChangePolicy().setAggregateChangeListener(clone, aggregateClone, unitOfWork, descriptor, getAttributeName());
377         }
378
379         setAttributeValueInObject(clone, aggregateClone);
380     }
381
382     /**
383      * INTERNAL:
384      * A combination of readFromRowIntoObject and buildClone.
385      * <p>
386      * buildClone assumes the attribute value exists on the original and can
387      * simply be copied.
388      * <p>
389      * readFromRowIntoObject assumes that one is building an original.
390      * <p>
391      * Both of the above assumptions are false in this method, and actually
392      * attempts to do both at the same time.
393      * <p>
394      * Extract value from the row and set the attribute to this value in the
395      * working copy clone.
396      * In order to bypass the shared cache when in transaction a UnitOfWork must
397      * be able to populate working copies directly from the row.
398      */

399     public void buildCloneFromRow(AbstractRecord databaseRow, JoinedAttributeManager joinManager, Object JavaDoc clone, ObjectBuildingQuery sourceQuery, UnitOfWorkImpl unitOfWork, AbstractSession executionSession) {
400         // This method is a combination of buildggregateFromRow and
401
// buildClonePart on the super class.
402
// none of buildClonePart used, as not an orignal new object, nor
403
// do we worry about creating heavy clones for aggregate objects.
404
Object JavaDoc clonedAttributeValue = buildAggregateFromRow(databaseRow, clone, joinManager, false, executionSession);
405         ClassDescriptor descriptor = getReferenceDescriptor(clonedAttributeValue, unitOfWork);
406         descriptor.getObjectChangePolicy().setAggregateChangeListener(clone, clonedAttributeValue, unitOfWork, descriptor, getAttributeName());
407         setAttributeValueInObject(clone, clonedAttributeValue);
408         return;
409     }
410
411     /**
412      * INTERNAL:
413      * Builds a shallow original object. Only direct attributes and primary
414      * keys are populated. In this way the minimum original required for
415      * instantiating a working copy clone can be built without placing it in
416      * the shared cache (no concern over cycles).
417      */

418     public void buildShallowOriginalFromRow(AbstractRecord databaseRow, Object JavaDoc original, JoinedAttributeManager joinManager, AbstractSession executionSession) {
419         Object JavaDoc aggregate = buildAggregateFromRow(databaseRow, original, joinManager, true, executionSession);// shallow only.
420
setAttributeValueInObject(original, aggregate);
421     }
422
423     /**
424      * INTERNAL:
425      * Build and return a "template" database row with all the fields
426      * set to null.
427      */

428     protected AbstractRecord buildTemplateInsertRow(AbstractSession session) {
429         AbstractRecord result = getReferenceDescriptor().getObjectBuilder().buildTemplateInsertRow(session);
430         List processedMappings = (List)getReferenceDescriptor().getMappings().clone();
431         if (getReferenceDescriptor().hasInheritance()) {
432             Enumeration children = getReferenceDescriptor().getInheritancePolicy().getChildDescriptors().elements();
433             while (children.hasMoreElements()) {
434                 Enumeration mappings = ((ClassDescriptor)children.nextElement()).getMappings().elements();
435                 while (mappings.hasMoreElements()) {
436                     DatabaseMapping mapping = (DatabaseMapping)mappings.nextElement();
437
438                     // Only write mappings once.
439
if (!processedMappings.contains(mapping)) {
440                         mapping.writeInsertFieldsIntoRow(result, session);
441                         processedMappings.add(mapping);
442                     }
443                 }
444             }
445         }
446         return result;
447     }
448
449     /**
450      * INTERNAL:
451      * Cascade perform delete through mappings that require the cascade
452      */

453     public void cascadePerformRemoveIfRequired(Object JavaDoc object, UnitOfWorkImpl uow, IdentityHashtable visitedObjects){
454         //objects referenced by this mapping are not registered as they have
455
// no identity, however mappings from the referenced object may need cascading.
456
Object JavaDoc objectReferenced = getRealAttributeValueFromObject(object, uow);
457         if ((objectReferenced == null)){
458             return ;
459         }
460         if ( ! visitedObjects.contains(objectReferenced)){
461             visitedObjects.put(objectReferenced, objectReferenced);
462             ObjectBuilder builder = getReferenceDescriptor(objectReferenced.getClass(), uow).getObjectBuilder();
463             builder.cascadePerformRemove(objectReferenced, uow, visitedObjects);
464         }
465         
466     }
467
468     /**
469      * INTERNAL:
470      * Cascade registerNew for Create through mappings that require the cascade
471      */

472     public void cascadeRegisterNewIfRequired(Object JavaDoc object, UnitOfWorkImpl uow, IdentityHashtable visitedObjects){
473         //aggregate objects are not registered but their mappings should be.
474
Object JavaDoc objectReferenced = getRealAttributeValueFromObject(object, uow);
475         if ( (objectReferenced == null) ){
476             return ;
477         }
478         if ( ! visitedObjects.contains(objectReferenced)){
479             visitedObjects.put(objectReferenced, objectReferenced);
480             ObjectBuilder builder = getReferenceDescriptor(objectReferenced.getClass(), uow).getObjectBuilder();
481             builder.cascadeRegisterNewForCreate(objectReferenced, uow, visitedObjects);
482         }
483     }
484
485     /**
486      * INTERNAL:
487      * Return the fields handled by the mapping.
488      */

489     protected Vector collectFields() {
490         return getReferenceFields();
491     }
492
493     /**
494      * PUBLIC:
495      * If <em>all</em> the fields in the database row for the aggregate object are NULL,
496      * then, by default, TopLink will place a null in the appropriate source object
497      * (as opposed to an aggregate object filled with nulls). This behavior can be
498      * explicitly set by calling #allowNull().
499      * To change this behavior, call #dontAllowNull(). Then TopLink
500      * will build a new instance of the aggregate object that is filled with nulls
501      * and place it in the source object.
502      * In either situation, when writing, TopLink will place a NULL in all the
503      * fields in the database row for the aggregate object.
504      */

505     public void dontAllowNull() {
506         setIsNullAllowed(false);
507     }
508
509     /**
510      * INTERNAL:
511      * Return a collection of the aggregate to source field name associations.
512      */

513     public Vector getAggregateToSourceFieldNameAssociations() {
514         Vector associations = new Vector(getAggregateToSourceFieldNames().size());
515         Iterator aggregateEnum = getAggregateToSourceFieldNames().keySet().iterator();
516         Iterator sourceEnum = getAggregateToSourceFieldNames().values().iterator();
517         while (aggregateEnum.hasNext()) {
518             associations.addElement(new Association(aggregateEnum.next(), sourceEnum.next()));
519         }
520
521         return associations;
522     }
523
524     /**
525      * INTERNAL:
526      * Return the hashtable that stores aggregate field name to source field name.
527      */

528     public Map getAggregateToSourceFieldNames() {
529         return aggregateToSourceFieldNames;
530     }
531
532     /**
533      * INTERNAL:
534      * Return the classification for the field contained in the mapping.
535      * This is used to convert the row value to a consistent Java value.
536      */

537     public Class JavaDoc getFieldClassification(DatabaseField fieldToClassify) {
538         DatabaseMapping mapping = getReferenceDescriptor().getObjectBuilder().getMappingForField(fieldToClassify);
539         if (mapping == null) {
540             return null;// Means that the mapping is read-only
541
}
542         return mapping.getFieldClassification(fieldToClassify);
543     }
544
545     /**
546      * INTERNAL:
547      * This is used to preserve object identity during a refreshObject()
548      * query. Return the object corresponding to the specified database row.
549      * The default is to simply return the attribute value.
550      */

551     protected Object JavaDoc getMatchingAttributeValueFromObject(AbstractRecord row, Object JavaDoc targetObject, AbstractSession session, ClassDescriptor descriptor) {
552         return getAttributeValueFromObject(targetObject);
553     }
554
555     /**
556      * INTERNAL:
557      * This is used to match up objects during an update in a UOW.
558      * Return the object corresponding to the specified attribute value.
559      * The default is to simply return the backup attribute value.
560      */

561     protected Object JavaDoc getMatchingBackupAttributeValue(WriteObjectQuery query, Object JavaDoc attributeValue) {
562         return getAttributeValueFromObject(query.getBackupClone());
563     }
564
565     /**
566      * INTERNAL:
567      * Since aggregate object mappings clone their descriptors, for inheritance the correct child clone must be found.
568      */

569     protected ClassDescriptor getReferenceDescriptor(Class JavaDoc theClass, AbstractSession session) {
570         if (getReferenceDescriptor().getJavaClass().equals(theClass)) {
571             return getReferenceDescriptor();
572         }
573
574         ClassDescriptor subclassDescriptor = getReferenceDescriptor().getInheritancePolicy().getSubclassDescriptor(theClass);
575         if (subclassDescriptor == null) {
576             throw DescriptorException.noSubClassMatch(theClass, this);
577         } else {
578             return subclassDescriptor;
579         }
580     }
581
582     /**
583      * INTERNAL:
584      * Return the fields used to build the aggregate object.
585      */

586     protected Vector getReferenceFields() {
587         return getReferenceDescriptor().getAllFields();
588     }
589
590     /**
591      * INTERNAL:
592      * Return if the mapping has any ownership or other dependency over its target object(s).
593      */

594     public boolean hasDependency() {
595         return getReferenceDescriptor().hasDependencyOnParts();
596     }
597
598     /**
599      * INTERNAL:
600      * For an aggregate mapping the reference descriptor is cloned. The cloned descriptor is then
601      * assigned primary keys and table names before initialize. Once the cloned descriptor is initialized
602      * it is assigned as reference descriptor in the aggregate mapping. This is a very specific
603      * behaviour for aggregate mappings. The original descriptor is used only for creating clones and
604      * after that the aggregate mapping never uses it.
605      * Some initialization is done in postInitialize to ensure the target descriptor's references are initialized.
606      */

607     public void initialize(AbstractSession session) throws DescriptorException {
608         super.initialize(session);
609
610         ClassDescriptor clonedDescriptor = (ClassDescriptor)getReferenceDescriptor().clone();
611         if (clonedDescriptor.isChildDescriptor()) {
612             ClassDescriptor parentDescriptor = session.getDescriptor(clonedDescriptor.getInheritancePolicy().getParentClass());
613             initializeParentInheritance(parentDescriptor, clonedDescriptor, session);
614         }
615         setReferenceDescriptor(clonedDescriptor);
616
617         initializeReferenceDescriptor(clonedDescriptor);
618         clonedDescriptor.preInitialize(session);
619         clonedDescriptor.initialize(session);
620         translateFields(clonedDescriptor, session);
621
622         if (clonedDescriptor.hasInheritance() && clonedDescriptor.getInheritancePolicy().hasChildren()) {
623             //clone child descriptors
624
initializeChildInheritance(clonedDescriptor, session);
625         }
626
627         setFields(collectFields());
628     }
629
630     /**
631      * INTERNAL:
632      * For an aggregate mapping the reference descriptor is cloned.
633      * If the reference descriptor is involved in an inheritance tree,
634      * all the parent and child descriptors are cloned also.
635      * The cloned descriptors are then assigned primary keys and
636      * table names before initialize.
637      * This is a very specific behaviour for aggregate mappings.
638      */

639     public void initializeChildInheritance(ClassDescriptor parentDescriptor, AbstractSession session) throws DescriptorException {
640         //recursive call to the further childern descriptors
641
if (parentDescriptor.getInheritancePolicy().hasChildren()) {
642             //setFields(clonedChildDescriptor.getFields());
643
Vector childDescriptors = parentDescriptor.getInheritancePolicy().getChildDescriptors();
644             Vector cloneChildDescriptors = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance();
645             for (Enumeration enumtr = childDescriptors.elements(); enumtr.hasMoreElements();) {
646                 ClassDescriptor clonedChildDescriptor = (ClassDescriptor)((ClassDescriptor)enumtr.nextElement()).clone();
647                 clonedChildDescriptor.getInheritancePolicy().setParentDescriptor(parentDescriptor);
648                 initializeReferenceDescriptor(clonedChildDescriptor);
649                 clonedChildDescriptor.preInitialize(session);
650                 clonedChildDescriptor.initialize(session);
651                 translateFields(clonedChildDescriptor, session);
652                 cloneChildDescriptors.addElement(clonedChildDescriptor);
653                 initializeChildInheritance(clonedChildDescriptor, session);
654             }
655             parentDescriptor.getInheritancePolicy().setChildDescriptors(cloneChildDescriptors);
656         }
657     }
658
659     /**
660      * INTERNAL:
661      * For an aggregate mapping the reference descriptor is cloned.
662      * If the reference descriptor is involved in an inheritance tree,
663      * all the parent and child descriptors are cloned also.
664      * The cloned descriptors are then assigned primary keys and
665      * table names before initialize.
666      * This is a very specific behaviour for aggregate mappings.
667      */

668     public void initializeParentInheritance(ClassDescriptor parentDescriptor, ClassDescriptor childDescriptor, AbstractSession session) throws DescriptorException {
669         ClassDescriptor clonedParentDescriptor = (ClassDescriptor)parentDescriptor.clone();
670
671         //recursive call to the further parent descriptors
672
if (clonedParentDescriptor.getInheritancePolicy().isChildDescriptor()) {
673             ClassDescriptor parentToParentDescriptor = session.getDescriptor(clonedParentDescriptor.getJavaClass());
674             initializeParentInheritance(parentToParentDescriptor, parentDescriptor, session);
675         }
676
677         initializeReferenceDescriptor(clonedParentDescriptor);
678         Vector children = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(1);
679         children.addElement(childDescriptor);
680         clonedParentDescriptor.getInheritancePolicy().setChildDescriptors(children);
681         clonedParentDescriptor.preInitialize(session);
682         clonedParentDescriptor.initialize(session);
683         translateFields(clonedParentDescriptor, session);
684     }
685
686     /**
687      * INTERNAL:
688      * Initialize the cloned reference descriptor with table names and primary keys
689      */

690     protected void initializeReferenceDescriptor(ClassDescriptor clonedDescriptor) {
691         // Must ensure default tables remains the same.
692
clonedDescriptor.setDefaultTable(getDescriptor().getDefaultTable());
693         clonedDescriptor.setTables(getDescriptor().getTables());
694         clonedDescriptor.setPrimaryKeyFields(getDescriptor().getPrimaryKeyFields());
695     }
696
697     /**
698      * INTERNAL:
699      * Related mapping should implement this method to return true.
700      */

701     public boolean isAggregateObjectMapping() {
702         return true;
703     }
704
705     /**
706      * INTERNAL:
707      * Return if this mapping supports change tracking.
708      */

709     public boolean isChangeTrackingSupported() {
710         return false;
711     }
712     
713     /**
714      * INTERNAL
715      * Return true if this mapping supports cascaded version optimistic locking.
716      */

717     public boolean isCascadedLockingSupported() {
718         return true;
719     }
720     
721     /**
722      * INTERNAL:
723      * Return setting.
724      */

725     public boolean isNullAllowed() {
726         return isNullAllowed;
727     }
728
729     /**
730      * INTERNAL:
731      * For an aggregate mapping the reference descriptor is cloned. The cloned descriptor is then
732      * assigned primary keys and table names before initialize. Once the cloned descriptor is initialized
733      * it is assigned as reference descriptor in the aggregate mapping. This is a very specific
734      * behaviour for aggregate mappings. The original descriptor is used only for creating clones and
735      * after that the aggregate mapping never uses it.
736      * Some initialization is done in postInitialize to ensure the target descriptor's references are initialized.
737      */

738     public void postInitialize(AbstractSession session) throws DescriptorException {
739         super.postInitialize(session);
740
741         if (getReferenceDescriptor() != null) {
742             getReferenceDescriptor().postInitialize(session);
743         }
744     }
745
746     /**
747      * INTERNAL:
748      * Build an aggregate object from the specified return row and put it
749      * in the specified target object.
750      * Return row is merged into object after execution of insert or update call
751      * accordiing to ReturningPolicy.
752      */

753     public Object JavaDoc readFromReturnRowIntoObject(AbstractRecord row, Object JavaDoc targetObject, ReadObjectQuery query, Collection handledMappings) throws DatabaseException {
754         Object JavaDoc aggregate = getAttributeValueFromObject(targetObject);
755         if (aggregate == null) {
756             aggregate = readFromRowIntoObject(row, null, targetObject, query);
757             handledMappings.add(this);
758             return aggregate;
759         }
760
761         for (int i = 0; i < getReferenceFields().size(); i++) {
762             DatabaseField field = (DatabaseField)getReferenceFields().elementAt(i);
763             if (row.containsKey(field)) {
764                 getObjectBuilder(aggregate, query.getSession()).assignReturnValueForField(aggregate, query, row, field, handledMappings);
765             }
766         }
767
768         if (isNullAllowed()) {
769             boolean allAttributesNull = true;
770             for (int i = 0; (i < getReferenceFields().size()) && allAttributesNull; i++) {
771                 DatabaseField field = (DatabaseField)fields.elementAt(i);
772                 if (row.containsKey(field)) {
773                     allAttributesNull = row.get(field) == null;
774                 } else {
775                     Object JavaDoc fieldValue = valueFromObject(targetObject, field, query.getSession());
776                     if (fieldValue == null) {
777                         Object JavaDoc baseValue = getDescriptor().getObjectBuilder().getBaseValueForField(field, targetObject);
778                         if (baseValue != null) {
779                             DatabaseMapping baseMapping = getDescriptor().getObjectBuilder().getBaseMappingForField(field);
780                             if (baseMapping.isForeignReferenceMapping()) {
781                                 ForeignReferenceMapping refMapping = (ForeignReferenceMapping)baseMapping;
782                                 if (refMapping.usesIndirection()) {
783                                     allAttributesNull = refMapping.getIndirectionPolicy().objectIsInstantiated(baseValue);
784                                 }
785                             }
786                         }
787                     } else {
788                         allAttributesNull = false;
789                     }
790                 }
791             }
792             if (allAttributesNull) {
793                 aggregate = null;
794                 setAttributeValueInObject(targetObject, aggregate);
795             }
796         }
797         handledMappings.add(this);
798         return aggregate;
799     }
800
801     /**
802      * INTERNAL:
803      * Build an aggregate object from the specified row and put it
804      * in the specified target object.
805      */

806     public Object JavaDoc readFromRowIntoObject(AbstractRecord databaseRow, JoinedAttributeManager joinManager, Object JavaDoc targetObject, ObjectBuildingQuery sourceQuery, AbstractSession executionSession) throws DatabaseException {
807         Object JavaDoc aggregate = buildAggregateFromRow(databaseRow, targetObject, joinManager, false, executionSession);// don't just build a shallow original
808
setAttributeValueInObject(targetObject, aggregate);
809         return aggregate;
810     }
811
812     /**
813      * INTERNAL:
814      * Rehash any hashtables based on fields.
815      * This is used to clone descriptors for aggregates, which hammer field names.
816      */

817     public void rehashFieldDependancies(AbstractSession session) {
818         getReferenceDescriptor().rehashFieldDependancies(session);
819     }
820
821     /**
822      * INTERNAL:
823      * Set a collection of the aggregate to source field name associations.
824      */

825     public void setAggregateToSourceFieldNameAssociations(Vector fieldAssociations) {
826         Hashtable fieldNames = new Hashtable(fieldAssociations.size() + 1);
827         for (Enumeration associationsEnum = fieldAssociations.elements();
828                  associationsEnum.hasMoreElements();) {
829             Association association = (Association)associationsEnum.nextElement();
830             fieldNames.put(association.getKey(), association.getValue());
831         }
832
833         setAggregateToSourceFieldNames(fieldNames);
834     }
835
836     /**
837      * INTERNAL:
838      * Set the hashtable that stores target field name to the source field name.
839      */

840     protected void setAggregateToSourceFieldNames(Map aggregateToSource) {
841         aggregateToSourceFieldNames = aggregateToSource;
842     }
843
844     /**
845      * INTERNAL:
846      * Will be used by Gromit only.
847      */

848     public void setIsNullAllowed(boolean aBoolean) {
849         isNullAllowed = aBoolean;
850     }
851
852     /**
853      * INTERNAL:
854      * If field names are different in the source and aggregate objects then the translation
855      * is done here. The aggregate field name is converted to source field name from the
856      * field name mappings stored.
857      */

858     protected void translateFields(ClassDescriptor clonedDescriptor, AbstractSession session) {
859         for (Enumeration entry = clonedDescriptor.getFields().elements(); entry.hasMoreElements();) {
860             DatabaseField field = (DatabaseField)entry.nextElement();
861             String JavaDoc nameInAggregate = field.getName();
862             String JavaDoc nameInSource = (String JavaDoc)getAggregateToSourceFieldNames().get(nameInAggregate);
863
864             // Do not modify non-translated fields.
865
if (nameInSource != null) {
866                 DatabaseField fieldInSource = new DatabaseField(nameInSource);
867
868                 // Check if the translated field specified a table qualifier.
869
if (fieldInSource.getName().equals(nameInSource)) {
870                     // No table so just set the field name.
871
field.setName(nameInSource);
872                 } else {
873                     // There is a table, so set the name and table.
874
field.setName(fieldInSource.getName());
875                     field.setTable(clonedDescriptor.getTable(fieldInSource.getTable().getName()));
876                 }
877             }
878         }
879
880         clonedDescriptor.rehashFieldDependancies(session);
881     }
882
883     /**
884      * INTERNAL:
885      * A subclass should implement this method if it wants different behaviour.
886      * Write the foreign key values from the attribute to the row.
887      */

888     
889     public void writeFromAttributeIntoRow(Object JavaDoc attribute, AbstractRecord row, AbstractSession session)
890     {
891           AbstractRecord targetRow = buildRowFromAggregate(null, attribute, session);
892           for (Enumeration stream = targetRow.keys(); stream.hasMoreElements(); ) {
893                   DatabaseField field = (DatabaseField) stream.nextElement();
894                   Object JavaDoc value = targetRow.get(field);
895                   row.put(field, value);
896           }
897     }
898     
899     /**
900      * INTERNAL:
901      * Extract value of the field from the object
902      */

903     public Object JavaDoc valueFromObject(Object JavaDoc object, DatabaseField field, AbstractSession session) throws DescriptorException {
904         Object JavaDoc attributeValue = getAttributeValueFromObject(object);
905         if (attributeValue == null) {
906             if (isNullAllowed()) {
907                 return null;
908             } else {
909                 throw DescriptorException.nullForNonNullAggregate(object, this);
910             }
911         } else {
912             return getObjectBuilder(attributeValue, session).extractValueFromObjectForField(attributeValue, field, session);
913         }
914     }
915
916     /**
917      * INTERNAL:
918      * Get the attribute value from the object and add the appropriate
919      * values to the specified database row.
920      */

921     public void writeFromObjectIntoRow(Object JavaDoc object, AbstractRecord databaseRow, AbstractSession session) throws DescriptorException {
922         if (isReadOnly()) {
923             return;
924         }
925         AbstractRecord targetRow = buildRowFromAggregate(object, getAttributeValueFromObject(object), session);
926         for (Enumeration stream = targetRow.keys(); stream.hasMoreElements();) {
927             DatabaseField field = (DatabaseField)stream.nextElement();
928             Object JavaDoc value = targetRow.get(field);
929             databaseRow.add(field, value);
930         }
931     }
932
933     /**
934      * INTERNAL:
935      * Get the attribute value from the object and add the appropriate
936      * values to the specified database row.
937      */

938     public void writeFromObjectIntoRowWithChangeRecord(ChangeRecord changeRecord, AbstractRecord databaseRow, AbstractSession session) throws DescriptorException {
939         if (isReadOnly()) {
940             return;
941         }
942         AbstractRecord targetRow = buildRowFromAggregateWithChangeRecord(changeRecord, (ObjectChangeSet)((AggregateChangeRecord)changeRecord).getChangedObject(), session);
943         for (Enumeration stream = targetRow.keys(); stream.hasMoreElements();) {
944             DatabaseField field = (DatabaseField)stream.nextElement();
945             Object JavaDoc value = targetRow.get(field);
946             databaseRow.add(field, value);
947         }
948     }
949
950     /**
951      * INTERNAL:
952      * Get the attribute value from the object and add the changed
953      * values to the specified database row.
954      */

955     public void writeFromObjectIntoRowForUpdate(WriteObjectQuery query, AbstractRecord databaseRow) throws DescriptorException {
956         if (isReadOnly()) {
957             return;
958         }
959         AbstractRecord targetRow = buildRowFromAggregateForUpdate(query, getAttributeValueFromObject(query.getObject()));
960         for (Enumeration stream = targetRow.keys(); stream.hasMoreElements();) {
961             DatabaseField field = (DatabaseField)stream.nextElement();
962             Object JavaDoc value = targetRow.get(field);
963             databaseRow.add(field, value);
964         }
965     }
966
967     /**
968      * INTERNAL:
969      * Write fields needed for insert into the template for with null values.
970      */

971     public void writeInsertFieldsIntoRow(AbstractRecord databaseRow, AbstractSession session) {
972         if (isReadOnly()) {
973             return;
974         }
975
976         AbstractRecord targetRow = buildTemplateInsertRow(session);
977         for (Enumeration keyEnum = targetRow.keys(); keyEnum.hasMoreElements();) {
978             DatabaseField field = (DatabaseField)keyEnum.nextElement();
979             Object JavaDoc value = targetRow.get(field);
980
981             //CR-3286097 - Should use add not put, to avoid linear search.
982
databaseRow.add(field, value);
983         }
984     }
985 }
986
Popular Tags