| /******************************************************************************* |
| * <copyright> |
| * |
| * Copyright (c) 2011, 2014 SAP AG. |
| * 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: |
| * Bug 336488 - DiagramEditor API |
| * Felix Velasco - mwenz - Bug 379788 - Memory leak in DefaultMarkerBehavior |
| * pjpaulin - Bug 352120 - Now uses IDiagramContainerUI interface |
| * mwenz - Bug 429215 - NPE in DefaultMarkerBehavior.dispose |
| * |
| * </copyright> |
| * |
| *******************************************************************************/ |
| package org.eclipse.graphiti.ui.editor; |
| |
| import java.util.LinkedHashMap; |
| import java.util.Map; |
| |
| 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.internal.GraphitiUIPlugin; |
| import org.eclipse.graphiti.ui.internal.T; |
| import org.eclipse.graphiti.ui.internal.services.GraphitiUiInternal; |
| import org.eclipse.swt.widgets.Display; |
| |
| /** |
| * The default implementation for the {@link DiagramBehavior} behavior extension |
| * that controls how markers are handled in the editor. Clients may subclass to |
| * change the marker behavior; use {@link DiagramBehavior#createMarkerBehavior()} |
| * to return the instance that shall be used.<br> |
| * Note that there is always a 1:1 relation with a {@link DiagramBehavior}. |
| * |
| * @since 0.9 |
| */ |
| public class DefaultMarkerBehavior { |
| |
| /** |
| * The associated {@link DiagramBehavior} |
| * |
| * @since 0.10 |
| */ |
| protected DiagramBehavior diagramBehavior; |
| |
| /** |
| * 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 DefaultMarkerBehavior(DiagramBehavior diagramBehavior) { |
| super(); |
| this.diagramBehavior = 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. |
| */ |
| 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, GraphitiUIPlugin.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); |
| T.racer().info(diagnostic.toString()); |
| } catch (final CoreException exception) { |
| T.racer().error(exception.getMessage(), 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 = GraphitiUiInternal.getEmfService().getFile(resource.getURI()); |
| final String fileName = file != null ? file.getFullPath().toString() : "unknown name"; //$NON-NLS-1$ |
| final BasicDiagnostic basicDiagnostic = new BasicDiagnostic( |
| Diagnostic.ERROR, |
| GraphitiUIPlugin.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, GraphitiUIPlugin.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: |
| case Resource.RESOURCE__ERRORS: |
| case Resource.RESOURCE__WARNINGS: { |
| 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); |
| } |
| }; |
| } |