blob: b697dc9e36f49486c22d72e5394029cfd1ddf5fd [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.sdo;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import commonj.sdo.ChangeSummary;
import commonj.sdo.DataGraph;
import commonj.sdo.DataObject;
import commonj.sdo.Property;
import commonj.sdo.Sequence;
import commonj.sdo.helper.HelperContext;
import org.eclipse.persistence.sdo.helper.ListWrapper;
import org.eclipse.persistence.sdo.helper.SDOCopyHelper;
/**
* <p><b>Purpose</b>:A change summary is used to record changes to DataObjects.
* <p><b>Responsibilities</b>:<ul>
* <li> Track changes to DataObjects that are within the scope of this ChangeSummary (based on the root object of the Changesummary).
* <li> Track if those DataObjects are created, modified or deleted.
* <li> Return the values at the time that ChangeSummary logging was turned on for DataObjects in scope.
* </ul>
* <p>This class is implemented as a Memento (283) [GOF - Gamma, Helm, Johnson, Vlissides] Design Pattern.<br>
* (Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later.)<br>
* <p>The class also realizes some aspects of the Command (233) Pattern - the undo-able operation part (from the first change).<br>
* (Encapsulate a request as an object, thereby letting you parameterize clients with different requests,
* queue or log requests, and support undo-able operations.)
*/
public class SDOChangeSummary implements ChangeSummary {
private SDODataObject rootDataObject;
private boolean logging;
//loggingMapping is the boolean that's mapped in OX, we don't really want to turn
//on logging until after the XML has all been processed at which time we set the
//real logging boolean
private boolean loggingMapping;
private DataGraph dataGraph;
private List createdList;
private List deletedList;
private Map deepCopies;
private List createdXPaths;//for ox mapping
/** The deletedXPaths field is picked up reflectively during marshal/unmarshal operations. */
@SuppressWarnings("unused") private List deletedXPaths; //for ox mapping
private List modifiedDoms;
private Map unsetPropsMap;
/** To implement ChangeSummary undo we require a copy of the original state of our model.
* The originalValueStores will be populated when we start logging - it will then point to the
* currentValueStore while the currentValueStore will be shallow copied (its child ValueStores
* are shared between both ValueStores)
*/
private Map originalValueStores;// HashMap<DataObject, ValueStore>
private Map originalElements;
// old values
private Map oldContainer;// Key is DataObject, value is DataObject
private Map oldContainmentProperty;// Key is DataObject, value is Property
/** Cache map of originalSequences {@code Map<DataObject, Sequence>} */
private Map oldSequences;// Key is DataObject, value is Sequence
/** Map of originalSequence {@code Map<DataObject, Sequence>} */
private Map originalSequences;// Key is DataObject, value is Sequence
private Map unsetOCPropsMap;
/** A HashMap of List objects keyed off SDODataObject, null key:value permitted */
private Map oldSettings;// List of SDOSettings
/** */
private Map reverseDeletedMap;
/** Hold the context containing all helpers so that we can preserve inter-helper relationships */
private HelperContext aHelperContext;
public SDOChangeSummary() {
// HelperContext is set during unmarshalling in SDOUnmarshalListener
createdList = new ArrayList();
deepCopies = new HashMap();
deletedList = new ArrayList();
oldSettings = new HashMap();
oldContainer = new HashMap();
oldContainmentProperty = new HashMap();
unsetPropsMap = new HashMap();
unsetOCPropsMap = new HashMap();
originalValueStores = new HashMap();
originalElements = new HashMap();
reverseDeletedMap = new HashMap();
}
public SDOChangeSummary(SDODataObject dataObject, HelperContext aContext) {
this();
aHelperContext = aContext;
rootDataObject = dataObject;
}
public SDOChangeSummary(SDODataGraph dataGraph, HelperContext aContext) {
this();
aHelperContext = aContext;
this.dataGraph = dataGraph;
}
/**
* Indicates whether change logging is on (<code>true</code>) or off (<code>false</code>).
* @return <code>true</code> if change logging is on.
* @see #beginLogging
* @see #endLogging
*/
@Override
public boolean isLogging() {
return logging;
}
/**
* INTERNAL:
* Set flag created value.
* @param created flag created's new value.
*/
public void setCreated(DataObject anObject, boolean created) {
if (getRootObject() == anObject) {
return;
}
// Explicitly clear the flag
if (isLogging() && !created) {
createdList.remove(anObject);
}
if (isLogging() && !isCreated(anObject)) {
if (created) {
// remove from other sets
deletedList.remove(anObject);
// add to set
createdList.add(anObject);
}
}
}
/**
* INTERNAL:
* Set flag modified value.
* @param deleted flag modified's new value.
*/
public boolean setDeleted(DataObject anObject, boolean deleted) {
if (getRootObject() == anObject) {
return false;
}
// Explicitly clear the flag
if (isLogging() && !deleted) {
deletedList.remove(anObject);
}
if (isLogging() && !this.isDeleted(anObject)) {
if (deleted) {
// remove from other sets
if(isCreated(anObject)){
createdList.remove(anObject);
oldSettings.remove(anObject);
originalValueStores.remove(anObject);
originalElements.remove(anObject);
return false;
}else {
pauseLogging();
deletedList.add(anObject);
resumeLogging();
}
}
}
return true;
}
/**
* INTERNAL:
* @param aKey
* @param aValue
* void
*/
public void setOldContainer(SDODataObject aKey, DataObject aValue) {
oldContainer.put(aKey, aValue);
}
/**
* INTERNAL:
*
* @return
*/
public Map getOldContainers() {
return oldContainer;
}
/**
* INTERNAL:
* @param aKey DataObject
* @param aValue Property
* void
*
*/
public void setOldContainmentProperty(SDODataObject aKey, Property aValue) {
oldContainmentProperty.put(aKey, aValue);
}
/**
* INTERNAL:
* @param aKey DataObject
* @param aValue Property
* void
*
*/
public void setOldSequence(SDODataObject aKey, Sequence aValue) {
getOldSequences().put(aKey, aValue);
}
/**
* Returns the {@link DataGraph data graph} associated with this change summary or null.
* @return the data graph.
* @see DataGraph#getChangeSummary
*/
@Override
public DataGraph getDataGraph() {
return dataGraph;
}
/**
* Returns a list consisting of all the {@link DataObject data objects} that have been changed while {@link #isLogging logging}.
* <p>
* The {@link #isCreated new} and {@link #isModified modified} objects in the List are references to objects
* associated with this ChangeSummary.
* The {@link #isDeleted deleted} objects in the List are references to objects
* at the time that event logging was enabled;
* <p> Each changed object must have exactly one of the following methods return true:
* {@link #isCreated isCreated},
* {@link #isDeleted isDeleted}, or
* {@link #isModified isModified}.
* @return a list of changed data objects.
* @see #isCreated(DataObject)
* @see #isDeleted(DataObject)
* @see #isModified(DataObject)
*/
@Override
public List getChangedDataObjects() {
// merge all the sets
ArrayList aList = new ArrayList();
aList.addAll(getModified());
if (deletedList != null) {
aList.addAll(deletedList);
}
if (createdList != null) {
aList.addAll(createdList);
}
return aList;
}
/**
* INTERNAL:
* Return all modified objects
* @return
* Set
*/
public List getModified() {
ArrayList modifiedList = new ArrayList();
getModified(rootDataObject, modifiedList);
return modifiedList;
}
private void getModified(SDODataObject sdoDataObject, List modifiedList) {
if(null == sdoDataObject) {
return;
}
if(isModified(sdoDataObject)) {
modifiedList.add(sdoDataObject);
}
List<Property> properties = sdoDataObject.getInstanceProperties();
for(int x=0; x<properties.size(); x++) {
Property property = properties.get(x);
if(property.isContainment()) {
if(property.isMany()) {
List<SDODataObject> dataObjects = sdoDataObject.getList(property);
for(int y=0; y<dataObjects.size(); y++) {
getModified(dataObjects.get(y), modifiedList);
}
} else {
if ((property.getType() != null) && !(((SDOType)property.getType()).isChangeSummaryType())) {
getModified(sdoDataObject.getDataObject(property), modifiedList);
}
}
}
}
}
/**
* INTERNAL:
* Return all deleted objects
* @return
* Set
*/
public List getDeleted() {
return deletedList;
}
/**
* INTERNAL:
* Return all created objects
* @return
* Set
*/
public List getCreated() {
return createdList;
}
/**
* Returns whether or not the specified data object was created while {@link #isLogging logging}.
* Any object that was added to the scope
* but was not in the scope when logging began,
* will be considered created.
* @param dataObject the data object in question.
* @return <code>true</code> if the specified data object was created.
* @see #getChangedDataObjects
*/
@Override
public boolean isCreated(DataObject dataObject) {
return (createdList != null) && createdList.contains(dataObject);
}
/**
* Returns whether or not the specified data object was deleted while {@link #isLogging logging}.
* Any object that is not in scope but was in scope when logging began
* will be considered deleted.
* @param dataObject the data object in question.
* @return <code>true</code> if the specified data object was deleted.
* @see #getChangedDataObjects
*/
@Override
public boolean isDeleted(DataObject dataObject) {
return (deletedList != null) && deletedList.contains(dataObject);
}
/**
* Returns whether or not the specified data object was updated while {@link #isLogging logging}.
* An object that was contained in the scope when logging began
* and remains in the scope when logging ends will be considered potentially modified.
* <p> An object considered modified must have at least one old value setting.
* @param dataObject the data object in question.
* @return <code>true</code> if the specified data object was modified.
* @see #getChangedDataObjects
*/
@Override
public boolean isModified(DataObject dataObject) {
// a modified data object is present in the original value
// stores list and has not been deleted
if (this.originalValueStores.get(dataObject) == null || isDeleted(dataObject)) {
return false;
}
return true;
}
/**
* Returns a list of {@link ChangeSummary.Setting settings}
* that represent the property values of the given <code>dataObject</code>
* at the point when logging {@link #beginLogging() began}.
* <p>In the case of a {@link #isDeleted(DataObject) deleted} object,
* the List will include settings for all the Properties.
* <p> An old value setting indicates the value at the
* point logging begins. A setting is only produced for
* {@link #isModified modified} objects if
* either the old value differs from the current value or
* if the isSet differs from the current value.
* <p> No settings are produced for {@link #isCreated created} objects.
* @param dataObject the object in question.
* @return a list of settings.
* @see #getChangedDataObjects
*/
@Override
public List getOldValues(DataObject dataObject) {
if ((dataObject == null) || (!isDeleted(dataObject) && ((((SDODataObject)dataObject).getChangeSummary() != null) && (((SDODataObject)dataObject).getChangeSummary() != this)))) {
return new ArrayList();
}
if (!isCreated(dataObject) && isDirty(dataObject)) {
List oldSettingsList = new ArrayList();
for (int i = 0; i < dataObject.getInstanceProperties().size(); i++) {
SDOProperty nextProp = (SDOProperty)dataObject.getInstanceProperties().get(i);
Setting setting = getOldValueForChangedDataObject(dataObject, nextProp);
if (setting != null) {
oldSettingsList.add(setting);
}
}
List openProps = (List)getUnsetOCPropertiesMap().get(dataObject);
if(openProps != null){
for(int i=0; i< openProps.size(); i++){
SDOProperty nextProp = (SDOProperty)openProps.get(i);
Setting setting = getOldValueForChangedDataObject(dataObject, nextProp);
if (setting != null) {
oldSettingsList.add(setting);
}
}
}
return oldSettingsList;
}
return new ArrayList();// Note: spec did not mention null value case.
}
/**
* INTERNAL:
*
* @param dataObject
* @return
*/
public List getUnsetProps(DataObject dataObject) {
//call getOldValues to populate oldSettings which will populate unsetPropsMap
getOldValues(dataObject);
Object value = unsetPropsMap.get(dataObject);
if (value == null) {
return new ArrayList();
}
return (List)value;
}
/**
* INTERNAL:
* Return the entire HashMap of lists of open content properties that were unset
* keyed on dataObject
* @return
*/
public Map getUnsetOCPropertiesMap() {
return unsetOCPropsMap;
}
/**
* INTERNAL:
* Return a List containing all open content properties that were unset
* @param dataObject
* @return
*/
public List getUnsetOCProperties(DataObject dataObject) {
Object value = unsetOCPropsMap.get(dataObject);
if (null == value) {
return new ArrayList();
}
return (List)value;
}
/**
* INTERNAL:
* Add an open content property that has been unset to the list keyed on dataObject
* @param dataObject
* @param ocKey
*/
public void setUnsetOCProperty(DataObject dataObject, Property ocKey) {
Object value = unsetOCPropsMap.get(dataObject);
if (null == value) {
// create a new list and populate
List aList = new ArrayList();
aList.add(ocKey);
unsetOCPropsMap.put(dataObject, aList);
} else {
// dont replace existing key (ie we readd a previously unset oc property)
if (!((List)value).contains(ocKey)) {
((List)value).add(ocKey);
}
}
}
/**
* INTERNAL:
* Delete an open content property from the list of unset oc properties keyed on dataObject
* @param dataObject
* @param ocKey
*/
public void removeUnsetOCProperty(DataObject dataObject, Property ocKey) {
Object value = unsetOCPropsMap.get(dataObject);
if (value != null) {
// if we referenced the property - check if it is the only one left in the map value
if (((List)value).remove(ocKey)) {
if(!(((List)value).size() > 0)) {
unsetOCPropsMap.remove(dataObject);
}
}
}
}
/**
* Clears the List of {@link #getChangedDataObjects changes} and turns change logging on.
* No operation occurs if logging is already on.
* @see #endLogging
* @see #isLogging
*/
@Override
public void beginLogging() {
if (!logging) {
logging = true;
loggingMapping = true;
resetChanges();
rootDataObject.resetChanges();
}
}
/**
* INTERNAL:
* Turn both logging flags back on.
*/
public void resumeLogging() {
if (!logging) {
logging = true;
loggingMapping = true;
}
}
/**
* An implementation that requires logging may throw an UnsupportedOperationException.
* Turns change logging off. No operation occurs if logging is already off.
* @see #beginLogging
* @see #isLogging
*/
@Override
public void endLogging() {
logging = false;
loggingMapping = false;
//October 12, 1007 - as per the spec do not clear anything on Changesummary on endLogging
}
/**
* INTERNAL:
* Turn both logging flags on.
*/
public void pauseLogging() {
logging = false;
loggingMapping = false;
}
/**
* INTERNAL:
* Called from beginLogging and undoChanges
*/
private void resetChanges() {
createdList.clear();
deletedList.clear();
// See spec. p.30 "List of changed DataObjects cleared"
oldSettings.clear();
deepCopies.clear();
oldContainer.clear();
oldContainmentProperty.clear();
unsetPropsMap.clear();
unsetOCPropsMap.clear();
originalValueStores.clear();
originalElements.clear();
reverseDeletedMap.clear();
getOldSequences().clear();
getOriginalSequences().clear();
}
/**
* Returns the ChangeSummary root DataObject - the object from which
* changes are tracked.
* When a DataGraph is used, this is the same as getDataGraph().getRootObject().
* @return the ChangeSummary root DataObject
*/
@Override
public SDODataObject getRootObject() {
return rootDataObject;
}
/**
* Returns a {@link ChangeSummary.Setting setting} for the specified property
* representing the property value of the given <code>dataObject</code>
* at the point when logging {@link #beginLogging() began}.
* <p>Returns null if the property was not modified and
* has not been {@link #isDeleted(DataObject) deleted}.
* @param dataObject the object in question.
* @param property the property of the object.
* @return the Setting for the specified property.
* @see #getChangedDataObjects
*/
@Override
public SDOChangeSummary.Setting getOldValue(DataObject dataObject, Property property) {
if ((dataObject == null) || (!isDeleted(dataObject) && ((((SDODataObject)dataObject).getChangeSummary() != null) && (((SDODataObject)dataObject).getChangeSummary() != this)))) {
return null;
}
if (!isCreated(dataObject) && isDirty(dataObject)) {
return getOldValueForChangedDataObject(dataObject, (SDOProperty) property);
}
return null;
}
/**
* INTERNAL:
* @param dataObject which is not null and not created and is dirty in the scope of this changesummary
* @param property
* @return new or already existing Setting
*/
private ChangeSummary.Setting getOldValueForChangedDataObject(DataObject dataObject, SDOProperty property) {
if ((null == property) || property.getType().isChangeSummaryType()) {
return null;
}
Setting setting = getPropertyInOldSettings(dataObject, property);
if (setting == null) {
SDODataObject sdoDataObject = ((SDODataObject)dataObject);
boolean isDeleted = isDeleted(dataObject);
Object oldValue = getPropertyInternal(sdoDataObject, property);
Object currentValue = sdoDataObject.getPropertyInternal(property);
boolean isSet = sdoDataObject.isSetInternal(property);
boolean wasSet = wasSet(sdoDataObject, property);
if (property.isMany()) {
currentValue = ((ListWrapper)currentValue).getCurrentElements();
if (isDirty(((ListWrapper)oldValue))) {
List elements = (List)getOriginalElements().get(oldValue);
oldValue = new ArrayList(elements);
if (!property.getType().isDataType()) {
for (int i = 0; i < ((List)oldValue).size(); i++) {
Object next = ((List)oldValue).get(i);
Object deepCopy = getOrCreateDeepCopy((DataObject)next);
((List)oldValue).set(i, deepCopy);
}
}
} else {
oldValue = currentValue;
}
}
if (isDeleted || ((wasSet != isSet) || (oldValue != currentValue))) {
if ((oldValue != null) && !property.getType().isDataType()) {
if (oldValue instanceof DataObject) {
oldValue = getOrCreateDeepCopy((DataObject)oldValue);
}
}
setting = buildAndAddOldSetting((SDODataObject)dataObject, property, oldValue, wasSet);
}
}
return setting;
}
/**
* INTERNAL:
*
* @param dataObject
* @param property
* @param value
* @param isSet
* @return
*/
private Setting buildAndAddOldSetting(SDODataObject dataObject, Property property, Object value, boolean isSet) {
SDOSetting setting = new SDOSetting(property, value);
setting.setIsSet(isSet);
if (oldSettings.get(dataObject) == null) {
List aList = new ArrayList();
aList.add(setting);
oldSettings.put(dataObject, aList);
} else {
List theList = (List)oldSettings.get(dataObject);
theList.add(setting);
}
if (!setting.isSet()) {
List theList = (List)unsetPropsMap.get(dataObject);
if (theList == null) {
List aList = new ArrayList();
aList.add(setting.getProperty().getName());
unsetPropsMap.put(dataObject, aList);
} else {
if (!theList.contains(setting.getProperty().getName())) {
theList.add(setting.getProperty().getName());
}
}
}
return setting;
}
/**
* Returns the value of the {@link DataObject#getContainer container} data object
* at the point when logging {@link #beginLogging() began}.
* @param dataObject the object in question.
* @return the old container data object.
*/
@Override
public SDODataObject getOldContainer(DataObject dataObject) {
return (SDODataObject) oldContainer.get(dataObject);
}
/**
* Returns the value of the {@link DataObject#getContainmentProperty containment property} data object property
* at the point when logging {@link #beginLogging() began}.
* @param dataObject the object in question.
* @return the old containment property.
*/
@Override
public SDOProperty getOldContainmentProperty(DataObject dataObject) {
return (SDOProperty) oldContainmentProperty.get(dataObject);
}
/**
* Returns the value of the {@link DataObject#getSequence sequence} for the data object
* at the point when logging {@link #beginLogging() began}.
* @param dataObject the object in question.
* @return the old containment property.
*/
@Override
public SDOSequence getOldSequence(DataObject dataObject) {
if ((dataObject == null) || (!isDeleted(dataObject) && ((((SDODataObject)dataObject).getChangeSummary() != null) && (((SDODataObject)dataObject).getChangeSummary() != this)))) {
return null;
}
if (!isCreated(dataObject) && dataObject.getType().isSequenced()) {
// check cache first
if (getOldSequences().containsKey(dataObject)) {
return (SDOSequence) getOldSequences().get(dataObject);
}
// no sequence - get from the original sequence map
SDOSequence originalSeq = (SDOSequence) getOriginalSequences().get(dataObject);
if (originalSeq == null) {
originalSeq = (SDOSequence) dataObject.getSequence();
}
SDOSequence seqWithDeepCopies = new SDOSequence((SDODataObject) dataObject);
for (int i = 0; i < originalSeq.size(); i++) {
// setting/value may be null in some cases
Object nextOriginalSettingValue = originalSeq.getValue(i);
if (nextOriginalSettingValue == null) {
continue;
}
// property may be null if the setting contains unstructured text
SDOProperty nextOriginalSettingProp = originalSeq.getProperty(i);
if (nextOriginalSettingProp == null) {
// handle unstructured text
seqWithDeepCopies.addText(nextOriginalSettingValue.toString());
} else if (nextOriginalSettingProp.getType().isDataType()) {
// handle simple types
seqWithDeepCopies.addSettingWithoutModifyingDataObject(nextOriginalSettingProp, nextOriginalSettingValue, false);
} else {
// handle complex types
seqWithDeepCopies.addSettingWithoutModifyingDataObject(nextOriginalSettingProp, getOrCreateDeepCopy((DataObject) nextOriginalSettingValue), false);
}
}
// store deep copy of old sequence in cache
getOldSequences().put(dataObject, seqWithDeepCopies);
return seqWithDeepCopies;
}
return null;
}
/**
* This method is intended for use by service implementations only.
* Undoes all changes in the log to restore the tree of
* DataObjects to its original state when logging began.
* isLogging() is unchanged. The log is cleared.
* @see #beginLogging
* @see #endLogging
* @see #isLogging
*/
@Override
public void undoChanges() {
/**
* See Jira SDO-109/107 and 125/225 for open issues with move optimization
* See bug#5882923 for smart local undo via set()/unset()
*/
Property oldProp = getOldContainmentProperty(rootDataObject);
String oldName = null;
if (oldProp != null) {
oldName = oldProp.getName();
}
rootDataObject.undoChanges(true, this, getOldContainer(rootDataObject), oldName);
resetChanges();
rootDataObject.resetChanges();
}
/**
* INTERNAL:
* Set the root DataObject for this ChangeSummary.
* @param dataObject the root of DataObject tree this ChangeSummary belongs to
*/
public void setRootDataObject(DataObject dataObject) {
rootDataObject = (SDODataObject)dataObject;
}
/**
* INTERNAL:
* Used by CopyHelper to set logging when creating a copy of a changesummary
* @param logging logging status
*/
public void setLogging(boolean logging) {
if (logging) {
beginLogging();
} else {
endLogging();
}
this.loggingMapping = logging;
}
/**
* INTERNAL:
* Check if a property is in its DataObject's oldsetting list
* @param dataObject property's owner
* @param property property to be checked
* @return property's Setting if this property is in list
*/
private ChangeSummary.Setting getPropertyInOldSettings(DataObject dataObject, Property property) {
Iterator iterOldSettings = null;
List aList = (List)oldSettings.get(dataObject);
if (aList != null) {
iterOldSettings = aList.iterator();
ChangeSummary.Setting curSetting;
while (iterOldSettings.hasNext()) {
curSetting = (ChangeSummary.Setting)iterOldSettings.next();
if (curSetting.getProperty().equals(property)) {
return curSetting;
}
}
}
return null;
}
/**
* INTERNAL:
* Set the helperContext if the default SDOChangeSummary constructor was used
* @param helperContext
*/
public void setHelperContext(HelperContext helperContext) {
aHelperContext = helperContext;
}
/**
* INTERNAL:
*
* @param createdXPathsList
*/
public void setCreatedXPaths(List createdXPathsList) {
createdXPaths = createdXPathsList;
}
/**
* INTERNAL:
*
* @return
*/
public List getCreatedXPaths() {
return createdXPaths;
}
/**
* INTERNAL:
* Return the logging state during mapping operations
* @return logging state
*/
public boolean isLoggingMapping() {
return loggingMapping;
}
/**
* INTERNAL:
*
* @param modifiedDomsList
*/
public void setModifiedDoms(List modifiedDomsList) {
modifiedDoms = modifiedDomsList;
}
/**
* INTERNAL:
*
* @return
*/
public List getModifiedDoms() {
return modifiedDoms;
}
/**
* INTERNAL:
* The deletedXPaths field is picked up reflectively during marshal/unmarshal operations.
* @param deletedXPathsList
*/
public void setDeletedXPaths(List deletedXPathsList) {
deletedXPaths = deletedXPathsList;
}
public Map getOldContainmentProperty() {
return oldContainmentProperty;
}
public Map getOldContainer() {
return oldContainer;
}
/**
* INTERNAL:
*
* @param dataObject
* @param property
* @return
*/
public boolean wasSet(DataObject dataObject, Property property) {
ValueStore vs = (ValueStore)originalValueStores.get(dataObject);
if (null == vs) {
vs = ((SDODataObject)dataObject)._getCurrentValueStore();
}
if (property.isOpenContent()) {
return vs.isSetOpenContentProperty(property);
} else {
return vs.isSetDeclaredProperty(((SDOProperty)property).getIndexInType());
}
}
/**
* INTERNAL:
*
* @param dataObject
* @param property
* @return
*/
public Object getPropertyInternal(DataObject dataObject, Property property) {
ValueStore vs = (ValueStore)originalValueStores.get(dataObject);
if (null == vs) {
vs = ((SDODataObject)dataObject)._getCurrentValueStore();
}
if (property.isOpenContent()) {
return vs.getOpenContentProperty(property);
} else {
return vs.getDeclaredProperty(((SDOProperty)property).getIndexInType());
}
}
/**
* INTERNAL:
*
* @param dataObject
* @param property
* @param value
*/
public void setPropertyInternal(DataObject dataObject, Property property, Object value) {
ValueStore vs = (ValueStore)originalValueStores.get(dataObject);
if (property.isOpenContent()) {
vs.setOpenContentProperty(property, value);
} else {
vs.setDeclaredProperty(((SDOProperty)property).getIndexInType(), value);
}
}
/**
* INTERNAL:
* Return the map of original ValueStores keyed on
* @return
*/
public Map getOriginalValueStores() {
return originalValueStores;
}
/**
* INTERNAL:
* Return whether the <code>dataObject</code> has been modified.
* @param dataObject
* @return
*/
public boolean isDirty(DataObject dataObject) {
ValueStore vs = (ValueStore)originalValueStores.get(dataObject);
return vs != null;
}
/**
* INTERNAL:
* Return whether the <code>aListWrapper</code> has been modified.
* @param aListWrapper
* @return
*/
public boolean isDirty(ListWrapper aListWrapper) {
Object originalList = getOriginalElements().get(aListWrapper);
return originalList != null;
}
/**
* INTERNAL:
* Return whether the <code>aSequence</code> has been modified.
* @param aSequence
* @return
*/
public boolean isDirty(SDOSequence aSequence) {
Object originalSequence = getOriginalSequences().get(aSequence.getDataObject());
return originalSequence != null;
}
/**
* INTERNAL:
*
* @param dataObject
* @param property
*/
public void unsetPropertyInternal(DataObject dataObject, Property property) {
ValueStore vs = (ValueStore)originalValueStores.get(dataObject);
if (property.isMany()) {
ListWrapper currentValue = (ListWrapper)dataObject.getList(property);
originalElements.put(currentValue, new ArrayList());
if (property.isOpenContent()) {
vs.unsetOpenContentProperty(property);
} else {
vs.unsetDeclaredProperty(((SDOProperty)property).getIndexInType());
}
} else {
if (property.isOpenContent()) {
vs.unsetOpenContentProperty(property);
} else {
vs.unsetDeclaredProperty(((SDOProperty)property).getIndexInType());
}
}
}
/**
* INTERNAL:
* @return Map of original elements, key and value are both listwrappers
*/
public Map getOriginalElements() {
return originalElements;
}
/**
* INTERNAL:
* @param original
* @return
*/
private DataObject getOrCreateDeepCopy(DataObject original) {
DataObject value = (DataObject)getDeepCopies().get(original);
if (null == value) {
DataObject undoneCopy = ((SDOCopyHelper)aHelperContext.getCopyHelper()).copy(original, this);
// if original is null we will create only 1 key:value null:null pair
getDeepCopies().put(original, undoneCopy);
getReverseDeletedMap().put(undoneCopy, original);
return undoneCopy;
} else {
return value;
}
}
/**
* INTERNAL:
* @return Map of deep copies of DataObjects key is original dataobject
*/
public Map getDeepCopies() {
return deepCopies;
}
/**
* INTERNAL:
* @return Map of deep copies of DataObjects key is copy of dataobject
*/
public Map getReverseDeletedMap() {
return reverseDeletedMap;
}
/**
* INTERNAL:
* Return a map of original sequences keyed on DataObject.
* @return Map of old Sequences
*/
public Map getOriginalSequences() {
if (null == originalSequences) {
originalSequences = new HashMap();
}
return originalSequences;
}
/**
* INTERNAL:
* Return a map of original sequences keyed on DataObject (cached values).
* @return Map of old Sequences that have deep copies of all DataObjects
*/
public Map getOldSequences() {
if (null == oldSequences) {
oldSequences = new HashMap();
}
return oldSequences;
}
/**
* INTERNAL:
* Return the string representation of the receiver.
*/
@Override
public String toString() {
StringBuffer aBuffer = new StringBuffer();
aBuffer.append("ChangeSummary@");
aBuffer.append(getClass().hashCode());
aBuffer.append(" [logging: ");
aBuffer.append(logging);
aBuffer.append(", root: ");
aBuffer.append(rootDataObject);
List aList = getChangedDataObjects();
if (aList != null) {
aBuffer.append(", ");
aBuffer.append(aList.size());
aBuffer.append(" changes: <");
boolean first = true;
for (Iterator i = aList.iterator(); i.hasNext();) {
if (first) {
first = false;
} else {
aBuffer.append(", ");
}
aBuffer.append(i.next());// null values are handled
}
}
aBuffer.append(">]");
return aBuffer.toString();
}
}