diff --git a/tools/io.incquery.cameo.model-coverage-report/META-INF/MANIFEST.MF b/tools/io.incquery.cameo.model-coverage-report/META-INF/MANIFEST.MF new file mode 100644 index 0000000..9cf35b3 --- /dev/null +++ b/tools/io.incquery.cameo.model-coverage-report/META-INF/MANIFEST.MF @@ -0,0 +1,8 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: io.incquery.cameo.model-coverage-report +Bundle-SymbolicName: io.incquery.cameo.model-coverage-report;singleton:=true +Bundle-Version: 0.1.0.qualifier +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Automatic-Module-Name: io.incquery.cameo.model-coverage-report +Export-Package: . diff --git a/tools/io.incquery.cameo.model-coverage-report/src/main/dist/template/data/resourcemanager/MDR_Example_Plugin_18351_descriptor.xml b/tools/io.incquery.cameo.model-coverage-report/src/main/dist/template/data/resourcemanager/MDR_Example_Plugin_18351_descriptor.xml new file mode 100644 index 0000000..bd824d6 --- /dev/null +++ b/tools/io.incquery.cameo.model-coverage-report/src/main/dist/template/data/resourcemanager/MDR_Example_Plugin_18351_descriptor.xml @@ -0,0 +1,32 @@ + + + + + Reader + Community + Standard + Professional Java + Professional C++ + Professional C# + Professional ArcStyler + Professional EFFS ArcStyler + OptimalJ + Professional + Architect + Enterprise + + + + + + + + diff --git a/tools/io.incquery.cameo.model-coverage-report/src/main/dist/template/plugins/${group}/plugin.xml b/tools/io.incquery.cameo.model-coverage-report/src/main/dist/template/plugins/${group}/plugin.xml new file mode 100644 index 0000000..8ba7bc2 --- /dev/null +++ b/tools/io.incquery.cameo.model-coverage-report/src/main/dist/template/plugins/${group}/plugin.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + diff --git a/tools/io.incquery.cameo.model-coverage-report/src/main/java/io/incquery/cameo/modelcoveragereport/MainMenuConfigurator.java b/tools/io.incquery.cameo.model-coverage-report/src/main/java/io/incquery/cameo/modelcoveragereport/MainMenuConfigurator.java new file mode 100644 index 0000000..ee5909e --- /dev/null +++ b/tools/io.incquery.cameo.model-coverage-report/src/main/java/io/incquery/cameo/modelcoveragereport/MainMenuConfigurator.java @@ -0,0 +1,58 @@ +/* + * + * Copyright (c) 2002 NoMagic, Inc. All Rights Reserved. + */ + +package io.incquery.cameo.modelcoveragereport; + +import com.nomagic.actions.AMConfigurator; +import com.nomagic.actions.ActionsCategory; +import com.nomagic.actions.ActionsManager; +import com.nomagic.actions.NMAction; +import com.nomagic.magicdraw.actions.MDActionsCategory; + +public class MainMenuConfigurator implements AMConfigurator { + + String REPORTING = "Report"; + + /** + * Action will be added to manager. + */ + private NMAction[] actions; + + /** + * Creates configurator. + * + * @param action + * action to be added to main menu. + */ + public MainMenuConfigurator(NMAction... actions) { + this.actions = actions; + } + + /** + * @see com.nomagic.actions.AMConfigurator#configure(ActionsManager) Methods + * adds action to given manager Examples category. + */ + @Override + public void configure(ActionsManager manager) { + // searching for Examples action category + ActionsCategory category = (ActionsCategory) manager.getActionFor(REPORTING); + + if (category == null) { + // creating new category + category = new MDActionsCategory(REPORTING, REPORTING); + category.setNested(true); + manager.addCategory(category); + } + for (NMAction action : actions) { + category.addAction(action); + } + } + + @Override + public int getPriority() { + return AMConfigurator.MEDIUM_PRIORITY; + } + +} \ No newline at end of file diff --git a/tools/io.incquery.cameo.model-coverage-report/src/main/java/io/incquery/cameo/modelcoveragereport/ModelCoverageReportPlugin.java b/tools/io.incquery.cameo.model-coverage-report/src/main/java/io/incquery/cameo/modelcoveragereport/ModelCoverageReportPlugin.java new file mode 100644 index 0000000..8d83873 --- /dev/null +++ b/tools/io.incquery.cameo.model-coverage-report/src/main/java/io/incquery/cameo/modelcoveragereport/ModelCoverageReportPlugin.java @@ -0,0 +1,28 @@ +package io.incquery.cameo.modelcoveragereport; + +import io.incquery.cameo.modelcoveragereport.actions.MetaModelCoverageAction; +import com.nomagic.magicdraw.actions.ActionsConfiguratorsManager; +import com.nomagic.magicdraw.plugins.Plugin; + +public class ModelCoverageReportPlugin extends Plugin { + + @Override + public boolean close() { + return true; + } + + @Override + public void init() { + + ActionsConfiguratorsManager manager = ActionsConfiguratorsManager.getInstance(); + manager.addMainMenuConfigurator(new MainMenuConfigurator(new MetaModelCoverageAction())); + manager.addContainmentBrowserContextConfigurator(new ProfileCoverageActionConfigurator()); + + } + + @Override + public boolean isSupported() { + return true; + } + +} diff --git a/tools/io.incquery.cameo.model-coverage-report/src/main/java/io/incquery/cameo/modelcoveragereport/ProfileCoverageActionConfigurator.java b/tools/io.incquery.cameo.model-coverage-report/src/main/java/io/incquery/cameo/modelcoveragereport/ProfileCoverageActionConfigurator.java new file mode 100644 index 0000000..5b53537 --- /dev/null +++ b/tools/io.incquery.cameo.model-coverage-report/src/main/java/io/incquery/cameo/modelcoveragereport/ProfileCoverageActionConfigurator.java @@ -0,0 +1,39 @@ +package io.incquery.cameo.modelcoveragereport; + +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + +import io.incquery.cameo.modelcoveragereport.actions.ProfileCoverageAction; +import com.nomagic.actions.AMConfigurator; +import com.nomagic.actions.ActionsCategory; +import com.nomagic.actions.ActionsManager; +import com.nomagic.magicdraw.actions.ActionsStateUpdater; +import com.nomagic.magicdraw.actions.BrowserContextAMConfigurator; +import com.nomagic.magicdraw.actions.MDActionsCategory; +import com.nomagic.magicdraw.ui.browser.Node; +import com.nomagic.magicdraw.ui.browser.Tree; +import com.nomagic.uml2.ext.magicdraw.mdprofiles.Profile; + +public class ProfileCoverageActionConfigurator implements BrowserContextAMConfigurator { + + @Override + public void configure(ActionsManager manager, Tree tree) { + ActionsCategory cat = manager.getCategory("REPORT"); + if(cat == null) { + cat = new MDActionsCategory("REPORT", "Generate Report..."); + manager.addCategory(cat); + } + Set selectedObjects = Arrays.stream(tree.getSelectedNodes()).map(Node::getUserObject) + .filter(Profile.class::isInstance).map(Profile.class::cast).collect(Collectors.toSet()); + if(!selectedObjects.isEmpty()) { + cat.addAction(new ProfileCoverageAction(selectedObjects.iterator().next())); + ActionsStateUpdater.updateActionsState(); + } + } + + @Override + public int getPriority() { + return AMConfigurator.MEDIUM_PRIORITY; + } +} diff --git a/tools/io.incquery.cameo.model-coverage-report/src/main/java/io/incquery/cameo/modelcoveragereport/actions/CoverageReportAction.java b/tools/io.incquery.cameo.model-coverage-report/src/main/java/io/incquery/cameo/modelcoveragereport/actions/CoverageReportAction.java new file mode 100644 index 0000000..d2d2dbf --- /dev/null +++ b/tools/io.incquery.cameo.model-coverage-report/src/main/java/io/incquery/cameo/modelcoveragereport/actions/CoverageReportAction.java @@ -0,0 +1,105 @@ +package io.incquery.cameo.modelcoveragereport.actions; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.nomagic.magicdraw.actions.MDAction; +import com.nomagic.magicdraw.core.Application; +import com.nomagic.magicdraw.core.GUILog; +import com.nomagic.magicdraw.core.Project; + +import javax.annotation.CheckForNull; +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Paths; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; + +public abstract class CoverageReportAction extends MDAction { + private static final long serialVersionUID = 1L; + protected static final String TEMPLATE__CAT_ID = "%S_COVERAGE_REPORT_GENERATOR"; + protected static final String TEMPLATE__CAT_NAME = "Generate %s Coverage Report"; + protected final String type; + private static File baseDir = null; + + @Override + public final void actionPerformed(@CheckForNull ActionEvent actionEvent) { + // select output directory + JFileChooser outputDirectorySelector = new JFileChooser(); + outputDirectorySelector.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + outputDirectorySelector.setDialogTitle("Output Directory Selector"); + outputDirectorySelector.setCurrentDirectory(baseDir); + if(outputDirectorySelector.showOpenDialog(Application.getInstance().getMainFrame()) == JFileChooser.APPROVE_OPTION) { + baseDir = outputDirectorySelector.getSelectedFile(); + } else { + return; + } + + // execute the report generation + Result result = actionPerformed(); + + // user feedback + GUILog log = Application.getInstance().getGUILog(); + if(result.succeeded) { + log.log(result.message); + } else if(result.error != null) { + log.showError(result.message, result.error); + } else { + log.showError(result.message); + } + } + + protected File getBaseDir() { + return baseDir; + } + + public abstract Result actionPerformed(); + + protected CoverageReportAction(String type) { + super(String.format(TEMPLATE__CAT_ID, type), String.format(TEMPLATE__CAT_NAME, type), null, null); + this.type = type.toLowerCase(); + } + protected final void serializeToJson(Object coverageDTO, File outputDir) { + Gson gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); + File outputFile = new File(outputDir, String.format("%sCoverageInfo.json", type)); + try (FileWriter writer = new FileWriter(outputFile)) { + gson.toJson(coverageDTO, writer); + } catch (IOException e) { + e.printStackTrace(); + } + } + + protected final File setupDestinationDirectory(Project project) { + Timestamp timestamp = new Timestamp(System.currentTimeMillis()); + SimpleDateFormat simpleDate = new SimpleDateFormat("yyyy.MM.dd_HH.mm.ss"); + String projectName = project.getName(); + + return new File(Paths.get( + getBaseDir() != null + ? getBaseDir().getAbsolutePath() + : "./ModelCoverageInfo", + projectName, + simpleDate.format(timestamp)).toString()); + } + + protected static final class Result { + public final boolean succeeded; + public final String message; + public final Throwable error; + + private static final String OUTPUT_TEMPLATE = "Report files were successfully generated into the following directory: %s"; + + public Result(File outputDir) { + this.succeeded = true; + this.message = String.format(OUTPUT_TEMPLATE, outputDir.toString()); + this.error = null; + } + public Result(String message, Throwable error) { + this.succeeded = true; + this.message = message; + this.error = error; + } + } +} diff --git a/tools/io.incquery.cameo.model-coverage-report/src/main/java/io/incquery/cameo/modelcoveragereport/actions/MetaModelCoverageAction.java b/tools/io.incquery.cameo.model-coverage-report/src/main/java/io/incquery/cameo/modelcoveragereport/actions/MetaModelCoverageAction.java new file mode 100644 index 0000000..2837534 --- /dev/null +++ b/tools/io.incquery.cameo.model-coverage-report/src/main/java/io/incquery/cameo/modelcoveragereport/actions/MetaModelCoverageAction.java @@ -0,0 +1,236 @@ +package io.incquery.cameo.modelcoveragereport.actions; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.stream.Collectors; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EDataType; +import org.eclipse.emf.ecore.EStructuralFeature; + +import io.incquery.cameo.modelcoveragereport.dtos.MetaModelCoverageDTO; +import com.nomagic.magicdraw.core.Application; +import com.nomagic.magicdraw.core.Project; +import com.nomagic.magicdraw.core.ProjectUtilities; +import com.nomagic.magicdraw.uml.BaseElement; +import com.nomagic.magicdraw.uml.Finder; +import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Element; +import com.nomagic.uml2.ext.magicdraw.metadata.UMLPackage; + +public class MetaModelCoverageAction extends CoverageReportAction { + + private static final long serialVersionUID = 2609117531520837581L; + private static final String MDTRANSIENT = "mdTransient"; + + public MetaModelCoverageAction() { + super("UML"); + } + + @Override + public Result actionPerformed() { + Project project = Application.getInstance().getProject(); + if(project == null) { + return new Result("You must open a project before generating the report", null); + } + File outputDir; + UMLPackage metamodel = UMLPackage.eINSTANCE; + + List eClasses = getEclasses(metamodel); + Set structuralFeatures = + getAllStructuralFeatures(metamodel); + Set filteredStructuralFeatures = + structuralFeatures.stream().filter(MetaModelCoverageAction::isFiltered).collect(Collectors.toSet()); + Set pureUmlFeatures = + structuralFeatures.stream().filter(MetaModelCoverageAction::isUmlStandard).collect(Collectors.toSet()); + + Map> eCoreCoverageMap = getClassCoverageMap(eClasses, project); + + outputDir = setupDestinationDirectory(project); + outputDir.mkdirs(); + + MetaModelCoverageDTO modelCoverage = MetaModelCoverageDTO.getInitializedInstance( + eClasses.size(), + structuralFeatures.size(), + filteredStructuralFeatures.size(), + pureUmlFeatures.size()); + + calculateEClassCoverage(eCoreCoverageMap, modelCoverage); + calculateEDataTypeCoverage(structuralFeatures, eCoreCoverageMap, modelCoverage); + modelCoverage.numOfCoveredFeatures = + calculateStructuralFeatureCoverage(structuralFeatures, eCoreCoverageMap, modelCoverage.coveredFeatures, modelCoverage.uncoveredFeatures, modelCoverage.features); + modelCoverage.coveredFilteredFeatures = + calculateStructuralFeatureCoverage(filteredStructuralFeatures, eCoreCoverageMap, null, modelCoverage.uncoveredFilteredFeatures, null); + + modelCoverage.coveredPureUMLFeatures = + calculateStructuralFeatureCoverage(pureUmlFeatures, eCoreCoverageMap, null, modelCoverage.uncoveredPureUMLFeatures, null); + + serializeToJson(modelCoverage, outputDir); + serializeToCsv(modelCoverage, outputDir); + + return new Result(outputDir); + } + + public static boolean isUmlStandard(EStructuralFeature struct) { + return !struct.getName().startsWith("_"); + } + + public static boolean isFiltered(EStructuralFeature struct) { + return !struct.isDerived() && struct.isChangeable() && !struct.isTransient() + && struct.getEAnnotations().stream().noneMatch(annot -> MDTRANSIENT.equals(annot.getSource())); + } + + private Map> getClassCoverageMap(List eClasses, Project project) { + Map> eCoreToModelElementsMap = new HashMap<>(); + for (EClassifier eclass : eClasses) { + Collection foundElements = Finder.byTypeRecursively() + .find(project.getPrimaryModel(), new Class[] { eclass.getInstanceClass() }).stream() + .collect(Collectors.toList()); + + if (!ProjectUtilities.isRemote(project.getPrimaryProject())) { + foundElements = foundElements.stream().filter(BaseElement::isEditable).collect(Collectors.toList()); + } + eCoreToModelElementsMap.put(eclass, foundElements); + } + return eCoreToModelElementsMap; + } + + private Set getAllStructuralFeatures(UMLPackage metamodel) { + return metamodel.getEClassifiers().stream() + .filter(EClass.class::isInstance).map(EClass.class::cast) + .flatMap(cls -> cls.getEStructuralFeatures().stream()).collect(Collectors.toSet()); + } + + private List getEclasses(UMLPackage metamodel) { + List eclasses = new ArrayList<>(); + for (EClassifier classifier : metamodel.getEClassifiers()) { + if((classifier instanceof EClass && !((EClass) classifier).isAbstract() && !((EClass) classifier).isInterface()) || + (classifier instanceof EDataType && classifier.getInstanceClass() != null && !classifier.getInstanceClass().isPrimitive() && classifier != metamodel.getString())) { + eclasses.add(classifier); + } + } + + return eclasses; + } + + public int calculateStructuralFeatureCoverage(Set structuralFeatures, + Map> eclassMap, Collection coveredFeatures, Collection uncoveredFeatures, Map features) { + int numOfCoveredFeatures = 0; + for (EStructuralFeature feature : structuralFeatures) { + if (isFeatureCovered(feature, eclassMap)) { + numOfCoveredFeatures++; + if(coveredFeatures != null) { + coveredFeatures.add(getStringRepresentationOfFeature(feature)); + } + if(features != null) { + features.put(feature, true); + } + } else { + uncoveredFeatures.add(getStringRepresentationOfFeature(feature)); + if(features != null) { + features.put(feature, false); + } + } + } + return numOfCoveredFeatures; + } + + private String getStringRepresentationOfFeature(EStructuralFeature feature) { + return feature.getEContainingClass().getName() + "::" + feature.getName(); + } + + public boolean isFeatureCovered(EStructuralFeature feature, Map> eclassMap) { + EClass containingEClass = feature.getEContainingClass(); + for (Entry> eClass : eclassMap.entrySet()) { + EClassifier cls = eClass.getKey(); + if (cls instanceof EClass && (cls == containingEClass || ((EClass) cls).getEAllSuperTypes().contains(containingEClass))) { + for (Element instance : eClass.getValue()) { + if (instance.eGet(feature) != null) { + return true; + } + } + } + } + return false; + } + + public void calculateEClassCoverage(Map> eclassMap, + MetaModelCoverageDTO coverageInfo) { + for (Entry> cls : eclassMap.entrySet()) { + if(cls.getKey() instanceof EClass) { + if (cls.getValue().isEmpty()) { + coverageInfo.uncoveredClasses.add(cls.getKey().getName()); + } else { + coverageInfo.numOfCoveredClasses++; + coverageInfo.coveredClasses.add(cls.getKey().getName()); + } + } + } + } + + private void calculateEDataTypeCoverage(Set structuralFeatures, + Map> eCoreCoverageMap, MetaModelCoverageDTO modelCoverage) { + eCoreCoverageMap.keySet().stream() + .filter(EDataType.class::isInstance).map(EDataType.class::cast) + .forEach(dt -> { + boolean dtFound = structuralFeatures.stream() + // sometimes different EDataTypes has the same instance class so practically they + // can be covered the same features (e.g. ParameterEffectKind and ParameterParameterEffectKind) + .filter(feature -> dt == feature.getEType() || dt.getInstanceClass() == feature.getEType().getInstanceClass()) + .anyMatch(feature -> isFeatureCovered(feature, eCoreCoverageMap)); + if(dtFound) { + modelCoverage.numOfCoveredClasses++; + modelCoverage.coveredClasses.add(dt.getName()); + } else { + modelCoverage.uncoveredClasses.add(dt.getName()); + } + }); + } + + private void serializeToCsv(MetaModelCoverageDTO coverageDTO, File outputDir) { + File typeOutputFile = new File(outputDir, String.format("%sTypeCoverageInfo.csv", type)); + try (FileWriter writer = new FileWriter(typeOutputFile)) { + writer.write("Type,Covered"+System.lineSeparator()); + for(String cl : coverageDTO.coveredClasses) { + writer.write(cl+",true"+System.lineSeparator()); + } + for(String cl : coverageDTO.uncoveredClasses) { + writer.write(cl+",false"+System.lineSeparator()); + } + } catch (IOException e) { + e.printStackTrace(); + } + File featureOutputFile = new File(outputDir, String.format("%sFeatureCoverageInfo.csv", type)); + try (FileWriter writer = new FileWriter(featureOutputFile)) { + writer.write("Feature,Covered,Defining Type,UML Standard,Filtered"+System.lineSeparator()); + for(Map.Entry feature : coverageDTO.features.entrySet()) { + writer.write(String.format("%s,%b,%s,%b,%b%s", + feature.getKey().getName(), + feature.getValue(), + definingTypeString(feature.getKey()), + isUmlStandard(feature.getKey()), + isFiltered(feature.getKey()), + System.lineSeparator())); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + private String definingTypeString(EStructuralFeature feature) { + EClass type = feature.getEContainingClass(); + if(type.isAbstract() || type.isInterface()) { + return "[Abstract] "+type.getName(); + } else { + return type.getName(); + } + } +} diff --git a/tools/io.incquery.cameo.model-coverage-report/src/main/java/io/incquery/cameo/modelcoveragereport/actions/ProfileCoverageAction.java b/tools/io.incquery.cameo.model-coverage-report/src/main/java/io/incquery/cameo/modelcoveragereport/actions/ProfileCoverageAction.java new file mode 100644 index 0000000..b3ae6fd --- /dev/null +++ b/tools/io.incquery.cameo.model-coverage-report/src/main/java/io/incquery/cameo/modelcoveragereport/actions/ProfileCoverageAction.java @@ -0,0 +1,157 @@ +package io.incquery.cameo.modelcoveragereport.actions; + +import io.incquery.cameo.modelcoveragereport.dtos.ProfileCoverageDTO; +import com.nomagic.magicdraw.core.Application; +import com.nomagic.magicdraw.core.Project; +import com.nomagic.magicdraw.core.ProjectUtilities; +import com.nomagic.magicdraw.uml.Finder; +import com.nomagic.uml2.ext.jmi.helpers.StereotypesHelper; +import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Element; +import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Property; +import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Slot; +import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.VisibilityKindEnum; +import com.nomagic.uml2.ext.magicdraw.mdprofiles.Profile; +import com.nomagic.uml2.ext.magicdraw.mdprofiles.Stereotype; +import com.nomagic.uml2.ext.magicdraw.metadata.UMLPackage; + +import java.io.File; +import java.util.*; +import java.util.Map.Entry; +import java.util.stream.Collectors; + +public class ProfileCoverageAction extends CoverageReportAction { + private static final long serialVersionUID = 7959507763497894824L; + private transient Element selectedObjects; + + public ProfileCoverageAction(Profile selectedObjects) { + super(selectedObjects.getName()); + this.selectedObjects = selectedObjects; + } + + @Override + public Result actionPerformed() { + Project project = Application.getInstance().getProject(); + File outputDir = setupDestinationDirectory(project); + outputDir.mkdirs(); + + Collection stereotypes = getAllStereotypes(selectedObjects); + Collection filteredStereotypes = filterStereotpyes(stereotypes); + Collection propertysOfStereotyps = getPropertysOfStereotyps(stereotypes); + ProfileCoverageDTO profileCoverageBean = new ProfileCoverageDTO(filteredStereotypes.size(), propertysOfStereotyps.size()); + + Map> stereotypedElementsMap = + calculateStereotypedElementsMap(filteredStereotypes, project); + Map> propertyCoverageMap = + calculatePropertyMap(stereotypedElementsMap, propertysOfStereotyps); + + calculateStereotypesCoverage(stereotypedElementsMap, profileCoverageBean); + + calculatePropertyCoverage(propertyCoverageMap, profileCoverageBean); + + serializeToJson(profileCoverageBean, outputDir); + + return new Result(outputDir); + } + + private Map> calculatePropertyMap( + Map> stereotypedElementsMap, Collection propertysOfStereotyps) { + Map> propertyCoverageMap = new HashMap<>(); + for (Property property : propertysOfStereotyps) { + propertyCoverageMap.put(property, new HashSet<>()); + } + + for (Entry> stereotypedElements : stereotypedElementsMap.entrySet()) { + Stereotype stereo = stereotypedElements.getKey(); + Collection currentProps = stereo.getMember().stream() + .filter(Property.class::isInstance).map(Property.class::cast) + .filter(prop -> prop.getVisibility() == VisibilityKindEnum.PUBLIC) + .collect(Collectors.toList()); + for (Element stereotypedElement : stereotypedElements.getValue()) { + for (Property property : currentProps) { + // MD190sp4 version + Slot slot = StereotypesHelper.getSlot(stereotypedElement, stereo, property, false, false); + if (slot != null) { + propertyCoverageMap.get(property).add(slot); + } + // MD2021xr1 version +// TaggedValue val = StereotypesHelper.getTaggedValue(stereotypedElement, property); +// if(val != null && val.hasValue()) { +// propertyCoverageMap.get(property).add(val); +// } + } + } + } + return propertyCoverageMap; + } + + private Map> calculateStereotypedElementsMap( + Collection filteredStereotypes, Project project) { + Map> stereotypedElementsMap = new HashMap<>(); + + for (Stereotype stereo : filteredStereotypes) { + stereotypedElementsMap.put(stereo, new HashSet<>()); + } + + project.getPrimaryModel().eAllContents().forEachRemaining(con -> { + if (con instanceof Element) { + Element elem = (Element) con; + if (elem.isEditable() || ProjectUtilities.isRemote( project.getPrimaryProject())) { + Collection foundStereotypes = StereotypesHelper + .getAllAssignedStereotypes(Collections.singletonList(elem)); + for (Stereotype st : foundStereotypes) { + if (stereotypedElementsMap.containsKey(st)) { + stereotypedElementsMap.get(st).add(elem); + } + } + } + } + + }); + return stereotypedElementsMap; + } + + private Collection getAllStereotypes(Element profile) { + return Finder.byTypeRecursively() + .find(profile, new Class[] { UMLPackage.eINSTANCE.getStereotype().getInstanceClass() }).stream() + .map(Stereotype.class::cast).collect(Collectors.toList()); + } + + private Collection filterStereotpyes(Collection stereotypes) { + return stereotypes.stream().filter(str -> !str.isAbstract()).collect(Collectors.toList()); + } + + private Collection getPropertysOfStereotyps(Collection stereotypes) { + return stereotypes.stream().flatMap(str -> str.getOwnedAttribute().stream()) + .filter(prop -> prop.getVisibility() == VisibilityKindEnum.PUBLIC).collect(Collectors.toList()); + } + + public void calculateStereotypesCoverage(Map> stereotypedElementsMap, ProfileCoverageDTO profileCoverageBean) { + int coveredStereotypes = 0; + for (Entry> stereotypedElements : stereotypedElementsMap.entrySet()) { + String stereoName = stereotypedElements.getKey().getQualifiedName(); + if (stereotypedElements.getValue().isEmpty()) { + profileCoverageBean.uncoveredStereotypes.add(stereoName); + } else { + coveredStereotypes++; + profileCoverageBean.coveredStereotypes.add(stereoName); + } + } + profileCoverageBean.numOfCoveredStereotypes = coveredStereotypes; + } + + public void calculatePropertyCoverage(Map> propertyCoverageMap, ProfileCoverageDTO profileCoverageBean) { + int coveredPublicProperties = 0; + for (Entry> properties : propertyCoverageMap.entrySet()) { + String propName = properties.getKey().getQualifiedName(); + if (properties.getValue().isEmpty()) { + profileCoverageBean.uncoveredPublicProperties.add(propName); + } else { + coveredPublicProperties++; + profileCoverageBean.coveredPublicProperties.add(propName); + } + } + profileCoverageBean.numOfCoveredPublicProperties = coveredPublicProperties; + + } + +} diff --git a/tools/io.incquery.cameo.model-coverage-report/src/main/java/io/incquery/cameo/modelcoveragereport/dtos/MetaModelCoverageDTO.java b/tools/io.incquery.cameo.model-coverage-report/src/main/java/io/incquery/cameo/modelcoveragereport/dtos/MetaModelCoverageDTO.java new file mode 100644 index 0000000..775e9b2 --- /dev/null +++ b/tools/io.incquery.cameo.model-coverage-report/src/main/java/io/incquery/cameo/modelcoveragereport/dtos/MetaModelCoverageDTO.java @@ -0,0 +1,81 @@ +package io.incquery.cameo.modelcoveragereport.dtos; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeSet; + +import com.google.gson.annotations.SerializedName; +import org.eclipse.emf.ecore.EStructuralFeature; + +public class MetaModelCoverageDTO { + + @SerializedName(value = "numOfAllClasses") + public int allClasses; + + @SerializedName(value = "numOfCoveredClasses") + public int numOfCoveredClasses; + + @SerializedName(value = "numOfAllFeatures") + public int allFeatures; + + @SerializedName(value = "numOfCoveredFeatures") + public int numOfCoveredFeatures; + + @SerializedName(value = "numOfAllFilteredFeatures") + public int allFilteredFeatures; + + @SerializedName(value = "numOfCoveredFilteredFeatures") + public int coveredFilteredFeatures; + + @SerializedName(value = "numOfAllPureUMLFeatures") + public int allPureUMLFeatures; + + @SerializedName(value = "numOfCoveredPureUMLFeatures") + public int coveredPureUMLFeatures; + + @SerializedName(value = "coveredClasses") + public Collection coveredClasses; + + @SerializedName(value = "uncoveredClasses") + public Collection uncoveredClasses; + + @SerializedName(value = "coveredFeatures") + public Collection coveredFeatures; + + public transient Map features; + + @SerializedName(value = "uncoveredFeatures") + public Collection uncoveredFeatures; + + @SerializedName(value = "uncoveredFilteredFeatures") + public Collection uncoveredFilteredFeatures; + + @SerializedName(value = "uncoveredPureUMLFeatures") + public Collection uncoveredPureUMLFeatures; + + public static MetaModelCoverageDTO getInitializedInstance(int allClasses, int allFeatures, int allFilteredFeatures, int allPureUMLFeatures) { + MetaModelCoverageDTO newBean = new MetaModelCoverageDTO(); + newBean.allClasses = allClasses; + newBean.allFeatures = allFeatures; + newBean.allFilteredFeatures = allFilteredFeatures; + newBean.allPureUMLFeatures = allPureUMLFeatures; + newBean.features = new HashMap<>(); + newBean.coveredClasses = new TreeSet<>(); + newBean.uncoveredClasses = new TreeSet<>(); + newBean.coveredFeatures = new TreeSet<>(); + newBean.uncoveredFeatures = new TreeSet<>(); + newBean.uncoveredFilteredFeatures = new TreeSet<>(); + newBean.uncoveredPureUMLFeatures = new TreeSet<>(); + return newBean; + } + + @Override + public String toString() { + return "ModelCoverageBean [\n\t Number of all eClasses=" + allClasses + ",\n\t Number of covered classes=" + + numOfCoveredClasses + ",\n\t Number of all eFeatures=" + allFeatures + + ",\n\t Number of all Filtered eFeatures=" + allFilteredFeatures + ",\n\t Number of covered eFeatures=" + + numOfCoveredFeatures + ",\n\t List of uncovered classes=" + uncoveredClasses + ",\n\t uncoveredFeatures=" + + uncoveredFeatures + "]"; + } +} diff --git a/tools/io.incquery.cameo.model-coverage-report/src/main/java/io/incquery/cameo/modelcoveragereport/dtos/ProfileCoverageDTO.java b/tools/io.incquery.cameo.model-coverage-report/src/main/java/io/incquery/cameo/modelcoveragereport/dtos/ProfileCoverageDTO.java new file mode 100644 index 0000000..da7598d --- /dev/null +++ b/tools/io.incquery.cameo.model-coverage-report/src/main/java/io/incquery/cameo/modelcoveragereport/dtos/ProfileCoverageDTO.java @@ -0,0 +1,53 @@ +package io.incquery.cameo.modelcoveragereport.dtos; + +import java.util.Collection; +import java.util.TreeSet; + +import com.google.gson.annotations.SerializedName; + +public class ProfileCoverageDTO { + + @SerializedName(value = "numOfAllStereotypes") + public int allStereotypes; + + @SerializedName(value = "numOfCoveredStereotypes") + public int numOfCoveredStereotypes; + + @SerializedName(value = "numOfAllPublicProperties") + public int allPublicProperties; + + @SerializedName(value = "numOfCoveredPublicProperties") + public int numOfCoveredPublicProperties; + + @SerializedName(value = "coveredStereotypes") + public Collection coveredStereotypes; + + @SerializedName(value = "uncoveredStereotypes") + public Collection uncoveredStereotypes; + + @SerializedName(value = "coveredPublicProperties") + public Collection coveredPublicProperties; + + @SerializedName(value = "uncoveredPublicProperties") + public Collection uncoveredPublicProperties; + + public ProfileCoverageDTO(int allStereotypes, int allPublicProperties) { + super(); + this.allStereotypes = allStereotypes; + this.allPublicProperties = allPublicProperties; + this.coveredStereotypes = new TreeSet<>(); + this.uncoveredStereotypes = new TreeSet<>(); + this.coveredPublicProperties = new TreeSet<>(); + this.uncoveredPublicProperties = new TreeSet<>(); + } + + @Override + public String toString() { + return "ModelCoverageBean [\n\t Number of all Stereotypes=" + allStereotypes + + ",\n\t Number of covered Stereotypes=" + numOfCoveredStereotypes + ",\n\t Number of all Public Properties=" + + allPublicProperties + ",\n\t Number of covered Public Properties=" + numOfCoveredPublicProperties + + ",\n\t List of uncovered Stereotypes=" + uncoveredStereotypes + ",\n\t uncoveredPublicProperties=" + + uncoveredPublicProperties + "]"; + } + +}