From 98beda1d92c279137f838fb57e5b17a9c931a3b8 Mon Sep 17 00:00:00 2001 From: Eddie Groh <groh@fortiss.org> Date: Mon, 3 Jul 2023 18:16:30 +0200 Subject: [PATCH] Refactored the data structure storing the metric data Issue-Ref: 4310 Issue-Url: https://git.fortiss.org/af3/af3/-/issues/4310 Signed-off-by: Eddie Groh <groh@fortiss.org> --- .../ui/ModelQualityExtractionMenu.java | 1 - .../ui/view/fx/MetricsFXController.java | 47 ++- .../META-INF/MANIFEST.MF | 1 + .../ext/quality/AF3QualityActivator.java | 1 - .../HierarchicElementSizeProvider.java | 163 +++++++++ .../{service => }/IModelQualityProvider.java | 64 +--- .../data/HierarchicalMetricDataContainer.java | 54 +++ .../quality/data/IMetricDataContainer.java | 25 ++ .../ext/quality/data/MetricDataManager.java | 57 ++++ .../ext/quality/data/MetricTreeNode.java | 102 ++++++ .../HierarchicElementSizeProvider.java | 311 ------------------ .../quality/service/IModelQualityService.java | 14 +- .../quality/service/ModelQualityService.java | 91 +++-- 13 files changed, 475 insertions(+), 456 deletions(-) create mode 100644 org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/HierarchicElementSizeProvider.java rename org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/{service => }/IModelQualityProvider.java (53%) create mode 100644 org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/data/HierarchicalMetricDataContainer.java create mode 100644 org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/data/IMetricDataContainer.java create mode 100644 org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/data/MetricDataManager.java create mode 100644 org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/data/MetricTreeNode.java delete mode 100644 org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/service/HierarchicElementSizeProvider.java diff --git a/org.fortiss.tooling.ext.quality.ui/src/org/fortiss/tooling/ext/quality/ui/ModelQualityExtractionMenu.java b/org.fortiss.tooling.ext.quality.ui/src/org/fortiss/tooling/ext/quality/ui/ModelQualityExtractionMenu.java index 56f749fa3..1bb5f9056 100644 --- a/org.fortiss.tooling.ext.quality.ui/src/org/fortiss/tooling/ext/quality/ui/ModelQualityExtractionMenu.java +++ b/org.fortiss.tooling.ext.quality.ui/src/org/fortiss/tooling/ext/quality/ui/ModelQualityExtractionMenu.java @@ -82,7 +82,6 @@ public class ModelQualityExtractionMenu implements IContextMenuContributor { public void run() { ITopLevelElement top = IPersistencyService.getInstance().getTopLevelElementFor(this.fp); IModelQualityService.getInstance().performMetricAnalysis(top); - System.out.println("We print something"); } } } diff --git a/org.fortiss.tooling.ext.quality.ui/src/org/fortiss/tooling/ext/quality/ui/view/fx/MetricsFXController.java b/org.fortiss.tooling.ext.quality.ui/src/org/fortiss/tooling/ext/quality/ui/view/fx/MetricsFXController.java index 29a4abe45..7fd2453d1 100644 --- a/org.fortiss.tooling.ext.quality.ui/src/org/fortiss/tooling/ext/quality/ui/view/fx/MetricsFXController.java +++ b/org.fortiss.tooling.ext.quality.ui/src/org/fortiss/tooling/ext/quality/ui/view/fx/MetricsFXController.java @@ -41,7 +41,7 @@ import org.fortiss.tooling.common.ui.javafx.layout.CompositeFXControllerBase; import org.fortiss.tooling.common.ui.javafx.style.FillStyle; import org.fortiss.tooling.common.ui.javafx.style.FontStyle; import org.fortiss.tooling.common.ui.javafx.style.LineStyle; -import org.fortiss.tooling.ext.quality.service.IModelQualityProvider; +import org.fortiss.tooling.ext.quality.data.MetricTreeNode; import org.fortiss.tooling.ext.quality.service.ModelQualityService; import org.fortiss.tooling.kernel.model.IProjectRootElement; import org.fortiss.tooling.spiderchart.control.SpiderChartViewer; @@ -111,8 +111,8 @@ public class MetricsFXController extends CompositeFXControllerBase<SplitPane, No public void selectionChanged(IWorkbenchPart part, ISelection selection) { ModelQualityService.getInstance(); - var last_metrics = ModelQualityService.last_metrics; - if(last_metrics == null) { + var manager = ModelQualityService.metricDataManagerInstance; + if(manager == null) { topLabel.setText( "No Metrics were available at " + timeFormat.format(LocalDateTime.now())); return; @@ -144,7 +144,7 @@ public class MetricsFXController extends CompositeFXControllerBase<SplitPane, No } if(selected instanceof IHierarchicElement) { - var root_provider = last_metrics.get(currentRootElement); + var root_provider = manager.getRootNodes().get(currentRootElement); if(root_provider == null) { topLabel.setText("No Metric found for this project, did you export metrics?"); @@ -152,24 +152,22 @@ public class MetricsFXController extends CompositeFXControllerBase<SplitPane, No } var element = (IHierarchicElement)selected; - var provider = root_provider.getProvider(element); - if(provider != null) { + var node = manager.getHierarchicLookupTable().get(element); + if(node != null) { - var string = provider.getExampleString(); - topLabel.setText(string); + topLabel.setText("Text"); - if(!provider.getContainedProviders().isEmpty()) { + if(!node.getChildren().isEmpty()) { - SpiderChartViewer viewer = - buildSpiderChart((IModelQualityProvider<IHierarchicElement>)provider); + SpiderChartViewer viewer = buildSpiderChart(node); borderPane.setCenter(viewer.getViewerPane()); // Create PieChart and add data pieChart.getData().clear(); - for(var prov : provider.getContainedProviders()) { + for(var child : node.getChildren()) { - PieChart.Data slice = new PieChart.Data(prov.getName(), - ((IModelQualityProvider<?>)prov).getExampleValues().get(0)); + PieChart.Data slice = + new PieChart.Data(child.getName(), child.getTotalPortAmount()); pieChart.getData().add(slice); pieChart.setLegendVisible(false); pieChart.setTitle(""); @@ -202,37 +200,34 @@ public class MetricsFXController extends CompositeFXControllerBase<SplitPane, No * the provider for which the spider chart should be generated * @return {@link SpiderChartViewer} with the matching metrics */ - private static SpiderChartViewer - buildSpiderChart(IModelQualityProvider<IHierarchicElement> provider) { + private static SpiderChartViewer buildSpiderChart(MetricTreeNode node) { SpiderChart spiderChart = new SpiderChart(); - spiderChart.setTitle("Smartphone Comparison Scale"); + spiderChart.setTitle("Amount of Ports"); spiderChart.setLegendLabel("Legend"); ChartStyle chartStyle = new ChartStyle(true, true, true); - List<IModelQualityProvider<IHierarchicElement>> containedProviders = - provider.getContainedProviders(); + List<MetricTreeNode> children = node.getChildren(); - if(containedProviders.isEmpty()) { + if(children.isEmpty()) { return null; } - double maxval = containedProviders.stream().mapToDouble(p -> p.getExampleValues().get(0)) - .max().getAsDouble(); + double maxval = + children.stream().mapToDouble(p -> p.getTotalPortAmount()).max().getAsDouble(); chartStyle.setUseIndividualAxisSegments(false); chartStyle.setTitleStyle(new FontStyle("Verdana", 14, BLUE.brighter())); DataSeries elementData = new DataSeries("Data"); - for(var prov : containedProviders) { - DoubleAxis testing = new DoubleAxis(prov.getName(), 0.0, maxval); + for(var child : node.getChildren()) { + DoubleAxis testing = new DoubleAxis(child.getName(), 0.0, maxval); AxisStyle aStyle3Segs = new AxisStyle(SOLID_BLACK_1PT, BLACK_VERDANA_14PT, 3, BLACK_VERDANA_8PT, new DecimalFormat("#.##")); chartStyle.setAxisStyle(testing, aStyle3Segs); spiderChart.addAxis(testing); - elementData.setPoint(testing, - ((IModelQualityProvider<?>)prov).getExampleValues().get(0)); + elementData.setPoint(testing, child.getTotalPortAmount()); } spiderChart.addData(elementData); diff --git a/org.fortiss.tooling.ext.quality/META-INF/MANIFEST.MF b/org.fortiss.tooling.ext.quality/META-INF/MANIFEST.MF index b7c389724..9b9be8e7a 100644 --- a/org.fortiss.tooling.ext.quality/META-INF/MANIFEST.MF +++ b/org.fortiss.tooling.ext.quality/META-INF/MANIFEST.MF @@ -13,5 +13,6 @@ Require-Bundle: org.eclipse.core.runtime, org.fortiss.tooling.kernel;visibility:=reexport, org.fortiss.af3.project Export-Package: org.fortiss.tooling.ext.quality, + org.fortiss.tooling.ext.quality.data, org.fortiss.tooling.ext.quality.service 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 7ab8cb483..ef935dcfc 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 @@ -18,7 +18,6 @@ package org.fortiss.tooling.ext.quality; import org.eclipse.core.runtime.Plugin; import org.fortiss.tooling.base.model.element.IHierarchicElement; -import org.fortiss.tooling.ext.quality.service.HierarchicElementSizeProvider; import org.fortiss.tooling.ext.quality.service.IModelQualityService; import org.fortiss.tooling.ext.quality.service.ModelQualityService; import org.osgi.framework.BundleContext; diff --git a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/HierarchicElementSizeProvider.java b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/HierarchicElementSizeProvider.java new file mode 100644 index 000000000..cb3eb2cb2 --- /dev/null +++ b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/HierarchicElementSizeProvider.java @@ -0,0 +1,163 @@ +/*-------------------------------------------------------------------------+ +| 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 java.util.Optional; + +import org.eclipse.emf.common.util.EList; +import org.fortiss.tooling.base.model.element.IHierarchicElement; +import org.fortiss.tooling.ext.quality.data.MetricDataManager; +import org.fortiss.tooling.ext.quality.data.MetricTreeNode; +import org.fortiss.tooling.kernel.model.INamedCommentedElement; + +/** + * {@link IModelQualityProvider} to count the ratio of filled out comments of + * INamedCommentedElements. + * + * @author blaschke + * @author groh + */ +public class HierarchicElementSizeProvider implements IModelQualityProvider<IHierarchicElement> { + /** + * returns an array of integers or <String,Double> Map Number of children of the + * hierarchical Element (on this level) Number of ports on the hierarchical + * Element (on this level) Number of channels in the hierarchical element (on + * this level) Number of totalPorts in whole tree Number of totalElements in + * whole tree Average SubTree Size + */ + + /** Key for the metric counting ports */ + public static final String NUMBER_OF_PORTS = "numberOfPorts"; + + /** Key for the metric counting elements */ + public static final String NUMBER_OF_CONTAINED_ELEMENTS = "numberOfContainedElements"; + + /** Key for the metric counting channels */ + public static final String NUMBER_OF_CHANNELS = "numberOfChannels"; + + /** Key for the metric counting the total amount of ports contained */ + public static final String NUMBER_OF_TOTAL_PORTS = "numberOfTotalPorts"; + /** Key for the metric counting the total amount of elements contained */ + public static final String NUMBER_OF_TOTAL_ELEMENTS = "numberOfTotalElements"; + /** Key for the metric counting the total amount of elements which can be commented */ + public static final String NUMBER_OF_TOTAL_COMMENTABLE_ELEMENTS = + "numberOfTotalCommentableElements"; + /** Key for the metric counting the total amount of elements which are commented */ + public static final String NUMBER_OF_TOTAL_COMMENTED_ELEMENTS = + "numberOfTotalCommentedElements"; + /** + * Key for the metric counting the total amount of elements which do not contain other elements + */ + public static final String NUMBER_OF_TOTAL_LEAF_ELEMENTS = "numberOfTotalLeafElements"; + + /** + * List containing all HierarchicElementSizeProvider for components inside the + * IHierarchicElement + */ + + /** */ + public HierarchicElementSizeProvider() { + super(); + } + + /** {@inheritDoc} */ + @Override + public void apply(MetricDataManager manager, MetricTreeNode node, + IHierarchicElement currentElement) { + + var metrics = node.getStoredMetrics(); + boolean isElementCommentable = currentElement instanceof INamedCommentedElement; + metrics.put(NUMBER_OF_TOTAL_COMMENTABLE_ELEMENTS, isElementCommentable ? 1.0 : 0.0); + String comment = + isElementCommentable ? ((INamedCommentedElement)currentElement).getComment() : null; + metrics.put(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)); + } else { + this.applyMetrics(manager, node, specificationElement); + } + // 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) { + + // TODO input ports number + // TODO output ports number + + // EcoreUtils.pickInstanceOf(target, srcList) + var metrics = node.getStoredMetrics(); + + metrics.put(NUMBER_OF_PORTS, (double)currentElement.getConnectors().size()); + metrics.put(NUMBER_OF_CONTAINED_ELEMENTS, + (double)currentElement.getContainedElements().size()); + metrics.put(NUMBER_OF_CHANNELS, (double)currentElement.getConnections().size()); + + // depth metrics + metrics.put(NUMBER_OF_TOTAL_PORTS, (double)currentElement.getConnectors().size()); + metrics.put(NUMBER_OF_TOTAL_ELEMENTS, 1.0); + + metrics.put(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(String key : new String[] {NUMBER_OF_TOTAL_PORTS, NUMBER_OF_TOTAL_ELEMENTS, + NUMBER_OF_TOTAL_COMMENTABLE_ELEMENTS, NUMBER_OF_TOTAL_COMMENTED_ELEMENTS, + 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/service/IModelQualityProvider.java b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/IModelQualityProvider.java similarity index 53% rename from org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/service/IModelQualityProvider.java rename to org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/IModelQualityProvider.java index d4496e97b..15cc41a29 100644 --- a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/service/IModelQualityProvider.java +++ b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/IModelQualityProvider.java @@ -13,12 +13,12 @@ | See the License for the specific language governing permissions and | | limitations under the License. | +--------------------------------------------------------------------------*/ -package org.fortiss.tooling.ext.quality.service; - -import java.util.List; +package org.fortiss.tooling.ext.quality; import org.eclipse.emf.ecore.EObject; -import org.fortiss.tooling.base.model.element.IHierarchicElement; +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,59 +31,5 @@ import org.fortiss.tooling.kernel.service.base.IEObjectAware; public interface IModelQualityProvider<C extends EObject> extends IEObjectAware<EObject> { /** Applies the IMetricProvider to the given model element. */ - void apply(C element); - - /** - * @return a simple example string demonstrating some metric capability - */ - String getExampleString(); - - /** - * @return a simple example values demonstrating some metric capability - */ - List<Double> getExampleValues(); - - /** - * @return providers contained in this provider - */ - List<IModelQualityProvider<C>> getContainedProviders(); - - /** - * @return the name of the element - */ - String getName(); - - /** - * @param element - * the element for which the matching {@link IModelQualityProvider} is searched - * @return the matching provider - */ - IModelQualityProvider<C> getProvider(IHierarchicElement element); - - /** - * Visits all nodes of the provider tree in post-order - * - * @param visitor - * the object on which the methods will be called - */ - void traverse(IVisitorModelQualityProvider visitor); - - /** - * @return set of keys of metrics contained in this provider - */ - List<String> metricKeys(); - - /** - * Visitor pattern class - * - * @author groh - */ - public interface IVisitorModelQualityProvider { - - /** - * @param provider - * gets a HierarchicElementSizeProvider - */ - void accept(HierarchicElementSizeProvider provider); - } + void apply(MetricDataManager manager, MetricTreeNode node, C element); } diff --git a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/data/HierarchicalMetricDataContainer.java b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/data/HierarchicalMetricDataContainer.java new file mode 100644 index 000000000..175f35a94 --- /dev/null +++ b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/data/HierarchicalMetricDataContainer.java @@ -0,0 +1,54 @@ +/*-------------------------------------------------------------------------+ +| 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.fortiss.tooling.ext.quality.HierarchicElementSizeProvider; + +/** + * Class for storing data generated by the {@link HierarchicElementSizeProvider} + * + * @author groh + */ +public class HierarchicalMetricDataContainer implements IMetricDataContainer { + + /** Map containing all metrics which are a double */ + private Map<String, Double> storedMetrics = new HashMap<>(); + + /** name of the element which is associated with this data */ + private String name; + + /** + * @return the name of the element which is associated with this data + */ + public String getName() { + return name; + } + + /** Set the name */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the stored metrics map + */ + public Map<String, Double> getStoredMetrics() { + return storedMetrics; + } +} diff --git a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/data/IMetricDataContainer.java b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/data/IMetricDataContainer.java new file mode 100644 index 000000000..7cbd00479 --- /dev/null +++ b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/data/IMetricDataContainer.java @@ -0,0 +1,25 @@ +/*-------------------------------------------------------------------------+ +| 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; + +/** + * Interface which is implemented by all + * + * @author groh + */ +public interface IMetricDataContainer { + // Nothing here yet +} diff --git a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/data/MetricDataManager.java b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/data/MetricDataManager.java new file mode 100644 index 000000000..c3f109913 --- /dev/null +++ b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/data/MetricDataManager.java @@ -0,0 +1,57 @@ +/*-------------------------------------------------------------------------+ +| 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.fortiss.tooling.base.model.element.IHierarchicElement; +import org.fortiss.tooling.kernel.model.IProjectRootElement; + +/** + * + * @author groh + */ +public class MetricDataManager { + + /** a map to lookup the corresponding tree node */ + private Map<IHierarchicElement, MetricTreeNode> hierarchicLookupTable; + + /** the root tree node */ + private Map<IProjectRootElement, MetricTreeNode> root_nodes; + + /** + * Constructs a new instance of the MetricDataManager + */ + public MetricDataManager() { + root_nodes = new HashMap<>(); + hierarchicLookupTable = new HashMap<>(); + } + + /** + * @return the lookup map + */ + public Map<IHierarchicElement, MetricTreeNode> getHierarchicLookupTable() { + return hierarchicLookupTable; + } + + /** + * @return a map containing all root nodes + */ + public Map<IProjectRootElement, MetricTreeNode> getRootNodes() { + return root_nodes; + } +} diff --git a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/data/MetricTreeNode.java b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/data/MetricTreeNode.java new file mode 100644 index 000000000..200c67bd8 --- /dev/null +++ b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/data/MetricTreeNode.java @@ -0,0 +1,102 @@ +/*-------------------------------------------------------------------------+ +| 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.HashMap; +import java.util.List; +import java.util.Map; + +import org.fortiss.tooling.ext.quality.IModelQualityProvider; + +/** + * A general class for storing all kinds of metric data in a hierarchical tree structure. + * + * Allows for efficient storage and retrieval of data + * + * @author groh + */ +public class MetricTreeNode { + + /** stores the children of this tree node */ + private List<MetricTreeNode> children; + + /** + * This map stores a relationship between a {@link IModelQualityProvider} and + * {@link IMetricDataContainer}. + * + * This allows all {@linkplain IModelQualityProvider} to deposit data inside this structure, and + * quickly retrieve it for processing + * + * The stored data can then also be accessed by other methods, for displaying information, + * creating statistics, etc. + */ + private Map<Class<? extends IModelQualityProvider<?>>, IMetricDataContainer> dataContainers; + + /** Map containing all metrics which are a double */ + private Map<String, Double> storedMetrics = new HashMap<>(); + + /** name of the element which is associated with this data */ + private String name; + + /** + * Constructs a new node + */ + public MetricTreeNode() { + children = new ArrayList<MetricTreeNode>(); + dataContainers = new HashMap<>(); + } + + /** + * @return the children of this node + */ + public List<MetricTreeNode> getChildren() { + return children; + } + + /** + * See {@link MetricTreeNode#dataContainers} + * + * @return the map storing all DataContainers in this node + */ + public Map<Class<? extends IModelQualityProvider<?>>, IMetricDataContainer> + getDataContainers() { + return dataContainers; + } + + /** + * @return the name of the element which is associated with this data + */ + public String getName() { + return name; + } + + /** Set the name */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the stored metrics map + */ + public Map<String, Double> getStoredMetrics() { + return storedMetrics; + } + + public double getTotalPortAmount() { + return storedMetrics.get("numberOfTotalPorts"); + } +} diff --git a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/service/HierarchicElementSizeProvider.java b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/service/HierarchicElementSizeProvider.java deleted file mode 100644 index da40a0282..000000000 --- a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/service/HierarchicElementSizeProvider.java +++ /dev/null @@ -1,311 +0,0 @@ -/*-------------------------------------------------------------------------+ -| 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.service; - -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -import org.eclipse.emf.common.util.EList; -import org.fortiss.tooling.base.model.element.IHierarchicElement; -import org.fortiss.tooling.kernel.model.INamedCommentedElement; - -/** - * {@link IModelQualityProvider} to count the ratio of filled out comments of - * INamedCommentedElements. - * - * @author blaschke - * @author groh - */ -public class HierarchicElementSizeProvider implements IModelQualityProvider<IHierarchicElement> { - /** - * returns an array of integers or <String,Double> Map Number of children of the - * hierarchical Element (on this level) Number of ports on the hierarchical - * Element (on this level) Number of channels in the hierarchical element (on - * this level) Number of totalPorts in whole tree Number of totalElements in - * whole tree Average SubTree Size - */ - - /** Key for the metric counting ports */ - public static final String NUMBER_OF_PORTS = "numberOfPorts"; - - /** Key for the metric counting elements */ - public static final String NUMBER_OF_CONTAINED_ELEMENTS = "numberOfContainedElements"; - - /** Key for the metric counting channels */ - public static final String NUMBER_OF_CHANNELS = "numberOfChannels"; - - /** Key for the metric counting the total amount of ports contained */ - public static final String NUMBER_OF_TOTAL_PORTS = "numberOfTotalPorts"; - /** Key for the metric counting the total amount of elements contained */ - public static final String NUMBER_OF_TOTAL_ELEMENTS = "numberOfTotalElements"; - /** Key for the metric counting the total amount of elements which can be commented */ - public static final String NUMBER_OF_TOTAL_COMMENTABLE_ELEMENTS = - "numberOfTotalCommentableElements"; - /** Key for the metric counting the total amount of elements which are commented */ - public static final String NUMBER_OF_TOTAL_COMMENTED_ELEMENTS = - "numberOfTotalCommentedElements"; - - /** Map containing all metrics concerning the current IHierarchicElement */ - private Map<String, Double> storedMetrics; - /** - * List containing all HierarchicElementSizeProvider for components inside the - * IHierarchicElement - */ - private List<HierarchicElementSizeProvider> containedProviders; - - /** - * Mapping containing all providers from this element and any elements contained in that element - */ - private Map<IHierarchicElement, HierarchicElementSizeProvider> allProviders; - - /** Variable storing the name */ - private String name; - - /** - * @return Map containing the mapping between a metric keys and the corresponding values for - * this element - */ - public Map<String, Double> getStoredMetrics() { - return storedMetrics; - } - - /** */ - public HierarchicElementSizeProvider() { - super(); - this.storedMetrics = new HashMap<>(); - this.allProviders = new HashMap<>(); - this.containedProviders = new ArrayList<>(); - this.name = null; - } - - /** {@inheritDoc} */ - @Override - public void apply(IHierarchicElement currentElement) { - - boolean isElementCommentable = currentElement instanceof INamedCommentedElement; - storedMetrics.put(NUMBER_OF_TOTAL_COMMENTABLE_ELEMENTS, isElementCommentable ? 1.0 : 0.0); - String comment = - isElementCommentable ? ((INamedCommentedElement)currentElement).getComment() : null; - storedMetrics.put(NUMBER_OF_TOTAL_COMMENTED_ELEMENTS, - comment != null && comment != "" ? 1.0 : 0.0); - if(isElementCommentable) { - this.name = ((INamedCommentedElement)currentElement).getName(); - } else { - this.name = "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 - allProviders.put(specificationElement, this); - // Skip the specification element to get a more useful tree structure - this.applyMetrics(containedElements.get(0)); - } else { - this.applyMetrics(specificationElement); - } - // Add reference from the element to this, so the lookups works as expected - allProviders.put(currentElement, this); - return; - } - this.applyMetrics(currentElement); - } - - /** - * @param currentElement - * the element on which to collect the metrics and recursively descend - */ - private void applyMetrics(IHierarchicElement currentElement) { - - storedMetrics.put(NUMBER_OF_PORTS, (double)currentElement.getConnectors().size()); - storedMetrics.put(NUMBER_OF_CONTAINED_ELEMENTS, - (double)currentElement.getContainedElements().size()); - storedMetrics.put(NUMBER_OF_CHANNELS, (double)currentElement.getConnections().size()); - - // depth metrics - storedMetrics.put(NUMBER_OF_TOTAL_PORTS, (double)currentElement.getConnectors().size()); - storedMetrics.put(NUMBER_OF_TOTAL_ELEMENTS, 1.0); - - for(IHierarchicElement containedElement : currentElement.getContainedElements()) { - HierarchicElementSizeProvider provider = new HierarchicElementSizeProvider(); - containedProviders.add(provider); - provider.apply(containedElement); - allProviders.putAll(provider.allProviders); - - for(String key : new String[] {NUMBER_OF_TOTAL_PORTS, NUMBER_OF_TOTAL_ELEMENTS, - NUMBER_OF_TOTAL_COMMENTABLE_ELEMENTS, NUMBER_OF_TOTAL_COMMENTED_ELEMENTS}) { - storedMetrics.merge(key, provider.storedMetrics.get(key), Double::sum); - } - } - - allProviders.put(currentElement, this); - } - - /** {@inheritDoc} */ - @Override - public String getExampleString() { - double commentable = storedMetrics.getOrDefault( - HierarchicElementSizeProvider.NUMBER_OF_TOTAL_COMMENTABLE_ELEMENTS, 0.0); - double commented = storedMetrics.getOrDefault( - HierarchicElementSizeProvider.NUMBER_OF_TOTAL_COMMENTED_ELEMENTS, 0.0); - - // Create a DecimalFormat object to format the percentage - DecimalFormat decimalFormat = new DecimalFormat("0.00"); - - // Format the percentage value and append the '%' symbol - String formattedPercentage = decimalFormat.format((commented / commentable) * 100) + "%"; - - return "Commented elements: " + (int)commented + ", Total elements: " + (int)commentable + - ", Ratio: " + formattedPercentage; - } - - /** {@inheritDoc} */ - @Override - public IModelQualityProvider<IHierarchicElement> getProvider(IHierarchicElement element) { - return allProviders.get(element); - } - - /** {@inheritDoc} */ - @Override - public void traverse(IVisitorModelQualityProvider visitor) { - - // iterate through all contained providers - for(HierarchicElementSizeProvider provider : containedProviders) { - provider.traverse(visitor); - } - - // visit this provider - visitor.accept(this); - } - - /** {@inheritDoc} */ - @Override - public List<String> metricKeys() { - return new ArrayList<>(storedMetrics.keySet()); - } - - /** {@inheritDoc} */ - @Override - public List<Double> getExampleValues() { - List<Double> list = new ArrayList<>(); - list.add(storedMetrics.get(NUMBER_OF_TOTAL_PORTS)); - return list; - } - - /** {@inheritDoc} */ - @Override - public List<IModelQualityProvider<IHierarchicElement>> getContainedProviders() { - List<IModelQualityProvider<IHierarchicElement>> list = new ArrayList<>(); - list.addAll(containedProviders); - return list; - } - - /** Returns name. */ - @Override - public String getName() { - return name; - } - - /* - * - * CLONE DETECTION START public class CloneDetection { - * - * private Map<Integer, List<IHierarchicElement>> map = new HashMap<>(); - * - * public void printResult() { System.out.println("Stuff"); } - * - * public void apply(IHierarchicElement element) { int hash = 1; for(IConnector - * connector : element.getConnectors()) { EList<IModelElementSpecification> - * specifications = connector.getSpecifications(); if(!specifications.isEmpty() - * && connector.getSpecifications().get(0) instanceof PortSpecification) { - * - * PortSpecification port = - * (PortSpecification)connector.getSpecifications().get(0); hash = 31 * hash + - * (connector instanceof InputPort ? 1 : 2) * - * port.getType().getClass().hashCode(); } else { // Clone Detection for State - * diagrams not supported return; } }map.putIfAbsent(hash,new ArrayList<>()); - * - * List<IHierarchicElement> elements = map.get(hash); - * - * for( IHierarchicElement element_b:elements) { - * - * List<String> element_a_input = new ArrayList<String>(); List<String> - * element_b_input = new ArrayList<String>(); List<String> element_a_output = - * new ArrayList<String>(); List<String> element_b_output = new - * ArrayList<String>(); - * - * for(IConnector connector : element.getConnectors()) { PortSpecification port - * = (PortSpecification)connector.getSpecifications().get(0); String type = - * port.getType().getClass().getSimpleName(); if(connector instanceof InputPort) - * { element_a_input.add(type); } else { element_a_output.add(type); } } - * - * for(IConnector connector : element_b.getConnectors()) { PortSpecification - * port = (PortSpecification)connector.getSpecifications().get(0); String type = - * port.getType().getClass().getSimpleName(); if(connector instanceof InputPort) - * { element_b_input.add(type); } else { element_b_output.add(type); } } - * - * boolean equals = true; - * - * for(String type_a_input : element_a_input) { - * if(!element_b_input.remove(type_a_input)) { equals = false; break; } - * - * } - * - * for(String type_a_output : element_a_output) { - * if(!element_b_output.remove(type_a_output)) { equals = false; break; } - * - * } if(equals) { System.out .println("Found duplicate: " + - * ((INamedCommentedElement)element).getName() + ", " + - * ((INamedCommentedElement)element_b).getName()); break; } else { - * System.out.println("Same but not duplicate"); } }elements.add(element); } - * CLONE DETECTION END - * - * - * public void printShare(HierarchicElementBase element) { - * - * Set<Tuple<String, Integer>> counts = new HashSet<>(); this.apply(element); - * counts.add(new Tuple<>("[Self]" + element.getName(), count)); this.count = 0; - * - * for(IHierarchicElement child : element.getContainedElements()) { - * - * HierarchicElementBase real_child = (HierarchicElementBase)child; - * - * recursiveDescent(real_child, this); - * - * counts.add(new Tuple<>(real_child.getName(), count)); count = 0; } - * - * double total = counts.stream().mapToInt(t -> t.getElement2()).sum(); - * - * System.out.println("[" + this.getClass().getSimpleName() + "] shares:"); - * for(Tuple<String, Integer> tuple : counts) { System.out.println("\t " + - * String.format("%05.2f%%", tuple.element2 / total * 100) + ", " + - * tuple.element2 + ": " + tuple.element1); } } - * - */ -} 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 b2e4cfceb..848aa951c 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,16 +15,13 @@ +--------------------------------------------------------------------------*/ package org.fortiss.tooling.ext.quality.service; -import java.util.Map; - -import org.eclipse.emf.ecore.EObject; +import org.fortiss.tooling.ext.quality.IModelQualityProvider; import org.fortiss.tooling.kernel.extension.data.ITopLevelElement; -import org.fortiss.tooling.kernel.model.IProjectRootElement; /** - * The migration service checks and upgrades the old models from the last release. + * Performs metric analysis * - * @author mou + * @author groh */ public interface IModelQualityService { /** Returns the service instance. */ @@ -35,7 +32,6 @@ public interface IModelQualityService { /** Registers the metric provider with the service. */ void registerMetricProvider(IModelQualityProvider<?> provider, Class<?> modelElementClass); - /** */ - Map<IProjectRootElement, IModelQualityProvider<? extends EObject>> - performMetricAnalysis(ITopLevelElement top); + /** 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 abfac2b2e..82c09021b 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 @@ -32,17 +32,17 @@ import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; -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.service.IModelQualityProvider.IVisitorModelQualityProvider; +import org.fortiss.tooling.ext.quality.HierarchicElementSizeProvider; +import org.fortiss.tooling.ext.quality.IModelQualityProvider; +import org.fortiss.tooling.ext.quality.data.MetricDataManager; +import org.fortiss.tooling.ext.quality.data.MetricTreeNode; import org.fortiss.tooling.kernel.extension.data.ITopLevelElement; import org.fortiss.tooling.kernel.introspection.IIntrospectionDetailsItem; import org.fortiss.tooling.kernel.introspection.IIntrospectionItem; @@ -79,9 +79,8 @@ public class ModelQualityService extends EObjectAwareServiceBase<IModelQualityPr /** The connector attribute name. */ private static final String ATTRIBUTE_NAME = "metricService"; - /** Static Map containing the last result of the metric extraction */ - public static Map<IProjectRootElement, IModelQualityProvider<? extends EObject>> last_metrics = - new HashMap<>(); + /** Static instance of the MetricDataManager */ + public static MetricDataManager metricDataManagerInstance = new MetricDataManager(); /** */ @Override @@ -121,39 +120,32 @@ public class ModelQualityService extends EObjectAwareServiceBase<IModelQualityPr /** * @param topLvl * the file project on which the analysis should be performed - * @return result resulting metrics */ @Override - public Map<IProjectRootElement, IModelQualityProvider<? extends EObject>> - performMetricAnalysis(ITopLevelElement topLvl) { - Map<IProjectRootElement, IModelQualityProvider<? extends EObject>> result = new HashMap<>(); + public void performMetricAnalysis(ITopLevelElement topLvl) { + for(IProjectRootElement rootElement : EcoreUtils .getChildrenWithType(topLvl.getRootModelElement(), IProjectRootElement.class)) { if(rootElement instanceof IHierarchicElement) { - // TODO check get(0) + EList<IHierarchicElement> elements = ((IHierarchicElement)rootElement).getContainedElements(); if(elements.isEmpty()) { continue; } + var root_nodes = metricDataManagerInstance.getRootNodes(); + // Get first element, there should not be any other elements IHierarchicElement firstNode = elements.get(0); - + MetricTreeNode root_node = new MetricTreeNode(); HierarchicElementSizeProvider hes = new HierarchicElementSizeProvider(); - hes.apply(firstNode); - System.out.println("checked in hierarchic metrics for: " + rootElement.getName()); - double commentable = hes.getStoredMetrics().getOrDefault( - HierarchicElementSizeProvider.NUMBER_OF_TOTAL_COMMENTABLE_ELEMENTS, 0.0); - double commented = hes.getStoredMetrics().getOrDefault( - HierarchicElementSizeProvider.NUMBER_OF_TOTAL_COMMENTED_ELEMENTS, 0.0); - System.out.println("Commented to total: " + commented + ", " + commentable + ", " + - commented / commentable); - result.put(rootElement, hes); + hes.apply(metricDataManagerInstance, root_node, firstNode); + root_nodes.put(rootElement, root_node); } } - result.forEach((k, v) -> last_metrics.merge(k, v, (oldValue, newValue) -> newValue)); - metricExtractionToCSV(result); - return result; + // result.forEach((k, v) -> last_metrics.merge(k, v, (oldValue, newValue) -> newValue)); + // metricExtractionToCSV(result); + // return result; } /** */ @@ -237,7 +229,8 @@ public class ModelQualityService extends EObjectAwareServiceBase<IModelQualityPr if(createNewIndex) { // Create new index and write it into the first line of the file var anyProvider = data.values().iterator().next(); - List<String> keys = anyProvider.metricKeys(); + // List<String> keys = anyProvider.metricKeys(); + List<String> keys = new ArrayList<String>(); allKeys.add("timestamp"); allKeys.add("root"); allKeys.add("node"); @@ -259,29 +252,29 @@ public class ModelQualityService extends EObjectAwareServiceBase<IModelQualityPr // iterate over all project roots String rootName = rootProject.getName(); - rootProvider.traverse(new IVisitorModelQualityProvider() { - - @Override - public void accept(HierarchicElementSizeProvider provider) { - - var metrics = provider.getStoredMetrics(); - // These 4 data entries are just concatenated in front - var startStream = Stream.of(formattedDateTime, rootName, "null", "null"); - // Collect all values from provider in the correct order and combine - var values = Stream - .concat(startStream, - valueKeys.stream().map(metrics::get).map(String::valueOf)) - .collect(Collectors.joining(",")); - - try { - // write values - writer.write(values); - writer.write("\n"); - } catch(IOException e) { - e.printStackTrace(); - } - } - }); + // rootProvider.traverse(new IVisitorModelQualityProvider() { + // + // @Override + // public void accept(HierarchicElementSizeProvider provider) { + // + // var metrics = provider.getStoredMetrics(); + // // These 4 data entries are just concatenated in front + // var startStream = Stream.of(formattedDateTime, rootName, "null", "null"); + // // Collect all values from provider in the correct order and combine + // var values = Stream + // .concat(startStream, + // valueKeys.stream().map(metrics::get).map(String::valueOf)) + // .collect(Collectors.joining(",")); + // + // try { + // // write values + // writer.write(values); + // writer.write("\n"); + // } catch(IOException e) { + // e.printStackTrace(); + // } + // } + // }); }); } catch(IOException e) { e.printStackTrace(); -- GitLab