blob: 1d7ded72aabf1395d9ab5cebdd710a2ede7a7350 [file] [log] [blame]
/*
* Copyright (c) 2009-2013 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Simon McDuff - initial API and implementation
* Eike Stepper - maintenance
* Victor Roldan Betancort - bug 338921
* Christian W. Damus (CEA) - bug 399279: support removal from resource set adapters
*/
package org.eclipse.emf.internal.cdo.view;
import org.eclipse.emf.cdo.CDOState;
import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
import org.eclipse.emf.cdo.common.util.CDOException;
import org.eclipse.emf.cdo.eresource.CDOResource;
import org.eclipse.emf.cdo.eresource.CDOResourceFactory;
import org.eclipse.emf.cdo.util.CDOURIUtil;
import org.eclipse.emf.cdo.view.CDOView;
import org.eclipse.emf.cdo.view.CDOViewProvider;
import org.eclipse.emf.cdo.view.CDOViewProvider.CDOViewProvider2;
import org.eclipse.emf.internal.cdo.messages.Messages;
import org.eclipse.net4j.util.WrappedException;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.notify.impl.NotificationImpl;
import org.eclipse.emf.common.notify.impl.NotifierImpl;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.spi.cdo.InternalCDOObject;
import org.eclipse.emf.spi.cdo.InternalCDOView;
import org.eclipse.emf.spi.cdo.InternalCDOViewSet;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.Callable;
/**
* @author Simon McDuff
* @since 2.0
*/
public class CDOViewSetImpl extends NotifierImpl implements InternalCDOViewSet
{
public static final String KEY_VIEW_URI = "org.eclipse.emf.cdo.viewURI";
private Set<InternalCDOView> views = new HashSet<InternalCDOView>();
private Map<URI, InternalCDOView> mapOfViews = new HashMap<URI, InternalCDOView>();
private CDOResourceFactory resourceFactory;
private CDOViewSetPackageRegistryImpl packageRegistry;
private ResourceSet resourceSet;
private ThreadLocal<Boolean> ignoreNotifications = new InheritableThreadLocal<Boolean>();
public CDOViewSetImpl()
{
}
public ResourceSet getResourceSet()
{
return resourceSet;
}
public EPackage.Registry getPackageRegistry()
{
return packageRegistry;
}
public CDOResourceFactory getResourceFactory()
{
return resourceFactory;
}
public CDOView[] getViews()
{
synchronized (views)
{
return views.toArray(new CDOView[views.size()]);
}
}
/**
* @throws IllegalArgumentException
* if repositoryUUID doesn't match any CDOView.
*/
@Deprecated
public InternalCDOView resolveView(String repositoryUUID)
{
return resolveView(CDOURIUtil.PROTOCOL_NAME + "://" + repositoryUUID);
}
/**
* @throws IllegalArgumentException
* if repositoryUUID doesn't match any CDOView.
*/
public InternalCDOView resolveView(URI viewURI)
{
InternalCDOView view = null;
synchronized (views)
{
view = mapOfViews.get(viewURI);
if (view == null)
{
if (viewURI != null)
{
throw new IllegalArgumentException(MessageFormat.format(Messages.getString("CDOViewSetImpl.0"), viewURI)); //$NON-NLS-1$
}
if (mapOfViews.size() == 1)
{
return views.iterator().next();
}
if (mapOfViews.size() == 0)
{
return null;
}
throw new IllegalStateException(Messages.getString("CDOViewSetImpl.1")); //$NON-NLS-1$
}
}
return view;
}
public InternalCDOView getView(String repositoryUUID)
{
synchronized (views)
{
return mapOfViews.get(repositoryUUID);
}
}
public void add(InternalCDOView view)
{
URI viewURI = getViewURI(view);
synchronized (views)
{
CDOView lookupView = mapOfViews.get(viewURI);
if (lookupView != null)
{
throw new RuntimeException(MessageFormat.format(Messages.getString("CDOViewSetImpl.2"), viewURI)); //$NON-NLS-1$
}
views.add(view);
mapOfViews.put(viewURI, view);
}
if (eNotificationRequired())
{
NotificationImpl notification = new NotificationImpl(NotificationImpl.ADD, null, view);
eNotify(notification);
}
}
public void remove(InternalCDOView view)
{
List<Resource> resToRemove = new ArrayList<Resource>();
synchronized (views)
{
// It is important to remove view from the list first. It is the way we can differentiate close and detach.
if (views.remove(view))
{
URI viewURI = getViewURI(view);
mapOfViews.remove(viewURI);
for (Resource resource : getResourceSet().getResources())
{
if (resource instanceof CDOResource)
{
CDOResource cdoRes = (CDOResource)resource;
if (cdoRes.cdoView() == view)
{
resToRemove.add(resource);
}
}
}
}
}
getResourceSet().getResources().removeAll(resToRemove);
if (eNotificationRequired())
{
NotificationImpl notification = new NotificationImpl(NotificationImpl.REMOVE, view, null);
eNotify(notification);
}
}
public Notifier getTarget()
{
return resourceSet;
}
public void setTarget(Notifier newTarget)
{
if (newTarget == resourceSet)
{
return;
}
if (newTarget == null && resourceSet != null)
{
if (!resourceSet.getResources().isEmpty())
{
if (!resourceSet.eAdapters().contains(this))
{
resourceSet.eAdapters().add(this); // add me back to the resource set's adapters
}
throw new IllegalArgumentException(Messages.getString("CDOViewSetImpl.5")); //$NON-NLS-1$
}
if (getViews().length > 0)
{
if (!resourceSet.eAdapters().contains(this))
{
resourceSet.eAdapters().add(this); // add me back to the resource set's adapters
}
throw new IllegalArgumentException(Messages.getString("CDOViewSetImpl.6")); //$NON-NLS-1$
}
}
else
{
if (!isAdapterForType(newTarget))
{
throw new IllegalArgumentException(MessageFormat.format(Messages.getString("CDOViewSetImpl.3"), newTarget)); //$NON-NLS-1$
}
if (resourceSet != null)
{
throw new IllegalStateException(Messages.getString("CDOViewSetImpl.4")); //$NON-NLS-1$
}
}
resourceSet = (ResourceSet)newTarget;
if (resourceSet != null)
{
EPackage.Registry oldPackageRegistry = resourceSet.getPackageRegistry();
packageRegistry = new CDOViewSetPackageRegistryImpl(this, oldPackageRegistry);
resourceSet.setPackageRegistry(packageRegistry);
Map<String, Object> map = resourceSet.getResourceFactoryRegistry().getProtocolToFactoryMap();
Resource.Factory resourceFactory = (Resource.Factory)map.get(CDOProtocolConstants.PROTOCOL_NAME);
if (resourceFactory instanceof CDOResourceFactory)
{
this.resourceFactory = (CDOResourceFactory)resourceFactory;
}
else if (resourceFactory == null)
{
this.resourceFactory = CDOResourceFactory.INSTANCE;
map.put(CDOProtocolConstants.PROTOCOL_NAME, this.resourceFactory);
}
}
}
public boolean isAdapterForType(Object type)
{
return type instanceof ResourceSet;
}
public synchronized <V> V executeWithoutNotificationHandling(Callable<V> callable)
{
Boolean wasIgnore = ignoreNotifications.get();
try
{
ignoreNotifications.set(true);
return callable.call();
}
catch (Exception ex)
{
throw WrappedException.wrap(ex);
}
finally
{
if (wasIgnore == null)
{
ignoreNotifications.remove();
}
}
}
public void notifyChanged(Notification notification)
{
// The resource <-> view association is done in CDOResourceImpl.basicSetResourceSet()
if (ignoreNotifications.get() == null)
{
// We need to deregister CDOResources from CDOView if removed from the ResourceSet, see bug 338921
switch (notification.getEventType())
{
case Notification.REMOVE_MANY:
deregisterResources((List<?>)notification.getOldValue());
break;
case Notification.REMOVE:
deregisterResources(Collections.singleton(notification.getOldValue()));
break;
}
}
}
private void deregisterResources(Collection<?> potentialResources)
{
List<CDOResource> allDirtyResources = new ArrayList<CDOResource>();
try
{
Map<CDOView, List<CDOResource>> resourcesPerView = getResourcesPerView(potentialResources);
for (Entry<CDOView, List<CDOResource>> entry : resourcesPerView.entrySet())
{
InternalCDOView view = (InternalCDOView)entry.getKey();
List<CDOResource> resources = entry.getValue();
if (view.isDirty())
{
List<CDOResource> dirtyResources = getDirtyResources(resources);
if (!dirtyResources.isEmpty())
{
allDirtyResources.addAll(dirtyResources);
resourceSet.getResources().addAll(resources);
continue;
}
}
for (CDOResource resource : resources)
{
InternalCDOObject internalResource = (InternalCDOObject)resource;
view.deregisterObject(internalResource);
internalResource.cdoInternalSetState(CDOState.INVALID);
}
}
}
finally
{
int size = allDirtyResources.size();
if (size == 1)
{
throw new CDOException("Attempt to remove a dirty resource from a resource set: " + allDirtyResources.get(0));
}
else if (size > 1)
{
throw new CDOException("Attempt to remove dirty resources from a resource set: " + allDirtyResources);
}
}
}
private List<CDOResource> getDirtyResources(List<CDOResource> resources)
{
List<CDOResource> dirtyResources = new ArrayList<CDOResource>();
for (CDOResource resource : resources)
{
switch (resource.cdoState())
{
case NEW:
case DIRTY:
case CONFLICT:
case INVALID_CONFLICT:
dirtyResources.addAll(resources);
}
}
return dirtyResources;
}
private Map<CDOView, List<CDOResource>> getResourcesPerView(Collection<?> potentialResources)
{
Map<CDOView, List<CDOResource>> resourcesPerView = new HashMap<CDOView, List<CDOResource>>();
for (Object potentialResource : potentialResources)
{
if (potentialResource instanceof CDOResource)
{
CDOResource resource = (CDOResource)potentialResource;
CDOView view = resource.cdoView();
if (views.contains(view))
{
List<CDOResource> resources = resourcesPerView.get(view);
if (resources == null)
{
resources = new ArrayList<CDOResource>();
resourcesPerView.put(view, resources);
}
resources.add(resource);
}
}
}
return resourcesPerView;
}
private URI getViewURI(InternalCDOView view)
{
Object value = view.properties().get(KEY_VIEW_URI);
if (value instanceof String)
{
return URI.createURI((String)value);
}
CDOViewProvider provider = view.getProvider();
if (provider instanceof CDOViewProvider2)
{
URI viewURI = ((CDOViewProvider2)provider).getViewURI(view);
if (viewURI != null)
{
return viewURI;
}
}
String uuid = view.getSession().getRepositoryInfo().getUUID();
return URI.createURI(CDOURIUtil.PROTOCOL_NAME + "://" + uuid);
}
}