/*
 * 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
/*
   DESCRIPTION
    Test SDOCopyHelper for deep and shallow copy
    References:
      SDO59-DeepCopy.doc
      SDO_Ref_BiDir_Relationships_DesignSpec.doc

   PRIVATE CLASSES

   NOTES

   MODIFIED    (MM/DD/YY)
    mfobrien    05/16/07 -
    dmahar      04/23/07 -
    mfobrien    07/31/06 - As part of the refactor of SDODataObject.properties <Map> into
                                     the ValueStore interfaces, these tests require non-Map specific tests.
                                     Currently testing is hardcoded to additional values() and keySet() functions
                                     on our RI MapValueStore
    dmahar      08/18/06 -
    mfobrien    08/19/06 - Remove special handling - getProperties() has returned as a Pluggable interface
    mfobrien    09/09/06 - Add bidirectional support to SDOCopyHelper - see SDO57/59
    mfobrien    09/18/06 - Subclass off common copy/equality testCases model
                                   - Add bidirectional testcases for the following cases
                                   - verify all properties that are bi-directional are type.dataType=false - spec 8.4
                                   - test bi-directional with one end at containment=true - spec 8.4
                                   - test bi-directional where both ends have the same value for readOnly = true - spec 8.4
                                   - test bi-directional where both ends have the same value for readOnly = false - spec 8.4
                                   - test bi-directional with containment where the non-containment Property has many=false. - spec 8.4
    mfobrien    03/19/07 - 5853175: bidirectional link between disjoint trees (as a result of an unset on one side)
                                   are spec compliant and will result in inequality between an original and copy of the root

 */
package org.eclipse.persistence.testing.sdo.helper.copyhelper;

import commonj.sdo.Type;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.persistence.sdo.SDOChangeSummary;
import org.eclipse.persistence.sdo.SDODataObject;
import org.eclipse.persistence.sdo.SDOProperty;
import org.eclipse.persistence.sdo.SDOType;
import org.eclipse.persistence.sdo.helper.ListWrapper;
import org.eclipse.persistence.sdo.helper.SDOCopyHelper;
import org.eclipse.persistence.sdo.helper.SDOTypeHelper;
import org.eclipse.persistence.testing.sdo.helper.SDOCopyEqualityHelperTestCases;
import commonj.sdo.ChangeSummary;
import commonj.sdo.DataObject;
import commonj.sdo.Property;

public class SDOCopyHelperDeepCopyTest extends SDOCopyEqualityHelperTestCases {
    public SDOCopyHelperDeepCopyTest(String name) {
        super(name);
    }

    public static void main(String[] args) {
        junit.textui.TestRunner.run(SDOCopyHelperDeepCopyTest.class);
    }

    private int getPropertiesSize(SDODataObject anObject) {
        int size = 0;
        for (int i = 0; i < anObject.getInstanceProperties().size(); i++) {
            Property next = (Property)anObject.getInstanceProperties().get(i);
            if (anObject.isSet(next)) {
                size++;
            }
        }
        return size;
    }

    public SDOChangeSummary getChangeSummary() {
        return null;
    }

    private Set getPropertiesKeySet(SDODataObject anObject) {
        HashSet aSet = new HashSet();
        if (anObject != null) {
            for (int i = 0; i < anObject.getType().getProperties().size(); i++) {
                //boolean isSet = anObject.getTypePropertiesIsSetStatus()[i];
                boolean isSet = anObject.isSet(i);
                if (isSet) {
                    aSet.add(((Property)anObject.getType().getProperties().get(i)).getName());
                }
            }
            for (int i = 0; i < anObject._getOpenContentProperties().size(); i++) {
                aSet.add(((Property)anObject._getOpenContentProperties().get(i)).getName());
            }
            return aSet;
        } else {
            return new HashSet();
        }
    }

    /**
     * Spec 8.4 "Properties that are bi-directional require that no more than one end has containment=true"
     */
    private boolean areBothBidirectionalOppositePropertiesContainmentTrue(//
    SDOProperty oppositeProperty1, SDOProperty oppositeProperty2) {
        return oppositeProperty1.isContainment() && oppositeProperty2.isContainment();
    }


    /**
     * Test whether EqualityHelper.compareProperty() handles nested objects that are null but isSet=true
     * (Where we also have a bidirectional child property)
     * See SDOEqualityHelperTest for the same tests without the bidirectional child
     *
     * Disjoint trees can maintain a bidirectional link
     *
     * Results: This test will verify that the bidirectional property is left unchanged
     * after an unset on its parent - in effect creating a nc link between 2 separate dataobject trees.
     * See SDO_Ref_BiDir_Relationships_DesignSpec.doc section 4.2.2
     *         See spec 3.1.7. If other DataObjects have one-way, non-containment properties that refer to deleted DataObjects,
     * then these references are not modified. - we retain both sides of the bidirectional link here.
     *         See spec 3.1.7. However, these properties can need changing to other values,
     * in order to restore closure to the data graph? - the implementer will need to be aware and fix any set/reset issues
     * - the SDO API will not attempt to remove any of these NC references for deleted objects.
     *     No code changes are required because non-equality is expected in this case.
     *        Leaving a bidirectional property set between two trees that do not share a common root is not an invalid state (step 2 above)
     *
     * root
     *   rp2 -> cdo (unset this one - placing it outside the copy tree)
     *                 cp2 -> cbcdo
     *                                cbcp1 -> cdo1 (bidir)
     *                                c1p3 -> cdo1 (bidir)
     *                 cp3 -> cbcdo3
     *                 cpcs -> cs
     *   rp3 -> cdo1
     *                 c1p1 -> cbcdo (bidir)
     *   rp4 -> lw
     */
    public void testDeepCopyAfterUnsetComplexChildWithBidirectionalChild_generatingLinkedDisjointTrees() {
        int aRootsize = preOrderTraversalDataObjectList(root).size();
        assertEquals(7, aRootsize);

        // save cdo before we unset it
        DataObject aCDO = (DataObject)root.get("rootproperty2-notdatatype");
        assertNotNull(aCDO);
        int aCDOsize = preOrderTraversalDataObjectList((SDODataObject)aCDO).size();
        // save child of cdo
        DataObject aCDO_CBCDO = (DataObject)aCDO.get("containedProperty2-notdataType");
        assertNotNull(aCDO_CBCDO);
        int aCDO_CBCDOsize = preOrderTraversalDataObjectList((SDODataObject)aCDO_CBCDO).size();
        // save bidirectional partner of cdo
        DataObject aCDO1 = (DataObject)root.get("rootproperty3-notdatatype");
        assertNotNull(aCDO1);
        int aCDO1size = preOrderTraversalDataObjectList((SDODataObject)aCDO1).size();

        // get a list of nodes (NC references do not recursively iterate)
        List<DataObject> rootPreOrderListBeforeUnset = preOrderTraversalDataObjectList(root);
        assertNotNull(rootPreOrderListBeforeUnset);
        assertEquals(aRootsize, rootPreOrderListBeforeUnset.size());
        rootPreOrderListBeforeUnset.contains(aCDO_CBCDO);
        rootPreOrderListBeforeUnset.contains(aCDO1);


        // NOTE: unset does not affect bidirectional properties between disjoint trees of dataObjects
        // the remaining unidirectional property is left unaffected.
        root.unset("rootproperty2-notdatatype");


        // verify that bidirectional property is still set
        DataObject aCDO1_CBCDO = (DataObject)aCDO1.get("contained1Property1-notdataType");
        assertNotNull(aCDO1_CBCDO);
        assertEquals(aCDO1_CBCDO, aCDO_CBCDO);
        DataObject aCBCDO_CDO1 = (DataObject)aCDO_CBCDO.get("containedByContainedProperty1-notdataType");
        assertNotNull(aCBCDO_CDO1);
        assertEquals(aCBCDO_CDO1, aCDO1);

        // get a list of nodes (NC references do not recursively iterate)
        List<DataObject> rootPreOrderListAfterUnset = preOrderTraversalDataObjectList(root);
        assertNotNull(rootPreOrderListAfterUnset);
        // 4 not 3 because we shallow iterate to the non-contaiment property c1p1
        assertEquals(aRootsize - aCDOsize, rootPreOrderListAfterUnset.size());
        rootPreOrderListAfterUnset.contains(aCDO_CBCDO);
        rootPreOrderListAfterUnset.contains(aCDO1);

        // get a list of nodes (NC references do not recursively iterate)
        List<DataObject> cdoPreOrderListAfterUnset = preOrderTraversalDataObjectList((SDODataObject)aCDO);
        assertNotNull(cdoPreOrderListAfterUnset);
        // 4 not 3 because we shallow iterate to the non-contaiment property c1p1
        assertEquals(aCDOsize, cdoPreOrderListAfterUnset.size());
        cdoPreOrderListAfterUnset.contains(aCDO_CBCDO);

        SDODataObject copyOfRoot = (SDODataObject)((SDOCopyHelper)copyHelper).copy(root, getChangeSummary());
        assertFalse(root.isSet("rootproperty2-notdatatype"));
        assertNotNull(copyOfRoot);

        DataObject aCDOc = (DataObject)copyOfRoot.get("rootproperty2-notdatatype");
        assertNull(aCDOc);
        DataObject aCDO1c = (DataObject)copyOfRoot.get("rootproperty3-notdatatype");
        assertNotNull(aCDO1c);
        int aCDO1sizec = preOrderTraversalDataObjectList((SDODataObject)aCDO1c).size();
        // 2 not 1 because the bidirectional property was not copied
        assertEquals(1, aCDO1sizec);

        // test copy helper results outside of equality helper (to keep equality helper issues outside of copyhelper testing)
        // get non-containment property between original cdo1 and unset cdo/cbcdo
        // verify that bidirectional property is not set on copy
        DataObject aCDO1_CBCDOc = (DataObject)aCDO1c.get("contained1Property1-notdataType");
        assertNull(aCDO1_CBCDOc);

        // lastly use equality helper to test copy helper
        assertFalse(equalityHelper.equal(root, copyOfRoot));

        // we need a diff function on cs or equalityHelper to show us that aCDO (copy) is not on copyOfRoot
    }

    // see same comments for testDeepCopyAfterUnsetComplexChildWithBidirectionalChild_generatingLinkedDisjointTrees() above
    public void testDeepCopyAfterSetNullComplexChildWithBidirectionalChild_generatingLinkedDisjointTrees() {
        // clear complex child
        root.set("rootproperty2-notdatatype", null);
        SDODataObject copyOfRoot = (SDODataObject)((SDOCopyHelper)copyHelper).copy(root, getChangeSummary());
        assertFalse(root.isSet("rootproperty2-notdatatype"));
        assertNotNull(copyOfRoot);
        // this assertion previously failed before fix for #5852525
        assertFalse(equalityHelper.equal(root, copyOfRoot));
    }

    // test R21 p44    3.9.4    For each Property where property.getType().isDataType() is false
    // the value is copied if it is a DataObject contained by the source dataObject.
    public void testR21P44Sect3_9_4_NonContainedDOsAreNotCopied() {

    }

    // test R22 p44    3.9.4    If a DataObject is outside the copy tree and the property is bidirectional then //
    // the DataObject is not copied and references to the object are also not copied.
    public void testR22P44Sect3_9_4_DOisOutsideCopyTreeWithBidirectionalPropertyNotCopied() {
        // cdo/cbcdo (depth2) and cdo1 (depth1) are bidirectional with each other
        // create copy of cdo1 and verify that cdo/cbcdo was not copied since it is outside the copy tree
        SDODataObject copy = (SDODataObject)((SDOCopyHelper)copyHelper).copy(containedDataObject, getChangeSummary());

        // verify that container is null
        assertNull(copy.getContainer());
        // get bidirectional node from the copy root
        SDODataObject bidirCopyViaCopyRoot = (SDODataObject)copy.get(//
        "containedProperty2-notdataType/containedByContainedProperty1-notdataType");
        assertNull(bidirCopyViaCopyRoot);
        // get bidirectional start node via opposite property
        SDODataObject bidirCopyStart = (SDODataObject)copy.get(//
        "containedProperty2-notdataType");
        assertNotNull(bidirCopyStart);
        // get property containing opposite property link (inside copy tree)
        SDOProperty bidirCopyOppositeStartProp = bidirCopyStart.getType().getProperty(//
        "containedByContainedProperty1-notdataType");
        assertNotNull(bidirCopyOppositeStartProp);
        // Spec 8.4 "Properties that are bi-directional require type.dataType=false"
        assertFalse(bidirCopyOppositeStartProp.getType().isDataType());
        // get bidir property outside of copy tree
        SDOProperty bidirCopyViaOpposite = bidirCopyOppositeStartProp.getType()//
        .getProperty("contained1Property1-notdataType");
        assertNull(bidirCopyViaOpposite);
        SDODataObject bidirCopyViaOppositeDO = (SDODataObject)copy.get(//
        "rootproperty3-notdatatype/contained1Property1-notdataType");
        assertNull(bidirCopyViaOppositeDO);
    }

    // test R23 p44    3.9.4    If a DataObject is outside the copy tree and the property is unidirectional then //
    // the same DataObject is referenced.
    public void testR23P44Sect3_9_4_DOisOutsideCopyTreeWithUnidirectionalPropertyThenSameDOreferenced() {

    }

    // we dont want changes to occur during the embedded set() calls in copy()
    // this test needs a deeper model in order to fully test the bug see ChangeSummaryXSDWithCSonChildUndoTestCases.testDeepCopyObjectWithCSLoggingOnDoesNotLogChangesInTheDeepCopy()
    public void testDeepCopyObjectWithCSLoggingOnDoesNotLogChangesInTheDeepCopy() {
        // verify logging is on
        assertNotNull(containedDataObject.getChangeSummary());
        assertTrue(containedDataObject.getChangeSummary().isLogging());
        //containedDataObject.getChangeSummary().endLogging();
        // take an object with CS on and deep copy it
        SDODataObject copy = (SDODataObject)((SDOCopyHelper)copyHelper).copy(containedDataObject, getChangeSummary());

        //containedDataObject.getChangeSummary().beginLogging();
        // verify that logging is still on
        assertTrue(containedDataObject.getChangeSummary().isLogging());
        assertTrue(copy.getChangeSummary().isLogging());
        ChangeSummary cs = copy.getChangeSummary();

        // verify that we have not logged changes during the copy
        assertEquals(0, cs.getChangedDataObjects().size());
    }


    // we dont want changes to occur during the embedded set() calls in copy()
    // #5878436 12-FEB-07 do not recurse into a non-containment relationship
    public void testNoInfiniteLoopTurningLoggingBackOnAfterADeepCopyObjectWithCSLoggingOff() {
        // verify logging is on
        assertNotNull(containedDataObject.getChangeSummary());
        assertTrue(containedDataObject.getChangeSummary().isLogging());
        containedDataObject.getChangeSummary().endLogging();
        // take an object with CS on and deep copy it
        SDODataObject copy = (SDODataObject)((SDOCopyHelper)copyHelper).copy(containedDataObject, getChangeSummary());
        containedDataObject.getChangeSummary().beginLogging();// infinite loop on resetChanges()
    }

    public void testUC010xDeepCopy1_1BidirectionalPropOutsideCopyTreeNotCopied() {
        // cdo/cbcdo (depth2) and cdo1 (depth1) are bidirectional with each other
        // create copy of cdo1 and verify that cdo/cbcdo was not copied since it is outside the copy tree
        SDODataObject copy = (SDODataObject)((SDOCopyHelper)copyHelper).copy(containedDataObject, getChangeSummary());

        // verify that container is null
        assertNull(copy.getContainer());
        // get bidirectional node from the copy root
        SDODataObject bidirCopyViaCopyRoot = (SDODataObject)copy.get(//
        "containedProperty2-notdataType/containedByContainedProperty1-notdataType");
        assertNull(bidirCopyViaCopyRoot);
        // get bidirectional start node via opposite property
        SDODataObject bidirCopyStart = (SDODataObject)copy.get(//
        "containedProperty2-notdataType");
        assertNotNull(bidirCopyStart);
        // get property containing opposite property link (inside copy tree)
        SDOProperty bidirCopyOppositeStartProp = bidirCopyStart.getType().getProperty(//
        "containedByContainedProperty1-notdataType");
        assertNotNull(bidirCopyOppositeStartProp);
        // Spec 8.4 "Properties that are bi-directional require type.dataType=false"
        assertFalse(bidirCopyOppositeStartProp.getType().isDataType());
        // get bidir property outside of copy tree
        SDOProperty bidirCopyViaOpposite = bidirCopyOppositeStartProp.getType()//
        .getProperty("contained1Property1-notdataType");
        assertNull(bidirCopyViaOpposite);
        SDODataObject bidirCopyViaOppositeDO = (SDODataObject)copy.get(//
        "rootproperty3-notdatatype/contained1Property1-notdataType");
        assertNull(bidirCopyViaOppositeDO);
    }

    public void testUC030xDeepCopy1_1BidirectionalPropInsideCopyTreeCopiedContTreeSameAsCopyTree() {
        // cdo/cbcdo (depth2) and cdo1 (depth1) are bidirectional with each other
        // create copy of the root and verifyt that both cdo1 and cdo/cbcdo were both copied as they are inside the copy tree
        SDODataObject copy = (SDODataObject)((SDOCopyHelper)copyHelper).copy(root, getChangeSummary());

        // verify that container is null
        assertNull(copy.getContainer());
        // get bidirectional node from the copy root (direction 1)
        SDODataObject bidirCopyViaCopyRoot = (SDODataObject)copy.get(//
        "rootproperty2-notdatatype/containedProperty2-notdataType/containedByContainedProperty1-notdataType");
        assertNotNull(bidirCopyViaCopyRoot);
        // get bidirectional start node via opposite property
        SDODataObject bidirCopyStart = (SDODataObject)copy.get(//
        "rootproperty2-notdatatype/containedProperty2-notdataType");
        assertNotNull(bidirCopyStart);
        // verify changeSummary is set on opposites
        ChangeSummary aCS2 = bidirCopyStart.getChangeSummary();
        assertNotNull(aCS2);
        // get property containing opposite property link (inside copy tree)
        SDOProperty bidirCopyOppositeStartProp = bidirCopyStart.getType().getProperty(//
        "containedByContainedProperty1-notdataType");
        assertNotNull(bidirCopyOppositeStartProp);
        // Spec 8.4 "Properties that are bi-directional require type.dataType=false"
        assertFalse(bidirCopyOppositeStartProp.getType().isDataType());
        // get bidir property inside copy tree
        SDOProperty oppositeEndProp = ((SDODataObject)copy.get(//
        "rootproperty3-notdatatype")).getType().getProperty("contained1Property1-notdataType").getOpposite();
        assertNotNull(oppositeEndProp);
        // Spec 8.4 "Properties that are bi-directional require type.dataType=false"
        assertFalse(oppositeEndProp.getType().isDataType());
        // get opposite DO (direction 2)
        SDODataObject bidirCopyViaOppositeDO = (SDODataObject)copy.get(//
        "rootproperty3-notdatatype/contained1Property1-notdataType");
        assertNotNull(bidirCopyViaOppositeDO);
        // verify changeSummary is set on opposites
        ChangeSummary aCS1 = bidirCopyViaOppositeDO.getChangeSummary();
        assertNotNull(aCS1);

        // Constraint C3 - Spec 3.9.4
        // verify there are no object pointers between source and copy (all objects are instance independent)
        // ie: a' != a  - or there are no references to "root" from "copy"
        assertNotSame(root.get("rootproperty2-notdatatype/containedProperty2-notdataType"),//
                copy.get("rootproperty2-notdatatype/containedProperty2-notdataType"));
        assertNotSame(root.get("rootproperty3-notdatatype"),//
                copy.get("rootproperty3-notdatatype"));
        assertNotSame(root.get(//
        "rootproperty2-notdatatype/containedProperty2-notdataType/containedByContainedProperty1-notdataType"),//
                copy.get("//" +//
                                        "rootproperty2-notdatatype/containedProperty2-notdataType/containedByContainedProperty1-notdataType"));

        // Spec 8.4 "Properties that are bi-directional require that no more than one end has containment=true"
        assertFalse(areBothBidirectionalOppositePropertiesContainmentTrue(//
        bidirCopyOppositeStartProp, oppositeEndProp));
    }

    public void testUC0302DeepCopy1_1BidirectionalPropInsideCopyTreeCopiedContTreeSameAsCopyTree() {
        // create copy of the root
        SDODataObject copy = (SDODataObject)((SDOCopyHelper)copyHelper).copy(rootUC4, getChangeSummary());

        // verify that container is null
        assertNull(copy.getContainer());
        // get bidirectional node from the copy root (direction 1)
        SDODataObject bidirCopyViaCopyRoot = (SDODataObject)copy.get(//
        "rootHome/homeAddress/addressWork");
        assertNotNull(bidirCopyViaCopyRoot);
        // get bidirectional start node via opposite property
        SDODataObject bidirCopyStart = (SDODataObject)copy.get(//
        "rootHome/homeAddress");
        assertNotNull(bidirCopyStart);

        // verify changeSummary is set on opposites
        //ChangeSummary aCS2 = bidirCopyStart.getChangeSummary();
        //assertNotNull(aCS2);
        // get property containing opposite property link (inside copy tree)
        SDOProperty bidirCopyOppositeStartProp = bidirCopyStart.getType().getProperty(//
        "addressWork");
        assertNotNull(bidirCopyOppositeStartProp);
        // Spec 8.4 "Properties that are bi-directional require type.dataType=false"
        assertFalse(bidirCopyOppositeStartProp.getType().isDataType());
        // get bidir property inside copy tree
        SDOProperty oppositeEndProp = ((SDODataObject)copy.get(//
        "rootWork")).getType().getProperty("workAddress").getOpposite();
        assertNotNull(oppositeEndProp);
        // Spec 8.4 "Properties that are bi-directional require type.dataType=false"
        assertFalse(oppositeEndProp.getType().isDataType());
        // get opposite DO (direction 2)
        SDODataObject bidirCopyViaOppositeDO = (SDODataObject)copy.get(//
        "rootWork/workAddress");
        assertNotNull(bidirCopyViaOppositeDO);
        // verify changeSummary is set on opposites
        //ChangeSummary aCS1 = bidirCopyViaOppositeDO.getChangeSummary();
        //assertNotNull(aCS1);
        // Constraint C3 - Spec 3.9.4
        // verify there are no object pointers between source and copy (all objects are instance independent)
        // ie: a' != a  - or there are no references to "root" from "copy"
        assertNotSame(rootUC4.get("rootHome/homeAddress"),//
                copy.get("rootHome/homeAddress"));
        assertNotSame(rootUC4.get("rootWork"),//
                copy.get("rootWork"));
        assertNotSame(rootUC4.get(//
        "rootHome/homeAddress/addressWork"),//
                copy.get("//" +//
                                        "rootHome/homeAddress/addressWork"));

        // Spec 8.4 "Properties that are bi-directional require that no more than one end has containment=true"
        assertFalse(areBothBidirectionalOppositePropertiesContainmentTrue(//
        bidirCopyOppositeStartProp, bidirCopyOppositeStartProp.getOpposite()));
    }

    // bidirectional property target is cached during containment iteration
    public void testUC0312DeepCopy1_nBidirectionalPropInsideCopyTreeCopiedContTreeSameAsCopyTree2ndNodeIsMany() {
        // set containment so that the bidirectional target is cached
        rootWorkm.setContainment(true);
        // create copy of the root
        SDODataObject copy = (SDODataObject)((SDOCopyHelper)copyHelper).copy(rootUC4m, getChangeSummary());

        // verify that container is null
        assertNull(copy.getContainer());
        // get bidirectional node from the copy root (direction 1)
        ListWrapper bidirCopyViaCopyRoot = (ListWrapper)copy.get(//
        "rootHome/homeAddress/addressWork");
        assertNotNull(bidirCopyViaCopyRoot);
        // get bidirectional start node via opposite property
        SDODataObject bidirCopyStart = (SDODataObject)copy.get(//
        "rootHome/homeAddress");
        assertNotNull(bidirCopyStart);

        // verify changeSummary is set on opposites
        //ChangeSummary aCS2 = bidirCopyStart.getChangeSummary();
        //assertNotNull(aCS2);
        // get property containing opposite property link (inside copy tree)
        SDOProperty bidirCopyOppositeStartProp = bidirCopyStart.getType().getProperty(//
        "addressWork");
        assertNotNull(bidirCopyOppositeStartProp);
        // Spec 8.4 "Properties that are bi-directional require type.dataType=false"
        assertFalse(bidirCopyOppositeStartProp.getType().isDataType());
        // get bidir property inside copy tree
        SDOProperty oppositeEndProp = ((SDODataObject)copy.get(//
        "rootHome/homeAddress")).getType().getProperty("addressWork");//.getOpposite();
        assertNotNull(oppositeEndProp);
        // Spec 8.4 "Properties that are bi-directional require type.dataType=false"
        assertFalse(oppositeEndProp.getType().isDataType());
        // get opposite DO (direction 2)
        SDODataObject bidirCopyViaOppositeDO = (SDODataObject)copy.get(//
        "rootWork/workAddress");

        //assertNotNull(bidirCopyViaOppositeDO);
        // verify changeSummary is set on opposites
        //ChangeSummary aCS1 = bidirCopyViaOppositeDO.getChangeSummary();
        //assertNotNull(aCS1);
        // Constraint C3 - Spec 3.9.4
        // verify there are no object pointers between source and copy (all objects are instance independent)
        // ie: a' != a  - or there are no references to "root" from "copy"
        assertNotSame(rootUC4m.get("rootHome/homeAddress"),//
                copy.get("rootHome/homeAddress"));
        // verify isMany objects are distinct
        ListWrapper do1 = (ListWrapper)rootUC4m.get("rootWork");
        ListWrapper do2 = (ListWrapper)copy.get("rootWork");

        // verify we are not just getting back empty lists for a null object lookup
        assertTrue(do1.size() > 0);
        assertTrue(do2.size() > 0);
        // verify lists are different (shallow check)
        assertNotSame(do1, do2);
        // verify list elements are different (deep check)
        assertNotNull(do1.get(0));
        assertNotNull(do2.get(0));
        assertFalse(do1.get(0) == do2.get(0));
        //assertNotSame((SDODataObject)rootUC4m.get(//
        //"rootHome/homeAddress/addressWork"),//
        //             (SDODataObject)copy.get("//" +//
        //                                    "rootHome/homeAddress/addressWork"));
        // Spec 8.4 "Properties that are bi-directional require that no more than one end has containment=true"
        assertFalse(areBothBidirectionalOppositePropertiesContainmentTrue(//
        bidirCopyOppositeStartProp, bidirCopyOppositeStartProp.getOpposite()));
    }

    // bidirectional property target is not cached during containment iteration - original unidirectional is cached
    public void testUC0313DeepCopy1_nBidirectionalPropInsideCopyTreeCopiedContTreeSameAsCopyTree2ndNodeIsManyNotPreCached() {
        // set containment so that the bidirectional target is notcached
        rootWorkm.setContainment(false);
        // create copy of the root
        SDODataObject copy = (SDODataObject)((SDOCopyHelper)copyHelper).copy(rootUC4m, getChangeSummary());

        // verify that container is null
        assertNull(copy.getContainer());
        // get bidirectional node from the copy root (direction 1)
        ListWrapper bidirCopyViaCopyRoot = (ListWrapper)copy.get(//
        "rootHome/homeAddress/addressWork");
        assertNotNull(bidirCopyViaCopyRoot);
        // get bidirectional start node via opposite property
        SDODataObject bidirCopyStart = (SDODataObject)copy.get(//
        "rootHome/homeAddress");
        assertNotNull(bidirCopyStart);

        // verify changeSummary is set on opposites
        //ChangeSummary aCS2 = bidirCopyStart.getChangeSummary();
        //assertNotNull(aCS2);
        // get property containing opposite property link (inside copy tree)
        SDOProperty bidirCopyOppositeStartProp = bidirCopyStart.getType().getProperty(//
        "addressWork");
        assertNotNull(bidirCopyOppositeStartProp);
        // Spec 8.4 "Properties that are bi-directional require type.dataType=false"
        assertFalse(bidirCopyOppositeStartProp.getType().isDataType());
        // get bidir property inside copy tree
        SDOProperty oppositeEndProp = ((SDODataObject)copy.get(//
        "rootHome/homeAddress")).getType().getProperty("addressWork");//.getOpposite();
        assertNotNull(oppositeEndProp);
        // Spec 8.4 "Properties that are bi-directional require type.dataType=false"
        assertFalse(oppositeEndProp.getType().isDataType());
        // get opposite DO (direction 2)
        SDODataObject bidirCopyViaOppositeDO = (SDODataObject)copy.get(//
        "rootWork/workAddress");

        //assertNotNull(bidirCopyViaOppositeDO);
        // verify changeSummary is set on opposites
        //ChangeSummary aCS1 = bidirCopyViaOppositeDO.getChangeSummary();
        //assertNotNull(aCS1);
        // Constraint C3 - Spec 3.9.4
        // verify there are no object pointers between source and copy (all objects are instance independent)
        // ie: a' != a  - or there are no references to "root" from "copy"
        assertNotSame(rootUC4m.get("rootHome/homeAddress"),//
                copy.get("rootHome/homeAddress"));
        // verify isMany objects are distinct
        ListWrapper do1 = (ListWrapper)rootUC4m.get("rootWork");
        ListWrapper do2 = (ListWrapper)copy.get("rootWork");

        // verify we are not just getting back empty lists for a null object lookup
        assertTrue(do1.size() > 0);
        assertTrue(do2.size() > 0);
        // verify lists are different (shallow check)
        assertNotSame(do1, do2);
        // verify list elements are different (deep check)
        assertNotNull(do1.get(0));
        assertNotNull(do2.get(0));
        // the unidirectional property rootWork gives us the original list in the copy
        assertTrue(do1.get(0) == do2.get(0));
        //assertNotSame((SDODataObject)rootUC4m.get(//
        //"rootHome/homeAddress/addressWork"),//
        //             (SDODataObject)copy.get("//" +//
        //                                    "rootHome/homeAddress/addressWork"));
        // Spec 8.4 "Properties that are bi-directional require that no more than one end has containment=true"
        assertFalse(areBothBidirectionalOppositePropertiesContainmentTrue(//
        bidirCopyOppositeStartProp, bidirCopyOppositeStartProp.getOpposite()));
    }

    // bidirectional property target is not cached during containment iteration - it is unset
    public void testUC0314DeepCopy1_nBidirectionalPropInsideCopyTreeCopiedContTreeSameAsCopyTree2ndNodeIsMany_unidirectionalUnset() {
        // set containment so that the bidirectional target is notcached
        //rootWorkm.setContainment(false);
        rootUC4m.unset(rootWorkm);
        // create copy of the root
        SDODataObject copy = (SDODataObject)((SDOCopyHelper)copyHelper).copy(rootUC4m, getChangeSummary());

        // verify that container is null
        assertNull(copy.getContainer());
        // get bidirectional node from the copy root (direction 1)
        ListWrapper bidirCopyViaCopyRoot = (ListWrapper)copy.get(//
        "rootHome/homeAddress/addressWork");
        assertNotNull(bidirCopyViaCopyRoot);
        // get bidirectional start node via opposite property
        SDODataObject bidirCopyStart = (SDODataObject)copy.get(//
        "rootHome/homeAddress");
        assertNotNull(bidirCopyStart);

        // verify changeSummary is set on opposites
        //ChangeSummary aCS2 = bidirCopyStart.getChangeSummary();
        //assertNotNull(aCS2);
        // get property containing opposite property link (inside copy tree)
        SDOProperty bidirCopyOppositeStartProp = bidirCopyStart.getType().getProperty(//
        "addressWork");
        assertNotNull(bidirCopyOppositeStartProp);
        // Spec 8.4 "Properties that are bi-directional require type.dataType=false"
        assertFalse(bidirCopyOppositeStartProp.getType().isDataType());
        // get bidir property inside copy tree
        SDOProperty oppositeEndProp = ((SDODataObject)copy.get(//
        "rootHome/homeAddress")).getType().getProperty("addressWork");//.getOpposite();
        assertNotNull(oppositeEndProp);
        // Spec 8.4 "Properties that are bi-directional require type.dataType=false"
        assertFalse(oppositeEndProp.getType().isDataType());
        // get opposite DO (direction 2)
        SDODataObject bidirCopyViaOppositeDO = (SDODataObject)copy.get(//
        "rootWork/workAddress");

        //assertNotNull(bidirCopyViaOppositeDO);
        // verify changeSummary is set on opposites
        //ChangeSummary aCS1 = bidirCopyViaOppositeDO.getChangeSummary();
        //assertNotNull(aCS1);
        // Constraint C3 - Spec 3.9.4
        // verify there are no object pointers between source and copy (all objects are instance independent)
        // ie: a' != a  - or there are no references to "root" from "copy"
        assertNotSame(rootUC4m.get("rootHome/homeAddress"),//
                copy.get("rootHome/homeAddress"));
        // verify isMany objects are distinct
        ListWrapper do1 = (ListWrapper)rootUC4m.get("rootWork");
        ListWrapper do2 = (ListWrapper)copy.get("rootWork");

        // Verify that bidirectional outside the copytree is not set
        assertTrue(do2.size() == 0);
        assertTrue(do1.size() == 0);
        // verify lists are different (shallow check)
        assertNotSame(do1, do2);
        // verify list elements are different (deep check)
        assertNull(do2.get(0));
        assertNull(do1.get(0));
        // the unidirectional property rootWork (unset) gives us no copy of the list
        //assertFalse(do1.get(0) == do2.get(0));
        //assertNotSame((SDODataObject)rootUC4m.get(//
        //"rootHome/homeAddress/addressWork"),//
        //             (SDODataObject)copy.get("//" +//
        //                                    "rootHome/homeAddress/addressWork"));
        // Spec 8.4 "Properties that are bi-directional require that no more than one end has containment=true"
        assertFalse(areBothBidirectionalOppositePropertiesContainmentTrue(//
        bidirCopyOppositeStartProp, bidirCopyOppositeStartProp.getOpposite()));
    }

    public void testUC0102DeepCopy1_1BidirectionalPropSubTreeOutsideCopyTreeNotCopied() {
        // create copy of home and verify that rw was not copied since it is outside the copy tree
        SDODataObject copy = (SDODataObject)((SDOCopyHelper)copyHelper).copy(homeObject, getChangeSummary());

        // verify that container is null
        assertNull(copy.getContainer());
        // get bidirectional node from the copy root
        SDODataObject bidirCopyViaCopyRoot = (SDODataObject)copy.get(//
        "homeAddress/addressWork");
        assertNotNull(bidirCopyViaCopyRoot);
        // get bidirectional start node via opposite property
        SDODataObject bidirCopyStart = (SDODataObject)copy.get(//
        "homeAddress");
        assertNotNull(bidirCopyStart);
        // get property containing opposite property link (inside copy tree)
        SDOProperty bidirCopyOppositeStartProp = bidirCopyStart.getType().getProperty(//
        "addressWork");
        assertNotNull(bidirCopyOppositeStartProp);
        // Spec 8.4 "Properties that are bi-directional require type.dataType=false"
        assertFalse(bidirCopyOppositeStartProp.getType().isDataType());
        // get bidir property outside of copy tree
        SDOProperty bidirCopyViaOpposite = bidirCopyOppositeStartProp.getType()//
        .getProperty("workAddress");
        assertNull(bidirCopyViaOpposite);
        SDODataObject bidirCopyViaOppositeDO = (SDODataObject)copy.get(//
        "rootWork/workAddress");
        assertNull(bidirCopyViaOppositeDO);
    }

    // purpose: copy and source reference same type and properties
    // TODO: 20060906 bidirectional/reference
    public void testCopySourceHaveSameType() {
        SDODataObject copy = (SDODataObject)((SDOCopyHelper)copyHelper).copy(root, getChangeSummary());
        assertFalse(copy.equals(root));
        assertEquals(copy.getType(), root.getType());
        assertEqualityHelperEqual(root, copy);
    }

    // purpose: copy and source have same map if source has no properties having not null opposite
    // or if source has but these properties are not set.
    // TODO: this tests only tests the MapValueStore impl, separate generic test required
    public void testCopySourceHaveSameMap() {
        SDODataObject copy = (SDODataObject)((SDOCopyHelper)copyHelper).copy(root, getChangeSummary());
        int copySize = getPropertiesSize(copy);
        assertEquals(copySize, getPropertiesSize(root));
        testCopySourceMap(root, copy);
        assertEqualityHelperEqual(root, copy);
    }

    // recursively compare source and copy DataObjects when they both contain othe DataObject
    // otherwise, just compare their primitive value and containment properties in the map
    // purpose: test case 1 and test case 2 : in tree dataobject and datatype value
    // TODO: this tests only tests the MapValueStore impl, separate generic test required
    private void testCopySourceMap(SDODataObject source, SDODataObject copy) {
        assertFalse(source.equals(copy));// make sure copy source are not the same object
        Set copyKeys = getPropertiesKeySet(copy);
        Set keys = getPropertiesKeySet(source);
        Iterator iterKeys = copyKeys.iterator();
        while (iterKeys.hasNext()) {
            String propertyName = (String)iterKeys.next();
            assertTrue(keys.contains(propertyName));
            Object s = source.get(propertyName);
            Object c = copy.get(propertyName);
            Property cProperty = copy.getInstanceProperty(propertyName);
            if (!(c instanceof SDODataObject) && !(c instanceof SDOChangeSummary) &&//
                    !cProperty.isMany()) {
                assertEquals(c, s);// DataType, then value should equals
            } else {
                if (c instanceof SDODataObject) {
                    if (cProperty.isContainment()) {
                        testCopySourceMap((SDODataObject)s, (SDODataObject)c);// contained DataObject, recursive
                    }
                }
                if (c instanceof SDOChangeSummary) {// ChangeSummary Type
                    assertEquals(((SDOChangeSummary)c).isLogging(),//
                                 ((SDOChangeSummary)s).isLogging());
                    assertEquals(copy, ((SDOChangeSummary)c).getRootObject());
                }
                if (cProperty.isMany()) {
                    Iterator iterList = ((List)c).iterator();
                    int i = 0;
                    while (iterList.hasNext()) {
                        SDODataObject cO = (SDODataObject)iterList.next();
                        SDODataObject sO = (SDODataObject)((List)s).get(i);
                        testCopySourceMap(sO, cO);
                        i++;
                    }
                }
            }
        }
    }

    // purpose: source add a new not containment property with null opposite and set its value,
    // then copy and source should still have same map.
    // Test case 4: unidirectional
    // TODO: this tests only tests the DefaultValueStore impl, separate generic test required
    // TODO: 20060906 bidirectional/reference
    public void testUnidirectionalOutsideCopyTreeReferencesSameDO() {
        // set this property value to containedDataObject
        //root.set("rootProperty_NotContainment", containedDataObject);
        SDODataObject copyOriginal = (SDODataObject)rootUCUniOutside.get("rootHome");
        SDODataObject copy = (SDODataObject)((SDOCopyHelper)copyHelper).copy(copyOriginal, getChangeSummary());
        int copySize = getPropertiesSize(copy);
        assertTrue(copySize == getPropertiesSize(copyOriginal));
        //  unidirectional, reference the same DataObject outside the tree
        // Constraint C4 - Spec 3.9.4
        assertTrue(copy.get("rootWork") == copyOriginal.get("rootWork"));
        assertEqualityHelperEqual(copyOriginal, copy);
    }

    public void testUnidirectionalInsideCopyTreeReferencesCopyDO() {
        // set this property value to containedDataObject
        root.set("rootProperty_NotContainment", containedDataObject);

        SDODataObject copy = (SDODataObject)((SDOCopyHelper)copyHelper).copy(root, getChangeSummary());
        int copySize = getPropertiesSize(copy);
        assertTrue(copySize == getPropertiesSize(root));
        //  unidirectional, reference the the copy DataObject inside the tree
        // Constraint C4 - Spec 3.9.4
        assertTrue(copy.get("rootProperty_NotContainment") != root.get("rootProperty_NotContainment"));
        assertEqualityHelperEqual(root, copy);
    }

    // purpose: test case 6 about changesummary
    public void testSourceHasPropertyWithChangeSummaryValue() {
        SDODataObject copy = (SDODataObject)((SDOCopyHelper)copyHelper).copy(root, getChangeSummary());
        SDODataObject copyContainedDataObject = (SDODataObject)copy.get(rootProperty2.getName());
        assertEqualityHelperEqual(root, copy);

        assertNotNull(copyContainedDataObject);

        SDOChangeSummary copyChSum = (SDOChangeSummary)copyContainedDataObject.get(containedProperty_ChangeSummary.getName());

        assertNotNull(copyChSum);
        ChangeSummary originalChSum = root.getDataObject("rootproperty2-notdatatype").getChangeSummary();
        assertFalse(copyChSum.equals(originalChSum));

        assertEquals(originalChSum.isLogging(), copyChSum.isLogging());// logging status is same
        // their root dataObject have the same setting
        testCopySourceMap(containedDataObject, copyChSum.getRootObject());

    }

    // Test case 3: for bidirectional property, skip it and no value will be referenced or copied.
    // TODO: 20060906 bidirectional/reference
    public void testBiDirectionalRelationInDeepCopy() {
        SDODataObject copyRoot = (SDODataObject)((SDOCopyHelper)copyHelper).copy(root, getChangeSummary());

        SDODataObject copyContainedByContainedDataObject = (SDODataObject)copyRoot//
        .get("rootproperty2-notdatatype/containedProperty2-notdataType");

        assertNotNull(copyContainedByContainedDataObject);
        assertFalse(copyContainedByContainedDataObject.equals(containedDataObject));// make sure not the same
        SDODataObject oppositeObject = (SDODataObject)copyContainedByContainedDataObject//
        .get("containedByContainedProperty1-notdataType");

        //assertNull(oppositeObject);
        assertNotNull(oppositeObject);// 20060906: bidirectional support added

        SDODataObject copyContainedDataObject1 = (SDODataObject)copyRoot.get(rootProperty3);

        assertNotNull(copyContainedDataObject1);
        assertFalse(copyContainedDataObject1.equals(containedDataObject1));
        SDODataObject oppositeObject1 = (SDODataObject)copyContainedDataObject1//
        .get(contained1Property1);

        //assertNull(oppositeObject1);
        assertNotNull(oppositeObject1);// 20060906: bidirectional support added
        assertEqualityHelperEqual(root, copyRoot);
    }

    // purpose: test case 5: not in containment tree
    public void testDataObjectNotInContainmentTree() {
        // create DataObject dataObjectNotInTreeroot contains another not in tree DataObject
        // dataObjectNotInTree
        SDOType notInTreeRootType = new SDOType((SDOTypeHelper) aHelperContext.getTypeHelper());
        SDOProperty notInTreeProperty = new SDOProperty(aHelperContext);
        notInTreeProperty.setName("notInTreeProperty");
        SDOType notInTreePropertyType = new SDOType((SDOTypeHelper) aHelperContext.getTypeHelper());
        notInTreePropertyType.setDataType(false);
        notInTreeProperty.setContainment(true);
        notInTreeProperty.setType(notInTreePropertyType);
        notInTreeRootType.addDeclaredProperty(notInTreeProperty);

        //SDODataObject dataObjectNotInTree = new SDODataObject();
        SDODataObject dataObjectNotInTree = (SDODataObject)dataFactory.create(notInTreeRootType);
        SDODataObject dataObjectNotInTreeroot = (SDODataObject)dataFactory.create(notInTreeRootType);
        dataObjectNotInTreeroot.set(notInTreeProperty, dataObjectNotInTree);

        // set dataObjectNotInTree as not containment property's value
        containedByContainedDataObject.set(containedByContainedProperty2, dataObjectNotInTree);

        SDODataObject copyRoot = null;
        try {
            copyRoot = (SDODataObject)((SDOCopyHelper)copyHelper).copy(root, getChangeSummary());
            //fail("IllegalArgumentException should be thrown.");
        } catch (IllegalArgumentException e) {
            assertNull(copyRoot);
        }
    }

    // purpose: negative test by passed in null value
    public void testCopyWithNullValue() {
        DataObject source = null;
        DataObject o = ((SDOCopyHelper)copyHelper).copy(source, getChangeSummary());
        assertNull(o);
    }

    // purpose: test containment property is many
    public void testCopyWithContainmentPropertyIsMany() {
        SDODataObject c = (SDODataObject)((SDOCopyHelper)copyHelper).copy(root, getChangeSummary());
        assertEqualityHelperEqual(root, c);
        List cL = (List)c.get(rootProperty4);
        List sL = (List)root.get(rootProperty4);
        assertFalse(cL == sL);
        assertTrue(cL.size() == sL.size());
        // check that items in the copied list are distinct objects from their original - same as !.equals
        assertNotNull(sL.get(0));
        assertNotNull(cL.get(0));
        assertFalse(sL.get(0) == cL.get(0));
        testCopySourceMap(root, c);

    }

    // purpose: test non-containment property is many (unidirectional)
    public void testCopyWithNotContainmentPropertyIsMany() {
        rootProperty4.setContainment(false);
        SDODataObject c = (SDODataObject)((SDOCopyHelper)copyHelper).copy(root, getChangeSummary());
        assertEqualityHelperEqual(root, c);
        List cL = (List)c.get(rootProperty4);
        List sL = (List)root.get(rootProperty4);
        assertFalse(cL == sL);
        //System.out.println(cL.size());
        assertTrue(sL.size() > 0);
        assertEquals(sL.size(), cL.size());
        assertEquals(sL.get(0), cL.get(0));
        // check that items in the copied list are not distinct objects from their original - same as .equals
        assertTrue(sL.get(0) == cL.get(0));
    }

    // purpose: test containment property is many
    public void testCopyWithNotContainmentPropertyIsManyValueNotInTree() {
        Type testType = new SDOType("uri", "name");
        SDODataObject test = (SDODataObject)dataFactory.create(testType);
        objects.add(test);
        root.set(rootProperty4, objects);
        rootProperty4.setContainment(false);
        //rootProperty4.setContainment(true);
        test._setContainer(null);
        SDODataObject c = null;
        try {
            c = (SDODataObject)((SDOCopyHelper)copyHelper).copy(root, getChangeSummary());
            //fail("IllegalArgumentException should be thrown.");
        } catch (IllegalArgumentException e) {
            assertNull(c);
        }
    }

    public void testShallowCopySourceHaveSameType() {
        SDODataObject copy = (SDODataObject) copyHelper.copyShallow(root);
        assertEquals(copy.getType(), root.getType());
    }

    // TODO: this tests only tests the MapValueStore impl, separate generic test required
    public void testShallowCopySourceHaveDifferentMap() {
        SDODataObject copy = (SDODataObject) copyHelper.copyShallow(root);
        int copySize = getPropertiesSize(copy);
        assertFalse(copySize == getPropertiesSize(root));
        //testCopySourceMap(root, copy);
    }

    // TODO: this tests only tests the MapValueStore impl, separate generic test required
    public void testShallowCopySourceMapsHaveSameDataTypeObj() {
        SDODataObject copy = (SDODataObject) copyHelper.copyShallow(root);
        int copySize = getPropertiesSize(copy);
        assertFalse(copySize == getPropertiesSize(root));

        Set copyKeys = getPropertiesKeySet(copy);
        Set keys = getPropertiesKeySet(root);
        Iterator iterKeys = copyKeys.iterator();
        while (iterKeys.hasNext()) {
            String propertyName = (String)iterKeys.next();
            assertTrue(keys.contains(propertyName));
            Object s = root.get(propertyName);
            Object c = copy.get(propertyName);
            if (!(c instanceof SDODataObject) && !(c instanceof SDOChangeSummary)) {
                assertEquals(c, s);
            }
        }
    }

    public void testShallowCopySourceHasPropertyWithChangeSummaryValue() {
        SDODataObject copy = (SDODataObject)copyHelper.copyShallow(containedDataObject);

        assertNotNull(copy);

        SDOChangeSummary copyChSum = (SDOChangeSummary)copy//
        .get(containedProperty_ChangeSummary.getName());

        assertNotNull(copyChSum);
        ChangeSummary originalChSum = root.getDataObject("rootproperty2-notdatatype").getChangeSummary();
        assertEquals(originalChSum.isLogging(), copyChSum.isLogging());// loggin status is same
    }

    public void testShallowCopyWithNullValue() {
        DataObject source = null;
        DataObject o = copyHelper.copyShallow(source);
        assertNull(o);
    }

    // add oc test
    public void assertEqualityHelperEqual(DataObject root, DataObject copy) {
        assertTrue(equalityHelper.equal(root, copy));

    }

    public void testDeleteLeaf() {
        assertEquals(containedByContainedDataObject, containedDataObject.get(containedProperty2));
        containedByContainedDataObject.getChangeSummary().endLogging();
        containedByContainedDataObject.unset(containedByContainedProperty1);
        containedByContainedDataObject.getChangeSummary().beginLogging();
        containedByContainedDataObject.delete();
        assertNull(containedDataObject.get(containedProperty2));
        DataObject deepCopy = ((SDOCopyHelper)copyHelper).copy(containedDataObject, getChangeSummary());
        assertEqualityHelperEqual(containedDataObject, deepCopy);
    }
}
