blob: 0191784d5f02286a361df1169f879e9b4c0d98c7 [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2013, 2017 CEA LIST 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:
* CEA LIST - Initial API and implementation
* Christian W. Damus (CEA) - bug 431618
*
*****************************************************************************/
package org.eclipse.papyrus.cdo.internal.ui.markers;
import java.util.Collection;
import java.util.Iterator;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.emf.cdo.eresource.CDOResource;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.WrappedException;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.transaction.RunnableWithResult;
import org.eclipse.emf.transaction.util.TransactionUtil;
import org.eclipse.gmf.runtime.common.core.command.AbstractCommand;
import org.eclipse.gmf.runtime.common.core.command.CommandResult;
import org.eclipse.gmf.runtime.common.core.command.ICommand;
import org.eclipse.papyrus.cdo.internal.ui.Activator;
import org.eclipse.papyrus.cdo.validation.problems.EProblem;
import org.eclipse.papyrus.cdo.validation.problems.edit.ProblemEditUtil;
import org.eclipse.papyrus.cdo.validation.problems.util.ProblemsManager;
import org.eclipse.papyrus.infra.core.resource.ModelSet;
import org.eclipse.papyrus.infra.services.markerlistener.IPapyrusMarker;
import org.eclipse.papyrus.infra.services.markerlistener.providers.AbstractMarkerProvider;
import org.eclipse.papyrus.infra.services.markerlistener.providers.IMarkerProvider2;
import org.eclipse.papyrus.infra.services.markerlistener.util.MarkerListenerUtils;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
/**
* This is the CDOMarkerProvider type. Enjoy.
*/
public class CDOMarkerProvider extends AbstractMarkerProvider implements IMarkerProvider2 {
private final ProblemEditUtil defaultUtil = new ProblemEditUtil(new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE));
public CDOMarkerProvider() {
super();
}
@Override
public boolean canProvideMarkersFor(Resource resource) {
return resource instanceof CDOResource;
}
@Override
public Collection<? extends IPapyrusMarker> getMarkers(final Resource resource, final String type, final boolean includeSubtypes) throws CoreException {
// run in a read-only transaction because the problems manager accesses
// a cross-reference adapter
return run(resource, CoreException.class, new RunnableWithResult.Impl<Collection<? extends IPapyrusMarker>>() {
@Override
public void run() {
setResult(Lists.newArrayList(Iterators.transform( //
getProblems(resource, type, includeSubtypes), //
CDOPapyrusMarker.wrap(getProblemEditUtil(resource)))));
}
});
}
protected Iterator<? extends EProblem> getProblems(final Resource resource, final String type, boolean includeSubtypes) {
final Predicate<EProblem> filter;
if (type == null) {
filter = Predicates.alwaysTrue();
} else if (includeSubtypes) {
filter = new Predicate<EProblem>() {
@Override
public boolean apply(EProblem input) {
return MarkerListenerUtils.isMarkerTypeSubtypeOf(input.getType(), type);
}
};
} else {
filter = new Predicate<EProblem>() {
@Override
public boolean apply(EProblem input) {
return type.equals(input.getType());
}
};
}
return Iterators.filter(getProblemsManager(resource).getAllProblems(resource), filter);
}
@Override
public void createMarkers(final Resource resource, final Diagnostic diagnostic, final IProgressMonitor monitor) throws CoreException {
// run in a read-only transaction because the problems manager accesses
// a cross-reference adapter. Note that a read/write transaction is not
// needed because we aren't modifying the contents of the resource set
// (the problems model is not stored in a resource)
run(resource, CoreException.class, new Runnable() {
@Override
public void run() {
try {
basicCreateMarkers(resource, diagnostic, monitor);
} catch (CoreException e) {
throw new WrappedException(e);
}
}
});
}
final void basicCreateMarkers(Resource resource, Diagnostic diagnostic, IProgressMonitor monitor) throws CoreException {
super.createMarkers(resource, diagnostic, monitor);
}
@Override
protected void doCreateMarker(Resource resource, Diagnostic diagnostic) throws CoreException {
ProblemsManager mgr = getProblemsManager(resource);
EProblem problem = mgr.createProblem(diagnostic);
if (problem != null) {
mgr.addProblem(problem);
}
}
@Override
protected void batchCreated(Resource resource) {
super.batchCreated(resource);
ResourceSet rset = resource.getResourceSet();
if (rset instanceof ModelSet) {
// yield the resource set to any other threads that might
// be waiting to read it
((ModelSet) rset).getTransactionalEditingDomain().yield();
}
}
@Override
public void deleteMarkers(final EObject object, final IProgressMonitor monitor, final String type, final boolean includeSubtypes) throws CoreException {
// run in a read-only transaction because the problems manager accesses
// a cross-reference adapter. Note that a read/write transaction is not
// needed because we aren't modifying the contents of the resource set
// (the problems model is not stored in a resource)
run(object.eResource(), CoreException.class, new Runnable() {
@Override
public void run() {
try {
basicDeleteMarkers(object, monitor, type, includeSubtypes);
} catch (CoreException e) {
throw new WrappedException(e);
}
}
});
}
protected final void basicDeleteMarkers(EObject object, IProgressMonitor monitor, String type, boolean includeSubtypes) throws CoreException {
super.deleteMarkers(object, monitor, type, includeSubtypes);
}
@Override
public void deleteMarkers(final Resource resource, IProgressMonitor monitor) {
try {
this.deleteMarkers(resource, monitor, null, true);
} catch (CoreException e) {
Activator.log.error(e);
}
}
@Override
public void deleteMarkers(final Resource resource, IProgressMonitor monitor, final String markerType, final boolean includeSubtypes) throws CoreException {
SubMonitor sub = SubMonitor.convert(monitor, IProgressMonitor.UNKNOWN);
// run in a read-only transaction because the problems manager accesses
// a cross-reference adapter. Note that a read/write transaction is not
// needed because we aren't modifying the contents of the resource set
// (the problems model is not stored in a resource)
run(resource, new Runnable() {
@Override
public void run() {
ProblemsManager mgr = getProblemsManager(resource);
if (markerType == null) {
// efficiently remove all markers for the resource
mgr.purgeProblems(resource);
} else {
// tediously remove the matching markers
for (EProblem next : Lists.newArrayList(getProblems(resource, markerType, includeSubtypes))) {
ProblemsManager.delete(next);
}
}
}
});
sub.done();
}
@Override
public boolean hasMarkers(Resource context, EObject object) {
ProblemsManager manager = getProblemsManager(context);
return (manager != null) && manager.getAllProblems(object).hasNext();
}
@Override
public ICommand getMarkerDeletionCommand(Resource context, EObject object) {
return new MarkerDeletionCommand(context, object);
}
private ProblemsManager getProblemsManager(Resource resource) {
return ProblemsManager.getProblemsManager(resource.getResourceSet());
}
private ProblemEditUtil getProblemEditUtil(Resource resource) {
ProblemEditUtil result = defaultUtil;
ResourceSet rset = resource.getResourceSet();
if (rset instanceof ModelSet) {
AdapterFactory factory = ((AdapterFactoryEditingDomain) ((ModelSet) rset).getTransactionalEditingDomain()).getAdapterFactory();
result = new ProblemEditUtil(factory);
}
return result;
}
static <X extends Throwable> void run(Resource context, Runnable runnable) {
run(context, RuntimeException.class, runnable);
}
static <X extends Throwable> void run(Resource context, Class<X> exceptionType, Runnable runnable) throws X {
ResourceSet rset = context.getResourceSet();
if (rset instanceof ModelSet) {
try {
((ModelSet) rset).getTransactionalEditingDomain().runExclusive(runnable);
} catch (WrappedException e) {
throw exceptionType.cast(e.exception());
} catch (InterruptedException e) {
Activator.log.error("CDO problem markers runnable interrupted.", e); //$NON-NLS-1$
}
} else {
runnable.run();
}
}
static <T, X extends Throwable> T run(Resource context, Class<X> exceptionType, RunnableWithResult<T> runnable) throws X {
T result;
ResourceSet rset = context.getResourceSet();
if (rset instanceof ModelSet) {
try {
result = TransactionUtil.runExclusive(((ModelSet) rset).getTransactionalEditingDomain(), runnable);
} catch (WrappedException e) {
throw exceptionType.cast(e.exception());
} catch (InterruptedException e) {
Activator.log.error("CDO problem markers runnable interrupted.", e); //$NON-NLS-1$
result = null;
}
} else {
runnable.run();
result = runnable.getResult();
}
return result;
}
//
// Nested types
//
private class MarkerDeletionCommand extends AbstractCommand {
private Resource context;
private EObject object;
private Collection<? extends EProblem> problemsForUndo;
public MarkerDeletionCommand(Resource context, EObject object) {
super("Delete markers");
this.context = context;
this.object = object;
}
@Override
public void dispose() {
context = null;
object = null;
problemsForUndo = null;
super.dispose();
}
@Override
protected CommandResult doExecuteWithResult(IProgressMonitor progressMonitor, IAdaptable info) throws ExecutionException {
ProblemsManager manager = getProblemsManager(context);
Collection<EProblem> problems = Lists.newArrayList(manager.getAllProblems(object));
if (problems.isEmpty()) {
// Nothing to do
return CommandResult.newOKCommandResult();
}
for (EProblem problem : problems) {
ProblemsManager.delete(problem);
}
// Initialize undo information
problemsForUndo = problems;
return CommandResult.newOKCommandResult();
}
@Override
protected CommandResult doUndoWithResult(IProgressMonitor progressMonitor, IAdaptable info) throws ExecutionException {
if (problemsForUndo == null) {
// Nothing to do
return CommandResult.newOKCommandResult();
}
// Detach undo information
final Collection<? extends EProblem> problems = problemsForUndo;
problemsForUndo = null;
Resource resource = object.eResource();
if (resource != null) { // Should have been reattached by now
context = resource;
ProblemsManager manager = getProblemsManager(resource);
for (EProblem problem : problems) {
problem.setElement(object);
manager.addProblem(problem);
}
}
return CommandResult.newOKCommandResult();
}
@Override
protected CommandResult doRedoWithResult(IProgressMonitor progressMonitor, IAdaptable info) throws ExecutionException {
return doExecuteWithResult(progressMonitor, info);
}
}
}