Browse Source

Issue 1190 (#1201)

* #1190

* Introducing IssueStore

* renamed StatechartIssue to SCTIssue

* clean up of decoration provider

* revert committed image

* removed logging

* removed stale flag

* Make registerValidationListener protected to allow overriding
Andreas Mülder 8 years ago
parent
commit
a750a51c4c

+ 4 - 4
plugins/org.yakindu.base.gmf.runtime/src/org/yakindu/base/gmf/runtime/decorators/AbstractMarkerBasedDecorationProvider.java

@@ -48,18 +48,18 @@ import org.eclipse.ui.PlatformUI;
  * @author andreas muelder - Initial contribution and API
  * 
  */
-public abstract class AbstractMarkerBasedDecorationProvider extends AbstractDecoratorProvider implements
-		IDecoratorProvider {
+public abstract class AbstractMarkerBasedDecorationProvider extends AbstractDecoratorProvider
+		implements IDecoratorProvider {
 
 	private static MarkerObserver fileObserver;
 
-	private static Map<String, List<IDecorator>> allDecorators = new HashMap<String, List<IDecorator>>();
+	protected static Map<String, List<IDecorator>> allDecorators = new HashMap<String, List<IDecorator>>();
 
 	protected abstract boolean shouldInstall(IEditorPart part);
 
 	protected abstract String getDecoratorKey();
 
-	protected abstract StatusDecorator createStatusDecorator(IDecoratorTarget decoratorTarget);
+	protected abstract IDecorator createStatusDecorator(IDecoratorTarget decoratorTarget);
 
 	public void createDecorators(IDecoratorTarget decoratorTarget) {
 		EditPart editPart = (EditPart) decoratorTarget.getAdapter(EditPart.class);

+ 3 - 0
plugins/org.yakindu.sct.domain.generic.editor/src/org/yakindu/sct/domain/generic/editor/GenericEditorModule.java

@@ -11,6 +11,7 @@
 package org.yakindu.sct.domain.generic.editor;
 
 import org.eclipse.xtext.service.AbstractGenericModule;
+import org.eclipse.xtext.ui.editor.validation.IValidationIssueProcessor;
 import org.eclipse.xtext.ui.editor.validation.MarkerCreator;
 import org.eclipse.xtext.ui.validation.MarkerTypeProvider;
 import org.eclipse.xtext.validation.IDiagnosticConverter;
@@ -24,6 +25,7 @@ import org.yakindu.sct.ui.editor.editor.proposals.SmartEditProposalProvider;
 import org.yakindu.sct.ui.editor.proposals.IEditProposalProvider;
 import org.yakindu.sct.ui.editor.providers.DefaultSCTPaletteFactory;
 import org.yakindu.sct.ui.editor.providers.ISCTPaletteFactory;
+import org.yakindu.sct.ui.editor.validation.DefaultValidationIssueStore;
 
 import com.google.inject.Binder;
 import com.google.inject.multibindings.Multibinder;
@@ -42,6 +44,7 @@ public class GenericEditorModule extends AbstractGenericModule {
 		proposalProviderBinder.addBinding().to(SmartEditProposalProvider.class);
 		proposalProviderBinder.addBinding().to(RefactoringProposalProvider.class);
 		binder.bind(IResourceValidator.class).to(SCTResourceValidatorImpl.class);
+		binder.bind(IValidationIssueProcessor.class).to(DefaultValidationIssueStore.class);
 	}
 
 	public Class<? extends ISCTPaletteFactory> bindISCTPaletteFactory() {

+ 2 - 2
plugins/org.yakindu.sct.model.sgraph.ui/src/org/yakindu/sct/model/sgraph/ui/validation/SCTDiagnosticConverterImpl.java

@@ -47,8 +47,8 @@ public class SCTDiagnosticConverterImpl extends DiagnosticConverterImpl {
 						if (eObject != null && eObject.eResource() != null) {
 							View notationView = findNotationView(eObject);
 							if (notationView != null && notationView.eResource() != null) {
-								acceptor.accept(new SCTMarkerCreator.WrappingIssue(t, notationView.eResource()
-										.getURIFragment(notationView)));
+								acceptor.accept(
+										new SCTIssue(t, notationView.eResource().getURIFragment(notationView)));
 								notAccepted = false;
 							}
 						}

+ 78 - 0
plugins/org.yakindu.sct.model.sgraph.ui/src/org/yakindu/sct/model/sgraph/ui/validation/SCTIssue.java

@@ -0,0 +1,78 @@
+/**
+ * Copyright (c) 2017 committers of YAKINDU 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:
+ * 	committers of YAKINDU - initial API and implementation
+ * 
+ */
+package org.yakindu.sct.model.sgraph.ui.validation;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.xtext.diagnostics.Severity;
+import org.eclipse.xtext.validation.CheckType;
+import org.eclipse.xtext.validation.Issue;
+import org.eclipse.xtext.validation.Issue.IssueImpl;
+
+/**
+ * 
+ * @author andreas muelder - Initial contribution and API
+ * 
+ */
+public class SCTIssue extends IssueImpl {
+
+	private final Issue delegate;
+
+	private final String notationViewURI;
+
+	public SCTIssue(final Issue delegate, String notationViewURI) {
+		this.delegate = delegate;
+		this.notationViewURI = notationViewURI;
+	}
+
+	public String getNotationViewURI() {
+		return notationViewURI;
+	}
+
+	public Severity getSeverity() {
+		return delegate.getSeverity();
+	}
+
+	public String getMessage() {
+		return delegate.getMessage();
+	}
+
+	public String getCode() {
+		return delegate.getCode();
+	}
+
+	public CheckType getType() {
+		return delegate.getType();
+	}
+
+	public URI getUriToProblem() {
+		return delegate.getUriToProblem();
+	}
+
+	public Integer getLineNumber() {
+		return delegate.getLineNumber();
+	}
+
+	public Integer getOffset() {
+		return delegate.getOffset();
+	}
+
+	public Integer getLength() {
+		return delegate.getLength();
+	}
+
+	public boolean isSyntaxError() {
+		return delegate.isSyntaxError();
+	}
+
+	public String[] getData() {
+		return delegate.getData();
+	}
+}

+ 3 - 70
plugins/org.yakindu.sct.model.sgraph.ui/src/org/yakindu/sct/model/sgraph/ui/validation/SCTMarkerCreator.java

@@ -13,12 +13,8 @@ package org.yakindu.sct.model.sgraph.ui.validation;
 import org.eclipse.core.resources.IMarker;
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.runtime.CoreException;
-import org.eclipse.emf.common.util.URI;
-import org.eclipse.xtext.diagnostics.Severity;
 import org.eclipse.xtext.ui.editor.validation.MarkerCreator;
-import org.eclipse.xtext.validation.CheckType;
 import org.eclipse.xtext.validation.Issue;
-import org.eclipse.xtext.validation.Issue.IssueImpl;
 
 /**
  * 
@@ -30,73 +26,10 @@ public class SCTMarkerCreator extends MarkerCreator {
 	public static final String ELEMENT_ID = "elementId";
 
 	@Override
-	protected void setMarkerAttributes(Issue issue, IResource resource,
-			IMarker marker) throws CoreException {
+	protected void setMarkerAttributes(Issue issue, IResource resource, IMarker marker) throws CoreException {
 		super.setMarkerAttributes(issue, resource, marker);
-		if (issue instanceof WrappingIssue) {
-			marker.setAttribute(ELEMENT_ID,
-					((WrappingIssue) issue).getNotationViewURI());
-		}
-	}
-
-	/**
-	 * 
-	 * @author andreas muelder - Initial contribution and API x
-	 */
-	public static class WrappingIssue extends IssueImpl {
-
-		private final Issue delegate;
-
-		private final String notationViewURI;
-
-		public WrappingIssue(final Issue delegate, String notationViewURI) {
-			this.delegate = delegate;
-			this.notationViewURI = notationViewURI;
-
-		}
-
-		public String getNotationViewURI() {
-			return notationViewURI;
-		}
-
-		public Severity getSeverity() {
-			return delegate.getSeverity();
-		}
-
-		public String getMessage() {
-			return delegate.getMessage();
-		}
-
-		public String getCode() {
-			return delegate.getCode();
-		}
-
-		public CheckType getType() {
-			return delegate.getType();
-		}
-
-		public URI getUriToProblem() {
-			return delegate.getUriToProblem();
-		}
-
-		public Integer getLineNumber() {
-			return delegate.getLineNumber();
-		}
-
-		public Integer getOffset() {
-			return delegate.getOffset();
-		}
-
-		public Integer getLength() {
-			return delegate.getLength();
-		}
-
-		public boolean isSyntaxError() {
-			return delegate.isSyntaxError();
-		}
-
-		public String[] getData() {
-			return delegate.getData();
+		if (issue instanceof SCTIssue) {
+			marker.setAttribute(ELEMENT_ID, ((SCTIssue) issue).getNotationViewURI());
 		}
 	}
 

+ 1 - 2
plugins/org.yakindu.sct.model.sgraph.ui/src/org/yakindu/sct/model/sgraph/ui/validation/SCTMarkerTypeProvider.java

@@ -12,7 +12,6 @@ package org.yakindu.sct.model.sgraph.ui.validation;
 
 import org.eclipse.xtext.ui.validation.MarkerTypeProvider;
 import org.eclipse.xtext.validation.Issue;
-import org.yakindu.sct.model.sgraph.ui.validation.SCTMarkerCreator.WrappingIssue;
 
 /**
  * 
@@ -23,7 +22,7 @@ public class SCTMarkerTypeProvider extends MarkerTypeProvider {
 
 	@Override
 	public String getMarkerType(Issue issue) {
-		if (issue instanceof WrappingIssue) {
+		if (issue instanceof SCTIssue) {
 			return "org.yakindu.sct.ui.editor.diagnostic";
 		}
 		return super.getMarkerType(issue);

+ 28 - 30
plugins/org.yakindu.sct.ui.editor/src/org/yakindu/sct/ui/editor/editor/StatechartDiagramEditor.java

@@ -15,7 +15,6 @@ import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IProjectDescription;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.draw2d.ColorConstants;
 import org.eclipse.emf.ecore.EObject;
@@ -56,12 +55,13 @@ import org.yakindu.sct.domain.extension.DomainStatus;
 import org.yakindu.sct.domain.extension.DomainStatus.Severity;
 import org.yakindu.sct.domain.extension.IDomain;
 import org.yakindu.sct.ui.editor.DiagramActivator;
-import org.yakindu.sct.ui.editor.partitioning.DiagramEditorInput;
 import org.yakindu.sct.ui.editor.partitioning.DiagramPartitioningEditor;
 import org.yakindu.sct.ui.editor.partitioning.DiagramPartitioningUtil;
 import org.yakindu.sct.ui.editor.proposals.ContentProposalViewerKeyHandler;
 import org.yakindu.sct.ui.editor.providers.ISCTOutlineFactory;
 import org.yakindu.sct.ui.editor.utils.HelpContextIds;
+import org.yakindu.sct.ui.editor.validation.IValidationIssueStore;
+import org.yakindu.sct.ui.editor.validation.LiveValidationListener;
 
 import com.google.inject.Injector;
 import com.google.inject.Key;
@@ -73,13 +73,15 @@ import com.google.inject.Key;
 @SuppressWarnings("restriction")
 public class StatechartDiagramEditor extends DiagramPartitioningEditor implements IGotoMarker {
 
+	private static final int INITIAL_PALETTE_SIZE = 175;
 	private static final Font INVALID_DOMAIN_FONT = new Font(null, new FontData("Verdana", 10, SWT.BOLD));
 	public static final String ID = "org.yakindu.sct.ui.editor.editor.StatechartDiagramEditor";
 
 	private KeyHandler keyHandler;
 
 	private DirtyStateListener domainAdapter;
-	private ResourceSetValidationListener validationListener;
+	private LiveValidationListener validationListener;
+	private IValidationIssueStore issueStore;
 
 	public StatechartDiagramEditor() {
 		super(true);
@@ -124,14 +126,6 @@ public class StatechartDiagramEditor extends DiagramPartitioningEditor implement
 		parent.pack(true);
 	}
 
-	@Override
-	public Object getAdapter(@SuppressWarnings("rawtypes") Class type) {
-		if (IContentOutlinePage.class.equals(type)) {
-			return createOutline(type);
-		}
-		return super.getAdapter(type);
-	}
-
 	protected Object createOutline(Class<?> type) {
 		Injector editorInjector = getEditorInjector();
 		boolean outlineBindingExists = null != editorInjector.getExistingBinding(Key.get(ISCTOutlineFactory.class));
@@ -150,10 +144,14 @@ public class StatechartDiagramEditor extends DiagramPartitioningEditor implement
 		registerValidationListener();
 	}
 
-	private void registerValidationListener() {
-		validationListener = getEditorInjector().getInstance(ResourceSetValidationListener.class);
+	protected void registerValidationListener() {
+		issueStore = getEditorInjector().getInstance(IValidationIssueStore.class);
+		issueStore.connect(getDiagram().eResource());
+		validationListener = getEditorInjector().getInstance(LiveValidationListener.class);
 		validationListener.setResource(getDiagram().eResource());
+		validationListener.setValidationIssueProcessor(issueStore);
 		getEditingDomain().addResourceSetListener(validationListener);
+		validationListener.scheduleValidation();
 	}
 
 	protected Injector getEditorInjector() {
@@ -162,7 +160,7 @@ public class StatechartDiagramEditor extends DiagramPartitioningEditor implement
 		return injector;
 	}
 
-	private void checkXtextNature() {
+	protected void checkXtextNature() {
 		IFileEditorInput editorInput = (IFileEditorInput) getEditorInput();
 		IProject project = editorInput.getFile().getProject();
 		if (project != null && !XtextProjectHelper.hasNature(project) && project.isAccessible()
@@ -258,10 +256,11 @@ public class StatechartDiagramEditor extends DiagramPartitioningEditor implement
 			// Zoom in - Windows - German layout ([CTRL++] propagates char 0x1d)
 			getKeyHandler().put(KeyStroke.getPressed((char) 0x1d, 0x2b, SWT.MOD1),
 					getActionRegistry().getAction(GEFActionConstants.ZOOM_IN));
-			
+
 			// Test Error - for AERI testing only
-//			DOWN: stateMask=0x50000 CTRL ALT, keyCode=0x6c 'l', character=0xc ''
-			getKeyHandler().put(KeyStroke.getPressed((char)0xc, 0x6c,  0x50000), new Action() {
+			// DOWN: stateMask=0x50000 CTRL ALT, keyCode=0x6c 'l', character=0xc
+			// ' '
+			getKeyHandler().put(KeyStroke.getPressed((char) 0xc, 0x6c, 0x50000), new Action() {
 				@Override
 				public void run() {
 					DiagramActivator.getDefault().getLog()
@@ -299,27 +298,26 @@ public class StatechartDiagramEditor extends DiagramPartitioningEditor implement
 		if (validationListener != null) {
 			validationListener.dispose();
 		}
+		issueStore.disconnect(getDiagram().eResource());
 		getEditingDomain().removeResourceSetListener(validationListener);
 		getEditingDomain().removeResourceSetListener(domainAdapter);
 		if (domainAdapter != null)
 			domainAdapter.dispose();
-		IFileEditorInput editorInput = (IFileEditorInput) getEditorInput();
-		try {
-
-			// Touch the file for revalidation, when the user did not save
-			// the changes, only for the "root editor"
-			if (editorInput != null && !(editorInput instanceof DiagramEditorInput) && isDirty()
-					&& editorInput.getFile() != null && editorInput.getFile().exists()) {
-				editorInput.getFile().touch(new NullProgressMonitor());
-			}
-		} catch (CoreException e) {
-			e.printStackTrace();
-		}
 		super.dispose();
 	}
 
 	@Override
 	protected int getInitialPaletteSize() {
-		return 175;
+		return INITIAL_PALETTE_SIZE;
+	}
+
+	@Override
+	public Object getAdapter(@SuppressWarnings("rawtypes") Class type) {
+		if (IContentOutlinePage.class.equals(type)) {
+			return createOutline(type);
+		} else if (IValidationIssueStore.class.equals(type)) {
+			return issueStore;
+		}
+		return super.getAdapter(type);
 	}
 }

+ 91 - 49
plugins/org.yakindu.sct.ui.editor/src/org/yakindu/sct/ui/editor/providers/StatechartValidationDecorationProvider.java

@@ -12,10 +12,16 @@ package org.yakindu.sct.ui.editor.providers;
 
 import java.util.List;
 
-import org.eclipse.core.resources.IMarker;
 import org.eclipse.draw2d.FlowLayout;
 import org.eclipse.draw2d.Label;
+import org.eclipse.gef.EditDomain;
+import org.eclipse.gef.EditPart;
+import org.eclipse.gef.editparts.AbstractConnectionEditPart;
 import org.eclipse.gmf.runtime.diagram.core.util.ViewUtil;
+import org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart;
+import org.eclipse.gmf.runtime.diagram.ui.editparts.IPrimaryEditPart;
+import org.eclipse.gmf.runtime.diagram.ui.parts.DiagramEditDomain;
+import org.eclipse.gmf.runtime.diagram.ui.services.decorator.AbstractDecorator;
 import org.eclipse.gmf.runtime.diagram.ui.services.decorator.IDecoratorProvider;
 import org.eclipse.gmf.runtime.diagram.ui.services.decorator.IDecoratorTarget;
 import org.eclipse.gmf.runtime.notation.Edge;
@@ -24,75 +30,111 @@ import org.eclipse.swt.graphics.Image;
 import org.eclipse.ui.IEditorPart;
 import org.eclipse.ui.ISharedImages;
 import org.eclipse.ui.PlatformUI;
-import org.yakindu.base.gmf.runtime.decorators.AbstractMarkerBasedDecorationProvider;
+import org.eclipse.xtext.diagnostics.Severity;
+import org.eclipse.xtext.validation.Issue;
+import org.yakindu.base.gmf.runtime.decorators.AbstractDecoratorProvider;
 import org.yakindu.sct.model.sgraph.FinalState;
 import org.yakindu.sct.model.sgraph.Pseudostate;
+import org.yakindu.sct.model.sgraph.ui.validation.SCTIssue;
 import org.yakindu.sct.ui.editor.editor.StatechartDiagramEditor;
-import org.yakindu.sct.ui.editor.validation.IMarkerType;
+import org.yakindu.sct.ui.editor.validation.IValidationIssueStore;
+import org.yakindu.sct.ui.editor.validation.IValidationIssueStore.IResourceIssueStoreListener;
 
-public class StatechartValidationDecorationProvider extends AbstractMarkerBasedDecorationProvider implements
-		IDecoratorProvider, IMarkerType {
+/**
+ * 
+ * @author Andreas Muelder - Initial contribution and API
+ *
+ */
+public class StatechartValidationDecorationProvider extends AbstractDecoratorProvider implements IDecoratorProvider {
 
 	private static final String KEY = "org.yakindu.sct.ui.editor.validation";
 
-	@Override
+	private IValidationIssueStore issueStore;
+
+	public void createDecorators(IDecoratorTarget decoratorTarget) {
+		EditPart editPart = (EditPart) decoratorTarget.getAdapter(EditPart.class);
+		if (editPart instanceof GraphicalEditPart || editPart instanceof AbstractConnectionEditPart) {
+			EditDomain ed = editPart.getViewer().getEditDomain();
+			if (!(ed instanceof DiagramEditDomain)) {
+				return;
+			}
+			if (shouldInstall(((DiagramEditDomain) ed).getEditorPart())) {
+				decoratorTarget.installDecorator(getDecoratorKey(), createStatusDecorator(decoratorTarget));
+			}
+		}
+	}
+
 	protected boolean shouldInstall(IEditorPart part) {
-		return part instanceof StatechartDiagramEditor;
+		if (part instanceof StatechartDiagramEditor) {
+			issueStore = (IValidationIssueStore) part.getAdapter(IValidationIssueStore.class);
+			return true;
+		}
+		return false;
 	}
 
-	@Override
 	protected String getDecoratorKey() {
 		return KEY;
 	}
 
-	@Override
-	protected StatusDecorator createStatusDecorator(IDecoratorTarget decoratorTarget) {
+	protected ValidationDecorator createStatusDecorator(IDecoratorTarget decoratorTarget) {
 		return new ValidationDecorator(decoratorTarget);
 	}
 
-	public static class ValidationDecorator extends StatusDecorator {
+	public class ValidationDecorator extends AbstractDecorator implements IResourceIssueStoreListener {
 
 		public ValidationDecorator(IDecoratorTarget decoratorTarget) {
 			super(decoratorTarget);
 		}
 
-		@Override
-		protected void createDecorators(View view, List<IMarker> markers) {
+		public void refresh() {
+			removeDecoration();
+			View view = (View) getDecoratorTarget().getAdapter(View.class);
+			if (view == null || view.eResource() == null) {
+				return;
+			}
+			EditPart editPart = (EditPart) getDecoratorTarget().getAdapter(EditPart.class);
+			if (editPart == null || editPart.getViewer() == null || !(editPart instanceof IPrimaryEditPart)) {
+				return;
+			}
+			decorate(view);
+		}
+
+		public void activate() {
+			issueStore.addIssueStoreListener(this);
+		}
+
+		public void deactivate() {
+			super.deactivate();
+		}
+
+		protected void decorate(View view) {
 			String elementId = ViewUtil.getIdStr(view);
 			if (elementId == null) {
 				return;
 			}
-			int severity = IMarker.SEVERITY_INFO;
-			IMarker foundMarker = null;
+			List<SCTIssue> issues = issueStore.getIssues(elementId);
+			Severity severity = Severity.INFO;
 			Label toolTip = null;
-			for (int i = 0; i < markers.size(); i++) {
-				IMarker marker = markers.get(i);
-				String attribute = marker.getAttribute(org.eclipse.gmf.runtime.common.ui.resources.IMarker.ELEMENT_ID,
-						"");
-				if (attribute.equals(elementId)) {
-					int nextSeverity = marker.getAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO);
-					Image nextImage = getImage(nextSeverity);
-					if (foundMarker == null) {
-						foundMarker = marker;
-						toolTip = new Label(marker.getAttribute(IMarker.MESSAGE, ""), //$NON-NLS-1$
-								nextImage);
-					} else {
-						if (toolTip.getChildren().isEmpty()) {
-							Label comositeLabel = new Label();
-							FlowLayout fl = new FlowLayout(false);
-							fl.setMinorSpacing(0);
-							comositeLabel.setLayoutManager(fl);
-							comositeLabel.add(toolTip);
-							toolTip = comositeLabel;
-						}
-						toolTip.add(new Label(marker.getAttribute(IMarker.MESSAGE, ""), //$NON-NLS-1$
-								nextImage));
+			if (issues.isEmpty())
+				return;
+			for (int i = 0; i < issues.size(); i++) {
+				Issue issue = issues.get(i);
+				Severity nextSeverity = issue.getSeverity();
+				Image nextImage = getImage(nextSeverity);
+				if (toolTip == null) {
+					toolTip = new Label(issue.getMessage(), nextImage);
+				} else {
+					if (toolTip.getChildren().isEmpty()) {
+						Label comositeLabel = new Label();
+						FlowLayout fl = new FlowLayout(false);
+						fl.setMinorSpacing(0);
+						comositeLabel.setLayoutManager(fl);
+						comositeLabel.add(toolTip);
+						toolTip = comositeLabel;
 					}
-					severity = (nextSeverity > severity) ? nextSeverity : severity;
+					toolTip.add(new Label(issue.getMessage(), nextImage));
 				}
-			}
-			if (foundMarker == null) {
-				return;
+				severity = (nextSeverity.ordinal() < severity.ordinal()) ? nextSeverity : severity;
 			}
 
 			if (view instanceof Edge) {
@@ -107,18 +149,13 @@ public class StatechartValidationDecorationProvider extends AbstractMarkerBasedD
 			}
 		}
 
-		@Override
-		protected String getMarkerType() {
-			return SCT_MARKER_TYPE;
-		}
-
-		private Image getImage(int severity) {
+		protected Image getImage(Severity severity) {
 			String imageName = ISharedImages.IMG_OBJS_ERROR_TSK;
 			switch (severity) {
-			case IMarker.SEVERITY_ERROR:
+			case ERROR:
 				imageName = ISharedImages.IMG_OBJS_ERROR_TSK;
 				break;
-			case IMarker.SEVERITY_WARNING:
+			case WARNING:
 				imageName = ISharedImages.IMG_OBJS_WARN_TSK;
 				break;
 			default:
@@ -126,5 +163,10 @@ public class StatechartValidationDecorationProvider extends AbstractMarkerBasedD
 			}
 			return PlatformUI.getWorkbench().getSharedImages().getImage(imageName);
 		}
+
+		@Override
+		public void issuesChanged() {
+			refresh();
+		}
 	}
 }

+ 207 - 0
plugins/org.yakindu.sct.ui.editor/src/org/yakindu/sct/ui/editor/validation/DefaultValidationIssueStore.java

@@ -0,0 +1,207 @@
+/**
+ * Copyright (c) 2017 committers of YAKINDU 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:
+ * 	committers of YAKINDU - initial API and implementation
+ * 
+ */
+package org.yakindu.sct.ui.editor.validation;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.workspace.util.WorkspaceSynchronizer;
+import org.eclipse.gmf.runtime.common.ui.resources.FileChangeManager;
+import org.eclipse.gmf.runtime.common.ui.resources.IFileObserver;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.xtext.ui.util.IssueUtil;
+import org.eclipse.xtext.validation.Issue;
+import org.yakindu.sct.model.sgraph.ui.validation.SCTIssue;
+import org.yakindu.sct.ui.editor.DiagramActivator;
+import org.yakindu.sct.ui.editor.preferences.StatechartPreferenceConstants;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+import com.google.inject.Inject;
+
+/**
+ * 
+ *
+ * @author Andreas Mülder - Initial contribution and API
+ *
+ */
+public class DefaultValidationIssueStore
+		implements IValidationIssueStore, IFileObserver, IMarkerType, IPropertyChangeListener {
+
+	@Inject
+	private IssueUtil issueCreator;
+
+	private List<IResourceIssueStoreListener> listener;
+	// the URI of the notation element
+	private Multimap<String, SCTIssue> persistentIssues;
+	private Multimap<String, SCTIssue> liveIssues;
+	private boolean connected = false;
+
+	private Resource resource;
+
+	public DefaultValidationIssueStore() {
+		listener = Lists.newArrayList();
+		persistentIssues = ArrayListMultimap.create();
+		liveIssues = ArrayListMultimap.create();
+		DiagramActivator.getDefault().getPreferenceStore().addPropertyChangeListener(this);
+	}
+
+	protected String getMarkerType() {
+		return SCT_MARKER_TYPE;
+	}
+
+	@Override
+	public void addIssueStoreListener(IResourceIssueStoreListener listener) {
+		this.listener.add(listener);
+	}
+
+	@Override
+	public void removeIssueStoreListener(IResourceIssueStoreListener listener) {
+		this.listener.remove(listener);
+	}
+
+	protected void notifyListeners() {
+		for (IResourceIssueStoreListener iResourceIssueStoreListener : listener) {
+			iResourceIssueStoreListener.issuesChanged();
+		}
+	}
+
+	@Override
+	public void connect(Resource resource) {
+		if (connected)
+			throw new IllegalStateException("Issue store is already connected to a resource");
+		connected = true;
+		this.resource = resource;
+		reloadMarkerIssues();
+	}
+
+	protected void reloadMarkerIssues() {
+		persistentIssues.clear();
+		if (liveValidationEnabled())
+			return;
+		IFile file = WorkspaceSynchronizer.getFile(resource);
+		if (file != null && file.isAccessible()) {
+			FileChangeManager.getInstance().addFileObserver(this);
+		}
+
+		List<IMarker> markers = new ArrayList<IMarker>();
+		try {
+			markers.addAll(Arrays.asList(file.findMarkers(getMarkerType(), true, IResource.DEPTH_INFINITE)));
+		} catch (CoreException e) {
+			e.printStackTrace();
+		}
+		for (IMarker iMarker : markers) {
+			SCTIssue issue = createFromMarker(iMarker);
+			persistentIssues.put(issue.getNotationViewURI(), issue);
+		}
+		notifyListeners();
+	}
+
+	@Override
+	public void disconnect(Resource resource) {
+		IFile file = WorkspaceSynchronizer.getFile(resource);
+		if (file != null && file.isAccessible()) {
+			FileChangeManager.getInstance().removeFileObserver(this);
+		}
+		persistentIssues.clear();
+		connected = false;
+	}
+
+	protected SCTIssue createFromMarker(IMarker marker) {
+		String notationURI = marker.getAttribute(org.eclipse.gmf.runtime.common.ui.resources.IMarker.ELEMENT_ID, "");
+		Issue delegate = issueCreator.createIssue(marker);
+		SCTIssue issue = new SCTIssue(delegate, notationURI);
+		return issue;
+	}
+
+	@Override
+	public void processIssues(List<Issue> issues, IProgressMonitor monitor) {
+		liveIssues.clear();
+		for (Issue issue : issues) {
+			if (issue instanceof SCTIssue) {
+				String notationViewURI = ((SCTIssue) issue).getNotationViewURI();
+				liveIssues.put(notationViewURI, (SCTIssue) issue);
+			}
+		}
+		notifyListeners();
+	}
+
+	@Override
+	public List<SCTIssue> getIssues(String uri) {
+		List<SCTIssue> result = Lists.newArrayList();
+		if (!liveValidationEnabled()) {
+			result.addAll(persistentIssues.get(uri));
+			return result;
+		} else {
+			result.addAll(liveIssues.get(uri));
+		}
+		return result;
+
+	}
+
+	protected boolean liveValidationEnabled() {
+		return DiagramActivator.getDefault().getPreferenceStore()
+				.getBoolean(StatechartPreferenceConstants.PREF_LIVE_VALIDATION);
+	}
+
+	@Override
+	public void handleMarkerAdded(IMarker marker) {
+		reloadMarkerIssues();
+	}
+
+	@Override
+	public void handleMarkerDeleted(IMarker marker, @SuppressWarnings("rawtypes") Map attributes) {
+		reloadMarkerIssues();
+
+	}
+
+	@Override
+	public void handleMarkerChanged(IMarker marker) {
+		reloadMarkerIssues();
+	}
+
+	@Override
+	public void propertyChange(PropertyChangeEvent event) {
+		if (StatechartPreferenceConstants.PREF_LIVE_VALIDATION.equals(event.getProperty())) {
+			reloadMarkerIssues();
+		}
+	}
+
+	@Override
+	public void handleFileRenamed(IFile oldFile, IFile file) {
+		// Nothing to do
+	}
+
+	@Override
+	public void handleFileMoved(IFile oldFile, IFile file) {
+		// Nothing to do
+	}
+
+	@Override
+	public void handleFileDeleted(IFile file) {
+		// Nothing to do
+	}
+
+	@Override
+	public void handleFileChanged(IFile file) {
+		// Nothing to do
+	}
+}

+ 45 - 0
plugins/org.yakindu.sct.ui.editor/src/org/yakindu/sct/ui/editor/validation/IValidationIssueStore.java

@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2017 committers of YAKINDU 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:
+ * 	committers of YAKINDU - initial API and implementation
+ * 
+ */
+package org.yakindu.sct.ui.editor.validation;
+
+import java.util.List;
+
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.xtext.ui.editor.validation.IValidationIssueProcessor;
+import org.yakindu.sct.model.sgraph.ui.validation.SCTIssue;
+
+import com.google.inject.ImplementedBy;
+
+/**
+ * 
+ *
+ * @author Andreas Muelder - Initial contribution and API
+ *
+ */
+@ImplementedBy(DefaultValidationIssueStore.class)
+public interface IValidationIssueStore extends IValidationIssueProcessor {
+
+	public interface IResourceIssueStoreListener {
+
+		public void issuesChanged();
+
+	}
+
+	public void addIssueStoreListener(IResourceIssueStoreListener listener);
+
+	public void removeIssueStoreListener(IResourceIssueStoreListener listener);
+
+	public void connect(Resource resource);
+
+	public void disconnect(Resource resource);
+
+	public List<SCTIssue> getIssues(String uri);
+}

+ 14 - 3
plugins/org.yakindu.sct.ui.editor/src/org/yakindu/sct/ui/editor/editor/ResourceSetValidationListener.java

@@ -8,7 +8,7 @@
  * committers of YAKINDU - initial API and implementation
  *
 */
-package org.yakindu.sct.ui.editor.editor;
+package org.yakindu.sct.ui.editor.validation;
 
 import org.eclipse.emf.common.notify.Notification;
 import org.eclipse.emf.ecore.EClass;
@@ -17,17 +17,17 @@ import org.eclipse.emf.ecore.resource.Resource;
 import org.eclipse.emf.transaction.ResourceSetChangeEvent;
 import org.eclipse.emf.transaction.ResourceSetListenerImpl;
 import org.eclipse.emf.workspace.util.WorkspaceSynchronizer;
+import org.eclipse.xtext.ui.editor.validation.IValidationIssueProcessor;
 import org.yakindu.sct.model.sgraph.SGraphPackage;
 import org.yakindu.sct.ui.editor.DiagramActivator;
 import org.yakindu.sct.ui.editor.preferences.StatechartPreferenceConstants;
-import org.yakindu.sct.ui.editor.validation.SCTValidationJob;
 
 import com.google.inject.Inject;
 
 /**
  * @author andreas muelder - Initial contribution and API
  */
-public class ResourceSetValidationListener extends ResourceSetListenerImpl {
+public class LiveValidationListener extends ResourceSetListenerImpl {
 
 	private static final int DELAY = 200; // ms
 
@@ -61,6 +61,12 @@ public class ResourceSetValidationListener extends ResourceSetListenerImpl {
 		}
 	}
 
+	public void scheduleValidation() {
+		if (liveValidationEnabled()) {
+			validationJob.schedule();
+		}
+	}
+
 	protected boolean liveValidationEnabled() {
 		return DiagramActivator.getDefault().getPreferenceStore()
 				.getBoolean(StatechartPreferenceConstants.PREF_LIVE_VALIDATION);
@@ -71,8 +77,13 @@ public class ResourceSetValidationListener extends ResourceSetListenerImpl {
 		validationJob.setRule(WorkspaceSynchronizer.getFile(resource));
 	}
 
+	public void setValidationIssueProcessor(IValidationIssueProcessor validationIssueProcessor) {
+		validationJob.setValidationIssueProcessor(validationIssueProcessor);
+	}
+
 	public void dispose() {
 		if (validationJob != null)
 			validationJob.cancel();
 	}
+
 }

+ 11 - 38
plugins/org.yakindu.sct.ui.editor/src/org/yakindu/sct/ui/editor/validation/SCTValidationJob.java

@@ -13,25 +13,19 @@ package org.yakindu.sct.ui.editor.validation;
 import java.util.List;
 
 import org.eclipse.core.commands.ExecutionException;
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IResource;
-import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IAdaptable;
 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.util.WrappedException;
 import org.eclipse.emf.ecore.resource.Resource;
 import org.eclipse.emf.transaction.RunnableWithResult;
 import org.eclipse.emf.transaction.TransactionalEditingDomain;
 import org.eclipse.emf.transaction.util.TransactionUtil;
-import org.eclipse.emf.workspace.util.WorkspaceSynchronizer;
 import org.eclipse.gmf.runtime.common.core.command.CommandResult;
 import org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand;
-import org.eclipse.ui.PlatformUI;
-import org.eclipse.xtext.ui.editor.validation.MarkerCreator;
+import org.eclipse.xtext.ui.editor.validation.IValidationIssueProcessor;
 import org.eclipse.xtext.util.CancelIndicator;
 import org.eclipse.xtext.validation.CheckMode;
 import org.eclipse.xtext.validation.IResourceValidator;
@@ -40,20 +34,18 @@ import org.yakindu.sct.model.sgraph.resource.AbstractSCTResource;
 import org.yakindu.sct.ui.editor.DiagramActivator;
 
 import com.google.inject.Inject;
-import com.google.inject.Singleton;
 
 /**
  * 
  * @author andreas muelder - Initial contribution and API
  * 
  */
-@Singleton
-public class SCTValidationJob extends Job implements IMarkerType {
+public class SCTValidationJob extends Job {
 
 	@Inject
 	private IResourceValidator validator;
-	@Inject
-	private MarkerCreator creator;
+
+	private IValidationIssueProcessor validationIssueProcessor;
 
 	private Resource resource;
 
@@ -98,7 +90,6 @@ public class SCTValidationJob extends Job implements IMarkerType {
 
 	@Override
 	public IStatus run(final IProgressMonitor monitor) {
-//		long t = System.currentTimeMillis();
 		try {
 			if (!resource.isLoaded())
 				return Status.CANCEL_STATUS;
@@ -120,44 +111,22 @@ public class SCTValidationJob extends Job implements IMarkerType {
 			try {
 				editingDomain.runExclusive(runner);
 			} catch (Throwable ex) {
-				//Since xtext 2.8 this may throw an OperationCanceledError
+				// Since xtext 2.8 this may throw an OperationCanceledError
 				return Status.CANCEL_STATUS;
 			}
 			final List<Issue> issues = runner.getResult();
 			if (issues == null)
 				return Status.CANCEL_STATUS;
-			final IFile target = WorkspaceSynchronizer.getFile(resource);
-			refreshMarkers(target, issues, monitor);
+
+			validationIssueProcessor.processIssues(issues, monitor);
 
 		} catch (Exception ex) {
 			ex.printStackTrace();
 			return new Status(IStatus.ERROR, DiagramActivator.PLUGIN_ID, ex.getMessage());
 		}
-//		System.out.println("Validation took " + (System.currentTimeMillis() - t));
 		return Status.OK_STATUS;
 	}
 
-	/**
-	 * Updates the markers. Execute the marker update in the UI thread, the
-	 * problem markers // will flicker otherwise
-	 */
-	private void refreshMarkers(final IFile target, final List<Issue> issues, final IProgressMonitor monitor) {
-		PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
-			public void run() {
-				try {
-					target.deleteMarkers(SCT_MARKER_TYPE, true, IResource.DEPTH_ZERO);
-					for (Issue issue : issues) {
-						if (monitor.isCanceled())
-							return;
-						creator.createMarker(issue, target, SCT_MARKER_TYPE);
-					}
-				} catch (CoreException e) {
-					throw new WrappedException(e);
-				}
-			}
-		});
-	}
-
 	/**
 	 * relinks the model before validation is executed
 	 */
@@ -183,5 +152,9 @@ public class SCTValidationJob extends Job implements IMarkerType {
 	public void setResource(Resource resource) {
 		this.resource = resource;
 	}
+	
+	public void setValidationIssueProcessor(IValidationIssueProcessor validationIssueProcessor) {
+		this.validationIssueProcessor = validationIssueProcessor;
+	}
 
 }