blob: 2f67b051da7a21882bb76da1dd8dfcc1ca9334d0 [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.internal.sessions;
import org.eclipse.persistence.indirection.IndirectCollection;
import org.eclipse.persistence.internal.queries.ContainerPolicy;
/**
* Abstract change record for collection type records that allow deferrable change detection.
* Used for change tracking when user sets entire collection.
*/
public abstract class DeferrableChangeRecord extends ChangeRecord {
/**
* Used for change tracking when user sets entire collection.
*/
protected transient Object originalCollection;
/**
* Used for change tracking when user sets entire collection.
*/
protected transient Object latestCollection;
/**
* Defines if this change should be calculated at commit time using the two collections.
* This is used to handle collection replacement.
*/
protected boolean isDeferred = false;
protected DeferrableChangeRecord() {
super();
}
protected DeferrableChangeRecord(ObjectChangeSet owner) {
this.owner = owner;
}
/**
* Returns if this change should be calculated at commit time using the two collections.
* This is used to handle collection replacement.
*/
public boolean isDeferred() {
return isDeferred;
}
/**
* Sets if this change should be calculated at commit time using the two collections.
* This is used to handle collection replacement.
*/
public void setIsDeferred(boolean isDeferred) {
this.isDeferred = isDeferred;
}
/**
* Used for change tracking when user sets entire collection.
* This is the last collection that was set on the object.
*/
public Object getLatestCollection() {
return latestCollection;
}
/**
* Used for change tracking when user sets entire collection.
* This is the original collection that was set on the object when it was cloned.
*/
public Object getOriginalCollection() {
return originalCollection;
}
/**
* Used for change tracking when user sets entire collection.
* This is the last collection that was set on the object.
*/
public void setLatestCollection(Object latestCollection) {
this.latestCollection = latestCollection;
}
/**
* Used for change tracking when user sets entire collection.
* This is the original collection that was set on the object when it was cloned.
*/
public void setOriginalCollection(Object originalCollection) {
this.originalCollection = originalCollection;
}
/**
* Recreates the original state of currentCollection.
*/
abstract public void internalRecreateOriginalCollection(Object currentCollection, AbstractSession session);
/**
* Clears info about added / removed objects set by change tracker.
* Called after the change info has been already used for creation of originalCollection.
* Also called to make sure there is no change info before comparison for change is performed
* (change info is still in the record after comparison for change is performed
* and may cause wrong results when the second comparison for change performed on the same change record).
*/
abstract public void clearChanges();
/**
* Recreates the original state of the collection.
*/
public void recreateOriginalCollection(Object currentCollection, AbstractSession session) {
if(currentCollection == null) {
this.setOriginalCollection(null);
return;
}
if(currentCollection instanceof IndirectCollection) {
// to avoid raising event when we add/remove elements from this collection later in this method.
setOriginalCollection(((IndirectCollection)currentCollection).getDelegateObject());
} else {
setOriginalCollection(currentCollection);
}
internalRecreateOriginalCollection(this.originalCollection, session);
clearChanges();
}
/**
* ADVANCED:
* If the owning UnitOfWork has shouldChangeRecordKeepOldValue set to true,
* then return the old value of the attribute represented by this ChangeRecord.
*/
@Override
public Object getOldValue() {
if(this.originalCollection != null) {
return this.originalCollection;
} else {
if(getOwner() != null) {
Object obj = ((org.eclipse.persistence.internal.sessions.ObjectChangeSet)getOwner()).getUnitOfWorkClone();
AbstractSession session = ((org.eclipse.persistence.internal.sessions.UnitOfWorkChangeSet)getOwner().getUOWChangeSet()).getSession();
if(obj != null && session != null) {
Object currentCollection = this.mapping.getAttributeValueFromObject(obj);
ContainerPolicy cp = this.mapping.getContainerPolicy();
Object cloneCurrentCollection = cp.containerInstance(cp.sizeFor(currentCollection));
for (Object valuesIterator = cp.iteratorFor(currentCollection); cp.hasNext(valuesIterator);) {
Object member = cp.next(valuesIterator, session);
cp.addInto(cp.keyFromIterator(valuesIterator), member, cloneCurrentCollection , session);
}
return getOldValue(cloneCurrentCollection, session);
}
}
return null;
}
}
public Object getOldValue(Object currentCollection, AbstractSession session) {
if(currentCollection != null) {
if(currentCollection instanceof IndirectCollection) {
currentCollection = ((IndirectCollection)currentCollection).getDelegateObject();
}
internalRecreateOriginalCollection(currentCollection, session);
}
return currentCollection;
}
}