package org.eclipse.bpmn2.modeler.ui.editor; import java.util.LinkedHashMap; import java.util.Map; import org.eclipse.bpmn2.modeler.core.Activator; import org.eclipse.bpmn2.modeler.core.utils.FileUtils; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.ui.MarkerHelper; import org.eclipse.emf.common.util.BasicDiagnostic; import org.eclipse.emf.common.util.Diagnostic; 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.edit.ui.util.EditUIMarkerHelper; import org.eclipse.emf.transaction.TransactionalEditingDomain; import org.eclipse.graphiti.ui.editor.DefaultMarkerBehavior; import org.eclipse.graphiti.ui.editor.DiagramBehavior; import org.eclipse.swt.widgets.Display; public class BPMN2EditorMarkerBehavior extends DefaultMarkerBehavior { /** * The marker helper instance is responsible for creating workspace resource * markers presented in Eclipse's Problems View. */ private MarkerHelper markerHelper = new EditUIMarkerHelper(); /** * Map to store the diagnostic associated with a resource. */ private Map<Resource, Diagnostic> resourceToDiagnosticMap = new LinkedHashMap<Resource, Diagnostic>(); /** * Controls whether the problem indication should be updated. */ private boolean updateProblemIndication = true; /** * Creates a new instance of {@link DefaultMarkerBehavior} that is * associated with the given {@link DiagramBehavior}. * * @param diagramBehavior * the associated {@link DiagramBehavior} * @since 0.10 */ public BPMN2EditorMarkerBehavior(DiagramBehavior diagramBehavior) { super(diagramBehavior); } /** * Initializes this marker behavior extension. The default implementation * simply registers an adapter that updates the markers when EMF objects * change. */ public void initialize() { diagramBehavior.getResourceSet().eAdapters().add(problemIndicationAdapter); } /** * Returns the adapter that is installed for updating the markers. * * @return the problemIndicationAdapter */ public EContentAdapter getProblemIndicationAdapter() { return problemIndicationAdapter; } /** * Can be called to (temporarily) disable the marker update adapter, so that * mass changes do not result in a bunch of notifications and cause * performance penalties. * * @see #enableProblemIndicationUpdate() */ public void disableProblemIndicationUpdate() { updateProblemIndication = false; } /** * Can be called to enable the marker update adapter again after it has been * disabled with {@link #disableProblemIndicationUpdate()}. The default * implementation also triggers an update of the markers. */ public void enableProblemIndicationUpdate() { updateProblemIndication = true; updateProblemIndication(); } /** * Updates the problems indication markers in the editor. The default * implementation used an EMF {@link BasicDiagnostic} to do the checks and * {@link EditUIMarkerHelper} to check and set markers for {@link EObject}s. */ protected void updateProblemIndication() { if (diagramBehavior == null) { // Already disposed return; } TransactionalEditingDomain editingDomain = diagramBehavior.getEditingDomain(); if (updateProblemIndication && editingDomain != null) { ResourceSet resourceSet = editingDomain.getResourceSet(); final BasicDiagnostic diagnostic = new BasicDiagnostic(Diagnostic.OK, Activator.PLUGIN_ID, 0, null, new Object[] { resourceSet }); for (final Diagnostic childDiagnostic : resourceToDiagnosticMap.values()) { if (childDiagnostic.getSeverity() != Diagnostic.OK) { diagnostic.add(childDiagnostic); } } if (markerHelper.hasMarkers(resourceSet)) { markerHelper.deleteMarkers(resourceSet); } if (diagnostic.getSeverity() != Diagnostic.OK) { try { markerHelper.createMarkers(diagnostic); } catch (final CoreException exception) { } } } } /** * Returns a diagnostic describing the errors and warnings listed in the * resource and the specified exception (if any). * * @param resource * the resource to analyze * @param exception * forwarded as data object to the {@link BasicDiagnostic} * @return a new {@link Diagnostic} for the given resource * */ public Diagnostic analyzeResourceProblems(Resource resource, Exception exception) { if ((!resource.getErrors().isEmpty() || !resource.getWarnings().isEmpty()) && updateProblemIndication) { final IFile file = FileUtils.getFile(resource.getURI()); final String fileName = file != null ? file.getFullPath().toString() : "unknown name"; //$NON-NLS-1$ final BasicDiagnostic basicDiagnostic = new BasicDiagnostic( Diagnostic.ERROR, Activator.PLUGIN_ID, 0, "Problems encountered in file " + fileName, new Object[] { exception == null ? (Object) resource : exception }); //$NON-NLS-1$ basicDiagnostic.merge(EcoreUtil.computeDiagnostic(resource, true)); return basicDiagnostic; } else if (exception != null) { return new BasicDiagnostic(Diagnostic.ERROR, Activator.PLUGIN_ID, 0, "Problems encountered in file", //$NON-NLS-1$ new Object[] { exception }); } else { return Diagnostic.OK_INSTANCE; } } /** * Called to dispose this instance when the editor is closed. The default * implementation simply disables the marker update adapter and removes it * from the resource set and clears its member variables. */ public void dispose() { disableProblemIndicationUpdate(); ResourceSet resourceSet = diagramBehavior.getResourceSet(); // Check for null to prevent NPE, see // https://bugs.eclipse.org/bugs/show_bug.cgi?id=429215 if (resourceSet != null) { resourceSet.eAdapters().remove(problemIndicationAdapter); } problemIndicationAdapter = null; markerHelper = null; diagramBehavior = null; resourceToDiagnosticMap.clear(); resourceToDiagnosticMap = null; } /** * Adapter used to update the problem indication when resources are demanded * loaded. */ private EContentAdapter problemIndicationAdapter = new EContentAdapter() { @Override public void notifyChanged(Notification notification) { if (notification.getNotifier() instanceof Resource) { switch (notification.getFeatureID(Resource.class)) { case Resource.RESOURCE__IS_LOADED: { final Resource resource = (Resource) notification.getNotifier(); final Diagnostic diagnostic = analyzeResourceProblems(resource, null); if (diagnostic.getSeverity() != Diagnostic.OK) { resourceToDiagnosticMap.put(resource, diagnostic); } else { resourceToDiagnosticMap.remove(resource); } if (updateProblemIndication) { Display.getDefault().asyncExec(new Runnable() { public void run() { updateProblemIndication(); } }); } break; } } } else { super.notifyChanged(notification); } } @Override protected void setTarget(Resource target) { basicSetTarget(target); } @Override protected void unsetTarget(Resource target) { basicUnsetTarget(target); } }; }