[CDO] Various improvements to CDOAwareModelSet, CDOSashModelProvider, and CDOContextStorageProvider

Change-Id: Ie8479055a6973f9ca47631adea1765044d2dc725
Signed-off-by: Eike Stepper <stepper@esc-net.de>
diff --git a/cdo/bundles/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/core/resource/CDOAwareModelSet.java b/cdo/bundles/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/core/resource/CDOAwareModelSet.java
index bfbda81..0dd6b93 100755
--- a/cdo/bundles/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/core/resource/CDOAwareModelSet.java
+++ b/cdo/bundles/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/core/resource/CDOAwareModelSet.java
@@ -21,7 +21,6 @@
 package org.eclipse.papyrus.cdo.core.resource;
 
 import java.io.IOException;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
@@ -38,10 +37,6 @@
 import org.eclipse.emf.cdo.dawn.gmf.util.DawnDiagramUpdater;
 import org.eclipse.emf.cdo.eresource.CDOResource;
 import org.eclipse.emf.cdo.eresource.CDOResourceNode;
-import org.eclipse.emf.cdo.explorer.CDOExplorerUtil;
-import org.eclipse.emf.cdo.explorer.checkouts.CDOCheckout;
-import org.eclipse.emf.cdo.explorer.checkouts.ResourceSetConfigurer;
-import org.eclipse.emf.cdo.internal.explorer.checkouts.CDOCheckoutViewProvider;
 import org.eclipse.emf.cdo.transaction.CDOTransaction;
 import org.eclipse.emf.cdo.util.CDOUtil;
 import org.eclipse.emf.cdo.util.CommitException;
@@ -49,6 +44,8 @@
 import org.eclipse.emf.cdo.view.CDOViewInvalidationEvent;
 import org.eclipse.emf.cdo.view.CDOViewSet;
 import org.eclipse.emf.common.notify.Adapter;
+import org.eclipse.emf.common.notify.Notification;
+import org.eclipse.emf.common.notify.impl.AdapterImpl;
 import org.eclipse.emf.common.util.EList;
 import org.eclipse.emf.common.util.URI;
 import org.eclipse.emf.ecore.EObject;
@@ -57,9 +54,10 @@
 import org.eclipse.emf.spi.cdo.FSMUtil;
 import org.eclipse.emf.transaction.TransactionalEditingDomain;
 import org.eclipse.gmf.runtime.notation.Diagram;
+import org.eclipse.net4j.util.collection.AbstractFilteredIterator;
+import org.eclipse.net4j.util.event.EventUtil;
 import org.eclipse.net4j.util.event.IEvent;
 import org.eclipse.net4j.util.event.IListener;
-import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
 import org.eclipse.papyrus.cdo.internal.core.CDOUtils;
 import org.eclipse.papyrus.cdo.internal.core.controlmode.CDOControlModeParticipant;
 import org.eclipse.papyrus.cdo.internal.core.controlmode.CDOProxyManager;
@@ -67,7 +65,6 @@
 import org.eclipse.papyrus.infra.core.resource.ModelMultiException;
 import org.eclipse.papyrus.infra.services.resourceloading.OnDemandLoadingModelSet;
 
-import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 
@@ -76,6 +73,9 @@
  */
 public class CDOAwareModelSet extends OnDemandLoadingModelSet {
 
+	@SuppressWarnings("restriction")
+	private static final String SCHEME = org.eclipse.emf.cdo.internal.explorer.checkouts.CDOCheckoutViewProvider.SCHEME;
+
 	private static final Set<CDOState> DIRTY_STATES = EnumSet.of(CDOState.NEW, CDOState.DIRTY, CDOState.CONFLICT, CDOState.INVALID_CONFLICT);
 
 	private final ThreadLocal<Boolean> inGetResource = new ThreadLocal<Boolean>();
@@ -83,21 +83,26 @@
 	private final CDOProxyManager proxyManager = new CDOProxyManager(this);
 
 	private final PapyrusCDOResourceFactory resourceFactory = new PapyrusCDOResourceFactory(this);
-	
-	private CDOCheckout checkout;
+
+	private final Adapter viewSetAdapter = new ViewSetAdapter();
+
+	private final IListener invalidationListener = new InvalidationListener();
 
 	private CDOView view;
 
-	private IListener invalidationListener;
+	private boolean unloading;
+
 
 	public CDOAwareModelSet() {
-		super();
-
 		this.resources = new SafeResourceList();
 
 		Map<String, Object> map = getResourceFactoryRegistry().getProtocolToFactoryMap();
 		map.put(CDOProtocolConstants.PROTOCOL_NAME, resourceFactory);
-		map.put(CDOCheckoutViewProvider.SCHEME, resourceFactory);
+		map.put(SCHEME, resourceFactory);
+	}
+
+	public final IListener getInvalidationListener() {
+		return invalidationListener;
 	}
 
 	@Override
@@ -110,7 +115,7 @@
 		if (CDOUtils.isCDOURI(uri)) {
 			return getCDOObject(uri, loadOnDemand);
 		}
-		
+
 		return super.getEObject(uri, loadOnDemand);
 	}
 
@@ -120,8 +125,8 @@
 			// instance that it needs (otherwise the 'non-null constraint' of
 			// all kinds of reference lists will be violated)
 			return proxyManager.getProxy(uri);
-		} 
-		
+		}
+
 		return super.getEObject(uri, loadOnDemand);
 	}
 
@@ -144,18 +149,13 @@
 	@Override
 	public Resource createResource(URI uri, String contentType) {
 		Resource resource = super.createResource(uri, contentType);
-		initTransaction(resource);
+		initView(resource);
 		return resource;
 	}
 
 	@Override
 	protected void demandLoad(Resource resource) throws IOException {
 		URI uri = resource.getURI();
-
-		// if (CDOCheckoutViewProvider.SCHEME.equals(uri.scheme())) {
-		//
-		// } else
-
 		if (CDOUtils.isCDOURI(uri)) {
 			// XML options not applicable to CDO resources
 			resource.load(null);
@@ -165,7 +165,7 @@
 			super.demandLoad(resource);
 		}
 
-		initTransaction(resource);
+		initView(resource);
 	}
 
 	@Override
@@ -217,130 +217,64 @@
 		}
 	}
 
-	protected void initTransaction(Resource resource) {
-		if (resource instanceof CDOResource) {
-			CDOResource cdoResource = (CDOResource) resource;
-			view = cdoResource.cdoView();
-
+	protected void initView(Resource resource) {
+		if (view == null && resource instanceof CDOResource) {
+			view = ((CDOResource) resource).cdoView();
 			if (view != null) {
-				final IListener invalidationListener = getInvalidationListener();
-				if (false == Arrays.asList(view.getListeners()).contains(invalidationListener)) {
-					view.addListener(getInvalidationListener());
-					checkout = CDOExplorerUtil.getCheckout(view);
-					ResourceSetConfigurer.Registry.INSTANCE.configureResourceSet(this, checkout);
+				CDOViewSet viewSet = view.getViewSet();
+
+				for (CDOView v : viewSet.getViews()) {
+					EventUtil.addUniqueListener(v, invalidationListener);
 				}
 
-				// URI from = URI.createURI("cdo://" + view.getSession().getRepositoryInfo().getName() + "/");
-				// URI to = URI.createURI("cdo.checkout://" + checkout.getID() + "/" + checkout.getRepository().getID() + "/");
-				// getURIConverter().getURIMap().put(from, to);
+				viewSet.eAdapters().add(viewSetAdapter);
+
+				// Don't clear adapters of the resource because ECrossReferenceAdapters would try to crawl the contents.
+				viewSet.setDefaultClearAdapterPolicy((object, adapter) -> !(object instanceof CDOResourceNode));
 			}
 		}
 	}
 
 	@Override
 	public void unload() {
-		boolean logError = true;
-		try {
-			logError = view == null || !view.isClosed();
+		boolean logError = view == null || !view.isClosed();
 
-			// CDOResources don't implement unload(), but we can remove adapters from
-			// all of the objects that we have loaded in this view
-			if (view != null && !view.isClosed()) {
-				CDOUtils.unload(view);
+		try {
+			if (view != null) {
+				CDOViewSet viewSet = view.getViewSet();
+				viewSet.eAdapters().remove(viewSetAdapter);
+
+				for (CDOView v : viewSet.getViews()) {
+					EventUtil.removeListener(v, invalidationListener);
+				}
+			}
+
+			unloading = true;
+			super.unload();
+		} catch (Exception ex) {
+			if (logError) {
+				Activator.log.error(ex);
 			}
 		} finally {
-			try {
-				super.unload();
-			} catch (Exception ex) {
-				if (logError) {
-					Activator.log.error(ex);
-				}
-			} finally {
-				LifecycleUtil.deactivate(view);
-				CDOCheckoutViewProvider.disposeResourceSet(this);
+			unloading = false;
 
-				// now, we can remove the CDOViewSet adapter
-				eAdapters().clear();
+			// Unload and remove the resources that were omitted in super.unload()
+			// because of CDOAwareModelSet.SafeResourceList.iterator().
+			EList<Resource> resources = getResources();
+			for (Resource resource : resources) {
+				resource.unload();
 			}
+
+			resources.clear();
 		}
 	}
 
-	protected final IListener getInvalidationListener() {
-		if (invalidationListener == null) {
-			invalidationListener = createInvalidationListener();
-		}
-		return invalidationListener;
-	}
-
-	protected IListener createInvalidationListener() {
-		return new InvalidationListener();
-	}
-
-	private class InvalidationListener implements IListener {
-		@Override
-		public void notifyEvent(IEvent event) {
-			if (event instanceof CDOViewInvalidationEvent) {
-				TransactionalEditingDomain domain = getTransactionalEditingDomain();
-				if (domain instanceof CDOAwareTransactionalEditingDomain) {
-					((CDOAwareTransactionalEditingDomain) domain).fireResourceSetChanged((CDOViewInvalidationEvent) event);
-				}
-			}
-		}
-	}
 
 	@Override
 	public boolean isUserModelResource(URI uri) {
 		return super.isUserModelResource(uri) || CDOUtils.isCDOURI(uri);
 	}
 
-	@Override
-	public EList<Adapter> eAdapters() {
-		if (eAdapters == null) {
-			eAdapters = new EAdapterList<Adapter>(this) {
-
-				private static final long serialVersionUID = 1L;
-
-				@Override
-				public Adapter remove(int index) {
-					Adapter toRemove = primitiveGet(index);
-					if ((toRemove instanceof CDOViewSet) && !canDisconnectCDOViewSet()) {
-						// don't allow its removal if my view is still open!
-						// (Papyrus attempts to clear the resource set's adapters when disposing a ModelSet)
-						return null;
-					}
-
-					return super.remove(index);
-				}
-
-				@Override
-				public void clear() {
-					if (!canDisconnectCDOViewSet()) {
-						// we can remove everything but the view-set adapter
-						Adapter viewSetAdapter = getViewSetAdapter();
-						if (viewSetAdapter != null) {
-							retainAll(Collections.singleton(viewSetAdapter));
-						} else {
-							super.clear();
-						}
-					} else {
-						super.clear();
-					}
-				}
-
-				private Adapter getViewSetAdapter() {
-					return Iterables.find(this, Predicates.instanceOf(CDOViewSet.class), null);
-				}
-			};
-		}
-
-		return eAdapters;
-	}
-
-	private boolean canDisconnectCDOViewSet() {
-		CDOView view = getCDOView();
-		return ((view == null) || view.isClosed()) && getResources().isEmpty();
-	}
-
 	boolean isDirty(Resource resource) {
 		return (resource instanceof CDOResource) && DIRTY_STATES.contains(((CDOResource) resource).cdoState());
 	}
@@ -475,22 +409,85 @@
 	//
 
 	/**
-	 * CDO doesn't permit resources to be removed from the resource set if they are {@linkplain CDOState#DIRTY dirty}, so this specialized list
-	 * prevents that.
+	 * CDO doesn't permit resources to be removed from the resource set if they are {@linkplain CDOState#DIRTY dirty},
+	 * so this specialized list prevents that.
+	 * 
+	 * It also prevents early removal of CDOResources during {@link CDOAwareModelSet#unload() unload()}.
+	 * 
+	 * @author Eike Stepper
 	 */
 	private class SafeResourceList extends ResourcesEList<Resource> {
-
 		private static final long serialVersionUID = 1L;
 
+		public SafeResourceList() {
+		}
+
 		@Override
 		public boolean remove(Object object) {
-			boolean result = !(object instanceof CDOResource) || !isDirty((CDOResource) object);
-
-			if (result) {
-				result = super.remove(object);
+			if (object instanceof CDOResource && isDirty((CDOResource) object)) {
+				return false;
 			}
 
-			return result;
+			return super.remove(object);
+		}
+
+		@Override
+		public Iterator<Resource> iterator() {
+			Iterator<Resource> delegate = super.iterator();
+
+			if (unloading) {
+				return new AbstractFilteredIterator<Resource>(delegate) {
+					@Override
+					protected boolean isValid(Resource resource) {
+						return !(resource instanceof CDOResource);
+					}
+				};
+			}
+
+			return delegate;
+		}
+	}
+
+	/**
+	 * @author Eike Stepper
+	 */
+	private final class ViewSetAdapter extends AdapterImpl {
+
+		public ViewSetAdapter() {
+		}
+
+		@Override
+		public void notifyChanged(Notification notification) {
+			switch (notification.getEventType()) {
+			case Notification.ADD:
+				CDOView newView = (CDOView) notification.getNewValue();
+				EventUtil.addUniqueListener(newView, invalidationListener);
+				break;
+
+			case Notification.REMOVE:
+				CDOView oldView = (CDOView) notification.getOldValue();
+				EventUtil.removeListener(oldView, invalidationListener);
+				break;
+			}
+		}
+	}
+
+	/**
+	 * @author Eike Stepper
+	 */
+	private final class InvalidationListener implements IListener {
+
+		public InvalidationListener() {
+		}
+
+		@Override
+		public void notifyEvent(IEvent event) {
+			if (event instanceof CDOViewInvalidationEvent) {
+				TransactionalEditingDomain domain = getTransactionalEditingDomain();
+				if (domain instanceof CDOAwareTransactionalEditingDomain) {
+					((CDOAwareTransactionalEditingDomain) domain).fireResourceSetChanged((CDOViewInvalidationEvent) event);
+				}
+			}
 		}
 	}
 }
diff --git a/cdo/bundles/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/internal/core/resource/CDOSashModelProvider.java b/cdo/bundles/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/internal/core/resource/CDOSashModelProvider.java
index 0424340..297cfd8 100755
--- a/cdo/bundles/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/internal/core/resource/CDOSashModelProvider.java
+++ b/cdo/bundles/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/internal/core/resource/CDOSashModelProvider.java
@@ -13,7 +13,8 @@
  *****************************************************************************/
 package org.eclipse.papyrus.cdo.internal.core.resource;
 
-import org.eclipse.core.runtime.IPath;
+import java.io.File;
+
 import org.eclipse.emf.cdo.explorer.CDOExplorerUtil;
 import org.eclipse.emf.cdo.explorer.checkouts.CDOCheckout;
 import org.eclipse.emf.cdo.util.CDOURIUtil;
@@ -29,12 +30,11 @@
  */
 public class CDOSashModelProvider extends AbstractSashModelProvider {
 
-	private static final IPath SASH_MODEL_STORAGE_ROOT = Activator.getDefault().getStateLocation().append("sashidx"); //$NON-NLS-1$
+	private static final String BASE_FOLDER_NAME = "sashidx"; // $NON-NLS-1$
 
-	private IPath indexFolder;
+	private File indexFolder;
 
 	public CDOSashModelProvider() {
-		super();
 	}
 
 	@Override
@@ -43,23 +43,23 @@
 
 		CDOCheckout checkout = CDOExplorerUtil.getCheckout(modelSet);
 		if (checkout != null) {
-			initialize(checkout);
+			indexFolder = checkout.getStateFolder(BASE_FOLDER_NAME, true);
 		} else {
-			// Model probably is in the workspace if null
-			indexFolder = SASH_MODEL_STORAGE_ROOT;
+			indexFolder = new File(Activator.getDefault().getStateLocation().toFile(), BASE_FOLDER_NAME);
+			Activator.log.debug("CDOSashModelProvider can not find a checkout for model set " + modelSet + ". Storing sash models in " + indexFolder);
 		}
 	}
 
-	public CDOSashModelProvider initialize(CDOCheckout checkout) {
-		indexFolder = SASH_MODEL_STORAGE_ROOT.append(checkout.getView().getSession().getRepositoryInfo().getUUID());
-		return this;
+	@Override
+	public void dispose() {
+		indexFolder = null;
+		super.dispose();
 	}
 
 	@Override
 	public URI getSashModelURI(URI userModelURI) {
-		URI uriWithoutExtension = userModelURI.trimFileExtension();
-		IPath stateLocation = indexFolder.append(CDOURIUtil.extractResourcePath(uriWithoutExtension));
-		return URI.createFileURI(stateLocation.toString()).appendFileExtension(SashModel.SASH_MODEL_FILE_EXTENSION);
+		String relativePath = CDOURIUtil.extractResourcePath(userModelURI.trimFileExtension());
+		String path = new File(indexFolder, relativePath).getAbsolutePath();
+		return URI.createFileURI(path).appendFileExtension(SashModel.SASH_MODEL_FILE_EXTENSION);
 	}
-
 }
diff --git a/cdo/bundles/org.eclipse.papyrus.cdo.ui.customization.properties/src/org/eclipse/papyrus/cdo/internal/ui/customization/properties/storage/CDOContextStorageProvider.java b/cdo/bundles/org.eclipse.papyrus.cdo.ui.customization.properties/src/org/eclipse/papyrus/cdo/internal/ui/customization/properties/storage/CDOContextStorageProvider.java
index 0300631..5fb80d5 100755
--- a/cdo/bundles/org.eclipse.papyrus.cdo.ui.customization.properties/src/org/eclipse/papyrus/cdo/internal/ui/customization/properties/storage/CDOContextStorageProvider.java
+++ b/cdo/bundles/org.eclipse.papyrus.cdo.ui.customization.properties/src/org/eclipse/papyrus/cdo/internal/ui/customization/properties/storage/CDOContextStorageProvider.java
@@ -21,6 +21,7 @@
 import java.util.Collections;
 
 import org.eclipse.core.runtime.CoreException;
+import org.eclipse.emf.cdo.common.util.CDOResourceNodeNotFoundException;
 import org.eclipse.emf.cdo.eresource.CDOResourceFolder;
 import org.eclipse.emf.cdo.eresource.CDOTextResource;
 import org.eclipse.emf.cdo.explorer.CDOExplorerUtil;
@@ -200,7 +201,7 @@
 
 			try {
 				folder = checkout.getView().getResourceFolder(CONTEXTS_PATH);
-			} catch (Exception e) {
+			} catch (CDOResourceNodeNotFoundException e) {
 				// normal consequence when the folder doesn't exist
 			}