Skip to content
Snippets Groups Projects
Commit 4ef727a3 authored by Simon Barner's avatar Simon Barner
Browse files

Merge branch '4310' into 'master'

Setting up Metric extraction plugin for AF3 :  Issue 4310

See merge request !210
parents b41ae985 105a0ee4
No related branches found
No related tags found
1 merge request!210Setting up Metric extraction plugin for AF3 : Issue 4310
Showing
with 1218 additions and 0 deletions
/*-------------------------------------------------------------------------+
| Copyright 2023 fortiss GmbH |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
+--------------------------------------------------------------------------*/
package org.fortiss.tooling.ext.quality.ui.view.fx;
import org.eclipse.ui.part.ViewPart;
import org.fortiss.tooling.common.ui.javafx.AF3FXViewPart;
import javafx.scene.Scene;
/**
* {@link ViewPart} for the FX implementation of the metrics view.
*
* @author groh
*/
public class ModelQualityFXViewPart extends AF3FXViewPart implements IModelQualityViewPart {
/** The FX Controller for this view. */
private static final ModelQualityFXController VIEW_CONTROLLER = new ModelQualityFXController();
/** Constructor. */
public ModelQualityFXViewPart() throws Exception {
super(VIEW_CONTROLLER, null);
}
/**
* Returns the initialized {@link ModelQualityFXController} for the quality
* version of the {@link AF3FXViewPart}.
*
* @return The view controller for the metrics view
*/
public static ModelQualityFXController getMetricsFXController() {
return VIEW_CONTROLLER;
}
/** {@inheritDoc} */
@Override
protected Scene createFxScene() {
Scene scene = super.createFxScene();
getSite().getWorkbenchWindow().getSelectionService().addSelectionListener(VIEW_CONTROLLER);
return scene;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="build"/>
</classpath>
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.fortiss.tooling.ext.quality</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.systemfocus.tooling.emfgeneration.git.EcoreBuilderGIT</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.systemfocus.tooling.codereview.builder.CodeReviewBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.systemfocus.tooling.codereview.builder.RemoveWarningsBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.systemfocus.tooling.codereview.builder.GuidelinesCheckBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.systemfocus.tooling.codereview.nature.CodeReviewNature</nature>
</natures>
</projectDescription>
Subproject commit 310d1c04f28f6252d5a02dd8fde1b76ae4a4da51
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Model Quality
Bundle-SymbolicName: org.fortiss.tooling.ext.quality;singleton:=true
Bundle-Version: 2.23.0.qualifier
Automatic-Module-Name: org.fortiss.tooling.ext.quality
Bundle-RequiredExecutionEnvironment: JavaSE-11
Bundle-Vendor: fortiss GmbH
Bundle-Activator: org.fortiss.tooling.ext.quality.ModelQualityActivator
Require-Bundle: org.eclipse.core.runtime,
org.eclipse.emf.ecore;visibility:=reexport,
org.fortiss.tooling.base;visibility:=reexport,
org.fortiss.tooling.kernel;visibility:=reexport
Export-Package: org.fortiss.tooling.ext.quality,
org.fortiss.tooling.ext.quality.data,
org.fortiss.tooling.ext.quality.service
Bundle-ActivationPolicy: lazy
# (c) 2023 fortiss GmbH
bin.includes = .,\
META-INF/,\
plugin.properties,\
plugin.xml
source.. = src/
output.. = build/
documentation.html b66c2bfb495047a8e4b680b02231ab2fdb34cec3 GREEN
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
</head>
<body lang="en-US" dir="ltr">
<h1>Developer Documentation for <i>Quality Functionality
(</i><i>org.fortiss.tooling.ext.quality</i><i>)</i></h1>
<h2>Metrics List </h2>
<p>Below is a list of all collected metrics. They are represented by the enum <code> org.fortiss.tooling.ext.quality.data.MetricKey</code></p>
<p>They are categorized by the respective provider collecting the metric</p>
<table border="1">
<thead>
<tr>
<th>Provider</th>
<th>Qualified Name</th>
<th>Metric</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>HierarchicElementMetricProvider</td>
<td>org.fortiss.tooling.ext.quality.HierarchicElementMetricProvider</td>
<td>UNIQUE_ID</td>
<td>Integer</td>
<td>This is an unique identifier for each element as specified in org.fortiss.tooling.kernel.model.IIdLabeled.</td>
</tr>
<tr>
<td>HierarchicElementMetricProvider</td>
<td>org.fortiss.tooling.ext.quality.HierarchicElementMetricProvider</td>
<td>NUMBER_OF_CONNECTORS</td>
<td>Integer</td>
<td>Number of connectors in the respective element.</td>
</tr>
<tr>
<td>HierarchicElementMetricProvider</td>
<td>org.fortiss.tooling.ext.quality.HierarchicElementMetricProvider</td>
<td>NUMBER_OF_CONTAINED_ELEMENTS</td>
<td>Integer</td>
<td>Number of entries in the containedElements list.</td>
</tr>
<tr>
<td>HierarchicElementMetricProvider</td>
<td>org.fortiss.tooling.ext.quality.HierarchicElementMetricProvider</td>
<td>NUMBER_OF_CONNECTIONS</td>
<td>Integer</td>
<td>Number of connections in this element. List of aggregated connection model elements. Usually aggregates all connections of its direct sub-structure.</td>
</tr>
<tr>
<td>HierarchicElementMetricProvider</td>
<td>org.fortiss.tooling.ext.quality.HierarchicElementMetricProvider</td>
<td>NUMBER_OF_TOTAL_CONNECTORS</td>
<td>Integer</td>
<td>Total number of connectors in this element and all contained elements.</td>
</tr>
<tr>
<td>HierarchicElementMetricProvider</td>
<td>org.fortiss.tooling.ext.quality.HierarchicElementMetricProvider</td>
<td>NUMBER_OF_TOTAL_ENTRY_CONNECTORS</td>
<td>Integer</td>
<td>Sum of all connectors which implement org.fortiss.tooling.base.model.base.EntryConnectorBase.</td>
</tr>
<tr>
<td>HierarchicElementMetricProvider</td>
<td>org.fortiss.tooling.ext.quality.HierarchicElementMetricProvider</td>
<td>NUMBER_OF_TOTAL_EXIT_CONNECTORS</td>
<td>Integer</td>
<td>Sum of all connectors which implement org.fortiss.tooling.base.model.base.ExitConnectorBase. Usually, the sum over this and NUMBER_OF_TOTAL_ENTRY_CONNECTORS should equal NUMBER_OF_TOTAL_CONNECTORS.</td>
</tr>
<tr>
<td>HierarchicElementMetricProvider</td>
<td>org.fortiss.tooling.ext.quality.HierarchicElementMetricProvider</td>
<td>NUMBER_OF_TOTAL_ELEMENTS</td>
<td>Integer</td>
<td>Total number of elements contained in this element. Includes the element for which this metric is recorded.</td>
</tr>
<tr>
<td>HierarchicElementMetricProvider</td>
<td>org.fortiss.tooling.ext.quality.HierarchicElementMetricProvider</td>
<td>NUMBER_OF_TOTAL_COMMENTABLE_ELEMENTS</td>
<td>Integer</td>
<td>Total number of elements implementing org.fortiss.tooling.kernel.model.INamedCommentedElement in this element. Includes the element for which this metric is recorded. Never exceeds NUMBER_OF_TOTAL_ELEMENTS.</td>
</tr>
<tr>
<td>HierarchicElementMetricProvider</td>
<td>org.fortiss.tooling.ext.quality.HierarchicElementMetricProvider</td>
<td>NUMBER_OF_TOTAL_COMMENTED_ELEMENTS</td>
<td>Integer</td>
<td>Total number of elements which are commentable and do not have null or an empty string as a comment.</td>
</tr>
<tr>
<td>HierarchicElementMetricProvider</td>
<td>org.fortiss.tooling.ext.quality.HierarchicElementMetricProvider</td>
<td>NUMBER_OF_TOTAL_LEAF_ELEMENTS</td>
<td>Integer</td>
<td>Total number of elements contained in this element which do not contain elements. Includes self.</td>
</tr>
<tr>
<td>GraphMetricsProvider</td>
<td>org.fortiss.tooling.ext.quality.GraphMetricsProvider</td>
<td>BETWEENESS_CENTRALITY</td>
<td>Double</td>
<td>Value of the betweenness centrality of this element embedded in a graph of consisting of all elements which are contained in the element which contains this element, as well as all neighbors of these elements.</td>
</tr>
<tr>
<td>GraphMetricsProvider</td>
<td>org.fortiss.tooling.ext.quality.GraphMetricsProvider</td>
<td>BETWEENESS_CENTRALITY_RECURSIVELY</td>
<td>Double</td>
<td>Same as above, but all connections are resolved to leaf elements, i.e. elements which do not contain other elements.</td>
</tr>
<tr>
<td>GraphMetricsProvider</td>
<td>org.fortiss.tooling.ext.quality.GraphMetricsProvider</td>
<td>CLUSTERING_COEFFICIENT</td>
<td>Double</td>
<td>Clustering coefficient of this element. Ignores connection direction and resolves all neighbors to leaf elements.</td>
</tr>
<tr>
<td>ModelQualityService</td>
<td>org.fortiss.tooling.ext.quality.service.ModelQualityService</td>
<td>NESTING_LEVEL</td>
<td>Integer</td>
<td>The depth of this element. Defined by the number of elements which have to be traversed to reach this element from the root.</td>
</tr>
<tr>
<td>ModelQualityService</td>
<td>org.fortiss.tooling.ext.quality.service.ModelQualityService</td>
<td>CONSTRAINT_VIOLATIONS_ERROR</td>
<td>Integer</td>
<td>Number of constraint violations of the severity error which have this element as source.</td>
</tr>
<tr>
<td>ModelQualityService</td>
<td>org.fortiss.tooling.ext.quality.service.ModelQualityService</td>
<td>CONSTRAINT_VIOLATIONS_WARNING</td>
<td>Integer</td>
<td>Number of constraint violations of the severity warning which have this element as source.</td>
</tr>
</tbody>
</table>
<h2></h2>
The included packages are:
<ul>
<li><i>org.fortiss.tooling.ext.quality</i>: containing the GraphMetrics and HierarchicElement providers. The other providers are therefore located on the
necessary plugin level to support the extraction of meta information from other elements. All metric providers implement the IMetricProvider interface.</li>
<li><i>org.fortiss.tooling.ext.quality.data</i>: contains the classes to handle the data structure and map of metrics. It manages the data during runtime
so the visualizations can receive the necessary data.</li>
<li><i>org.fortiss.tooling.ext.quality.service</i>: The interface and implementation of the registered service.</li>
<li><i>org.fortiss.tooling.ext.quality.storage</i>: The classes here manage the csv extraction to archive the metrics.</li>
</ul>
</body>
</html>
\ No newline at end of file
<!-- (c) 2023 fortiss GmbH -->
pluginName = Model Quality
providerName = fortiss GmbH
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
</plugin>
GraphMetricsProvider.java ce70c838b887df0bdf776f462b23cf594c700822 GREEN
HierarchicElementMetricProvider.java 966b3aeef92475034fc35e68ba5344688988b88a GREEN
IMetricProvider.java 99fc8993b0e65b2f8757978eeb0481d912f5608c GREEN
IMetricUpdateListener.java c24dc7c0f282623bbf1eefac1fbbb6752c97ddf0 GREEN
ModelQualityActivator.java 6d5f0794aa48670cf45fb405bd7641bce6c1fec4 GREEN
/*-------------------------------------------------------------------------+
| Copyright 2023 fortiss GmbH |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
+--------------------------------------------------------------------------*/
package org.fortiss.tooling.ext.quality;
import static org.fortiss.tooling.kernel.utils.EcoreUtils.pickInstanceOf;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.Stack;
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.IConnection;
import org.fortiss.tooling.base.model.element.IConnector;
import org.fortiss.tooling.base.model.element.IHierarchicElement;
import org.fortiss.tooling.ext.quality.data.MetricData;
import org.fortiss.tooling.ext.quality.data.MetricKey;
import org.fortiss.tooling.ext.quality.data.MetricTreeNode;
/**
* Supplies methods to compute certain graph metrics.
*
* @author groh
*/
// TODO (#4332) Restructure GraphMetricsProvider
public class GraphMetricsProvider {
/**
* Creates a list of nodes contained in the graph centered around the provided element.
* It will contain all elements contained within the provided element as well as all elements
* connected to the provided element.
*
* @param scopeElement
* element defining the scope of the graph
* @param recursively
* if true, everything is resolved to leaf elements
* @return node list
*/
private static Set<IHierarchicElement> getLocalGraphView(IHierarchicElement scopeElement,
boolean recursively) {
// This ensures that elements inside this element communicating with elements outside
// this element are properly recognized
Set<IHierarchicElement> graphNodes = new HashSet<>();
if(recursively) {
recursivelyGetLeafElements(scopeElement, graphNodes);
} else {
graphNodes.addAll(scopeElement.getContainedElements());
}
// Collect all elements to which currentElement has a outgoing connection
for(ExitConnectorBase exitConnector : pickInstanceOf(ExitConnectorBase.class,
scopeElement.getConnectors())) {
for(IConnection exitConnection : exitConnector.getOutgoing()) {
graphNodes.add(exitConnection.getTarget().getOwner());
}
}
// Collect all elements to which currentElement has a incoming connection
for(EntryConnectorBase entryConnector : pickInstanceOf(EntryConnectorBase.class,
scopeElement.getConnectors())) {
for(IConnection entryConnection : entryConnector.getIncoming()) {
graphNodes.add(entryConnection.getSource().getOwner());
}
}
return graphNodes;
}
/**
* Get all leafs inside the provided element.
*
* @param element
* the element containing leafs
* @param allElements
* list where the leafs are saved
*/
private static void recursivelyGetLeafElements(IHierarchicElement element,
Set<IHierarchicElement> allElements) {
allElements.add(element);
for(IHierarchicElement containedElement : element.getContainedElements()) {
recursivelyGetLeafElements(containedElement, allElements);
}
}
/**
* Follows outgoing connections until the connector has no further outgoing connections and then
* saves the owner of this connector to the provided list.
*
* @param connector
* starting point for search
* @param allElements
* list where the owners are collected
*/
private static void recursivlyFollowOutgoingConnection(IConnector connector,
Set<IHierarchicElement> allElements) {
EList<IConnection> outgoingConnections = connector.getOutgoing();
if(outgoingConnections.isEmpty()) {
allElements.add(connector.getOwner());
return;
}
for(IConnection outgoingConnection : outgoingConnections) {
recursivlyFollowOutgoingConnection(outgoingConnection.getTarget(), allElements);
}
}
/**
* Follows incoming connections until the connector has no further incoming connections and
* then saves the owner of this connector to the provided list.
*
* @param connector
* starting point for search
* @param allElements
* list where the owners are collected
*/
private static void recursivlyFollowIncomingConnection(IConnector connector,
Set<IHierarchicElement> allElements) {
EList<IConnection> incomingConnections = connector.getIncoming();
if(incomingConnections.isEmpty()) {
allElements.add(connector.getOwner());
return;
}
for(IConnection incomingConnection : incomingConnections) {
recursivlyFollowIncomingConnection(incomingConnection.getSource(), allElements);
}
}
/**
* Collects all neighbors regardless of the direction of connection.
*
* @param element
* of which the neighbors should be collected
* @return set of the neighbors
*/
private static Set<IHierarchicElement> getUndirectedNeighbors(IHierarchicElement element) {
Set<IHierarchicElement> neighbors = new HashSet<>();
for(ExitConnectorBase exitConnectors : pickInstanceOf(ExitConnectorBase.class,
element.getConnectors())) {
recursivlyFollowOutgoingConnection(exitConnectors, neighbors);
}
for(EntryConnectorBase entryConnectors : pickInstanceOf(EntryConnectorBase.class,
element.getConnectors())) {
recursivlyFollowIncomingConnection(entryConnectors, neighbors);
}
return neighbors;
}
/**
* Calculates the clustering coefficient based on the neighborhood.
* See, e.g., https://en.wikipedia.org/wiki/Clustering_coefficient
* Clustering coefficient C for a node V is counting the triangles divided by the "number of
* neighbors of "V" times "number of neighbors of V - 1" which results in the potential number
* of maximum links between the nodes
*
* @param element
* of which the coefficient shall be calculated
* @return value of the coefficient
*/
public static double calculateClusteringCoefficent(IHierarchicElement element) {
Set<IHierarchicElement> neighbors = getUndirectedNeighbors(element);
int totalEdges = neighbors.size();
if(totalEdges < 2) {
return 0.0; // If there are fewer than 2 neighbors, the coefficient is undefined (or 0).
}
// Iterate over neighbors and get their neighbors and check if they are also our neighbor in
// which case we have a triangle
int triangles = 0;
for(IHierarchicElement neighbor : neighbors) {
Set<IHierarchicElement> neighborNeighbors = getUndirectedNeighbors(neighbor);
for(IHierarchicElement secondNeighbor : neighbors) {
if(secondNeighbor != neighbor && neighborNeighbors.contains(secondNeighbor)) {
triangles++;
}
}
}
double clusteringCoefficient = (double)triangles / (totalEdges * (totalEdges - 1));
return clusteringCoefficient;
}
/**
* Calculates and saves the betweenness centrality of all elements contained inside the provided
* scopeElement. See, e.g., https://en.wikipedia.org/wiki/Betweenness_centrality
*
* @param scopeElement
* the scope of this calculation
* @param metricData
* location to save the metrics
* @param recursively
* if true, all calculation will be carried out on leaf elements
* if false, it is only computing the centrality with the children
* which means the inner circle.
*/
public static void calculateBetweennessCentrality(IHierarchicElement scopeElement,
MetricData metricData, boolean recursively) {
// Abort if no elements are found
if(scopeElement.getContainedElements().isEmpty()) {
return;
}
Set<IHierarchicElement> graphNodes = getLocalGraphView(scopeElement, recursively);
// Initialize the betweenness values for all nodes to zero
Map<IHierarchicElement, Double> betweenness = new HashMap<>();
for(IHierarchicElement elementNode : graphNodes) {
betweenness.put(elementNode, 0.0);
}
// Iterate over all collected nodes
for(IHierarchicElement elementNode : graphNodes) {
// Initialize all variables with standard values
Stack<IHierarchicElement> stack = new Stack<>();
Map<IHierarchicElement, List<IHierarchicElement>> predecessors = new HashMap<>();
Map<IHierarchicElement, Integer> distance = new HashMap<>();
Map<IHierarchicElement, Double> dependency = new HashMap<>();
Queue<IHierarchicElement> queue = new LinkedList<>();
for(IHierarchicElement v : graphNodes) {
predecessors.put(v, new ArrayList<>());
distance.put(v, -1);
dependency.put(v, 0.0);
}
dependency.put(elementNode, 1.0);
distance.put(elementNode, 0);
queue.add(elementNode);
processCentrality(queue, stack, predecessors, distance, dependency, graphNodes,
elementNode, recursively);
// Initialize delta
Map<IHierarchicElement, Double> delta = new HashMap<>();
for(IHierarchicElement v : graphNodes) {
delta.put(v, 0.0);
}
while(!stack.isEmpty()) {
IHierarchicElement w = stack.pop();
for(IHierarchicElement v : predecessors.get(w)) {
double c = (dependency.get(v) / dependency.get(w)) * (1.0 + delta.get(w));
delta.put(v, delta.get(v) + c);
}
if(!w.equals(elementNode)) {
betweenness.put(w, betweenness.get(w) + delta.get(w));
}
}
}
// Save metric
saveMetric(scopeElement, metricData, recursively, betweenness);
}
/** Saving the metrics for each contained element of an {@link IHierarchicElement}. */
private static void saveMetric(IHierarchicElement scopeElement, MetricData metricData,
boolean recursively, Map<IHierarchicElement, Double> betweenness) {
MetricKey key = recursively ? MetricKey.BETWEENESS_CENTRALITY_RECURSIVELY
: MetricKey.BETWEENESS_CENTRALITY;
for(IHierarchicElement child : scopeElement.getContainedElements()) {
MetricTreeNode node = metricData.getTreeNodeLookupTable().get(child);
node.getDoubleMetrics().put(key, betweenness.get(child));
}
}
/**
* Performs intermediate calculations for the betweenness centrality algorithm.
*
* @param queue
* of nodes for Breadth-First Search.
* @param stack
* of nodes for Breadth-First Search.
* @param predecessor
* maps to lists used to keep track of the predecessors of nodes in the shortest
* paths.
* @param distance
* map used to store the shortest distances from the source node to other nodes.
* @param dependency
* map used to calculate and store the dependency values for nodes in the network.
* These values are part of the calculation for betweenness centrality.
* @param graphNodes
* list of nodes
* @param scopeElement
* the scope of this calculation
* @param recursively
* if true, all calculation will be carried out on leaf elements
*/
private static void processCentrality(Queue<IHierarchicElement> queue,
Stack<IHierarchicElement> stack,
Map<IHierarchicElement, List<IHierarchicElement>> predecessor,
Map<IHierarchicElement, Integer> distance, Map<IHierarchicElement, Double> dependency,
Set<IHierarchicElement> graphNodes, IHierarchicElement scopeElement,
boolean recursively) {
while(!queue.isEmpty()) {
IHierarchicElement v = queue.remove();
stack.push(v);
// Iterate over all connectors going out from the element
for(ExitConnectorBase exitConnectors : pickInstanceOf(ExitConnectorBase.class,
v.getConnectors())) {
// Iterate over all connections
for(IConnection outgoingConnection : exitConnectors.getOutgoing()) {
Set<IHierarchicElement> targetElements = new HashSet<>();
if(recursively) {
recursivlyFollowOutgoingConnection(outgoingConnection.getTarget(),
targetElements);
} else {
IHierarchicElement primaryTargetElement =
outgoingConnection.getTarget().getOwner();
// When we have a connection to the scopeElement, we have a
// connection leaving the local scope
if(scopeElement == primaryTargetElement) {
// Continue searching for outgoing connections on the target
// Connector
IConnector targetConnector = outgoingConnection.getTarget();
for(IConnection secondaryOutgoingConnection : targetConnector
.getOutgoing()) {
targetElements
.add(secondaryOutgoingConnection.getTarget().getOwner());
}
} else {
targetElements.add(primaryTargetElement);
}
}
for(IHierarchicElement targetElement : targetElements) {
// Check if element is in node list, to avoid creating data for
// nodes outside of our graph
if(graphNodes.contains(targetElement)) {
var targetDistance = distance.get(targetElement);
var vDistance = distance.get(v);
// first start of distance calculation from v to the target Element
if(targetDistance < 0) {
queue.add(targetElement);
distance.put(targetElement, vDistance + 1);
} else if(targetDistance == vDistance + 1) {
dependency.put(targetElement,
dependency.get(targetElement) + dependency.get(v));
predecessor.get(targetElement).add(v);
}
}
}
}
}
}
}
}
/*-------------------------------------------------------------------------+
| Copyright 2023 fortiss GmbH |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
+--------------------------------------------------------------------------*/
package org.fortiss.tooling.ext.quality;
import static org.fortiss.tooling.kernel.utils.EcoreUtils.pickInstanceOf;
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.MetricKey;
import org.fortiss.tooling.ext.quality.data.MetricTreeNode;
import org.fortiss.tooling.kernel.model.IIdLabeled;
import org.fortiss.tooling.kernel.model.INamedCommentedElement;
/**
* {@link IMetricProvider} to collect various metrics from an {@link IHierarchicElement}.
*
* @author groh
*/
public class HierarchicElementMetricProvider implements IMetricProvider<IHierarchicElement> {
/** {@inheritDoc} */
@Override
public void collectMetrics(MetricTreeNode node, IHierarchicElement currentElement) {
var intMetrics = node.getIntegerMetrics();
// Only collect these metrics if the name is null, in which case they have not been
// collected before
// This is used in some cases to combine metrics from two different IHierarchicElement
if(node.getName() == null) {
// Metrics are always set to default value, as these metric are combined later
if(currentElement instanceof INamedCommentedElement) {
intMetrics.put(MetricKey.NUMBER_OF_TOTAL_COMMENTABLE_ELEMENTS, 1);
INamedCommentedElement nce = (INamedCommentedElement)currentElement;
String comment = nce.getComment();
if(comment != null && comment != "") {
intMetrics.put(MetricKey.NUMBER_OF_TOTAL_COMMENTED_ELEMENTS, 1);
}
node.setName(nce.getName());
} else {
intMetrics.put(MetricKey.NUMBER_OF_TOTAL_COMMENTABLE_ELEMENTS, 0);
intMetrics.put(MetricKey.NUMBER_OF_TOTAL_COMMENTED_ELEMENTS, 0);
node.setName("No Name existant");
}
if(currentElement instanceof IIdLabeled) {
intMetrics.put(MetricKey.UNIQUE_ID, ((IIdLabeled)currentElement).getId());
} else {
intMetrics.put(MetricKey.UNIQUE_ID, -1);
}
}
// connector metrics
var connectors = currentElement.getConnectors();
intMetrics.put(MetricKey.NUMBER_OF_CONNECTORS, connectors.size());
intMetrics.put(MetricKey.NUMBER_OF_CONTAINED_ELEMENTS,
currentElement.getContainedElements().size());
intMetrics.put(MetricKey.NUMBER_OF_CONNECTIONS, currentElement.getConnections().size());
// depth metrics
intMetrics.put(MetricKey.NUMBER_OF_TOTAL_CONNECTORS, connectors.size());
var entry_connectors = pickInstanceOf(EntryConnectorBase.class, connectors);
var exit_connectors = pickInstanceOf(ExitConnectorBase.class, connectors);
intMetrics.put(MetricKey.NUMBER_OF_TOTAL_ENTRY_CONNECTORS, entry_connectors.size());
intMetrics.put(MetricKey.NUMBER_OF_TOTAL_EXIT_CONNECTORS, exit_connectors.size());
intMetrics.put(MetricKey.NUMBER_OF_TOTAL_ELEMENTS, 1);
if(currentElement.getContainedElements().isEmpty()) {
intMetrics.put(MetricKey.NUMBER_OF_TOTAL_LEAF_ELEMENTS, 0);
} else {
intMetrics.put(MetricKey.NUMBER_OF_TOTAL_LEAF_ELEMENTS, 1);
}
// graph metrics
}
}
/*-------------------------------------------------------------------------+
| Copyright 2023 fortiss GmbH |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
+--------------------------------------------------------------------------*/
package org.fortiss.tooling.ext.quality;
import org.eclipse.emf.ecore.EObject;
import org.fortiss.tooling.ext.quality.data.MetricTreeNode;
import org.fortiss.tooling.ext.quality.service.IModelQualityService;
import org.fortiss.tooling.kernel.service.base.IEObjectAware;
/**
* Interface for all metric providers.
* They are called by {@link IModelQualityService} to collect metrics on model elements.
*
* @author blaschke
*/
public interface IMetricProvider<C extends EObject> extends IEObjectAware<EObject> {
/** Applies the IMetricProvider to the given model element. */
void collectMetrics(MetricTreeNode node, C element);
}
/*-------------------------------------------------------------------------+
| Copyright 2023 fortiss GmbH |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
+--------------------------------------------------------------------------*/
package org.fortiss.tooling.ext.quality;
import org.fortiss.tooling.kernel.model.IProjectRootElement;
/**
* Listener getting called when metrics update
*
* @author groh
*/
public interface IMetricUpdateListener {
/**
* Callback for metric update. This method will get called every time the metrics are updated.
*
* @param updatedElement
* the corresponding root element which was updated
*/
public void metricUpdate(IProjectRootElement updatedElement);
}
package org.fortiss.tooling.ext.quality;
/*-------------------------------------------------------------------------+
| Copyright 2023 fortiss GmbH |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
+--------------------------------------------------------------------------*/
import org.eclipse.core.runtime.Plugin;
import org.fortiss.tooling.base.model.element.IHierarchicElement;
import org.fortiss.tooling.ext.quality.service.IModelQualityService;
import org.osgi.framework.BundleContext;
/**
* The activator class controls the plug-in life cycle.
*
* @author blaschke
*/
public class ModelQualityActivator extends Plugin {
/** The plug-in ID. */
public static final String PLUGIN_ID = ModelQualityActivator.class.getPackage().getName(); // $NON-NLS-1$
/** The shared instance. */
private static ModelQualityActivator plugin;
/** {@inheritDoc} */
@Override
public void start(BundleContext context) throws Exception {
super.start(context);
plugin = this;
System.out.println("[Plugin] " + PLUGIN_ID + " started.");
IModelQualityService mqs = IModelQualityService.getInstance();
mqs.startService();
mqs.registerMetricProvider(new HierarchicElementMetricProvider(),
IHierarchicElement.class);
}
/** {@inheritDoc} */
@Override
public void stop(BundleContext context) throws Exception {
plugin = null;
super.stop(context);
}
/** Returns the shared instance. */
public static ModelQualityActivator getDefault() {
return plugin;
}
}
DataRootElement.java 7b655236c155e3a4473301aa8c3ecfe769cf071c GREEN
MetricData.java b3c1a395cca29ced5845b789d76ee2c965ec66f6 GREEN
MetricKey.java d78026f0c68aeeff0cc758bbd76fbb84da65afd6 GREEN
MetricTreeNode.java 88ddbdfc62fb8efee71aa3ee44257d1a1a582fe6 GREEN
/*-------------------------------------------------------------------------+
| Copyright 2023 fortiss GmbH |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
+--------------------------------------------------------------------------*/
package org.fortiss.tooling.ext.quality.data;
import java.time.LocalDateTime;
import org.fortiss.tooling.kernel.extension.data.ITopLevelElement;
/**
* Storage element corresponding to a {@Link IProjectRootElement}. It stores the last refresh time
* as {@link LocalDateTime}, its respective {@link ITopLevelElement} and the {@link MetricTreeNode}
* storing the data.
*
* @author groh
*/
public class DataRootElement {
/** The last time a metric tree was refreshed. */
private LocalDateTime lastRefresh;
/** The last time the metric tree was refreshed. */
private ITopLevelElement topLevelElement;
/** Root {@link MetricTreeNode} for the metric tree containing all data. */
private MetricTreeNode rootNode;
/** Constructor. */
public DataRootElement(LocalDateTime lastRefresh, ITopLevelElement topLevelElement,
MetricTreeNode rootNode) {
super();
this.lastRefresh = lastRefresh;
this.topLevelElement = topLevelElement;
this.rootNode = rootNode;
}
/** Getter for {@link #lastRefresh}. */
public LocalDateTime getLastRefresh() {
return lastRefresh;
}
/** Setter for {@link #lastRefresh}. */
public void setLastRefresh(LocalDateTime lastRefresh) {
this.lastRefresh = lastRefresh;
}
/** Getter for {@link #topLevelElement}. */
public ITopLevelElement getTopLevelElement() {
return topLevelElement;
}
/** Setter for {@link #topLevelElement}. */
public void setTopLevelElement(ITopLevelElement topLevelElement) {
this.topLevelElement = topLevelElement;
}
/** Getter for {@link #rootNode}. */
public MetricTreeNode getRootNode() {
return rootNode;
}
/** Setter for {@link #rootNode}. */
public void setRootNode(MetricTreeNode metricTreeNode) {
this.rootNode = metricTreeNode;
}
}
/*-------------------------------------------------------------------------+
| Copyright 2023 fortiss GmbH |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
+--------------------------------------------------------------------------*/
package org.fortiss.tooling.ext.quality.data;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.emf.ecore.EObject;
import org.fortiss.tooling.kernel.model.IProjectRootElement;
/**
* Class storing the metrics computed for the all suitable elements.
*
* @author groh
*/
public class MetricData {
/** A map to lookup the corresponding tree node for any {@link EObject}. */
private Map<EObject, MetricTreeNode> treeNodeLookupTable;
/** A map to lookup the corresponding root tree node for a project. */
private Map<IProjectRootElement, DataRootElement> dataRootElementMap;
/** Constructor. */
public MetricData() {
dataRootElementMap = new HashMap<>();
treeNodeLookupTable = new HashMap<>();
}
/** Getter for {@link #treeNodeLookupTable}. */
public Map<EObject, MetricTreeNode> getTreeNodeLookupTable() {
return treeNodeLookupTable;
}
/** Getter for {@link #dataRootElementMap}. */
public Map<IProjectRootElement, DataRootElement> getDataRootElementMap() {
return dataRootElementMap;
}
}
/*-------------------------------------------------------------------------+
| Copyright 2023 fortiss GmbH |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
+--------------------------------------------------------------------------*/
package org.fortiss.tooling.ext.quality.data;
import java.util.ArrayList;
import java.util.List;
/**
* Enum class representing a key which can be used to store a respective metric.
* Metrics provided by the HierarchicElementProvider.
*
* @author groh
*/
public enum MetricKey {
/** Unique ID of the element */
UNIQUE_ID(false),
/** Key for the metric counting ports. */
NUMBER_OF_CONNECTORS(false),
/** Key for the metric counting elements. */
NUMBER_OF_CONTAINED_ELEMENTS(false),
/** Key for the metric counting channels. */
NUMBER_OF_CONNECTIONS(false),
// TODO (#4333):
/** Safety level of the element. */
SAFETY_LEVEL(false, true),
/** How deeply nested the corresponding element is. */
NESTING_LEVEL(false),
/** The betweenness centrality of the corresponding element. */
BETWEENESS_CENTRALITY(false, 0.0),
/**
* The betweenness centrality of the corresponding element considering other elements
* recursively.
*/
BETWEENESS_CENTRALITY_RECURSIVELY(false, 0.0),
/** The clustering coefficient of the element considering its neighbors. */
CLUSTERING_COEFFICIENT(false, 0.0),
/** How many constraints this element violates with severity error. */
CONSTRAINT_VIOLATIONS_ERROR(false),
/** How many constraints this element violates with severity warning. */
CONSTRAINT_VIOLATIONS_WARNING(false),
// TODO(#4333) Remove knowledge about af3 from the kernel.
/** Number of allocated components to this task. */
TASK_ALLOCATED_COMPONENTS(false),
// TODO (#4333): Remove knowledge about af3 from the kernel.
/** How many constraints this element violates with severity warning. */
EXECUTION_UNIT_ALLOCATED_TASKS(false),
// Metrics which are collected over all children nodes
/** Key for the metric counting the total amount of ports contained. */
NUMBER_OF_TOTAL_CONNECTORS(true),
/** Key for the metric counting the total amount of input ports contained. */
NUMBER_OF_TOTAL_ENTRY_CONNECTORS(true),
/** Key for the metric counting the total amount of output ports contained. */
NUMBER_OF_TOTAL_EXIT_CONNECTORS(true),
/** Key for the metric counting the total amount of elements contained. */
NUMBER_OF_TOTAL_ELEMENTS(true),
/** Key for the metric counting the total amount of elements which can be commented. */
NUMBER_OF_TOTAL_COMMENTABLE_ELEMENTS(true),
/** Key for the metric counting the total amount of elements which are commented. */
NUMBER_OF_TOTAL_COMMENTED_ELEMENTS(true),
/**
* Key for the metric counting the total amount of elements which do not contain other elements.
*/
NUMBER_OF_TOTAL_LEAF_ELEMENTS(true),
/**
* Key for the metric counting of the total amount of constant values in all code
* specifications.
*/
NUMBER_OF_TOTAL_CODE_CONSTANTS(true),
/** Cyclomatic complexity number as sum over all components. */
NUMBER_OF_TOTAL_CODE_CYCLOMATIC_COMPLEXITY(true),
/** Counts the Lines of Code (LoC) in the component and its sub-components. */
NUMBER_OF_TOTAL_CODE_LINES(true),
/** Representation of the total amount of comment blocks in all code specifications. */
NUMBER_OF_TOTAL_CODE_COMMENTS(true);
/** Metrics which are a combination from the data of multiple nodes */
private static List<MetricKey> collectorKeys = new ArrayList<>();
/** Getter for {@link #collectorKeys}. */
public static List<MetricKey> getCollectorKeys() {
if(collectorKeys == null) {
collectorKeys = new ArrayList<>();
for(MetricKey key : MetricKey.values()) {
if(key.isCollectorKey) {
collectorKeys.add(key);
}
}
}
return collectorKeys;
}
/** Used for populating the {@link MetricKey#getCollectorKeys()} method. */
private boolean isCollectorKey;
// TODO (4336): In MetricTreeNode, there are int, double, and String metrics. However, here are
// only doubles and strings. What about marking the type of a MetricKey with a
// Class<? extends Number> field? In the constructor, you can check that only the admissible
// types Integer, Double, String are passed.
/** Determines if the corresponding value to this key is a string or not */
private boolean isStringValue;
/** Determines if the corresponding value to this key is a integer or not */
private boolean isDoubleValue;
/**
* Constructor for regular integer metric keys.
*
* @param isCollectorKey
* decides if this is a key returned by {@link MetricKey#getCollectorKeys()}
*/
private MetricKey(boolean isCollectorKey) {
this(isCollectorKey, false);
}
/**
* Constructor for metric key storing a double.
*
* @param isCollectorKey
* decides if this is a key returned by {@link MetricKey#getCollectorKeys()}
* @param isDoubleValue
* regardless of argument will make the key to a double key
*/
private MetricKey(boolean isCollectorKey, double isDoubleValue) {
this.isCollectorKey = isCollectorKey;
this.isStringValue = false;
this.isDoubleValue = true;
}
/**
* Constructor for metric key storing a string.
*
* @param isCollectorKey
* decides if this is a key returned by {@link MetricKey#getCollectorKeys()}
* @param isStringValue
* marks if the value associated with this key is a string
*/
private MetricKey(boolean isCollectorKey, boolean isStringValue) {
this.isCollectorKey = isCollectorKey;
this.isStringValue = isStringValue;
this.isDoubleValue = false;
}
/**
* If this value is true, it should be expected that the value corresponding to this key should
* be a string and be stored in an appropriate place.
*
* @return if the value associated with this key is a string
*/
public boolean isStringValue() {
return isStringValue;
}
/**
* If this value is true, it should be expected that the value corresponding to this key should
* be a integer and be stored in an appropriate place.
*
* @return if the value associated with this key is a integer
*/
public boolean isDoubleValue() {
return isDoubleValue;
}
/** Get all collector keys. */
static {
collectorKeys = getCollectorKeys();
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment