| /* |
| * 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.helper; |
| |
| import commonj.sdo.Property; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.persistence.sdo.DefaultValueStore; |
| import org.eclipse.persistence.sdo.SDOChangeSummary; |
| import org.eclipse.persistence.sdo.SDODataObject; |
| import org.eclipse.persistence.sdo.SDOProperty; |
| import org.eclipse.persistence.sdo.SDOSequence; |
| import org.eclipse.persistence.sdo.ValueStore; |
| import org.eclipse.persistence.exceptions.SDOException; |
| import commonj.sdo.ChangeSummary; |
| import commonj.sdo.DataObject; |
| import commonj.sdo.helper.CopyHelper; |
| import commonj.sdo.helper.HelperContext; |
| import commonj.sdo.impl.HelperProvider; |
| import org.eclipse.persistence.oxm.XMLRoot; |
| import org.eclipse.persistence.oxm.sequenced.Setting; |
| |
| /** |
| * <b>Purpose:</b> |
| * <ul><li>A helper class for making deep or shallow copies of DataObjects.</li> |
| * </ul> |
| * <p> |
| * <b>Responsibilities:</b> |
| * <ul> |
| * <li>Perform shallow and deep copy operations on {@link DataObject DataObject}s.</li> |
| * </ul> |
| * |
| * @see org.eclipse.persistence.sdo.SDODataObject |
| * @since Oracle TopLink 11.1.1.0.0 |
| */ |
| public class SDOCopyHelper implements CopyHelper { |
| |
| /* |
| 09/12/06 - Add bidirectional property copy support |
| 09/16/06 - Use design #1a over #2 |
| Modify 1 pass opposite property set algorithm to use 2 passes, |
| the first gathers a list of opposites, the 2nd pass iterates the list after tree completion. |
| Runtime is the same since we still need a hash lookup of the opposite DO |
| 09/19/06 - Adjust copy algorithm to check for opposites before checking containment |
| cont=false, opp=false -> unidirectional |
| cont=false, opp=true -> bidirectional |
| cont=true, opp=false -> normal containment |
| cont=true, opp=true -> bidirectional |
| The code for copyPropertyValue() has been moved up to copy() |
| where we use the two cached maps to set non-containment properties |
| 10/02/06 - Finish isMany 1-n bidirectional/unidirectional support in copy() function |
| 11/09/06 - Jira#129: implement HelperContext via new HelperProvider.getDefaultContext() |
| 01/29/07 - #5852525 handle null properties with isSet=true |
| 02/14/07 - #5878605 do not generate oldSettings during copy of cs with logging=true |
| 02/15/07 - #5878605 move double iteration of cs list in many case to single iteration |
| 02/23/07 - #5897730 implement ChangeSummary deep copy |
| 03/14/07 - p.142 limit scope of while by using for |
| - put isSet() check back into open content part of copyChangeSummary |
| 04/11/07 - Implement Sequence functionality |
| 05/01/07 - #6026714: because of enlarged scope, copy of copy sets previous iteration |
| value of unset complex setting |
| */ |
| |
| // hold the context containing all helpers so that we can preserve inter-helper relationships |
| private HelperContext aHelperContext; |
| |
| /** |
| * INTERNAL: |
| * This default constructor must be used in conjunction with the setHelperContext() function. |
| * The custom constructor that takes a HelperContext parameter is recommended over this default constructor. |
| */ |
| public SDOCopyHelper() { |
| } |
| |
| /** |
| * Constructor that takes in a HelperContext instance that contains this copyHelper.<br> |
| * This is the recommended constructor. |
| * @param aContext |
| */ |
| public SDOCopyHelper(HelperContext aContext) { |
| aHelperContext = aContext; |
| } |
| |
| /** |
| * Create a shallow copy of the DataObject dataObject: |
| * Creates a new DataObject copiedDataObject with the same values |
| * as the source dataObject for each property where |
| * property.getType().isDataType() is true. |
| * The value of such a Property property in copiedDataObject is: |
| * dataObject.get(property) for single-valued Properties |
| * (copiedDataObject.get(property) equals() dataObject.get(property)), or |
| * a List where each member is equal to the member at the |
| * same index in dataObject for multi-valued Properties |
| * copiedDataObject.getList(property).get(i) equals() dataObject.getList(property).get(i) |
| * The copied Object is unset for each Property where |
| * property.getType().isDataType() is false |
| * since they are not copied. |
| * Read-only properties are copied. |
| * A copied object shares metadata with the source object |
| * sourceDO.getType() == copiedDO.getType() |
| * If a ChangeSummary is part of the source DataObject |
| * the copy has a new, empty ChangeSummary. |
| * Logging state is the same as the source ChangeSummary. |
| * |
| * @param dataObject to be copied |
| * @return copy of dataObject |
| */ |
| @Override |
| public DataObject copyShallow(DataObject dataObject) { |
| if (null == dataObject) { |
| return null; |
| } |
| SDODataObject copy = (SDODataObject)getHelperContext().getDataFactory().create(dataObject.getType().getURI(), dataObject.getType().getName()); |
| |
| List ocListOriginal = ((SDODataObject)dataObject)._getOpenContentProperties(); |
| for (Iterator anOCIterator = ocListOriginal.iterator(); anOCIterator.hasNext();) { |
| copy.addOpenContentProperty((Property)anOCIterator.next()); |
| } |
| |
| List ocAttrsListOriginal = ((SDODataObject)dataObject)._getOpenContentPropertiesAttributes(); |
| for (Iterator anOCAttrIterator = ocAttrsListOriginal.iterator(); anOCAttrIterator.hasNext();) { |
| copy.addOpenContentProperty((Property)anOCAttrIterator.next()); |
| } |
| |
| List allProperties = copy.getInstanceProperties();// start iterating all copy's properties |
| Iterator iterProperties = allProperties.iterator(); |
| while (iterProperties.hasNext()) { |
| SDOProperty eachProperty = (SDOProperty)iterProperties.next(); |
| if (dataObject.isSet(eachProperty)) { |
| Object o = getValue((SDODataObject)dataObject, eachProperty, null); |
| if (eachProperty.getType().isDataType()) { |
| if (!eachProperty.getType().isChangeSummaryType()) { |
| // we defer sequence updates at this point |
| copy.setInternal(eachProperty, o, false);// make copy if current property is datatype |
| } |
| } |
| } |
| } |
| |
| if (dataObject.getType().isSequenced()) { |
| List<Setting> settings = ((SDOSequence)dataObject.getSequence()).getSettings(); |
| for (int index = 0, size = dataObject.getSequence().size(); index < size; index++) { |
| Setting nextSetting = settings.get(index); |
| |
| Property prop = dataObject.getSequence().getProperty(index); |
| if (prop == null || prop.getType().isDataType()) { |
| Setting copySetting = nextSetting.copy(copy); |
| copy.getSequence().getSettings().add(copySetting); |
| copy.getSequence().addValueToSettings(copySetting); |
| } |
| } |
| } |
| |
| if ((copy != null) && (copy.getChangeSummary() != null) && (copy.getType().getChangeSummaryProperty() != null)) { |
| if (((SDODataObject)dataObject).getChangeSummary().isLogging()) { |
| copy.getChangeSummary().setLogging(true); |
| } |
| } |
| |
| return copy; |
| } |
| |
| /** |
| * Create a deep copy of the DataObject tree: |
| * Copies the dataObject and all its {@link commonj.sdo.Property#isContainment() contained} |
| * DataObjects recursively. |
| * Values of Properties are copied as in shallow copy, |
| * and values of Properties where |
| * property.getType().isDataType() is false |
| * are copied where each value copied must be a |
| * DataObject contained by the source dataObject. |
| * If a DataObject is outside the DataObject tree and the |
| * property is bidirectional, then the DataObject is skipped. |
| * If a DataObject is outside the DataObject tree and the |
| * property is unidirectional, then the same DataObject is referenced. |
| * Read-only properties are copied. |
| * If any DataObject referenced is not in the containment |
| * tree an IllegalArgumentException is thrown. |
| * If a ChangeSummary is part of the copy tree the new |
| * ChangeSummary refers to objects in the new DataObject tree. |
| * Logging state is the same as the source ChangeSummary. |
| * |
| * @param dataObject to be copied. |
| * @return copy of dataObject |
| * @throws IllegalArgumentException if any referenced DataObject |
| * is not part of the containment tree. |
| */ |
| @Override |
| public DataObject copy(DataObject dataObject) throws IllegalArgumentException { |
| return copy(dataObject, null); |
| } |
| |
| /** |
| * Create a deep copy of the DataObject tree: |
| * Copies the dataObject and all its {@link commonj.sdo.Property#isContainment() contained} |
| * DataObjects recursively. |
| * <p>For each Property where property.type.dataType is true, the values of Properties are |
| * copied as in shallow copy, and values of Properties where |
| * property.getType().isDataType() is false |
| * are copied where each value copied must be a |
| * DataObject contained by the source dataObject. |
| * <p>If a DataObject is outside the DataObject tree and the |
| * property is bidirectional, then the DataObject is not copied and |
| * references to the object are also not copied. |
| * <p>If a DataObject is outside the DataObject tree and the |
| * property is unidirectional, then the same DataObject is referenced. |
| * <p>Read-only properties are copied. |
| * <p>If any DataObject referenced is not in the containment |
| * tree an IllegalArgumentException is thrown. |
| * <p>If a ChangeSummary is part of the copy tree the new |
| * ChangeSummary refers to objects in the new DataObject tree. |
| * Logging state is the same as the source ChangeSummary. |
| * |
| * @param dataObject to be copied. |
| * @return copy of dataObject |
| * @throws IllegalArgumentException if any referenced DataObject |
| * is not part of the containment tree. |
| */ |
| public DataObject copy(DataObject dataObject, SDOChangeSummary cs) throws IllegalArgumentException { |
| if (null == dataObject) { |
| return null; |
| } else { |
| // cache one side of opposite property objects as we iterate to the 2nd side - non-deleted objects only |
| //* @param doMap (cache original -> copy DataObject instances to set non-containment properties after tree construction) |
| HashMap doMap = new HashMap(); |
| |
| //* @param propMap (cache original DO:non-containment property values to be set after tree construction) |
| HashMap ncPropMap = new HashMap(); |
| |
| // build object instances by recursing the tree |
| SDODataObject aCopy = copyPrivate((SDODataObject)dataObject, doMap, ncPropMap, cs); |
| |
| // After the copy containment tree has been built |
| // Iterate the non-containment nodes and copy all uni/bi-directional properties on the copy. |
| processNonContainmentNodesPrivate(doMap, ncPropMap); |
| |
| // Iterate the map of containment nodes and populate sequenced objects in the copy. |
| processContainmentSequencesPrivate(doMap, cs); |
| |
| /** |
| * ChangeSummary on Root Case: |
| * We only need to handle a single root element - isMany=true root elements are not valid |
| * Copy changeSummary state from original to copy on all cs-root elements that are also roots. |
| * Setting logging to true only after recursively setting internal properties will not create oldSettings in the copy |
| * The setLogging call will create oldContainer/oldContainmentProperty entries in the copy. |
| */ |
| if ((aCopy != null) && (aCopy.getChangeSummary() != null) && (aCopy.getType().getChangeSummaryProperty() != null)) { |
| // re-reference copy objects in the copy changeSummary |
| if (((SDODataObject)dataObject).getChangeSummary().isLogging()) {// switch and use resume logging |
| (aCopy.getChangeSummary()).resumeLogging();//.setLogging(true); |
| } |
| copyChangeSummary(((SDODataObject)dataObject).getChangeSummary(),// |
| aCopy.getChangeSummary(), doMap); |
| } |
| return aCopy; |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Iterate the non-containment nodes and copy all uni/bi-directional properties on the copy. |
| * @param doMap |
| * @param ncPropMap |
| */ |
| private void processNonContainmentNodesPrivate(HashMap doMap, HashMap ncPropMap) { |
| // for the doMap and ncPropMap we can either deep copy them all now or each one during rereferencing later |
| // iterate nc property map and set bidirectional/unidirectional properties that are in scope |
| for (Iterator ncIterator = ncPropMap.keySet().iterator(); ncIterator.hasNext();) {// p.142 limit scope of while by using for |
| // get current source dataobject (the one we will set properties on) |
| DataObject sourceDO = (DataObject)ncIterator.next(); |
| |
| // get the list of properties for the current do |
| ArrayList aList = (ArrayList)ncPropMap.get(sourceDO); |
| |
| // iterate property list |
| for (Iterator propIterator = aList.iterator(); propIterator.hasNext();) {// p.142 limit scope of while by using for |
| // get current property |
| SDOProperty aProperty = (SDOProperty)propIterator.next(); |
| |
| /* |
| * Stored in the map we have |
| * doMap: (a=key, a'=value) |
| * ncPropMap (a=key, (list of props)=value |
| * We get the copy from the doMap |
| * We get the copy of the source by doing a get on the current source do |
| */ |
| |
| // get original object that sourceDO points to via current property |
| Object targetDO = sourceDO.get(aProperty); |
| |
| // flag whether the property is inside the copy tree |
| boolean isPropertyInsideCopyTreeScope = false; |
| |
| // get sourceDO copy that we will be setting the property on |
| DataObject sourceDOCopy = null; |
| |
| // lookup copy of targetDO in map |
| Object targetDOCopy = null; |
| |
| /* |
| * Handle 1-n many case |
| * For containment=true |
| * the DO's will be cached previously, and both bidirectional (one) and unidirectional |
| * properties will set the copy on the copy object |
| * For containment=false |
| * the DO's will not be cached (outside the tree), only unidirectional properties |
| * will be set using the original list |
| */ |
| if (aProperty.isMany()) { |
| // create new list to hold copied list items |
| ListWrapper targetList = (ListWrapper)targetDO; |
| |
| // get source\DO copy that we will be setting the property on |
| sourceDOCopy = (DataObject)doMap.get(sourceDO); |
| |
| // lookup copy of targetDO in map |
| targetDOCopy = new ArrayList(); |
| for (int i = 0, size = targetList.size(); i < size; i++) { |
| // get sourceDO key - used as a lookup in our doMap |
| DataObject sourceDOCopyKey = (DataObject)targetList.get(i); |
| DataObject sourceDOCopyValue = (DataObject)doMap.get(sourceDOCopyKey); |
| |
| // add copy to new list |
| if (sourceDOCopyValue != null) { |
| // bidirectional/unidirectional inside copy tree - use copy object |
| ((List)targetDOCopy).add(sourceDOCopyValue); |
| } else { |
| // non-containment properties are not cached - store original for unidirectional |
| //targetDOCopy.add(sourceDOCopyKey); |
| } |
| } |
| |
| // check if the target copies are in our map (inside copy tree scope) |
| // when containment = false then targetDOCopy is empty |
| // Assume: all items in the list share the same property |
| isPropertyInsideCopyTreeScope = ((List)targetDOCopy).size() > 0; |
| } else { |
| // handle 1-1 DataObject |
| // lookup copy of targetDO in map |
| targetDOCopy = doMap.get(targetDO); |
| |
| // get sourceDO copy that we will be setting the property on |
| sourceDOCopy = (DataObject)doMap.get(sourceDO); |
| |
| // check if the target copy is in our map (inside copy tree scope) |
| isPropertyInsideCopyTreeScope = targetDOCopy != null; |
| } |
| |
| // set nc property if we are in the copy tree |
| // check if the target copy is in our map (inside copy tree scope) |
| if (isPropertyInsideCopyTreeScope) { |
| ((SDODataObject)sourceDOCopy).set(aProperty, targetDOCopy, false); |
| } else { |
| // only set unidirectional properties |
| if (null == aProperty.getOpposite()) { |
| // spec 3.9.4 set property to original object when unidirectional |
| ((SDODataObject)sourceDOCopy).set(aProperty, targetDO, false); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Make a copy of all settings on the original sequence onto the copy sequence while using a |
| * cross-reference doMap that relates the original DataObject key to the copy DataObject key. |
| * This function is used during deep copy and changeSummary copy. |
| * @param origSequence |
| * @param copySequence |
| * @param doMap |
| */ |
| private void replicateAndRereferenceSequenceCopyPrivate(SDOSequence origSequence, SDOSequence copySequence, DataObject dataObject, DataObject copy, Map doMap, SDOChangeSummary cs) { |
| if (cs != null && cs.isDeleted(dataObject)) { |
| origSequence = cs.getOldSequence(dataObject); |
| } |
| |
| SDOProperty seqProperty = null; |
| try { |
| List<Setting> settings = origSequence.getSettings(); |
| for (int index = 0, size = origSequence.size(); index < size; index++) { |
| Setting nextSetting = settings.get(index); |
| seqProperty = origSequence.getProperty(nextSetting); |
| |
| if ((null == seqProperty) || seqProperty.getType().isDataType()) { |
| Setting copySetting = nextSetting.copy(copy); |
| copySequence.getSettings().add(copySetting); |
| copySequence.addValueToSettings(copySetting); |
| } else { |
| Object copySeqValue = null; |
| Object origSeqValue = origSequence.getValue(index); |
| |
| if (cs != null) { |
| Object orig = cs.getReverseDeletedMap().get(origSeqValue); |
| if (orig != null) { |
| origSeqValue = orig; |
| } |
| } |
| |
| if (origSeqValue instanceof XMLRoot) { |
| origSeqValue = ((XMLRoot)origSeqValue).getObject(); |
| } |
| |
| // lookup copy if not null, if null then the copySeqValue will be null in the reduced scope assignment above |
| if (null != origSeqValue) { |
| copySeqValue = doMap.get(origSeqValue); |
| } else { |
| // as a secondary verification to the assignment above - make sure the copy is null as well |
| copySeqValue = null;// this assignment is however redundant in our reduced scope assignment above |
| } |
| |
| //now we have the new value |
| Setting copySetting = nextSetting.copy(copy, copySeqValue); |
| copySequence.getSettings().add(copySetting); |
| copySequence.addValueToSettings(copySetting); |
| } |
| |
| /** |
| * Move assignment inside the loop to minimize scope and to |
| * initialize the variable to null to handle the case where the original is null and |
| * no lookup is performed. |
| * Do not move the scope of this variable outside the for loop or we will end up |
| * with the previous iterations' value set for a complex object that is unset(null). |
| * see #6026714 |
| */ |
| } |
| } catch (ClassCastException cce) {// catch any failure of a DataObject cast above |
| throw SDOException.foundSimpleValueForNonDataTypeProperty(seqProperty.getName()); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Iterate the map of containment nodes and populate sequenced objects in the copy. |
| * @param doMap |
| */ |
| private void processContainmentSequencesPrivate(Map doMap, SDOChangeSummary cs) { |
| |
| /** |
| * Prerequisites: - the copy tree has been built and the doMap has been |
| * fully populated with containment nodes.<p> Iterate the doMap and |
| * for each containment dataObject populate the sequence</p> |
| */ |
| |
| // iterate containment nodes |
| for (Iterator cIterator = doMap.keySet().iterator(); cIterator.hasNext();) { |
| DataObject cObject = (DataObject)cIterator.next(); |
| DataObject copyDO = (DataObject)doMap.get(cObject); |
| |
| // process sequences |
| if (cObject.getType().isSequenced()) { |
| SDOSequence origSequence = (SDOSequence)cObject.getSequence(); |
| |
| // iterate the original object sequence - in sequence and create settings on the copy |
| replicateAndRereferenceSequenceCopyPrivate(origSequence, (SDOSequence)copyDO.getSequence(), cObject, copyDO, doMap, cs); |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: Create a new uninitialized ValueStore. |
| * |
| * @return |
| */ |
| private ValueStore createValueStore() { |
| return new DefaultValueStore(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Implement ChangeSummary deep copy |
| * Note: a copy with a CS requires the DefautlValueStore implementation |
| * because of changes outside the ValueStore interface for the dataObject field |
| * DeepCopy the original changeSummary into the copy dataObject. |
| * All deleted keys in originalValueStore, deletedMap, deepCopies are the same original object. |
| * We require the following relationships in order to build copies of originals and copies of copies of originals. |
| * origDOtoCopyDOMap |
| * original object (deleted + current) - in original DO : copy of original object - in copy DO |
| * copyDOtoCopyOfDOMap |
| * |
| * Assumptions: |
| * Property objects instances are copied only by reference - metadata is the same in the copy. |
| * Deleted objects never exist in the currentValueStore (and are therefore not in the doMap). |
| * Created and modified objects are always in the currentValueStore (and are in the doMap). |
| * |
| * @param anOriginalCS |
| * @param aCopyCS |
| * @param origDOCS1toCopyDOCS2Map (map of original do's (CS1) to their copy do's in (CS2)) |
| */ |
| private void copyChangeSummary(ChangeSummary anOriginalCS, ChangeSummary aCopyCS,// |
| Map origDOCS1toCopyDOCS2Map) { |
| // cast interfaces to concrete classes in one place |
| SDOChangeSummary originalCS = (SDOChangeSummary)anOriginalCS; |
| SDOChangeSummary copyCS = (SDOChangeSummary)aCopyCS; |
| |
| // handled by copy constructor |
| // map of copy of original ListWrapper (CS2) to its new copy of a copy (CS2) - link ValueStores to Elements |
| HashMap copyListWrapperCS2toCopyOfListCS2Map = new HashMap(); |
| |
| // in the absence of a ListWrapper.getProperty() we keep a map |
| HashMap propertyToOriginalListMap = new HashMap(); |
| |
| /** |
| * In 3 parts we add deleted objects to the global doMap and copy modified, created nodes |
| **/ |
| |
| // fields that need re-referencing from original to copy |
| DataObject anOriginalObject = null; |
| DataObject aCopyOfOriginalObject = null; |
| |
| // iterate deleted objects |
| for (Iterator anIterator = originalCS.getDeleted().iterator(); anIterator.hasNext();) { |
| anOriginalObject = (DataObject)anIterator.next(); |
| aCopyOfOriginalObject = copy(anOriginalObject, null); |
| // fix deletedList |
| copyCS.getDeleted().add(aCopyOfOriginalObject); |
| // Assumption check do map before a possible re-add - reset() |
| if (null == origDOCS1toCopyDOCS2Map.get(anOriginalObject)) { |
| // add temp map of original : copy of original |
| origDOCS1toCopyDOCS2Map.put(anOriginalObject, aCopyOfOriginalObject); |
| } |
| } |
| |
| // iterate created objects |
| for (Iterator aIterator = originalCS.getCreated().iterator(); aIterator.hasNext();) { |
| copyCS.getCreated().add(origDOCS1toCopyDOCS2Map.get(aIterator.next())); |
| } |
| |
| // add modified objects |
| for (Iterator anIterator = originalCS.getModified().iterator(); anIterator.hasNext();) { |
| copyCS.getModified().add(origDOCS1toCopyDOCS2Map.get(anIterator.next())); |
| } |
| |
| /** |
| * Fix originalValueStores by deep copying the original dataObject:key and the original valueStore:value |
| * key is original deleted object in [deepCopies] - value is copy of the ValueStore |
| */ |
| ValueStore aVSCopy = null; |
| ValueStore aVSOriginal = null; |
| for (Iterator anIterator = originalCS.getOriginalValueStores().keySet().iterator(); |
| anIterator.hasNext();) { |
| anOriginalObject = (DataObject)anIterator.next(); |
| // deep copy to get corresponding copy DataObject (deleted objects were added to doMap) |
| aCopyOfOriginalObject = (DataObject)origDOCS1toCopyDOCS2Map.get(anOriginalObject); |
| |
| /** |
| * Recursively shallow-copy elements (by iterating the ovs map and iterating the properties of each item) |
| * Fix the dataObject pointer |
| */ |
| aVSCopy = createValueStore(); |
| aVSOriginal = (ValueStore)originalCS.getOriginalValueStores().get(anOriginalObject); |
| // changes made to the copy VS must not affect the original -hence the dataObject field must be a copy of the original |
| aVSCopy.initialize(aCopyOfOriginalObject); |
| Object aVSPropertyItem = null; |
| |
| // get the # of non-opencontent properties for the object holding the CS - do not use DVS.getTypePropertyValues() |
| for (int size = anOriginalObject.getType().getDeclaredProperties().size(), i = 0; |
| i < size; i++) { |
| aVSPropertyItem = aVSOriginal.getDeclaredProperty(i); |
| // only iterate set properties |
| if (aVSOriginal.isSetDeclaredProperty(i)) { |
| // shallow copy the object values |
| // handle single case |
| SDOProperty currentProperty = (SDOProperty) anOriginalObject.getType().getDeclaredProperties().get(i); |
| if (currentProperty.isMany()) { |
| propertyToOriginalListMap.put(aVSPropertyItem, currentProperty); |
| |
| // handle many case - handled by originalElements |
| // container DO must be in our reference map |
| SDODataObject copyContainer = (SDODataObject)origDOCS1toCopyDOCS2Map.get(anOriginalObject); |
| ListWrapper aCopyOfListCopy = (ListWrapper)((DataObject)copyContainer).getList(currentProperty); |
| |
| // add reference of new copy of original List keyed on original List |
| copyListWrapperCS2toCopyOfListCS2Map.put((anOriginalObject).getList(currentProperty), aCopyOfListCopy); |
| aVSCopy.setDeclaredProperty(i, aCopyOfListCopy); |
| } else { |
| // COMPLEX SINGLE |
| if (!currentProperty.getType().isDataType()) { |
| // are we using the cast to DataObject as a sort of instance check that would throw a CCE? |
| aVSCopy.setDeclaredProperty(i, origDOCS1toCopyDOCS2Map.get(aVSPropertyItem)); |
| } else { |
| // SIMPLE SINGLE |
| // skip changeSummary property |
| if (!currentProperty.getType().isChangeSummaryType()) { |
| // simple singles set |
| aVSCopy.setDeclaredProperty(i, aVSPropertyItem); |
| } |
| } |
| } |
| } |
| } |
| |
| // create list of unset and current open content properties |
| List ocPropertiesList = new ArrayList(); |
| ocPropertiesList.addAll(originalCS.getUnsetOCProperties(anOriginalObject)); |
| // add existing properties |
| ocPropertiesList.addAll(((SDODataObject)anOriginalObject)._getOpenContentProperties()); |
| ocPropertiesList.addAll(((SDODataObject)anOriginalObject)._getOpenContentPropertiesAttributes()); |
| // iterate existing open content properties |
| for (Iterator i = ocPropertiesList.iterator(); i.hasNext();) { |
| SDOProperty ocProperty = (SDOProperty)i.next(); |
| if (aVSOriginal.isSetOpenContentProperty(ocProperty)) { |
| // get oc value |
| Object anOCPropertyItem = aVSOriginal.getOpenContentProperty(ocProperty); |
| |
| // get oc copy - shallow copy the object values |
| if (ocProperty.isMany()) { |
| // handle many case - handled by originalElements |
| // container DO must be in our reference map |
| SDODataObject copyContainer = (SDODataObject)origDOCS1toCopyDOCS2Map.get(anOriginalObject); |
| ListWrapper aCopyOfListCopy = (ListWrapper)((DataObject)copyContainer).getList(ocProperty); |
| |
| // add reference of new copy of original List keyed on original List |
| copyListWrapperCS2toCopyOfListCS2Map.put((anOriginalObject).getList(ocProperty), aCopyOfListCopy); |
| aVSCopy.setOpenContentProperty(ocProperty, aCopyOfListCopy); |
| } else { |
| // handle complex single case |
| if (!ocProperty.getType().isDataType()) { |
| aVSCopy.setOpenContentProperty(ocProperty, origDOCS1toCopyDOCS2Map.get(aVSPropertyItem)); |
| } else { |
| // simple singles set |
| aVSCopy.setOpenContentProperty(ocProperty, anOCPropertyItem); |
| } |
| } |
| } |
| } |
| |
| // set the copy map entry keyed on copy with value a deep copy of the copy |
| copyCS.getOriginalValueStores().put(aCopyOfOriginalObject, aVSCopy); |
| } |
| |
| // end originalValueStore iteration |
| |
| /** |
| * Fix originalElements by deep copying the original dataObject:key and the original List:value |
| * key is original deleted object in [deepCopies] - value is copy of the elements |
| * The instances of ListWrapper inside the valueStores must be the same ones used in the originalElements |
| */ |
| ListWrapper anOriginalListKey = null; |
| ListWrapper aCopyListWrapper = null; |
| List aCopyList = null; |
| for (Iterator anIterator = originalCS.getOriginalElements().keySet().iterator(); |
| anIterator.hasNext();) { |
| anOriginalListKey = (ListWrapper)anIterator.next(); |
| // create a new ListWrapper |
| SDOProperty aProperty = (SDOProperty) propertyToOriginalListMap.get(anOriginalListKey); |
| aCopyListWrapper = (ListWrapper)copyListWrapperCS2toCopyOfListCS2Map.get(anOriginalListKey); |
| aCopyList = new ArrayList(); |
| |
| /** |
| * For each key:ListWrapper |
| * - shallow copy all the items in the currentElements list |
| * - replace the dataObject with its copy of the copy |
| * - leave the property as is |
| * For each value:ArrayList |
| * - replace all values with their copy |
| */ |
| Object aListItem = null; |
| Object aListItemCopy = null; |
| for (Iterator anItemIterator = anOriginalListKey.iterator(); anItemIterator.hasNext();) { |
| aListItem = anItemIterator.next(); |
| // for simple many types we use the original in the copy |
| if (!aProperty.getType().isDataType()) { |
| // get the copy of the original (in the current valuestore) - we need do not make a copy of this copy |
| // we should have a copy of the copy for List items - ListWrapper.add(item) will remove the item from its original wrapper |
| aListItemCopy = origDOCS1toCopyDOCS2Map.get(aListItem); |
| } else { |
| aListItemCopy = aListItem; |
| } |
| aCopyList.add(aListItemCopy); |
| } |
| |
| // add element list directly to the ListWrapper and bypass the cs element copy and containment updates |
| aCopyListWrapper.setCurrentElements(aCopyList); |
| List listValueCopy = new ArrayList(); |
| |
| // fix ArrayList value |
| List listValue = (List)originalCS.getOriginalElements().get(anOriginalListKey); |
| aListItem = null; |
| aListItemCopy = null; |
| for (Iterator aListIterator = listValue.iterator(); aListIterator.hasNext();) { |
| aListItem = aListIterator.next(); |
| // for simple many types we use the original in the copy |
| if (!aProperty.getType().isDataType()) { |
| aListItemCopy = origDOCS1toCopyDOCS2Map.get(aListItem); |
| } else { |
| aListItemCopy = aListItem; |
| } |
| |
| // don't add nulls to the listWrapper so an undoChanges will encounter an NPE later |
| if (aListItemCopy != null) { |
| listValueCopy.add(aListItemCopy); |
| } |
| } |
| |
| // set the copy map entry keyed on copy with value a deep copy of the copy |
| copyCS.getOriginalElements().put(aCopyListWrapper, listValueCopy); |
| } |
| |
| // end originalist Iteration |
| |
| /** |
| * fields that are already set when logging is turned on but need to be fixed (deleted objects need references) |
| */ |
| Map oldContainersMap = originalCS.getOldContainers(); |
| Map copyContainersMap = copyCS.getOldContainers(); |
| DataObject oldContainerKey = null; |
| DataObject copyContainerKey = null; |
| |
| // convert any existing entries in the Map - should normally be 0 - unless any OC properties were unset |
| for (Iterator anIterator = oldContainersMap.keySet().iterator(); anIterator.hasNext();) { |
| oldContainerKey = (DataObject)anIterator.next(); |
| // get corresponding copy |
| copyContainerKey = (SDODataObject)origDOCS1toCopyDOCS2Map.get(oldContainerKey); |
| // check existing copyContainers for existing objects - should be 0 - add all objects when pauseLogging() used |
| DataObject oldContainerValue = null; |
| if (null == copyContainersMap.get(copyContainerKey)) { |
| oldContainerValue = (DataObject)oldContainersMap.get(oldContainerKey); |
| // set copy key:value pair on copy map directly |
| copyContainersMap.put(copyContainerKey, origDOCS1toCopyDOCS2Map.get(oldContainerValue)); |
| } |
| } |
| |
| Map oldContainmentPropertyMap = originalCS.getOldContainmentProperty(); |
| Map copyContainmentPropertyMap = copyCS.getOldContainmentProperty(); |
| DataObject oldContainmentPropertyKey = null; |
| DataObject copyContainmentPropertyKey = null; |
| |
| // convert any existing entries in the Map - should normally be 0 - unless any OC properties were unset |
| for (Iterator iterContProp = oldContainmentPropertyMap.keySet().iterator(); |
| iterContProp.hasNext();) { |
| oldContainmentPropertyKey = (DataObject)iterContProp.next(); |
| // get corresponding copy |
| copyContainmentPropertyKey = (SDODataObject)origDOCS1toCopyDOCS2Map.get(oldContainmentPropertyKey); |
| // check existing copyContainers for existing objects - should be 0 - add all objects when pauseLogging() used |
| if (null == copyContainmentPropertyMap.get(copyContainmentPropertyKey)) { |
| // set copy key:value pair on copy map directly |
| copyContainmentPropertyMap.put(copyContainmentPropertyKey, oldContainmentPropertyMap.get(oldContainmentPropertyKey)); |
| } |
| } |
| |
| Map oldUnsetOCPropertyMap = originalCS.getUnsetOCPropertiesMap(); |
| SDODataObject oldOCPropertyContainer = null; |
| |
| // convert any existing entries in the Map - should normally be 0 |
| for (Iterator iterContainer = oldUnsetOCPropertyMap.keySet().iterator(); |
| iterContainer.hasNext();) { |
| // DataObject will be non-Null |
| oldOCPropertyContainer = (SDODataObject)iterContainer.next(); |
| // check existing copyContainers for existing objects - should be 0 - add all objects when pauseLogging() used |
| for (Iterator iterUnset = ((List)oldUnsetOCPropertyMap.get(oldOCPropertyContainer)).iterator(); |
| iterUnset.hasNext();) { |
| // set/create new list on copy Map with corresponding copy of container |
| copyCS.setUnsetOCProperty((SDODataObject)origDOCS1toCopyDOCS2Map.get(// |
| oldOCPropertyContainer), (Property)iterUnset.next()); |
| } |
| } |
| |
| // process sequences |
| |
| /** |
| * Fix originalSequences by deep copying the original dataObject:key and the original Sequence:value |
| * key is original deleted object in [deepCopies] - value is copy of the settings in the sequence. |
| * The instances of Sequence inside the originalSequences must be the same ones used in the originalElements |
| */ |
| |
| // iterate the map of <DataObject, Sequence> |
| for (Iterator aMapIterator = originalCS.getOriginalSequences().keySet().iterator(); |
| aMapIterator.hasNext();) { |
| SDODataObject sequenceDataObjectKey = (SDODataObject)aMapIterator.next(); |
| SDOSequence originalSequence = (SDOSequence)originalCS.getOriginalSequences().get(sequenceDataObjectKey); |
| |
| // create a new Sequence with a pointer to the copy of the original DataObject backpointer |
| // assume that all dataObject backpointers are containment objects. |
| SDODataObject copyOriginalSequenceDataObject = (SDODataObject)origDOCS1toCopyDOCS2Map.get(originalSequence.getDataObject()); |
| SDOSequence copySequence = new SDOSequence(copyOriginalSequenceDataObject); |
| |
| replicateAndRereferenceSequenceCopyPrivate(originalSequence, copySequence, originalSequence.getDataObject(), copyOriginalSequenceDataObject, origDOCS1toCopyDOCS2Map, originalCS); |
| |
| // set the copy map entry keyed on copy with value a deep copy of the copy |
| copyCS.getOriginalSequences().put(copyOriginalSequenceDataObject, copySequence); |
| } |
| |
| /** |
| * fields to ignore |
| // aHelperContext SDOHelperContext (id=42) |
| // dataGraph null |
| // logging true |
| // loggingMapping true |
| // unsetPropsMap HashMap<K,V> (id=117) |
| // createdXPaths null |
| // deletedXPaths null |
| // modifiedDoms null |
| */ |
| /** |
| * fields to ignore that are on-demand |
| * oldSettings HashMap<K,V> (id=110) |
| * reverseDeletedMap HashMap<K,V> (id=116) |
| * oldSequences HashMap<K,V> (id=88) |
| */ |
| } |
| |
| /** |
| * INTERNAL: |
| * Build the copy tree and cache all reachable DataObjects with their copy<br> |
| * Cache all non-containment properties - to be set after tree construction<br> |
| * Recurse the tree in preorder traversal (root, child1-n) |
| * Scope: We do not have to check the copyTree scope when iterating opposites since |
| * we will not enter any opposite property dataTree that is outside of the copyTree scope |
| * @param doMap (cache original -{@literal >} copy DataObject instances to set non-containment properties after tree construction) |
| * @param ncPropMap (cache original DO:non-containment property values to be set after tree construction) |
| */ |
| private SDODataObject copyPrivate(SDODataObject dataObject, HashMap doMap,// |
| HashMap ncPropMap, SDOChangeSummary cs) throws IllegalArgumentException { |
| // check for null DataObject |
| if (null == dataObject) { |
| return null;// this is acceptable behavior |
| } |
| |
| SDODataObject copy = (SDODataObject)getHelperContext()// |
| .getDataFactory().create(dataObject.getType().getURI(), dataObject.getType().getName()); |
| |
| // store current object for reference by the non-containment map |
| doMap.put(dataObject, copy); |
| |
| List ocListOriginal = dataObject._getOpenContentProperties(); |
| for (Iterator anOCIterator = ocListOriginal.iterator(); anOCIterator.hasNext();) { |
| copy.addOpenContentProperty((Property)anOCIterator.next()); |
| } |
| |
| List ocAttrsListOriginal = dataObject._getOpenContentPropertiesAttributes(); |
| for (Iterator anOCAttrIterator = ocAttrsListOriginal.iterator(); anOCAttrIterator.hasNext();) { |
| copy.addOpenContentProperty((Property)anOCAttrIterator.next()); |
| } |
| |
| // start iterating all copy's properties |
| for (Iterator iterInstanceProperties = copy.getInstanceProperties().iterator(); |
| iterInstanceProperties.hasNext();) { |
| SDOProperty eachProperty = (SDOProperty)iterInstanceProperties.next(); |
| boolean isSet = isSet(dataObject, eachProperty, cs); |
| if (isSet) { |
| Object o = getValue(dataObject, eachProperty, cs); |
| if (eachProperty.getType().isDataType()) { |
| if (!eachProperty.getType().isChangeSummaryType()) { |
| |
| /** |
| * ChangeSummaries must be cleared with logging set to the original state without creating oldSettings. |
| * The logging flag from the original will be set on the copy after this copy call completes |
| * and gets set on its container. |
| * The cs is off by default in the copy object. |
| * updateSequence flag is false - we will populate the sequence in order after subtree creation |
| */ |
| copy.setInternal(eachProperty, o, false); |
| } |
| } else { |
| // case matrix for containment and opposite combinations |
| // cont=false, opp=false -> unidirectional |
| // cont=false, opp=true -> bidirectional |
| // cont=true, opp=false -> normal containment |
| // cont=true, opp=true -> bidirectional |
| if (eachProperty.isContainment()) { |
| // process containment properties (normal, half of bidirectionals) |
| copyContainmentPropertyValue(copy, eachProperty, o, doMap, ncPropMap, cs); |
| } else { |
| // copy non-containment do (not properties (unidirectional, half of bidirectionals)) |
| //copyPropertyValue(dataObject, copy, eachProperty, o, copyRoot, doMap); |
| //cacheNContainmentPropertyValue(copy, eachProperty, o, doMap, ncPropMap); |
| // store non-containment properties |
| ArrayList anArray = (ArrayList)ncPropMap.get(dataObject); |
| if (anArray == null) { |
| anArray = new ArrayList(); |
| anArray.add(eachProperty); |
| // store property array into the map for the first time |
| ncPropMap.put(dataObject, anArray); |
| } else { |
| // add to existing array of nc properties in the map |
| anArray.add(eachProperty); |
| } |
| } |
| } |
| } |
| } |
| |
| // sequences will not be processed until the entire tree is copied so we can resolve any reference relationships |
| return copy; |
| } |
| |
| /** |
| * INTERNAL: |
| * Recursive function deep copies all contained properties. |
| * Requirements: The value object has isSet=true for all callers. |
| * @param copy |
| * @param property |
| * @param value |
| * @param doMap (cache original -{@literal >} copy DataObject instances to set non-containment properties after tree construction) |
| * @param ncPropMap (cache original DO:non-containment property values to be set after tree construction) |
| */ |
| private void copyContainmentPropertyValue(SDODataObject copy, SDOProperty property, Object value,// |
| HashMap doMap, HashMap ncPropMap, SDOChangeSummary cs) { |
| if (property.isMany()) { |
| List copyValue = new ArrayList(); |
| |
| // set the copy to an empty list and add each items from the original in sequence |
| // updateSequence flag is false - we will populate the sequence in order after subtree creation |
| copy.setInternal(property, copyValue, false); |
| for (Iterator iterValues = ((List)value).iterator(); iterValues.hasNext();) { |
| SDODataObject o = (SDODataObject)iterValues.next(); |
| SDODataObject copyO = copyPrivate(o, doMap, ncPropMap, cs); |
| ((ListWrapper)copy.getList(property)).add(copyO, false); |
| // set changeSummary on all cs-root elements in the list after they are added to the containment tree |
| if ((copyO != null) && (copyO.getChangeSummary() != null) && (copyO.getType().getChangeSummaryProperty() != null)) { |
| // re-reference copy objects in the copy changeSummary |
| if (o.getChangeSummary().isLogging()) { |
| (copyO.getChangeSummary()).setLogging(true); |
| } |
| copyChangeSummary(o.getChangeSummary(),// |
| copyO.getChangeSummary(), doMap); |
| } |
| } |
| } else {// handle non-many case |
| // implementers of this function will always pass in a DataObject that may be null |
| SDODataObject copyO = copyPrivate((SDODataObject)value, doMap, ncPropMap, cs); |
| |
| // #5852525 handle null properties with isSet=true - fixed 20070130 |
| // we will set the isSet index in the ValueStore to true for all isSet=true objects, even NULL ones. |
| // updateSequence flag is false - we will populate the sequence in order after subtree creation |
| copy.setInternal(property, copyO, false); |
| |
| // set changeSummary on all cs-root elements in the list after they are added to the containment tree using the original logging value |
| if ((copyO != null) && (copyO.getChangeSummary() != null) && (copyO.getType().getChangeSummaryProperty() != null)) { |
| // re-reference copy objects in the copy changeSummary |
| if (((SDODataObject)value).getChangeSummary().isLogging()) { |
| copyO.getChangeSummary().setLogging(true); |
| } |
| copyChangeSummary(((SDODataObject)value).getChangeSummary(),// |
| copyO.getChangeSummary(), doMap); |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the helperContext containing this copyHelper. |
| * @return |
| */ |
| public HelperContext getHelperContext() { |
| if(null == aHelperContext) { |
| aHelperContext = HelperProvider.getDefaultContext(); |
| } |
| return aHelperContext; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the helperContext if this copyHelper was created using the default constructor. |
| * @param helperContext |
| */ |
| public void setHelperContext(HelperContext helperContext) { |
| aHelperContext = helperContext; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used during XML Unmarshal. |
| * @param dataObject |
| * @param property |
| * @param cs |
| * @return |
| */ |
| private boolean isSet(SDODataObject dataObject, Property property, SDOChangeSummary cs) { |
| if (cs != null) { |
| return cs.wasSet(dataObject, property); |
| } |
| return dataObject.isSetInternal(property); |
| } |
| |
| /** |
| * INTERNAL: |
| * Used during XML Unmarshal. |
| * @param dataObject |
| * @param property |
| * @param cs |
| * @return |
| */ |
| private Object getValue(SDODataObject dataObject, Property property, SDOChangeSummary cs) { |
| if (cs != null) { |
| Object returnValue = cs.getPropertyInternal(dataObject, property); |
| if (property.isMany()) { |
| if (cs.getOriginalElements().containsKey(returnValue)) { |
| // are we using the cast to List as a sort of instance check that would throw a CCE? |
| return cs.getOriginalElements().get(returnValue); |
| } |
| } |
| return returnValue; |
| } |
| return dataObject.get(property);// get the value of current property |
| } |
| } |