blob: 72ee9067814591dc8ab204301f57934c73b3c0b4 [file] [log] [blame]
/*
* Copyright (c) 1998, 2020 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.descriptors.changetracking;
import java.beans.*;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.descriptors.changetracking.*;
import org.eclipse.persistence.internal.helper.*;
import org.eclipse.persistence.mappings.foundation.*;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.internal.sessions.UnitOfWorkChangeSet;
import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl;
import org.eclipse.persistence.exceptions.ValidationException;
/**
* <p>
* <b>Purpose</b>: Define a listener for attribute change tracking.
* <p>
* <b>Description</b>: Listener is notified on a PropertyChangeEvent from the object it belongs to.
* <p>
* <b>Responsibilities</b>: Set the flag to true and build ObjectChangeSet that includes the
* ChangeRecords for the changed attributes.
*/
public class AttributeChangeListener extends ObjectChangeListener {
protected transient ClassDescriptor descriptor;
protected transient UnitOfWorkImpl uow;
protected org.eclipse.persistence.internal.sessions.ObjectChangeSet objectChangeSet;
protected Object owner;
/**
* INTERNAL:
* Create a AttributeChangeListener with a descriptor and unit of work
*/
public AttributeChangeListener(ClassDescriptor descriptor, UnitOfWorkImpl uow, Object owner) {
super();
this.descriptor = descriptor;
this.uow = uow;
this.owner = owner;
}
/**
* INTERNAL:
* Return the object change set associated with this listener
*/
public org.eclipse.persistence.internal.sessions.ObjectChangeSet getObjectChangeSet() {
return objectChangeSet;
}
/**
* INTERNAL:
* Return the object change set associated with this listener
*/
public void setObjectChangeSet(org.eclipse.persistence.internal.sessions.ObjectChangeSet changeSet) {
this.objectChangeSet = changeSet;
}
/**
* INTERNAL:
* Return the descriptor associated with this listener
*/
public ClassDescriptor getDescriptor() {
return descriptor;
}
/**
* INTERNAL:
* Set the descriptor associated with this listener
*/
public void setDescriptor(ClassDescriptor descriptor) {
this.descriptor = descriptor;
}
/**
* INTERNAL:
* Return the unit of work associated with this listener
*/
public UnitOfWorkImpl getUnitOfWork() {
return uow;
}
/**
* INTERNAL:
* Set the unit of work associated with this listener
*/
public void setUnitOfWork(UnitOfWorkImpl uow) {
this.uow = uow;
}
/**
* PUBLIC:
* This method creates the object change set if necessary. It also creates/updates
* the change record based on the new value. Object should check the if newValue and
* oldValue are identical. If they are identical, do not create PropertyChangeEvent
* and call this method.
*/
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (this.ignoreEvents){
return;
}
internalPropertyChange(evt);
}
/**
* INTERNAL:
* This method marks the object as changed. This method is only
* called by EclipseLink
*/
@Override
public void internalPropertyChange(PropertyChangeEvent evt) {
if (evt.getNewValue() == evt.getOldValue()) {
return;
}
DatabaseMapping mapping = descriptor.getObjectBuilder().getMappingForAttributeName(evt.getPropertyName());
//Bug#4127952 Throw an exception indicating there is no mapping for the property name.
if (mapping == null) {
throw ValidationException.wrongPropertyNameInChangeEvent(owner.getClass(), evt.getPropertyName());
}
if (mapping instanceof AbstractDirectMapping || mapping instanceof AbstractTransformationMapping) {
//If both newValue and oldValue are null, or newValue is not null and newValue equals oldValue, don't build ChangeRecord
if (((evt.getNewValue() == null) && (evt.getOldValue() == null)) || ((evt.getNewValue() != null) && (evt.getNewValue()).equals(evt.getOldValue()))) {
return;
}
}
super.internalPropertyChange(evt);
if (uow.getUnitOfWorkChangeSet() == null) {
uow.setUnitOfWorkChangeSet(new UnitOfWorkChangeSet(uow));
}
if (objectChangeSet == null) {//only null if new or if in a new UOW
//add to tracker list to prevent GC of clone if using weak references
//put it in here so that it only occurs on the 1st change for a particular UOW
uow.addToChangeTrackedHardList(owner);
objectChangeSet = getDescriptor().getObjectBuilder().createObjectChangeSet(owner, (UnitOfWorkChangeSet) uow.getUnitOfWorkChangeSet(), false, uow);
}
if (evt.getClass().equals(ClassConstants.PropertyChangeEvent_Class)) {
mapping.updateChangeRecord(evt.getSource(), evt.getNewValue(), evt.getOldValue(), objectChangeSet, getUnitOfWork());
} else if (evt.getClass().equals(ClassConstants.CollectionChangeEvent_Class) || (evt.getClass().equals(ClassConstants.MapChangeEvent_Class))) {
mapping.updateCollectionChangeRecord((CollectionChangeEvent)evt, objectChangeSet, getUnitOfWork());
} else {
throw ValidationException.wrongChangeEvent(evt.getClass());
}
}
/**
* INTERNAL:
* Clear the changes in this listener
*/
@Override
public void clearChanges(boolean forRefresh) {
super.clearChanges(forRefresh);
if (forRefresh && this.objectChangeSet != null){
this.objectChangeSet.clear(true);
}
this.objectChangeSet = null;
}
@Override
public String toString() {
return getClass().getSimpleName() + "(" + getObjectChangeSet() + ")";
}
}