/**
* Merge the changes from the collection of a source object into the target using the backup as the diff.
* Return true if any changes occurred.
*/
public boolean mergeChangesInCollection(Object source, Object target, Object backup, DatabaseMapping mapping) {
ContainerPolicy containerPolicy = mapping.getContainerPolicy();
// The vectors must be converted to identity sets to avoid dependency on #equals().
IdentityHashtable backupSet = buildIdentitySet(backup, containerPolicy, false);
IdentityHashtable sourceSet = null;
IdentityHashtable targetToSources = null;
// We need either a source or target-to-source set, depending on the merge type.
if (shouldMergeWorkingCopyIntoOriginal()) {
sourceSet = buildIdentitySet(source, containerPolicy, false);
} else {
targetToSources = buildIdentitySet(source, containerPolicy, true);
}
boolean changeOccured = false;
// If the backup is also the target, clone it; since it may change during the loop.
if (backup == target) {
backup = containerPolicy.cloneFor(backup);
}
// Handle removed elements.
for (Object backupIter = containerPolicy.iteratorFor(backup);
containerPolicy.hasNext(backupIter);) {
Object backupElement = containerPolicy.next(backupIter, getSession());
// The check for delete depends on the type of merge.
if (shouldMergeWorkingCopyIntoOriginal()) {// Source and backup are the same space.
if (!sourceSet.containsKey(backupElement)) {
changeOccured = true;
containerPolicy.removeFrom((Object)null, getTargetVersionOfSourceObject(backupElement), target, getSession());
// Registered new object in nested units of work must not be registered into the parent,
// so this records them in the merge to parent case.
if (mapping.isPrivateOwned()) {
registerRemovedNewObjectIfRequired(backupElement);
}
}
} else {// Target and backup are same for all types of merge.
if (!targetToSources.containsKey(backupElement)) {// If no source for target then was removed.
changeOccured = true;
containerPolicy.removeFrom((Object)null, backupElement, target, getSession());// Backup value is same as target value.
}
}
}
// Handle added elements.
for (Object sourceIter = containerPolicy.iteratorFor(source);
containerPolicy.hasNext(sourceIter);) {
Object sourceElement = containerPolicy.next(sourceIter, getSession());
// The target object must be completely merged before adding to the collection
// otherwise another thread could pick up the partial object.
mapping.cascadeMerge(sourceElement, this);
// The check for add depends on the type of merge.
if (shouldMergeWorkingCopyIntoOriginal()) {// Source and backup are the same space.
if (!backupSet.containsKey(sourceElement)) {
changeOccured = true;
containerPolicy.addInto(getTargetVersionOfSourceObject(sourceElement), target, getSession());
} else {
containerPolicy.validateElementAndRehashIfRequired(sourceElement, target, getSession(), getTargetVersionOfSourceObject(sourceElement));
}
} else {// Target and backup are same for all types of merge.
Object targetVersionOfSourceElement = getTargetVersionOfSourceObject(sourceElement);
if (!backupSet.containsKey(targetVersionOfSourceElement)) {// Backup value is same as target value.
changeOccured = true;
containerPolicy.addInto(targetVersionOfSourceElement, target, getSession());
}
}
}
return changeOccured;