| /* |
| * 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() + ")"; |
| } |
| } |