From 13f24e83f3b2b2b232fbefe6dabb676b7a973c95 Mon Sep 17 00:00:00 2001
From: Andreas Bayha <bayha@fortiss.org>
Date: Thu, 27 Apr 2023 14:32:05 +0200
Subject: [PATCH] ConstraintCheckerService: Added support for asynchronous
 checks

Extended the IConstraintCheckerService and MarkerService to allow
registration and execution of asynchronous constraint checkers which do
not block the constraint checker service.

Issue-ref: 4309
Issue-URL: af3#4309

Signed-off-by: Andreas Bayha <bayha@fortiss.org>
---
 .../tooling/kernel/ui/internal/.ratings       |  2 +-
 .../kernel/ui/internal/MarkerService.java     | 16 ++++
 .../fortiss/tooling/kernel/internal/.ratings  |  2 +-
 .../internal/ConstraintCheckerService.java    | 76 +++++++++++++++++++
 .../fortiss/tooling/kernel/service/.ratings   |  2 +-
 .../service/IConstraintCheckerService.java    | 30 ++++++++
 6 files changed, 125 insertions(+), 3 deletions(-)

diff --git a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/internal/.ratings b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/internal/.ratings
index d9c3d0574..c9bdb8362 100644
--- a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/internal/.ratings
+++ b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/internal/.ratings
@@ -1,6 +1,6 @@
 ActionService.java e29126b5947c9fd2f1d82bb87001b9d0ead50c3b GREEN
 ContextMenuService.java 9021e4eeb5d7be5d73d87e5947564bdf17f07b9d GREEN
-MarkerService.java 0bfe2c67638db4e506ea5dc7680765f2a8d632e1 GREEN
+MarkerService.java 92c9e7410a39040554473bd59d7b75a76ad47f97 YELLOW
 ModelEditorBindingService.java f304addb514cd2de443997e0b52cef7a3a9897bf GREEN
 ModelElementHandlerService.java 34adeef844bf98c69f1b9a7252f34d0a2b741b54 GREEN
 NavigatorService.java 1d773dde3791ddf7051616fe249558e7e307757d GREEN
diff --git a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/internal/MarkerService.java b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/internal/MarkerService.java
index 0bfe2c676..92c9e7410 100644
--- a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/internal/MarkerService.java
+++ b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/internal/MarkerService.java
@@ -181,6 +181,16 @@ public class MarkerService implements IMarkerService, IPersistencyServiceListene
 		IConstraintCheckerService ccs = IConstraintCheckerService.getInstance();
 		List<IConstraintViolation<? extends EObject>> checkResult =
 				ccs.performAllConstraintChecksRecursively(rootModelElement);
+
+		ccs.performAllAsynchronousConstrintChecksRecursively(rootModelElement, violations -> {
+			synchronized(violationCache) {
+				CacheEntry entry = getCacheEntry(element);
+				entry.addNewViolations(violations);
+			}
+
+			updateUI.schedule();
+		});
+
 		synchronized(violationCache) {
 			CacheEntry entry = getCacheEntry(element);
 			entry.updateCacheEntries(checkResult);
@@ -279,6 +289,12 @@ public class MarkerService implements IMarkerService, IPersistencyServiceListene
 				updateCacheEntries(List<IConstraintViolation<? extends EObject>> newViolations) {
 			clearCachedLists();
 			// update cache entries
+			addNewViolations(newViolations);
+		}
+
+		/** Add the given violations to the cache entry. */
+		public synchronized void
+				addNewViolations(List<IConstraintViolation<? extends EObject>> newViolations) {
 			for(IConstraintViolation<? extends EObject> violation : newViolations) {
 				getCachedList(violation.getSource()).add(violation);
 			}
diff --git a/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/internal/.ratings b/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/internal/.ratings
index 5d2058ca4..bed54e7c3 100644
--- a/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/internal/.ratings
+++ b/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/internal/.ratings
@@ -1,7 +1,7 @@
 CommandLineInterfaceService.java 6b5c94c52702f773c60b181eff52204ab379b248 GREEN
 CommandStackService.java 957bda69b5feb91f002aed4d25ed334e92801e7e GREEN
 ConnectionCompositorService.java 5a52f8a3e88c167ae6909c3d9eb3fb4706177e8b GREEN
-ConstraintCheckerService.java abd4667ceef11c47235e20a6566d8943f3417cf3 GREEN
+ConstraintCheckerService.java 7aeea15cffbe42b5819dd77f6a386aea18def37d YELLOW
 DummyTopLevelElement.java 21807bbdafec2e0ef28f0ee9090218f90bd73aee GREEN
 ElementCompositorService.java b1924b5b349118a70149cfac5b48544897d26e9e GREEN
 LoggingService.java da784259f7b456b54bf75c41ec268f64919ce78d GREEN
diff --git a/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/internal/ConstraintCheckerService.java b/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/internal/ConstraintCheckerService.java
index abd4667ce..7aeea15cf 100644
--- a/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/internal/ConstraintCheckerService.java
+++ b/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/internal/ConstraintCheckerService.java
@@ -17,12 +17,16 @@ package org.fortiss.tooling.kernel.internal;
 
 import static java.util.Collections.emptyList;
 import static java.util.Collections.unmodifiableMap;
+import static org.conqat.lib.commons.reflect.ReflectionUtils.performNearestClassLookup;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
 
 import org.eclipse.emf.ecore.EObject;
 import org.fortiss.tooling.kernel.extension.IConstraintChecker;
@@ -45,6 +49,14 @@ public class ConstraintCheckerService extends EObjectAwareServiceBase<IConstrain
 	/** The singleton instance. */
 	private static final ConstraintCheckerService INSTANCE = new ConstraintCheckerService();
 
+	/** Map of all classes to the respective registered asynchronous constraint checkers. */
+	Map<Class<?>, List<IConstraintChecker<EObject>>> asynchronousConstraintCheckers =
+			new HashMap<Class<?>, List<IConstraintChecker<EObject>>>();
+
+	/** Maps all constraint checkers to the last started Thread, each. */
+	Map<IConstraintChecker<? extends EObject>, Thread> asynchronousChecks =
+			new HashMap<IConstraintChecker<? extends EObject>, Thread>();
+
 	/** Returns singleton instance of the service. */
 	public static ConstraintCheckerService getInstance() {
 		return INSTANCE;
@@ -130,6 +142,50 @@ public class ConstraintCheckerService extends EObjectAwareServiceBase<IConstrain
 		}
 	}
 
+	/** {@inheritDoc} */
+	@Override
+	public void performAllAsynchronousConstrintChecksRecursively(EObject modelElement,
+			Consumer<List<IConstraintViolation<? extends EObject>>> addMarkers) {
+		performAllAsynchronousConstrintChecks(modelElement, addMarkers);
+
+		for(Iterator<EObject> iter = modelElement.eAllContents(); iter.hasNext();) {
+			performAllAsynchronousConstrintChecks(iter.next(), addMarkers);
+		}
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public void performAllAsynchronousConstrintChecks(EObject modelElement,
+			Consumer<List<IConstraintViolation<? extends EObject>>> addMarkers) {
+		List<IConstraintChecker<EObject>> asyncHandlers =
+				performNearestClassLookup(modelElement.getClass(), asynchronousConstraintCheckers);
+
+		if(asyncHandlers == null) {
+			return;
+		}
+
+		for(IConstraintChecker<EObject> checker : asyncHandlers) {
+			Thread checkerThread = asynchronousChecks.get(checker);
+			if(checkerThread != null && checkerThread.isAlive()) {
+				checkerThread.interrupt();
+			}
+
+			checkerThread = new Thread(new Runnable() {
+				@Override
+				public void run() {
+					List<IConstraintViolation<? extends EObject>> results =
+							new ArrayList<IConstraintViolation<? extends EObject>>();
+					results.addAll(checker.apply(modelElement));
+
+					addMarkers.accept(results);
+				}
+			});
+			asynchronousChecks.put(checker, checkerThread);
+
+			checkerThread.start();
+		}
+	}
+
 	/** {@inheritDoc} */
 	@Override
 	public List<IConstraintChecker<? extends EObject>>
@@ -179,4 +235,24 @@ public class ConstraintCheckerService extends EObjectAwareServiceBase<IConstrain
 	public IIntrospectionDetailsItem getDetailsItem() {
 		return new ConstraintCheckerKISSDetailsItem(unmodifiableMap(handlerMap));
 	}
+
+	/** {@inheritDoc} */
+	@SuppressWarnings("unchecked")
+	@Override
+	public <T extends EObject> void registerAsynchronousConstraintChecker(
+			IConstraintChecker<T> checker, Class<T> modelElementClass) {
+		if(checker == null) {
+			return;
+		}
+
+		List<IConstraintChecker<EObject>> checkers =
+				asynchronousConstraintCheckers.get(modelElementClass);
+
+		if(checkers == null) {
+			checkers = new ArrayList<IConstraintChecker<EObject>>();
+			asynchronousConstraintCheckers.put(modelElementClass, checkers);
+		}
+
+		checkers.add((IConstraintChecker<EObject>)checker);
+	}
 }
diff --git a/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/service/.ratings b/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/service/.ratings
index 7b59a6c18..32deb7d47 100644
--- a/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/service/.ratings
+++ b/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/service/.ratings
@@ -1,7 +1,7 @@
 ICommandLineInterfaceService.java c3e3ba08b2a1b8125b43abd1c29b7dc0a0be2b80 GREEN
 ICommandStackService.java 678dcd1a6ab435ed0870fa2a9ec48ce47f25a187 GREEN
 IConnectionCompositorService.java 0cdf4568b2cd3e95ea195df90a84699eff36442b GREEN
-IConstraintCheckerService.java 291e53297aaea213e07e78f63350938ee2c7b155 GREEN
+IConstraintCheckerService.java ef0f6fe43e3858b145ea10da6566fd0e3b00a48f YELLOW
 IEclipseResourceStorageService.java b1155ca15cd9474d4d533d6cb2725e8a22040ec9 GREEN
 IElementCompositorService.java acd462ec15f3bcc247b544b46ceebee971fe1408 GREEN
 IKernelIntrospectionSystemService.java 7005c3acb4c6f978729d93279c595765e94e38eb GREEN
diff --git a/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/service/IConstraintCheckerService.java b/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/service/IConstraintCheckerService.java
index 291e53297..ef0f6fe43 100644
--- a/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/service/IConstraintCheckerService.java
+++ b/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/service/IConstraintCheckerService.java
@@ -16,6 +16,7 @@
 package org.fortiss.tooling.kernel.service;
 
 import java.util.List;
+import java.util.function.Consumer;
 
 import org.eclipse.emf.ecore.EObject;
 import org.fortiss.tooling.kernel.extension.IConstraintChecker;
@@ -47,6 +48,31 @@ public interface IConstraintCheckerService {
 	List<IConstraintViolation<? extends EObject>>
 			performAllConstraintChecksRecursively(EObject modelElement);
 
+	/**
+	 * Performs all registered asynchronous constraint checks for the given modelElement and all its
+	 * contained child elements.
+	 * 
+	 * @param modelElement
+	 *            The {@link EObject} for which all checks shall be performed recursively.
+	 * @param addMarkers
+	 *            A {@link Consumer} to receive all constraint violations from the asynchronous
+	 *            checks.
+	 */
+	void performAllAsynchronousConstrintChecksRecursively(EObject modelElement,
+			Consumer<List<IConstraintViolation<? extends EObject>>> addMarkers);
+
+	/**
+	 * Performs all registered asynchronous constraint checks for the given modelElement.
+	 * 
+	 * @param modelElement
+	 *            The {@link EObject} for which all checks shall be performed.
+	 * @param addMarkers
+	 *            A {@link Consumer} to receive all constraint violations from the asynchronous
+	 *            checks.
+	 */
+	void performAllAsynchronousConstrintChecks(EObject modelElement,
+			Consumer<List<IConstraintViolation<? extends EObject>>> addMarkers);
+
 	/**
 	 * Performs all constraint checks only on the given model element and
 	 * returns the check results.
@@ -63,4 +89,8 @@ public interface IConstraintCheckerService {
 
 	/** Registers the given checker with the service. */
 	void registerConstraintChecker(IConstraintChecker<EObject> checker, Class<?> modelElementClass);
+
+	/** Registers the given checker to be run asynchronously in the service. */
+	<T extends EObject> void registerAsynchronousConstraintChecker(IConstraintChecker<T> checker,
+			Class<T> modelElementClass);
 }
-- 
GitLab