0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * // Copyright (c) 1998, 2007, Oracle. All rights reserved.
0005: *
0006: *
0007: * The contents of this file are subject to the terms of either the GNU
0008: * General Public License Version 2 only ("GPL") or the Common Development
0009: * and Distribution License("CDDL") (collectively, the "License"). You
0010: * may not use this file except in compliance with the License. You can obtain
0011: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
0012: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
0013: * language governing permissions and limitations under the License.
0014: *
0015: * When distributing the software, include this License Header Notice in each
0016: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
0017: * Sun designates this particular file as subject to the "Classpath" exception
0018: * as provided by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the License
0020: * Header, with the fields enclosed by brackets [] replaced by your own
0021: * identifying information: "Portions Copyrighted [year]
0022: * [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * If you wish your version of this file to be governed by only the CDDL or
0027: * only the GPL Version 2, indicate your decision by adding "[Contributor]
0028: * elects to include this software in this distribution under the [CDDL or GPL
0029: * Version 2] license." If you don't indicate a single choice of license, a
0030: * recipient has the option to distribute your version of this file under
0031: * either the CDDL, the GPL Version 2 or to extend the choice of license to
0032: * its licensees as provided above. However, if you add GPL Version 2 code
0033: * and therefore, elected the GPL Version 2 license, then the option applies
0034: * only if the new code is made subject to such option by the copyright
0035: * holder.
0036: */
0037: package oracle.toplink.essentials.mappings;
0038:
0039: import java.security.AccessController;
0040: import java.security.PrivilegedActionException;
0041: import java.util.*;
0042: import oracle.toplink.essentials.exceptions.*;
0043: import oracle.toplink.essentials.internal.descriptors.*;
0044: import oracle.toplink.essentials.internal.security.PrivilegedAccessHelper;
0045: import oracle.toplink.essentials.internal.security.PrivilegedClassForName;
0046: import oracle.toplink.essentials.internal.sessions.*;
0047: import oracle.toplink.essentials.queryframework.*;
0048: import oracle.toplink.essentials.sessions.ObjectCopyingPolicy;
0049: import oracle.toplink.essentials.descriptors.DescriptorEventManager;
0050: import oracle.toplink.essentials.descriptors.DescriptorQueryManager;
0051: import oracle.toplink.essentials.internal.sessions.AbstractRecord;
0052: import oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl;
0053: import oracle.toplink.essentials.internal.sessions.AbstractSession;
0054: import oracle.toplink.essentials.descriptors.ClassDescriptor;
0055: import oracle.toplink.essentials.internal.queryframework.JoinedAttributeManager;
0056:
0057: /**
0058: * <b>Purpose</b>: Two objects can be considered to be related by aggregation if there is a strict
0059: * 1:1 relationship between the objects. This means that if the source (parent)object exists, then
0060: * the target (child or owned) object must exist. This class implements the behavior common to the
0061: * aggregate object and structure mappings.
0062: *
0063: * @author Sati
0064: * @since TopLink for Java 1.0
0065: */
0066: public abstract class AggregateMapping extends DatabaseMapping {
0067:
0068: /** Stores a reference class */
0069: protected Class referenceClass;
0070: protected String referenceClassName;
0071:
0072: /** The descriptor of the reference class */
0073: protected ClassDescriptor referenceDescriptor;
0074:
0075: /**
0076: * Default constructor.
0077: */
0078: public AggregateMapping() {
0079: super ();
0080: }
0081:
0082: /**
0083: * Make a copy of the sourceQuery for the attribute.
0084: */
0085: protected DeleteObjectQuery buildAggregateDeleteQuery(
0086: WriteObjectQuery sourceQuery, Object sourceAttributeValue) {
0087: DeleteObjectQuery aggregateQuery = new DeleteObjectQuery();
0088: buildAggregateModifyQuery(sourceQuery, aggregateQuery,
0089: sourceAttributeValue);
0090: return aggregateQuery;
0091: }
0092:
0093: /**
0094: * Initialize the aggregate query with the settings from the source query.
0095: */
0096: protected void buildAggregateModifyQuery(
0097: ObjectLevelModifyQuery sourceQuery,
0098: ObjectLevelModifyQuery aggregateQuery,
0099: Object sourceAttributeValue) {
0100: if (sourceQuery.getSession().isUnitOfWork()) {
0101: Object backupAttributeValue = getAttributeValueFromBackupClone(sourceQuery
0102: .getBackupClone());
0103: if (backupAttributeValue == null) {
0104: backupAttributeValue = getObjectBuilder(
0105: sourceAttributeValue, sourceQuery.getSession())
0106: .buildNewInstance();
0107: }
0108: aggregateQuery.setBackupClone(backupAttributeValue);
0109: }
0110: aggregateQuery.setCascadePolicy(sourceQuery.getCascadePolicy());
0111: aggregateQuery.setObject(sourceAttributeValue);
0112: aggregateQuery.setTranslationRow(sourceQuery
0113: .getTranslationRow());
0114: aggregateQuery.setSession(sourceQuery.getSession());
0115: aggregateQuery.setProperties(sourceQuery.getProperties());
0116: }
0117:
0118: /**
0119: * Make a copy of the sourceQuery for the attribute.
0120: */
0121: protected WriteObjectQuery buildAggregateWriteQuery(
0122: WriteObjectQuery sourceQuery, Object sourceAttributeValue) {
0123: WriteObjectQuery aggregateQuery = new WriteObjectQuery();
0124: buildAggregateModifyQuery(sourceQuery, aggregateQuery,
0125: sourceAttributeValue);
0126: return aggregateQuery;
0127: }
0128:
0129: /**
0130: * INTERNAL:
0131: * Clone the attribute from the clone and assign it to the backup.
0132: */
0133: public void buildBackupClone(Object clone, Object backup,
0134: UnitOfWorkImpl unitOfWork) {
0135: Object attributeValue = getAttributeValueFromObject(clone);
0136: setAttributeValueInObject(backup, buildBackupClonePart(
0137: attributeValue, unitOfWork));
0138: }
0139:
0140: /**
0141: * INTERNAL:
0142: * Build and return a backup clone of the attribute.
0143: */
0144: protected Object buildBackupClonePart(Object attributeValue,
0145: UnitOfWorkImpl unitOfWork) {
0146: if (attributeValue == null) {
0147: return null;
0148: }
0149: return getObjectBuilder(attributeValue, unitOfWork)
0150: .buildBackupClone(attributeValue, unitOfWork);
0151: }
0152:
0153: /**
0154: * INTERNAL:
0155: * Clone the attribute from the original and assign it to the clone.
0156: */
0157: public void buildClone(Object original, Object clone,
0158: UnitOfWorkImpl unitOfWork,
0159: JoinedAttributeManager joinedAttributeManager) {
0160: Object attributeValue = getAttributeValueFromObject(original);
0161: setAttributeValueInObject(clone, buildClonePart(original,
0162: attributeValue, unitOfWork));
0163: }
0164:
0165: /**
0166: * INTERNAL:
0167: * A combination of readFromRowIntoObject and buildClone.
0168: * <p>
0169: * buildClone assumes the attribute value exists on the original and can
0170: * simply be copied.
0171: * <p>
0172: * readFromRowIntoObject assumes that one is building an original.
0173: * <p>
0174: * Both of the above assumptions are false in this method, and actually
0175: * attempts to do both at the same time.
0176: * <p>
0177: * Extract value from the row and set the attribute to this value in the
0178: * working copy clone.
0179: * In order to bypass the shared cache when in transaction a UnitOfWork must
0180: * be able to populate working copies directly from the row.
0181: */
0182: public void buildCloneFromRow(AbstractRecord databaseRow,
0183: JoinedAttributeManager joinManager, Object clone,
0184: ObjectBuildingQuery sourceQuery, UnitOfWorkImpl unitOfWork,
0185: AbstractSession executionSession) {
0186: // automatically returns a uow result from scratch that doesn't need cloning
0187: Object cloneAttributeValue = valueFromRow(databaseRow,
0188: joinManager, sourceQuery, executionSession);
0189: setAttributeValueInObject(clone, cloneAttributeValue);
0190: }
0191:
0192: /**
0193: * INTERNAL:
0194: * Build and return a clone of the attribute.
0195: */
0196: protected Object buildClonePart(Object original,
0197: Object attributeValue, UnitOfWorkImpl unitOfWork) {
0198: if (attributeValue == null) {
0199: return null;
0200: }
0201: if (unitOfWork.isOriginalNewObject(original)) {
0202: unitOfWork.addNewAggregate(attributeValue);
0203: }
0204:
0205: // Do not clone for read-only.
0206: if (unitOfWork.isClassReadOnly(attributeValue.getClass())) {
0207: return attributeValue;
0208: }
0209:
0210: ObjectBuilder aggregateObjectBuilder = getObjectBuilder(
0211: attributeValue, unitOfWork);
0212:
0213: // bug 2612602 as we are building the working copy make sure that we call to correct clone method.
0214: Object clonedAttributeValue = aggregateObjectBuilder
0215: .instantiateWorkingCopyClone(attributeValue, unitOfWork);
0216: aggregateObjectBuilder.populateAttributesForClone(
0217: attributeValue, clonedAttributeValue, unitOfWork, null);
0218:
0219: return clonedAttributeValue;
0220: }
0221:
0222: /**
0223: * INTERNAL:
0224: * Copy of the attribute of the object.
0225: * This is NOT used for unit of work but for templatizing an object.
0226: */
0227: public void buildCopy(Object copy, Object original,
0228: ObjectCopyingPolicy policy) {
0229: Object attributeValue = getAttributeValueFromObject(original);
0230: setAttributeValueInObject(copy, buildCopyOfAttributeValue(
0231: attributeValue, policy));
0232: }
0233:
0234: /**
0235: * Copy of the attribute of the object.
0236: * This is NOT used for unit of work but for templatizing an object.
0237: */
0238: protected Object buildCopyOfAttributeValue(Object attributeValue,
0239: ObjectCopyingPolicy policy) {
0240: if (attributeValue == null) {
0241: return null;
0242: }
0243: return getObjectBuilder(attributeValue, policy.getSession())
0244: .copyObject(attributeValue, policy);
0245: }
0246:
0247: /**
0248: * INTERNAL:
0249: * Build and return a new instance of the specified attribute.
0250: * This will be populated by a merge.
0251: */
0252: protected Object buildNewMergeInstanceOf(
0253: Object sourceAttributeValue, AbstractSession session) {
0254: return getObjectBuilder(sourceAttributeValue, session)
0255: .buildNewInstance();
0256: }
0257:
0258: /**
0259: * INTERNAL:
0260: * Cascade perform delete through mappings that require the cascade
0261: */
0262: // public void cascadePerformDeleteIfRequired(Object object, UnitOfWork uow, IdentityHashtable visitedObjects){
0263: //objects referenced by this mapping are not registered as they have
0264: // no identity, this is a no-op.
0265: // }
0266: /**
0267: * INTERNAL:
0268: * Cascade registerNew for Create through mappings that require the cascade
0269: */
0270: // public void cascadeRegisterNewIfRequired(Object object, UnitOfWork uow, IdentityHashtable visitedObjects){
0271: //aggregate objects are not registeres as they have no identity, this is a no-op.
0272: // }
0273: /**
0274: * INTERNAL:
0275: * Compare the attributes. Return true if they are alike.
0276: */
0277: protected boolean compareAttributeValues(Object attributeValue1,
0278: Object attributeValue2, AbstractSession session) {
0279: if ((attributeValue1 == null) && (attributeValue2 == null)) {
0280: return true;
0281: }
0282: if ((attributeValue1 == null) || (attributeValue2 == null)) {
0283: return false;
0284: }
0285: if (attributeValue1.getClass() != attributeValue2.getClass()) {
0286: return false;
0287: }
0288: return getObjectBuilder(attributeValue1, session)
0289: .compareObjects(attributeValue1, attributeValue2,
0290: session);
0291: }
0292:
0293: /**
0294: * INTERNAL:
0295: * Compare the changes between two aggregates.
0296: * Return a change record holding the changes.
0297: */
0298: public ChangeRecord compareForChange(Object clone, Object backup,
0299: ObjectChangeSet owner, AbstractSession session) {
0300: Object cloneAttribute = getAttributeValueFromObject(clone);
0301: Object backupAttribute = null;
0302:
0303: if (!owner.isNew()) {
0304: backupAttribute = getAttributeValueFromObject(backup);
0305: if ((cloneAttribute == null) && (backupAttribute == null)) {
0306: return null;// no change
0307: }
0308: if ((cloneAttribute != null)
0309: && (backupAttribute != null)
0310: && (!cloneAttribute.getClass().equals(
0311: backupAttribute.getClass()))) {
0312: backupAttribute = null;
0313: }
0314: }
0315:
0316: AggregateChangeRecord changeRecord = new AggregateChangeRecord(
0317: owner);
0318: changeRecord.setAttribute(getAttributeName());
0319: changeRecord.setMapping(this );
0320:
0321: if (cloneAttribute == null) {// the attribute was set to null
0322: changeRecord.setChangedObject(null);
0323: return changeRecord;
0324: }
0325:
0326: ObjectBuilder builder = getObjectBuilder(cloneAttribute,
0327: session);
0328:
0329: //if the owner is new then the backup will be null, if the owner is new then the aggregate is new
0330: //if the backup is null but the owner is not new then this aggregate is new
0331: ObjectChangeSet initialChanges = builder.createObjectChangeSet(
0332: cloneAttribute, (UnitOfWorkChangeSet) owner
0333: .getUOWChangeSet(), (backupAttribute == null),
0334: session);
0335: ObjectChangeSet changeSet = builder.compareForChange(
0336: cloneAttribute, backupAttribute,
0337: (UnitOfWorkChangeSet) owner.getUOWChangeSet(), session);
0338: if (changeSet == null) {
0339: if (initialChanges.isNew()) {
0340: // This happens if original aggregate is of class A, the new aggregate
0341: // is of class B (B inherits from A) - and neither A nor B has any mapped attributes.
0342: // CR3664
0343: changeSet = initialChanges;
0344: } else {
0345: return null;// no change
0346: }
0347: }
0348: changeRecord.setChangedObject(changeSet);
0349: return changeRecord;
0350: }
0351:
0352: /**
0353: * INTERNAL:
0354: * Compare the attributes belonging to this mapping for the objects.
0355: */
0356: public boolean compareObjects(Object firstObject,
0357: Object secondObject, AbstractSession session) {
0358: return compareAttributeValues(
0359: getAttributeValueFromObject(firstObject),
0360: getAttributeValueFromObject(secondObject), session);
0361: }
0362:
0363: /**
0364: * INTERNAL:
0365: * Convert all the class-name-based settings in this mapping to actual class-based
0366: * settings. This method is used when converting a project that has been built
0367: * with class names to a project with classes.
0368: * @param classLoader
0369: */
0370: public void convertClassNamesToClasses(ClassLoader classLoader) {
0371: Class referenceClass = null;
0372: try {
0373: if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
0374: try {
0375: referenceClass = (Class) AccessController
0376: .doPrivileged(new PrivilegedClassForName(
0377: getReferenceClassName(), true,
0378: classLoader));
0379: } catch (PrivilegedActionException exception) {
0380: throw ValidationException
0381: .classNotFoundWhileConvertingClassNames(
0382: getReferenceClassName(), exception
0383: .getException());
0384: }
0385: } else {
0386: referenceClass = oracle.toplink.essentials.internal.security.PrivilegedAccessHelper
0387: .getClassForName(getReferenceClassName(), true,
0388: classLoader);
0389: }
0390: } catch (ClassNotFoundException exc) {
0391: throw ValidationException
0392: .classNotFoundWhileConvertingClassNames(
0393: getReferenceClassName(), exc);
0394: }
0395: setReferenceClass(referenceClass);
0396: };
0397:
0398: /**
0399: * INTERNAL:
0400: * Execute a descriptor event for the specified event code.
0401: */
0402: protected void executeEvent(int eventCode,
0403: ObjectLevelModifyQuery query) {
0404: ClassDescriptor referenceDescriptor = getReferenceDescriptor(
0405: query.getObject(), query.getSession());
0406:
0407: // PERF: Avoid events if no listeners.
0408: if (referenceDescriptor.getEventManager()
0409: .hasAnyEventListeners()) {
0410: referenceDescriptor
0411: .getEventManager()
0412: .executeEvent(
0413: new oracle.toplink.essentials.descriptors.DescriptorEvent(
0414: eventCode, query));
0415: }
0416: }
0417:
0418: /**
0419: * Return the appropriate attribute value.
0420: * This method is a hack to allow the aggregate collection
0421: * subclass to override....
0422: */
0423: protected Object getAttributeValueFromBackupClone(Object backupClone) {
0424: return getAttributeValueFromObject(backupClone);
0425: }
0426:
0427: /**
0428: * Convenience method
0429: */
0430: protected ObjectBuilder getObjectBuilderForClass(Class javaClass,
0431: AbstractSession session) {
0432: return getReferenceDescriptor(javaClass, session)
0433: .getObjectBuilder();
0434: }
0435:
0436: /**
0437: * Convenience method
0438: */
0439: protected ObjectBuilder getObjectBuilder(Object attributeValue,
0440: AbstractSession session) {
0441: return getReferenceDescriptor(attributeValue, session)
0442: .getObjectBuilder();
0443: }
0444:
0445: /**
0446: * Convenience method
0447: */
0448: protected DescriptorQueryManager getQueryManager(
0449: Object attributeValue, AbstractSession session) {
0450: return getReferenceDescriptor(attributeValue, session)
0451: .getQueryManager();
0452: }
0453:
0454: /**
0455: * PUBLIC:
0456: * Returns the reference class
0457: */
0458: public Class getReferenceClass() {
0459: return referenceClass;
0460: }
0461:
0462: /**
0463: * INTERNAL:
0464: * Used by MW.
0465: */
0466: public String getReferenceClassName() {
0467: if ((referenceClassName == null) && (referenceClass != null)) {
0468: referenceClassName = referenceClass.getName();
0469: }
0470: return referenceClassName;
0471: }
0472:
0473: /**
0474: * INTERNAL:
0475: * Return the referenceDescriptor. This is a descriptor which is associated with the reference class.
0476: * NOTE: If you are looking for the descriptor for a specific aggregate object, use
0477: * #getReferenceDescriptor(Object). This will ensure you get the right descriptor if the object's
0478: * descriptor is part of an inheritance tree.
0479: */
0480: public ClassDescriptor getReferenceDescriptor() {
0481: return referenceDescriptor;
0482: }
0483:
0484: /**
0485: * INTERNAL:
0486: * For inheritance purposes.
0487: */
0488: protected ClassDescriptor getReferenceDescriptor(Class theClass,
0489: AbstractSession session) {
0490: if (getReferenceDescriptor().getJavaClass().equals(theClass)) {
0491: return getReferenceDescriptor();
0492: }
0493:
0494: ClassDescriptor subclassDescriptor = session
0495: .getDescriptor(theClass);
0496: if (subclassDescriptor == null) {
0497: throw DescriptorException.noSubClassMatch(theClass, this );
0498: } else {
0499: return subclassDescriptor;
0500: }
0501: }
0502:
0503: /**
0504: * Convenience method
0505: */
0506: protected ClassDescriptor getReferenceDescriptor(
0507: Object attributeValue, AbstractSession session) {
0508: if (attributeValue == null) {
0509: return getReferenceDescriptor();
0510: } else {
0511: return getReferenceDescriptor(attributeValue.getClass(),
0512: session);
0513: }
0514: }
0515:
0516: /**
0517: * INTERNAL:
0518: * Initialize the reference descriptor.
0519: */
0520: public void initialize(AbstractSession session)
0521: throws DescriptorException {
0522: super .initialize(session);
0523:
0524: if (getReferenceClass() == null) {
0525: throw DescriptorException.referenceClassNotSpecified(this );
0526: }
0527:
0528: setReferenceDescriptor(session
0529: .getDescriptor(getReferenceClass()));
0530:
0531: ClassDescriptor refDescriptor = this .getReferenceDescriptor();
0532: if (refDescriptor == null) {
0533: session.getIntegrityChecker().handleError(
0534: DescriptorException.descriptorIsMissing(
0535: getReferenceClass().getName(), this ));
0536: }
0537: if (refDescriptor.isAggregateDescriptor()) {
0538: refDescriptor.checkInheritanceTreeAggregateSettings(
0539: session, this );
0540: } else {
0541: session.getIntegrityChecker()
0542: .handleError(
0543: DescriptorException
0544: .referenceDescriptorIsNotAggregate(
0545: getReferenceClass()
0546: .getName(), this ));
0547: }
0548: }
0549:
0550: /**
0551: * INTERNAL:
0552: * Related mapping should implement this method to return true.
0553: */
0554: public boolean isAggregateMapping() {
0555: return true;
0556: }
0557:
0558: /**
0559: * INTERNAL:
0560: * Iterate on the appropriate attribute value.
0561: */
0562: public void iterate(DescriptorIterator iterator) {
0563: iterateOnAttributeValue(
0564: iterator,
0565: getAttributeValueFromObject(iterator.getVisitedParent()));
0566: }
0567:
0568: /**
0569: * Iterate on the specified attribute value.
0570: */
0571: protected void iterateOnAttributeValue(DescriptorIterator iterator,
0572: Object attributeValue) {
0573: iterator.iterateForAggregateMapping(attributeValue, this ,
0574: getReferenceDescriptor(attributeValue, iterator
0575: .getSession()));
0576: }
0577:
0578: /**
0579: * Merge the attribute values.
0580: */
0581: protected void mergeAttributeValue(Object targetAttributeValue,
0582: boolean isTargetUnInitialized, Object sourceAttributeValue,
0583: MergeManager mergeManager) {
0584: // don't merge read-only attributes
0585: if (mergeManager.getSession().isClassReadOnly(
0586: sourceAttributeValue.getClass())) {
0587: return;
0588: }
0589: if (mergeManager.getSession().isClassReadOnly(
0590: targetAttributeValue.getClass())) {
0591: return;
0592: }
0593:
0594: getObjectBuilder(sourceAttributeValue,
0595: mergeManager.getSession()).mergeIntoObject(
0596: targetAttributeValue, isTargetUnInitialized,
0597: sourceAttributeValue, mergeManager);
0598: }
0599:
0600: /**
0601: * INTERNAL:
0602: * Merge changes from the source to the target object.
0603: * With aggregates the merge must cascade to the object changes for the aggregate object
0604: * because aggregate objects have no identity outside of themselves.
0605: * The actual aggregate object does not need to be replaced, because even if the clone references
0606: * another aggregate it appears the same to TopLink
0607: */
0608: public void mergeChangesIntoObject(Object target,
0609: ChangeRecord changeRecord, Object source,
0610: MergeManager mergeManager) {
0611: ObjectChangeSet aggregateChangeSet = (ObjectChangeSet) ((AggregateChangeRecord) changeRecord)
0612: .getChangedObject();
0613: if (aggregateChangeSet == null) {// the change was to set the value to null
0614: setAttributeValueInObject(target, null);
0615: return;
0616: }
0617:
0618: Object sourceAggregate = null;
0619: if (source != null) {
0620: sourceAggregate = getAttributeValueFromObject(source);
0621: }
0622: ObjectBuilder objectBuilder = getObjectBuilderForClass(
0623: aggregateChangeSet.getClassType(mergeManager
0624: .getSession()), mergeManager.getSession());
0625: //Bug#4719341 Always obtain aggregate attribute value from the target object regardless of new or not
0626: Object targetAggregate = getAttributeValueFromObject(target);
0627: if (targetAggregate == null) {
0628: targetAggregate = objectBuilder.buildNewInstance();
0629: } else {
0630: if ((sourceAggregate != null)
0631: && (sourceAggregate.getClass() != targetAggregate
0632: .getClass())) {
0633: targetAggregate = objectBuilder.buildNewInstance();
0634: }
0635: }
0636: objectBuilder.mergeChangesIntoObject(targetAggregate,
0637: aggregateChangeSet, sourceAggregate, mergeManager);
0638: setAttributeValueInObject(target, targetAggregate);
0639: }
0640:
0641: /**
0642: * INTERNAL:
0643: * Merge changes from the source to the target object. This merge is only called when a changeSet for the target
0644: * does not exist or the target is uninitialized
0645: */
0646: public void mergeIntoObject(Object target,
0647: boolean isTargetUnInitialized, Object source,
0648: MergeManager mergeManager) {
0649: Object sourceAttributeValue = getAttributeValueFromObject(source);
0650: if (sourceAttributeValue == null) {
0651: setAttributeValueInObject(target, null);
0652: return;
0653: }
0654:
0655: Object targetAttributeValue = getAttributeValueFromObject(target);
0656: if (targetAttributeValue == null) {
0657: // avoid null-pointer/nothing to merge to - create a new instance
0658: // (a new clone cannot be used as all changes must be merged)
0659: targetAttributeValue = buildNewMergeInstanceOf(
0660: sourceAttributeValue, mergeManager.getSession());
0661: mergeAttributeValue(targetAttributeValue, true,
0662: sourceAttributeValue, mergeManager);
0663: // setting new instance so fire event as if set was called by user.
0664: // this call will eventually get passed to updateChangeRecord which will
0665: //ensure this new aggregates is fully initilized with listeners.
0666: this .getDescriptor().getObjectChangePolicy()
0667: .raiseInternalPropertyChangeEvent(target,
0668: getAttributeName(),
0669: getAttributeValueFromObject(target),
0670: targetAttributeValue);
0671:
0672: } else {
0673: mergeAttributeValue(targetAttributeValue,
0674: isTargetUnInitialized, sourceAttributeValue,
0675: mergeManager);
0676: }
0677:
0678: // allow setter to re-morph any changes...
0679: setAttributeValueInObject(target, targetAttributeValue);
0680: }
0681:
0682: /**
0683: * INTERNAL:
0684: * The message is passed to its reference class descriptor.
0685: */
0686: public void postDelete(WriteObjectQuery query)
0687: throws DatabaseException, OptimisticLockException {
0688: if (!isReadOnly()) {
0689: postDeleteAttributeValue(query,
0690: getAttributeValueFromObject(query.getObject()));
0691: }
0692: }
0693:
0694: /**
0695: * INTERNAL:
0696: * The message is passed to its reference class descriptor.
0697: */
0698: protected void postDeleteAttributeValue(WriteObjectQuery query,
0699: Object attributeValue) throws DatabaseException,
0700: OptimisticLockException {
0701: if (attributeValue == null) {
0702: return;
0703: }
0704: DeleteObjectQuery aggregateQuery = buildAggregateDeleteQuery(
0705: query, attributeValue);
0706: getQueryManager(attributeValue, query.getSession()).postDelete(
0707: aggregateQuery);
0708: executeEvent(DescriptorEventManager.PostDeleteEvent,
0709: aggregateQuery);
0710: }
0711:
0712: /**
0713: * INTERNAL:
0714: * The message is passed to its reference class descriptor.
0715: */
0716: public void postInsert(WriteObjectQuery query)
0717: throws DatabaseException, OptimisticLockException {
0718: if (!isReadOnly()) {
0719: postInsertAttributeValue(query,
0720: getAttributeValueFromObject(query.getObject()));
0721: }
0722: }
0723:
0724: /**
0725: * INTERNAL:
0726: * The message is passed to its reference class descriptor.
0727: */
0728: protected void postInsertAttributeValue(WriteObjectQuery query,
0729: Object attributeValue) throws DatabaseException,
0730: OptimisticLockException {
0731: if (attributeValue == null) {
0732: return;
0733: }
0734: WriteObjectQuery aggregateQuery = buildAggregateWriteQuery(
0735: query, attributeValue);
0736: getQueryManager(attributeValue, query.getSession()).postInsert(
0737: aggregateQuery);
0738: executeEvent(DescriptorEventManager.PostInsertEvent,
0739: aggregateQuery);
0740: // aggregates do not actually use a query to write to the database so the post write must be called here
0741: executeEvent(DescriptorEventManager.PostWriteEvent,
0742: aggregateQuery);
0743: }
0744:
0745: /**
0746: * INTERNAL:
0747: * The message is passed to its reference class descriptor.
0748: */
0749: public void postUpdate(WriteObjectQuery query)
0750: throws DatabaseException, OptimisticLockException {
0751: if (!isReadOnly()) {
0752: postUpdateAttributeValue(query,
0753: getAttributeValueFromObject(query.getObject()));
0754: }
0755: }
0756:
0757: /**
0758: * INTERNAL:
0759: * The message is passed to its reference class descriptor.
0760: */
0761: protected void postUpdateAttributeValue(WriteObjectQuery query,
0762: Object attributeValue) throws DatabaseException,
0763: OptimisticLockException {
0764: if (attributeValue == null) {
0765: return;
0766: }
0767: ObjectChangeSet changeSet = null;
0768: UnitOfWorkChangeSet uowChangeSet = null;
0769: if (query.getSession().isUnitOfWork()
0770: && (((UnitOfWorkImpl) query.getSession())
0771: .getUnitOfWorkChangeSet() != null)) {
0772: uowChangeSet = (UnitOfWorkChangeSet) ((UnitOfWorkImpl) query
0773: .getSession()).getUnitOfWorkChangeSet();
0774: changeSet = (ObjectChangeSet) uowChangeSet
0775: .getObjectChangeSetForClone(attributeValue);
0776: }
0777: WriteObjectQuery aggregateQuery = buildAggregateWriteQuery(
0778: query, attributeValue);
0779: aggregateQuery.setObjectChangeSet(changeSet);
0780: getQueryManager(attributeValue, query.getSession()).postUpdate(
0781: aggregateQuery);
0782: executeEvent(DescriptorEventManager.PostUpdateEvent,
0783: aggregateQuery);
0784: // aggregates do not actually use a query to write to the database so the post write must be called here
0785: executeEvent(DescriptorEventManager.PostWriteEvent,
0786: aggregateQuery);
0787: }
0788:
0789: /**
0790: * INTERNAL:
0791: * The message is passed to its reference class descriptor.
0792: */
0793: public void preDelete(WriteObjectQuery query)
0794: throws DatabaseException, OptimisticLockException {
0795: if (!isReadOnly()) {
0796: preDeleteAttributeValue(query,
0797: getAttributeValueFromObject(query.getObject()));
0798: }
0799: }
0800:
0801: /**
0802: * INTERNAL:
0803: * The message is passed to its reference class descriptor.
0804: */
0805: protected void preDeleteAttributeValue(WriteObjectQuery query,
0806: Object attributeValue) throws DatabaseException,
0807: OptimisticLockException {
0808: if (attributeValue == null) {
0809: return;
0810: }
0811: DeleteObjectQuery aggregateQuery = buildAggregateDeleteQuery(
0812: query, attributeValue);
0813: executeEvent(DescriptorEventManager.PreDeleteEvent,
0814: aggregateQuery);
0815: getQueryManager(attributeValue, query.getSession()).preDelete(
0816: aggregateQuery);
0817: }
0818:
0819: /**
0820: * INTERNAL:
0821: * The message is passed to its reference class descriptor.
0822: */
0823: public void preInsert(WriteObjectQuery query)
0824: throws DatabaseException, OptimisticLockException {
0825: if (!isReadOnly()) {
0826: preInsertAttributeValue(query,
0827: getAttributeValueFromObject(query.getObject()));
0828: }
0829: }
0830:
0831: /**
0832: * INTERNAL:
0833: * The message is passed to its reference class descriptor.
0834: */
0835: protected void preInsertAttributeValue(WriteObjectQuery query,
0836: Object attributeValue) throws DatabaseException,
0837: OptimisticLockException {
0838: if (attributeValue == null) {
0839: return;
0840: }
0841: WriteObjectQuery aggregateQuery = buildAggregateWriteQuery(
0842: query, attributeValue);
0843: getQueryManager(attributeValue, query.getSession()).preInsert(
0844: aggregateQuery);
0845: }
0846:
0847: /**
0848: * INTERNAL:
0849: * The message is passed to its reference class descriptor.
0850: */
0851: public void preUpdate(WriteObjectQuery query)
0852: throws DatabaseException, OptimisticLockException {
0853: if (!isReadOnly()) {
0854: preUpdateAttributeValue(query,
0855: getAttributeValueFromObject(query.getObject()));
0856: }
0857: }
0858:
0859: /**
0860: * INTERNAL:
0861: * The message is passed to its reference class descriptor.
0862: */
0863: protected void preUpdateAttributeValue(WriteObjectQuery query,
0864: Object attributeValue) throws DatabaseException,
0865: OptimisticLockException {
0866: if (attributeValue == null) {
0867: return;
0868: }
0869: WriteObjectQuery aggregateQuery = buildAggregateWriteQuery(
0870: query, attributeValue);
0871: ObjectChangeSet changeSet = null;
0872: UnitOfWorkChangeSet uowChangeSet = null;
0873: if (query.getSession().isUnitOfWork()
0874: && (((UnitOfWorkImpl) query.getSession())
0875: .getUnitOfWorkChangeSet() != null)) {
0876: uowChangeSet = (UnitOfWorkChangeSet) ((UnitOfWorkImpl) query
0877: .getSession()).getUnitOfWorkChangeSet();
0878: changeSet = (ObjectChangeSet) uowChangeSet
0879: .getObjectChangeSetForClone(aggregateQuery
0880: .getObject());
0881: }
0882:
0883: aggregateQuery.setObjectChangeSet(changeSet);
0884: // aggregates do not actually use a query to write to the database so the pre-write must be called here
0885: if (changeSet == null) {// then we didn't fire events at calculations
0886: executeEvent(DescriptorEventManager.PreWriteEvent,
0887: aggregateQuery);
0888: executeEvent(DescriptorEventManager.PreUpdateEvent,
0889: aggregateQuery);
0890: }
0891: getQueryManager(attributeValue, query.getSession()).preUpdate(
0892: aggregateQuery);
0893: }
0894:
0895: /**
0896: * PUBLIC:
0897: * This is a reference class whose instances this mapping will store in the domain objects.
0898: */
0899: public void setReferenceClass(Class aClass) {
0900: referenceClass = aClass;
0901: }
0902:
0903: /**
0904: * INTERNAL:
0905: * Used by MW.
0906: */
0907: public void setReferenceClassName(String aClassName) {
0908: referenceClassName = aClassName;
0909: }
0910:
0911: /**
0912: * INTERNAL:
0913: * Set the referenceDescriptor. This is a descriptor which is associated with
0914: * the reference class.
0915: */
0916: protected void setReferenceDescriptor(ClassDescriptor aDescriptor) {
0917: referenceDescriptor = aDescriptor;
0918: }
0919:
0920: /**
0921: * INTERNAL:
0922: * Either create a new change record or update the change record with the new value.
0923: * This is used by attribute change tracking. Only call this method if a different aggregate
0924: * is being set in the object, if the aggregate itself is being updated then call the
0925: * update on the corresponding mapping within the aggregate.
0926: */
0927: public void updateChangeRecord(Object sourceClone, Object newValue,
0928: Object oldValue, ObjectChangeSet objectChangeSet,
0929: UnitOfWorkImpl uow) throws DescriptorException {
0930: //This method will be called when either the referenced aggregate has
0931: //been changed or a component of the referenced aggregate has been changed
0932: //this case is determined by the value of the sourceClone
0933:
0934: AggregateChangeRecord changeRecord = (AggregateChangeRecord) objectChangeSet
0935: .getChangesForAttributeNamed(this .getAttributeName());
0936: if (changeRecord == null) {
0937: changeRecord = new AggregateChangeRecord(objectChangeSet);
0938: changeRecord.setAttribute(this .getAttributeName());
0939: changeRecord.setMapping(this );
0940: objectChangeSet.addChange(changeRecord);
0941: }
0942:
0943: if (sourceClone.getClass().equals(
0944: objectChangeSet.getClassType(uow))) {
0945: // event was fired on the parent to the aggregate, the attribute value changed.
0946: ClassDescriptor referenceDescriptor = getReferenceDescriptor(
0947: newValue, uow);
0948: if (newValue == null) { // attribute set to null
0949: changeRecord.setChangedObject(null);
0950: return;
0951: } else { // attribute set to new aggregate
0952: UnitOfWorkChangeSet uowChangeSet = (UnitOfWorkChangeSet) objectChangeSet
0953: .getUOWChangeSet();
0954: //force comparison change detection to build changeset.
0955: ObjectChangeSet aggregateChangeSet = (ObjectChangeSet) uowChangeSet
0956: .getObjectChangeSetForClone(newValue);
0957: if (aggregateChangeSet != null) {
0958: aggregateChangeSet.clear(); // old differences must be thrown away because difference is between old value and new value
0959: }
0960: //force comparison change detection to build changeset.
0961: changeRecord.setChangedObject(referenceDescriptor
0962: .getObjectChangePolicy()
0963: .createObjectChangeSetThroughComparison(
0964: newValue, oldValue, uowChangeSet,
0965: (oldValue == null), uow,
0966: referenceDescriptor));
0967: referenceDescriptor.getObjectChangePolicy()
0968: .setChangeSetOnListener(
0969: (ObjectChangeSet) changeRecord
0970: .getChangedObject(), newValue);
0971:
0972: }
0973: } else {
0974: // not tracked at attribute level, lets force build a changeset then.
0975: changeRecord.setChangedObject(referenceDescriptor
0976: .getObjectChangePolicy()
0977: .createObjectChangeSetThroughComparison(
0978: sourceClone,
0979: null,
0980: (UnitOfWorkChangeSet) objectChangeSet
0981: .getUOWChangeSet(), true, uow,
0982: referenceDescriptor));
0983: }
0984: }
0985:
0986: /**
0987: * INTERNAL:
0988: * Return whether the specified object and all its components have been deleted.
0989: */
0990: public boolean verifyDelete(Object object, AbstractSession session)
0991: throws DatabaseException {
0992: return verifyDeleteOfAttributeValue(
0993: getAttributeValueFromObject(object), session);
0994: }
0995:
0996: /**
0997: * INTERNAL:
0998: * Return whether the specified object and all its components have been deleted.
0999: */
1000: protected boolean verifyDeleteOfAttributeValue(
1001: Object attributeValue, AbstractSession session)
1002: throws DatabaseException {
1003: if (attributeValue == null) {
1004: return true;
1005: }
1006: for (Enumeration mappings = getReferenceDescriptor(
1007: attributeValue, session).getMappings().elements(); mappings
1008: .hasMoreElements();) {
1009: DatabaseMapping mapping = (DatabaseMapping) mappings
1010: .nextElement();
1011: if (!mapping.verifyDelete(attributeValue, session)) {
1012: return false;
1013: }
1014: }
1015: return true;
1016: }
1017: }
|