/*
 * 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.testing.sdo.model.changesummary;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import junit.textui.TestRunner;
import org.eclipse.persistence.sdo.SDOChangeSummary;
import org.eclipse.persistence.sdo.helper.SDOTypeHelper;
import org.eclipse.persistence.sdo.helper.delegates.SDOTypeHelperDelegate;
import org.eclipse.persistence.testing.sdo.helper.xmlhelper.loadandsave.changesummary.ChangeSummaryRootLoadAndSaveTestCases;
import commonj.sdo.ChangeSummary;
import commonj.sdo.DataObject;
import commonj.sdo.Property;
import commonj.sdo.helper.XMLDocument;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;

public abstract class ChangeSummaryOnRootTestCases extends ChangeSummaryRootLoadAndSaveTestCases {
    protected DataObject rootObject;
    protected ChangeSummary cs;
    public static final String URINAME = "http://www.example.org";
    public static final String TYPENAME = "purchaseOrder";

    @Override
    protected String getControlRootURI() {
        return URINAME;
    }

    @Override
    protected String getControlRootName() {
        return TYPENAME;
    }

    @Override
    protected String getRootInterfaceName() {
        return "PurchaseOrderType";
    }

    @Override
    protected String getControlFileName() {
        // implemented by subclass
        return getControlFileName2();
    }

    @Override
    public void testClassGenerationLoadAndSave() throws Exception {
      //TODO:implement
    }

    protected abstract String getControlFileName2();

    //protected String getNoSchemaControlFileName() {
    //    return ("./org/eclipse/persistence/testing/sdo/helper/xmlhelper/PurchaseOrderDeepWithCS.xml");
    //}
    protected String getModelFileName() {
        return ("./org/eclipse/persistence/testing/sdo/helper/xmlhelper/PurchaseOrderDeepWithCS.xml");
    }

    @Override
    public void testNoSchemaLoadFromInputStreamSaveDataObjectToString() throws Exception {
        //do nothing
        //TODO: need to make this test run
    }

    public ChangeSummaryOnRootTestCases(String name) {
        super(name);
    }

    @Override
    public String getSchemaName() {
        return ("./org/eclipse/persistence/testing/sdo/helper/xmlhelper/PurchaseOrderDeepWithCS.xsd");
    }

    public static void main(String[] args) {
        String[] arguments = { "-c", "org.eclipse.persistence.testing.sdo.model.changesummary.ChangeSummaryOnRootTestCases" };
        TestRunner.main(arguments);
    }

    @Override
    public void setUp() {
        super.setUp();// watch setup redundancy
        //define types from deep with cs
        try {
            InputStream is = new FileInputStream(getSchemaName());
            List types = xsdHelper.define(is, null);
            XMLDocument document = xmlHelper.load(new FileInputStream(getModelFileName()));
            rootObject = document.getRootObject();
            cs = rootObject.getChangeSummary();
            cs.endLogging();
        } catch (Exception e) {
            e.printStackTrace();
            fail("An error occurred loading the xsd");
        }
    }

    @Override
    protected String getControlString(String fileName) {
        try {
            FileInputStream inputStream = new FileInputStream(fileName);
            byte[] bytes = new byte[inputStream.available()];
            inputStream.read(bytes);
            return new String(bytes);
        } catch (Exception e) {
            fail("An error occurred loading the control document");
            e.printStackTrace();
            return null;
        }
    }

    @Override
    protected void verifyAfterLoad(XMLDocument document) {
        assertNull(document.getRootObject().getContainer());
    }

    @Override
    protected List defineTypes() {
        // do not define types twice - so we do not set dirty=true
        //return xsdHelper.define(getSchema(getSchemaName()));
        return new ArrayList(((SDOTypeHelper)typeHelper).getTypesHashMap().values());
    }

    @Override
    protected String getSchemaLocation() {
        return "";
    }

    public DataObject loadObjectFromInputStream() throws Exception {
        List types = defineTypes();
        FileInputStream inputStream = new FileInputStream(getControlFileName());
        XMLDocument document = xmlHelper.load(inputStream);

        // reset global variables for this xml based test
        rootObject = document.getRootObject();
        cs = document.getRootObject().getChangeSummary();

        verifyAfterLoad(document);
        return document.getRootObject();

        //compareXML(getControlString(getLoadControlFileName()), outputStream.toString());
        //ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        //xmlHelper.save(document, outputStream, null);
        //compareXML(getControlString(getControlFileName()), outputStream.toString());
    }

    public void saveObjectToOutputStream() throws Exception {
        //List types = defineTypes();
        //FileInputStream inputStream = new FileInputStream(getControlFileName());
        //XMLDocument document = xmlHelper.load(inputStream);
        //verifyAfterLoad(document);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
            xmlHelper.save(rootObject, URINAME,//
                           TYPENAME,//
                           //System.out);//outputStream);
            outputStream);
        } catch (Exception e) {
            System.out.println("Exception: " + e.getMessage());
            e.printStackTrace();
        }
        StringReader sreader = new StringReader(outputStream.toString());
        InputSource inputSource = new InputSource(sreader);
        Document testDocument = parser.parse(inputSource);
        sreader.close();
        assertXMLIdentical(getDocument(getControlFileName()), testDocument);
    }

    public void assertDeleteDetachUnsetComplexManyBelowRoot(boolean testLoadSave,//
                                                            boolean isDeleted,//
                                                            DataObject itemsDO,//
                                                            DataObject item2DO,//
                                                            DataObject item2ProductDO,//
                                                            DataObject item2ProductPrice1DO,//
                                                            DataObject item2ProductPrice2DO) {
        if (!testLoadSave) {
            assertModified(itemsDO, cs);
            if (isDeleted) {
                assertDeleted(item2DO, cs);
                assertDeleted(item2ProductDO, cs);
                assertDeleted(item2ProductPrice1DO, cs);
                assertDeleted(item2ProductPrice2DO, cs);
            } else {
                assertDetached(item2DO, cs);
                assertDetached(item2ProductDO, cs, false);
                assertDetached(item2ProductPrice1DO, cs, false);
                assertDetached(item2ProductPrice2DO, cs, false);
            }
        }

        assertEquals(5, cs.getChangedDataObjects().size());

        assertEquals(14, ((SDOChangeSummary)cs).getOldContainer().size());
        assertEquals(14, ((SDOChangeSummary)cs).getOldContainmentProperty().size());

        if (!testLoadSave) {
            assertEquals(1, cs.getOldValues(itemsDO).size());
            ChangeSummary.Setting containmentSetting = (ChangeSummary.Setting)cs.getOldValues(itemsDO).get(0);
            assertEquals("item", containmentSetting.getProperty().getName());
            assertTrue(containmentSetting.getValue() instanceof List);
            assertEquals(2, ((List)containmentSetting.getValue()).size());
            assertEquals(true, containmentSetting.isSet());
        }
    }

    public void assertDeleteComplexSingle(DataObject shipToDO,//
                                          DataObject yardDO,//
                                          List phoneList,//
                                          DataObject phone1,//
                                          DataObject phone2,//
                                          Property containmentProp,//
                                          Object oldStreet) {
        // for dataType verify copy of shipTo has a set child in deleted list and current value is unset
        assertNull(shipToDO.get("street"));
        DataObject deepCopyShipTo = (DataObject)((SDOChangeSummary)cs).getDeepCopies().get(shipToDO);
        assertEquals("123 Maple Street", deepCopyShipTo.get("street"));

        assertEquals(14, ((SDOChangeSummary)cs).getOldContainer().size());
        assertEquals(14, ((SDOChangeSummary)cs).getOldContainmentProperty().size());

        assertModified(rootObject, cs);
        assertNotNull(cs.getOldContainmentProperty(shipToDO));
        assertNotNull(cs.getOldContainer(shipToDO));
        assertEquals(rootObject, cs.getOldContainer(shipToDO));

        assertEquals(shipToDO, cs.getOldContainer(yardDO));
        assertEquals(shipToDO, cs.getOldContainer(phone1));
        assertEquals(shipToDO, cs.getOldContainer(phone2));

        assertEquals(containmentProp, cs.getOldContainmentProperty(shipToDO));
        assertDeleted(shipToDO, cs);
        //assertDeleted(yardDO, cs);
        assertYardDeleted(shipToDO, yardDO, cs, true);
        assertDeleted(phone1, cs);
        assertDeleted(phone2, cs);
        assertEquals(5, cs.getChangedDataObjects().size());
        assertEquals(1, cs.getOldValues(rootObject).size());
        ChangeSummary.Setting setting = (ChangeSummary.Setting)cs.getOldValues(rootObject).get(0);
        assertEquals(containmentProp, setting.getProperty());
        assertEquals(shipToDO, setting.getValue());
        assertEquals(true, setting.isSet());
        assertEquals(8, cs.getOldValues(shipToDO).size());

        assertEquals(1, cs.getOldValues(phone1).size());
        assertEquals(1, cs.getOldValues(phone2).size());
        ChangeSummary.Setting phone2setting = (ChangeSummary.Setting)cs.getOldValues(phone2).get(0);
        assertEquals("number", phone2setting.getProperty().getName());
        assertEquals("2345678", phone2setting.getValue());
        assertEquals(true, phone2setting.isSet());

    }

    public void assertDeleteDetachUnsetComplexSingleBelowRoot//testDeleteYard() {
    (boolean testLoadSave, boolean isDeleted, DataObject shipToDO, DataObject yardDO) {
        //DataObject shipToDO = rootObject.getDataObject("shipTo");
        //DataObject yardDO = shipToDO.getDataObject("yard");
        //cs.beginLogging();
        //yardDO.delete();
        assertEquals(2, cs.getChangedDataObjects().size());
        //assertEquals(1, cs.getOldValues(shipToDO).size());
        if (!testLoadSave) {
            if (isDeleted) {
                assertYardDeleted(shipToDO, yardDO, cs, true);
            } else {
                assertYardDetached(shipToDO, yardDO, cs, false);
            }
            assertModified(shipToDO, cs);
            ChangeSummary.Setting shipToSetting = (ChangeSummary.Setting)cs.getOldValues(shipToDO).get(0);
            assertEquals("yard", shipToSetting.getProperty().getName());
            assertEquals(true, shipToSetting.isSet());
            DataObject deepCopyYard = (DataObject)((SDOChangeSummary)cs).getDeepCopies().get(yardDO);
            assertEquals(deepCopyYard, shipToSetting.getValue());
            assertEquals(shipToDO, cs.getOldContainer(yardDO));
        }

        assertEquals(14, ((SDOChangeSummary)cs).getOldContainer().size());
        assertEquals(14, ((SDOChangeSummary)cs).getOldContainmentProperty().size());

    }

    public void assertCSonRootDeleteDetachUnsetComplexManyAtRoot//testDeleteItems() {
    (boolean testLoadSave,//
     DataObject itemsDO,//
     DataObject item1DO,//
     DataObject item1ProductDO,//
     DataObject item1ProductPrice1DO,//
     DataObject item1ProductPrice2DO,//
     DataObject item2DO,//
     DataObject item2ProductDO,//
     DataObject item2ProductPrice1DO,//
     DataObject item2ProductPrice2DO) {
        //DataObject itemsDO = rootObject.getDataObject("items");
        //DataObject item1DO = rootObject.getDataObject("items/item[1]");
        //DataObject item2DO = rootObject.getDataObject("items/item[2]");
        //DataObject item1ProductDO = item1DO.getDataObject("product");
        //DataObject item1ProductPrice1DO = item1ProductDO.getDataObject("price[1]");
        //DataObject item1ProductPrice2DO = item1ProductDO.getDataObject("price[2]");
        //DataObject item2ProductDO = item2DO.getDataObject("product");
        //DataObject item2ProductPrice1DO = item2ProductDO.getDataObject("price[1]");
        //DataObject item2ProductPrice2DO = item2ProductDO.getDataObject("price[2]");
        //cs.beginLogging();
        //itemsDO.delete();
        if (!testLoadSave) {
            assertNotNull(item1DO);
            assertNotNull(item2DO);

            assertModified(rootObject, cs);
            assertDeleted(itemsDO, cs);
            assertDeleted(item1DO, cs);
            assertDeleted(item2DO, cs);
            assertDeleted(item1ProductDO, cs);
            assertDeleted(item1ProductPrice1DO, cs);
            assertDeleted(item1ProductPrice2DO, cs);
            assertDeleted(item2ProductDO, cs);
            assertDeleted(item2ProductPrice1DO, cs);
            assertDeleted(item2ProductPrice2DO, cs);
        }
        assertEquals(10, cs.getChangedDataObjects().size());

        assertEquals(14, ((SDOChangeSummary)cs).getOldContainer().size());//  from 9

        assertEquals(14, ((SDOChangeSummary)cs).getOldContainmentProperty().size());// from 9

        // verify keys of oldSettings Map
        //checkOldSettingsSizeTree("0000", changeSummaryB, root, dataObjectB, dataObjectC, dataObjectD);
    }

    public void assertCSonRootDeleteDetachUnsetComplexManyLeafBelowComplexManyBelowRoot// testDetachItem2Price1() {
    (boolean testLoadSave,//
     DataObject itemsDO,//
     DataObject item1DO,//
     DataObject item1ProductDO,//
     DataObject item1ProductPrice1DO,//
     DataObject item1ProductPrice2DO,//
     DataObject item2DO,//
     DataObject item2ProductDO,//
     DataObject item2ProductPrice1DO,//
     DataObject item2ProductPrice2DO) {
        //DataObject itemsDO = rootObject.getDataObject("items");
        //DataObject item1DO = rootObject.getDataObject("items/item[1]");
        //DataObject item2DO = rootObject.getDataObject("items/item[2]");
        //DataObject item1ProductDO = item1DO.getDataObject("product");
        //DataObject item1ProductPrice1DO = item1ProductDO.getDataObject("price[1]");
        //DataObject item1ProductPrice2DO = item1ProductDO.getDataObject("price[2]");
        //DataObject item2ProductDO = item2DO.getDataObject("product");
        //DataObject item2ProductPrice1DO = item2ProductDO.getDataObject("price[1]");
        //DataObject item2ProductPrice2DO = item2ProductDO.getDataObject("price[2]");
        //cs.beginLogging();
        //item2ProductPrice1DO.detach();
        if (!testLoadSave) {
            assertModified(item2ProductDO, cs);
            assertDetached(item2ProductPrice1DO, cs);
            ChangeSummary.Setting containmentSetting = (ChangeSummary.Setting)cs.getOldValues(item2ProductDO).get(0);
            assertEquals("price", containmentSetting.getProperty().getName());
            assertTrue(containmentSetting.getValue() instanceof List);
            assertEquals(2, ((List)containmentSetting.getValue()).size());
            assertEquals(true, containmentSetting.isSet());
        }
        assertEquals(2, cs.getChangedDataObjects().size());

        assertEquals(14, ((SDOChangeSummary)cs).getOldContainer().size());
        assertEquals(14, ((SDOChangeSummary)cs).getOldContainmentProperty().size());

    }

    /*
        // verify unset after unset - logging on in the middle
        public void testUnsetCommentsAfterLoggingOnAfterUnset() {
            //cs.beginLogging();
            rootObject.unset("comment");
            cs.beginLogging();
            rootObject.unset("comment");
            assertUnchanged(rootObject, cs);//
            assertEquals(0, cs.getChangedDataObjects().size());

            List oldValues = cs.getOldValues(rootObject);
            assertEquals(0, oldValues.size());
        }


        public void testDetachItems() {
            DataObject itemsDO = rootObject.getDataObject("items");
            DataObject item1DO = rootObject.getDataObject("items/item[1]");
            DataObject item2DO = rootObject.getDataObject("items/item[2]");

            DataObject item1ProductDO = item1DO.getDataObject("product");
            DataObject item1ProductPrice1DO = item1ProductDO.getDataObject("price[1]");
            DataObject item1ProductPrice2DO = item1ProductDO.getDataObject("price[2]");

            DataObject item2ProductDO = item2DO.getDataObject("product");
            DataObject item2ProductPrice1DO = item2ProductDO.getDataObject("price[1]");
            DataObject item2ProductPrice2DO = item2ProductDO.getDataObject("price[2]");

            cs.beginLogging();
            itemsDO.detach();

            assertNotNull(item1DO);
            assertNotNull(item2DO);

            assertModified(rootObject, cs);
            assertDetached(itemsDO, cs);
            assertDetached(item1DO, cs, false); // internal children of a detach have a non-null container
            assertDetached(item2DO, cs, false); // internal children of a detach have a non-null container
            assertDetached(item1ProductDO, cs, false); // internal children of a detach have a non-null container
            assertDetached(item1ProductPrice1DO, cs, false); // internal children of a detach have a non-null container
            assertDetached(item1ProductPrice2DO, cs, false); // internal children of a detach have a non-null container
            assertDetached(item2ProductDO, cs, false); // internal children of a detach have a non-null container
            assertDetached(item2ProductPrice1DO, cs, false); // internal children of a detach have a non-null container
            assertDetached(item2ProductPrice2DO, cs, false); // internal children of a detach have a non-null container
            assertEquals(10, cs.getChangedDataObjects().size());

            assertEquals(14, ((SDOChangeSummary)cs).getOldContainer().size());//  from 9

            assertEquals(14, ((SDOChangeSummary)cs).getOldContainmentProperty().size());// from 9
            assertEquals(10, ((SDOChangeSummary)cs).getOldSettings().size());

            // verify keys of oldSettings Map
            //checkOldSettingsSizeTree("0000", changeSummaryB, root, dataObjectB, dataObjectC, dataObjectD);
        }

        // test unset of single property containing an isMany=true set of DataObjects
        public void testUnsetItems() {
            DataObject itemsDO = rootObject.getDataObject("items");
            DataObject item1DO = rootObject.getDataObject("items/item[1]");
            DataObject item2DO = rootObject.getDataObject("items/item[2]");

            DataObject item1ProductDO = item1DO.getDataObject("product");
            DataObject item1ProductPrice1DO = item1ProductDO.getDataObject("price[1]");
            DataObject item1ProductPrice2DO = item1ProductDO.getDataObject("price[2]");

            DataObject item2ProductDO = item2DO.getDataObject("product");
            DataObject item2ProductPrice1DO = item2ProductDO.getDataObject("price[1]");
            DataObject item2ProductPrice2DO = item2ProductDO.getDataObject("price[2]");
            Property containmentProp = itemsDO.getContainmentProperty();

            cs.beginLogging();
            rootObject.unset(containmentProp);

    //        verifyShipToDetachedOrUnset(shipToDO, yardDO,//
    //                phoneList, phone1, phone2, containmentProp, oldStreet);

            assertNotNull(item1DO);
            assertNotNull(item2DO);

            assertModified(rootObject, cs);
            assertDetached(itemsDO, cs);
            assertDetached(item1DO, cs, false); // internal children of a detach have a non-null container
            assertDetached(item2DO, cs, false); // internal children of a detach have a non-null container
            assertDetached(item1ProductDO, cs, false); // internal children of a detach have a non-null container
            assertDetached(item1ProductPrice1DO, cs, false); // internal children of a detach have a non-null container
            assertDetached(item1ProductPrice2DO, cs, false); // internal children of a detach have a non-null container
            assertDetached(item2ProductDO, cs, false); // internal children of a detach have a non-null container
            assertDetached(item2ProductPrice1DO, cs, false); // internal children of a detach have a non-null container
            assertDetached(item2ProductPrice2DO, cs, false); // internal children of a detach have a non-null container
            assertEquals(10, cs.getChangedDataObjects().size());

            assertEquals(14, ((SDOChangeSummary)cs).getOldContainer().size());//  from 9

            assertEquals(14, ((SDOChangeSummary)cs).getOldContainmentProperty().size());// from 9
            assertEquals(10, ((SDOChangeSummary)cs).getOldSettings().size());

            // verify keys of oldSettings Map
            //checkOldSettingsSizeTree("0000", changeSummaryB, root, dataObjectB, dataObjectC, dataObjectD);
        }

        // Test Scenarios:
        // - many delete
        public void testDeleteItem2Price1() {
            DataObject itemsDO = rootObject.getDataObject("items");
            DataObject item1DO = rootObject.getDataObject("items/item[1]");
            DataObject item2DO = rootObject.getDataObject("items/item[2]");

            DataObject item1ProductDO = item1DO.getDataObject("product");
            DataObject item1ProductPrice1DO = item1ProductDO.getDataObject("price[1]");
            DataObject item1ProductPrice2DO = item1ProductDO.getDataObject("price[2]");

            DataObject item2ProductDO = item2DO.getDataObject("product");
            DataObject item2ProductPrice1DO = item2ProductDO.getDataObject("price[1]");
            DataObject item2ProductPrice2DO = item2ProductDO.getDataObject("price[2]");

            cs.beginLogging();
            item2ProductPrice1DO.delete();

            assertModified(item2ProductDO, cs);
            assertDeleted(item2ProductPrice1DO, cs);

            assertEquals(2, cs.getChangedDataObjects().size());

            assertEquals(14, ((SDOChangeSummary)cs).getOldContainer().size());
            assertEquals(14, ((SDOChangeSummary)cs).getOldContainmentProperty().size());
            assertEquals(2, ((SDOChangeSummary)cs).getOldSettings().size());

            ChangeSummary.Setting containmentSetting = (ChangeSummary.Setting)cs.getOldValues(item2ProductDO).get(0);
            assertEquals("price", containmentSetting.getProperty().getName());
            assertTrue(containmentSetting.getValue() instanceof List);
            assertEquals(2, ((List)containmentSetting.getValue()).size());
            assertEquals(true, containmentSetting.isSet());
        }

        public void testDetachItem2Price1() {
            DataObject itemsDO = rootObject.getDataObject("items");
            DataObject item1DO = rootObject.getDataObject("items/item[1]");
            DataObject item2DO = rootObject.getDataObject("items/item[2]");

            DataObject item1ProductDO = item1DO.getDataObject("product");
            DataObject item1ProductPrice1DO = item1ProductDO.getDataObject("price[1]");
            DataObject item1ProductPrice2DO = item1ProductDO.getDataObject("price[2]");

            DataObject item2ProductDO = item2DO.getDataObject("product");
            DataObject item2ProductPrice1DO = item2ProductDO.getDataObject("price[1]");
            DataObject item2ProductPrice2DO = item2ProductDO.getDataObject("price[2]");

            cs.beginLogging();
            item2ProductPrice1DO.detach();

            assertModified(item2ProductDO, cs);
            assertDetached(item2ProductPrice1DO, cs);

            assertEquals(2, cs.getChangedDataObjects().size());

            assertEquals(14, ((SDOChangeSummary)cs).getOldContainer().size());
            assertEquals(14, ((SDOChangeSummary)cs).getOldContainmentProperty().size());
            assertEquals(2, ((SDOChangeSummary)cs).getOldSettings().size());

            ChangeSummary.Setting containmentSetting = (ChangeSummary.Setting)cs.getOldValues(item2ProductDO).get(0);
            assertEquals("price", containmentSetting.getProperty().getName());
            assertTrue(containmentSetting.getValue() instanceof List);
            assertEquals(2, ((List)containmentSetting.getValue()).size());
            assertEquals(true, containmentSetting.isSet());
        }

        // Note: unset of a single item in a list is different than a detach in that all the items under
        // property being unset are detached.
        public void testUnsetItem2Price() {
            DataObject itemsDO = rootObject.getDataObject("items");
            DataObject item1DO = rootObject.getDataObject("items/item[1]");
            DataObject item2DO = rootObject.getDataObject("items/item[2]");

            DataObject item1ProductDO = item1DO.getDataObject("product");
            DataObject item1ProductPrice1DO = item1ProductDO.getDataObject("price[1]");
            DataObject item1ProductPrice2DO = item1ProductDO.getDataObject("price[2]");

            DataObject item2ProductDO = item2DO.getDataObject("product");
            DataObject item2ProductPrice1DO = item2ProductDO.getDataObject("price[1]");
            DataObject item2ProductPrice2DO = item2ProductDO.getDataObject("price[2]");
            Property containmentProp = item2ProductPrice1DO.getContainmentProperty();

            cs.beginLogging();
            // test isMany section of unset() - all items including price1 and price2 are unset
            item2ProductDO.unset(containmentProp);

            assertModified(item2ProductDO, cs);
            assertDetached(item2ProductPrice1DO, cs);
            assertDetached(item2ProductPrice2DO, cs);

            assertEquals(3, cs.getChangedDataObjects().size());

            assertEquals(14, ((SDOChangeSummary)cs).getOldContainer().size());
            assertEquals(14, ((SDOChangeSummary)cs).getOldContainmentProperty().size());
            assertEquals(3, ((SDOChangeSummary)cs).getOldSettings().size());

            ChangeSummary.Setting containmentSetting = (ChangeSummary.Setting)cs.getOldValues(item2ProductDO).get(0);
            assertEquals("price", containmentSetting.getProperty().getName());
            assertTrue(containmentSetting.getValue() instanceof List);
            assertEquals(2, ((List)containmentSetting.getValue()).size());
            assertEquals(true, containmentSetting.isSet());
        }

        // unset a single item in a list
        // TODO: this test will fail as of 20070111 until bug#5757236 is fixed
        public void testUnsetItem2Price2() {
            DataObject item2DO = rootObject.getDataObject("items/item[2]");

            DataObject item2ProductDO = item2DO.getDataObject("product");
            DataObject item2ProductPrice1DO = item2ProductDO.getDataObject("price[1]");
            DataObject item2ProductPrice2DO = item2ProductDO.getDataObject("price[2]");

            cs.beginLogging();
            // test isMany section of unset() - all items including price1 and price2 are unset

            item2ProductDO.unset("price[2]");

            assertModified(item2ProductDO, cs);
            assertDetached(item2ProductPrice1DO, cs);
            assertUnchanged(item2ProductPrice2DO, cs); // TODO: failure starts here

            assertEquals(2, cs.getChangedDataObjects().size());

            assertEquals(14, ((SDOChangeSummary)cs).getOldContainer().size());
            assertEquals(14, ((SDOChangeSummary)cs).getOldContainmentProperty().size());
            assertEquals(2, ((SDOChangeSummary)cs).getOldSettings().size());

            ChangeSummary.Setting containmentSetting = (ChangeSummary.Setting)cs.getOldValues(item2ProductDO).get(0);
            assertEquals("price", containmentSetting.getProperty().getName());
            assertTrue(containmentSetting.getValue() instanceof List);
            assertEquals(2, ((List)containmentSetting.getValue()).size());
            assertEquals(true, containmentSetting.isSet());
        }

        public void testDetachAndResetYardToSamePlace() {
            DataObject shipToDO = rootObject.getDataObject("shipTo");
            Property containmentProp = shipToDO.getInstanceProperty("yard");

            DataObject yardDO = shipToDO.getDataObject("yard");

            Object length = yardDO.get("length");

            cs.beginLogging();
            yardDO.detach();

            List yardDOSettings = cs.getOldValues(yardDO);
            assertEquals(3, yardDOSettings.size());

            assertDetached(yardDO, cs);
            assertModified(shipToDO, cs);
            assertFalse(cs.isModified(yardDO));

            assertEquals(2, cs.getChangedDataObjects().size());

            shipToDO.set("yard", yardDO);

            assertFalse(cs.isDeleted(yardDO));
            //assertFalse(cs.isModified(shipToDO)); //if we add new logic
            assertTrue(cs.isModified(shipToDO));

            assertFalse(cs.isModified(yardDO));

            //assertEquals(0, cs.getChangedDataObjects().size());  //if we add new logic
            assertEquals(1, cs.getChangedDataObjects().size());//just shipTo

            assertEquals(14, ((SDOChangeSummary)cs).getOldContainer().size());
            assertEquals(14, ((SDOChangeSummary)cs).getOldContainmentProperty().size());

            //assertEquals(0, ((SDOChangeSummary)cs).getOldSettings().size()); // if we add new logic
            assertEquals(2, ((SDOChangeSummary)cs).getOldSettings().size());//

            List shipToSettings = cs.getOldValues(shipToDO);
            assertEquals(1, shipToSettings.size());

            yardDOSettings = cs.getOldValues(yardDO);
            // see cs.getOldValues() does not return and empty List when yardDO is !modified and !deleted (but !created as well) - (re)set
            assertEquals(3, yardDOSettings.size());

            // see cs.getOldValue() does not return null when yardDO is !modified and !deleted (but !created as well) - (re)set
            ChangeSummary.Setting lengthSetting = cs.getOldValue(yardDO, yardDO.getInstanceProperty("length"));
            assertNotNull(lengthSetting);
            assertEquals(length, lengthSetting.getValue());

        }

        // Verify that an unset followed by a (re)set of the same subtree returns tree to original state
        public void testDetachAndReSetPrice1BackToSameIndex() {
            DataObject itemsDO = rootObject.getDataObject("items");
            DataObject item1DO = rootObject.getDataObject("items/item[1]");
            DataObject item2DO = rootObject.getDataObject("items/item[2]");

            DataObject item1ProductDO = item1DO.getDataObject("product");
            DataObject item1ProductPrice1DO = item1ProductDO.getDataObject("price[1]");
            DataObject item1ProductPrice2DO = item1ProductDO.getDataObject("price[2]");

            DataObject item2ProductDO = item2DO.getDataObject("product");
            DataObject item2ProductPrice1DO = item2ProductDO.getDataObject("price[1]");
            DataObject item2ProductPrice2DO = item2ProductDO.getDataObject("price[2]");

            cs.beginLogging();
            item2ProductPrice1DO.detach();

            assertModified(item2ProductDO, cs);
            assertDetached(item2ProductPrice1DO, cs);

            assertEquals(2, cs.getChangedDataObjects().size());

            assertEquals(14, ((SDOChangeSummary)cs).getOldContainer().size());
            assertEquals(14, ((SDOChangeSummary)cs).getOldContainmentProperty().size());
            assertEquals(2, ((SDOChangeSummary)cs).getOldSettings().size());

            ChangeSummary.Setting containmentSetting = (ChangeSummary.Setting)cs.getOldValues(item2ProductDO).get(0);
            assertEquals("price", containmentSetting.getProperty().getName());
            assertTrue(containmentSetting.getValue() instanceof List);
            assertEquals(2, ((List)containmentSetting.getValue()).size());
            assertEquals(true, containmentSetting.isSet());

            // UNDO - reattach subtree
            ((ListWrapper)item2ProductDO.get("price")).add(0, item2ProductPrice1DO);

            assertFalse(cs.isDeleted(item2ProductPrice1DO));
            //assertUnchanged(cs.isModified(item2ProductDO)); //if we add new logic
            assertModified(item2ProductDO, cs);

            assertFalse(cs.isModified(item2ProductPrice1DO));

            //assertEquals(0, cs.getChangedDataObjects().size());  //if we add new logic
            assertEquals(1, cs.getChangedDataObjects().size());

            assertEquals(14, ((SDOChangeSummary)cs).getOldContainer().size());
            assertEquals(14, ((SDOChangeSummary)cs).getOldContainmentProperty().size());
            //assertEquals(0, ((SDOChangeSummary)cs).getOldSettings().size()); // if we add new logic
            assertEquals(2, ((SDOChangeSummary)cs).getOldSettings().size());
        }

        // Verify that an unset followed by a set (swap) at a different position does not return the tree to the original state
        // price[1] becomes price[2], and price[2] becomes price[1]
        public void testSwapUsingDetachAndSetPrice1ToLastIndex2() {
            DataObject itemsDO = rootObject.getDataObject("items");
            DataObject item1DO = rootObject.getDataObject("items/item[1]");
            DataObject item2DO = rootObject.getDataObject("items/item[2]");

            DataObject item1ProductDO = item1DO.getDataObject("product");
            DataObject item1ProductPrice1DO = item1ProductDO.getDataObject("price[1]");
            DataObject item1ProductPrice2DO = item1ProductDO.getDataObject("price[2]");

            DataObject item2ProductDO = item2DO.getDataObject("product");
            DataObject item2ProductPrice1DO = item2ProductDO.getDataObject("price[1]");
            DataObject item2ProductPrice2DO = item2ProductDO.getDataObject("price[2]");

            cs.beginLogging();
            item2ProductPrice1DO.detach();

            assertModified(item2ProductDO, cs);
            assertDetached(item2ProductPrice1DO, cs);

            assertEquals(2, cs.getChangedDataObjects().size());

            assertEquals(14, ((SDOChangeSummary)cs).getOldContainer().size());
            assertEquals(14, ((SDOChangeSummary)cs).getOldContainmentProperty().size());
            assertEquals(2, ((SDOChangeSummary)cs).getOldSettings().size());

            ChangeSummary.Setting containmentSetting = (ChangeSummary.Setting)cs.getOldValues(item2ProductDO).get(0);
            assertEquals("price", containmentSetting.getProperty().getName());
            assertTrue(containmentSetting.getValue() instanceof List);
            assertEquals(2, ((List)containmentSetting.getValue()).size());
            assertEquals(true, containmentSetting.isSet());

            // Swap/move - reattach subtree in new positon
            ((ListWrapper)item2ProductDO.get("price")).add(//
            1,//
            item2ProductPrice1DO);

            // verify that container does not reference itself
            assertNotSame(item2ProductPrice1DO, item2ProductPrice1DO.getContainer());
            assertModified(item2ProductDO, cs);
            // TODO: fails
            //assertModified(item2ProductPrice1DO, cs);// assertDeleted
            assertFalse(cs.isModified(item2ProductPrice1DO));

            //assertEquals(0, cs.getChangedDataObjects().size());  //if we add new logic
            assertEquals(1, cs.getChangedDataObjects().size());

            assertEquals(14, ((SDOChangeSummary)cs).getOldContainer().size());
            assertEquals(14, ((SDOChangeSummary)cs).getOldContainmentProperty().size());
            assertEquals(2, ((SDOChangeSummary)cs).getOldSettings().size());
        }

        // test deletion inside a list - see detach() isMany section
        // Test Scenarios:
        // - many delete
        public void testDeleteMultipleInList() {
            cs.endLogging();
            DataObject itemsDO = rootObject.getDataObject("items");
            DataObject item1DO = rootObject.getDataObject("items/item[1]");
            DataObject item3DO = dataFactory.create(item1DO.getType());
            DataObject item4DO = dataFactory.create(item1DO.getType());
            DataObject item5DO = dataFactory.create(item1DO.getType());
            DataObject item6DO = dataFactory.create(item1DO.getType());
            itemsDO.getList("item").add(item3DO);
            itemsDO.getList("item").add(item4DO);
            itemsDO.getList("item").add(item5DO);
            itemsDO.getList("item").add(item6DO);
            cs.beginLogging();
            item3DO.delete();
            item5DO.delete();
            assertDeleted(item3DO, cs);// 5/2
            assertDeleted(item5DO, cs);// 5/2
            assertModified(itemsDO, cs);

            assertEquals(3, cs.getChangedDataObjects().size());
            assertEquals(18, ((SDOChangeSummary)cs).getOldContainer().size());// 14 + 4 new objects
            assertEquals(18, ((SDOChangeSummary)cs).getOldContainmentProperty().size());// 14 + 4 new objects
            assertEquals(3, ((SDOChangeSummary)cs).getOldSettings().size());
            assertEquals(1, ((SDOChangeSummary)cs).getOldValues(itemsDO).size());// from 2 (only 1st change on items is recorded)
            assertEquals(5, ((SDOChangeSummary)cs).getOldValues(item3DO).size());// 1
            assertEquals(5, ((SDOChangeSummary)cs).getOldValues(item5DO).size());// 1
            ChangeSummary.Setting containmentSetting = (ChangeSummary.Setting)cs.getOldValues(itemsDO).get(0);
            assertEquals("item", containmentSetting.getProperty().getName());
            assertTrue(containmentSetting.getValue() instanceof List);
            assertEquals(6, ((List)containmentSetting.getValue()).size());
            assertEquals(true, containmentSetting.isSet());// see detach(boolean)
        }

        // test deletion inside a list - see detach() isMany section
        // Test Scenarios:
        // - many delete
        public void testDeleteItem2() {
            DataObject itemsDO = rootObject.getDataObject("items");
            DataObject item1DO = rootObject.getDataObject("items/item[1]");
            DataObject item2DO = rootObject.getDataObject("items/item[2]");

            DataObject item2ProductDO = item2DO.getDataObject("product");
            DataObject item2ProductPrice1DO = item2ProductDO.getDataObject("price[1]");
            DataObject item2ProductPrice2DO = item2ProductDO.getDataObject("price[2]");

            cs.beginLogging();
            item2DO.delete();

            assertModified(itemsDO, cs);
            assertDeleted(item2DO, cs);
            assertDeleted(item2ProductDO, cs);
            assertDeleted(item2ProductPrice1DO, cs);
            assertDeleted(item2ProductPrice2DO, cs);

            assertEquals(5, cs.getChangedDataObjects().size());

            assertEquals(14, ((SDOChangeSummary)cs).getOldContainer().size());
            assertEquals(14, ((SDOChangeSummary)cs).getOldContainmentProperty().size());
            assertEquals(5, ((SDOChangeSummary)cs).getOldSettings().size());

            assertEquals(1, cs.getOldValues(itemsDO).size());
            ChangeSummary.Setting containmentSetting = (ChangeSummary.Setting)cs.getOldValues(itemsDO).get(0);
            assertEquals("item", containmentSetting.getProperty().getName());
            assertTrue(containmentSetting.getValue() instanceof List);
            assertEquals(2, ((List)containmentSetting.getValue()).size());
            assertEquals(true, containmentSetting.isSet());
        }

        public void testMoveYardWithSet() {
            DataObject shipToDO = rootObject.getDataObject("shipTo");
            Property containmentProp = shipToDO.getInstanceProperty("yard");

            DataObject billToDO = rootObject.getDataObject("billTo");
            DataObject yardDO = shipToDO.getDataObject("yard");
            cs.beginLogging();
            billToDO.set("yard", yardDO);

            assertModified(shipToDO, cs);
            assertModified(billToDO, cs);
            assertFalse(cs.isDeleted(yardDO));
            assertEquals(2, cs.getChangedDataObjects().size());

            assertEquals(billToDO.getChangeSummary(),yardDO.getChangeSummary());

            assertEquals(14, ((SDOChangeSummary)cs).getOldContainer().size());
            assertEquals(14, ((SDOChangeSummary)cs).getOldContainmentProperty().size());
            // test detach from updateContainment
            assertEquals(3, ((SDOChangeSummary)cs).getOldSettings().size());
            List shipToSettings = cs.getOldValues(shipToDO);
            List billToSettings = cs.getOldValues(billToDO);
            assertEquals(1, billToSettings.size());
            assertEquals(1, shipToSettings.size());

            ChangeSummary.Setting billToSetting = (ChangeSummary.Setting)billToSettings.get(0);
            assertEquals("yard", billToSetting.getProperty().getName());
            assertEquals(null, billToSetting.getValue());

            //TODO: comment out this line when null policy stuff is working
            //assertEquals(false, billToSetting.isSet());
            ChangeSummary.Setting shipToSetting = (ChangeSummary.Setting)shipToSettings.get(0);
            assertEquals("yard", shipToSetting.getProperty().getName());
            assertEquals(yardDO, shipToSetting.getValue());
            assertEquals(true, shipToSetting.isSet());

            assertEquals(shipToDO, cs.getOldContainer(yardDO));
            assertEquals(containmentProp, cs.getOldContainmentProperty(yardDO));
        }

        public void testMoveYardWithDetachAndSet() {
            DataObject shipToDO = rootObject.getDataObject("shipTo");
            Property containmentProp = shipToDO.getInstanceProperty("yard");

            DataObject billToDO = rootObject.getDataObject("billTo");
            DataObject yardDO = shipToDO.getDataObject("yard");
            cs.beginLogging();
            yardDO.detach();
            assertTrue(cs.isDeleted(yardDO));
            billToDO.set("yard", yardDO);

            assertModified(shipToDO, cs);
            assertModified(billToDO, cs);
            assertFalse(cs.isDeleted(yardDO));
            assertEquals(2, cs.getChangedDataObjects().size());

            assertEquals(14, ((SDOChangeSummary)cs).getOldContainer().size());
            assertEquals(14, ((SDOChangeSummary)cs).getOldContainmentProperty().size());
            // TODO: VERIFY 2 -> 3
            assertEquals(3, ((SDOChangeSummary)cs).getOldSettings().size());
            List shipToSettings = cs.getOldValues(shipToDO);
            List billToSettings = cs.getOldValues(billToDO);
            assertEquals(1, billToSettings.size());
            assertEquals(1, shipToSettings.size());

            ChangeSummary.Setting billToSetting = (ChangeSummary.Setting)billToSettings.get(0);
            assertEquals("yard", billToSetting.getProperty().getName());
            assertEquals(null, billToSetting.getValue());

            //TODO: comment out this line when null policy stuff is working
            //assertEquals(false, billToSetting.isSet());
            ChangeSummary.Setting shipToSetting = (ChangeSummary.Setting)shipToSettings.get(0);
            assertEquals("yard", shipToSetting.getProperty().getName());
            assertEquals(yardDO, shipToSetting.getValue());
            assertEquals(true, shipToSetting.isSet());

            assertEquals(shipToDO, cs.getOldContainer(yardDO));
            assertEquals(containmentProp, cs.getOldContainmentProperty(yardDO));
        }

        public void testDeleteYard() {
            DataObject shipToDO = rootObject.getDataObject("shipTo");
            DataObject yardDO = shipToDO.getDataObject("yard");
            cs.beginLogging();
            yardDO.delete();

            assertYardDeleted(shipToDO, yardDO, cs, true);
            assertModified(shipToDO, cs);

            assertEquals(2, cs.getChangedDataObjects().size());

            assertEquals(1, cs.getOldValues(shipToDO).size());
            ChangeSummary.Setting shipToSetting = (ChangeSummary.Setting)cs.getOldValues(shipToDO).get(0);
            assertEquals("yard", shipToSetting.getProperty().getName());
            assertEquals(true, shipToSetting.isSet());
            assertEquals(yardDO, shipToSetting.getValue());

            assertEquals(14, ((SDOChangeSummary)cs).getOldContainer().size());
            assertEquals(14, ((SDOChangeSummary)cs).getOldContainmentProperty().size());

            assertEquals(shipToDO, cs.getOldContainer(yardDO));
        }

        // test deletion inside a list - see detach() isMany section
        // Test Scenarios:
        // - many delete
        public void testDeletePhone2() {
            DataObject shipToDO = rootObject.getDataObject("shipTo");
            DataObject phoneDO = (DataObject)shipToDO.getDataObject("phone[2]");

            cs.beginLogging();
            phoneDO.delete();

            assertDeleted(phoneDO, cs);
            assertModified(shipToDO, cs);

            assertEquals(2, cs.getChangedDataObjects().size());
            assertEquals(1, cs.getOldValues(phoneDO).size());
            assertEquals(1, cs.getOldValues(shipToDO).size());

            ChangeSummary.Setting phoneSetting = (ChangeSummary.Setting)cs.getOldValues(shipToDO).get(0);
            assertEquals("phone", phoneSetting.getProperty().getName());
            assertEquals(true, phoneSetting.isSet());
            assertTrue(phoneSetting.getValue() instanceof List);
            assertEquals(2, ((List)phoneSetting.getValue()).size());
            assertTrue(((List)phoneSetting.getValue()).contains(phoneDO));

            assertEquals(14, ((SDOChangeSummary)cs).getOldContainer().size());
            assertEquals(14, ((SDOChangeSummary)cs).getOldContainmentProperty().size());

            assertEquals(shipToDO, cs.getOldContainer(phoneDO));
        }

    */
    public void assertCSonRootUnsetSimpleManyAtRootAfterSetNull(boolean testLoadSave) {//testUnsetComments() {
        assertUnchanged(rootObject, cs);
        assertEquals(0, cs.getChangedDataObjects().size());

        List oldValues = cs.getOldValues(rootObject);
        assertEquals(0, oldValues.size());
    }

    public void assertCSonRootUnsetSimpleManyAtRootAfterUnset(boolean testLoadSave) {//testUnsetComments() {
        assertModified(rootObject, cs);
        assertEquals(1, cs.getChangedDataObjects().size());

        List oldValues = cs.getOldValues(rootObject);
        assertEquals(1, oldValues.size());

        ChangeSummary.Setting theSetting = (ChangeSummary.Setting)oldValues.get(0);
        assertEquals("comment", theSetting.getProperty().getName());
        assertEquals(true, theSetting.isSet());
        assertEquals(null, theSetting.getValue());
    }

    public void assertCSonRootUnsetSimpleSingleAtRootAfterSetNull(boolean testLoadSave) {//testUnsetSimpleIDOnRoot() {
        assertModified(rootObject, cs);
        assertEquals(1, cs.getChangedDataObjects().size());

        List oldValues = cs.getOldValues(rootObject);
        assertEquals(1, oldValues.size());

        ChangeSummary.Setting theSetting = (ChangeSummary.Setting)oldValues.get(0);
        assertEquals("poId", theSetting.getProperty().getName());
        assertEquals(true, theSetting.isSet());
        assertEquals(null, theSetting.getValue());
    }

    public void assertCSonRootUnsetSimpleSingleAtRootAfterUnset(boolean testLoadSave) {//testUnsetNameAfterLoggingOnAfterUnset() {
        assertUnchanged(rootObject, cs);//
        assertEquals(0, cs.getChangedDataObjects().size());

        List oldValues = cs.getOldValues(rootObject);
        assertEquals(0, oldValues.size());
    }

    /*
            // test that we do not loose containment in the oldSetting - by unwrapping the ListWrapper into a List
            public void testUnsetFilledSimpleListOnRootRetainsContainmentInOldSetting() {
                List comments = new ArrayList();
                comments.add("comment1");
                comments.add("comment2");
                rootObject.set("comment", comments);
                cs.beginLogging();
                rootObject.unset("comment");

                assertModified(rootObject, cs);
                assertEquals(1, cs.getChangedDataObjects().size());

                List oldValues = cs.getOldValues(rootObject);
                assertEquals(1, oldValues.size());

                ChangeSummary.Setting theSetting = (ChangeSummary.Setting)oldValues.get(0);
                assertEquals("comment", theSetting.getProperty().getName());
                assertEquals(true, theSetting.isSet());
                assertEquals(comments.size(), ((List)theSetting.getValue()).size());
            }

            public void testSetCommentToSameValue() {
                rootObject.set("poId", "123");
                cs.beginLogging();
                rootObject.set("poId", "123");

                assertUnchanged(rootObject, cs);
                assertEquals(0, cs.getChangedDataObjects().size());

                List oldValues = cs.getOldValues(rootObject);
                assertEquals(0, oldValues.size());
            }
        */
    public void assertCSonRootDetachComplexSingleAtRoot(boolean testLoadSave, DataObject shipToDO) {// detachShipTo() {
        //DataObject shipToDO = rootObject.getDataObject("shipTo");
        DataObject yardDO = shipToDO.getDataObject("yard");
        List phoneList = shipToDO.getList("phone");
        DataObject phone1 = (DataObject)phoneList.get(0);
        DataObject phone2 = (DataObject)phoneList.get(1);
        Property containmentProp = shipToDO.getContainmentProperty();

        Object oldStreet = shipToDO.get("street");

        cs.beginLogging();
        shipToDO.detach();
        assertNull(shipToDO.getChangeSummary());
        assertNull(yardDO.getChangeSummary());
        verifyCSonRootDetachUnsetComplexSingleAtRoot(testLoadSave, shipToDO, yardDO,//
                                                     phoneList, phone1, phone2, containmentProp, oldStreet);
    }

    public void verifyCSonRootDetachUnsetComplexSingleAtRoot//testDeleteShipTo()
    (boolean testLoadSave, DataObject shipToDO, Property containmentProp, DataObject yardDO, DataObject phone1, DataObject phone2) {
        //DataObject shipToDO = rootObject.getDataObject("shipTo");
        //DataObject yardDO = shipToDO.getDataObject("yard");
        //List phoneList = shipToDO.getList("phone");
        //DataObject phone1 = (DataObject)phoneList.get(0);
        //DataObject phone2 = (DataObject)phoneList.get(1);
        //Property containmentProp = shipToDO.getContainmentProperty();
        assertEquals(14, ((SDOChangeSummary)cs).getOldContainer().size());
        assertEquals(14, ((SDOChangeSummary)cs).getOldContainmentProperty().size());

        if (!testLoadSave) {
            // for dataType verify copy of shipTo has a set child in deleted list and current value is unset - reverse for unset/detach
            assertNotNull(shipToDO.get("street"));
            DataObject oldContainer = ((SDOChangeSummary)cs).getOldContainer(shipToDO);
            Property shipToProp = oldContainer.getInstanceProperty("shipTo");
            ChangeSummary.Setting oldSetting = cs.getOldValue(oldContainer, shipToProp);
            DataObject deepCopyShipTo = (DataObject)oldSetting.getValue();

            assertEquals("123 Maple Street", deepCopyShipTo.get("street"));
            assertModified(rootObject, cs);
            assertNotNull(cs.getOldContainmentProperty(shipToDO));
            assertNotNull(cs.getOldContainer(shipToDO));
            assertEquals(rootObject, cs.getOldContainer(shipToDO));

            assertEquals(shipToDO, cs.getOldContainer(yardDO));
            assertEquals(shipToDO, cs.getOldContainer(phone1));
            assertEquals(shipToDO, cs.getOldContainer(phone2));

            assertEquals(containmentProp, cs.getOldContainmentProperty(shipToDO));
            assertDetached(shipToDO, cs, false);
            //assertDeleted(yardDO, cs);
            assertYardDetached(shipToDO, yardDO, cs, false);
            assertDetached(phone1, cs, false);
            assertDetached(phone2, cs, false);
        }
        assertEquals(5, cs.getChangedDataObjects().size());
        //assertEquals(1, cs.getOldValues(rootObject).size());
        ChangeSummary.Setting setting = (ChangeSummary.Setting)cs.getOldValues(rootObject).get(0);
        if (!testLoadSave) {
            assertEquals(containmentProp, setting.getProperty());
            DataObject deepCopyShipTo = (DataObject)((SDOChangeSummary)cs).getDeepCopies().get(shipToDO);
            assertEquals(deepCopyShipTo, setting.getValue());
            assertEquals(true, setting.isSet());
            assertEquals(8, cs.getOldValues(shipToDO).size());
            assertEquals(1, cs.getOldValues(phone1).size());
            assertEquals(1, cs.getOldValues(phone2).size());
            ChangeSummary.Setting phone2setting = (ChangeSummary.Setting)cs.getOldValues(phone2).get(0);
            assertEquals("number", phone2setting.getProperty().getName());
            assertEquals("2345678", phone2setting.getValue());
            assertEquals(true, phone2setting.isSet());
        }
    }

    public void assertCSonRootUnsetComplexSingleAtRoot(boolean testLoadSave, DataObject shipToDO) {// unsetShipTo() {
        DataObject yardDO = shipToDO.getDataObject("yard");
        List phoneList = shipToDO.getList("phone");
        DataObject phone1 = (DataObject)phoneList.get(0);
        DataObject phone2 = (DataObject)phoneList.get(1);
        Property containmentProp = shipToDO.getContainmentProperty();

        Object oldStreet = shipToDO.get("street");

        cs.beginLogging();
        rootObject.unset(containmentProp);

        verifyCSonRootDetachUnsetComplexSingleAtRoot(testLoadSave, shipToDO, yardDO,//
                                                     phoneList, phone1, phone2, containmentProp, oldStreet);
    }

    public void verifyCSonRootDetachUnsetComplexSingleAtRoot(//
    boolean testLoadSave,//
    DataObject shipToDO,//
    DataObject yardDO,//
    List phoneList,//
    DataObject phone1,//
    DataObject phone2,//
    Property containmentProp,//
    Object oldStreet) {
        // verify containers are not unset for children (oldSetting is same object as original - inside List)
        //assertNotNull(((DataObject)shipToDO.getList("phone").get(0)).getContainer());
        //assertNotNull(phone1.getContainer());
        //assertEquals(phone1.getContainer(), shipToDO);
        assertEquals(14, ((SDOChangeSummary)cs).getOldContainer().size());
        assertEquals(14, ((SDOChangeSummary)cs).getOldContainmentProperty().size());

        assertEquals("123 Maple Street", shipToDO.get("street"));
        DataObject deepCopyShipTo = (DataObject)((SDOChangeSummary)cs).getDeepCopies().get(shipToDO);
        assertEquals("123 Maple Street", deepCopyShipTo.get("street"));

        if (!testLoadSave) {
            assertModified(rootObject, cs);
            assertNotNull(cs.getOldContainmentProperty(shipToDO));
            assertNotNull(cs.getOldContainer(shipToDO));
            assertEquals(rootObject, cs.getOldContainer(shipToDO));
            assertEquals(containmentProp, cs.getOldContainmentProperty(shipToDO));
            assertDetached(shipToDO, cs);// 8/0
            assertYardDetached(shipToDO, yardDO, cs, false);//
            assertDetached(phone1, cs, false);
            assertDetached(phone2, cs, false);
        }

        assertEquals(5, cs.getChangedDataObjects().size());// 2
        assertEquals(1, cs.getOldValues(rootObject).size());
        ChangeSummary.Setting setting = (ChangeSummary.Setting)cs.getOldValues(rootObject).get(0);
        assertEquals(containmentProp, setting.getProperty());
        assertEquals(shipToDO, setting.getValue());
        assertEquals(true, setting.isSet());
        assertEquals(8, cs.getOldValues(shipToDO).size());// 0

        Object newStreet = shipToDO.get("street");

        // verify that children of detached objects are not unset
        assertNotNull(newStreet);
        assertEquals(oldStreet, newStreet);
        assertEquals(1, cs.getOldValues(phone1).size());// 0
        assertEquals(1, cs.getOldValues(phone2).size());// 0
        ChangeSummary.Setting phone2setting = (ChangeSummary.Setting)cs.getOldValues(phone2).get(0);
        assertEquals("number", phone2setting.getProperty().getName());
        assertEquals("2345678", phone2setting.getValue());
        assertEquals(true, phone2setting.isSet());
        //writeXML(rootObject);
    }

    public void assertYardDeleted(DataObject shipToDO, DataObject yardDO, ChangeSummary cs, boolean nullContainer) {
        //assertYardDetached(shipToDO, yardDO, cs, nullContainer);
        assertDeleted(yardDO, cs, nullContainer);
        assertEquals(3, cs.getOldValues(yardDO).size());
        Property sfProp = yardDO.getInstanceProperty("squarefootage");
        Property widthProp = yardDO.getInstanceProperty("width");
        Property lengthProp = yardDO.getInstanceProperty("length");
        ChangeSummary.Setting yardSFsetting = cs.getOldValue(yardDO, sfProp);
        assertEquals(yardSFsetting.getValue(), null);

        //TODO: uncomment this line.  Will fail unless Node Null policy stuff is fixed
        //assertEquals(false, yardSFsetting.isSet());
        ChangeSummary.Setting yardWidthsetting = cs.getOldValue(yardDO, widthProp);
        assertEquals("65", yardWidthsetting.getValue());
        assertEquals(true, yardWidthsetting.isSet());

        ChangeSummary.Setting yardLengththsetting = cs.getOldValue(yardDO, lengthProp);
        assertEquals("45", yardLengththsetting.getValue());
        assertEquals(true, yardLengththsetting.isSet());
        // shipToDO was not deleted, only check yardDO
        assertChildrenUnset(yardDO);
    }

    public void assertYardDetached(DataObject shipToDO, DataObject yardDO, ChangeSummary cs, boolean nullContainer) {
        assertDetached(yardDO, cs, nullContainer);
        assertEquals(3, cs.getOldValues(yardDO).size());
        Property sfProp = yardDO.getInstanceProperty("squarefootage");
        Property widthProp = yardDO.getInstanceProperty("width");
        Property lengthProp = yardDO.getInstanceProperty("length");
        ChangeSummary.Setting yardSFsetting = cs.getOldValue(yardDO, sfProp);
        assertEquals(yardSFsetting.getValue(), null);

        //TODO: uncomment this line.  Will fail unless Node Null policy stuff is fixed
        //assertEquals(false, yardSFsetting.isSet());
        ChangeSummary.Setting yardWidthsetting = cs.getOldValue(yardDO, widthProp);
        assertEquals("65", yardWidthsetting.getValue());
        assertEquals(true, yardWidthsetting.isSet());

        ChangeSummary.Setting yardLengththsetting = cs.getOldValue(yardDO, lengthProp);
        assertEquals("45", yardLengththsetting.getValue());
        assertEquals(true, yardLengththsetting.isSet());
    }
}
