387563: Generalize the "lock of new objects on commit" feature to the "lock/unlock objects on commit" 

Task-Url: https://bugs.eclipse.org/bugs/show_bug.cgi?id=387563
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_247141_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_247141_Test.java
index aa3f421..3f93fe1 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_247141_Test.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_247141_Test.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2012, 2015 Eike Stepper (Berlin, Germany) and others.
+ * Copyright (c) 2011, 2012 Eike Stepper (Berlin, Germany) and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -15,24 +15,26 @@
 import org.eclipse.emf.cdo.eresource.CDOResource;
 import org.eclipse.emf.cdo.session.CDOSession;
 import org.eclipse.emf.cdo.tests.AbstractCDOTest;
+import org.eclipse.emf.cdo.tests.model1.Company;
 import org.eclipse.emf.cdo.tests.model1.Customer;
 import org.eclipse.emf.cdo.transaction.CDOTransaction;
 import org.eclipse.emf.cdo.util.CDOLazyContentAdapter;
 import org.eclipse.emf.cdo.util.CDOUtil;
 import org.eclipse.emf.cdo.view.CDOView;
 
-import org.eclipse.emf.ecore.util.EContentAdapter;
+import org.eclipse.emf.common.notify.Adapter;
 
 /**
  * Create a lazy self-attaching adapter for CDOObject
  * <p>
  * See bug 247141
- * 
+ *
  * @author Victor Roldan Betancort
  */
 public class Bugzilla_247141_Test extends AbstractCDOTest
 {
-  public void testContentAdapterBehavior() throws Exception
+
+  public void testContentAdapterBehaviour() throws Exception
   {
     CDOID id1 = null;
     CDOID id2 = null;
@@ -69,7 +71,7 @@
     CDOSession session = openSession();
     CDOTransaction transaction = session.openTransaction();
     CDOResource resource1 = transaction.getResource(getResourcePath("/test1"));
-    EContentAdapter adapter = new CDOLazyContentAdapter();
+    Adapter.Internal adapter = new CDOLazyContentAdapter();
     resource1.eAdapters().add(adapter);
 
     CDOView view = resource1.cdoView();
@@ -99,7 +101,7 @@
     assertEquals(false, resource1.eAdapters().contains(adapter));
   }
 
-  public void testBehaviorOnUncommittedObjects() throws Exception
+  public void testBehaviourOnUncommittedObjects() throws Exception
   {
     CDOSession session = openSession();
     CDOTransaction transaction = session.openTransaction();
@@ -112,7 +114,7 @@
 
     transaction.commit();
 
-    EContentAdapter adapter = new CDOLazyContentAdapter();
+    Adapter.Internal adapter = new CDOLazyContentAdapter();
     resource1.eAdapters().add(adapter);
 
     // resource1 and all its loaded contents should have been adapted
@@ -140,4 +142,85 @@
     assertEquals(false, customer3.eAdapters().contains(adapter));
     assertEquals(false, customer4.eAdapters().contains(adapter));
   }
+
+  public void testContentAdapterBehaviourOnNonResourceRoot() throws Exception
+  {
+    CDOID id1 = null;
+    CDOID id1_1 = null;
+
+    CDOID id2 = null;
+    CDOID id3 = null;
+    CDOID id4 = null;
+
+    {
+      CDOSession session = openSession();
+      CDOTransaction transaction = session.openTransaction();
+      CDOResource resource1 = transaction.createResource(getResourcePath("/test1"));
+
+      // 2 level containment.
+      Company comp1 = getModel1Factory().createCompany();
+      Customer customer1 = getModel1Factory().createCustomer();
+      comp1.getCustomers().add(customer1);
+
+      Company comp2 = getModel1Factory().createCompany();
+
+      resource1.getContents().add(comp1);
+      resource1.getContents().add(comp2);
+
+      CDOResource resource2 = transaction.createResource(getResourcePath("/test2"));
+
+      Company comp3 = getModel1Factory().createCompany();
+      Company comp4 = getModel1Factory().createCompany();
+
+      resource2.getContents().add(comp3);
+      resource2.getContents().add(comp4);
+
+      transaction.commit();
+
+      id1 = CDOUtil.getCDOObject(comp1).cdoID();
+      id1_1 = CDOUtil.getCDOObject(customer1).cdoID();
+
+      id2 = CDOUtil.getCDOObject(comp2).cdoID();
+      id3 = CDOUtil.getCDOObject(comp3).cdoID();
+      id4 = CDOUtil.getCDOObject(comp4).cdoID();
+
+      transaction.close();
+      session.close();
+    }
+
+    CDOSession session = openSession();
+    CDOTransaction transaction = session.openTransaction();
+    CDOResource resource1 = transaction.getResource(getResourcePath("/test1"));
+    Adapter.Internal adapter = new CDOLazyContentAdapter();
+
+    CDOView view = resource1.cdoView();
+    CDOObject object1 = view.getObject(id1);
+    CDOObject object1_1 = view.getObject(id1_1);
+    CDOObject object2 = view.getObject(id2);
+
+    object1.eAdapters().add(adapter);
+
+    // object1 and all its loaded contents should have been adapted
+    assertEquals(true, object1.eAdapters().contains(adapter));
+    assertEquals(true, object1_1.eAdapters().contains(adapter));
+    assertEquals(false, object2.eAdapters().contains(adapter));
+
+    // res2 should NOT be adapted, as its not in the content tree of res1
+    CDOResource resource2 = transaction.getResource(getResourcePath("/test2"));
+    // neither should its children
+    CDOObject object3 = view.getObject(id3);
+    CDOObject object4 = view.getObject(id4);
+
+    assertEquals(false, resource2.eAdapters().contains(adapter));
+    assertEquals(false, object3.eAdapters().contains(adapter));
+    assertEquals(false, object4.eAdapters().contains(adapter));
+
+    // Removing adapter
+    adapter.unsetTarget(object1);
+
+    assertEquals(false, object1.eAdapters().contains(adapter));
+    assertEquals(false, object1_1.eAdapters().contains(adapter));
+    assertEquals(false, object2.eAdapters().contains(adapter));
+    assertEquals(false, resource1.eAdapters().contains(adapter));
+  }
 }
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CDOLazyContentAdapter.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CDOLazyContentAdapter.java
index 21ba71b..a216d54 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CDOLazyContentAdapter.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CDOLazyContentAdapter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2012, 2014 Eike Stepper (Berlin, Germany) and others.
+ * Copyright (c) 2011, 2012 Eike Stepper (Berlin, Germany) and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -15,9 +15,12 @@
 import org.eclipse.emf.cdo.view.CDOObjectHandler;
 import org.eclipse.emf.cdo.view.CDOView;
 
+import org.eclipse.emf.common.notify.Notification;
 import org.eclipse.emf.common.notify.Notifier;
+import org.eclipse.emf.common.notify.impl.AdapterImpl;
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
 import org.eclipse.emf.ecore.util.EContentAdapter;
 import org.eclipse.emf.ecore.util.EcoreUtil;
 import org.eclipse.emf.spi.cdo.FSMUtil;
@@ -34,37 +37,56 @@
  * @author Victor Roldan Betancort
  * @since 4.0
  */
-public class CDOLazyContentAdapter extends EContentAdapter
+public class CDOLazyContentAdapter extends AdapterImpl
 {
   private CDOObjectHandler handler = new CleanObjectHandler();
 
   private Set<WeakReference<CDOObject>> adaptedObjects = new HashSet<WeakReference<CDOObject>>();
 
-  /**
-   * The root object to be adapted.
-   */
-  private WeakReference<CDOObject> adaptedRoot;
+  private WeakReference<Notifier> adaptedRoot;
 
   @Override
+  public Notifier getTarget()
+  {
+    return null;
+  }
+
+  @Override
+  public void setTarget(Notifier target)
+  {
+    if (isConnectedObject(target))
+    {
+      if (adaptedRoot == null)
+      {
+        adaptedRoot = new WeakReference<Notifier>(CDOUtil.getCDOObject(target));
+      }
+
+      if (target instanceof Resource)
+      {
+        addCleanObjectHandler(target);
+      }
+    }
+  }
+
+  @Override
+  public void notifyChanged(Notification notification)
+  {
+  }
+
   protected void setTarget(EObject target)
   {
     if (isConnectedObject(target))
     {
       if (adaptedRoot == null)
       {
-        adaptedRoot = new WeakReference<CDOObject>(CDOUtil.getCDOObject(target));
+        adaptedRoot = new WeakReference<Notifier>(CDOUtil.getCDOObject(target));
       }
 
-      basicSetTarget(target);
       if (target instanceof Resource)
       {
         addCleanObjectHandler(target);
       }
     }
-    else
-    {
-      super.setTarget(target);
-    }
   }
 
   /**
@@ -72,11 +94,10 @@
    * loaded objects
    */
   @Override
-  protected void unsetTarget(EObject target)
+  public void unsetTarget(Notifier target)
   {
     if (isConnectedObject(target))
     {
-      basicUnsetTarget(target);
       if (target instanceof Resource)
       {
         InternalCDOView view = getCDOView(target);
@@ -97,10 +118,6 @@
         removeCleanObjectHandler(target);
       }
     }
-    else
-    {
-      super.unsetTarget(target);
-    }
   }
 
   private void addCleanObjectHandler(EObject target)
@@ -147,15 +164,17 @@
     }
   }
 
-  @Override
   protected void addAdapter(Notifier notifier)
   {
     if (isConnectedObject(notifier) && !isAlreadyAdapted(notifier))
     {
       adaptedObjects.add(new WeakReference<CDOObject>(CDOUtil.getCDOObject((EObject)notifier)));
     }
+  }
 
-    super.addAdapter(notifier);
+  protected void removeAdapter(Notifier notifier)
+  {
+    notifier.eAdapters().remove(this);
   }
 
   private boolean isAlreadyAdapted(Notifier notifier)
@@ -163,6 +182,49 @@
     return notifier.eAdapters().contains(this);
   }
 
+  /**
+   * Checks if the argument is contained in the object graph of the root element
+   */
+  private boolean isContained(CDOObject object)
+  {
+    if (adaptedRoot == null)
+    {
+      return false;
+    }
+
+    Notifier root = adaptedRoot.get();
+    return isContained(object, root);
+  }
+
+  private boolean isContained(CDOObject object, Notifier root)
+  {
+    if (object == null || root == null)
+    {
+      return false;
+    }
+
+    if (root instanceof Resource)
+    {
+      return root == (object instanceof Resource ? object : object.cdoResource());
+    }
+
+    if (root instanceof ResourceSet)
+    {
+      ResourceSet resourceSet = (ResourceSet)root;
+      for (Resource resource : resourceSet.getResources())
+      {
+        if (isContained(object, resource))
+        {
+          return true;
+        }
+      }
+
+      return false;
+    }
+
+    return EcoreUtil.isAncestor((EObject)root, object);
+  }
+
   private static InternalCDOView getCDOView(EObject target)
   {
     CDOObject object = CDOUtil.getCDOObject(target);
@@ -189,30 +251,6 @@
   }
 
   /**
-   * Checks if the argument is contained in the object graph of the root element
-   */
-  private boolean isContained(CDOObject object)
-  {
-    if (adaptedRoot == null)
-    {
-      return false;
-    }
-
-    CDOObject root = adaptedRoot.get();
-    if (object == null)
-    {
-      return false;
-    }
-
-    if (root instanceof Resource)
-    {
-      return root == (object instanceof Resource ? object : object.cdoResource());
-    }
-
-    return EcoreUtil.isAncestor(root, object);
-  }
-
-  /**
    * @author Victor Roldan Betancort
    */
   private final class CleanObjectHandler implements CDOObjectHandler