blob: ca81bf3efe8b625d582972218cd15f2febd00094 [file] [log] [blame]
/**
* <copyright>
*
* Copyright (c) 2008-2013 See4sys, itemis and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
*
* Contributors:
* See4sys - Initial API and implementation
* itemis - [409014] Listener URIChangeDetector registered for all transactional editing domains
* itemis - [409510] Enable resource scope-sensitive proxy resolutions without forcing metamodel implementations to subclass EObjectImpl
*
* </copyright>
*/
package org.eclipse.sphinx.emf.internal.ecore.proxymanagement.blacklist;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.transaction.NotificationFilter;
import org.eclipse.emf.transaction.ResourceSetChangeEvent;
import org.eclipse.emf.transaction.ResourceSetListenerImpl;
import org.eclipse.sphinx.emf.Activator;
import org.eclipse.sphinx.emf.domain.factory.AbstractResourceSetListenerInstaller;
import org.eclipse.sphinx.emf.internal.ecore.proxymanagement.ProxyHelper;
import org.eclipse.sphinx.emf.internal.ecore.proxymanagement.ProxyHelperAdapterFactory;
import org.eclipse.sphinx.emf.model.IModelDescriptor;
import org.eclipse.sphinx.emf.model.ModelDescriptorRegistry;
import org.eclipse.sphinx.platform.IExtendedPlatformConstants;
import org.eclipse.sphinx.platform.resources.DefaultResourceChangeHandler;
import org.eclipse.sphinx.platform.resources.ResourceDeltaVisitor;
import org.eclipse.sphinx.platform.util.PlatformLogUtil;
import org.eclipse.sphinx.platform.util.StatusUtil;
@SuppressWarnings("deprecation")
public class ModelIndexUpdater extends ResourceSetListenerImpl implements IResourceChangeListener {
public static class ModelIndexUpdaterInstaller extends AbstractResourceSetListenerInstaller<ModelIndexUpdater> {
public ModelIndexUpdaterInstaller() {
super(ModelIndexUpdater.class);
}
}
public ModelIndexUpdater() {
super(NotificationFilter.ANY);
ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
}
@Override
protected void finalize() throws Throwable {
ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
super.finalize();
}
@Override
public void resourceSetChanged(ResourceSetChangeEvent event) {
ProxyHelper proxyHelper = ProxyHelperAdapterFactory.INSTANCE.adapt(event.getEditingDomain().getResourceSet());
List<?> notifications = event.getNotifications();
for (Object object : notifications) {
if (object instanceof Notification) {
Notification notification = (Notification) object;
Object notifier = notification.getNotifier();
if (notifier instanceof Resource) {
Resource resource = (Resource) notifier;
if (notification.getFeatureID(Resource.class) == Resource.RESOURCE__IS_LOADED) {
if (resource.isLoaded()) {
proxyHelper.getBlackList().updateIndexOnResourceLoaded(resource);
} else {
// FIXME when called on post commit, resource content is empty
proxyHelper.getBlackList().updateIndexOnResourceUnloaded(resource);
}
}
} else if (notifier instanceof EObject) {
// Check if new model objects that are potential targets for black-listed proxy URIs have been added
EStructuralFeature feature = (EStructuralFeature) notification.getFeature();
if (feature instanceof EReference) {
EReference reference = (EReference) feature;
if (reference.isContainment()) {
if (notification.getEventType() == Notification.SET || notification.getEventType() == Notification.ADD
|| notification.getEventType() == Notification.ADD_MANY) {
// Get black-listed proxy URI pointing at changed model object as well as all
// black-listed proxy URIs pointing at model objects that are directly and indirectly
// contained by the former removed
proxyHelper.getBlackList().updateIndexOnResourceLoaded(((EObject) notifier).eResource());
}
}
}
// Check if existing model objects have been renamed and thereby became potential targets for
// black-listed proxy URIs (this is necessary for metamodels that derive proxy URIs from the values
// of certain string attributes on the target object and/or its containers)
else if (feature instanceof EAttribute) {
EAttribute attribute = (EAttribute) feature;
if (attribute.getEType().getInstanceClass() == String.class) {
if (notification.getEventType() == Notification.SET) {
// Get black-listed proxy URI pointing at changed model object as well as all
// black-listed proxy URIs pointing at model objects that are directly and indirectly
// contained by the former removed
proxyHelper.getBlackList().updateIndexOnResourceLoaded(((EObject) notifier).eResource());
}
}
}
}
}
}
}
private void handleProjectDescriptionChanged(final IProject project) {
/*
* !! Important Note !! Handle project description change in an asynchronous operation with exclusive access to
* the affected project for the following two reasons: 1/ In order to avoid deadlocks. The workspace is locked
* while IResourceChangeListeners are processed (exclusive workspace access) and updating the model descriptor
* registry may involve creating transactions (exclusive model access). In cases where another thread is around
* while we are called here which already has exclusive model access but waits for exclusive workspace access we
* would end up in a deadlock otherwise. 2/ In order to make sure that the model descriptor registry gets
* updated only AFTER all other IResourceChangeListeners have been processed which may be present and rely on
* the model descriptor registry's state BEFORE the update.
*/
if (project != null) {
// TODO externalize job label string
Job job = new Job("Update model index") {
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
HashSet<Resource> updatedResources = new HashSet<Resource>();
IProjectDescription description = project.getDescription();
if (description != null) {
IProject[] referencedProjects = description.getReferencedProjects();
for (IProject refrencedProject : referencedProjects) {
Collection<IModelDescriptor> projectModels = ModelDescriptorRegistry.INSTANCE.getModels(refrencedProject);
for (IModelDescriptor modelDescriptor : projectModels) {
updatedResources.addAll(modelDescriptor.getLoadedResources(true));
}
}
}
for (Resource resource : updatedResources) {
if (resource.isLoaded()) {
ProxyHelper proxyHelper = ProxyHelperAdapterFactory.INSTANCE.adapt(resource.getResourceSet());
proxyHelper.getBlackList().updateIndexOnResourceLoaded(resource);
}
}
return Status.OK_STATUS;
} catch (OperationCanceledException ex) {
return Status.CANCEL_STATUS;
} catch (CoreException ex) {
return ex.getStatus();
} catch (Exception ex) {
return StatusUtil.createErrorStatus(Activator.getPlugin(), ex);
}
}
@Override
public boolean belongsTo(Object family) {
return IExtendedPlatformConstants.FAMILY_MODEL_LOADING.equals(family)
|| IExtendedPlatformConstants.FAMILY_LONG_RUNNING.equals(family);
}
};
job.setPriority(Job.BUILD);
job.setRule(project);
job.setSystem(true);
job.schedule();
}
}
@Override
public void resourceChanged(IResourceChangeEvent event) {
try {
IResourceDelta delta = event.getDelta();
if (delta != null) {
IResourceDeltaVisitor visitor = new ResourceDeltaVisitor(event.getType(), new DefaultResourceChangeHandler() {
@Override
public void handleProjectDescriptionChanged(int eventType, IProject project) {
ModelIndexUpdater.this.handleProjectDescriptionChanged(project);
}
});
delta.accept(visitor);
}
} catch (Exception ex) {
PlatformLogUtil.logAsError(Activator.getDefault(), ex);
}
}
}