From ebd4c9a2e525d883d95c0054ccdad71948612fe2 Mon Sep 17 00:00:00 2001
From: Eddie Groh <groh@fortiss.org>
Date: Mon, 10 Jul 2023 18:36:27 +0200
Subject: [PATCH] Refactored metric collection to proper provider pattern

Issue-Ref: 4310
Issue-Url: https://git.fortiss.org/af3/af3/-/issues/4310

Signed-off-by: Eddie Groh <groh@fortiss.org>
---
 .../META-INF/MANIFEST.MF                      |  2 +
 .../quality/ui/ModelQualityUIActivator.java   |  6 +-
 .../META-INF/MANIFEST.MF                      |  1 +
 .../ext/quality/AF3QualityActivator.java      |  4 +-
 .../quality/HierarchicElementProvider.java    | 89 +++---------------
 .../tooling/ext/quality/IMetricProvider.java  |  3 +-
 .../quality/service/IModelQualityService.java |  4 +-
 .../quality/service/ModelQualityService.java  | 92 +++++++++++++++++--
 8 files changed, 112 insertions(+), 89 deletions(-)

diff --git a/org.fortiss.tooling.ext.quality.ui/META-INF/MANIFEST.MF b/org.fortiss.tooling.ext.quality.ui/META-INF/MANIFEST.MF
index 1da1df17e..22556059e 100644
--- a/org.fortiss.tooling.ext.quality.ui/META-INF/MANIFEST.MF
+++ b/org.fortiss.tooling.ext.quality.ui/META-INF/MANIFEST.MF
@@ -14,3 +14,5 @@ Require-Bundle: org.eclipse.ui.ide;visibility:=reexport,
  org.eclipse.emf.ecore,
  org.fortiss.af3.project;bundle-version="2.23.0"
 Export-Package: org.fortiss.tooling.ext.quality.ui.view.fx
+Bundle-Activator: org.fortiss.tooling.ext.quality.ui.ModelQualityUIActivator
+Bundle-ActivationPolicy: lazy
diff --git a/org.fortiss.tooling.ext.quality.ui/src/org/fortiss/tooling/ext/quality/ui/ModelQualityUIActivator.java b/org.fortiss.tooling.ext.quality.ui/src/org/fortiss/tooling/ext/quality/ui/ModelQualityUIActivator.java
index 0248951db..2a6eea8fe 100644
--- a/org.fortiss.tooling.ext.quality.ui/src/org/fortiss/tooling/ext/quality/ui/ModelQualityUIActivator.java
+++ b/org.fortiss.tooling.ext.quality.ui/src/org/fortiss/tooling/ext/quality/ui/ModelQualityUIActivator.java
@@ -23,6 +23,7 @@ public class ModelQualityUIActivator extends Plugin {
 	public void start(BundleContext context) throws Exception {
 		super.start(context);
 		plugin = this;
+		System.out.println("[Plugin] " + PLUGIN_ID + " started.");
 	}
 
 	/** {@inheritDoc} */
@@ -35,7 +36,7 @@ public class ModelQualityUIActivator extends Plugin {
 	/**
 	 * Returns the shared instance.
 	 *
-	 * @return The shared instance of ToolingReuseActivator
+	 * @return The shared instance of ModelQualityUIActivator
 	 */
 	public static ModelQualityUIActivator getDefault() {
 		return plugin;
@@ -44,7 +45,8 @@ public class ModelQualityUIActivator extends Plugin {
 	/**
 	 * Returns the image descriptor for the given icon file.
 	 *
-	 * @param path The path to the icon file of the image descriptor
+	 * @param path
+	 *            The path to the icon file of the image descriptor
 	 * @return The image descriptor
 	 */
 	public static ImageDescriptor getImageDescriptor(String path) {
diff --git a/org.fortiss.tooling.ext.quality/META-INF/MANIFEST.MF b/org.fortiss.tooling.ext.quality/META-INF/MANIFEST.MF
index 9b9be8e7a..31fb47eef 100644
--- a/org.fortiss.tooling.ext.quality/META-INF/MANIFEST.MF
+++ b/org.fortiss.tooling.ext.quality/META-INF/MANIFEST.MF
@@ -15,4 +15,5 @@ Require-Bundle: org.eclipse.core.runtime,
 Export-Package: org.fortiss.tooling.ext.quality,
  org.fortiss.tooling.ext.quality.data,
  org.fortiss.tooling.ext.quality.service
+Bundle-ActivationPolicy: lazy
 
diff --git a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/AF3QualityActivator.java b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/AF3QualityActivator.java
index 9debc4bb2..3c8aaf351 100644
--- a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/AF3QualityActivator.java
+++ b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/AF3QualityActivator.java
@@ -41,8 +41,8 @@ public class AF3QualityActivator extends Plugin {
 		plugin = this;
 		System.out.println("[Plugin] " + PLUGIN_ID + " started.");
 		ModelQualityService.getInstance().startService();
-		IModelQualityService.getInstance().registerMetricProvider(
-				new HierarchicElementProvider(), IHierarchicElement.class);
+		IModelQualityService.getInstance().registerMetricProvider(new HierarchicElementProvider(),
+				IHierarchicElement.class);
 	}
 
 	/** {@inheritDoc} */
diff --git a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/HierarchicElementProvider.java b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/HierarchicElementProvider.java
index 7008b8eb3..16b732c97 100644
--- a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/HierarchicElementProvider.java
+++ b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/HierarchicElementProvider.java
@@ -15,13 +15,9 @@
 +--------------------------------------------------------------------------*/
 package org.fortiss.tooling.ext.quality;
 
-import java.util.Optional;
-
-import org.eclipse.emf.common.util.EList;
 import org.fortiss.tooling.base.model.base.EntryConnectorBase;
 import org.fortiss.tooling.base.model.base.ExitConnectorBase;
 import org.fortiss.tooling.base.model.element.IHierarchicElement;
-import org.fortiss.tooling.ext.quality.data.MetricDataManager;
 import org.fortiss.tooling.ext.quality.data.MetricKey;
 import org.fortiss.tooling.ext.quality.data.MetricTreeNode;
 import org.fortiss.tooling.kernel.model.IIdLabeled;
@@ -56,64 +52,26 @@ public class HierarchicElementProvider implements IMetricProvider<IHierarchicEle
 
 	/** {@inheritDoc} */
 	@Override
-	public void apply(MetricDataManager manager, MetricTreeNode node,
-			IHierarchicElement currentElement) {
+	public void collectMetrics(MetricTreeNode node, IHierarchicElement currentElement) {
 
 		var metrics = node.getStoredMetrics();
-		boolean isElementCommentable = currentElement instanceof INamedCommentedElement;
-		metrics.put(MetricKey.NUMBER_OF_TOTAL_COMMENTABLE_ELEMENTS,
-				isElementCommentable ? 1.0 : 0.0);
-		String comment =
-				isElementCommentable ? ((INamedCommentedElement)currentElement).getComment() : null;
-		metrics.put(MetricKey.NUMBER_OF_TOTAL_COMMENTED_ELEMENTS,
-				comment != null && comment != "" ? 1.0 : 0.0);
-		if(isElementCommentable) {
-			node.setName(((INamedCommentedElement)currentElement).getName());
-		} else {
-			node.setName("Unnamable Element");
-		}
-
-		// Check if any of the specifications is an IHierarchicElement
-		// This is for example the case with an StateAutomaton
-		Optional<IHierarchicElement> hierarchicElementOptional = currentElement.getSpecifications()
-				.stream().filter(s -> s instanceof IHierarchicElement)
-				.map(s -> (IHierarchicElement)s).findAny();
-		if(hierarchicElementOptional.isPresent()) {
-			// Do not collect statistics for the current element, but instead collect them for the
-			// specification
-			IHierarchicElement specificationElement = hierarchicElementOptional.get();
-			EList<IHierarchicElement> containedElements =
-					specificationElement.getContainedElements();
-			if(containedElements.size() == 1) {
-				// Add reference from the element to this, so the lookups works as expected
-				manager.getHierarchicLookupTable().put(specificationElement, node);
-				// Skip the specification element to get a more useful tree structure
-				this.applyMetrics(manager, node, containedElements.get(0));
+		if(node.getName() == null) {
+			boolean isElementCommentable = currentElement instanceof INamedCommentedElement;
+			metrics.put(MetricKey.NUMBER_OF_TOTAL_COMMENTABLE_ELEMENTS,
+					isElementCommentable ? 1.0 : 0.0);
+			String comment = isElementCommentable
+					? ((INamedCommentedElement)currentElement).getComment() : null;
+			metrics.put(MetricKey.NUMBER_OF_TOTAL_COMMENTED_ELEMENTS,
+					comment != null && comment != "" ? 1.0 : 0.0);
+			if(isElementCommentable) {
+				node.setName(((INamedCommentedElement)currentElement).getName());
 			} else {
-				this.applyMetrics(manager, node, specificationElement);
+				node.setName("Unnamable Element");
 			}
-			// Add reference from the element to this, so the lookups works as expected
-			manager.getHierarchicLookupTable().put(currentElement, node);
-			return;
-		}
-		this.applyMetrics(manager, node, currentElement);
-	}
-
-	/**
-	 * @param manager
-	 *            for saving the lookup data
-	 * @param node
-	 *            for saving the metric data
-	 * @param currentElement
-	 *            the element on which to collect data
-	 */
-	private void applyMetrics(MetricDataManager manager, MetricTreeNode node,
-			IHierarchicElement currentElement) {
 
-		var metrics = node.getStoredMetrics();
-
-		metrics.put(MetricKey.UNQIUE_ID, (currentElement instanceof IIdLabeled)
-				? ((IIdLabeled)currentElement).getId() : -1.0);
+			metrics.put(MetricKey.UNQIUE_ID, (currentElement instanceof IIdLabeled)
+					? ((IIdLabeled)currentElement).getId() : -1.0);
+		}
 
 		var connectors = currentElement.getConnectors();
 		metrics.put(MetricKey.NUMBER_OF_CONNECTORS, (double)connectors.size());
@@ -132,22 +90,5 @@ public class HierarchicElementProvider implements IMetricProvider<IHierarchicEle
 
 		metrics.put(MetricKey.NUMBER_OF_TOTAL_LEAF_ELEMENTS,
 				currentElement.getContainedElements().size() == 0 ? 1.0 : 0.0);
-
-		for(IHierarchicElement containedElement : currentElement.getContainedElements()) {
-			MetricTreeNode child = new MetricTreeNode();
-			node.getChildren().add(child);
-			apply(manager, child, containedElement);
-
-			for(MetricKey key : new MetricKey[] {MetricKey.NUMBER_OF_TOTAL_CONNECTORS,
-					MetricKey.NUMBER_OF_TOTAL_ENTRY_CONNECTORS,
-					MetricKey.NUMBER_OF_TOTAL_EXIT_CONNECTORS, MetricKey.NUMBER_OF_TOTAL_ELEMENTS,
-					MetricKey.NUMBER_OF_TOTAL_COMMENTABLE_ELEMENTS,
-					MetricKey.NUMBER_OF_TOTAL_COMMENTED_ELEMENTS,
-					MetricKey.NUMBER_OF_TOTAL_LEAF_ELEMENTS}) {
-				metrics.merge(key, child.getStoredMetrics().get(key), Double::sum);
-			}
-		}
-
-		manager.getHierarchicLookupTable().put(currentElement, node);
 	}
 }
diff --git a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/IMetricProvider.java b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/IMetricProvider.java
index 01f3d4d7d..43606a0df 100644
--- a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/IMetricProvider.java
+++ b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/IMetricProvider.java
@@ -16,7 +16,6 @@
 package org.fortiss.tooling.ext.quality;
 
 import org.eclipse.emf.ecore.EObject;
-import org.fortiss.tooling.ext.quality.data.MetricDataManager;
 import org.fortiss.tooling.ext.quality.data.MetricTreeNode;
 import org.fortiss.tooling.ext.quality.service.IModelQualityService;
 import org.fortiss.tooling.kernel.service.base.IEObjectAware;
@@ -31,5 +30,5 @@ import org.fortiss.tooling.kernel.service.base.IEObjectAware;
 public interface IMetricProvider<C extends EObject> extends IEObjectAware<EObject> {
 
 	/** Applies the IMetricProvider to the given model element. */
-	void apply(MetricDataManager manager, MetricTreeNode node, C element);
+	void collectMetrics(MetricTreeNode node, C element);
 }
diff --git a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/service/IModelQualityService.java b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/service/IModelQualityService.java
index 5400dc318..025bc2e75 100644
--- a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/service/IModelQualityService.java
+++ b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/service/IModelQualityService.java
@@ -15,6 +15,7 @@
 +--------------------------------------------------------------------------*/
 package org.fortiss.tooling.ext.quality.service;
 
+import org.eclipse.emf.ecore.EObject;
 import org.fortiss.tooling.ext.quality.IMetricProvider;
 import org.fortiss.tooling.kernel.extension.data.ITopLevelElement;
 
@@ -30,7 +31,8 @@ public interface IModelQualityService {
 	}
 
 	/** Registers the metric provider with the service. */
-	void registerMetricProvider(IMetricProvider<?> provider, Class<?> modelElementClass);
+	<T extends EObject> void registerMetricProvider(IMetricProvider<T> provider,
+			Class<T> modelElementClass);
 
 	/** analyses the metrics and processes them */
 	void performMetricAnalysis(ITopLevelElement top);
diff --git a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/service/ModelQualityService.java b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/service/ModelQualityService.java
index f02363a74..96f6737ad 100644
--- a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/service/ModelQualityService.java
+++ b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/service/ModelQualityService.java
@@ -16,7 +16,6 @@
 package org.fortiss.tooling.ext.quality.service;
 
 import static java.util.Collections.emptyList;
-import static org.fortiss.tooling.kernel.utils.EcoreUtils.getFirstChildWithType;
 
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
@@ -33,13 +32,13 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Optional;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import org.eclipse.emf.common.util.EList;
 import org.eclipse.emf.ecore.EObject;
 import org.fortiss.tooling.base.model.element.IHierarchicElement;
-import org.fortiss.tooling.ext.quality.HierarchicElementProvider;
 import org.fortiss.tooling.ext.quality.IMetricProvider;
 import org.fortiss.tooling.ext.quality.data.MetricDataManager;
 import org.fortiss.tooling.ext.quality.data.MetricKey;
@@ -91,8 +90,8 @@ public class ModelQualityService extends EObjectAwareServiceBase<IMetricProvider
 
 	/** Registers the migration provider with the service. */
 	@Override
-	public void registerMetricProvider(IMetricProvider<?> provider,
-			Class<?> modelElementClass) {
+	public <T extends EObject> void registerMetricProvider(IMetricProvider<T> provider,
+			Class<T> modelElementClass) {
 		addHandler(modelElementClass, provider);
 	}
 
@@ -110,7 +109,7 @@ public class ModelQualityService extends EObjectAwareServiceBase<IMetricProvider
 	protected List<IMetricProvider<?>> getAllMetricProviders(EObject input) {
 		List<IMetricProvider<?>> providers = new ArrayList<>();
 		for(Entry<Class<?>, List<IMetricProvider<?>>> migEntry : handlerMap.entrySet()) {
-			if(getFirstChildWithType(input, migEntry.getKey()) != null) {
+			if(migEntry.getKey().isAssignableFrom(input.getClass())) {
 				providers.addAll(migEntry.getValue());
 			}
 		}
@@ -137,10 +136,9 @@ public class ModelQualityService extends EObjectAwareServiceBase<IMetricProvider
 				var root_nodes = metricDataManagerInstance.getRootNodes();
 
 				// Get first element, there should not be any other elements
-				IHierarchicElement firstNode = elements.get(0);
+				IHierarchicElement firstElement = elements.get(0);
 				MetricTreeNode root_node = new MetricTreeNode();
-				HierarchicElementProvider hes = new HierarchicElementProvider();
-				hes.apply(metricDataManagerInstance, root_node, firstNode);
+				recursivlyCollectMetrics(root_node, firstElement);
 				root_nodes.put(rootElement, root_node);
 			}
 		}
@@ -148,6 +146,84 @@ public class ModelQualityService extends EObjectAwareServiceBase<IMetricProvider
 		metricExtractionToCSV(metricDataManagerInstance.getRootNodes());
 	}
 
+	/**
+	 * @param node
+	 *            to which the data is collected
+	 * @param currentElement
+	 *            element to collect the data from
+	 */
+	private void recursivlyCollectMetrics(MetricTreeNode node, IHierarchicElement currentElement) {
+
+		var manager = metricDataManagerInstance;
+
+		collectMetrics(node, currentElement);
+
+		// Check if any of the specifications is an IHierarchicElement
+		// This is for example the case with an StateAutomaton
+		Optional<IHierarchicElement> hierarchicElementOptional = currentElement.getSpecifications()
+				.stream().filter(s -> s instanceof IHierarchicElement)
+				.map(s -> (IHierarchicElement)s).findAny();
+		if(hierarchicElementOptional.isPresent()) {
+			// Do not collect statistics for the current element, but instead collect them for the
+			// specification
+			IHierarchicElement specificationElement = hierarchicElementOptional.get();
+			EList<IHierarchicElement> containedElements =
+					specificationElement.getContainedElements();
+			if(containedElements.size() == 1) {
+				// Add reference from the element to this, so the lookups works as expected
+				manager.getHierarchicLookupTable().put(specificationElement, node);
+				// Skip the specification element to get a more useful tree structure
+				recursivlyCollectMetrics(node, containedElements.get(0));
+			} else {
+				recursivlyCollectMetrics(node, specificationElement);
+			}
+			// Add reference from the element to this, so the lookups works as expected
+			manager.getHierarchicLookupTable().put(currentElement, node);
+		} else {
+
+			// Iterate over all children and
+			for(IHierarchicElement containedElement : currentElement.getContainedElements()) {
+				MetricTreeNode child = new MetricTreeNode();
+				node.getChildren().add(child);
+				recursivlyCollectMetrics(child, containedElement);
+
+				for(MetricKey key : new MetricKey[] {MetricKey.NUMBER_OF_TOTAL_CONNECTORS,
+						MetricKey.NUMBER_OF_TOTAL_ENTRY_CONNECTORS,
+						MetricKey.NUMBER_OF_TOTAL_EXIT_CONNECTORS,
+						MetricKey.NUMBER_OF_TOTAL_ELEMENTS,
+						MetricKey.NUMBER_OF_TOTAL_COMMENTABLE_ELEMENTS,
+						MetricKey.NUMBER_OF_TOTAL_COMMENTED_ELEMENTS,
+						MetricKey.NUMBER_OF_TOTAL_LEAF_ELEMENTS}) {
+					node.getStoredMetrics().merge(key, child.getStoredMetrics().get(key),
+							Double::sum);
+				}
+			}
+
+			manager.getHierarchicLookupTable().put(currentElement, node);
+		}
+	}
+
+	/**
+	 * @param node
+	 *            to which the data is collected
+	 * @param element
+	 *            element to collect the data from
+	 */
+	@SuppressWarnings("unchecked")
+	private void collectMetrics(MetricTreeNode node, IHierarchicElement element) {
+
+		List<IMetricProvider<?>> providers = getAllMetricProviders(element);
+
+		if(providers.isEmpty()) {
+			System.out.println("KATASTROPHE");
+			getAllMetricProviders(element);
+		}
+
+		for(IMetricProvider<?> provider : providers) {
+			((IMetricProvider<EObject>)provider).collectMetrics(node, element);
+		}
+	}
+
 	/** */
 	@Override
 	protected String getExtensionPointName() {
-- 
GitLab