From 05cf2e9aaf5df4fe395a8c168114c864036df536 Mon Sep 17 00:00:00 2001 From: Johannes Brandt <5931715+eTralse@users.noreply.github.com> Date: Wed, 11 Jul 2018 16:20:55 +0200 Subject: [PATCH 01/37] Changes priority of Ecore DiagramTextProvider to "normal". MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Priority "normal" or "5" (Integer equivalent) is needed to trump the Ecore visualiser developed by Hallvard Trætteberg et al at https://github.com/hallvard/plantuml. --- org.moflon.core.ui/plugin.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.moflon.core.ui/plugin.xml b/org.moflon.core.ui/plugin.xml index 4ba391e5..4dab65f8 100644 --- a/org.moflon.core.ui/plugin.xml +++ b/org.moflon.core.ui/plugin.xml @@ -240,7 +240,7 @@ - + From 9175f4322b01fcc890d6553b85cfc5a0c034fd29 Mon Sep 17 00:00:00 2001 From: Johannes Brandt <5931715+eTralse@users.noreply.github.com> Date: Wed, 11 Jul 2018 18:39:39 +0200 Subject: [PATCH 02/37] Refactors Visualiser to support checking mechanisms for editor and selection without calculating the actual diagram text. This change is introduced to conform to the PlantUML API. It may as well reduce computation time, although negligible. However, it is important for understanding and maintaining the code to have these methods implemented as intended. Both methods clearly describe which editors (or in this case their input) and which selections are supported. supportsEditor(...) checks for proper file extensions of the file currently edited, e.g. "ecore", and as well whether or not the editor internally handles EObjects via its EditingDomain. supportsSelection(...) checks the selection for elements that cannot be displayed. If there are any, the selection will be reported as not supported. Utility methods have been added with VisualiserUtilities. The intention of this class is to provide most of the behavior which interacts with the Eclipse Platform API, to keep the Visualiser code as clean as possible. This should in turn improve the maintainability of the Visualiser code. --- org.moflon.core.ui/META-INF/MANIFEST.MF | 1 + .../moflon/core/ui/VisualiserUtilities.java | 235 ++++++++++++++++++ .../EMoflonModelAndMetamodelVisualiser.java | 47 +++- 3 files changed, 273 insertions(+), 10 deletions(-) create mode 100644 org.moflon.core.ui/src/org/moflon/core/ui/VisualiserUtilities.java diff --git a/org.moflon.core.ui/META-INF/MANIFEST.MF b/org.moflon.core.ui/META-INF/MANIFEST.MF index 644af445..49e5f9c7 100644 --- a/org.moflon.core.ui/META-INF/MANIFEST.MF +++ b/org.moflon.core.ui/META-INF/MANIFEST.MF @@ -34,4 +34,5 @@ Export-Package: org.moflon.core.ui, Bundle-Activator: org.moflon.core.ui.MoflonCoreUiActivator Bundle-ActivationPolicy: lazy Automatic-Module-Name: org.moflon.core.ui +Import-Package: org.eclipse.emf.edit.domain diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/VisualiserUtilities.java b/org.moflon.core.ui/src/org/moflon/core/ui/VisualiserUtilities.java new file mode 100644 index 00000000..dcf0d0c1 --- /dev/null +++ b/org.moflon.core.ui/src/org/moflon/core/ui/VisualiserUtilities.java @@ -0,0 +1,235 @@ +/** + * + */ +package org.moflon.core.ui; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.core.resources.IFile; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.edit.domain.EditingDomain; +import org.eclipse.emf.edit.domain.IEditingDomainProvider; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IFileEditorInput; +import org.moflon.core.ui.visualisation.EMoflonVisualiser; + +/** + * Utility class with the intend to support implementations of + * {@link EMoflonVisualiser} in interacting with the Eclipse platform. + * + * @author Johannes Brandt + * + */ +public class VisualiserUtilities { + + /** + *

+ * Checks whether or not the given editor currently has a file open with the + * given file extension. + *

+ * + * @param editor + * whose file input shall be checked for the given file extension + * @param expectedExtension + * that is to be compared with the extension of the currently opened + * file in the editor. If null is given, an empty string + * will be used for comparison. + * @return true: only if... + * + * false: otherwise. + */ + public static boolean checkFileExtensionSupport(IEditorPart editor, String expectedExtension) { + if (editor == null || editor.getEditorInput() == null + || !(editor.getEditorInput() instanceof IFileEditorInput)) { + return false; + } + + IFile file = ((IFileEditorInput) editor.getEditorInput()).getFile(); + if (file == null || file.getFileExtension() == null) { + return false; + } + + final String checkedFileExtension = (expectedExtension != null) ? expectedExtension : ""; + return file.getFileExtension().equals(checkedFileExtension); + } + + /** + * Retrieves all Ecore modeling elements from the given editor, if the editor + * handles Ecore modeling elements. + * + * @param editor + * The editor, of which all Ecore modeling elements are to be + * extracted. + * @return null is returned, if the given editor is + * null or does not implement the + * {@link IEditingDomainProvider} interface, and if the + * {@link EditingDomain} or the {@link ResourceSet} of the editor is + * null. If there is a {@link ResourceSet} stored with the + * editor, then all resources will be extracted and the resulting list + * of {@link EObject}s is returned. This list can be empty, if no + * {@link EObject}s are stored in the editor's resources, however, no + * null references will be contained. + */ + public static List extractEcoreElements(IEditorPart editor) { + if (editor == null || !(editor instanceof IEditingDomainProvider)) { + return null; + } + + IEditingDomainProvider edp = (IEditingDomainProvider) editor; + if (edp.getEditingDomain() == null || edp.getEditingDomain().getResourceSet() == null) { + return null; + } + + return expandResources(edp.getEditingDomain().getResourceSet()); + } + + /** + * Checks, if the given {@link ISelection} object contains an Ecore selection. + * + * An Ecore selection does not contain null references, only + * {@link EObject} and {@link Resource} references. + * + * @param selection + * The selection object which is to be checked. + * @return true if the given selection only contains + * {@link EObject} and {@link Resource} references. false + * otherwise. + */ + public static boolean isEcoreSelection(ISelection selection) { + if (selection == null || !(selection instanceof IStructuredSelection)) { + return false; + } + + List internalSelection = ((IStructuredSelection) selection).toList(); + if (internalSelection == null) { + return false; + } + + for (Object obj : internalSelection) { + // an Ecore selection must not contain null references + if (obj == null) { + return false; + } + // an Ecore selection must contain Resources and EObjects only + if (!(obj instanceof EObject) && !(obj instanceof Resource)) { + return false; + } + } + + return true; + } + + /** + * Retrieves the Ecore selection from the given {@link ISelection}, if possible. + * + *

+ * Note: Calls {@link VisualiserUtilities#isEcoreSelection(ISelection)}, + * to check whether the given selection is an Ecore selection. If not, + * null is returned. + *

+ * + * @param selection + * from which all elements are to be returned, if they are not + * null. The given selection cannot be + * null. + * @return null is returned, if the given selection is not an Ecore + * selection. Otherwise the internal selection is extracted and + * returned. The returned list can be empty, if the selection is empty. + * The wrapped list won't contain any null references. + */ + public static List extractEcoreSelection(ISelection selection) { + if (!isEcoreSelection(selection)) { + return null; + } + + // retrieve internal selection + List internalSelection = ((IStructuredSelection) selection).toList(); + + // expand resources and add them to result + List result = internalSelection.stream()// + .filter(e -> e != null)// + .filter(Resource.class::isInstance)// + .map(Resource.class::cast)// + .flatMap(VisualiserUtilities::expandResource)// + .collect(Collectors.toList()); + + // enhances performance of contains operation later + HashSet cache = new HashSet<>(result); + + // add remaining EObjects of selection, apart from those already contained in + // resources, to the result. + internalSelection.stream()// + .filter(e -> e != null)// + .filter(EObject.class::isInstance)// + .map(EObject.class::cast)// + .filter(e -> !cache.contains(e))// + .forEach(result::add); + + return result; + } + + /** + * Extracts all {@link EObject} instances from a given {@link ResourceSet}. + * + *

+ * Note: The order of the returned elements is prescribed by + * {@link Resource#getAllContents()}. + *

+ * + *

+ * Note: There won't be any null references in the returned + * list. + *

+ * + * @param resources + * of which all contained elements are to be returned - cannot be + * null + * @return the {@link EObject} instances contained in each of the given + * resources, in order of expansion + */ + public static List expandResources(ResourceSet resources) { + return resources.getResources().stream()// + .filter(res -> res != null)// + .flatMap(VisualiserUtilities::expandResource)// + .collect(Collectors.toList()); + } + + /** + * Extracts all {@link EObject} instances from a given {@link Resource}. + * + *

+ * Note: The order of the returned elements is prescribed by + * {@link Resource#getAllContents()}. + *

+ * + *

+ * Note: There won't be any null references in the returned + * stream. + *

+ * + * @param resource + * The resource that is to be expanded. + * @return The stream of {@link EObject} instances in order of expansions. + */ + private static Stream expandResource(Resource resource) { + List elements = new ArrayList<>(); + resource.getAllContents().forEachRemaining(elements::add); + return elements.stream()// + .filter(elem -> elem != null); + } +} diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelAndMetamodelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelAndMetamodelVisualiser.java index fbdb9d5c..f171f0bf 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelAndMetamodelVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelAndMetamodelVisualiser.java @@ -21,9 +21,11 @@ import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.ui.IEditorPart; +import org.moflon.core.ui.VisualiserUtilities; public class EMoflonModelAndMetamodelVisualiser extends EMoflonVisualiser { private IEditorPart editor; + private boolean isEmptySelectionSupported = false; @Override protected String getDiagramBody(IEditorPart editor, ISelection selection) { @@ -55,8 +57,8 @@ private Collection handleEOpposites(Collection refs) { return refsWithOnlyOneEOpposite; } - - private Collection handleEOppositesForLinks(Collection links){ + + private Collection handleEOppositesForLinks(Collection links) { Collection linksWithOnlyOneEOpposite = new ArrayList<>(); for (VisualEdge link : links) { if (!link.hasEOpposite() || !linksWithOnlyOneEOpposite.contains(link.findEOpposite(links).orElse(null))) @@ -205,17 +207,17 @@ private boolean checkForCorrespondenceModel(Collection chosenObjectsfro private boolean objectIsACorrespondenceLink(EObject next) { EStructuralFeature refSrc = next.eClass().getEStructuralFeature("source"); EStructuralFeature refTrg = next.eClass().getEStructuralFeature("target"); - - if(refSrc != null && refTrg != null) { - if(refSrc instanceof EReference && refTrg instanceof EReference) { + + if (refSrc != null && refTrg != null) { + if (refSrc instanceof EReference && refTrg instanceof EReference) { EObject src = (EObject) next.eGet(refSrc); EObject trg = (EObject) next.eGet(refTrg); - - if(src != null && trg != null) + + if (src != null && trg != null) return true; } } - + return false; } @@ -242,12 +244,37 @@ private Collection determineLinksToVisualizeForCorrModel( @Override public boolean supportsEditor(IEditorPart editor) { this.editor = editor; - return extractMetamodelElementsFromEditor(editor).isPresent() - || extractModelElementsFromEditor(editor).isPresent(); + + // check if editor currently has Ecore related model loaded + boolean hasEcoreFileLoaded = VisualiserUtilities.checkFileExtensionSupport(editor, "ecore") + || VisualiserUtilities.checkFileExtensionSupport(editor, "xmi") + || VisualiserUtilities.checkFileExtensionSupport(editor, "genmodel"); + + // check if the editor internally handles Ecore EObjects + List allElements = VisualiserUtilities.extractEcoreElements(editor); + isEmptySelectionSupported = allElements != null; + + // if only one of the above conditions is true, there is still a possibility + // that a given selection might be supported + return hasEcoreFileLoaded || isEmptySelectionSupported; } @Override public boolean supportsSelection(ISelection selection) { + // only Ecore selections are supported + if (!VisualiserUtilities.isEcoreSelection(selection)) { + return false; + } + + // empty Ecore selections are supported only if the editor can provide Ecore + // elements, this is checked and remembered in supportsEditor(...) + List ecoreSelection = VisualiserUtilities.extractEcoreSelection(selection); + if (ecoreSelection.size() == 0 && !isEmptySelectionSupported) { + return false; + } + + // TODO: add distinction for metamodels and models + return true; } } From 4119f036471a095561d5ef52debc9aedc05ab9d5 Mon Sep 17 00:00:00 2001 From: Johannes Brandt <5931715+eTralse@users.noreply.github.com> Date: Fri, 20 Jul 2018 09:40:16 +0200 Subject: [PATCH 03/37] Splits behavior for visualising Ecore metamodels and models. This is done to improve maintainability. Common behavior is moved to a common super class EMoflonEcoreVisualiser, whereas metamodel or model specific code is moved to the according subclass EMoflonMetamodelVisualiser and EMoflonModelVisualiser. Additional, more precise code for PlantUML API interaction has been added, to determine as precise as possible, whether a given selection is handled by the metamodel or model visualiser, or not at all. Ecore visualisers now should act as intended by the PlantUML API. Also removes code to handle correspondence model. Firstly, this functionality does not belong with functionality to visualise Ecore metamodels or models. Secondly, the improved checking methods for supported editors and selections allow for a more appropriate visualiser to handle correspondence models. --- org.moflon.core.ui/plugin.xml | 6 +- .../visualisation/EMoflonEcoreVisualiser.java | 112 +++++++ .../EMoflonMetamodelVisualiser.java | 134 +++++++++ .../EMoflonModelAndMetamodelVisualiser.java | 280 ------------------ .../visualisation/EMoflonModelVisualiser.java | 156 ++++++++++ 5 files changed, 406 insertions(+), 282 deletions(-) create mode 100644 org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java create mode 100644 org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java delete mode 100644 org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelAndMetamodelVisualiser.java create mode 100644 org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java diff --git a/org.moflon.core.ui/plugin.xml b/org.moflon.core.ui/plugin.xml index 4dab65f8..965918e5 100644 --- a/org.moflon.core.ui/plugin.xml +++ b/org.moflon.core.ui/plugin.xml @@ -240,7 +240,9 @@ - - + + + + diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java new file mode 100644 index 00000000..c557f9d3 --- /dev/null +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java @@ -0,0 +1,112 @@ +/** + * + */ +package org.moflon.core.ui.visualisation; + +import java.util.List; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ui.IEditorPart; +import org.moflon.core.ui.VisualiserUtilities; + +/** + * Abstract implementation for the visualisation of Ecore metamodels and models. + * + * @author Johannes Brandt (initial contribution) + * + */ +public abstract class EMoflonEcoreVisualiser extends EMoflonVisualiser { + + /** + * Stores whether or not the superset of Ecore elements can be retrieved from + * currently associated editor. + */ + private boolean isEmptySelectionSupported = false; + + /** + * Stores the superset of Ecore elements, that are to be visualised. + */ + private List latestEditorContents; + + /** + * Stores a subset of Ecore elements, that are to be visualised. + */ + private List latestSelection; + + @Override + public boolean supportsEditor(IEditorPart editor) { + // check if editor currently has Ecore related model loaded + boolean hasEcoreFileLoaded = VisualiserUtilities.checkFileExtensionSupport(editor, "ecore") + || VisualiserUtilities.checkFileExtensionSupport(editor, "xmi"); + // || VisualiserUtilities.checkFileExtensionSupport(editor, "genmodel"); + + // Check if the editor internally handles Ecore EObjects. + // Since some editors allow to load both .ecore and .xmi Resources at the same + // time, it is not possible to check for specific elements from Ecore metamodels + // or models. This has to be done in #supportsSelection(...), when it is clear + // whether the selection is empty or not. + latestEditorContents = VisualiserUtilities.extractEcoreElements(editor); + isEmptySelectionSupported = latestEditorContents != null; + + // if only one of the above conditions is true, there is still a possibility + // that a given selection might be supported + return hasEcoreFileLoaded || isEmptySelectionSupported; + } + + @Override + public boolean supportsSelection(ISelection selection) { + // only Ecore selections are supported + if (!VisualiserUtilities.isEcoreSelection(selection)) { + return false; + } + + // empty Ecore selections are supported only if the editor can provide Ecore + // elements, this is checked and remembered in supportsEditor(...) + latestSelection = VisualiserUtilities.extractEcoreSelection(selection); + if (latestSelection.isEmpty() && !isEmptySelectionSupported) { + return false; + } + + boolean isSupported = supportsSelection(latestEditorContents, latestSelection); + return isSupported; + } + + /** + * Checks whether or not a given Ecore selection, with respect to a superset of + * Ecore elements, is supposed to be supported by this Ecore visualiser. + * + * @param superset + * All Ecore elements that could be visualised. Contains the given + * selection as a subset. + * @param selection + * All Ecore elements that supposed to be visualised. + * @return true if the given selection is supported, otherwise + * false. + */ + protected abstract boolean supportsSelection(List superset, List selection); + + @Override + public String getDiagramBody(IEditorPart editor, ISelection selection) { + if (latestSelection.isEmpty() && latestEditorContents.isEmpty()) { + // If both editor and selection are empty, an empty diagram is returned. + return EMoflonPlantUMLGenerator.emptyDiagram(); + } else if (latestSelection.isEmpty()) { + // Instead of an empty selection the whole editor content is visualised. + return getDiagramBody(latestEditorContents); + } else { + return getDiagramBody(latestSelection); + } + } + + /** + * Calculates the diagram text for the given Ecore elements. + * + * @param elements + * The Ecore elements that are to be visualised. + * @return The generated diagram text describing the given elements using the + * PlantUML DSL. + */ + protected abstract String getDiagramBody(List elements); + +} diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java new file mode 100644 index 00000000..cc69606d --- /dev/null +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java @@ -0,0 +1,134 @@ +/** + * + */ +package org.moflon.core.ui.visualisation; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.tuple.Pair; +import org.eclipse.emf.common.util.TreeIterator; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EGenericType; +import org.eclipse.emf.ecore.EModelElement; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.IEditorPart; + +/** + * Visualises UML Class Diagrams for Ecore metamodels. + * + */ +public class EMoflonMetamodelVisualiser extends EMoflonEcoreVisualiser { + + @Override + public boolean supportsSelection(List superset, List selection) { + // If no element is selected, the whole editor content is to be displayed, + // therefore it must be checked for the correct Ecore elements. + List ecoreSelection = selection.isEmpty() ? superset : selection; + + // An Ecore metamodel must contain EModelElements only. If it contains other + // elements, the selection is not supported by this visualiser. + return !ecoreSelection.stream()// + .filter(e -> !(e instanceof EModelElement))// + .filter(e -> !(e instanceof EGenericType))// + .findAny()// + .isPresent(); + } + + @Override + public String getDiagramBody(IEditorPart editor, ISelection selection) { + // TODO: refactor + return maybeVisualiseMetamodel(editor)// + .orElse(EMoflonPlantUMLGenerator.emptyDiagram());// + } + + @Override + protected String getDiagramBody(List elements) { + throw new UnsupportedOperationException("Not yet implemented!"); + } + + // -------------------------------------------------------------------------- + // -------------------------------------------------------------------------- + // -------------------------------------------------------------------------- + // TODO: REFACTOR + private Optional maybeVisualiseMetamodel(IEditorPart editor) { + return extractMetamodelElementsFromEditor(editor) + .map(p -> EMoflonPlantUMLGenerator.visualiseEcoreElements(p.getLeft(), handleEOpposites(p.getRight()))); + } + + private Collection handleEOpposites(Collection refs) { + Collection refsWithOnlyOneEOpposite = new ArrayList<>(); + for (EReference eReference : refs) { + if (eReference.getEOpposite() == null || !refsWithOnlyOneEOpposite.contains(eReference.getEOpposite())) + refsWithOnlyOneEOpposite.add(eReference); + } + + return refsWithOnlyOneEOpposite; + } + + private Optional, Collection>> extractMetamodelElementsFromEditor( + IEditorPart editor) { + return Optional.of(editor)// + .flatMap(this::multiSelectionInEcoreEditor)// + .map(this::determineClassesAndRefsToVisualise)// + .map(p -> p.getLeft().isEmpty() ? null : p);// + } + + @SuppressWarnings("rawtypes") + private Optional multiSelectionInEcoreEditor(IEditorPart editor) { + return Optional.of(editor.getSite().getSelectionProvider())// + .flatMap(maybeCast(ISelectionProvider.class))// + .map(ISelectionProvider::getSelection)// + .flatMap(maybeCast(IStructuredSelection.class))// + .map(IStructuredSelection::toList);// + } + + private Pair, Collection> determineClassesAndRefsToVisualise( + Collection selection) { + Collection chosenClassesfromResource = new ArrayList(); + if (selection.size() == 1 && !resourceChosen(selection).isEmpty()) { + TreeIterator eAllContents = resourceChosen(selection).get(0).getAllContents(); + while (eAllContents.hasNext()) { + EObject next = eAllContents.next(); + if (next instanceof EClass) { + chosenClassesfromResource.add((EClass) next); + } + } + + return Pair.of(chosenClassesfromResource, determineReferencesToVisualize(chosenClassesfromResource)); + } + + else { + Collection chosenClasses = selection.stream()// + .filter(EClass.class::isInstance)// + .map(EClass.class::cast)// + .collect(Collectors.toSet());// + + return Pair.of(chosenClasses, determineReferencesToVisualize(chosenClasses)); + } + } + + private List resourceChosen(Collection selection) { + List resourceChosen = selection.stream()// + .filter(Resource.class::isInstance)// + .map(Resource.class::cast)// + .collect(Collectors.toList());// + return resourceChosen; + + } + + private Collection determineReferencesToVisualize(Collection chosenClasses) { + Collection refs = chosenClasses.stream()// + .flatMap(c -> c.getEReferences().stream())// + .collect(Collectors.toSet());// + return refs; + } +} diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelAndMetamodelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelAndMetamodelVisualiser.java deleted file mode 100644 index f171f0bf..00000000 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelAndMetamodelVisualiser.java +++ /dev/null @@ -1,280 +0,0 @@ -package org.moflon.core.ui.visualisation; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -import org.apache.commons.lang3.tuple.Pair; -import org.eclipse.emf.common.util.TreeIterator; -import org.eclipse.emf.ecore.EClass; -import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.EReference; -import org.eclipse.emf.ecore.EStructuralFeature; -import org.eclipse.emf.ecore.resource.Resource; -import org.eclipse.emf.ecore.util.EContentsEList; -import org.eclipse.emf.ecore.util.EContentsEList.FeatureIterator; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.ISelectionProvider; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.ui.IEditorPart; -import org.moflon.core.ui.VisualiserUtilities; - -public class EMoflonModelAndMetamodelVisualiser extends EMoflonVisualiser { - private IEditorPart editor; - private boolean isEmptySelectionSupported = false; - - @Override - protected String getDiagramBody(IEditorPart editor, ISelection selection) { - return maybeVisualiseMetamodel(editor)// - .orElse(maybeVisualiseModel(editor)// - .orElse(EMoflonPlantUMLGenerator.emptyDiagram()));// - } - - @SuppressWarnings("rawtypes") - private Optional multiSelectionInEcoreEditor(IEditorPart editor) { - return Optional.of(editor.getSite().getSelectionProvider())// - .flatMap(maybeCast(ISelectionProvider.class))// - .map(ISelectionProvider::getSelection)// - .flatMap(maybeCast(IStructuredSelection.class))// - .map(IStructuredSelection::toList);// - } - - private Optional maybeVisualiseMetamodel(IEditorPart editor) { - return extractMetamodelElementsFromEditor(editor) - .map(p -> EMoflonPlantUMLGenerator.visualiseEcoreElements(p.getLeft(), handleEOpposites(p.getRight()))); - } - - private Collection handleEOpposites(Collection refs) { - Collection refsWithOnlyOneEOpposite = new ArrayList<>(); - for (EReference eReference : refs) { - if (eReference.getEOpposite() == null || !refsWithOnlyOneEOpposite.contains(eReference.getEOpposite())) - refsWithOnlyOneEOpposite.add(eReference); - } - - return refsWithOnlyOneEOpposite; - } - - private Collection handleEOppositesForLinks(Collection links) { - Collection linksWithOnlyOneEOpposite = new ArrayList<>(); - for (VisualEdge link : links) { - if (!link.hasEOpposite() || !linksWithOnlyOneEOpposite.contains(link.findEOpposite(links).orElse(null))) - linksWithOnlyOneEOpposite.add(link); - } - - return linksWithOnlyOneEOpposite; - } - - private Optional maybeVisualiseModel(IEditorPart editor) { - return extractModelElementsFromEditor(editor).map(p -> { - if (checkForCorrespondenceModel(p.getLeft())) { - return EMoflonPlantUMLGenerator.visualiseCorrModel(p.getLeft(), - sourceTargetObjectsForCorrespondenceModel(p.getLeft(), "source"), - sourceTargetObjectsForCorrespondenceModel(p.getLeft(), "target"), - determineLinksToVisualizeForCorrModel(p.getLeft())); - } else { - return EMoflonPlantUMLGenerator.visualiseModelElements(p.getLeft(), p.getRight()); - } - }); - } - - private Optional, Collection>> extractModelElementsFromEditor( - IEditorPart editor2) { - return Optional.of(editor)// - .flatMap(this::multiSelectionInEcoreEditor)// - .map(this::determineObjectsAndLinksToVisualise)// - .map(p -> p.getLeft().isEmpty() ? null : p);// - } - - private Optional, Collection>> extractMetamodelElementsFromEditor( - IEditorPart editor) { - return Optional.of(editor)// - .flatMap(this::multiSelectionInEcoreEditor)// - .map(this::determineClassesAndRefsToVisualise)// - .map(p -> p.getLeft().isEmpty() ? null : p);// - } - - private Pair, Collection> determineClassesAndRefsToVisualise( - Collection selection) { - Collection chosenClassesfromResource = new ArrayList(); - if (selection.size() == 1 && !resourceChosen(selection).isEmpty()) { - TreeIterator eAllContents = resourceChosen(selection).get(0).getAllContents(); - while (eAllContents.hasNext()) { - EObject next = eAllContents.next(); - if (next instanceof EClass) { - chosenClassesfromResource.add((EClass) next); - } - } - - return Pair.of(chosenClassesfromResource, determineReferencesToVisualize(chosenClassesfromResource)); - } - - else { - Collection chosenClasses = selection.stream()// - .filter(EClass.class::isInstance)// - .map(EClass.class::cast)// - .collect(Collectors.toSet());// - - return Pair.of(chosenClasses, determineReferencesToVisualize(chosenClasses)); - } - } - - private Pair, Collection> determineObjectsAndLinksToVisualise( - Collection selection) { - Collection chosenObjectsfromResource = new ArrayList(); - if (selection.size() == 1 && !resourceChosen(selection).isEmpty()) { - TreeIterator eAllContents = resourceChosen(selection).get(0).getAllContents(); - while (eAllContents.hasNext()) { - EObject next = eAllContents.next(); - if (next instanceof EObject) - chosenObjectsfromResource.add(next); - } - - return Pair.of(chosenObjectsfromResource, determineLinksToVisualize(chosenObjectsfromResource)); - } - - else { - Collection chosenObjects = selection.stream()// - .filter(EObject.class::isInstance)// - .map(EObject.class::cast)// - .collect(Collectors.toSet());// - - return Pair.of(chosenObjects, determineLinksToVisualize(chosenObjects)); - } - } - - private Collection determineReferencesToVisualize(Collection chosenClasses) { - Collection refs = chosenClasses.stream()// - .flatMap(c -> c.getEReferences().stream())// - .collect(Collectors.toSet());// - return refs; - } - - @SuppressWarnings("rawtypes") - private Collection determineLinksToVisualize(Collection chosenObjects) { - Collection links = new HashSet<>(); - for (EObject o : new ArrayList(chosenObjects)) { - for (EContentsEList.FeatureIterator featureIterator = // - (EContentsEList.FeatureIterator) o.eCrossReferences().iterator(); featureIterator.hasNext();) { - addVisualEdge(featureIterator, chosenObjects, links, o); - } - for (EContentsEList.FeatureIterator featureIterator = // - (EContentsEList.FeatureIterator) o.eContents().iterator(); featureIterator.hasNext();) { - addVisualEdge(featureIterator, chosenObjects, links, o); - } - } - - return handleEOppositesForLinks(links); - } - - @SuppressWarnings("rawtypes") - private void addVisualEdge(FeatureIterator featureIterator, Collection chosenObjects, - Collection refs, EObject src) { - EObject trg = (EObject) featureIterator.next(); - EReference eReference = (EReference) featureIterator.feature(); - if (chosenObjects.contains(trg)) - refs.add(new VisualEdge(eReference, src, trg)); - } - - private List resourceChosen(Collection selection) { - List resourceChosen = selection.stream()// - .filter(Resource.class::isInstance)// - .map(Resource.class::cast)// - .collect(Collectors.toList());// - return resourceChosen; - - } - - private boolean checkForCorrespondenceModel(Collection chosenObjectsfromResource) { - // We do not expect any links to be present - if (!determineLinksToVisualize(chosenObjectsfromResource).isEmpty()) - return false; - - // We expect all objects to be of the form <--src--corr--trg--> - Iterator eAllContents = chosenObjectsfromResource.iterator(); - while (eAllContents.hasNext()) { - EObject next = eAllContents.next(); - if (!objectIsACorrespondenceLink(next)) - return false; - } - - return true; - } - - private boolean objectIsACorrespondenceLink(EObject next) { - EStructuralFeature refSrc = next.eClass().getEStructuralFeature("source"); - EStructuralFeature refTrg = next.eClass().getEStructuralFeature("target"); - - if (refSrc != null && refTrg != null) { - if (refSrc instanceof EReference && refTrg instanceof EReference) { - EObject src = (EObject) next.eGet(refSrc); - EObject trg = (EObject) next.eGet(refTrg); - - if (src != null && trg != null) - return true; - } - } - - return false; - } - - private Collection sourceTargetObjectsForCorrespondenceModel(Collection chosenObjectsfromResource, - String sourceOrTarget) { - Collection sourceOrTargetObjects = new ArrayList(); - Iterator eAllContents = chosenObjectsfromResource.iterator(); - while (eAllContents.hasNext()) { - EObject next = eAllContents.next(); - sourceOrTargetObjects.add((EObject) next.eGet(next.eClass().getEStructuralFeature(sourceOrTarget))); - } - - return sourceOrTargetObjects; - } - - private Collection determineLinksToVisualizeForCorrModel( - Collection chosenObjectsfromResource) { - Collection correspondenceObjects = new ArrayList(); - correspondenceObjects.addAll(sourceTargetObjectsForCorrespondenceModel(chosenObjectsfromResource, "source")); - correspondenceObjects.addAll(sourceTargetObjectsForCorrespondenceModel(chosenObjectsfromResource, "target")); - return determineLinksToVisualize(correspondenceObjects); - } - - @Override - public boolean supportsEditor(IEditorPart editor) { - this.editor = editor; - - // check if editor currently has Ecore related model loaded - boolean hasEcoreFileLoaded = VisualiserUtilities.checkFileExtensionSupport(editor, "ecore") - || VisualiserUtilities.checkFileExtensionSupport(editor, "xmi") - || VisualiserUtilities.checkFileExtensionSupport(editor, "genmodel"); - - // check if the editor internally handles Ecore EObjects - List allElements = VisualiserUtilities.extractEcoreElements(editor); - isEmptySelectionSupported = allElements != null; - - // if only one of the above conditions is true, there is still a possibility - // that a given selection might be supported - return hasEcoreFileLoaded || isEmptySelectionSupported; - } - - @Override - public boolean supportsSelection(ISelection selection) { - // only Ecore selections are supported - if (!VisualiserUtilities.isEcoreSelection(selection)) { - return false; - } - - // empty Ecore selections are supported only if the editor can provide Ecore - // elements, this is checked and remembered in supportsEditor(...) - List ecoreSelection = VisualiserUtilities.extractEcoreSelection(selection); - if (ecoreSelection.size() == 0 && !isEmptySelectionSupported) { - return false; - } - - // TODO: add distinction for metamodels and models - - return true; - } -} diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java new file mode 100644 index 00000000..bdc4aaba --- /dev/null +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java @@ -0,0 +1,156 @@ +/** + * + */ +package org.moflon.core.ui.visualisation; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.tuple.Pair; +import org.eclipse.emf.common.util.TreeIterator; +import org.eclipse.emf.ecore.EGenericType; +import org.eclipse.emf.ecore.EModelElement; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.util.EContentsEList; +import org.eclipse.emf.ecore.util.EContentsEList.FeatureIterator; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.IEditorPart; + +/** + * Visualises UML Object Diagrams for Ecore models. + * + */ +public class EMoflonModelVisualiser extends EMoflonEcoreVisualiser { + + @Override + public boolean supportsSelection(List superset, List selection) { + // If no element is selected, the whole editor content is to be displayed, + // therefore it must be checked for the correct Ecore elements. + List ecoreSelection = selection.isEmpty() ? superset : selection; + + // An Ecore model must contain EObjects only, which are not EModelElements. If + // it contains other + // elements, the selection is not supported by this visualiser. + return !ecoreSelection.stream()// + .filter(e -> e instanceof EModelElement || e instanceof EGenericType)// + .findAny()// + .isPresent(); + } + + @Override + public String getDiagramBody(IEditorPart editor, ISelection selection) { + // TODO: refactor + return maybeVisualiseModel(editor)// + .orElse(EMoflonPlantUMLGenerator.emptyDiagram());// + } + + @Override + protected String getDiagramBody(List elements) { + throw new UnsupportedOperationException("Not yet implemented!"); + } + + // -------------------------------------------------------------------------- + // -------------------------------------------------------------------------- + // -------------------------------------------------------------------------- + // TODO: REFACTOR + + private Optional maybeVisualiseModel(IEditorPart editor) { + return extractModelElementsFromEditor(editor)// + .map(p -> EMoflonPlantUMLGenerator.visualiseModelElements(p.getLeft(), p.getRight())); + } + + private Optional, Collection>> extractModelElementsFromEditor( + IEditorPart editor2) { + return Optional.of(editor2)// + .flatMap(this::multiSelectionInEcoreEditor)// + .map(this::determineObjectsAndLinksToVisualise)// + .map(p -> p.getLeft().isEmpty() ? null : p);// + } + + @SuppressWarnings("rawtypes") + private Optional multiSelectionInEcoreEditor(IEditorPart editor) { + return Optional.of(editor.getSite().getSelectionProvider())// + .flatMap(maybeCast(ISelectionProvider.class))// + .map(ISelectionProvider::getSelection)// + .flatMap(maybeCast(IStructuredSelection.class))// + .map(IStructuredSelection::toList);// + } + + private Pair, Collection> determineObjectsAndLinksToVisualise( + Collection selection) { + Collection chosenObjectsfromResource = new ArrayList(); + if (selection.size() == 1 && !resourceChosen(selection).isEmpty()) { + TreeIterator eAllContents = resourceChosen(selection).get(0).getAllContents(); + while (eAllContents.hasNext()) { + EObject next = eAllContents.next(); + if (next instanceof EObject) + chosenObjectsfromResource.add(next); + } + + return Pair.of(chosenObjectsfromResource, determineLinksToVisualize(chosenObjectsfromResource)); + } + + else { + Collection chosenObjects = selection.stream()// + .filter(EObject.class::isInstance)// + .map(EObject.class::cast)// + .collect(Collectors.toSet());// + + return Pair.of(chosenObjects, determineLinksToVisualize(chosenObjects)); + } + } + + private List resourceChosen(Collection selection) { + List resourceChosen = selection.stream()// + .filter(Resource.class::isInstance)// + .map(Resource.class::cast)// + .collect(Collectors.toList());// + return resourceChosen; + + } + + @SuppressWarnings("rawtypes") + private Collection determineLinksToVisualize(Collection chosenObjects) { + Collection links = new HashSet<>(); + for (EObject o : new ArrayList(chosenObjects)) { + for (EContentsEList.FeatureIterator featureIterator = // + (EContentsEList.FeatureIterator) o.eCrossReferences().iterator(); featureIterator.hasNext();) { + addVisualEdge(featureIterator, chosenObjects, links, o); + } + for (EContentsEList.FeatureIterator featureIterator = // + (EContentsEList.FeatureIterator) o.eContents().iterator(); featureIterator.hasNext();) { + addVisualEdge(featureIterator, chosenObjects, links, o); + } + } + + return handleEOppositesForLinks(links); + } + + @SuppressWarnings("rawtypes") + private void addVisualEdge(FeatureIterator featureIterator, Collection chosenObjects, + Collection refs, EObject src) { + EObject trg = (EObject) featureIterator.next(); + EReference eReference = (EReference) featureIterator.feature(); + if (chosenObjects.contains(trg)) + refs.add(new VisualEdge(eReference, src, trg)); + } + + private Collection handleEOppositesForLinks(Collection links) { + Collection linksWithOnlyOneEOpposite = new ArrayList<>(); + for (VisualEdge link : links) { + if (!link.hasEOpposite() || !linksWithOnlyOneEOpposite.contains(link.findEOpposite(links).orElse(null))) + linksWithOnlyOneEOpposite.add(link); + } + + return linksWithOnlyOneEOpposite; + } + +} From b223b9776f60f5404136e8004309cc0a8b3e8004 Mon Sep 17 00:00:00 2001 From: Johannes Brandt <5931715+eTralse@users.noreply.github.com> Date: Fri, 20 Jul 2018 11:34:36 +0200 Subject: [PATCH 04/37] Refactoring: Removes Eclipse Platform interaction code from metamodel and model visualisers. Since supportsEditor(...) and supportsSelection(...) already required functionality to extract selections from IEditorPart and ISelection instances, it was no longer required to keep this code in the respective implementations for metamodel and model visualisers. Likewise for the expansion of Resource elements. The functionality is given in VisualiserUtilities since commit 9175f4322b01fcc890d6553b85cfc5a0c034fd29. --- .../visualisation/EMoflonEcoreVisualiser.java | 1 + .../EMoflonMetamodelVisualiser.java | 81 ++++--------------- .../visualisation/EMoflonModelVisualiser.java | 73 ++--------------- 3 files changed, 24 insertions(+), 131 deletions(-) diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java index c557f9d3..78fb087b 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java @@ -88,6 +88,7 @@ public boolean supportsSelection(ISelection selection) { @Override public String getDiagramBody(IEditorPart editor, ISelection selection) { + // TODO: Fix case in which selection is null and editor contains both metamodel and model elements. if (latestSelection.isEmpty() && latestEditorContents.isEmpty()) { // If both editor and selection are empty, an empty diagram is returned. return EMoflonPlantUMLGenerator.emptyDiagram(); diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java index cc69606d..d80321c8 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java @@ -6,21 +6,14 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.Optional; import java.util.stream.Collectors; import org.apache.commons.lang3.tuple.Pair; -import org.eclipse.emf.common.util.TreeIterator; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EGenericType; import org.eclipse.emf.ecore.EModelElement; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; -import org.eclipse.emf.ecore.resource.Resource; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.ISelectionProvider; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.ui.IEditorPart; /** * Visualises UML Class Diagrams for Ecore metamodels. @@ -43,26 +36,16 @@ public boolean supportsSelection(List superset, List selection .isPresent(); } - @Override - public String getDiagramBody(IEditorPart editor, ISelection selection) { - // TODO: refactor - return maybeVisualiseMetamodel(editor)// - .orElse(EMoflonPlantUMLGenerator.emptyDiagram());// - } - @Override protected String getDiagramBody(List elements) { - throw new UnsupportedOperationException("Not yet implemented!"); + Pair, Collection> p = determineClassesAndRefsToVisualise(elements); + return EMoflonPlantUMLGenerator.visualiseEcoreElements(p.getLeft(), handleEOpposites(p.getRight())); } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // TODO: REFACTOR - private Optional maybeVisualiseMetamodel(IEditorPart editor) { - return extractMetamodelElementsFromEditor(editor) - .map(p -> EMoflonPlantUMLGenerator.visualiseEcoreElements(p.getLeft(), handleEOpposites(p.getRight()))); - } private Collection handleEOpposites(Collection refs) { Collection refsWithOnlyOneEOpposite = new ArrayList<>(); @@ -74,55 +57,21 @@ private Collection handleEOpposites(Collection refs) { return refsWithOnlyOneEOpposite; } - private Optional, Collection>> extractMetamodelElementsFromEditor( - IEditorPart editor) { - return Optional.of(editor)// - .flatMap(this::multiSelectionInEcoreEditor)// - .map(this::determineClassesAndRefsToVisualise)// - .map(p -> p.getLeft().isEmpty() ? null : p);// - } - - @SuppressWarnings("rawtypes") - private Optional multiSelectionInEcoreEditor(IEditorPart editor) { - return Optional.of(editor.getSite().getSelectionProvider())// - .flatMap(maybeCast(ISelectionProvider.class))// - .map(ISelectionProvider::getSelection)// - .flatMap(maybeCast(IStructuredSelection.class))// - .map(IStructuredSelection::toList);// - } - private Pair, Collection> determineClassesAndRefsToVisualise( - Collection selection) { - Collection chosenClassesfromResource = new ArrayList(); - if (selection.size() == 1 && !resourceChosen(selection).isEmpty()) { - TreeIterator eAllContents = resourceChosen(selection).get(0).getAllContents(); - while (eAllContents.hasNext()) { - EObject next = eAllContents.next(); - if (next instanceof EClass) { - chosenClassesfromResource.add((EClass) next); - } - } - - return Pair.of(chosenClassesfromResource, determineReferencesToVisualize(chosenClassesfromResource)); - } - - else { - Collection chosenClasses = selection.stream()// - .filter(EClass.class::isInstance)// - .map(EClass.class::cast)// - .collect(Collectors.toSet());// - - return Pair.of(chosenClasses, determineReferencesToVisualize(chosenClasses)); - } - } - - private List resourceChosen(Collection selection) { - List resourceChosen = selection.stream()// - .filter(Resource.class::isInstance)// - .map(Resource.class::cast)// - .collect(Collectors.toList());// - return resourceChosen; + Collection selection) { + // TODO: Fix selection bug where only EClasses will be visualised, but not + // eclasses of operations / attributes and other lower-level EModelElements. + // Instead, if the selection contains such an element, it should be replaced by + // its defining EClass, if this EClass is not already present in the selection. + // TODO: Fix bug where EPackages cannot be visualised. Instead, they should + // represent their content, i.e. all EClass and elements contained in EClasses + // (see above) should be visualised. + Collection chosenClasses = selection.stream()// + .filter(EClass.class::isInstance)// + .map(EClass.class::cast)// + .collect(Collectors.toSet());// + return Pair.of(chosenClasses, determineReferencesToVisualize(chosenClasses)); } private Collection determineReferencesToVisualize(Collection chosenClasses) { diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java index bdc4aaba..8d599728 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java @@ -7,22 +7,15 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; -import java.util.Optional; import java.util.stream.Collectors; import org.apache.commons.lang3.tuple.Pair; -import org.eclipse.emf.common.util.TreeIterator; import org.eclipse.emf.ecore.EGenericType; import org.eclipse.emf.ecore.EModelElement; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; -import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.EContentsEList; import org.eclipse.emf.ecore.util.EContentsEList.FeatureIterator; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.ISelectionProvider; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.ui.IEditorPart; /** * Visualises UML Object Diagrams for Ecore models. @@ -45,16 +38,10 @@ public boolean supportsSelection(List superset, List selection .isPresent(); } - @Override - public String getDiagramBody(IEditorPart editor, ISelection selection) { - // TODO: refactor - return maybeVisualiseModel(editor)// - .orElse(EMoflonPlantUMLGenerator.emptyDiagram());// - } - @Override protected String getDiagramBody(List elements) { - throw new UnsupportedOperationException("Not yet implemented!"); + Pair, Collection> p = determineObjectsAndLinksToVisualise(elements); + return EMoflonPlantUMLGenerator.visualiseModelElements(p.getLeft(), p.getRight()); } // -------------------------------------------------------------------------- @@ -62,58 +49,15 @@ protected String getDiagramBody(List elements) { // -------------------------------------------------------------------------- // TODO: REFACTOR - private Optional maybeVisualiseModel(IEditorPart editor) { - return extractModelElementsFromEditor(editor)// - .map(p -> EMoflonPlantUMLGenerator.visualiseModelElements(p.getLeft(), p.getRight())); - } - - private Optional, Collection>> extractModelElementsFromEditor( - IEditorPart editor2) { - return Optional.of(editor2)// - .flatMap(this::multiSelectionInEcoreEditor)// - .map(this::determineObjectsAndLinksToVisualise)// - .map(p -> p.getLeft().isEmpty() ? null : p);// - } - - @SuppressWarnings("rawtypes") - private Optional multiSelectionInEcoreEditor(IEditorPart editor) { - return Optional.of(editor.getSite().getSelectionProvider())// - .flatMap(maybeCast(ISelectionProvider.class))// - .map(ISelectionProvider::getSelection)// - .flatMap(maybeCast(IStructuredSelection.class))// - .map(IStructuredSelection::toList);// - } - private Pair, Collection> determineObjectsAndLinksToVisualise( - Collection selection) { - Collection chosenObjectsfromResource = new ArrayList(); - if (selection.size() == 1 && !resourceChosen(selection).isEmpty()) { - TreeIterator eAllContents = resourceChosen(selection).get(0).getAllContents(); - while (eAllContents.hasNext()) { - EObject next = eAllContents.next(); - if (next instanceof EObject) - chosenObjectsfromResource.add(next); - } - - return Pair.of(chosenObjectsfromResource, determineLinksToVisualize(chosenObjectsfromResource)); - } + Collection selection) { - else { - Collection chosenObjects = selection.stream()// - .filter(EObject.class::isInstance)// - .map(EObject.class::cast)// - .collect(Collectors.toSet());// + Collection chosenObjects = selection.stream()// + .filter(EObject.class::isInstance)// + .map(EObject.class::cast)// + .collect(Collectors.toSet());// - return Pair.of(chosenObjects, determineLinksToVisualize(chosenObjects)); - } - } - - private List resourceChosen(Collection selection) { - List resourceChosen = selection.stream()// - .filter(Resource.class::isInstance)// - .map(Resource.class::cast)// - .collect(Collectors.toList());// - return resourceChosen; + return Pair.of(chosenObjects, determineLinksToVisualize(chosenObjects)); } @@ -152,5 +96,4 @@ private Collection handleEOppositesForLinks(Collection l return linksWithOnlyOneEOpposite; } - } From a6ec3e2101973d75e3f3803d220783923fb3b01e Mon Sep 17 00:00:00 2001 From: Johannes Brandt <5931715+eTralse@users.noreply.github.com> Date: Fri, 20 Jul 2018 13:03:32 +0200 Subject: [PATCH 05/37] Fixes bug where metamodel and model elements are visualised in the same diagram. Reason for this bug is, that even if a selection is reported to be not supported (in contrast to the editor), PlantUML will call getDiagramText(...) with a null selection. Semi-correctly, it was assumed that the editor output can be used to generate the diagram text. However, it was not checked wether or not the editor output can be clearly classified to contain only metamodel or model elements. After adding this check and performing some extra improvements on the EMoflonEcoreVisualiser code the bug had been fixed. --- .../moflon/core/ui/VisualiserUtilities.java | 39 +++++++++++++- .../visualisation/EMoflonEcoreVisualiser.java | 51 +++++++++---------- .../EMoflonMetamodelVisualiser.java | 15 ++---- .../visualisation/EMoflonModelVisualiser.java | 14 ++--- 4 files changed, 69 insertions(+), 50 deletions(-) diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/VisualiserUtilities.java b/org.moflon.core.ui/src/org/moflon/core/ui/VisualiserUtilities.java index dcf0d0c1..6d271b60 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/VisualiserUtilities.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/VisualiserUtilities.java @@ -10,6 +10,8 @@ import java.util.stream.Stream; import org.eclipse.core.resources.IFile; +import org.eclipse.emf.ecore.EGenericType; +import org.eclipse.emf.ecore.EModelElement; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; @@ -167,7 +169,7 @@ public static List extractEcoreSelection(ISelection selection) { .map(Resource.class::cast)// .flatMap(VisualiserUtilities::expandResource)// .collect(Collectors.toList()); - + // enhances performance of contains operation later HashSet cache = new HashSet<>(result); @@ -232,4 +234,39 @@ private static Stream expandResource(Resource resource) { return elements.stream()// .filter(elem -> elem != null); } + + /** + * States whether the given list of elements contains any metamodel elements. + * + * @param elements + * The list of elements that is to be checked for the existence of + * metamodel elements. This list, nor any of its elements, must be + * null. + * @return true, if the given list contains at least one metamodel + * element. false otherwise. + */ + public static boolean hasMetamodelElements(List elements) { + return elements.stream()// + .filter(e -> e instanceof EModelElement || e instanceof EGenericType)// + .findAny()// + .isPresent(); + } + + /** + * States whether the given list of elements contains any model elements. + * + * @param elements + * The list of elements that is to be checked for the existence of + * model elements. This list, nor any of its elements, must be + * null. + * @return true, if the given list contains at least one model + * element. false otherwise. + */ + public static boolean hasModelElements(List elements) { + return elements.stream()// + .filter(e -> !(e instanceof EModelElement))// + .filter(e -> !(e instanceof EGenericType))// + .findAny()// + .isPresent(); + } } diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java index 78fb087b..b1e676b3 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java @@ -25,12 +25,7 @@ public abstract class EMoflonEcoreVisualiser extends EMoflonVisualiser { private boolean isEmptySelectionSupported = false; /** - * Stores the superset of Ecore elements, that are to be visualised. - */ - private List latestEditorContents; - - /** - * Stores a subset of Ecore elements, that are to be visualised. + * Stores a subset of Ecore elements, that are to be visualised.. */ private List latestSelection; @@ -46,8 +41,8 @@ public boolean supportsEditor(IEditorPart editor) { // time, it is not possible to check for specific elements from Ecore metamodels // or models. This has to be done in #supportsSelection(...), when it is clear // whether the selection is empty or not. - latestEditorContents = VisualiserUtilities.extractEcoreElements(editor); - isEmptySelectionSupported = latestEditorContents != null; + latestSelection = VisualiserUtilities.extractEcoreElements(editor); + isEmptySelectionSupported = latestSelection != null; // if only one of the above conditions is true, there is still a possibility // that a given selection might be supported @@ -63,41 +58,45 @@ public boolean supportsSelection(ISelection selection) { // empty Ecore selections are supported only if the editor can provide Ecore // elements, this is checked and remembered in supportsEditor(...) - latestSelection = VisualiserUtilities.extractEcoreSelection(selection); - if (latestSelection.isEmpty() && !isEmptySelectionSupported) { + List ecoreSelection = VisualiserUtilities.extractEcoreSelection(selection); + if (ecoreSelection == null || ecoreSelection.isEmpty()) { return false; } + latestSelection = ecoreSelection; - boolean isSupported = supportsSelection(latestEditorContents, latestSelection); + boolean isSupported = supportsSelection(latestSelection); return isSupported; } /** - * Checks whether or not a given Ecore selection, with respect to a superset of - * Ecore elements, is supposed to be supported by this Ecore visualiser. + * Checks whether or not a given Ecore selection is supposed to be supported by + * this Ecore visualiser. * - * @param superset - * All Ecore elements that could be visualised. Contains the given - * selection as a subset. * @param selection - * All Ecore elements that supposed to be visualised. + * All Ecore elements that are supposed to be visualised. * @return true if the given selection is supported, otherwise * false. */ - protected abstract boolean supportsSelection(List superset, List selection); + protected abstract boolean supportsSelection(List selection); @Override public String getDiagramBody(IEditorPart editor, ISelection selection) { - // TODO: Fix case in which selection is null and editor contains both metamodel and model elements. - if (latestSelection.isEmpty() && latestEditorContents.isEmpty()) { - // If both editor and selection are empty, an empty diagram is returned. + // In order to save processing time latestSelection already contains the + // best fit for an ecore selection and does not have to be recalculated from + // editor and selection. It is determined during the calls of + // supportsEdidtor(...) and supportsSelection(...). + // Note that if a specific selection is null, empty, or simply not supported, + // latestSelection is supposed to contain the whole editor output. + // This in turn can be again null, empty or not supported, because the check for + // editor support is quite tolerant. This is why this has to be checked here + // again. + if (latestSelection == null || latestSelection.isEmpty()) { + return EMoflonPlantUMLGenerator.emptyDiagram(); + } else if (VisualiserUtilities.hasMetamodelElements(latestSelection) + && VisualiserUtilities.hasModelElements(latestSelection)) { return EMoflonPlantUMLGenerator.emptyDiagram(); - } else if (latestSelection.isEmpty()) { - // Instead of an empty selection the whole editor content is visualised. - return getDiagramBody(latestEditorContents); - } else { - return getDiagramBody(latestSelection); } + return getDiagramBody(latestSelection); } /** diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java index d80321c8..da9d2a7e 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java @@ -10,10 +10,9 @@ import org.apache.commons.lang3.tuple.Pair; import org.eclipse.emf.ecore.EClass; -import org.eclipse.emf.ecore.EGenericType; -import org.eclipse.emf.ecore.EModelElement; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; +import org.moflon.core.ui.VisualiserUtilities; /** * Visualises UML Class Diagrams for Ecore metamodels. @@ -22,18 +21,10 @@ public class EMoflonMetamodelVisualiser extends EMoflonEcoreVisualiser { @Override - public boolean supportsSelection(List superset, List selection) { - // If no element is selected, the whole editor content is to be displayed, - // therefore it must be checked for the correct Ecore elements. - List ecoreSelection = selection.isEmpty() ? superset : selection; - + public boolean supportsSelection(List selection) { // An Ecore metamodel must contain EModelElements only. If it contains other // elements, the selection is not supported by this visualiser. - return !ecoreSelection.stream()// - .filter(e -> !(e instanceof EModelElement))// - .filter(e -> !(e instanceof EGenericType))// - .findAny()// - .isPresent(); + return !VisualiserUtilities.hasModelElements(selection); } @Override diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java index 8d599728..5beb612f 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java @@ -10,12 +10,11 @@ import java.util.stream.Collectors; import org.apache.commons.lang3.tuple.Pair; -import org.eclipse.emf.ecore.EGenericType; -import org.eclipse.emf.ecore.EModelElement; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.util.EContentsEList; import org.eclipse.emf.ecore.util.EContentsEList.FeatureIterator; +import org.moflon.core.ui.VisualiserUtilities; /** * Visualises UML Object Diagrams for Ecore models. @@ -24,18 +23,11 @@ public class EMoflonModelVisualiser extends EMoflonEcoreVisualiser { @Override - public boolean supportsSelection(List superset, List selection) { - // If no element is selected, the whole editor content is to be displayed, - // therefore it must be checked for the correct Ecore elements. - List ecoreSelection = selection.isEmpty() ? superset : selection; - + public boolean supportsSelection(List selection) { // An Ecore model must contain EObjects only, which are not EModelElements. If // it contains other // elements, the selection is not supported by this visualiser. - return !ecoreSelection.stream()// - .filter(e -> e instanceof EModelElement || e instanceof EGenericType)// - .findAny()// - .isPresent(); + return !VisualiserUtilities.hasMetamodelElements(selection); } @Override From 1cf2759619555c2461ab0f4992dbd4b7ac97442c Mon Sep 17 00:00:00 2001 From: Johannes Brandt <5931715+eTralse@users.noreply.github.com> Date: Fri, 20 Jul 2018 16:51:14 +0200 Subject: [PATCH 06/37] Refactoring: Flattens call stack of metamodel and model visualiser. Metamodel and model visualiser both contained chained methods, which were simply flattened to achieve a better readable source code. --- .../EMoflonMetamodelVisualiser.java | 48 ++++++++----------- .../visualisation/EMoflonModelVisualiser.java | 34 +++++-------- 2 files changed, 31 insertions(+), 51 deletions(-) diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java index da9d2a7e..13abbf09 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java @@ -4,11 +4,9 @@ package org.moflon.core.ui.visualisation; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.stream.Collectors; -import org.apache.commons.lang3.tuple.Pair; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; @@ -29,27 +27,12 @@ public boolean supportsSelection(List selection) { @Override protected String getDiagramBody(List elements) { - Pair, Collection> p = determineClassesAndRefsToVisualise(elements); - return EMoflonPlantUMLGenerator.visualiseEcoreElements(p.getLeft(), handleEOpposites(p.getRight())); + List classes = determineClassesToVisualise(elements); + List refs = handleEOpposites(determineReferencesToVisualise(classes)); + return EMoflonPlantUMLGenerator.visualiseEcoreElements(classes, refs); } - // -------------------------------------------------------------------------- - // -------------------------------------------------------------------------- - // -------------------------------------------------------------------------- - // TODO: REFACTOR - - private Collection handleEOpposites(Collection refs) { - Collection refsWithOnlyOneEOpposite = new ArrayList<>(); - for (EReference eReference : refs) { - if (eReference.getEOpposite() == null || !refsWithOnlyOneEOpposite.contains(eReference.getEOpposite())) - refsWithOnlyOneEOpposite.add(eReference); - } - - return refsWithOnlyOneEOpposite; - } - - private Pair, Collection> determineClassesAndRefsToVisualise( - Collection selection) { + private List determineClassesToVisualise(List selection) { // TODO: Fix selection bug where only EClasses will be visualised, but not // eclasses of operations / attributes and other lower-level EModelElements. // Instead, if the selection contains such an element, it should be replaced by @@ -57,18 +40,25 @@ private Pair, Collection> determineClassesAndRefs // TODO: Fix bug where EPackages cannot be visualised. Instead, they should // represent their content, i.e. all EClass and elements contained in EClasses // (see above) should be visualised. - Collection chosenClasses = selection.stream()// + return selection.stream()// .filter(EClass.class::isInstance)// .map(EClass.class::cast)// - .collect(Collectors.toSet());// - - return Pair.of(chosenClasses, determineReferencesToVisualize(chosenClasses)); + .collect(Collectors.toList());// } - private Collection determineReferencesToVisualize(Collection chosenClasses) { - Collection refs = chosenClasses.stream()// + private List determineReferencesToVisualise(List chosenClasses) { + return chosenClasses.stream()// .flatMap(c -> c.getEReferences().stream())// - .collect(Collectors.toSet());// - return refs; + .collect(Collectors.toList());// + } + + private List handleEOpposites(List refs) { + List refsWithOnlyOneEOpposite = new ArrayList<>(); + for (EReference eReference : refs) { + if (eReference.getEOpposite() == null || !refsWithOnlyOneEOpposite.contains(eReference.getEOpposite())) + refsWithOnlyOneEOpposite.add(eReference); + } + + return refsWithOnlyOneEOpposite; } } diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java index 5beb612f..437b11fb 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java @@ -9,7 +9,6 @@ import java.util.List; import java.util.stream.Collectors; -import org.apache.commons.lang3.tuple.Pair; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.util.EContentsEList; @@ -32,31 +31,22 @@ public boolean supportsSelection(List selection) { @Override protected String getDiagramBody(List elements) { - Pair, Collection> p = determineObjectsAndLinksToVisualise(elements); - return EMoflonPlantUMLGenerator.visualiseModelElements(p.getLeft(), p.getRight()); + List objects = determineObjectsToVisualise(elements); + List links = handleEOppositesForLinks(determineLinksToVisualise(objects)); + return EMoflonPlantUMLGenerator.visualiseModelElements(objects, links); } - // -------------------------------------------------------------------------- - // -------------------------------------------------------------------------- - // -------------------------------------------------------------------------- - // TODO: REFACTOR - - private Pair, Collection> determineObjectsAndLinksToVisualise( - Collection selection) { - - Collection chosenObjects = selection.stream()// + private List determineObjectsToVisualise(List selection) { + return selection.stream()// .filter(EObject.class::isInstance)// .map(EObject.class::cast)// - .collect(Collectors.toSet());// - - return Pair.of(chosenObjects, determineLinksToVisualize(chosenObjects)); - + .collect(Collectors.toList());// } @SuppressWarnings("rawtypes") - private Collection determineLinksToVisualize(Collection chosenObjects) { - Collection links = new HashSet<>(); - for (EObject o : new ArrayList(chosenObjects)) { + private List determineLinksToVisualise(List chosenObjects) { + HashSet links = new HashSet<>(); + for (EObject o : chosenObjects) { for (EContentsEList.FeatureIterator featureIterator = // (EContentsEList.FeatureIterator) o.eCrossReferences().iterator(); featureIterator.hasNext();) { addVisualEdge(featureIterator, chosenObjects, links, o); @@ -67,7 +57,7 @@ private Collection determineLinksToVisualize(Collection cho } } - return handleEOppositesForLinks(links); + return new ArrayList<>(links); } @SuppressWarnings("rawtypes") @@ -79,8 +69,8 @@ private void addVisualEdge(FeatureIterator featureIterator, Collection refs.add(new VisualEdge(eReference, src, trg)); } - private Collection handleEOppositesForLinks(Collection links) { - Collection linksWithOnlyOneEOpposite = new ArrayList<>(); + private List handleEOppositesForLinks(List links) { + List linksWithOnlyOneEOpposite = new ArrayList<>(); for (VisualEdge link : links) { if (!link.hasEOpposite() || !linksWithOnlyOneEOpposite.contains(link.findEOpposite(links).orElse(null))) linksWithOnlyOneEOpposite.add(link); From c2d0e84e5b280d2a02289e664a9852ec7ce9f404 Mon Sep 17 00:00:00 2001 From: Johannes Brandt <5931715+eTralse@users.noreply.github.com> Date: Fri, 20 Jul 2018 22:07:13 +0200 Subject: [PATCH 07/37] Fixes bug in metamodel visualiser when selectioning only EPackages, EOperations, EParameters, EGenericTypes or EStructuralFeatures. Previously, if one of the above element types has been selected in the metamodel visualiser, instead of its containing class element, the EObject itself was visualised by the model visualiser or not at all, if it was contained in a selection consisting of EObjects and EModelElements / EGenericTypes. This was the case because no representation for these elements were chosen, only EClasses in a selection could be visualised by the metamodel visualiser. Now, EStructuralFeatures, EOperations and EParameters are represented by their according containing EClasses. EPackages are represented by their contained EClassifiers and those contained in their subpackages. An EGernericType is represented by its EClassifier. One problem still remains, EDataTypes (a specialization of EClassifier) cannot be represented by an EClass. Which means, they cannot be visualised, if selected (an empty PlantUML diagram will be returned). --- .../EMoflonMetamodelVisualiser.java | 57 ++++++++++++++++--- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java index 13abbf09..f5cddb6b 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java @@ -4,12 +4,19 @@ package org.moflon.core.ui.visualisation; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EGenericType; import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EOperation; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EParameter; import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; import org.moflon.core.ui.VisualiserUtilities; /** @@ -33,17 +40,49 @@ protected String getDiagramBody(List elements) { } private List determineClassesToVisualise(List selection) { - // TODO: Fix selection bug where only EClasses will be visualised, but not - // eclasses of operations / attributes and other lower-level EModelElements. - // Instead, if the selection contains such an element, it should be replaced by - // its defining EClass, if this EClass is not already present in the selection. - // TODO: Fix bug where EPackages cannot be visualised. Instead, they should - // represent their content, i.e. all EClass and elements contained in EClasses - // (see above) should be visualised. - return selection.stream()// + ArrayList result = new ArrayList<>(selection.size()); + + HashSet cache = new HashSet<>(); + + // retrieve classes, and enclosing classes of operations, attributes... + // TODO: resolve EDataType as well? + for (EObject eobject : selection) { + EClass eclass = null; + + if (eobject instanceof EClass) { + eclass = (EClass) eobject; + } else if (eobject instanceof EStructuralFeature) { + // EReference and EAttribute + EStructuralFeature efeature = (EStructuralFeature) eobject; + eclass = efeature.getEContainingClass(); + } else if (eobject instanceof EOperation) { + EOperation eoperation = (EOperation) eobject; + eclass = eoperation.getEContainingClass(); + } else if (eobject instanceof EParameter) { + EParameter eparameter = (EParameter) eobject; + eclass = eparameter.getEOperation().getEContainingClass(); + } else if(eobject instanceof EGenericType) { + EGenericType etype = (EGenericType) eobject; + eclass = etype.getEClassifier() instanceof EClass ? (EClass) etype.getEClassifier() : null; + } + + if (eclass != null && cache.add(eclass)) { + result.add(eclass); + } + } + + // expand EPackages, retrieve classes and add them to result + selection.stream()// + .filter(EPackage.class::isInstance)// + .map(EPackage.class::cast)// + .flatMap(epackage -> Stream.concat(Stream.of(epackage), epackage.getESubpackages().stream()))// + .flatMap(epackage -> epackage.getEClassifiers().stream())// .filter(EClass.class::isInstance)// .map(EClass.class::cast)// - .collect(Collectors.toList());// + .filter(cache::add)// + .forEach(result::add); + + return result; } private List determineReferencesToVisualise(List chosenClasses) { From f82257e61fb9ac129a714e479cf4bc0d51bde347 Mon Sep 17 00:00:00 2001 From: Johannes Brandt <5931715+eTralse@users.noreply.github.com> Date: Sun, 22 Jul 2018 18:31:56 +0200 Subject: [PATCH 08/37] Refactoring: Unifies usage of VisualEdge in metamodel and model visualiser. Common behavior, i.e. #handleOpposites(...) is moved to Ecore visualiser and can therefore be used by both concrete visualisers. The PlantUMLGenerator has been adjusted accordingly. This commit is a preparation for a larger refactoring, in which the control over which elements are visualised is isolated from the PlantUML generator. --- .../visualisation/EMoflonEcoreVisualiser.java | 21 ++++++++++++++++++- .../EMoflonMetamodelVisualiser.java | 20 +++++------------- .../visualisation/EMoflonModelVisualiser.java | 12 +---------- .../EMoflonPlantUMLGenerator.xtend | 15 +++++++------ 4 files changed, 35 insertions(+), 33 deletions(-) diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java index b1e676b3..30b0bc6a 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java @@ -3,6 +3,7 @@ */ package org.moflon.core.ui.visualisation; +import java.util.ArrayList; import java.util.List; import org.eclipse.emf.ecore.EObject; @@ -92,7 +93,8 @@ public String getDiagramBody(IEditorPart editor, ISelection selection) { // again. if (latestSelection == null || latestSelection.isEmpty()) { return EMoflonPlantUMLGenerator.emptyDiagram(); - } else if (VisualiserUtilities.hasMetamodelElements(latestSelection) + } + if (VisualiserUtilities.hasMetamodelElements(latestSelection) && VisualiserUtilities.hasModelElements(latestSelection)) { return EMoflonPlantUMLGenerator.emptyDiagram(); } @@ -109,4 +111,21 @@ public String getDiagramBody(IEditorPart editor, ISelection selection) { */ protected abstract String getDiagramBody(List elements); + /** + * For a given list of {@link VisualEdge} instances, return only one {@link VisualEdge} for every bidirectional association. + * + * For example, if two classes cl1 and cl2 do share a bidirectional association cl1 <-> cl2, only one {@link VisualEdge} instance will be returned, describing only one navigation direction, e.g. cl1 <- cl2. In short, for every bidirectional association, the opposing edge will not be returned. No guarantees can be made which edge of bidirectional association will be returned. + * + * @param edges The list of edges, each one representing one navigation direction of an association, which may contain the opposing navigation direction of a bidirectional association. + * @return The list of edges without any opposing navigation direction. + */ + protected List handleOpposites(List edges) { + List edgesWithoutEOpposite = new ArrayList<>(); + for (VisualEdge edge : edges) { + if (!edge.hasEOpposite() || !edgesWithoutEOpposite.contains(edge.findEOpposite(edges).orElse(null))) + edgesWithoutEOpposite.add(edge); + } + + return edgesWithoutEOpposite; + } } diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java index f5cddb6b..acc848d7 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java @@ -15,7 +15,6 @@ import org.eclipse.emf.ecore.EOperation; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EParameter; -import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.moflon.core.ui.VisualiserUtilities; @@ -35,7 +34,7 @@ public boolean supportsSelection(List selection) { @Override protected String getDiagramBody(List elements) { List classes = determineClassesToVisualise(elements); - List refs = handleEOpposites(determineReferencesToVisualise(classes)); + List refs = handleOpposites(determineReferencesToVisualise(classes)); return EMoflonPlantUMLGenerator.visualiseEcoreElements(classes, refs); } @@ -61,10 +60,10 @@ private List determineClassesToVisualise(List selection) { } else if (eobject instanceof EParameter) { EParameter eparameter = (EParameter) eobject; eclass = eparameter.getEOperation().getEContainingClass(); - } else if(eobject instanceof EGenericType) { + } else if (eobject instanceof EGenericType) { EGenericType etype = (EGenericType) eobject; eclass = etype.getEClassifier() instanceof EClass ? (EClass) etype.getEClassifier() : null; - } + } if (eclass != null && cache.add(eclass)) { result.add(eclass); @@ -85,19 +84,10 @@ private List determineClassesToVisualise(List selection) { return result; } - private List determineReferencesToVisualise(List chosenClasses) { + private List determineReferencesToVisualise(List chosenClasses) { return chosenClasses.stream()// .flatMap(c -> c.getEReferences().stream())// + .map(ref -> new VisualEdge(ref, ref.getEContainingClass(), ref.getEReferenceType()))// .collect(Collectors.toList());// } - - private List handleEOpposites(List refs) { - List refsWithOnlyOneEOpposite = new ArrayList<>(); - for (EReference eReference : refs) { - if (eReference.getEOpposite() == null || !refsWithOnlyOneEOpposite.contains(eReference.getEOpposite())) - refsWithOnlyOneEOpposite.add(eReference); - } - - return refsWithOnlyOneEOpposite; - } } diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java index 437b11fb..8332c37a 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java @@ -32,7 +32,7 @@ public boolean supportsSelection(List selection) { @Override protected String getDiagramBody(List elements) { List objects = determineObjectsToVisualise(elements); - List links = handleEOppositesForLinks(determineLinksToVisualise(objects)); + List links = handleOpposites(determineLinksToVisualise(objects)); return EMoflonPlantUMLGenerator.visualiseModelElements(objects, links); } @@ -68,14 +68,4 @@ private void addVisualEdge(FeatureIterator featureIterator, Collection if (chosenObjects.contains(trg)) refs.add(new VisualEdge(eReference, src, trg)); } - - private List handleEOppositesForLinks(List links) { - List linksWithOnlyOneEOpposite = new ArrayList<>(); - for (VisualEdge link : links) { - if (!link.hasEOpposite() || !linksWithOnlyOneEOpposite.contains(link.findEOpposite(links).orElse(null))) - linksWithOnlyOneEOpposite.add(link); - } - - return linksWithOnlyOneEOpposite; - } } diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend index 82ab19a7..735844fc 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend @@ -43,7 +43,7 @@ class EMoflonPlantUMLGenerator { ''' } - def static String visualiseEcoreElements(Collection eclasses, Collection refs){ + def static String visualiseEcoreElements(Collection eclasses, Collection refs){ ''' «FOR c : eclasses» «IF(c.abstract)»abstract «ENDIF»class «identifierForClass(c)» @@ -52,13 +52,16 @@ class EMoflonPlantUMLGenerator { «identifierForClass(s)»<|--«identifierForClass(c)» «ENDFOR» «ENDFOR» - «FOR r : refs» - «IF(r.EOpposite === null)» - «identifierForClass(r.EContainingClass)»«IF r.isContainment» *«ENDIF»--> "«multiplicityFor(r)»" «identifierForClass(r.EReferenceType)» : "«r.name»" + «FOR ref : refs» + «var EReference r = ref.getType()» + «var EClass src = r.EContainingClass» + «var EClass trg = r.EReferenceType» + «IF(!ref.hasEOpposite)» + «identifierForClass(src)»«IF r.isContainment» *«ENDIF»--> "«multiplicityFor(r)»" «identifierForClass(trg)» : "«r.name»" «ELSE» - «identifierForClass(r.EContainingClass)»"«r.EOpposite.name» «multiplicityFor(r.EOpposite)»" «IF r.isContainment»*«ELSE»<«ENDIF»--«IF r.EOpposite.isContainment»*«ELSE»>«ENDIF» "«r.name» «multiplicityFor(r)»" «identifierForClass(r.EReferenceType)» + «identifierForClass(src)»"«r.EOpposite.name» «multiplicityFor(r.EOpposite)»" «IF r.isContainment»*«ELSE»<«ENDIF»--«IF r.EOpposite.isContainment»*«ELSE»>«ENDIF» "«r.name» «multiplicityFor(r)»" «identifierForClass(trg)» «ENDIF» - «IF(r.EReferenceType.abstract)»abstract «ENDIF»class «identifierForClass(r.EReferenceType)» + «IF(trg.abstract)»abstract «ENDIF»class «identifierForClass(trg)» «ENDFOR» ''' } From 0720cad1d859fea6c41902ef29fc21ab38a47c27 Mon Sep 17 00:00:00 2001 From: Johannes Brandt <5931715+eTralse@users.noreply.github.com> Date: Mon, 23 Jul 2018 09:26:23 +0200 Subject: [PATCH 09/37] Refactoring: Moves control over which element to draw to visualisers. Before this commit, the PlantUML generator had partial control over which elements to visualise and which not. For example, the superclass of an EClass was always visualised, including the generalisation dependency. This behavior may change in the future, amongst other. The new agenda for the PlantUML generator is to preciselly draw what is given to it. All calculations for determining superclasses have been added to the metamodel visualiser and removed from the generator. The generalisation dependency is now given in the form of a VisualEdge. To differentiate between the different kinds of edges, an Enum has been added, which for now differentiates between LINK (model), REFERENCE (metamodel) and GENERALISATION (metamodel). Accordingly, the generation of edges in the PlantUML generator has been unified into one method. --- .../moflon/core/ui/VisualiserUtilities.java | 3 -- .../visualisation/EMoflonEcoreVisualiser.java | 20 ++++++--- .../EMoflonMetamodelVisualiser.java | 21 ++++++--- .../visualisation/EMoflonModelVisualiser.java | 5 +-- .../EMoflonPlantUMLGenerator.xtend | 44 +++++++++++-------- .../core/ui/visualisation/EdgeType.java | 9 ++++ .../core/ui/visualisation/VisualEdge.java | 35 ++++++++++----- 7 files changed, 88 insertions(+), 49 deletions(-) create mode 100644 org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EdgeType.java diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/VisualiserUtilities.java b/org.moflon.core.ui/src/org/moflon/core/ui/VisualiserUtilities.java index 6d271b60..551ff289 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/VisualiserUtilities.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/VisualiserUtilities.java @@ -1,6 +1,3 @@ -/** - * - */ package org.moflon.core.ui; import java.util.ArrayList; diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java index 30b0bc6a..705c9b95 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java @@ -1,6 +1,3 @@ -/** - * - */ package org.moflon.core.ui.visualisation; import java.util.ArrayList; @@ -93,7 +90,7 @@ public String getDiagramBody(IEditorPart editor, ISelection selection) { // again. if (latestSelection == null || latestSelection.isEmpty()) { return EMoflonPlantUMLGenerator.emptyDiagram(); - } + } if (VisualiserUtilities.hasMetamodelElements(latestSelection) && VisualiserUtilities.hasModelElements(latestSelection)) { return EMoflonPlantUMLGenerator.emptyDiagram(); @@ -112,11 +109,20 @@ public String getDiagramBody(IEditorPart editor, ISelection selection) { protected abstract String getDiagramBody(List elements); /** - * For a given list of {@link VisualEdge} instances, return only one {@link VisualEdge} for every bidirectional association. + * For a given list of {@link VisualEdge} instances, return only one + * {@link VisualEdge} for every bidirectional association. * - * For example, if two classes cl1 and cl2 do share a bidirectional association cl1 <-> cl2, only one {@link VisualEdge} instance will be returned, describing only one navigation direction, e.g. cl1 <- cl2. In short, for every bidirectional association, the opposing edge will not be returned. No guarantees can be made which edge of bidirectional association will be returned. + * For example, if two classes cl1 and cl2 do share a bidirectional association + * cl1 <-> cl2, only one {@link VisualEdge} instance will be returned, + * describing only one navigation direction, e.g. cl1 <- cl2. In short, for + * every bidirectional association, the opposing edge will not be returned. No + * guarantees can be made which direction of a bidirectional association will be + * returned. If there is a unidirectional {@link VisualEdge}, it will be returned. * - * @param edges The list of edges, each one representing one navigation direction of an association, which may contain the opposing navigation direction of a bidirectional association. + * @param edges + * The list of edges, each one representing one navigation direction + * of an association, which may contain the opposing navigation + * direction of a bidirectional association. * @return The list of edges without any opposing navigation direction. */ protected List handleOpposites(List edges) { diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java index acc848d7..2f0883c6 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java @@ -1,6 +1,3 @@ -/** - * - */ package org.moflon.core.ui.visualisation; import java.util.ArrayList; @@ -85,9 +82,23 @@ private List determineClassesToVisualise(List selection) { } private List determineReferencesToVisualise(List chosenClasses) { - return chosenClasses.stream()// + HashSet cache = new HashSet<>(chosenClasses); + + // Gather all references in between selected classes and between selected classes and non-selectéd classes. + List result = chosenClasses.stream()// .flatMap(c -> c.getEReferences().stream())// - .map(ref -> new VisualEdge(ref, ref.getEContainingClass(), ref.getEReferenceType()))// + .filter(ref -> cache.contains(ref.getEContainingClass()) || cache.contains(ref.getEReferenceType())) + .map(ref -> new VisualEdge(ref, EdgeType.REFERENCE, ref.getEContainingClass(), ref.getEReferenceType()))// .collect(Collectors.toList());// + // TODO: Add support for unidirectional refs with source not in and target in + // list of chosen classes. + + // Gather all generalisation references. + chosenClasses.stream()// + .filter(c -> !c.getESuperTypes().isEmpty())// + .forEach(c -> c.getESuperTypes().forEach(s -> result.add(new VisualEdge(null, EdgeType.GENERALISATION, c, s)))); + // TODO: Add support for base class outside of selection and super class inside of selection. + + return result; } } diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java index 8332c37a..ea477f64 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java @@ -1,6 +1,3 @@ -/** - * - */ package org.moflon.core.ui.visualisation; import java.util.ArrayList; @@ -66,6 +63,6 @@ private void addVisualEdge(FeatureIterator featureIterator, Collection EObject trg = (EObject) featureIterator.next(); EReference eReference = (EReference) featureIterator.feature(); if (chosenObjects.contains(trg)) - refs.add(new VisualEdge(eReference, src, trg)); + refs.add(new VisualEdge(eReference, EdgeType.LINK, src, trg)); } } diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend index 735844fc..798efbb3 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend @@ -47,22 +47,8 @@ class EMoflonPlantUMLGenerator { ''' «FOR c : eclasses» «IF(c.abstract)»abstract «ENDIF»class «identifierForClass(c)» - «FOR s : c.ESuperTypes» - «IF(s.abstract)»abstract «ENDIF»class «identifierForClass(s)» - «identifierForClass(s)»<|--«identifierForClass(c)» - «ENDFOR» - «ENDFOR» - «FOR ref : refs» - «var EReference r = ref.getType()» - «var EClass src = r.EContainingClass» - «var EClass trg = r.EReferenceType» - «IF(!ref.hasEOpposite)» - «identifierForClass(src)»«IF r.isContainment» *«ENDIF»--> "«multiplicityFor(r)»" «identifierForClass(trg)» : "«r.name»" - «ELSE» - «identifierForClass(src)»"«r.EOpposite.name» «multiplicityFor(r.EOpposite)»" «IF r.isContainment»*«ELSE»<«ENDIF»--«IF r.EOpposite.isContainment»*«ELSE»>«ENDIF» "«r.name» «multiplicityFor(r)»" «identifierForClass(trg)» - «ENDIF» - «IF(trg.abstract)»abstract «ENDIF»class «identifierForClass(trg)» «ENDFOR» + «visualiseEdges(refs)» ''' } @@ -75,11 +61,31 @@ class EMoflonPlantUMLGenerator { «visualiseAllAttributes(o)» } «ENDFOR» - «FOR l : links» - «IF(!l.hasEOpposite)» - «identifierForObject(l.src)» --> «identifierForObject(l.trg)» : "«l.name»" + «visualiseEdges(links)» + ''' + } + + def private static String visualiseEdges(Collection edges) { + ''' + «FOR edge: edges» + «IF(edge.edgeType == EdgeType.REFERENCE)» + «var EReference ref = edge.type» + «var EClass src = ref.EContainingClass» + «var EClass trg = ref.EReferenceType» + «IF(!edge.hasEOpposite)» + «identifierForClass(src)»«IF ref.isContainment» *«ENDIF»--> "«multiplicityFor(ref)»" «identifierForClass(trg)» : "«ref.name»" + «ELSE» + «identifierForClass(src)»"«ref.EOpposite.name» «multiplicityFor(ref.EOpposite)»" «IF ref.isContainment»*«ELSE»<«ENDIF»--«IF ref.EOpposite.isContainment»*«ELSE»>«ENDIF» "«ref.name» «multiplicityFor(ref)»" «identifierForClass(trg)» + «ENDIF» + «ELSEIF(edge.edgeType == EdgeType.GENERALISATION)» + «identifierForClass(edge.trg as EClass)»<|--«identifierForClass(edge.src as EClass)» + «ELSEIF(edge.edgeType == EdgeType.LINK)» + «IF(!edge.hasEOpposite)» + «identifierForObject(edge.src)» --> «identifierForObject(edge.trg)» : "«edge.name»" + «ELSE» + «identifierForObject(edge.src)» "«edge.oppositeName»" <--> "«edge.name»" «identifierForObject(edge.trg)» + «ENDIF» «ELSE» - «identifierForObject(l.src)» "«l.oppositeName»" <--> "«l.name»" «identifierForObject(l.trg)» «ENDIF» «ENDFOR» ''' diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EdgeType.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EdgeType.java new file mode 100644 index 00000000..b33e3da1 --- /dev/null +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EdgeType.java @@ -0,0 +1,9 @@ +package org.moflon.core.ui.visualisation; + +/** + * @author Johannes Brandt (initial contribution) + * + */ +public enum EdgeType { + GENERALISATION, REFERENCE, LINK; +} diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/VisualEdge.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/VisualEdge.java index 0439719b..b6900613 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/VisualEdge.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/VisualEdge.java @@ -8,11 +8,13 @@ public class VisualEdge { private EReference type; + private EdgeType edgeType; private EObject src; private EObject trg; - - public VisualEdge(EReference type, EObject src, EObject trg) { + + public VisualEdge(EReference type, EdgeType edgeType, EObject src, EObject trg) { this.type = type; + this.edgeType = edgeType; this.src = src; this.trg = trg; } @@ -21,6 +23,10 @@ public EReference getType() { return type; } + public EdgeType getEdgeType() { + return edgeType; + } + public EObject getSrc() { return src; } @@ -28,24 +34,30 @@ public EObject getSrc() { public EObject getTrg() { return trg; } - + public String getName() { + if (edgeType == EdgeType.GENERALISATION || type == null) { + return ""; + } return type.getName(); } - + public String getOppositeName() { - assert(hasEOpposite()); + assert (hasEOpposite()); return type.getEOpposite().getName(); } public boolean hasEOpposite() { + if (edgeType == EdgeType.GENERALISATION || type == null) { + return false; + } return type.getEOpposite() != null; } public Optional findEOpposite(Collection links) { - assert(hasEOpposite()); - VisualEdge opposite = new VisualEdge(type.getEOpposite(), trg, src); - if(links.contains(opposite)) + assert (hasEOpposite()); + VisualEdge opposite = new VisualEdge(type.getEOpposite(), edgeType, trg, src); + if (links.contains(opposite)) return Optional.of(opposite); else return Optional.empty(); @@ -58,6 +70,7 @@ public int hashCode() { result = prime * result + ((src == null) ? 0 : src.hashCode()); result = prime * result + ((trg == null) ? 0 : trg.hashCode()); result = prime * result + ((type == null) ? 0 : type.hashCode()); + result = prime * result + ((edgeType == null) ? 0 : edgeType.hashCode()); return result; } @@ -65,7 +78,7 @@ public int hashCode() { public String toString() { return "--" + getName() + "-->"; } - + @Override public boolean equals(Object obj) { if (this == obj) @@ -75,6 +88,8 @@ public boolean equals(Object obj) { if (getClass() != obj.getClass()) return false; VisualEdge other = (VisualEdge) obj; + if (edgeType != other.edgeType) + return false; if (src == null) { if (other.src != null) return false; @@ -92,6 +107,4 @@ public boolean equals(Object obj) { return false; return true; } - - } From 3c18d4ed42735e20b40202432e8767ac0f8831a5 Mon Sep 17 00:00:00 2001 From: Johannes Brandt <5931715+eTralse@users.noreply.github.com> Date: Mon, 23 Jul 2018 15:33:18 +0200 Subject: [PATCH 10/37] Fixes bug where 1-neighborhood was not correctly determined. --- .../moflon/core/ui/VisualiserUtilities.java | 17 +++++- .../visualisation/EMoflonEcoreVisualiser.java | 28 ++++++++- .../EMoflonMetamodelVisualiser.java | 59 +++++++++++++++---- .../visualisation/EMoflonModelVisualiser.java | 33 +++++++++-- 4 files changed, 115 insertions(+), 22 deletions(-) diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/VisualiserUtilities.java b/org.moflon.core.ui/src/org/moflon/core/ui/VisualiserUtilities.java index 551ff289..7f45361c 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/VisualiserUtilities.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/VisualiserUtilities.java @@ -7,6 +7,8 @@ import java.util.stream.Stream; import org.eclipse.core.resources.IFile; +import org.eclipse.emf.ecore.EDataType; +import org.eclipse.emf.ecore.EEnumLiteral; import org.eclipse.emf.ecore.EGenericType; import org.eclipse.emf.ecore.EModelElement; import org.eclipse.emf.ecore.EObject; @@ -195,6 +197,11 @@ public static List extractEcoreSelection(ISelection selection) { * list. *

* + *

+ * Note: There won't be any {@link EGenericType}, {@link EEnumLiteral} or + * {@link EDataType} references in the returned list. + *

+ * * @param resources * of which all contained elements are to be returned - cannot be * null @@ -221,6 +228,11 @@ public static List expandResources(ResourceSet resources) { * stream. *

* + *

+ * Note: There won't be any {@link EGenericType}, {@link EEnumLiteral} or + * {@link EDataType} references in the returned stream. + *

+ * * @param resource * The resource that is to be expanded. * @return The stream of {@link EObject} instances in order of expansions. @@ -229,7 +241,10 @@ private static Stream expandResource(Resource resource) { List elements = new ArrayList<>(); resource.getAllContents().forEachRemaining(elements::add); return elements.stream()// - .filter(elem -> elem != null); + .filter(elem -> elem != null)// + .filter(elem -> !(elem instanceof EGenericType))// + .filter(elem -> !(elem instanceof EEnumLiteral))// + .filter(elem -> !(elem instanceof EDataType)); } /** diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java index 705c9b95..7b4dd47c 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java @@ -27,6 +27,13 @@ public abstract class EMoflonEcoreVisualiser extends EMoflonVisualiser { */ private List latestSelection; + /** + * Resembles the superset of all elements that could potentially be visualised. + * Typically determined by all the elements currently handled by the associated + * editor. + */ + private List allElements; + @Override public boolean supportsEditor(IEditorPart editor) { // check if editor currently has Ecore related model loaded @@ -39,8 +46,9 @@ public boolean supportsEditor(IEditorPart editor) { // time, it is not possible to check for specific elements from Ecore metamodels // or models. This has to be done in #supportsSelection(...), when it is clear // whether the selection is empty or not. - latestSelection = VisualiserUtilities.extractEcoreElements(editor); - isEmptySelectionSupported = latestSelection != null; + allElements = VisualiserUtilities.extractEcoreElements(editor); + latestSelection = allElements; + isEmptySelectionSupported = allElements != null; // if only one of the above conditions is true, there is still a possibility // that a given selection might be supported @@ -61,6 +69,10 @@ public boolean supportsSelection(ISelection selection) { return false; } latestSelection = ecoreSelection; + // in case no elements can be extracted from the editor, allElements is set to the selection + if(!isEmptySelectionSupported) { + allElements = ecoreSelection; + } boolean isSupported = supportsSelection(latestSelection); return isSupported; @@ -107,6 +119,15 @@ public String getDiagramBody(IEditorPart editor, ISelection selection) { * PlantUML DSL. */ protected abstract String getDiagramBody(List elements); + + /** + * Getter for {@link #allElements}. + * + * @return All elements that can potentially be visualised. + */ + protected List getAllElements() { + return allElements; + } /** * For a given list of {@link VisualEdge} instances, return only one @@ -117,7 +138,8 @@ public String getDiagramBody(IEditorPart editor, ISelection selection) { * describing only one navigation direction, e.g. cl1 <- cl2. In short, for * every bidirectional association, the opposing edge will not be returned. No * guarantees can be made which direction of a bidirectional association will be - * returned. If there is a unidirectional {@link VisualEdge}, it will be returned. + * returned. If there is a unidirectional {@link VisualEdge}, it will be + * returned. * * @param edges * The list of edges, each one representing one navigation direction diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java index 2f0883c6..9d6d2b23 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java @@ -12,6 +12,7 @@ import org.eclipse.emf.ecore.EOperation; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EParameter; +import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.moflon.core.ui.VisualiserUtilities; @@ -30,9 +31,16 @@ public boolean supportsSelection(List selection) { @Override protected String getDiagramBody(List elements) { - List classes = determineClassesToVisualise(elements); - List refs = handleOpposites(determineReferencesToVisualise(classes)); - return EMoflonPlantUMLGenerator.visualiseEcoreElements(classes, refs); + List allClasses = getAllElements().stream()// + .filter(EClass.class::isInstance)// + .map(EClass.class::cast)// + .collect(Collectors.toList()); + + List chosenClasses = determineClassesToVisualise(elements); + List chosenClassesWithNeighbors = expandByNeighborHood(allClasses, chosenClasses); + + List refs = handleOpposites(determineReferencesToVisualise(chosenClassesWithNeighbors)); + return EMoflonPlantUMLGenerator.visualiseEcoreElements(chosenClassesWithNeighbors, refs); } private List determineClassesToVisualise(List selection) { @@ -80,24 +88,51 @@ private List determineClassesToVisualise(List selection) { return result; } + + private List expandByNeighborHood(List allClasses, List chosenClasses) { + HashSet cache = new HashSet<>(chosenClasses); + + // add all neighboring classes by reference and generalisation dependency + HashSet result = new HashSet<>(chosenClasses); + for(EClass c : allClasses) { + // neighborhood by reference + for(EReference r : c.getEReferences()) { + EClass t = r.getEReferenceType(); + if(cache.contains(c) || cache.contains(t)) { + result.add(c); + result.add(t); + } + } + // neighborhood by generalisation dependency + for(EClass s : c.getESuperTypes()) { + if(cache.contains(c) || cache.contains(s)) { + result.add(c); + result.add(s); + } + } + } + + return new ArrayList<>(result); + } private List determineReferencesToVisualise(List chosenClasses) { HashSet cache = new HashSet<>(chosenClasses); - // Gather all references in between selected classes and between selected classes and non-selectéd classes. + // Gather all references in between selected classes and between selected classes and non-selected classes. List result = chosenClasses.stream()// .flatMap(c -> c.getEReferences().stream())// - .filter(ref -> cache.contains(ref.getEContainingClass()) || cache.contains(ref.getEReferenceType())) + .filter(ref -> cache.contains(ref.getEContainingClass()) && cache.contains(ref.getEReferenceType())) .map(ref -> new VisualEdge(ref, EdgeType.REFERENCE, ref.getEContainingClass(), ref.getEReferenceType()))// .collect(Collectors.toList());// - // TODO: Add support for unidirectional refs with source not in and target in - // list of chosen classes. - // Gather all generalisation references. - chosenClasses.stream()// - .filter(c -> !c.getESuperTypes().isEmpty())// - .forEach(c -> c.getESuperTypes().forEach(s -> result.add(new VisualEdge(null, EdgeType.GENERALISATION, c, s)))); - // TODO: Add support for base class outside of selection and super class inside of selection. + // Gather all generalisation dependencies. + for(EClass c : chosenClasses) { + for(EClass s : c.getESuperTypes()) { + if(cache.contains(c) && cache.contains(s)) { + result.add(new VisualEdge(null, EdgeType.GENERALISATION, c, s)); + } + } + } return result; } diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java index ea477f64..a60f5d9e 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java @@ -4,8 +4,6 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; -import java.util.stream.Collectors; - import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.util.EContentsEList; @@ -33,11 +31,34 @@ protected String getDiagramBody(List elements) { return EMoflonPlantUMLGenerator.visualiseModelElements(objects, links); } + @SuppressWarnings("rawtypes") private List determineObjectsToVisualise(List selection) { - return selection.stream()// - .filter(EObject.class::isInstance)// - .map(EObject.class::cast)// - .collect(Collectors.toList());// + HashSet cache = new HashSet<>(selection); + + List result = new ArrayList<>(selection); + for (EObject o : selection) { + // add parent if there is one + if(o.eContainer() != null && cache.add(o.eContainer())) { + result.add(o.eContainer()); + } + + for (EContentsEList.FeatureIterator featureIterator = // + (EContentsEList.FeatureIterator) o.eCrossReferences().iterator(); featureIterator.hasNext();) { + EObject trg = (EObject) featureIterator.next(); + if(cache.add(trg)) { + result.add(trg); + } + } + for (EContentsEList.FeatureIterator featureIterator = // + (EContentsEList.FeatureIterator) o.eContents().iterator(); featureIterator.hasNext();) { + EObject trg = (EObject) featureIterator.next(); + if(cache.add(trg)) { + result.add(trg); + } + } + } + + return result; } @SuppressWarnings("rawtypes") From 938224dacaf37c73e44d270444cd543060d9e9ea Mon Sep 17 00:00:00 2001 From: Johannes Brandt <5931715+eTralse@users.noreply.github.com> Date: Thu, 26 Jul 2018 21:20:50 +0200 Subject: [PATCH 11/37] Refactoring: Impelements strategy pattern for diagram creation in metamodel and model visualisers. This refactoring includes various strategies for the computation of object and class diagrams by the model and meta model visualiser. To simplify the chaining of multiple strategies for diagram manipulation, new container types are introduced: Diagram, ClassDiagram and ObjectDiagram. Amongst others, they store the selection and neighbourhood of nodes, as well as the edges between them. The PlantUML generator was adjusted to draw ClassDiagrams and ObjectDiagrams. Hence, future functional enhancements might benefit from these new container classes. The new container types are important for strategy operations, since chaining of strategies is more easily possible. Initial strategies include: edge creation for a selection of nodes and bidirectional expansion of the current neighbourhood by a degree of one. If a higher degree is wished, the latter method can simply be chained. The mentioned strategies have been added for object and class diagrams. In proceeding of the refactoring, all List type-references have been changed to the more general Collection, whereas internally HashMaps are used if a collection type is to be instantiated. This should improve the performance, as the order of elements appears to be not of relevance. --- .../moflon/core/ui/VisualiserUtilities.java | 67 ++++----- .../core/ui/visualisation/ClassDiagram.java | 28 ++++ .../moflon/core/ui/visualisation/Diagram.java | 137 ++++++++++++++++++ .../visualisation/EMoflonEcoreVisualiser.java | 21 ++- .../EMoflonMetamodelVisualiser.java | 98 ++++--------- .../visualisation/EMoflonModelVisualiser.java | 88 +++-------- .../EMoflonPlantUMLGenerator.xtend | 20 ++- .../core/ui/visualisation/EdgeType.java | 7 + .../core/ui/visualisation/ObjectDiagram.java | 28 ++++ .../strategy/ClassDiagramStrategies.java | 120 +++++++++++++++ .../strategy/ObjectDiagramStrategies.java | 124 ++++++++++++++++ 11 files changed, 550 insertions(+), 188 deletions(-) create mode 100644 org.moflon.core.ui/src/org/moflon/core/ui/visualisation/ClassDiagram.java create mode 100644 org.moflon.core.ui/src/org/moflon/core/ui/visualisation/Diagram.java create mode 100644 org.moflon.core.ui/src/org/moflon/core/ui/visualisation/ObjectDiagram.java create mode 100644 org.moflon.core.ui/src/org/moflon/core/ui/visualisation/strategy/ClassDiagramStrategies.java create mode 100644 org.moflon.core.ui/src/org/moflon/core/ui/visualisation/strategy/ObjectDiagramStrategies.java diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/VisualiserUtilities.java b/org.moflon.core.ui/src/org/moflon/core/ui/VisualiserUtilities.java index 7f45361c..0bf5f6e2 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/VisualiserUtilities.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/VisualiserUtilities.java @@ -1,6 +1,6 @@ package org.moflon.core.ui; -import java.util.ArrayList; +import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.stream.Collectors; @@ -81,12 +81,12 @@ public static boolean checkFileExtensionSupport(IEditorPart editor, String expec * {@link IEditingDomainProvider} interface, and if the * {@link EditingDomain} or the {@link ResourceSet} of the editor is * null. If there is a {@link ResourceSet} stored with the - * editor, then all resources will be extracted and the resulting list - * of {@link EObject}s is returned. This list can be empty, if no + * editor, then all resources will be extracted and the resulting collection + * of {@link EObject}s is returned. This collection can be empty, if no * {@link EObject}s are stored in the editor's resources, however, no * null references will be contained. */ - public static List extractEcoreElements(IEditorPart editor) { + public static Collection extractEcoreElements(IEditorPart editor) { if (editor == null || !(editor instanceof IEditingDomainProvider)) { return null; } @@ -150,10 +150,10 @@ public static boolean isEcoreSelection(ISelection selection) { * null. * @return null is returned, if the given selection is not an Ecore * selection. Otherwise the internal selection is extracted and - * returned. The returned list can be empty, if the selection is empty. - * The wrapped list won't contain any null references. + * returned. The returned collection can be empty, if the selection is empty. + * The wrapped collection won't contain any null references. */ - public static List extractEcoreSelection(ISelection selection) { + public static Collection extractEcoreSelection(ISelection selection) { if (!isEcoreSelection(selection)) { return null; } @@ -162,15 +162,12 @@ public static List extractEcoreSelection(ISelection selection) { List internalSelection = ((IStructuredSelection) selection).toList(); // expand resources and add them to result - List result = internalSelection.stream()// + HashSet result = internalSelection.stream()// .filter(e -> e != null)// .filter(Resource.class::isInstance)// .map(Resource.class::cast)// .flatMap(VisualiserUtilities::expandResource)// - .collect(Collectors.toList()); - - // enhances performance of contains operation later - HashSet cache = new HashSet<>(result); + .collect(Collectors.toCollection(HashSet::new)); // add remaining EObjects of selection, apart from those already contained in // resources, to the result. @@ -178,7 +175,7 @@ public static List extractEcoreSelection(ISelection selection) { .filter(e -> e != null)// .filter(EObject.class::isInstance)// .map(EObject.class::cast)// - .filter(e -> !cache.contains(e))// + .filter(e -> !result.contains(e))// .forEach(result::add); return result; @@ -188,42 +185,32 @@ public static List extractEcoreSelection(ISelection selection) { * Extracts all {@link EObject} instances from a given {@link ResourceSet}. * *

- * Note: The order of the returned elements is prescribed by - * {@link Resource#getAllContents()}. - *

- * - *

* Note: There won't be any null references in the returned - * list. + * collection. *

* *

* Note: There won't be any {@link EGenericType}, {@link EEnumLiteral} or - * {@link EDataType} references in the returned list. + * {@link EDataType} references in the returned collection. *

* * @param resources * of which all contained elements are to be returned - cannot be - * null + * null * @return the {@link EObject} instances contained in each of the given - * resources, in order of expansion + * resources */ - public static List expandResources(ResourceSet resources) { + public static Collection expandResources(ResourceSet resources) { return resources.getResources().stream()// .filter(res -> res != null)// .flatMap(VisualiserUtilities::expandResource)// - .collect(Collectors.toList()); + .collect(Collectors.toCollection(HashSet::new)); } /** * Extracts all {@link EObject} instances from a given {@link Resource}. * *

- * Note: The order of the returned elements is prescribed by - * {@link Resource#getAllContents()}. - *

- * - *

* Note: There won't be any null references in the returned * stream. *

@@ -238,7 +225,7 @@ public static List expandResources(ResourceSet resources) { * @return The stream of {@link EObject} instances in order of expansions. */ private static Stream expandResource(Resource resource) { - List elements = new ArrayList<>(); + HashSet elements = new HashSet<>(); resource.getAllContents().forEachRemaining(elements::add); return elements.stream()// .filter(elem -> elem != null)// @@ -248,16 +235,16 @@ private static Stream expandResource(Resource resource) { } /** - * States whether the given list of elements contains any metamodel elements. + * States whether the given collection of elements contains any metamodel elements. * * @param elements - * The list of elements that is to be checked for the existence of - * metamodel elements. This list, nor any of its elements, must be + * The collection of elements that is to be checked for the existence of + * metamodel elements. Neither this collection, nor any of its elements, must be * null. - * @return true, if the given list contains at least one metamodel + * @return true, if the given collection contains at least one metamodel * element. false otherwise. */ - public static boolean hasMetamodelElements(List elements) { + public static boolean hasMetamodelElements(Collection elements) { return elements.stream()// .filter(e -> e instanceof EModelElement || e instanceof EGenericType)// .findAny()// @@ -265,16 +252,16 @@ public static boolean hasMetamodelElements(List elements) { } /** - * States whether the given list of elements contains any model elements. + * States whether the given collection of elements contains any model elements. * * @param elements - * The list of elements that is to be checked for the existence of - * model elements. This list, nor any of its elements, must be + * The collection of elements that is to be checked for the existence of + * model elements. Neither this collection, nor any of its elements, must be * null. - * @return true, if the given list contains at least one model + * @return true, if the given collection contains at least one model * element. false otherwise. */ - public static boolean hasModelElements(List elements) { + public static boolean hasModelElements(Collection elements) { return elements.stream()// .filter(e -> !(e instanceof EModelElement))// .filter(e -> !(e instanceof EGenericType))// diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/ClassDiagram.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/ClassDiagram.java new file mode 100644 index 00000000..3d02bbab --- /dev/null +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/ClassDiagram.java @@ -0,0 +1,28 @@ +/** + * + */ +package org.moflon.core.ui.visualisation; + +import java.util.Collection; + +import org.eclipse.emf.ecore.EClass; + +/** + * Represents a class diagram. + * + * The nodes of this diagram type are {@link EClass} instances. + * + * @author Johannes Brandt + * + */ +public class ClassDiagram extends Diagram { + + public ClassDiagram(Collection superset) { + super(superset); + } + + public ClassDiagram(Collection superset, Collection selection) { + super(superset, selection); + } + +} diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/Diagram.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/Diagram.java new file mode 100644 index 00000000..43c889cf --- /dev/null +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/Diagram.java @@ -0,0 +1,137 @@ +/** + * + */ +package org.moflon.core.ui.visualisation; + +import java.util.Collection; +import java.util.HashSet; + +/** + * Represents a generic diagram. + * + * Stores nodes and the edges connecting them. Edges are represented by + * {@link VisualEdge} instances. + * + * @author Johannes Brandt + * + * @param + * The node type. + */ +public class Diagram { + + /** + * Stores all elements that may be visualised. + */ + final protected Collection superset; + + /** + * Stores all selected elements of the diagram. Should be distinct from + * {@link Diagram#neighbourhood}. + */ + protected Collection selection; + + /** + * Stores the neighbourhood of {@link Diagram#selection}. Should be distinct + * from {@link Diagram#selection}. + */ + protected Collection neighbourhood; + + /** + * Stores the edges that are to be visualised. + */ + protected Collection edges; + + /** + * Parameterized constructor. + * + * @param superset + * All elements of the diagram. Cannot be changed once initialized. + */ + public Diagram(Collection superset) { + this.superset = superset; + this.selection = new HashSet<>(); + this.neighbourhood = new HashSet<>(); + this.edges = new HashSet<>(); + } + + /** + * Parameterized constructor. + * + * @param superset + * All elements of the diagram. Cannot be changed once initialized. + * @param selection + * The selected elements of the diagram. + */ + public Diagram(Collection superset, Collection selection) { + this.superset = superset; + this.selection = selection; + this.neighbourhood = new HashSet<>(); + this.edges = new HashSet<>(); + } + + /** + * Getter for {@link Diagram#superset}. + * + * @return All nodes of the diagram. + */ + public Collection getSuperset() { + return superset; + } + + /** + * Getter for {@link Diagram#selection}. + * + * @return The selected nodes of the diagram. + */ + public Collection getSelection() { + return selection; + } + + /** + * Setter for {@link Diagram#selection}. + * + * @param selection + * The new selection of nodes. + */ + public void setSelection(Collection selection) { + this.selection = selection; + } + + /** + * Getter for {@link Diagram#neighbourhood}. + * + * @return All nodes that are considered to be in the neighbourhood. + */ + public Collection getNeighbourhood() { + return neighbourhood; + } + + /** + * Setter for {@link Diagram#neighbourhood}. + * + * @param neighbourhood + * The new neighbourhood. + */ + public void setNeighbourhood(Collection neighbourhood) { + this.neighbourhood = neighbourhood; + } + + /** + * Getter for {@link #edges}. + * + * @return The edges of this diagram. + */ + public Collection getEdges() { + return edges; + } + + /** + * Setter for {@link #edges}. + * + * @param edges + * The new edges for this diagram. + */ + public void setEdges(Collection edges) { + this.edges = edges; + } +} diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java index 7b4dd47c..a3f58f88 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java @@ -1,8 +1,7 @@ package org.moflon.core.ui.visualisation; -import java.util.ArrayList; -import java.util.List; - +import java.util.Collection; +import java.util.HashSet; import org.eclipse.emf.ecore.EObject; import org.eclipse.jface.viewers.ISelection; import org.eclipse.ui.IEditorPart; @@ -25,14 +24,14 @@ public abstract class EMoflonEcoreVisualiser extends EMoflonVisualiser { /** * Stores a subset of Ecore elements, that are to be visualised.. */ - private List latestSelection; + private Collection latestSelection; /** * Resembles the superset of all elements that could potentially be visualised. * Typically determined by all the elements currently handled by the associated * editor. */ - private List allElements; + private Collection allElements; @Override public boolean supportsEditor(IEditorPart editor) { @@ -64,7 +63,7 @@ public boolean supportsSelection(ISelection selection) { // empty Ecore selections are supported only if the editor can provide Ecore // elements, this is checked and remembered in supportsEditor(...) - List ecoreSelection = VisualiserUtilities.extractEcoreSelection(selection); + Collection ecoreSelection = VisualiserUtilities.extractEcoreSelection(selection); if (ecoreSelection == null || ecoreSelection.isEmpty()) { return false; } @@ -87,7 +86,7 @@ public boolean supportsSelection(ISelection selection) { * @return true if the given selection is supported, otherwise * false. */ - protected abstract boolean supportsSelection(List selection); + protected abstract boolean supportsSelection(Collection selection); @Override public String getDiagramBody(IEditorPart editor, ISelection selection) { @@ -118,14 +117,14 @@ public String getDiagramBody(IEditorPart editor, ISelection selection) { * @return The generated diagram text describing the given elements using the * PlantUML DSL. */ - protected abstract String getDiagramBody(List elements); + protected abstract String getDiagramBody(Collection elements); /** * Getter for {@link #allElements}. * * @return All elements that can potentially be visualised. */ - protected List getAllElements() { + protected Collection getAllElements() { return allElements; } @@ -147,8 +146,8 @@ protected List getAllElements() { * direction of a bidirectional association. * @return The list of edges without any opposing navigation direction. */ - protected List handleOpposites(List edges) { - List edgesWithoutEOpposite = new ArrayList<>(); + protected Collection handleOpposites(Collection edges) { + HashSet edgesWithoutEOpposite = new HashSet<>(); for (VisualEdge edge : edges) { if (!edge.hasEOpposite() || !edgesWithoutEOpposite.contains(edge.findEOpposite(edges).orElse(null))) edgesWithoutEOpposite.add(edge); diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java index 9d6d2b23..7857e44d 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java @@ -1,8 +1,8 @@ package org.moflon.core.ui.visualisation; -import java.util.ArrayList; +import java.util.Collection; import java.util.HashSet; -import java.util.List; +import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -12,9 +12,9 @@ import org.eclipse.emf.ecore.EOperation; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EParameter; -import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.moflon.core.ui.VisualiserUtilities; +import org.moflon.core.ui.visualisation.strategy.ClassDiagramStrategies; /** * Visualises UML Class Diagrams for Ecore metamodels. @@ -22,32 +22,46 @@ */ public class EMoflonMetamodelVisualiser extends EMoflonEcoreVisualiser { + /** + * Allows chained operations on a diagram. Should at least be {@link Function#identity()}. + */ + private Function strategy; + + public EMoflonMetamodelVisualiser() { + super(); + + // set default strategy + strategy = ClassDiagramStrategies::determineEdgesForSelection;// + strategy = strategy.andThen(ClassDiagramStrategies::expandNeighbourhoodBidirectional); + } + @Override - public boolean supportsSelection(List selection) { + public boolean supportsSelection(Collection selection) { // An Ecore metamodel must contain EModelElements only. If it contains other // elements, the selection is not supported by this visualiser. return !VisualiserUtilities.hasModelElements(selection); } @Override - protected String getDiagramBody(List elements) { - List allClasses = getAllElements().stream()// + protected String getDiagramBody(Collection selection) { + HashSet allClasses = getAllElements().stream()// .filter(EClass.class::isInstance)// .map(EClass.class::cast)// - .collect(Collectors.toList()); + .collect(Collectors.toCollection(HashSet::new)); + + // For every selected EModelElement choose an appropriate EClass to represent it. + Collection chosenClasses = resolveSelection(selection); - List chosenClasses = determineClassesToVisualise(elements); - List chosenClassesWithNeighbors = expandByNeighborHood(allClasses, chosenClasses); + // Create diagram and process it using the defined strategy. + ClassDiagram diagram = strategy.apply(new ClassDiagram(allClasses, chosenClasses)); + diagram.setEdges(handleOpposites(diagram.getEdges())); - List refs = handleOpposites(determineReferencesToVisualise(chosenClassesWithNeighbors)); - return EMoflonPlantUMLGenerator.visualiseEcoreElements(chosenClassesWithNeighbors, refs); + return EMoflonPlantUMLGenerator.visualiseEcoreElements(diagram); } - private List determineClassesToVisualise(List selection) { - ArrayList result = new ArrayList<>(selection.size()); - - HashSet cache = new HashSet<>(); - + private Collection resolveSelection(Collection selection) { + HashSet result = new HashSet<>(selection.size()); + // retrieve classes, and enclosing classes of operations, attributes... // TODO: resolve EDataType as well? for (EObject eobject : selection) { @@ -70,7 +84,7 @@ private List determineClassesToVisualise(List selection) { eclass = etype.getEClassifier() instanceof EClass ? (EClass) etype.getEClassifier() : null; } - if (eclass != null && cache.add(eclass)) { + if (eclass != null && !result.contains(eclass)) { result.add(eclass); } } @@ -83,57 +97,9 @@ private List determineClassesToVisualise(List selection) { .flatMap(epackage -> epackage.getEClassifiers().stream())// .filter(EClass.class::isInstance)// .map(EClass.class::cast)// - .filter(cache::add)// + .filter(cls -> !result.contains(cls))// .forEach(result::add); return result; } - - private List expandByNeighborHood(List allClasses, List chosenClasses) { - HashSet cache = new HashSet<>(chosenClasses); - - // add all neighboring classes by reference and generalisation dependency - HashSet result = new HashSet<>(chosenClasses); - for(EClass c : allClasses) { - // neighborhood by reference - for(EReference r : c.getEReferences()) { - EClass t = r.getEReferenceType(); - if(cache.contains(c) || cache.contains(t)) { - result.add(c); - result.add(t); - } - } - // neighborhood by generalisation dependency - for(EClass s : c.getESuperTypes()) { - if(cache.contains(c) || cache.contains(s)) { - result.add(c); - result.add(s); - } - } - } - - return new ArrayList<>(result); - } - - private List determineReferencesToVisualise(List chosenClasses) { - HashSet cache = new HashSet<>(chosenClasses); - - // Gather all references in between selected classes and between selected classes and non-selected classes. - List result = chosenClasses.stream()// - .flatMap(c -> c.getEReferences().stream())// - .filter(ref -> cache.contains(ref.getEContainingClass()) && cache.contains(ref.getEReferenceType())) - .map(ref -> new VisualEdge(ref, EdgeType.REFERENCE, ref.getEContainingClass(), ref.getEReferenceType()))// - .collect(Collectors.toList());// - - // Gather all generalisation dependencies. - for(EClass c : chosenClasses) { - for(EClass s : c.getESuperTypes()) { - if(cache.contains(c) && cache.contains(s)) { - result.add(new VisualEdge(null, EdgeType.GENERALISATION, c, s)); - } - } - } - - return result; - } } diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java index a60f5d9e..f171b462 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java @@ -1,14 +1,11 @@ package org.moflon.core.ui.visualisation; -import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; -import java.util.List; +import java.util.function.Function; + import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.EReference; -import org.eclipse.emf.ecore.util.EContentsEList; -import org.eclipse.emf.ecore.util.EContentsEList.FeatureIterator; import org.moflon.core.ui.VisualiserUtilities; +import org.moflon.core.ui.visualisation.strategy.ObjectDiagramStrategies; /** * Visualises UML Object Diagrams for Ecore models. @@ -16,8 +13,21 @@ */ public class EMoflonModelVisualiser extends EMoflonEcoreVisualiser { + /** + * Allows chained operations on a diagram. Should at least be {@link Function#identity()}. + */ + private Function strategy; + + public EMoflonModelVisualiser() { + super(); + + // set default strategy + strategy = ObjectDiagramStrategies::determineEdgesForSelection; + strategy = strategy.andThen(ObjectDiagramStrategies::expandNeighbourhoodBidirectional); + } + @Override - public boolean supportsSelection(List selection) { + public boolean supportsSelection(Collection selection) { // An Ecore model must contain EObjects only, which are not EModelElements. If // it contains other // elements, the selection is not supported by this visualiser. @@ -25,65 +35,13 @@ public boolean supportsSelection(List selection) { } @Override - protected String getDiagramBody(List elements) { - List objects = determineObjectsToVisualise(elements); - List links = handleOpposites(determineLinksToVisualise(objects)); - return EMoflonPlantUMLGenerator.visualiseModelElements(objects, links); - } - - @SuppressWarnings("rawtypes") - private List determineObjectsToVisualise(List selection) { - HashSet cache = new HashSet<>(selection); + protected String getDiagramBody(Collection selection) { + Collection allObjects = getAllElements(); - List result = new ArrayList<>(selection); - for (EObject o : selection) { - // add parent if there is one - if(o.eContainer() != null && cache.add(o.eContainer())) { - result.add(o.eContainer()); - } - - for (EContentsEList.FeatureIterator featureIterator = // - (EContentsEList.FeatureIterator) o.eCrossReferences().iterator(); featureIterator.hasNext();) { - EObject trg = (EObject) featureIterator.next(); - if(cache.add(trg)) { - result.add(trg); - } - } - for (EContentsEList.FeatureIterator featureIterator = // - (EContentsEList.FeatureIterator) o.eContents().iterator(); featureIterator.hasNext();) { - EObject trg = (EObject) featureIterator.next(); - if(cache.add(trg)) { - result.add(trg); - } - } - } + // Create diagram and process it using the defined strategy. + ObjectDiagram diagram = strategy.apply(new ObjectDiagram(allObjects, selection)); + diagram.setEdges(handleOpposites(diagram.getEdges())); - return result; - } - - @SuppressWarnings("rawtypes") - private List determineLinksToVisualise(List chosenObjects) { - HashSet links = new HashSet<>(); - for (EObject o : chosenObjects) { - for (EContentsEList.FeatureIterator featureIterator = // - (EContentsEList.FeatureIterator) o.eCrossReferences().iterator(); featureIterator.hasNext();) { - addVisualEdge(featureIterator, chosenObjects, links, o); - } - for (EContentsEList.FeatureIterator featureIterator = // - (EContentsEList.FeatureIterator) o.eContents().iterator(); featureIterator.hasNext();) { - addVisualEdge(featureIterator, chosenObjects, links, o); - } - } - - return new ArrayList<>(links); - } - - @SuppressWarnings("rawtypes") - private void addVisualEdge(FeatureIterator featureIterator, Collection chosenObjects, - Collection refs, EObject src) { - EObject trg = (EObject) featureIterator.next(); - EReference eReference = (EReference) featureIterator.feature(); - if (chosenObjects.contains(trg)) - refs.add(new VisualEdge(eReference, EdgeType.LINK, src, trg)); + return EMoflonPlantUMLGenerator.visualiseModelElements(diagram); } } diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend index 798efbb3..65dbd8db 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend @@ -43,25 +43,33 @@ class EMoflonPlantUMLGenerator { ''' } - def static String visualiseEcoreElements(Collection eclasses, Collection refs){ + def static String visualiseEcoreElements(ClassDiagram diagram){ ''' - «FOR c : eclasses» + «FOR c : diagram.getSelection» «IF(c.abstract)»abstract «ENDIF»class «identifierForClass(c)» «ENDFOR» - «visualiseEdges(refs)» + «FOR c : diagram.getNeighbourhood» + «IF(c.abstract)»abstract «ENDIF»class «identifierForClass(c)» + «ENDFOR» + «visualiseEdges(diagram.getEdges)» ''' } - def static String visualiseModelElements(Collection objects, Collection links){ + def static String visualiseModelElements(ObjectDiagram diagram){ idMap.clear ''' - «FOR o : objects» + «FOR o : diagram.getSelection» object «identifierForObject(o)»{ «visualiseAllAttributes(o)» } «ENDFOR» - «visualiseEdges(links)» + «FOR o : diagram.getNeighbourhood» + object «identifierForObject(o)»{ + «visualiseAllAttributes(o)» + } + «ENDFOR» + «visualiseEdges(diagram.getEdges)» ''' } diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EdgeType.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EdgeType.java index b33e3da1..7a14a4b9 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EdgeType.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EdgeType.java @@ -1,6 +1,13 @@ package org.moflon.core.ui.visualisation; /** + * Used to distinguish classes of edges. + * + * {@link EdgeType#GENERALISATION} identifies generalisation dependencies in + * class diagrams, {@link EdgeType#REFERENCE} identifies references in class + * diagrams, and {@link EdgeType#LINK} is used to identify cross-references or + * containment-relations in object diagrams. + * * @author Johannes Brandt (initial contribution) * */ diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/ObjectDiagram.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/ObjectDiagram.java new file mode 100644 index 00000000..a2a7fa62 --- /dev/null +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/ObjectDiagram.java @@ -0,0 +1,28 @@ +/** + * + */ +package org.moflon.core.ui.visualisation; + +import java.util.Collection; + +import org.eclipse.emf.ecore.EObject; + +/** + * Represents an object diagram. + * + * The nodes of this diagram type are {@link EObject} instances. + * + * @author Johannes Brandt + * + */ +public class ObjectDiagram extends Diagram { + + public ObjectDiagram(Collection superset) { + super(superset); + } + + public ObjectDiagram(Collection superset, Collection selection) { + super(superset, selection); + } + +} diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/strategy/ClassDiagramStrategies.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/strategy/ClassDiagramStrategies.java new file mode 100644 index 00000000..22c61fbd --- /dev/null +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/strategy/ClassDiagramStrategies.java @@ -0,0 +1,120 @@ +package org.moflon.core.ui.visualisation.strategy; + +import java.util.Collection; +import java.util.HashSet; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.emf.ecore.EClass; +import org.moflon.core.ui.visualisation.ClassDiagram; +import org.moflon.core.ui.visualisation.EdgeType; +import org.moflon.core.ui.visualisation.VisualEdge; + +/** + * Contains various methods for manipulating {@link ClassDiagram} instances. + * + * @author Johannes Brandt + * + */ +public class ClassDiagramStrategies { + + /** + * Computes all edges between selected classes in the given class diagram. + * + * This method will add each computed edge to the edges stored with the diagram. + * Only edges between selected classes will be computed and added. + * + * @param diagram + * The class diagram containing the selection, for which the edges + * are to be computed. + * @return The resulting class diagram. + */ + public static ClassDiagram determineEdgesForSelection(ClassDiagram diagram) { + Collection selection = diagram.getSelection(); + Collection edges = diagram.getEdges(); + + determineOutboundEdgesBetween(selection, selection, edges); + + return diagram; + } + + /** + * Expands the given {@link ClassDiagram}'s neighbourhood by one degree, + * bidirectional. + * + * The given diagram's neighbourhood is expanded, by adding all neighbors of the + * current neighbourhood. The direction of the associations between classes is + * irrelevant. If no neighbourhood is defined with the given diagram, then the + * selection's neighbours are added to the neighbourhood. + * + *

+ * Note: If a neighbourhood expansion of a degree greater than one is + * wished, this method can simple be chained. + *

+ * + * @param diagram + * The diagram, of which the neighbourhood is to be increased by a + * degree of one. + * @return The diagram with the increased neighbourhood degree. + */ + public static ClassDiagram expandNeighbourhoodBidirectional(ClassDiagram diagram) { + Collection selection = diagram.getSelection(); + Collection neighbourhood = diagram.getNeighbourhood(); + Collection others = diagram.getSuperset().stream()// + .filter(cls -> !selection.contains(cls))// + .filter(cls -> !neighbourhood.contains(cls))// + .collect(Collectors.toCollection(HashSet::new)); + Collection edges = diagram.getEdges(); + + // find 1-neighbourhood edges + if (neighbourhood.isEmpty()) { + determineOutboundEdgesBetween(selection, others, edges); + determineOutboundEdgesBetween(others, selection, edges); + } else { + determineOutboundEdgesBetween(neighbourhood, others, edges); + determineOutboundEdgesBetween(others, neighbourhood, edges); + } + + // update neighbourhood + edges.stream()// + .flatMap(edge -> Stream.of((EClass) edge.getSrc(), (EClass) edge.getTrg()))// + .filter(cls -> !selection.contains(cls))// + .filter(cls -> !neighbourhood.contains(cls))// + .forEach(neighbourhood::add); + + return diagram; + } + + /** + * Determines all outbound edges from classes in sourceElements to + * classes in targetElements. + * + * @param sourceElements + * The set of classes for which all outbound edges shall be + * determined. + * @param targetElements + * The set of classes which represent targets of all outbound edges + * from the set of source elements. + * @param edges + * All edges with a class from sourceElements as source, + * and a class from targetElements as target. + */ + private static void determineOutboundEdgesBetween(Collection sourceElements, + Collection targetElements, Collection edges) { + // search references + sourceElements.stream()// + .flatMap(cls -> cls.getEReferences().stream())// + .filter(ref -> targetElements.contains(ref.getEReferenceType()))// + .map(ref -> new VisualEdge(ref, EdgeType.REFERENCE, ref.getEContainingClass(), ref.getEReferenceType()))// + .forEach(edges::add);// + + // search generalisations + for (EClass c : sourceElements) { + for (EClass s : c.getESuperTypes()) { + if (targetElements.contains(s)) { + edges.add(new VisualEdge(null, EdgeType.GENERALISATION, c, s)); + } + } + } + } +} diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/strategy/ObjectDiagramStrategies.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/strategy/ObjectDiagramStrategies.java new file mode 100644 index 00000000..abcb34d6 --- /dev/null +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/strategy/ObjectDiagramStrategies.java @@ -0,0 +1,124 @@ +package org.moflon.core.ui.visualisation.strategy; + +import java.util.Collection; +import java.util.HashSet; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.util.EContentsEList; +import org.moflon.core.ui.visualisation.EdgeType; +import org.moflon.core.ui.visualisation.ObjectDiagram; +import org.moflon.core.ui.visualisation.VisualEdge; + +/** + * Contains various methods for manipulating {@link ObjectDiagram} instances. + * + * @author Johannes Brandt + * + */ +public class ObjectDiagramStrategies { + + /** + * Computes all edges between selected objects in the given object diagram. + * + * This method will add each computed edge to the edges stored with the diagram. + * Only edges between selected objects will be computed and added. + * + * @param diagram + * The object diagram containing the selection, for which the edges + * are to be computed. + * @return The resulting object diagram. + */ + public static ObjectDiagram determineEdgesForSelection(ObjectDiagram diagram) { + Collection selection = diagram.getSelection(); + Collection edges = diagram.getEdges(); + + determineOutboundEdgesBetween(selection, selection, edges); + + return diagram; + } + + /** + * Expands the given {@link ObjectDiagram}'s neighbourhood by one degree, + * bidirectional. + * + * The given diagram's neighbourhood is expanded, by adding all neighbors of the + * current neighbourhood. The direction of the associations between objects is + * irrelevant. If no neighbourhood is defined with the given diagram, then the + * selection's neighbours are added to the neighbourhood. + * + *

+ * Note: If a neighbourhood expansion of a degree greater than one is + * wished, this method can simple be chained. + *

+ * + * @param diagram + * The diagram, of which the neighbourhood is to be increased by a + * degree of one. + * @return The diagram with the increased neighbourhood degree. + */ + public static ObjectDiagram expandNeighbourhoodBidirectional(ObjectDiagram diagram) { + Collection selection = diagram.getSelection(); + Collection neighbourhood = diagram.getNeighbourhood(); + Collection others = diagram.getSuperset().stream()// + .filter(obj -> !selection.contains(obj))// + .filter(obj -> !neighbourhood.contains(obj))// + .collect(Collectors.toCollection(HashSet::new)); + Collection edges = diagram.getEdges(); + + // find 1-neighbourhood edges + if (neighbourhood.isEmpty()) { + determineOutboundEdgesBetween(selection, others, edges); + determineOutboundEdgesBetween(others, selection, edges); + } else { + determineOutboundEdgesBetween(neighbourhood, others, edges); + determineOutboundEdgesBetween(others, neighbourhood, edges); + } + + // update neighbourhood + edges.stream()// + .flatMap(edge -> Stream.of(edge.getSrc(), edge.getTrg()))// + .filter(obj -> !selection.contains(obj))// + .filter(obj -> !neighbourhood.contains(obj))// + .forEach(neighbourhood::add); + + return diagram; + } + + /** + * Determines all outbound edges from objects in sourceElements to + * objects in targetElements. + * + * @param sourceElements + * The set of objects for which all outbound edges shall be + * determined. + * @param targetElements + * The set of objects which represent targets of all outbound edges + * from the set of source elements. + * @param edges + * All edges with an object from sourceElements as source, + * and an object from targetElements as target. + */ + @SuppressWarnings("rawtypes") + private static void determineOutboundEdgesBetween(Collection sourceElements, Collection targetElements, + Collection edges) { + for (EObject obj : sourceElements) { + for (EContentsEList.FeatureIterator featureIterator = // + (EContentsEList.FeatureIterator) obj.eCrossReferences().iterator(); featureIterator.hasNext();) { + EObject trg = (EObject) featureIterator.next(); + EReference eReference = (EReference) featureIterator.feature(); + if (targetElements.contains(trg)) + edges.add(new VisualEdge(eReference, EdgeType.LINK, obj, trg)); + } + for (EContentsEList.FeatureIterator featureIterator = // + (EContentsEList.FeatureIterator) obj.eContents().iterator(); featureIterator.hasNext();) { + EObject trg = (EObject) featureIterator.next(); + EReference eReference = (EReference) featureIterator.feature(); + if (targetElements.contains(trg)) + edges.add(new VisualEdge(eReference, EdgeType.LINK, obj, trg)); + } + } + } +} From 9c63732b7560551eeeebbf5d4b80a23643af7d84 Mon Sep 17 00:00:00 2001 From: Johannes Brandt <5931715+eTralse@users.noreply.github.com> Date: Mon, 30 Jul 2018 20:33:55 +0200 Subject: [PATCH 12/37] Improvement: Makes eMoflon visualisers configurable. If an eMoflon visualiser implements the ConfigurableVisualiser interface, it can be configured by the singleton Configurator class. E.g. handlers can then instruct the configurator to set a diagram processing strategy or style bits for the PlantUML code generation. Everything method that conforms to the functional interface DiagramStrategy may be passed to the configurator as a strategy. A DiagramStrategy takes a diagram as input, and returns the processed diagram. The actual type, or what passes as a diagram, is not enforced. However, the ConfigurableVisualiser interface defines a generic type paramater to associate a configurable visualiser with a diagram type and hence, the diagram type supported by a strategy for that visualiser. For example, the metamodel visualiser uses ClassDiagram as container type for a class diagram. Therefore it will only accept methods as strategy which take a ClassDiagram instance as input and return value. The EMoflonPlantUMLGenerator has been adjusted to take style bits as an input for its metamodel and model generation methods. However, no functionality has been implemented with it, as this will follow in later commits. --- .../visualisation/ConfigurableVisualiser.java | 85 ++++++ .../core/ui/visualisation/Configurator.java | 261 ++++++++++++++++++ .../visualisation/EMoflonEcoreVisualiser.java | 69 +++-- .../EMoflonMetamodelVisualiser.java | 51 ++-- .../visualisation/EMoflonModelVisualiser.java | 45 ++- .../EMoflonPlantUMLGenerator.xtend | 4 +- .../ui/visualisation/EMoflonVisualiser.java | 4 + .../strategy/DiagramStrategy.java | 29 ++ 8 files changed, 496 insertions(+), 52 deletions(-) create mode 100644 org.moflon.core.ui/src/org/moflon/core/ui/visualisation/ConfigurableVisualiser.java create mode 100644 org.moflon.core.ui/src/org/moflon/core/ui/visualisation/Configurator.java create mode 100644 org.moflon.core.ui/src/org/moflon/core/ui/visualisation/strategy/DiagramStrategy.java diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/ConfigurableVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/ConfigurableVisualiser.java new file mode 100644 index 00000000..15397da6 --- /dev/null +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/ConfigurableVisualiser.java @@ -0,0 +1,85 @@ +package org.moflon.core.ui.visualisation; + +import org.moflon.core.ui.visualisation.Configurator.StrategyPart; +import org.moflon.core.ui.visualisation.strategy.DiagramStrategy; + +/** + * This interface defines the required method signatures for a visualiser (such + * as all classes inheriting from {@link EMoflonVisualiser}) to be configurable. + * + * A visualiser is configurable, if style bits can be set to influence the + * PlantUML DSL code generation, and if a strategy can be set for the + * computation of the diagram nodes and edges that are to be visualised. + * Furthermore, a configurable visualiser is required to implement a check + * method, such that a {@link Configurator} instance might check, whether or not + * a specific diagram type is supported by this visualiser. + * + * @author Johannes Brandt + * + * @param + * The specific type for which a strategy can be applied. Usually + * this type encapsulates all the information necessary to describe a + * diagram that is to be visualised by this visualiser. Examples for + * such a type are {@link Diagram}, and all its inheriting classes, + * such as {@link ClassDiagram} and {@link ObjectDiagram}. However, + * the type parameter does not necessarily have to be or extend + * {@link Diagram}. + */ +public interface ConfigurableVisualiser { + + /** + * Sets the style bits that are to be employed during the PlantUML diagram text + * generation. + * + * Style bits define how a diagram is visualised, e.g. which elements of the + * diagram are shown or in which color. For the declaration of some general + * style bits see {@link EMoflonPlantUMLGenerator}. + * + * @param style + * The style bits. + */ + void setDiagramStyle(int style); + + /** + * Sets a strategy for processing diagrams of type T, i.e. the + * diagram type that is supported by this visualiser. + * + * @param strategy + * The strategy that is to be applied to diagrams handled by this + * visualiser. + */ + void setDiagramStrategy(DiagramStrategy strategy); + + /** + * This method is used by configurator units, such as {@link Configurator}. If + * the given {@link Class} instance encapsulates the diagram type supported by + * this visualiser, this method is expected to return true. This + * method is called by configurators, to ensure, that a specific strategy can be + * applied to this visualiser. + * + * @param diagramClass + * The {@link Class} instance resembling a diagram type, for which it + * is to be checked, whether it is supported by this visualiser. + * @return true, if the given diagram class describes a diagram + * type, which is supported by this visualiser, false + * otherwise. + */ + boolean supportsDiagramType(Class diagramClass); + + /** + * Provides a default strategy for every {@link StrategyPart}. + * + *

+ * Note: The default implementation of this methods returns + * {@link UnaryOperator#identity()} for every {@link StrategyPart}. + *

+ * + * @param part + * The strategy part, for which a partial default strategy is to be + * returned. + * @return The default strategy for the given strategy part. + */ + default DiagramStrategy getDefaultStrategy(StrategyPart part) { + return DiagramStrategy.identity(); + } +} diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/Configurator.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/Configurator.java new file mode 100644 index 00000000..d6b0c24c --- /dev/null +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/Configurator.java @@ -0,0 +1,261 @@ +package org.moflon.core.ui.visualisation; + +import java.util.HashMap; +import org.moflon.core.ui.visualisation.strategy.DiagramStrategy; + +/** + * Configures {@link EMoflonVisualiser}s with regard to their diagram processing + * strategy (manipulation of nodes and edges) and diagram style (PlantUML code + * generation). + * + * For an {@link EMoflonVisualiser} to be configured by this class, it needs to + * be implement the {@link ConfigurableVisualiser} interface. Then, the diagram + * style is set for all {@link ConfigurableVisualiser} instances, if it is + * changed via {@link #setDiagramStyle(int, boolean)}. The diagram processing + * strategy (= diagram strategy) can only be applied to visualisers, which + * support a certain diagram type (see + * {@link #setDiagramStrategy(Class, StrategyPart, DiagramStrategy)}. A complete + * diagram strategy consists of multiple partial strategies. Each part of a + * complete diagram strategy is identified by a value of {@link StrategyPart}. + * If one partial strategy is changed via + * {@link #setDiagramStrategy(Class, StrategyPart, DiagramStrategy)}, the + * complete diagram strategy is recomposed and applied to all visualisers, which + * support the diagram type associated with the strategy. + * + *

+ * Note: This class is singleton. + *

+ * + * @author Johannes Brandt + * + */ +public class Configurator { + + /** + * Used to identify a specific part of a strategy. + * + * A strategy, as implied by this enum, consists of two parts, initialization + * and neighbourhood calculation. The first is associated with + * {@link StrategyPart#INIT}, the latter with + * {@link StrategyPart#NEIGHBOURHOOD}. Therefore, this enum enables e.g. command + * handlers to call + * {@link Configurator#setDiagramStrategy(Class, StrategyPart, DiagramStrategy)}, + * and precisely state which part of the strategy they wish to change. + * + * @author Johannes Brandt + * + */ + public static enum StrategyPart { + // TODO: Improve mechanism to support the isolation of strategy parts, which are + // defined elsewhere, i.e. make the configurator "unaware" of the specific + // strategy parts. + INIT, NEIGHBOURHOOD; + } + + /** + * The monitor used to synchronize access to visualiser style or strategy. + */ + private static final Object MONITOR = new Object(); + + /** + * Stores all {@link EMoflonVisualiser} instances registered to this Eclipse + * Platform. + */ + private HashMap, EMoflonVisualiser> visualisers; + + /** + * Style bits for the {@link EMoflonVisualiser}s. + */ + private int style = 1; + + /** + * Stores the strategy for the part {@link StrategyPart#INIT} for each diagram + * type. + */ + private HashMap, DiagramStrategy> diagramTypeToInitStrategy; + + /** + * Stores the strategy for the part {@link StrategyPart#NEIGHBOURHOOD} for each + * diagram type. + */ + private HashMap, DiagramStrategy> diagramTypeToNeighbourhoodStrategy; + + /** + * Provides the {@link Configurator} singleton instance. + */ + private static class InstanceHolder { + private static final Configurator INSTANCE = new Configurator(); + } + + /** + * Private constructor to prevent instance creation. + */ + private Configurator() { + visualisers = new HashMap<>(); + diagramTypeToInitStrategy = new HashMap<>(); + diagramTypeToNeighbourhoodStrategy = new HashMap<>(); + } + + /** + * Provides thread-safe singleton access. + * + * @return The singleton instance for {@link Configurator}. + */ + public static Configurator getInstance() { + return InstanceHolder.INSTANCE; + } + + /** + * Registers a visualiser for configuration. + * + * Any {@link EMoflonVisualiser} can be registered with this configurator, such + * that diagram style and strategy configuration can be passed that visualiser. + * + *

+ * Note: For an {@link EMoflonVisualiser} to be actually handled in terms + * of diagram style and diagram strategy configuration, it needs to implement + * the {@link ConfigurableVisualiser} interface. + *

+ * + * @param eMoflonVisualiser + * The visualiser that is to be configured by this configurator. + */ + public void registerVisualiser(EMoflonVisualiser eMoflonVisualiser) { + synchronized (MONITOR) { + visualisers.put(eMoflonVisualiser.getClass(), eMoflonVisualiser); + } + } + + /** + * Sets the given style bits in the current diagram style and applies them to + * all registered visualisers. + * + * @param styleBits + * The style bits that are to be set or unset. + * @param doSet + * If true, the 1-bits in styleBits are set in the + * diagram style, otherwise the 1-bits in styleBits are unset in the + * diagram style. + */ + public void setDiagramStyle(int styleBits, boolean doSet) { + synchronized (MONITOR) { + if (doSet) { + style |= styleBits; + } else { + style &= ~styleBits; + } + applyStyle(style); + } + } + + /** + * Sets the given strategy for the specified strategy part and diagram class, + * composes the full strategy, and applies it to all registered visualisers, + * that support the given diagram clas. + * + * @param diagramClass + * The diagram class for which this partial strategy is to be set. + * @param part + * Identifies the part of a full strategy that is to be set. + * @param strategy + * The partial strategy, that is to be set and applied to all + * registered and supported visualisers. + */ + @SuppressWarnings("unchecked") + public void setDiagramStrategy(Class diagramClass, StrategyPart part, + DiagramStrategy strategy) { + if (diagramClass == null || part == null || strategy == null) { + return; + } + + synchronized (MONITOR) { + switch (part) { + case INIT: + if (!diagramTypeToNeighbourhoodStrategy.containsKey(diagramClass)) { + diagramTypeToNeighbourhoodStrategy.put(diagramClass, + getDefaultStrategy(diagramClass, StrategyPart.NEIGHBOURHOOD)); + } + diagramTypeToInitStrategy.put(diagramClass, strategy); + break; + case NEIGHBOURHOOD: + if (!diagramTypeToInitStrategy.containsKey(diagramClass)) { + diagramTypeToInitStrategy.put(diagramClass, getDefaultStrategy(diagramClass, StrategyPart.INIT)); + } + diagramTypeToNeighbourhoodStrategy.put(diagramClass, strategy); + break; + default: + return; + } + + DiagramStrategy fullStrategy = (DiagramStrategy) diagramTypeToInitStrategy.get(diagramClass); + fullStrategy = fullStrategy + .andThen((DiagramStrategy) diagramTypeToNeighbourhoodStrategy.get(diagramClass)); + applyStrategy(diagramClass, fullStrategy); + } + } + + /** + * Applies the given diagram style to all registered {@link EMoflonVisualiser}s. + * + * @param style + * The style that is to be applied. + */ + private void applyStyle(int style) { + synchronized (MONITOR) { + visualisers.values().stream()// + .filter(ConfigurableVisualiser.class::isInstance)// + .map(ConfigurableVisualiser.class::cast)// + .forEach(configurable -> configurable.setDiagramStyle(style)); + } + } + + /** + * Used to determine the default partial strategies for all registered + * {@link EMoflonVisualiser}s, which support a specified diagram class. + * + * @param diagramClass + * The diagram class that needs to be supported by at least one of + * the visualisers. + * @param part + * The identifier for the part of a strategy, for which a default + * implementation is wanted. + * @return The default implementation for the partial strategy, which supports + * the processing of the specified diagram class. + */ + @SuppressWarnings("unchecked") + private DiagramStrategy getDefaultStrategy(Class diagramClass, StrategyPart part) { + // TODO: Improve mechanism. Only one visualiser gets to determine the default + // strategy. If there are multiple visualisers supporting the same diagram type, + // then this implementation returns the first. Any other is not taken into + // consideration. + return visualisers.values().stream()// + .filter(ConfigurableVisualiser.class::isInstance)// + .map(ConfigurableVisualiser.class::cast)// + .filter(cVis -> cVis.supportsDiagramType(diagramClass))// + .map(cVis -> cVis.getDefaultStrategy(part))// + .findFirst()// + .orElse(DiagramStrategy.identity()); + } + + /** + * Applies the specified strategy to all visualisers that support the given + * diagram class. + * + * @param diagramClass + * Identifies the diagram type that is supported by the given + * strategy. + * @param strategy + * The strategy that is to be applied to all visualisers, which + * support the specified diagram class. + */ + @SuppressWarnings("unchecked") + private void applyStrategy(Class diagramClass, DiagramStrategy strategy) { + synchronized (MONITOR) { + visualisers.values().stream()// + .filter(ConfigurableVisualiser.class::isInstance)// + .map(ConfigurableVisualiser.class::cast)// + .filter(cVis -> cVis.supportsDiagramType(diagramClass))// + .forEach(cVis -> cVis.setDiagramStrategy(strategy)); + } + } +} diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java index a3f58f88..225983c0 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java @@ -2,10 +2,13 @@ import java.util.Collection; import java.util.HashSet; + import org.eclipse.emf.ecore.EObject; +import org.eclipse.jdt.core.dom.PrimitiveType.Code; import org.eclipse.jface.viewers.ISelection; import org.eclipse.ui.IEditorPart; import org.moflon.core.ui.VisualiserUtilities; +import org.moflon.core.ui.visualisation.strategy.DiagramStrategy; /** * Abstract implementation for the visualisation of Ecore metamodels and models. @@ -13,7 +16,7 @@ * @author Johannes Brandt (initial contribution) * */ -public abstract class EMoflonEcoreVisualiser extends EMoflonVisualiser { +public abstract class EMoflonEcoreVisualiser extends EMoflonVisualiser implements ConfigurableVisualiser { /** * Stores whether or not the superset of Ecore elements can be retrieved from @@ -33,6 +36,17 @@ public abstract class EMoflonEcoreVisualiser extends EMoflonVisualiser { */ private Collection allElements; + /** + * Diagram style bits - used in PlantUML diagram text generation. + */ + protected int style = 1; + + /** + * Allows chained operations on a diagram with node type {@link Code T}. Should + * at least be {@link UnaryOperator#identity()}. + */ + protected DiagramStrategy strategy; + @Override public boolean supportsEditor(IEditorPart editor) { // check if editor currently has Ecore related model loaded @@ -68,8 +82,9 @@ public boolean supportsSelection(ISelection selection) { return false; } latestSelection = ecoreSelection; - // in case no elements can be extracted from the editor, allElements is set to the selection - if(!isEmptySelectionSupported) { + // in case no elements can be extracted from the editor, allElements is set to + // the selection + if (!isEmptySelectionSupported) { allElements = ecoreSelection; } @@ -77,17 +92,6 @@ public boolean supportsSelection(ISelection selection) { return isSupported; } - /** - * Checks whether or not a given Ecore selection is supposed to be supported by - * this Ecore visualiser. - * - * @param selection - * All Ecore elements that are supposed to be visualised. - * @return true if the given selection is supported, otherwise - * false. - */ - protected abstract boolean supportsSelection(Collection selection); - @Override public String getDiagramBody(IEditorPart editor, ISelection selection) { // In order to save processing time latestSelection already contains the @@ -99,16 +103,45 @@ public String getDiagramBody(IEditorPart editor, ISelection selection) { // This in turn can be again null, empty or not supported, because the check for // editor support is quite tolerant. This is why this has to be checked here // again. + String result = EMoflonPlantUMLGenerator.emptyDiagram(); if (latestSelection == null || latestSelection.isEmpty()) { - return EMoflonPlantUMLGenerator.emptyDiagram(); + return result; } if (VisualiserUtilities.hasMetamodelElements(latestSelection) && VisualiserUtilities.hasModelElements(latestSelection)) { - return EMoflonPlantUMLGenerator.emptyDiagram(); + return result; + } + + synchronized (this) { + result = getDiagramBody(latestSelection); } - return getDiagramBody(latestSelection); + return result; + } + + @Override + public synchronized void setDiagramStyle(int style) { + this.style = style; + } + + @Override + public synchronized void setDiagramStrategy(DiagramStrategy strategy) { + if (strategy == null) { + throw new IllegalArgumentException("Strategy cannot be null!"); + } + this.strategy = strategy; } + /** + * Checks whether or not a given Ecore selection is supposed to be supported by + * this Ecore visualiser. + * + * @param selection + * All Ecore elements that are supposed to be visualised. + * @return true if the given selection is supported, otherwise + * false. + */ + protected abstract boolean supportsSelection(Collection selection); + /** * Calculates the diagram text for the given Ecore elements. * @@ -118,7 +151,7 @@ public String getDiagramBody(IEditorPart editor, ISelection selection) { * PlantUML DSL. */ protected abstract String getDiagramBody(Collection elements); - + /** * Getter for {@link #allElements}. * diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java index 7857e44d..cea46389 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java @@ -2,7 +2,6 @@ import java.util.Collection; import java.util.HashSet; -import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -14,27 +13,27 @@ import org.eclipse.emf.ecore.EParameter; import org.eclipse.emf.ecore.EStructuralFeature; import org.moflon.core.ui.VisualiserUtilities; +import org.moflon.core.ui.visualisation.Configurator.StrategyPart; import org.moflon.core.ui.visualisation.strategy.ClassDiagramStrategies; +import org.moflon.core.ui.visualisation.strategy.DiagramStrategy; /** * Visualises UML Class Diagrams for Ecore metamodels. * */ -public class EMoflonMetamodelVisualiser extends EMoflonEcoreVisualiser { +public class EMoflonMetamodelVisualiser extends EMoflonEcoreVisualiser { - /** - * Allows chained operations on a diagram. Should at least be {@link Function#identity()}. - */ - private Function strategy; - public EMoflonMetamodelVisualiser() { super(); - + // set default strategy - strategy = ClassDiagramStrategies::determineEdgesForSelection;// - strategy = strategy.andThen(ClassDiagramStrategies::expandNeighbourhoodBidirectional); + strategy = getDefaultStrategy(StrategyPart.INIT)// + .andThen(getDefaultStrategy(StrategyPart.NEIGHBOURHOOD)); + + @SuppressWarnings("unused") + boolean blabla = true; } - + @Override public boolean supportsSelection(Collection selection) { // An Ecore metamodel must contain EModelElements only. If it contains other @@ -42,26 +41,44 @@ public boolean supportsSelection(Collection selection) { return !VisualiserUtilities.hasModelElements(selection); } + @Override + public boolean supportsDiagramType(Class diagramClass) { + return ClassDiagram.class == diagramClass; + } + + @Override + public DiagramStrategy getDefaultStrategy(StrategyPart part) { + switch (part) { + case INIT: + return ClassDiagramStrategies::determineEdgesForSelection; + case NEIGHBOURHOOD: + return DiagramStrategy.identity(); + default: + return super.getDefaultStrategy(part); + } + } + @Override protected String getDiagramBody(Collection selection) { HashSet allClasses = getAllElements().stream()// .filter(EClass.class::isInstance)// .map(EClass.class::cast)// .collect(Collectors.toCollection(HashSet::new)); - - // For every selected EModelElement choose an appropriate EClass to represent it. + + // For every selected EModelElement choose an appropriate EClass to represent + // it. Collection chosenClasses = resolveSelection(selection); - + // Create diagram and process it using the defined strategy. ClassDiagram diagram = strategy.apply(new ClassDiagram(allClasses, chosenClasses)); diagram.setEdges(handleOpposites(diagram.getEdges())); - - return EMoflonPlantUMLGenerator.visualiseEcoreElements(diagram); + + return EMoflonPlantUMLGenerator.visualiseEcoreElements(diagram, style); } private Collection resolveSelection(Collection selection) { HashSet result = new HashSet<>(selection.size()); - + // retrieve classes, and enclosing classes of operations, attributes... // TODO: resolve EDataType as well? for (EObject eobject : selection) { diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java index f171b462..4aeb480c 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java @@ -1,31 +1,29 @@ package org.moflon.core.ui.visualisation; import java.util.Collection; -import java.util.function.Function; - import org.eclipse.emf.ecore.EObject; import org.moflon.core.ui.VisualiserUtilities; +import org.moflon.core.ui.visualisation.Configurator.StrategyPart; +import org.moflon.core.ui.visualisation.strategy.DiagramStrategy; import org.moflon.core.ui.visualisation.strategy.ObjectDiagramStrategies; /** * Visualises UML Object Diagrams for Ecore models. * */ -public class EMoflonModelVisualiser extends EMoflonEcoreVisualiser { +public class EMoflonModelVisualiser extends EMoflonEcoreVisualiser { - /** - * Allows chained operations on a diagram. Should at least be {@link Function#identity()}. - */ - private Function strategy; - public EMoflonModelVisualiser() { super(); - + // set default strategy - strategy = ObjectDiagramStrategies::determineEdgesForSelection; - strategy = strategy.andThen(ObjectDiagramStrategies::expandNeighbourhoodBidirectional); + strategy = getDefaultStrategy(StrategyPart.INIT)// + .andThen(getDefaultStrategy(StrategyPart.NEIGHBOURHOOD)); + + @SuppressWarnings("unused") + boolean blabla = true; } - + @Override public boolean supportsSelection(Collection selection) { // An Ecore model must contain EObjects only, which are not EModelElements. If @@ -34,14 +32,31 @@ public boolean supportsSelection(Collection selection) { return !VisualiserUtilities.hasMetamodelElements(selection); } + @Override + public boolean supportsDiagramType(Class diagramClass) { + return ObjectDiagram.class == diagramClass; + } + + @Override + public DiagramStrategy getDefaultStrategy(StrategyPart part) { + switch (part) { + case INIT: + return ObjectDiagramStrategies::determineEdgesForSelection; + case NEIGHBOURHOOD: + return DiagramStrategy.identity(); + default: + return super.getDefaultStrategy(part); + } + } + @Override protected String getDiagramBody(Collection selection) { Collection allObjects = getAllElements(); - + // Create diagram and process it using the defined strategy. ObjectDiagram diagram = strategy.apply(new ObjectDiagram(allObjects, selection)); diagram.setEdges(handleOpposites(diagram.getEdges())); - - return EMoflonPlantUMLGenerator.visualiseModelElements(diagram); + + return EMoflonPlantUMLGenerator.visualiseModelElements(diagram, style); } } diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend index 65dbd8db..0c27a191 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend @@ -43,7 +43,7 @@ class EMoflonPlantUMLGenerator { ''' } - def static String visualiseEcoreElements(ClassDiagram diagram){ + def static String visualiseEcoreElements(ClassDiagram diagram, int diagramStyle){ ''' «FOR c : diagram.getSelection» «IF(c.abstract)»abstract «ENDIF»class «identifierForClass(c)» @@ -55,7 +55,7 @@ class EMoflonPlantUMLGenerator { ''' } - def static String visualiseModelElements(ObjectDiagram diagram){ + def static String visualiseModelElements(ObjectDiagram diagram, int diagramStyle){ idMap.clear ''' diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonVisualiser.java index cd4fa6f0..b2f63c24 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonVisualiser.java @@ -15,6 +15,10 @@ public abstract class EMoflonVisualiser implements DiagramTextProvider { private static final Logger logger = Logger.getLogger(EMoflonVisualiser.class); private static final int MAX_SIZE = 500; + public EMoflonVisualiser() { + Configurator.getInstance().registerVisualiser(this); + } + @Override public String getDiagramText(IEditorPart editor, ISelection selection) { Optional diagram = Optional.empty(); diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/strategy/DiagramStrategy.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/strategy/DiagramStrategy.java new file mode 100644 index 00000000..ad2ddee7 --- /dev/null +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/strategy/DiagramStrategy.java @@ -0,0 +1,29 @@ +/** + * + */ +package org.moflon.core.ui.visualisation.strategy; + +import java.util.Objects; +import java.util.function.UnaryOperator; + +/** + * @author Johannes Brandt + * + */ +@FunctionalInterface +public interface DiagramStrategy extends UnaryOperator { + + static DiagramStrategy identity() { + return arg -> arg; + } + + default DiagramStrategy andThen(DiagramStrategy after) { + Objects.requireNonNull(after); + return arg -> after.apply(this.apply(arg)); + } + + default DiagramStrategy compose(DiagramStrategy before) { + Objects.requireNonNull(before); + return arg -> this.apply(before.apply(arg)); + } +} From 4ff2bcc8fdbb42fead137e15ac9368b38ac30e89 Mon Sep 17 00:00:00 2001 From: Johannes Brandt <5931715+eTralse@users.noreply.github.com> Date: Mon, 30 Jul 2018 23:51:56 +0200 Subject: [PATCH 13/37] Adds "Show model details" button to view menu of PlantUML view. This toggle button allows to switch between additional details for models or none. Internally, a style bit in combination with the Configurator class has been used. The style bits are handed to the PlantUMLGenerator, where they are evaluated. In addition, the PlantUMLGenerator has been extended to generate operations and attributes of EClass elements as well. --- org.moflon.core.ui/plugin.xml | 37 ++++++++++++ .../ui/handler/ShowModelDetailsHandler.java | 60 +++++++++++++++++++ .../visualisation/EMoflonEcoreVisualiser.java | 2 +- .../EMoflonPlantUMLGenerator.xtend | 39 ++++++++++++ 4 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 org.moflon.core.ui/src/org/moflon/core/ui/handler/ShowModelDetailsHandler.java diff --git a/org.moflon.core.ui/plugin.xml b/org.moflon.core.ui/plugin.xml index 965918e5..f72d2b71 100644 --- a/org.moflon.core.ui/plugin.xml +++ b/org.moflon.core.ui/plugin.xml @@ -96,6 +96,23 @@ + + + + + + + + + + @@ -137,6 +154,10 @@ + + @@ -227,6 +248,22 @@ + + + + + + diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/handler/ShowModelDetailsHandler.java b/org.moflon.core.ui/src/org/moflon/core/ui/handler/ShowModelDetailsHandler.java new file mode 100644 index 00000000..8271f6c9 --- /dev/null +++ b/org.moflon.core.ui/src/org/moflon/core/ui/handler/ShowModelDetailsHandler.java @@ -0,0 +1,60 @@ +/** + * + */ +package org.moflon.core.ui.handler; + +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; +import org.eclipse.core.commands.Command; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.handlers.HandlerUtil; +import org.moflon.core.ui.AbstractCommandHandler; +import org.moflon.core.ui.visualisation.Configurator; +import org.moflon.core.ui.visualisation.EMoflonPlantUMLGenerator; + +/** + * Handles the "Show Model Details" command. + * + * @author Johannes Brandt + * + */ +public class ShowModelDetailsHandler extends AbstractCommandHandler { + + private static final Logger logger = LogManager.getLogger(ShowModelDetailsHandler.class); + + public ShowModelDetailsHandler() { + super(); + } + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + logger.debug("Executing 'Show Model Details'."); + + // toggle the state of the command and retrieve the old value of the state + // (before it was clicked) + Command command = event.getCommand(); + boolean toggleState = !HandlerUtil.toggleCommandState(command); + + // retrieve / change / set configuration + Configurator.getInstance().setDiagramStyle(EMoflonPlantUMLGenerator.SHOW_MODEL_DETAILS, toggleState); + + // notify for change (indirectly, by setting focus to the editor, listeners are + // fired, which in turn update the PlantUml view + final IEditorPart linkedEditor = HandlerUtil.getActiveEditor(event); + updateEditor(linkedEditor); + + return null; + } + + private void updateEditor(IEditorPart editor) { + if (editor != null) { + editor.setFocus(); + } + else { + logger.warn("Could not find any appropriate editor instance to initiate an update of the PlantUml viewpart."); + } + } + +} diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java index 225983c0..abafdac3 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonEcoreVisualiser.java @@ -39,7 +39,7 @@ public abstract class EMoflonEcoreVisualiser extends EMoflonVisualiser implem /** * Diagram style bits - used in PlantUML diagram text generation. */ - protected int style = 1; + protected int style = EMoflonPlantUMLGenerator.SHOW_MODEL_DETAILS; /** * Allows chained operations on a diagram with node type {@link Code T}. Should diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend index 0c27a191..21c778fc 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend @@ -6,8 +6,11 @@ import org.apache.commons.lang3.StringUtils import org.eclipse.emf.ecore.EClass import org.eclipse.emf.ecore.EObject import org.eclipse.emf.ecore.EReference +import org.eclipse.emf.ecore.EOperation class EMoflonPlantUMLGenerator { + public final static int SHOW_MODEL_DETAILS = 1<<0; + static var idMap = new HashMap(); static def String wrapInTags(String body){ @@ -45,11 +48,16 @@ class EMoflonPlantUMLGenerator { def static String visualiseEcoreElements(ClassDiagram diagram, int diagramStyle){ ''' + «plantUMLPreamble(diagramStyle)» «FOR c : diagram.getSelection» «IF(c.abstract)»abstract «ENDIF»class «identifierForClass(c)» + «visualiseEcoreClassAttributes(c)» + «visualiseEcoreClassOperations(c)» «ENDFOR» «FOR c : diagram.getNeighbourhood» «IF(c.abstract)»abstract «ENDIF»class «identifierForClass(c)» + «visualiseEcoreClassAttributes(c)» + «visualiseEcoreClassOperations(c)» «ENDFOR» «visualiseEdges(diagram.getEdges)» ''' @@ -59,6 +67,7 @@ class EMoflonPlantUMLGenerator { idMap.clear ''' + «plantUMLPreamble(diagramStyle)» «FOR o : diagram.getSelection» object «identifierForObject(o)»{ «visualiseAllAttributes(o)» @@ -106,6 +115,30 @@ class EMoflonPlantUMLGenerator { private def static String identifierForClass(EClass c) '''"«c.EPackage.name».«c.name»"''' + private def static String visualiseEcoreClassAttributes(EClass eclass) { + ''' + «FOR a : eclass.EAllAttributes» + «identifierForClass(eclass)» : «a.name» : «a.EType.name» + «ENDFOR» + ''' + } + + private def static String visualiseEcoreClassOperations(EClass eclass) { + ''' + «FOR op : eclass.EAllOperations» + «identifierForClass(eclass)» : «visualiseEcoreOperation(op)» + «ENDFOR» + ''' + } + + private def static String visualiseEcoreOperation(EOperation op) { + '''«op.name»«visualiseEcoreOperationParameterList(op)»«IF(op.EType !== null)» : «op.EType.name»«ENDIF»''' + } + + private def static String visualiseEcoreOperationParameterList(EOperation op) { + '''«IF op.EParameters.size == 0»()«ENDIF»«FOR param : op.EParameters BEFORE '(' SEPARATOR ', ' AFTER ')'»«param.name» : «param.EType.name»«ENDFOR»''' + } + def static visualiseAllAttributes(EObject o) { ''' «FOR a : o.eClass.EAllAttributes» @@ -172,4 +205,10 @@ class EMoflonPlantUMLGenerator { } ''' } + + def static CharSequence plantUMLPreamble(int style){ + ''' + hide «IF(style.bitwiseAnd(SHOW_MODEL_DETAILS) > 0)»empty «ENDIF»members + ''' + } } From dc4bee75fe6fa46a5931e64bda773f3051d23024 Mon Sep 17 00:00:00 2001 From: Johannes Brandt <5931715+eTralse@users.noreply.github.com> Date: Tue, 31 Jul 2018 18:57:43 +0200 Subject: [PATCH 14/37] Adds ViewAction to PlantUML view to toggle abbreviation of labels in models. For this addition, a new command, handler and menu contribution has been added to plugin.xml. The according handler class sets a style bit in the Configurator instance. This stylebit is handed to the EMoflonPlantUMLGenerator via the configurable metamodel and model visualisers. Internally the generator uses apache StringUtils to crop the middle of an oversized string. By default, a string counts as oversized if its length exceeds 11 characters. This length bound is not configurable, however, a realization using the preference store should be feasible. In addition, the notation in model visualisations has been adjusted. Objects are now named : instead of .. --- org.moflon.core.ui/plugin.xml | 26 +++++- .../ui/handler/AbbreviateLabelsHandler.java | 59 ++++++++++++ .../EMoflonPlantUMLGenerator.xtend | 93 +++++++++++++------ 3 files changed, 148 insertions(+), 30 deletions(-) create mode 100644 org.moflon.core.ui/src/org/moflon/core/ui/handler/AbbreviateLabelsHandler.java diff --git a/org.moflon.core.ui/plugin.xml b/org.moflon.core.ui/plugin.xml index f72d2b71..7482b511 100644 --- a/org.moflon.core.ui/plugin.xml +++ b/org.moflon.core.ui/plugin.xml @@ -103,7 +103,7 @@ @@ -113,6 +113,18 @@ + + + + + + + + @@ -158,6 +170,10 @@ class="org.moflon.core.ui.handler.ShowModelDetailsHandler" commandId="org.moflon.core.ui.commands.visualisation.ShowModelDetailsCommand"> + + @@ -263,6 +279,14 @@ style="toggle" tooltip="Shows or hides additional information of model elements. "> + + diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/handler/AbbreviateLabelsHandler.java b/org.moflon.core.ui/src/org/moflon/core/ui/handler/AbbreviateLabelsHandler.java new file mode 100644 index 00000000..60529a09 --- /dev/null +++ b/org.moflon.core.ui/src/org/moflon/core/ui/handler/AbbreviateLabelsHandler.java @@ -0,0 +1,59 @@ +/** + * + */ +package org.moflon.core.ui.handler; + +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; +import org.eclipse.core.commands.Command; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.handlers.HandlerUtil; +import org.moflon.core.ui.AbstractCommandHandler; +import org.moflon.core.ui.visualisation.Configurator; +import org.moflon.core.ui.visualisation.EMoflonPlantUMLGenerator; + +/** + * Handles the "Abbreviate labels" command. + * + * @author Johannes Brandt + * + */ +public class AbbreviateLabelsHandler extends AbstractCommandHandler { + + private static final Logger logger = LogManager.getLogger(AbbreviateLabelsHandler.class); + + public AbbreviateLabelsHandler() { + super(); + } + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + logger.debug("Executing 'Show Model Details'."); + + // toggle the state of the command and retrieve the old value of the state + // (before it was clicked) + Command command = event.getCommand(); + boolean toggleState = !HandlerUtil.toggleCommandState(command); + + // retrieve / change / set configuration + Configurator.getInstance().setDiagramStyle(EMoflonPlantUMLGenerator.ABBR_LABELS, toggleState); + + // notify for change (indirectly, by setting focus to the editor, listeners are + // fired, which in turn update the PlantUml view + final IEditorPart linkedEditor = HandlerUtil.getActiveEditor(event); + updateEditor(linkedEditor); + + return null; + } + + private void updateEditor(IEditorPart editor) { + if (editor != null) { + editor.setFocus(); + } + else { + logger.warn("Could not find any appropriate editor instance to initiate an update of the PlantUml viewpart."); + } + } +} diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend index 21c778fc..2cedef1f 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend @@ -7,9 +7,15 @@ import org.eclipse.emf.ecore.EClass import org.eclipse.emf.ecore.EObject import org.eclipse.emf.ecore.EReference import org.eclipse.emf.ecore.EOperation +import org.eclipse.emf.ecore.ENamedElement class EMoflonPlantUMLGenerator { - public final static int SHOW_MODEL_DETAILS = 1<<0; + + public static final int SHOW_MODEL_DETAILS = 1<<0; + public static final int ABBR_LABELS = 1<<1; + + private static final String REPL_STR = "…"; + private static final int REPL_LEN = 11; static var idMap = new HashMap(); @@ -50,16 +56,16 @@ class EMoflonPlantUMLGenerator { ''' «plantUMLPreamble(diagramStyle)» «FOR c : diagram.getSelection» - «IF(c.abstract)»abstract «ENDIF»class «identifierForClass(c)» - «visualiseEcoreClassAttributes(c)» - «visualiseEcoreClassOperations(c)» + «IF(c.abstract)»abstract «ENDIF»class «identifierForClass(c, diagramStyle)» as «identifierForClass(c)» + «visualiseEcoreClassAttributes(c, diagramStyle)» + «visualiseEcoreClassOperations(c, diagramStyle)» «ENDFOR» «FOR c : diagram.getNeighbourhood» - «IF(c.abstract)»abstract «ENDIF»class «identifierForClass(c)» - «visualiseEcoreClassAttributes(c)» - «visualiseEcoreClassOperations(c)» + «IF(c.abstract)»abstract «ENDIF»class «identifierForClass(c, diagramStyle)» as «identifierForClass(c)» + «visualiseEcoreClassAttributes(c, diagramStyle)» + «visualiseEcoreClassOperations(c, diagramStyle)» «ENDFOR» - «visualiseEdges(diagram.getEdges)» + «visualiseEdges(diagram.getEdges, diagramStyle)» ''' } @@ -69,20 +75,20 @@ class EMoflonPlantUMLGenerator { ''' «plantUMLPreamble(diagramStyle)» «FOR o : diagram.getSelection» - object «identifierForObject(o)»{ - «visualiseAllAttributes(o)» + object «identifierForObject(o, diagramStyle)» as «identifierForObject(o)» { + «visualiseAllAttributes(o, diagramStyle)» } «ENDFOR» «FOR o : diagram.getNeighbourhood» - object «identifierForObject(o)»{ - «visualiseAllAttributes(o)» + object «identifierForObject(o, diagramStyle)» as «identifierForObject(o)» { + «visualiseAllAttributes(o, diagramStyle)» } «ENDFOR» - «visualiseEdges(diagram.getEdges)» + «visualiseEdges(diagram.getEdges, diagramStyle)» ''' } - def private static String visualiseEdges(Collection edges) { + def private static String visualiseEdges(Collection edges, int style) { ''' «FOR edge: edges» «IF(edge.edgeType == EdgeType.REFERENCE)» @@ -90,19 +96,18 @@ class EMoflonPlantUMLGenerator { «var EClass src = ref.EContainingClass» «var EClass trg = ref.EReferenceType» «IF(!edge.hasEOpposite)» - «identifierForClass(src)»«IF ref.isContainment» *«ENDIF»--> "«multiplicityFor(ref)»" «identifierForClass(trg)» : "«ref.name»" + «identifierForClass(src)»«IF ref.isContainment» *«ENDIF»--> "«multiplicityFor(ref)»" «identifierForClass(trg)» : "«nameFor(ref, style)»" «ELSE» - «identifierForClass(src)»"«ref.EOpposite.name» «multiplicityFor(ref.EOpposite)»" «IF ref.isContainment»*«ELSE»<«ENDIF»--«IF ref.EOpposite.isContainment»*«ELSE»>«ENDIF» "«ref.name» «multiplicityFor(ref)»" «identifierForClass(trg)» + «identifierForClass(src)»"«nameFor(ref.EOpposite, style)» «multiplicityFor(ref.EOpposite)»" «IF ref.isContainment»*«ELSE»<«ENDIF»--«IF ref.EOpposite.isContainment»*«ELSE»>«ENDIF» "«nameFor(ref, style)» «multiplicityFor(ref)»" «identifierForClass(trg)» «ENDIF» «ELSEIF(edge.edgeType == EdgeType.GENERALISATION)» «identifierForClass(edge.trg as EClass)»<|--«identifierForClass(edge.src as EClass)» «ELSEIF(edge.edgeType == EdgeType.LINK)» «IF(!edge.hasEOpposite)» - «identifierForObject(edge.src)» --> «identifierForObject(edge.trg)» : "«edge.name»" + «identifierForObject(edge.src)» --> «identifierForObject(edge.trg)» : "«IF style.bitwiseAnd(ABBR_LABELS) > 0»«abbr(edge.name)»«ELSE»«edge.name»«ENDIF»" «ELSE» - «identifierForObject(edge.src)» "«edge.oppositeName»" <--> "«edge.name»" «identifierForObject(edge.trg)» + «identifierForObject(edge.src)» "«IF style.bitwiseAnd(ABBR_LABELS) > 0»«abbr(edge.oppositeName)»«ELSE»«edge.oppositeName»«ENDIF»" <--> "«IF style.bitwiseAnd(ABBR_LABELS) > 0»«abbr(edge.name)»«ELSE»«edge.name»«ENDIF»" «identifierForObject(edge.trg)» «ENDIF» - «ELSE» «ENDIF» «ENDFOR» ''' @@ -112,31 +117,42 @@ class EMoflonPlantUMLGenerator { '''«IF r.lowerBound == -1»*«ELSE»«r.lowerBound»«ENDIF»..«IF r.upperBound == -1»*«ELSE»«r.upperBound»«ENDIF»''' } + private def static String identifierForClass(EClass c, int style) + '''"«nameFor(c, style)»"''' + private def static String identifierForClass(EClass c) - '''"«c.EPackage.name».«c.name»"''' + '''«nameFor(c.EPackage)».«nameFor(c)»''' - private def static String visualiseEcoreClassAttributes(EClass eclass) { + private def static String visualiseEcoreClassAttributes(EClass eclass, int style) { ''' «FOR a : eclass.EAllAttributes» - «identifierForClass(eclass)» : «a.name» : «a.EType.name» + «identifierForClass(eclass)» : «nameFor(a, style)» : «nameFor(a.EType, style)» «ENDFOR» ''' } - private def static String visualiseEcoreClassOperations(EClass eclass) { + private def static String visualiseEcoreClassOperations(EClass eclass, int style) { ''' «FOR op : eclass.EAllOperations» - «identifierForClass(eclass)» : «visualiseEcoreOperation(op)» + «identifierForClass(eclass)» : «visualiseEcoreOperation(op, style)» «ENDFOR» ''' } - private def static String visualiseEcoreOperation(EOperation op) { - '''«op.name»«visualiseEcoreOperationParameterList(op)»«IF(op.EType !== null)» : «op.EType.name»«ENDIF»''' + private def static String visualiseEcoreOperation(EOperation op, int style) { + '''«nameFor(op, style)»«visualiseEcoreOperationParameterList(op, style)»«IF(op.EType !== null)» : «nameFor(op.EType, style)»«ENDIF»''' + } + + private def static String visualiseEcoreOperationParameterList(EOperation op, int style) { + '''«IF op.EParameters.size == 0»()«ENDIF»«FOR param : op.EParameters BEFORE '(' SEPARATOR ', ' AFTER ')'»«nameFor(param, style)» : «nameFor(param.EType, style)»«ENDFOR»''' } - private def static String visualiseEcoreOperationParameterList(EOperation op) { - '''«IF op.EParameters.size == 0»()«ENDIF»«FOR param : op.EParameters BEFORE '(' SEPARATOR ', ' AFTER ')'»«param.name» : «param.EType.name»«ENDFOR»''' + def static visualiseAllAttributes(EObject o, int style) { + ''' + «FOR a : o.eClass.EAllAttributes» + «nameFor(a, style)» = «IF o.eGet(a) !== null && style.bitwiseAnd(ABBR_LABELS) > 0»«abbr(o.eGet(a).toString)»«ELSE»«o.eGet(a)»«ENDIF» + «ENDFOR» + ''' } def static visualiseAllAttributes(EObject o) { @@ -147,11 +163,18 @@ class EMoflonPlantUMLGenerator { ''' } + private def static Object identifierForObject(EObject o, int style){ + if(!idMap.containsKey(o)) + idMap.put(o, '''o«idMap.keySet.size + 1»''') + + '''"«idMap.get(o)» : «nameFor(o.eClass, style)»"''' + } + private def static Object identifierForObject(EObject o){ if(!idMap.containsKey(o)) idMap.put(o, '''o«idMap.keySet.size + 1»''') - '''«idMap.get(o)».«o.eClass.name»''' + '''«idMap.get(o)».«nameFor(o.eClass)»''' } def static String visualiseCorrModel(Collection corrObjects, Collection sourceObjects, Collection targetObjects, Collection links) @@ -211,4 +234,16 @@ class EMoflonPlantUMLGenerator { hide «IF(style.bitwiseAnd(SHOW_MODEL_DETAILS) > 0)»empty «ENDIF»members ''' } + + def private static String nameFor(ENamedElement elem, int style) { + '''«IF style.bitwiseAnd(ABBR_LABELS) > 0»«abbr(elem.name)»«ELSE»«elem.name»«ENDIF»''' + } + + def private static String nameFor(ENamedElement elem) { + '''«elem.name»''' + } + + def private static String abbr(String longString) { + '''«StringUtils.abbreviateMiddle(longString, REPL_STR, REPL_LEN)»''' + } } From 7eba86216c942670aadd308c2d0c35459cfdd75c Mon Sep 17 00:00:00 2001 From: Johannes Brandt <5931715+eTralse@users.noreply.github.com> Date: Tue, 31 Jul 2018 20:16:28 +0200 Subject: [PATCH 15/37] Adds ViewAction for showing 1-neighbourhood for metmodel and model visualiser. If the action is active, the associated handler sets the a the according bidirectional 1-neighbourhood strategy via the configurator. The metamodel and model visualisers will execute this strategy, and the 1-neighbourhood for the current selection will be added to the diagram. So far, this is only supported with the model and metamodel visualisers. This commit does not include an enableWhen clause for the handler. If one wishes to add an own handler, to enable the support for other visualisers as well, this has to be done, e.g. via a property tester, which calls supportsEditor and supportsSelection on the visualisers. Only one handler can be active for a command at the same time, which is why the included handler cannot be active in this case. --- org.moflon.core.ui/plugin.xml | 24 +++++ .../handler/NeighbourhoodStrategyHandler.java | 97 +++++++++++++++++++ .../visualisation/ConfigurableVisualiser.java | 2 +- .../EMoflonMetamodelVisualiser.java | 2 +- .../visualisation/EMoflonModelVisualiser.java | 2 +- 5 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 org.moflon.core.ui/src/org/moflon/core/ui/handler/NeighbourhoodStrategyHandler.java diff --git a/org.moflon.core.ui/plugin.xml b/org.moflon.core.ui/plugin.xml index 7482b511..ec249f0b 100644 --- a/org.moflon.core.ui/plugin.xml +++ b/org.moflon.core.ui/plugin.xml @@ -125,6 +125,18 @@ + + + + + + + + @@ -174,6 +186,10 @@ class="org.moflon.core.ui.handler.AbbreviateLabelsHandler" commandId="org.moflon.core.ui.commands.visualisation.AbbreviateLabelsCommand"> + + @@ -287,6 +303,14 @@ style="toggle" tooltip="Toggle for abbreviating labels in the PlantUML view, which exceed a certain length."> + + diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/handler/NeighbourhoodStrategyHandler.java b/org.moflon.core.ui/src/org/moflon/core/ui/handler/NeighbourhoodStrategyHandler.java new file mode 100644 index 00000000..958d9e46 --- /dev/null +++ b/org.moflon.core.ui/src/org/moflon/core/ui/handler/NeighbourhoodStrategyHandler.java @@ -0,0 +1,97 @@ +/** + * + */ +package org.moflon.core.ui.handler; + +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; +import org.eclipse.core.commands.Command; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.handlers.HandlerUtil; +import org.moflon.core.ui.AbstractCommandHandler; +import org.moflon.core.ui.visualisation.ClassDiagram; +import org.moflon.core.ui.visualisation.Configurator; +import org.moflon.core.ui.visualisation.ObjectDiagram; +import org.moflon.core.ui.visualisation.Configurator.StrategyPart; +import org.moflon.core.ui.visualisation.strategy.ClassDiagramStrategies; +import org.moflon.core.ui.visualisation.strategy.DiagramStrategy; +import org.moflon.core.ui.visualisation.strategy.ObjectDiagramStrategies; + +/** + * Allows to enable the visualisation of the 1-neighbouhood of a selection for + * metamodel and model visualiser. + * + * @author Johannes Brandt + * + */ +public class NeighbourhoodStrategyHandler extends AbstractCommandHandler { + + private static final Logger logger = LogManager.getLogger(NeighbourhoodStrategyHandler.class); + + private DiagramStrategy classToggleActive; + private DiagramStrategy objectToggleActive; + private DiagramStrategy classToggleInactive; + private DiagramStrategy objectToggleInactive; + + public NeighbourhoodStrategyHandler() { + super(); + + // initialize strategies + classToggleActive = ClassDiagramStrategies::expandNeighbourhoodBidirectional; + classToggleInactive = DiagramStrategy.identity(); + objectToggleActive = ObjectDiagramStrategies::expandNeighbourhoodBidirectional; + objectToggleInactive = DiagramStrategy.identity(); + } + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + logger.debug("Executing 'Show Model Details'."); + + // toggle the state of the command and retrieve the old value of the state + // (before it was clicked) + Command command = event.getCommand(); + boolean toggleState = !HandlerUtil.toggleCommandState(command); + + // retrieve / change / set configuration + setStrategy(toggleState); + + // notify for change (indirectly, by setting focus to the editor, listeners are + // fired, which in turn update the PlantUml view + final IEditorPart linkedEditor = HandlerUtil.getActiveEditor(event); + updateView(linkedEditor); + + return null; + } + + /** + * Instructs the configurator to set the desired strategy, depending on the + * specified toggle state. + * + * @param toggleState + * Iff true, the 1-neighbourhood strategy is chosen. + */ + private void setStrategy(boolean toggleState) { + if (toggleState) { + Configurator.getInstance().setDiagramStrategy(ClassDiagram.class, StrategyPart.NEIGHBOURHOOD, + classToggleActive); + Configurator.getInstance().setDiagramStrategy(ObjectDiagram.class, StrategyPart.NEIGHBOURHOOD, + objectToggleActive); + } else { + Configurator.getInstance().setDiagramStrategy(ClassDiagram.class, StrategyPart.NEIGHBOURHOOD, + classToggleInactive); + Configurator.getInstance().setDiagramStrategy(ObjectDiagram.class, StrategyPart.NEIGHBOURHOOD, + objectToggleInactive); + } + } + + private void updateView(IEditorPart editor) { + if (editor != null) { + editor.setFocus(); + } else { + logger.warn( + "Could not find any appropriate editor instance to initiate an update of the PlantUml viewpart."); + } + } +} diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/ConfigurableVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/ConfigurableVisualiser.java index 15397da6..05092e8a 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/ConfigurableVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/ConfigurableVisualiser.java @@ -71,7 +71,7 @@ public interface ConfigurableVisualiser { * *

* Note: The default implementation of this methods returns - * {@link UnaryOperator#identity()} for every {@link StrategyPart}. + * {@link DiagramStrategy#identity()} for every {@link StrategyPart}. *

* * @param part diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java index cea46389..86011d80 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java @@ -52,7 +52,7 @@ public DiagramStrategy getDefaultStrategy(StrategyPart part) { case INIT: return ClassDiagramStrategies::determineEdgesForSelection; case NEIGHBOURHOOD: - return DiagramStrategy.identity(); + return ClassDiagramStrategies::expandNeighbourhoodBidirectional; default: return super.getDefaultStrategy(part); } diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java index 4aeb480c..7d81015b 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java @@ -43,7 +43,7 @@ public DiagramStrategy getDefaultStrategy(StrategyPart part) { case INIT: return ObjectDiagramStrategies::determineEdgesForSelection; case NEIGHBOURHOOD: - return DiagramStrategy.identity(); + return ObjectDiagramStrategies::expandNeighbourhoodBidirectional; default: return super.getDefaultStrategy(part); } From 9c0b53d0ec0adb16f9591dedfb1d6e4203e0ce53 Mon Sep 17 00:00:00 2001 From: Johannes Brandt <5931715+eTralse@users.noreply.github.com> Date: Wed, 1 Aug 2018 19:56:15 +0200 Subject: [PATCH 16/37] Adds "Show documentation" ViewAction to PlantUML view. The metamodel visualiser was extended to support the extraction of documentation values from EAnnotations. A handler for a ViewAction has been added, to toggle the visualisation of these documentation values in the PlantUML view. To toggle this, the Handler instructs the Configurator to set a specific style bit. In addition, a bug in the VisualiserUtilities has been fixed. Previously, EMap Entries where contained in an expanded Resource / ResourceSet, which couldn't be handled by the metamodel visualiser. This is has been fixed by removing these elements from the result. --- org.moflon.core.ui/plugin.xml | 24 ++++ .../moflon/core/ui/VisualiserUtilities.java | 4 +- .../ui/handler/ShowDocumentationHandler.java | 53 +++++++ .../core/ui/visualisation/ClassDiagram.java | 33 +++++ .../EMoflonMetamodelVisualiser.java | 131 +++++++++++++++--- .../EMoflonPlantUMLGenerator.xtend | 40 +++++- 6 files changed, 263 insertions(+), 22 deletions(-) create mode 100644 org.moflon.core.ui/src/org/moflon/core/ui/handler/ShowDocumentationHandler.java diff --git a/org.moflon.core.ui/plugin.xml b/org.moflon.core.ui/plugin.xml index ec249f0b..770789ee 100644 --- a/org.moflon.core.ui/plugin.xml +++ b/org.moflon.core.ui/plugin.xml @@ -137,6 +137,18 @@ + + + + + + + +
@@ -190,6 +202,10 @@ class="org.moflon.core.ui.handler.NeighbourhoodStrategyHandler" commandId="org.moflon.core.ui.commands.visualisation.ShowNeighbourhoodCommand"> + + @@ -311,6 +327,14 @@ style="toggle" tooltip="Shows or hides the 1-neighborhood for the current selection."> + + diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/VisualiserUtilities.java b/org.moflon.core.ui/src/org/moflon/core/ui/VisualiserUtilities.java index 0bf5f6e2..2e760599 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/VisualiserUtilities.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/VisualiserUtilities.java @@ -3,6 +3,7 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -231,7 +232,8 @@ private static Stream expandResource(Resource resource) { .filter(elem -> elem != null)// .filter(elem -> !(elem instanceof EGenericType))// .filter(elem -> !(elem instanceof EEnumLiteral))// - .filter(elem -> !(elem instanceof EDataType)); + .filter(elem -> !(elem instanceof EDataType))// + .filter(elem -> !(elem instanceof Map.Entry)); } /** diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/handler/ShowDocumentationHandler.java b/org.moflon.core.ui/src/org/moflon/core/ui/handler/ShowDocumentationHandler.java new file mode 100644 index 00000000..255701f2 --- /dev/null +++ b/org.moflon.core.ui/src/org/moflon/core/ui/handler/ShowDocumentationHandler.java @@ -0,0 +1,53 @@ +/** + * + */ +package org.moflon.core.ui.handler; + +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; +import org.eclipse.core.commands.Command; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.handlers.HandlerUtil; +import org.moflon.core.ui.AbstractCommandHandler; +import org.moflon.core.ui.visualisation.Configurator; +import org.moflon.core.ui.visualisation.EMoflonPlantUMLGenerator; + +/** + * @author Johannes Brandt + * + */ +public class ShowDocumentationHandler extends AbstractCommandHandler { + + private static final Logger logger = LogManager.getLogger(ShowModelDetailsHandler.class); + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + logger.debug("Executing 'Show Model Details'."); + + // toggle the state of the command and retrieve the old value of the state + // (before it was clicked) + Command command = event.getCommand(); + boolean toggleState = !HandlerUtil.toggleCommandState(command); + + // retrieve / change / set configuration + Configurator.getInstance().setDiagramStyle(EMoflonPlantUMLGenerator.SHOW_DOCUMENTATION, toggleState); + + // notify for change (indirectly, by setting focus to the editor, listeners are + // fired, which in turn update the PlantUml view + final IEditorPart linkedEditor = HandlerUtil.getActiveEditor(event); + updateEditor(linkedEditor); + + return null; + } + + private void updateEditor(IEditorPart editor) { + if (editor != null) { + editor.setFocus(); + } + else { + logger.warn("Could not find any appropriate editor instance to initiate an update of the PlantUml viewpart."); + } + } +} diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/ClassDiagram.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/ClassDiagram.java index 3d02bbab..5cb678ec 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/ClassDiagram.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/ClassDiagram.java @@ -4,7 +4,11 @@ package org.moflon.core.ui.visualisation; import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import org.eclipse.emf.ecore.EAnnotation; import org.eclipse.emf.ecore.EClass; /** @@ -17,12 +21,41 @@ */ public class ClassDiagram extends Diagram { + /** + * Stores the documentation elements an the EClasses they are to be attached to. + */ + protected Map> docToEClasses; + public ClassDiagram(Collection superset) { super(superset); + + docToEClasses = new HashMap<>(); } public ClassDiagram(Collection superset, Collection selection) { super(superset, selection); + + docToEClasses = new HashMap<>(); + } + + /** + * Getter for {@link #docToEClasses}. + * + * @return A map of documentation elements to their respective EClass they are + * attached to. + */ + public Map> getDoumentation() { + return docToEClasses; + } + + /** + * Setter for {@link #docToEClasses}. + * + * @param docToEClasses + * The new mapping of documentation elements to EClasses. + */ + public void setDocumentation(Map> docToEClasses) { + this.docToEClasses = docToEClasses; } } diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java index 86011d80..bc2c5ebb 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java @@ -1,10 +1,14 @@ package org.moflon.core.ui.visualisation; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.eclipse.emf.ecore.EAnnotation; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EGenericType; import org.eclipse.emf.ecore.EObject; @@ -23,6 +27,8 @@ */ public class EMoflonMetamodelVisualiser extends EMoflonEcoreVisualiser { + private static final String DOCUMENTATION_KEY = "documentation"; + public EMoflonMetamodelVisualiser() { super(); @@ -73,6 +79,13 @@ protected String getDiagramBody(Collection selection) { ClassDiagram diagram = strategy.apply(new ClassDiagram(allClasses, chosenClasses)); diagram.setEdges(handleOpposites(diagram.getEdges())); + // extract and resolve documentation for selected AND neighboured EClasses + HashSet chosenAnnotations = getAllElements().stream()// + .filter(EAnnotation.class::isInstance)// + .map(EAnnotation.class::cast)// + .collect(Collectors.toCollection(HashSet::new)); + diagram.setDocumentation(resolveAnnotations(chosenAnnotations, diagram, selection)); + return EMoflonPlantUMLGenerator.visualiseEcoreElements(diagram, style); } @@ -82,25 +95,7 @@ private Collection resolveSelection(Collection selection) { // retrieve classes, and enclosing classes of operations, attributes... // TODO: resolve EDataType as well? for (EObject eobject : selection) { - EClass eclass = null; - - if (eobject instanceof EClass) { - eclass = (EClass) eobject; - } else if (eobject instanceof EStructuralFeature) { - // EReference and EAttribute - EStructuralFeature efeature = (EStructuralFeature) eobject; - eclass = efeature.getEContainingClass(); - } else if (eobject instanceof EOperation) { - EOperation eoperation = (EOperation) eobject; - eclass = eoperation.getEContainingClass(); - } else if (eobject instanceof EParameter) { - EParameter eparameter = (EParameter) eobject; - eclass = eparameter.getEOperation().getEContainingClass(); - } else if (eobject instanceof EGenericType) { - EGenericType etype = (EGenericType) eobject; - eclass = etype.getEClassifier() instanceof EClass ? (EClass) etype.getEClassifier() : null; - } - + EClass eclass = resolveObject(eobject); if (eclass != null && !result.contains(eclass)) { result.add(eclass); } @@ -119,4 +114,102 @@ private Collection resolveSelection(Collection selection) { return result; } + + /** + * Finds an EClass instance, which represents the specified EObject. + * + * @param eobject + * The object for which an {@link EClass} representation is required. + * @return The representing {@link EClass}. Typically resolved using the + * containment relation. Is null, iff no representation can + * be found. + */ + private EClass resolveObject(EObject eobject) { + EClass eclass = null; + if (eobject instanceof EClass) { + eclass = (EClass) eobject; + } else if (eobject instanceof EStructuralFeature) { + // EReference and EAttribute + EStructuralFeature efeature = (EStructuralFeature) eobject; + eclass = efeature.getEContainingClass(); + } else if (eobject instanceof EOperation) { + EOperation eoperation = (EOperation) eobject; + eclass = eoperation.getEContainingClass(); + } else if (eobject instanceof EParameter) { + EParameter eparameter = (EParameter) eobject; + eclass = eparameter.getEOperation().getEContainingClass(); + } else if (eobject instanceof EGenericType) { + EGenericType etype = (EGenericType) eobject; + eclass = etype.getEClassifier() instanceof EClass ? (EClass) etype.getEClassifier() : null; + } else if (eobject instanceof EAnnotation) { + EAnnotation eannot = (EAnnotation) eobject; + if (eannot.getDetails().containsKey(DOCUMENTATION_KEY)) { + eclass = resolveObject(eannot.getEModelElement()); + } + } + return eclass; + } + + /** + * Maps annotation elements to the EClass they are attached to, respectively the + * EClass representing the EModelElement the annotation element is attached to. + * + * An EAnnotation from the specified {@code allAnnotations} collection is only + * considered to be added to the returned mapping, if it contains the + * "documentation" key and the corresponding String value. + * + * @param allAnnotations + * The annotations that are to be mapped, if their representing + * EClass is contained in either the diagram selection of + * neighbourhood. + * @param diagram + * The diagram containing the selected and neighbourhood EClasses. + * @param originalSelection + * If no representing EClass can be found, then the original + * selection is checked whether or not the annotation even has to be + * mapped. + * @return The mapping of EAnnotations to the EClasses. The EClass may be + * null, if the EAnnotation is attached to an EPackage, + * i.e. for which no EClass representation can be found. This is why + * EAnnotations are not directly mapped to EClasses, but an Optional. + */ + private Map> resolveAnnotations(Collection allAnnotations, + ClassDiagram diagram, Collection originalSelection) { + Map> result = new HashMap<>(allAnnotations.size()); + Collection chosenClasses = diagram.getSelection(); + Collection chosenNeighbourhood = diagram.getNeighbourhood(); + + for (EAnnotation documenter : allAnnotations) { + if (documenter.getDetails().get(DOCUMENTATION_KEY) != null) { + EClass documentee = resolveObject(documenter); + if (chosenClasses.contains(documentee) || chosenNeighbourhood.contains(documentee) + || isContained(originalSelection, documenter)) { + result.put(documenter, Optional.ofNullable(documentee)); + } + } + } + + return result; + } + + /** + * Checks whether or not the specified element is contained in the specified + * search space. + * + * @param searchSpace + * All EObjects, that will be checked for containment. + * @param element + * The element for which the check is performed. + * @return Result is true, iff any of the EObjects in the search + * space, or one of their sub elements (eContents relation) is the + * specified element. + */ + private boolean isContained(Collection searchSpace, EObject element) { + return searchSpace.stream()// + .flatMap(obj -> Stream.concat(Stream.of(obj), obj.eContents().stream()))// + .filter(obj -> obj != null)// + .filter(obj -> obj == element)// + .findFirst()// + .isPresent(); + } } diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend index 2cedef1f..7c79a98b 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend @@ -2,17 +2,22 @@ package org.moflon.core.ui.visualisation import java.util.Collection import java.util.HashMap +import java.util.Map import org.apache.commons.lang3.StringUtils import org.eclipse.emf.ecore.EClass +import org.eclipse.emf.ecore.ENamedElement import org.eclipse.emf.ecore.EObject -import org.eclipse.emf.ecore.EReference import org.eclipse.emf.ecore.EOperation -import org.eclipse.emf.ecore.ENamedElement +import org.eclipse.emf.ecore.EReference +import org.eclipse.emf.ecore.EAnnotation +import org.eclipse.emf.ecore.EPackage +import java.util.Optional class EMoflonPlantUMLGenerator { public static final int SHOW_MODEL_DETAILS = 1<<0; public static final int ABBR_LABELS = 1<<1; + public static final int SHOW_DOCUMENTATION = 1<<2; private static final String REPL_STR = "…"; private static final int REPL_LEN = 11; @@ -66,9 +71,36 @@ class EMoflonPlantUMLGenerator { «visualiseEcoreClassOperations(c, diagramStyle)» «ENDFOR» «visualiseEdges(diagram.getEdges, diagramStyle)» + «IF diagramStyle.bitwiseAnd(SHOW_DOCUMENTATION) > 0» + «visualiseDocumentation(diagram.getDoumentation)» + «ENDIF» ''' } + def private static visualiseDocumentation(Map> map) { + ''' + «FOR a : map.keySet» + «var d = a.details.get("documentation")» + note "«identifierForAnnotation(d)»" as «aliasForDoc(a, d)» + «ENDFOR» + «FOR a : map.keySet» + «var optC = map.get(a)» + «IF optC.isPresent» + «var d = a.details.get("documentation")» + «aliasForDoc(a, d)» .. «identifierForClass(optC.get)» + «ENDIF» + «ENDFOR» + ''' + } + + def private static aliasForDoc(EAnnotation a, String doc) { + '''«IF packageNameFor(a) !== ""»«packageNameFor(a)».«ENDIF»«doc.replaceAll("[\\W]", "_")»«a.hashCode()»''' + } + + def private static identifierForAnnotation(String d) { + '''«d.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "'")»''' + } + def static String visualiseModelElements(ObjectDiagram diagram, int diagramStyle){ idMap.clear @@ -243,6 +275,10 @@ class EMoflonPlantUMLGenerator { '''«elem.name»''' } + def private static String packageNameFor(EObject obj) { + '''«IF obj instanceof EPackage»«nameFor(obj as EPackage)»«ELSE»«IF obj.eContainer !== null»«packageNameFor(obj.eContainer)»«ENDIF»«ENDIF»''' + } + def private static String abbr(String longString) { '''«StringUtils.abbreviateMiddle(longString, REPL_STR, REPL_LEN)»''' } From 80be879e562b6f5ecdb67b223ed1731099f98244 Mon Sep 17 00:00:00 2001 From: Johannes Brandt <5931715+eTralse@users.noreply.github.com> Date: Thu, 2 Aug 2018 01:02:11 +0200 Subject: [PATCH 17/37] Changes colors for metamodel and model and reworks naming mechanism for objects in models. The calculation for object naming is now performed in the model visualiser itself, the result is stored in the ObjectDiagram. This is handed to the generator. The names are named like this instead of . Object instance names are still not ordered according to their containment relation, this is future work. Instead, the numbers trailing the Object instance names are unique, but randomly assigned to the object. The colors for both metamodel and model have been adjusted to be black and white, with the exception of EPackage representations in metamodels. They are colored in a light gray. --- .../EMoflonMetamodelVisualiser.java | 3 - .../visualisation/EMoflonModelVisualiser.java | 57 ++++++++++++++++++- .../EMoflonPlantUMLGenerator.xtend | 42 +++++++++++--- .../core/ui/visualisation/ObjectDiagram.java | 29 ++++++++++ 4 files changed, 117 insertions(+), 14 deletions(-) diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java index bc2c5ebb..4292e5a5 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonMetamodelVisualiser.java @@ -35,9 +35,6 @@ public EMoflonMetamodelVisualiser() { // set default strategy strategy = getDefaultStrategy(StrategyPart.INIT)// .andThen(getDefaultStrategy(StrategyPart.NEIGHBOURHOOD)); - - @SuppressWarnings("unused") - boolean blabla = true; } @Override diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java index 7d81015b..ec7da063 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java @@ -1,6 +1,14 @@ package org.moflon.core.ui.visualisation; import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.Optional; +import java.util.Queue; + +import org.eclipse.emf.common.util.TreeIterator; +import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.moflon.core.ui.VisualiserUtilities; import org.moflon.core.ui.visualisation.Configurator.StrategyPart; @@ -19,9 +27,6 @@ public EMoflonModelVisualiser() { // set default strategy strategy = getDefaultStrategy(StrategyPart.INIT)// .andThen(getDefaultStrategy(StrategyPart.NEIGHBOURHOOD)); - - @SuppressWarnings("unused") - boolean blabla = true; } @Override @@ -57,6 +62,52 @@ protected String getDiagramBody(Collection selection) { ObjectDiagram diagram = strategy.apply(new ObjectDiagram(allObjects, selection)); diagram.setEdges(handleOpposites(diagram.getEdges())); + diagram = determineObjectNames(diagram); return EMoflonPlantUMLGenerator.visualiseModelElements(diagram, style); } + + /** + * Determines instance names for all EObjects in selection and neighbourhood + * collection in the specified diagram. + * + * @param diagram + * The diagram, for which the EObject instance names shall be + * determined. + * @return The diagram with the EObject instance names. + */ + private ObjectDiagram determineObjectNames(ObjectDiagram diagram) { + int noEClassCount = 1; + Map instanceNames = diagram.getInstanceNames(); + Map instanceCounts = new HashMap<>(); + + determineObjectNames(diagram.getSelection(), instanceNames, instanceCounts, noEClassCount); + determineObjectNames(diagram.getNeighbourhood(), instanceNames, instanceCounts, noEClassCount); + + return diagram; + } + + private void determineObjectNames(Collection elements, Map instanceNames, + Map instanceCounts, int noEClassCount) { + for(EObject current : elements) { + // use EClass name with lower case first letter, if no EClass: "o" + String name = (current.eClass() != null) ? current.eClass().getName() : "o"; + name = (name == null || name.length() == 0) ? "o" : name; + name = name.substring(0, 1).toLowerCase() + name.substring(1); + + // no EClass -> use global object counter + if (current.eClass() == null) { + instanceNames.put(current, name + noEClassCount); + noEClassCount++; + continue; + } + + // determine and update instance count + int instanceCount = 1; + if (instanceCounts.containsKey(current.eClass())) { + instanceCount = instanceCounts.get(current.eClass()) + 1; + } + instanceNames.put(current, name + instanceCount); + instanceCounts.put(current.eClass(), instanceCount); + } + } } diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend index 7c79a98b..381f69ab 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonPlantUMLGenerator.xtend @@ -23,6 +23,7 @@ class EMoflonPlantUMLGenerator { private static final int REPL_LEN = 11; static var idMap = new HashMap(); + private static Map instanceNames; static def String wrapInTags(String body){ ''' @@ -103,6 +104,7 @@ class EMoflonPlantUMLGenerator { def static String visualiseModelElements(ObjectDiagram diagram, int diagramStyle){ idMap.clear + instanceNames = diagram.eObjectsToNames; ''' «plantUMLPreamble(diagramStyle)» @@ -196,17 +198,11 @@ class EMoflonPlantUMLGenerator { } private def static Object identifierForObject(EObject o, int style){ - if(!idMap.containsKey(o)) - idMap.put(o, '''o«idMap.keySet.size + 1»''') - - '''"«idMap.get(o)» : «nameFor(o.eClass, style)»"''' + '''"«IF style.bitwiseAnd(ABBR_LABELS) > 0»«abbr(instanceNames.get(o))»«ELSE»«instanceNames.get(o)»«ENDIF» : «nameFor(o.eClass, style)»"''' } private def static Object identifierForObject(EObject o){ - if(!idMap.containsKey(o)) - idMap.put(o, '''o«idMap.keySet.size + 1»''') - - '''«idMap.get(o)».«nameFor(o.eClass)»''' + '''«instanceNames.get(o)».«nameFor(o.eClass)»''' } def static String visualiseCorrModel(Collection corrObjects, Collection sourceObjects, Collection targetObjects, Collection links) @@ -264,6 +260,36 @@ class EMoflonPlantUMLGenerator { def static CharSequence plantUMLPreamble(int style){ ''' hide «IF(style.bitwiseAnd(SHOW_MODEL_DETAILS) > 0)»empty «ENDIF»members + + skinparam shadowing false + skinparam StereotypeABackgroundColor White + skinparam StereotypeCBackgroundColor White + + skinparam class { + BorderColor Black + BackgroundColor White + ArrowColor Black + StereotypeABackgroundColor White + StereotypeCBackgroundColor White + } + + skinparam package { + BackgroundColor GhostWhite + BorderColor LightSlateGray + Fontcolor LightSlateGray + } + + skinparam object { + BorderColor Black + BackgroundColor White + ArrowColor Black + } + + skinparam note { + BorderColor Black + BackgroundColor White + ArrowColor Black + } ''' } diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/ObjectDiagram.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/ObjectDiagram.java index a2a7fa62..fa6ecee0 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/ObjectDiagram.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/ObjectDiagram.java @@ -4,6 +4,8 @@ package org.moflon.core.ui.visualisation; import java.util.Collection; +import java.util.HashMap; +import java.util.Map; import org.eclipse.emf.ecore.EObject; @@ -17,12 +19,39 @@ */ public class ObjectDiagram extends Diagram { + /** + * Provides an instance name for an EObject. + */ + protected Map eObjectsToNames; + public ObjectDiagram(Collection superset) { super(superset); + + eObjectsToNames = new HashMap<>(); } public ObjectDiagram(Collection superset, Collection selection) { super(superset, selection); + + eObjectsToNames = new HashMap<>(); } + /** + * Getter for {@link #eObjectsToNames}. + * + * @return The mapping of EObjects to their respective instance name. + */ + public Map getInstanceNames() { + return eObjectsToNames; + } + + /** + * Setter for {@link #eObjectsToNames}. + * + * @param eObjectsToNames + * The new mapping of EObjects to their respective instance name. + */ + public void setInstanceNames(Map eObjectsToNames) { + this.eObjectsToNames = eObjectsToNames; + } } From c112cba163f6d7b390a357c37419c1a1c4885242 Mon Sep 17 00:00:00 2001 From: Johannes Brandt <5931715+eTralse@users.noreply.github.com> Date: Thu, 2 Aug 2018 01:14:39 +0200 Subject: [PATCH 18/37] Removes warnings in model visualiser. --- .../moflon/core/ui/visualisation/EMoflonModelVisualiser.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java index ec7da063..ec1e5b1d 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/EMoflonModelVisualiser.java @@ -2,12 +2,7 @@ import java.util.Collection; import java.util.HashMap; -import java.util.LinkedList; import java.util.Map; -import java.util.Optional; -import java.util.Queue; - -import org.eclipse.emf.common.util.TreeIterator; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.moflon.core.ui.VisualiserUtilities; From 8e0a2718d795ca5caa77aaa55a252d98fa8cb015 Mon Sep 17 00:00:00 2001 From: Johannes Brandt <5931715+eTralse@users.noreply.github.com> Date: Thu, 2 Aug 2018 17:42:32 +0200 Subject: [PATCH 19/37] Fixes nondeterministic behavior for metamodel and model visualiser if both .xmi and .ecore Resources are selected. The problem: If both .xmi and .ecore Resources were selected in a supported editor, either the EcoreVisualiser correctly reported an error diagram or the plantuml.ecore plugin visualised one of the resources. The cause: Both PlantUML ecore visualisers and both the emMoflon metamodel and model visualiser had the same "default" priority. Dependending on the order, in which eMoflon and PlantUML ecore visualiser were loaded by the PlantUML.eclipse plugin, either one of those was asked first to visualise. The order in which the PlantUML.eclipse Activator loads the visualisers appears to be undeterministic. Furthermore, the supportsSelection check of the PlatnUML.ecore visualiser checks only the first element of a selection. Which again is influenced by which Resource is selected first in the Editor. The process of choosing a visualiser for getting the diagram text is devided into two phases: first supportsEditor and supportsSelection is called. If supportsEditor returns true, but not supportsSelection, the second phase starts. There again supportsEditor and supportsSelection are called, the latter with a null argument for the selection. Both PlantUML.ecore and eModflon visualisers seem to return the same result in this case, i.e. true, such that the first visualiser in line is chosen for diagram text generation. The fix: The priority value of the eMoflon metamodel and model visualisers has been increased to "custom" (=10), in contrast to "default" (=5). This way the eMoflon metamodel and model visualisers always chosen first for diagram text generation. Thus, the behavior is deterministic. --- org.moflon.core.ui/plugin.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/org.moflon.core.ui/plugin.xml b/org.moflon.core.ui/plugin.xml index 770789ee..dea43fb5 100644 --- a/org.moflon.core.ui/plugin.xml +++ b/org.moflon.core.ui/plugin.xml @@ -349,9 +349,9 @@ - + - + From 61e305e94d9a6e47b17f7de585137d854f977122 Mon Sep 17 00:00:00 2001 From: Roland Kluge Date: Thu, 9 Aug 2018 07:26:51 +0200 Subject: [PATCH 20/37] Update URL of Eclipse tutorial Closes eMoflon/emoflon-core/issues/92 --- org.moflon.core.releng.updatesite/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.moflon.core.releng.updatesite/index.html b/org.moflon.core.releng.updatesite/index.html index 5d50dfe6..9da3e66c 100644 --- a/org.moflon.core.releng.updatesite/index.html +++ b/org.moflon.core.releng.updatesite/index.html @@ -13,6 +13,6 @@ This is an Eclipse UpdateSite and is not meant to be viewed in a normal browser.
To install eMoflon, enter this UpdateSite in the Eclipse Update Manager.
- If you do not know what that is then work through this tutorial. + For further help, please consult the Eclipe tutorial on installing new software. \ No newline at end of file From 5d4c4af03c524ca0e578d38fa00717212e5c4568 Mon Sep 17 00:00:00 2001 From: Roland Kluge Date: Thu, 9 Aug 2018 07:30:56 +0200 Subject: [PATCH 21/37] Fix typos --- org.moflon.core.releng.updatesite/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/org.moflon.core.releng.updatesite/index.html b/org.moflon.core.releng.updatesite/index.html index 9da3e66c..a857cfe1 100644 --- a/org.moflon.core.releng.updatesite/index.html +++ b/org.moflon.core.releng.updatesite/index.html @@ -11,8 +11,8 @@ - This is an Eclipse UpdateSite and is not meant to be viewed in a normal browser.
- To install eMoflon, enter this UpdateSite in the Eclipse Update Manager.
+ This is an Eclipse update site and is not meant to be viewed in a normal browser.
+ To install eMoflon, enter the URL of this update site in the Eclipse Update Manager.
For further help, please consult the Eclipe tutorial on installing new software. \ No newline at end of file From ecf4d12401a985a15869b1b7d6d66f02d15020ec Mon Sep 17 00:00:00 2001 From: Roland Kluge Date: Thu, 9 Aug 2018 08:05:49 +0200 Subject: [PATCH 22/37] Avoid implementations of BiMap and BiHashMap #84 Removing unused classes from package org.moflon.core.xtext.utils * BiMap * HashBiMap * XtextUtil Formatting and cleanup Mark singleton getters as deprecated Closes https://github.com/eMoflon/emoflon-core/issues/84 --- org.moflon.core.xtext/META-INF/MANIFEST.MF | 8 +- .../xtext/scoping/ScopeProviderHelper.java | 2 +- .../xtext/scoping/utils/MOSLScopeUtil.java | 27 ++-- .../scoping/utils/ScopeProviderHelper.java | 29 ++-- .../org/moflon/core/xtext/utils/BiMap.java | 14 -- .../moflon/core/xtext/utils/HashBiMap.java | 113 --------------- .../moflon/core/xtext/utils/ResourceUtil.java | 116 +++++++++------ .../core/xtext/utils/WorkspaceHelper.java | 137 ------------------ .../moflon/core/xtext/utils/XtextUtil.java | 65 --------- 9 files changed, 105 insertions(+), 406 deletions(-) delete mode 100644 org.moflon.core.xtext/src/org/moflon/core/xtext/utils/BiMap.java delete mode 100644 org.moflon.core.xtext/src/org/moflon/core/xtext/utils/HashBiMap.java delete mode 100644 org.moflon.core.xtext/src/org/moflon/core/xtext/utils/WorkspaceHelper.java delete mode 100644 org.moflon.core.xtext/src/org/moflon/core/xtext/utils/XtextUtil.java diff --git a/org.moflon.core.xtext/META-INF/MANIFEST.MF b/org.moflon.core.xtext/META-INF/MANIFEST.MF index 4a613e0d..ae0b04ec 100644 --- a/org.moflon.core.xtext/META-INF/MANIFEST.MF +++ b/org.moflon.core.xtext/META-INF/MANIFEST.MF @@ -2,11 +2,11 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: org.moflon.core.xtext Bundle-SymbolicName: org.moflon.core.xtext;singleton:=true -Bundle-Version: 1.0.0.qualifier +Bundle-Version: 2.0.0.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Require-Bundle: org.eclipse.xtext.ui +Require-Bundle: org.eclipse.xtext.ui, + org.moflon.core.utilities;bundle-version="3.0.0" Export-Package: org.moflon.core.xtext.exceptions, org.moflon.core.xtext.scoping, - org.moflon.core.xtext.scoping.utils, - org.moflon.core.xtext.utils + org.moflon.core.xtext.scoping.utils Bundle-Vendor: eMoflon Developers diff --git a/org.moflon.core.xtext/src/org/moflon/core/xtext/scoping/ScopeProviderHelper.java b/org.moflon.core.xtext/src/org/moflon/core/xtext/scoping/ScopeProviderHelper.java index a5445dcd..016644e0 100644 --- a/org.moflon.core.xtext/src/org/moflon/core/xtext/scoping/ScopeProviderHelper.java +++ b/org.moflon.core.xtext/src/org/moflon/core/xtext/scoping/ScopeProviderHelper.java @@ -29,7 +29,7 @@ public ScopeProviderHelper(ResourceSet resSet) { public ScopeProviderHelper() { init(); - resourceSet = MOSLScopeUtil.getInstance().getResourceSet("ecore"); + resourceSet = MOSLScopeUtil.getResourceSet("ecore"); } private void init(){ diff --git a/org.moflon.core.xtext/src/org/moflon/core/xtext/scoping/utils/MOSLScopeUtil.java b/org.moflon.core.xtext/src/org/moflon/core/xtext/scoping/utils/MOSLScopeUtil.java index d690dacf..cca3e36a 100644 --- a/org.moflon.core.xtext/src/org/moflon/core/xtext/scoping/utils/MOSLScopeUtil.java +++ b/org.moflon.core.xtext/src/org/moflon/core/xtext/scoping/utils/MOSLScopeUtil.java @@ -17,12 +17,13 @@ public class MOSLScopeUtil { + @Deprecated // Since 2018-08-09 private static MOSLScopeUtil instance; - private MOSLScopeUtil() - { - } - + /** + * @deprecated All methods are now static, so we do not need the synthetic singleton + */ + @Deprecated // Since 2018-08-09 public static MOSLScopeUtil getInstance() { if (instance == null) @@ -30,7 +31,7 @@ public static MOSLScopeUtil getInstance() return instance; } - public R getRootObject(EObject context, Class clazz) + public static R getRootObject(EObject context, Class clazz) { Stack stack = new Stack(); stack.push(context); @@ -47,25 +48,25 @@ public R getRootObject(EObject context, Class clazz) return null; } - public List getObjectsFromResource(Resource resource, Class clazz){ + public static List getObjectsFromResource(Resource resource, Class clazz){ List allContent = new ArrayList<>(); resource.getAllContents().forEachRemaining(allContent::add); return allContent.parallelStream().filter(clazz::isInstance).map(clazz::cast).collect(Collectors.toList()); } - - public E getObjectFromResourceSet(URI uri, ResourceSet resourceSet, Class clazz) + + public static E getObjectFromResourceSet(URI uri, ResourceSet resourceSet, Class clazz) { Resource res = getResource(uri, resourceSet, true); E scopingRoot = clazz.cast(res.getContents().get(0)); return scopingRoot; } - public ResourceSet getResourceSet() + public static ResourceSet getResourceSet() { return getResourceSet("xmi"); } - private Resource getResource(URI uri, ResourceSet resourceSet, boolean load) + private static Resource getResource(URI uri, ResourceSet resourceSet, boolean load) { try { @@ -83,7 +84,7 @@ private Resource getResource(URI uri, ResourceSet resourceSet, boolean load) } } - public Resource addToResource(URI uri, ResourceSet resourceSet, EObject obj) + public static Resource addToResource(URI uri, ResourceSet resourceSet, EObject obj) { Resource resource = getResource(uri, resourceSet, false); resource.getContents().clear(); @@ -91,7 +92,7 @@ public Resource addToResource(URI uri, ResourceSet resourceSet, EObject obj) return resource; } - public void saveToResource(URI uri, ResourceSet resourceSet, EObject obj) + public static void saveToResource(URI uri, ResourceSet resourceSet, EObject obj) { try { @@ -103,7 +104,7 @@ public void saveToResource(URI uri, ResourceSet resourceSet, EObject obj) } } - public ResourceSet getResourceSet(String ext) + public static ResourceSet getResourceSet(String ext) { ResourceSet resourceSet = new ResourceSetImpl(); Resource.Factory.Registry reg = Resource.Factory.Registry.INSTANCE; diff --git a/org.moflon.core.xtext/src/org/moflon/core/xtext/scoping/utils/ScopeProviderHelper.java b/org.moflon.core.xtext/src/org/moflon/core/xtext/scoping/utils/ScopeProviderHelper.java index cf2bc856..93c84860 100644 --- a/org.moflon.core.xtext/src/org/moflon/core/xtext/scoping/utils/ScopeProviderHelper.java +++ b/org.moflon.core.xtext/src/org/moflon/core/xtext/scoping/utils/ScopeProviderHelper.java @@ -17,33 +17,30 @@ import org.moflon.core.xtext.exceptions.CannotFindScopeException; import org.moflon.core.xtext.utils.ResourceUtil; - - - public class ScopeProviderHelper { private Map existingScopingRoots; private Map> oldCandidates; private ResourceSet resourceSet; - + public ScopeProviderHelper(ResourceSet resSet) { init(); resourceSet = resSet; } - + public ScopeProviderHelper() { init(); - resourceSet = ResourceUtil.getInstance().getResourceSet("ecore"); + resourceSet = ResourceUtil.getResourceSet("ecore"); } - + private void init(){ existingScopingRoots = new HashMap<>(); oldCandidates = new HashMap<>(); } - + public ResourceSet getResourceSet(){ return resourceSet; } - + private E getScopingObject(URI uri, Class clazz) throws IOException{ if(existingScopingRoots.containsKey(uri)){ return existingScopingRoots.get(uri); @@ -57,21 +54,21 @@ private E getScopingObject(URI uri, Class clazz) throws IOException{ existingScopingRoots.put(uri, scopingRoot); return scopingRoot; } - } - + } + public IScope createScope(List uris, Class clazz, Class type) throws CannotFindScopeException{ return createScope(uris, clazz, type, null); - } - + } + public IScope createScope(List uris, Class clazz, Class type, List currentFound) throws CannotFindScopeException{ try { List candidates=null; if(oldCandidates.containsKey(type.toGenericString())){ candidates=oldCandidates.get(type.toGenericString()); } - else { + else { candidates = new ArrayList<>(); - + for(URI uri : uris){ E scopingObject=getScopingObject(uri, clazz); Iterator candidateIterator = scopingObject.eAllContents(); @@ -90,5 +87,5 @@ public IScope createScope(List uris, Class clazz, Cl }catch (Exception ioobe){ throw new CannotFindScopeException("Cannot find Resource"); } - } + } } diff --git a/org.moflon.core.xtext/src/org/moflon/core/xtext/utils/BiMap.java b/org.moflon.core.xtext/src/org/moflon/core/xtext/utils/BiMap.java deleted file mode 100644 index fed4fe52..00000000 --- a/org.moflon.core.xtext/src/org/moflon/core/xtext/utils/BiMap.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.moflon.core.xtext.utils; - -import java.util.Map; - -public interface BiMap extends Map{ - V getValue(K key); - K getKey(V value); - - V getValueOrDefault(K key, V defaultValue); - K getKeyOrDefault(V value, K defaultKey); - - V removeKey(K key); - K removeValue(V value); -} diff --git a/org.moflon.core.xtext/src/org/moflon/core/xtext/utils/HashBiMap.java b/org.moflon.core.xtext/src/org/moflon/core/xtext/utils/HashBiMap.java deleted file mode 100644 index 90db3c50..00000000 --- a/org.moflon.core.xtext/src/org/moflon/core/xtext/utils/HashBiMap.java +++ /dev/null @@ -1,113 +0,0 @@ -package org.moflon.core.xtext.utils; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -public class HashBiMap implements BiMap{ - - private Map map = new HashMap(); - private Map inversedMap = new HashMap(); - - public V put(K k, V v) { - map.put(k, v); - inversedMap.put(v, k); - return v; - } - - @Override - public V get(Object k) { - return map.get(k); - } - - public K getKey(V v) { - return inversedMap.get(v); - } - - @Override - public int size() { - return map.size(); - } - - @Override - public boolean isEmpty() { - return map.isEmpty(); - } - - @Override - public boolean containsKey(Object key) { - return map.containsKey(key); - } - - @Override - public boolean containsValue(Object value) { - return inversedMap.containsKey(value); - } - - @Override - public V remove(Object key) { - V value = map.remove(key); - inversedMap.remove(value); - return value; - } - - @Override - public void putAll(Map otherMap) { - map.putAll(otherMap); - otherMap.entrySet().parallelStream().forEach(entry -> inversedMap.put(entry.getValue(),entry.getKey())); - } - - @Override - public void clear() { - map.clear(); - inversedMap.clear(); - } - - @Override - public Set keySet() { - return map.keySet(); - } - - @Override - public Collection values() { - return inversedMap.keySet(); - } - - @Override - public Set> entrySet() { - return map.entrySet(); - } - - @Override - public V getValue(K key) { - return map.get(key); - } - - @Override - public V removeKey(K key) { - return this.remove(key); - } - - @Override - public K removeValue(V value) { - K key = inversedMap.remove(value); - map.remove(key); - return key; - } - - @Override - public V getValueOrDefault(K key, V defaultValue) { - return map.getOrDefault(key, defaultValue); - } - - @Override - public K getKeyOrDefault(V value, K defaultKey) { - return inversedMap.getOrDefault(value, defaultKey); - } - - public String toString() { - return map.toString(); - } - -} diff --git a/org.moflon.core.xtext/src/org/moflon/core/xtext/utils/ResourceUtil.java b/org.moflon.core.xtext/src/org/moflon/core/xtext/utils/ResourceUtil.java index 53a41c2a..f37e910f 100644 --- a/org.moflon.core.xtext/src/org/moflon/core/xtext/utils/ResourceUtil.java +++ b/org.moflon.core.xtext/src/org/moflon/core/xtext/utils/ResourceUtil.java @@ -7,7 +7,11 @@ import java.util.Map; import java.util.Stack; +import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; @@ -16,18 +20,26 @@ import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl; public class ResourceUtil { + + @Deprecated // Since 2018-08-09 private static ResourceUtil instance; + @Deprecated // Since 2018-08-09 private ResourceUtil() { } + /** + * @deprecated All methods are now static, so we do not need the synthetic + * singleton + */ + @Deprecated // Since 2018-08-09 public static ResourceUtil getInstance() { if (instance == null) instance = new ResourceUtil(); return instance; } - public R getRootObject(EObject context, Class clazz) { + public static R getRootObject(EObject context, Class clazz) { Stack stack = new Stack(); stack.push(context); while (!stack.isEmpty()) { @@ -41,17 +53,17 @@ public R getRootObject(EObject context, Class clazz) { return null; } - public E getObjectFromResourceSet(URI uri, ResourceSet resourceSet, Class clazz) { + public static E getObjectFromResourceSet(URI uri, ResourceSet resourceSet, Class clazz) { Resource res = getResource(uri, resourceSet, true); E scopingRoot = clazz.cast(res.getContents().get(0)); return scopingRoot; } - public ResourceSet getResourceSet() { + public static ResourceSet getResourceSet() { return getResourceSet("xmi"); } - public Resource getResource(URI uri, ResourceSet resourceSet, boolean load) { + public static Resource getResource(URI uri, ResourceSet resourceSet, boolean load) { try { Resource res = resourceSet.getResource(uri, false); if (res == null) { @@ -65,63 +77,81 @@ public Resource getResource(URI uri, ResourceSet resourceSet, boolean load) { } } - public Resource addToResource(URI uri, ResourceSet resourceSet, EObject obj) { + public static Resource addToResource(URI uri, ResourceSet resourceSet, EObject obj) { Resource resource = getResource(uri, resourceSet, false); resource.getContents().clear(); resource.getContents().add(obj); return resource; } - public void saveToResource(URI uri, ResourceSet resourceSet, EObject obj) { + public static void saveToResource(URI uri, ResourceSet resourceSet, EObject obj) { saveToResource(uri, resourceSet, obj, true); } - public void saveToResource(URI uri, ResourceSet resourceSet, EObject obj, boolean deleteOld) - { - try - { - Resource resource = addToResource(uri, resourceSet, obj); - if(deleteOld) { - IResource iResource = WorkspaceHelper.INSTANCE.getIResource(uri); - if(iResource.exists()) { - iResource.delete(true, null); - } -// File file = new File(filePath); -// if(file.exists()) { -// file.delete(); -// } - } - - resource.save(Collections.EMPTY_MAP); - } catch (Exception e) - { - throw new RuntimeException(e); - } - } - - public String getProjectNameFromURI(URI uri) { + public static void saveToResource(URI uri, ResourceSet resourceSet, EObject obj, boolean deleteOld) { + try { + Resource resource = addToResource(uri, resourceSet, obj); + if (deleteOld) { + IResource iResource = getIResource(uri); + if (iResource.exists()) { + iResource.delete(true, null); + } + } + + resource.save(Collections.EMPTY_MAP); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static IResource getIResource(URI uri) throws CoreException { + IProject project = getProjectByURI(uri); + project.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor()); + IPath path = getPathByURI(project, uri); + if (path.toFile().isDirectory()) + return project.getFolder(path); + else + return project.getFile(path); + } + + public static IProject getProjectByURI(URI uri) { + String projectName = getProjectNameFromURI(uri); + return org.moflon.core.utilities.WorkspaceHelper.getProjectByName(projectName); + } + + public static IPath getPathByURI(IResource iResource, URI uri) { + String[] segments = uri.segments(); + IPath path = iResource.getLocation(); + for (int index = 2; index < segments.length; ++index) { + String segment = segments[index]; + path = path.append(segment); + } + return path; + } + + public static String getProjectNameFromURI(URI uri) { return uri.segment(1); } - - public String getProjectNameFromResource(Resource resource) { + + public static String getProjectNameFromResource(Resource resource) { return getProjectNameFromURI(resource.getURI()); } - - public URI createURIFromResource(Resource resource, String folder, String file) { + + public static URI createURIFromResource(Resource resource, String folder, String file) { URI originUri = resource.getURI(); - return createURIFromOtherURI(originUri, getProjectNameFromURI(originUri), folder, file); + return createURIFromOtherURI(originUri, getProjectNameFromURI(originUri), folder, file); } - - public URI createPluginURI(String projectName, String path) { + + public static URI createPluginURI(String projectName, String path) { return URI.createPlatformPluginURI(projectName + "/" + path, false); } - - public URI createResourceURI(String projectName, String path) { + + public static URI createResourceURI(String projectName, String path) { return URI.createPlatformResourceURI(projectName + "/" + path, false); } - - public URI createURIFromOtherURI(URI originUri, String projectName, String folder, String file) { - List segments = Arrays.asList(originUri.toString().split("/")); + + public static URI createURIFromOtherURI(URI originUri, String projectName, String folder, String file) { + List segments = Arrays.asList(originUri.toString().split("/")); if (segments.size() >= 3) { String prefix = segments.get(0) + "/" + segments.get(1) + "/"; String path = prefix + projectName + "/" + folder + "/" + file; @@ -130,7 +160,7 @@ public URI createURIFromOtherURI(URI originUri, String projectName, String folde return originUri; } - public ResourceSet getResourceSet(String ext) { + public static ResourceSet getResourceSet(String ext) { ResourceSet resourceSet = new ResourceSetImpl(); Resource.Factory.Registry reg = Resource.Factory.Registry.INSTANCE; Map m = reg.getExtensionToFactoryMap(); diff --git a/org.moflon.core.xtext/src/org/moflon/core/xtext/utils/WorkspaceHelper.java b/org.moflon.core.xtext/src/org/moflon/core/xtext/utils/WorkspaceHelper.java deleted file mode 100644 index 1b4f9c32..00000000 --- a/org.moflon.core.xtext/src/org/moflon/core/xtext/utils/WorkspaceHelper.java +++ /dev/null @@ -1,137 +0,0 @@ -package org.moflon.core.xtext.utils; - -import java.util.Arrays; -import java.util.List; -import java.util.Optional; - -import org.eclipse.core.resources.IFolder; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IProjectDescription; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.resources.IWorkspace; -import org.eclipse.core.resources.IWorkspaceRoot; -import org.eclipse.core.resources.ResourcesPlugin; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IPath; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.NullProgressMonitor; -import org.eclipse.emf.common.util.URI; -import org.eclipse.emf.ecore.resource.Resource; - -public class WorkspaceHelper { - - public final static WorkspaceHelper INSTANCE = new WorkspaceHelper(); - - private final String BIN = "bin"; - private final String SRC = "src"; - private final String SRC_GEN = "src-gen"; - private WorkspaceHelper() { - - } - - /** - * Returns the list of all projects in the workspace - */ - public List getAllProjectsInWorkspace() { - return Arrays.asList(ResourcesPlugin.getWorkspace().getRoot().getProjects()); - } - - public boolean projectExist(String projectName) { - return getProjectMonad(projectName).isPresent(); - } - - public IProject createEmptyProject(String projectName) throws CoreException { - IProgressMonitor progressMonitor = new NullProgressMonitor(); - IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); - IProject project = root.getProject(projectName); - project.create(progressMonitor); - project.open(progressMonitor); - - return project; - } - - public IProject getProjectByName(String projectName) { - Optional monad = getProjectMonad(projectName); - return monad.isPresent()? monad.get() : null; - } - - public IResource getIResource(URI uri) throws CoreException { - IProject project = getProjectByURI(uri); - project.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor()); - IPath path = getPathByURI(project, uri); - if(path.toFile().isDirectory()) - return project.getFolder(path); - else - return project.getFile(path); - } - - public IPath getPathByURI(IResource iResource, URI uri) { - String[] segments = uri.segments(); - IPath path = iResource.getLocation(); - for(int index = 2; index < segments.length; ++index) { - String segment = segments[index]; - path = path.append(segment); - } - return path; - } - - public IProject getProjectByURI(URI uri) { - String projectName = ResourceUtil.getInstance().getProjectNameFromURI(uri); - return getProjectByName(projectName); - } - - public IProject getProjectByResource(Resource resource) { - return getProjectByURI(resource.getURI()); - } - - private Optional getProjectMonad(String projectName){ - return getAllProjectsInWorkspace().parallelStream().filter(project -> projectName.equalsIgnoreCase(project.getName())).findFirst(); - } - - public void addNature(IProject project, String natureID) throws CoreException { - IProjectDescription description = project.getDescription(); - - String[] natures = description.getNatureIds(); - String[] newNatures = new String[natures.length + 1]; - System.arraycopy(natures, 0, newNatures, 0, natures.length); - newNatures[natures.length] = natureID; - - // validate the natures - IWorkspace workspace = ResourcesPlugin.getWorkspace(); - IStatus status = workspace.validateNatureSet(newNatures); - - // only apply new nature, if the status is ok - if (status.getCode() == IStatus.OK) { - description.setNatureIds(newNatures); - project.setDescription(description, null); - } - } - - public IFolder getSrcFolder(IProject project) { - return project.getFolder(SRC); - } - - public IFolder getBinFolder(IProject project) { - return project.getFolder(BIN); - } - - public IFolder getSrcGenFolder(IProject project) { - return project.getFolder(SRC_GEN); - } - - public IFolder getSubFolderFromQualifiedName(IFolder folder, String qualifiedName) { - List parts = Arrays.asList(qualifiedName.split("\\.")); - IFolder current = folder; - IFolder parent; - for(String part : parts) { - parent = current; - current = parent.getFolder(part); - if(!current.exists()) { - return parent; - } - } - return current; - } - -} diff --git a/org.moflon.core.xtext/src/org/moflon/core/xtext/utils/XtextUtil.java b/org.moflon.core.xtext/src/org/moflon/core/xtext/utils/XtextUtil.java deleted file mode 100644 index 61d60171..00000000 --- a/org.moflon.core.xtext/src/org/moflon/core/xtext/utils/XtextUtil.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.moflon.core.xtext.utils; - -import java.util.Collection; -import java.util.List; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -import org.eclipse.emf.ecore.EDataType; -import org.eclipse.emf.ecore.EPackage; -import org.eclipse.emf.ecore.EcoreFactory; -import org.eclipse.emf.ecore.util.EcoreUtil; - -public class XtextUtil -{ - private static XtextUtil instance; - - private final EPackage ecoreEPackage; - - private final List ecoreEDataTypes; - - private XtextUtil(){ - ecoreEPackage = EcoreFactory.eINSTANCE.getEcorePackage(); - EcoreUtil.resolveAll(ecoreEPackage); - ecoreEDataTypes = mapToSubtype(ecoreEPackage.getEClassifiers(), EDataType.class); - } - - public static XtextUtil getInstance(){ - if(instance == null) - instance = new XtextUtil(); - return instance; - } - - public List mapToSupertypeUnsorted(Collection subTypes, Class clazz) - { - return subTypes.parallelStream().map(clazz::cast).collect(Collectors.toList()); - } - - public List mapToSubtypeUnsorted(Collection list, Class clazz) - { - return list.parallelStream().filter(clazz::isInstance).map(clazz::cast).collect(Collectors.toList()); - } - - public List mapToSubtype(Collection list, Class clazz) - { - return list.stream().filter(clazz::isInstance).map(clazz::cast).collect(Collectors.toList()); - } - - public boolean exist(List list, Predicate predicate){ - return list.stream().anyMatch(predicate); - } - - public List mapToSupertype(Collection subTypes, Class clazz) - { - return subTypes.stream().map(clazz::cast).collect(Collectors.toList()); - } - - public final EPackage getEcoreEPackage(){ - return ecoreEPackage; - } - - public final List getEcoreEDataTypes(){ - return ecoreEDataTypes; - } - -} From e5f35387e74150c34874a35b73494de9d8cd5b59 Mon Sep 17 00:00:00 2001 From: Roland Kluge Date: Thu, 9 Aug 2018 08:20:05 +0200 Subject: [PATCH 23/37] Formatting --- .../MultiStatusAwareErrorReporter.java | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/errorhandling/MultiStatusAwareErrorReporter.java b/org.moflon.core.ui/src/org/moflon/core/ui/errorhandling/MultiStatusAwareErrorReporter.java index b671ed0e..438f22f9 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/errorhandling/MultiStatusAwareErrorReporter.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/errorhandling/MultiStatusAwareErrorReporter.java @@ -56,25 +56,29 @@ public final void report(final IStatus status) { } } } - + /** * Returns the file configured via the constructor + * * @return the file of this reporter */ - public IFile getFile() - { - return file; - } + public IFile getFile() { + return file; + } /** - * This method reports a leaf status (not a {@link MultiStatus}) by creating a marker at the configured file (#getFile()) - * @param status the status to report - * @throws CoreException if creating the marker fails + * This method reports a leaf status (not a {@link MultiStatus}) by creating a + * marker at the configured file (#getFile()) + * + * @param status + * the status to report + * @throws CoreException + * if creating the marker fails */ protected void reportLeafStatus(final IStatus status) throws CoreException { - final IFile file = getFile(); - final IResource markedResource = file.exists() ? file : file.getProject(); - final IMarker validationMarker = markedResource.createMarker(WorkspaceHelper.MOFLON_PROBLEM_MARKER_ID); + final IFile file = getFile(); + final IResource markedResource = file.exists() ? file : file.getProject(); + final IMarker validationMarker = markedResource.createMarker(WorkspaceHelper.MOFLON_PROBLEM_MARKER_ID); validationMarker.setAttribute(IMarker.MESSAGE, status.getMessage()); validationMarker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH); validationMarker.setAttribute(IMarker.SEVERITY, @@ -83,10 +87,14 @@ protected void reportLeafStatus(final IStatus status) throws CoreException { } /** - * Converts the severity values from the world of {@link IStatus} to the world of {@link IMarker}. - * @param value the {@link IStatus} severity + * Converts the severity values from the world of {@link IStatus} to the world + * of {@link IMarker}. + * + * @param value + * the {@link IStatus} severity * @return the {@link IMarker} severity - * @throws CoreException if the value cannot be translated + * @throws CoreException + * if the value cannot be translated */ private static final int convertStatusSeverityToEclipseMarkerSeverity(final int value) throws CoreException { if (value >= IStatus.ERROR) { From b3943a1580024dd188d05fdbe571d231827e4a0c Mon Sep 17 00:00:00 2001 From: Anthony Anjorin Date: Thu, 9 Aug 2018 09:29:20 +0200 Subject: [PATCH 24/37] Got rid of warnings when using Java 10 --- .../src/org/moflon/core/ui/AbstractCommandHandler.java | 2 +- .../src/org/moflon/emf/injection/unparsing/MethodVisitor.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/AbstractCommandHandler.java b/org.moflon.core.ui/src/org/moflon/core/ui/AbstractCommandHandler.java index d0ef2d83..39308255 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/AbstractCommandHandler.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/AbstractCommandHandler.java @@ -105,7 +105,7 @@ protected static IFile getEditedFile(final ExecutionEvent event) { protected void openInEditor(final IFile targetFile) throws CoreException, PartInitException { IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); HashMap map = new HashMap(); - map.put(IMarker.LINE_NUMBER, new Integer(1)); + map.put(IMarker.LINE_NUMBER, Integer.valueOf(1)); IMarker marker; marker = targetFile.createMarker(IMarker.TEXT); diff --git a/org.moflon.emf.injection/src/org/moflon/emf/injection/unparsing/MethodVisitor.java b/org.moflon.emf.injection/src/org/moflon/emf/injection/unparsing/MethodVisitor.java index 18e46ca2..a09efe5b 100644 --- a/org.moflon.emf.injection/src/org/moflon/emf/injection/unparsing/MethodVisitor.java +++ b/org.moflon.emf.injection/src/org/moflon/emf/injection/unparsing/MethodVisitor.java @@ -24,7 +24,7 @@ public class MethodVisitor extends ASTVisitor { private final List methods = new ArrayList(); public MethodVisitor(final String codeForProject) { - final ASTParser parser = ASTParser.newParser(AST.JLS8); + final ASTParser parser = ASTParser.newParser(AST.JLS10); parser.setSource(codeForProject.toCharArray()); parser.setIgnoreMethodBodies(true); From cf63f50fa33e06fdde4f217c5445df4942a80116 Mon Sep 17 00:00:00 2001 From: Anthony Anjorin Date: Thu, 9 Aug 2018 09:30:31 +0200 Subject: [PATCH 25/37] Wizard now supports xcore --- .../ui/AbstractMoflonProjectInfoPage.java | 40 +++- .../META-INF/MANIFEST.MF | 3 +- .../core/utilities/WorkspaceHelper.java | 4 + .../moflon/emf/build/MoflonEmfBuilder.java | 181 +++++++++++------- ...or.xtend => DefaultContentGenerator.xtend} | 21 +- .../ui/wizard/NewMoflonEmfProjectWizard.java | 10 +- 6 files changed, 178 insertions(+), 81 deletions(-) rename org.moflon.emf.ui/src/org/moflon/emf/ui/wizard/{DefaultEPackageContentGenerator.xtend => DefaultContentGenerator.xtend} (58%) diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/AbstractMoflonProjectInfoPage.java b/org.moflon.core.ui/src/org/moflon/core/ui/AbstractMoflonProjectInfoPage.java index 1085617f..252fce78 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/AbstractMoflonProjectInfoPage.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/AbstractMoflonProjectInfoPage.java @@ -43,6 +43,8 @@ public abstract class AbstractMoflonProjectInfoPage extends WizardPage { private Text projectNameTextfield; + private boolean generateDefaultXcore = true; + public AbstractMoflonProjectInfoPage(String name, String title, String desc) { super(name); projectName = ""; @@ -137,6 +139,27 @@ public void widgetDefaultSelected(final SelectionEvent e) { }); defaultLocationCheckbox.setSelection(useDefaultLocation); defaultLocationSelectionChanged(); + + Button generateDefaultXcoreButton = new Button(container, SWT.RADIO); + generateDefaultXcoreButton.addSelectionListener(new SelectionListener() { + @Override + public void widgetSelected(SelectionEvent e) { + generateDefaultXcore = generateDefaultXcoreButton.getSelection(); + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + generateDefaultXcore = generateDefaultXcoreButton.getSelection(); + } + }); + generateDefaultXcoreButton.setText("Generate default .xcore in project"); + generateDefaultXcoreButton.setSelection(true); + + createDummyLabel(container); + createDummyLabel(container); + + Button generateDefaultEcore = new Button(container, SWT.RADIO); + generateDefaultEcore.setText("Generate default .ecore in project"); } public void createControlsForProjectName(final Composite container) { @@ -209,9 +232,8 @@ public IPath getProjectLocation() { /** * Sets the project path to be used * - * @param projectLocation - * the desired project location. null indicates the - * default location + * @param projectLocation the desired project location. null + * indicates the default location */ private void setProjectLocation(final String projectLocation) { this.projectLocation = projectLocation; @@ -220,8 +242,7 @@ private void setProjectLocation(final String projectLocation) { /** * Sets the given error message to indicate a validation problem * - * @param message - * the message or null if no problems were detected + * @param message the message or null if no problems were detected */ private final void updateStatus(final String message) { setErrorMessage(message); @@ -244,10 +265,8 @@ private void dialogChanged() { * Checks if given name is a valid name for a new project in the current * workspace. * - * @param projectName - * Name of project to be created in current workspace - * @param pluginId - * ID of bundle + * @param projectName Name of project to be created in current workspace + * @param pluginId ID of bundle * @return A status object indicating success or failure and a relevant message. */ private static IStatus validateProjectName(final String projectName) { @@ -270,4 +289,7 @@ private static IStatus validateProjectName(final String projectName) { return new Status(IStatus.OK, pluginId, "Project name is valid"); } + public boolean generateDefaultXCoreFile() { + return generateDefaultXcore; + } } \ No newline at end of file diff --git a/org.moflon.core.utilities/META-INF/MANIFEST.MF b/org.moflon.core.utilities/META-INF/MANIFEST.MF index 0456326b..b29606a7 100644 --- a/org.moflon.core.utilities/META-INF/MANIFEST.MF +++ b/org.moflon.core.utilities/META-INF/MANIFEST.MF @@ -57,6 +57,7 @@ Require-Bundle: org.apache.commons.io;bundle-version="[2.0.1,3.0.0)";visibility: org.eclipse.ui, org.eclipse.emf.codegen.ecore, org.eclipse.pde.core, - org.eclipse.emf.mwe.core + org.eclipse.emf.mwe.core, + org.gervarro.eclipse.workspace.util Bundle-ActivationPolicy: lazy Automatic-Module-Name: org.moflon.core.utilities diff --git a/org.moflon.core.utilities/src/org/moflon/core/utilities/WorkspaceHelper.java b/org.moflon.core.utilities/src/org/moflon/core/utilities/WorkspaceHelper.java index 7aa4cd50..3420de7a 100644 --- a/org.moflon.core.utilities/src/org/moflon/core/utilities/WorkspaceHelper.java +++ b/org.moflon.core.utilities/src/org/moflon/core/utilities/WorkspaceHelper.java @@ -515,6 +515,10 @@ public static boolean isFile(final IResource resource) { public static boolean isEcoreFile(final IResource resource) { return isFile(resource) && resource.getName().endsWith(".ecore"); } + + public static boolean isXcoreFile(final IResource resource) { + return isFile(resource) && resource.getName().endsWith(".xcore"); + } /** * Returns the {@link IProject} with the given name (if exists) diff --git a/org.moflon.emf.build/src/org/moflon/emf/build/MoflonEmfBuilder.java b/org.moflon.emf.build/src/org/moflon/emf/build/MoflonEmfBuilder.java index c0ef3ab3..5db0cff6 100644 --- a/org.moflon.emf.build/src/org/moflon/emf/build/MoflonEmfBuilder.java +++ b/org.moflon.emf.build/src/org/moflon/emf/build/MoflonEmfBuilder.java @@ -1,6 +1,9 @@ package org.moflon.emf.build; +import java.io.IOException; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import org.apache.log4j.Logger; import org.eclipse.core.resources.IBuildConfiguration; @@ -9,6 +12,7 @@ import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; @@ -17,7 +21,12 @@ import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.emf.codegen.ecore.genmodel.GenModel; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; +import org.eclipse.emf.ecore.util.EcoreUtil; import org.gervarro.eclipse.workspace.util.AntPatternCondition; import org.moflon.core.build.AbstractVisitorBuilder; import org.moflon.core.build.CleanVisitor; @@ -47,7 +56,7 @@ public class MoflonEmfBuilder extends AbstractVisitorBuilder { * This builder gets triggered whenever any ecore file in /models changes */ public MoflonEmfBuilder() { - super(new AntPatternCondition(new String[] { "model/*.ecore" })); + super(new AntPatternCondition(new String[] { "model/*.ecore", "model/*.xcore" })); } public static String getId() { @@ -79,10 +88,8 @@ protected void clean(final IProgressMonitor monitor) throws CoreException { /** * Converts the given {@link Status} to problem markers in the Eclipse UI * - * @param status - * the status to be converted - * @param file - * the file contains problems + * @param status the status to be converted + * @param file the file contains problems */ public void handleErrorsInEclipse(final IStatus status, final IFile file) { final String reporterClass = "org.moflon.core.ui.errorhandling.MultiStatusAwareErrorReporter"; @@ -96,64 +103,107 @@ public void handleErrorsInEclipse(final IStatus status, final IFile file) { } @Override - protected void processResource(final IResource ecoreResource, final int kind, Map args, + protected void processResource(IResource resource, final int kind, Map args, final IProgressMonitor monitor) { - if (WorkspaceHelper.isEcoreFile(ecoreResource)) { - final IFile ecoreFile = Platform.getAdapterManager().getAdapter(ecoreResource, IFile.class); - final MultiStatus emfBuilderStatus = new MultiStatus(WorkspaceHelper.getPluginId(getClass()), 0, - "Problems during EMF code generation", null); - try { - final SubMonitor subMon = SubMonitor.convert(monitor, - "Generating code for project " + getProject().getName(), 13); - - final IProject project = getProject(); - createFoldersIfNecessary(project, subMon.split(1)); - ClasspathUtil.makeSourceFolderIfNecessary(WorkspaceHelper.getGenFolder(getProject())); - ClasspathUtil.makeSourceFolderIfNecessary(WorkspaceHelper.getInjectionFolder(getProject())); - - // Compute project dependencies - final IBuildConfiguration[] referencedBuildConfigs = project - .getReferencedBuildConfigs(project.getActiveBuildConfig().getName(), false); - for (final IBuildConfiguration referencedConfig : referencedBuildConfigs) { - addTriggerProject(referencedConfig.getProject()); - } - - // Remove markers and delete generated code - deleteProblemMarkers(); - removeGeneratedCode(project); - - // Build - final ResourceSet resourceSet = eMoflonEMFUtil.createDefaultResourceSet(); - eMoflonEMFUtil.installCrossReferencers(resourceSet); - subMon.worked(1); - - final MoflonEmfCodeGenerator codeGenerationTask = new MoflonEmfCodeGenerator(ecoreFile, resourceSet, - EMoflonPreferencesActivator.getDefault().getPreferencesStorage()); - - final IStatus status = codeGenerationTask.run(subMon.split(1)); - subMon.worked(3); - emfBuilderStatus.add(status); - - if (!emfBuilderStatus.isOK()) - return; - - final GenModel genModel = codeGenerationTask.getGenModel(); - if (genModel == null) { - emfBuilderStatus.add(new Status(IStatus.ERROR, WorkspaceHelper.getPluginId(getClass()), - String.format("No GenModel found for '%s'", getProject()))); - } else { - ExportedPackagesInManifestUpdater.updateExportedPackageInManifest(project, genModel); - - PluginXmlUpdater.updatePluginXml(project, genModel, subMon.split(1)); - } - ResourcesPlugin.getWorkspace().checkpoint(false); - - } catch (final CoreException e) { - emfBuilderStatus.add(new Status(e.getStatus().getSeverity(), WorkspaceHelper.getPluginId(getClass()), - e.getMessage(), e)); - } finally { - handleErrorsInEclipse(emfBuilderStatus, ecoreFile); + + if (WorkspaceHelper.isXcoreFile(resource)) + resource = convertXcoreToEcore(resource); + + if (WorkspaceHelper.isEcoreFile(resource)) + buildEcoreFile(resource, monitor); + } + + private void buildEcoreFile(IResource resource, final IProgressMonitor monitor) { + final IFile ecoreFile = Platform.getAdapterManager().getAdapter(resource, IFile.class); + final MultiStatus emfBuilderStatus = new MultiStatus(WorkspaceHelper.getPluginId(getClass()), 0, + "Problems during EMF code generation", null); + try { + final SubMonitor subMon = SubMonitor.convert(monitor, + "Generating code for project " + getProject().getName(), 13); + + final IProject project = getProject(); + createFoldersIfNecessary(project, subMon.split(1)); + ClasspathUtil.makeSourceFolderIfNecessary(WorkspaceHelper.getGenFolder(getProject())); + ClasspathUtil.makeSourceFolderIfNecessary(WorkspaceHelper.getInjectionFolder(getProject())); + + // Compute project dependencies + final IBuildConfiguration[] referencedBuildConfigs = project + .getReferencedBuildConfigs(project.getActiveBuildConfig().getName(), false); + for (final IBuildConfiguration referencedConfig : referencedBuildConfigs) { + addTriggerProject(referencedConfig.getProject()); + } + + // Remove markers and delete generated code + deleteProblemMarkers(); + removeGeneratedCode(project); + + // Build + final ResourceSet resourceSet = eMoflonEMFUtil.createDefaultResourceSet(); + eMoflonEMFUtil.installCrossReferencers(resourceSet); + subMon.worked(1); + + final MoflonEmfCodeGenerator codeGenerationTask = new MoflonEmfCodeGenerator(ecoreFile, resourceSet, + EMoflonPreferencesActivator.getDefault().getPreferencesStorage()); + + final IStatus status = codeGenerationTask.run(subMon.split(1)); + subMon.worked(3); + emfBuilderStatus.add(status); + + if (!emfBuilderStatus.isOK()) + return; + + final GenModel genModel = codeGenerationTask.getGenModel(); + if (genModel == null) { + emfBuilderStatus.add(new Status(IStatus.ERROR, WorkspaceHelper.getPluginId(getClass()), + String.format("No GenModel found for '%s'", getProject()))); + } else { + ExportedPackagesInManifestUpdater.updateExportedPackageInManifest(project, genModel); + + PluginXmlUpdater.updatePluginXml(project, genModel, subMon.split(1)); } + ResourcesPlugin.getWorkspace().checkpoint(false); + + } catch (final CoreException e) { + emfBuilderStatus.add(new Status(e.getStatus().getSeverity(), WorkspaceHelper.getPluginId(getClass()), + e.getMessage(), e)); + } finally { + handleErrorsInEclipse(emfBuilderStatus, ecoreFile); + } + } + + private IResource convertXcoreToEcore(IResource resource) { + ResourceSet rs = new ResourceSetImpl(); + final URI projectURI = URI.createPlatformResourceURI(getProject().getName() + "/", true); + final URI resourceURI = URI.createURI(resource.getProjectRelativePath().toString()).resolve(projectURI); + Resource r = rs.createResource(resourceURI); + + try { + r.load(null); + + List metamodels = r.getContents()// + .stream()// + .filter(EPackage.class::isInstance)// + .map(EPackage.class::cast)// + .collect(Collectors.toList()); + + EcoreUtil.resolveAll(r); + + IPath ecoreFilePath = resource.getProjectRelativePath().removeFileExtension().addFileExtension("ecore"); + final URI ecoreFileURI = URI.createURI(ecoreFilePath.toString()).resolve(projectURI); + Resource ecoreFileResource = rs.createResource(ecoreFileURI); + + if (metamodels.size() != 1) { + throw new IllegalStateException( + "Xcore file " + resource.getName() + " must contain exactly one package."); + } + + ecoreFileResource.getContents().add(metamodels.get(0)); + ecoreFileResource.save(null); + + return getProject().getFile(ecoreFilePath); + } catch (IOException e) { + e.printStackTrace(); + return resource; } } @@ -165,8 +215,7 @@ protected final AntPatternCondition getTriggerCondition(final IProject project) /** * Handles errors and warning produced by the code generation task * - * @param status - * the {@link IStatus} that contains the errors and warnings + * @param status the {@link IStatus} that contains the errors and warnings */ protected void handleErrorsAndWarnings(final IStatus status, final IFile ecoreFile) throws CoreException { if (status.matches(IStatus.ERROR)) { @@ -180,10 +229,8 @@ protected void handleErrorsAndWarnings(final IStatus status, final IFile ecoreFi /** * Removes all contents in /gen, but preserves all versioning files * - * @param project - * the project to be cleaned - * @throws CoreException - * if cleaning fails + * @param project the project to be cleaned + * @throws CoreException if cleaning fails */ private void removeGeneratedCode(final IProject project) throws CoreException { final CleanVisitor cleanVisitor = new CleanVisitor(project, // diff --git a/org.moflon.emf.ui/src/org/moflon/emf/ui/wizard/DefaultEPackageContentGenerator.xtend b/org.moflon.emf.ui/src/org/moflon/emf/ui/wizard/DefaultContentGenerator.xtend similarity index 58% rename from org.moflon.emf.ui/src/org/moflon/emf/ui/wizard/DefaultEPackageContentGenerator.xtend rename to org.moflon.emf.ui/src/org/moflon/emf/ui/wizard/DefaultContentGenerator.xtend index a34a2de4..3a8b3aab 100644 --- a/org.moflon.emf.ui/src/org/moflon/emf/ui/wizard/DefaultEPackageContentGenerator.xtend +++ b/org.moflon.emf.ui/src/org/moflon/emf/ui/wizard/DefaultContentGenerator.xtend @@ -3,7 +3,7 @@ package org.moflon.emf.ui.wizard /** * This class provides the default content of generated Ecore files */ -class DefaultEPackageContentGenerator { +class DefaultContentGenerator { /** * Generates an XMI representation of the EPackage corresponding to the given @@ -14,7 +14,7 @@ class DefaultEPackageContentGenerator { * @param packageUri the NS URI of the EPackage * @return the raw XMI file content */ - public static def String generateDefaultEPackageForProject(String projectName, String packageName, String packageUri) { + static def String generateDefaultEPackageForProject(String projectName, String packageName, String packageUri) { ''' ''' } + + /** + * Generates a default Xcore file representation of the EPackage corresponding to the given + * project name + * + * @param projectName the name of the containing project + * @param packageName the name of the EPackage + * @param packageUri the NS URI of the EPackage + * @return the default Xcore file's content as a string + */ + static def String generateDefaultXCoreFileForProject(String projectName, String packageName, String packageUri) { + ''' + @xcore.lang.Ecore(nsURI="«packageUri»", nsPrefix="«projectName»") + @xcore.lang.GenModel(modelDirectory="/«projectName»/gen") + package «packageName» + ''' + } } \ No newline at end of file diff --git a/org.moflon.emf.ui/src/org/moflon/emf/ui/wizard/NewMoflonEmfProjectWizard.java b/org.moflon.emf.ui/src/org/moflon/emf/ui/wizard/NewMoflonEmfProjectWizard.java index a7ade4e0..bd1ec378 100644 --- a/org.moflon.emf.ui/src/org/moflon/emf/ui/wizard/NewMoflonEmfProjectWizard.java +++ b/org.moflon.emf.ui/src/org/moflon/emf/ui/wizard/NewMoflonEmfProjectWizard.java @@ -106,8 +106,14 @@ protected void generateDefaultFiles(final IProgressMonitor monitor, final IProje final String packageName = MoflonUtil.lastSegmentOf(projectName); final URI projectUri = MoflonGenModelBuilder.determineProjectUriBasedOnPreferences(project); final URI packageUri = URI.createURI(projectUri.toString() + MoflonConventions.getDefaultPathToEcoreFileInProject(projectName)); - final String defaultEcoreFile = DefaultEPackageContentGenerator.generateDefaultEPackageForProject(projectName, packageName, packageUri.toString()); - WorkspaceHelper.addFile(project, MoflonConventions.getDefaultPathToEcoreFileInProject(projectName), defaultEcoreFile, subMon.split(1)); + + if(projectInfo.generateDefaultXCoreFile()) { + final String defaultXcoreFile = DefaultContentGenerator.generateDefaultXCoreFileForProject(projectName, packageName, packageUri.toString()); + WorkspaceHelper.addFile(project, MoflonConventions.getDefaultPathToFileInProject(projectName, ".xcore"), defaultXcoreFile, subMon.split(1)); + } else { + final String defaultEcoreFile = DefaultContentGenerator.generateDefaultEPackageForProject(projectName, packageName, packageUri.toString()); + WorkspaceHelper.addFile(project, MoflonConventions.getDefaultPathToEcoreFileInProject(projectName), defaultEcoreFile, subMon.split(1)); + } } /** From 1207669aeba7fd634fd604b6e05f38b55859861c Mon Sep 17 00:00:00 2001 From: Roland Kluge Date: Thu, 9 Aug 2018 10:45:08 +0200 Subject: [PATCH 26/37] Improve error reporting if something goes wrong during genmodel processing Related to https://github.com/eMoflon/emoflon-core/issues/95 --- .../moflon/core/utilities/ExceptionUtil.java | 12 +++++++++- .../org/moflon/core/utilities/MoflonUtil.java | 16 +++++++++---- .../emf/build/MonitoredGenModelBuilder.java | 23 ++++++++++++++++++- 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/org.moflon.core.utilities/src/org/moflon/core/utilities/ExceptionUtil.java b/org.moflon.core.utilities/src/org/moflon/core/utilities/ExceptionUtil.java index a82dd456..79ea7dd2 100644 --- a/org.moflon.core.utilities/src/org/moflon/core/utilities/ExceptionUtil.java +++ b/org.moflon.core.utilities/src/org/moflon/core/utilities/ExceptionUtil.java @@ -18,6 +18,16 @@ private ExceptionUtil() { throw new UtilityClassNotInstantiableException(); } + /** + * Creates an {@link IStatus} that reports the given {@link Throwable}. + * @param reportingClass the class that reports the error (used for determining the plugin id) + * @param t the error to report + * @return the resulting {@link IStatus} having flag {@link IStatus#ERROR} and the message of the {@link Throwable} as status message + */ + public static IStatus createDefaultErrorStatus(final Class reportingClass, final Throwable t) { + return new Status(IStatus.ERROR, WorkspaceHelper.getPluginId(reportingClass), t.getMessage(), t); + } + /** * Formats the given exception for debugging purposes. * @@ -46,7 +56,7 @@ public static String displayExceptionAsString(final Exception e) { /** * Throws a {@link CoreException} that contains an {@link IStatus} with the * given message, plugin ID, and wrapped exception - * + * * @param message * error message * @param plugin diff --git a/org.moflon.core.utilities/src/org/moflon/core/utilities/MoflonUtil.java b/org.moflon.core.utilities/src/org/moflon/core/utilities/MoflonUtil.java index 18d3f20c..359d2760 100644 --- a/org.moflon.core.utilities/src/org/moflon/core/utilities/MoflonUtil.java +++ b/org.moflon.core.utilities/src/org/moflon/core/utilities/MoflonUtil.java @@ -8,6 +8,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; +import org.eclipse.emf.ecore.EClassifier; import org.eclipse.emf.ecore.ENamedElement; import org.eclipse.emf.ecore.EcorePackage; @@ -44,7 +45,12 @@ public static String eCoreTypeToJavaType(final String eCoreType) throws IllegalA // Derive the java data type from the Ecore class name try { - javaType = EcorePackage.eINSTANCE.getEClassifier(eCoreType).getInstanceClass().getSimpleName(); + final EClassifier eClassifier = EcorePackage.eINSTANCE.getEClassifier(eCoreType); + if (eClassifier != null) { + javaType = eClassifier.getInstanceClass().getSimpleName(); + } else { + javaType = eCoreType; + } } catch (Exception e) { logger.debug("Cannot derive Java data type from the given Ecore data type = '" + eCoreType + "'. Using Ecore type instead."); @@ -94,7 +100,7 @@ public static String handlePrefixForBooleanAttributes(final String packageName, * Returns the last segment of a fully-qualified name * * Example: For the name 'x.y.z.A', the last segment is 'A' - * + * * @param name * the fully-qualified name * @return the last segment of the name @@ -112,7 +118,7 @@ public static String lastSegmentOf(final String name) { /** * Returns the capitalized last segment of the given fully-qualified name - * + * * @param name * the name * @return the result of capitalizing {@link #lastSegmentOf(String)} @@ -125,7 +131,7 @@ public static String lastCapitalizedSegmentOf(final String name) { * Returns all segments but the last one of the given fully-qualified name * * Example: For the name 'x.y.z.A', the result is 'x.y.z' - * + * * @param name * the fully-qualified name * @return all but the last segment @@ -164,7 +170,7 @@ public static String transformPackageNameUsingImportMapping(final String fullyQu /** * Joins the given list of segments using '.', resulting in a qualified name - * + * * @param segments * the segments * @return the qualified name diff --git a/org.moflon.emf.build/src/org/moflon/emf/build/MonitoredGenModelBuilder.java b/org.moflon.emf.build/src/org/moflon/emf/build/MonitoredGenModelBuilder.java index 596f3ae1..8e148a03 100644 --- a/org.moflon.emf.build/src/org/moflon/emf/build/MonitoredGenModelBuilder.java +++ b/org.moflon.emf.build/src/org/moflon/emf/build/MonitoredGenModelBuilder.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.Map; +import org.eclipse.core.internal.resources.ResourceException; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IProgressMonitor; @@ -18,6 +19,7 @@ import org.gervarro.eclipse.task.ITask; import org.moflon.core.propertycontainer.MoflonPropertiesContainer; import org.moflon.core.propertycontainer.MoflonPropertiesContainerHelper; +import org.moflon.core.utilities.ExceptionUtil; import org.moflon.core.utilities.WorkspaceHelper; import org.moflon.core.utilities.eMoflonEMFUtil; import org.moflon.emf.codegen.MoflonGenModelBuilder; @@ -80,7 +82,7 @@ public final IStatus run(final IProgressMonitor monitor) { try { this.genModel = genModelBuilder.buildGenModel(genModelURI); } catch (final RuntimeException e) { - return new Status(IStatus.ERROR, WorkspaceHelper.getPluginId(getClass()), e.getMessage(), e); + return handleExceptionDuringGenmodelProcessing(e); } subMon.worked(30); @@ -117,6 +119,25 @@ public final IStatus run(final IProgressMonitor monitor) { return saveStatus.isOK() ? Status.OK_STATUS : saveStatus; } + /** + * This method creates an {@link IStatus} from the given {@link Exception} + * + * If the cause of the {@link Exception} is a {@link ResourceException}, an informative message for the user is created. + * + * @param e the {@link Exception} to report + * @return the resulting {@link IStatus} + */ + @SuppressWarnings("restriction") + private IStatus handleExceptionDuringGenmodelProcessing(final Exception e) { + final Throwable cause = e.getCause(); + if (cause != null && cause instanceof ResourceException) { + final String message = "A required resource could not be found. This may mean that a required project could not be built. Please fix the required resource first and then rebuild this project. Details: " + cause.getMessage(); + return new Status(IStatus.ERROR, WorkspaceHelper.getPluginId(getClass()), message); + } else { + return ExceptionUtil.createDefaultErrorStatus(getClass(), e); + } + } + /** * Saves the {@link GenModel} of this builder * From 8e6bd33df8c0f16b52854d04df4c6fcf631e1ffe Mon Sep 17 00:00:00 2001 From: Roland Kluge Date: Thu, 9 Aug 2018 12:26:32 +0200 Subject: [PATCH 27/37] Reset JLS level to Java 1.8 --- .../moflon/emf/injection/unparsing/MethodVisitor.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/org.moflon.emf.injection/src/org/moflon/emf/injection/unparsing/MethodVisitor.java b/org.moflon.emf.injection/src/org/moflon/emf/injection/unparsing/MethodVisitor.java index a09efe5b..6c594e88 100644 --- a/org.moflon.emf.injection/src/org/moflon/emf/injection/unparsing/MethodVisitor.java +++ b/org.moflon.emf.injection/src/org/moflon/emf/injection/unparsing/MethodVisitor.java @@ -24,7 +24,7 @@ public class MethodVisitor extends ASTVisitor { private final List methods = new ArrayList(); public MethodVisitor(final String codeForProject) { - final ASTParser parser = ASTParser.newParser(AST.JLS10); + final ASTParser parser = ASTParser.newParser(getJLSLevel()); parser.setSource(codeForProject.toCharArray()); parser.setIgnoreMethodBodies(true); @@ -33,6 +33,15 @@ public MethodVisitor(final String codeForProject) { node.accept(this); } + /** + * Returns the JLS level to be used by the {@link MethodVisitor} + * @return the JLS level + */ + @SuppressWarnings("deprecation") + private int getJLSLevel() { + return AST.JLS8; + } + @Override public boolean visit(final MethodDeclaration node) { methods.add(node); From 32dd3c7bde2cbdae2098d267411a3f148b026973 Mon Sep 17 00:00:00 2001 From: Roland Kluge Date: Thu, 9 Aug 2018 12:26:43 +0200 Subject: [PATCH 28/37] Avoid empty plugin.xml --- org.moflon.emf.codegen/plugin.xml | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 org.moflon.emf.codegen/plugin.xml diff --git a/org.moflon.emf.codegen/plugin.xml b/org.moflon.emf.codegen/plugin.xml deleted file mode 100644 index ce1e986c..00000000 --- a/org.moflon.emf.codegen/plugin.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - From 0d7f2df9ffdda92b7f1f7427c5a07aa5e971a6f7 Mon Sep 17 00:00:00 2001 From: Roland Kluge Date: Thu, 9 Aug 2018 12:27:02 +0200 Subject: [PATCH 29/37] Avoid warnings by using fully-qualified name --- .../src/org/moflon/emf/build/MonitoredGenModelBuilder.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/org.moflon.emf.build/src/org/moflon/emf/build/MonitoredGenModelBuilder.java b/org.moflon.emf.build/src/org/moflon/emf/build/MonitoredGenModelBuilder.java index 8e148a03..0242a624 100644 --- a/org.moflon.emf.build/src/org/moflon/emf/build/MonitoredGenModelBuilder.java +++ b/org.moflon.emf.build/src/org/moflon/emf/build/MonitoredGenModelBuilder.java @@ -5,7 +5,6 @@ import java.util.List; import java.util.Map; -import org.eclipse.core.internal.resources.ResourceException; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IProgressMonitor; @@ -130,7 +129,7 @@ public final IStatus run(final IProgressMonitor monitor) { @SuppressWarnings("restriction") private IStatus handleExceptionDuringGenmodelProcessing(final Exception e) { final Throwable cause = e.getCause(); - if (cause != null && cause instanceof ResourceException) { + if (cause != null && cause instanceof org.eclipse.core.internal.resources.ResourceException) { final String message = "A required resource could not be found. This may mean that a required project could not be built. Please fix the required resource first and then rebuild this project. Details: " + cause.getMessage(); return new Status(IStatus.ERROR, WorkspaceHelper.getPluginId(getClass()), message); } else { From 4f7f2bdf9349793cc8116790c582acea973010f6 Mon Sep 17 00:00:00 2001 From: Roland Kluge Date: Thu, 9 Aug 2018 12:28:40 +0200 Subject: [PATCH 30/37] Remove plugin.xml from build.properties --- org.moflon.emf.codegen/build.properties | 1 - 1 file changed, 1 deletion(-) diff --git a/org.moflon.emf.codegen/build.properties b/org.moflon.emf.codegen/build.properties index 631108b9..bf298db6 100644 --- a/org.moflon.emf.codegen/build.properties +++ b/org.moflon.emf.codegen/build.properties @@ -3,7 +3,6 @@ output.. = bin/ bin.includes = META-INF/,\ .,\ src/,\ - plugin.xml,\ schema/,\ .project,\ .gitignore,\ From 5dbc726169d7200855a663ac9ac0041bbe6646b0 Mon Sep 17 00:00:00 2001 From: Roland Kluge Date: Thu, 9 Aug 2018 13:07:42 +0200 Subject: [PATCH 31/37] Create more expressive error message if bundle is broken Closes Avoid NPE in eMoflonUtils getSymbolicName #341 --- .../src/org/moflon/core/utilities/eMoflonEMFUtil.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/org.moflon.core.utilities/src/org/moflon/core/utilities/eMoflonEMFUtil.java b/org.moflon.core.utilities/src/org/moflon/core/utilities/eMoflonEMFUtil.java index 539caa68..18cce04b 100644 --- a/org.moflon.core.utilities/src/org/moflon/core/utilities/eMoflonEMFUtil.java +++ b/org.moflon.core.utilities/src/org/moflon/core/utilities/eMoflonEMFUtil.java @@ -1050,8 +1050,13 @@ private static ECrossReferenceAdapter getCRAdapter(final EObject target) { * @param pluginModel * the plugin model * @return the symbolic name + * @throws Exception if the symbolic name cannot be extracted from the model */ private static String extractSymbolicName(final IPluginModelBase pluginModel) { + if (pluginModel == null || pluginModel.getBundleDescription() == null) { + throw new IllegalArgumentException("Cannot extract symbolic name from bundle."); + } + return pluginModel.getBundleDescription().getSymbolicName(); } From a59445dd87799a2570633e30c86501b52d4a979d Mon Sep 17 00:00:00 2001 From: Roland Kluge Date: Thu, 9 Aug 2018 13:55:57 +0200 Subject: [PATCH 32/37] Add menu entry to register EMF metamodel #89 Closes #89 --- .../.classpath | 7 + .../.gitignore | 3 + .../.project | 28 +++ .../META-INF/MANIFEST.MF | 15 ++ .../build.properties | 5 + .../icons/register.png | Bin 0 -> 48181 bytes .../plugin.xml | 30 ++++ .../EmfRegistryManager.java | 166 ++++++++++++++++++ .../RegisterMetamodelHandler.java | 57 ++++++ org.moflon.core.ui/plugin.xml | 2 +- projectSet.psf | 2 + 11 files changed, 314 insertions(+), 1 deletion(-) create mode 100644 org.moflon.core.ui.packageregistration/.classpath create mode 100644 org.moflon.core.ui.packageregistration/.gitignore create mode 100644 org.moflon.core.ui.packageregistration/.project create mode 100644 org.moflon.core.ui.packageregistration/META-INF/MANIFEST.MF create mode 100644 org.moflon.core.ui.packageregistration/build.properties create mode 100644 org.moflon.core.ui.packageregistration/icons/register.png create mode 100644 org.moflon.core.ui.packageregistration/plugin.xml create mode 100644 org.moflon.core.ui.packageregistration/src/org/moflon/core/ui/packageregistration/EmfRegistryManager.java create mode 100644 org.moflon.core.ui.packageregistration/src/org/moflon/core/ui/packageregistration/RegisterMetamodelHandler.java diff --git a/org.moflon.core.ui.packageregistration/.classpath b/org.moflon.core.ui.packageregistration/.classpath new file mode 100644 index 00000000..22f30643 --- /dev/null +++ b/org.moflon.core.ui.packageregistration/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/org.moflon.core.ui.packageregistration/.gitignore b/org.moflon.core.ui.packageregistration/.gitignore new file mode 100644 index 00000000..3d1f6cf4 --- /dev/null +++ b/org.moflon.core.ui.packageregistration/.gitignore @@ -0,0 +1,3 @@ +/.settings/ +/bin +/target \ No newline at end of file diff --git a/org.moflon.core.ui.packageregistration/.project b/org.moflon.core.ui.packageregistration/.project new file mode 100644 index 00000000..f2fa45b9 --- /dev/null +++ b/org.moflon.core.ui.packageregistration/.project @@ -0,0 +1,28 @@ + + + org.moflon.core.ui.packageregistration + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/org.moflon.core.ui.packageregistration/META-INF/MANIFEST.MF b/org.moflon.core.ui.packageregistration/META-INF/MANIFEST.MF new file mode 100644 index 00000000..991a00af --- /dev/null +++ b/org.moflon.core.ui.packageregistration/META-INF/MANIFEST.MF @@ -0,0 +1,15 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: org.moflon.core.ui.packageregistration +Bundle-SymbolicName: org.moflon.core.ui.packageregistration;singleton:=true +Bundle-Version: 1.0.0.qualifier +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Require-Bundle: org.eclipse.emf.ecore;bundle-version="2.11.0", + org.eclipse.core.resources, + org.eclipse.equinox.registry, + org.apache.log4j, + org.eclipse.jface, + org.eclipse.ui.workbench, + org.eclipse.emf.ecore.xmi, + org.moflon.core.ui +Bundle-Vendor: eMoflon Developers diff --git a/org.moflon.core.ui.packageregistration/build.properties b/org.moflon.core.ui.packageregistration/build.properties new file mode 100644 index 00000000..e9863e28 --- /dev/null +++ b/org.moflon.core.ui.packageregistration/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml diff --git a/org.moflon.core.ui.packageregistration/icons/register.png b/org.moflon.core.ui.packageregistration/icons/register.png new file mode 100644 index 0000000000000000000000000000000000000000..bd198799bc432b0177437d51fc9390fb8ea4af7d GIT binary patch literal 48181 zcmb^21#sNJnjm1uPV6|Q7-D9OnHgip%*+g9W@cuFF*7qWQ_RfF%*>v1-rKjeyH$5} zyLT!{E&Xb#TP@X$G~aZHjFd3kH_UHfU|?{fBERK6((iw#uaF;eyc>}2NAlT@UsU1i z$Kd|eAoyb)+FC@_4h#$i>F@Mudd>6oBZz7*sA4Z*Yp82)Vr7Z1U}9kiM#oCaz(h;O zTGbZp`jG?uZw`4YBYP)ZTSKs423C58_(CSOhE7(tW_HrGcv2sE&a7>fl%3Pg=B6zC z=)V842?~to7qzRXZinK0Vn11Bz{$F%S<0#~nfvl=OAEr=_lKP(8-@1r^x#@uTU*I) zC@4=*s-Kf9o!4@kYA@j=9o7nyFE42=nenVG0&2b(co~42SHmDaO8Vz;DZRv?kz=sb zN8=UCCOJjLtCdK#UA}EaOHYOur-)EyiP~w9dzZO!pv5wj+IZM?$B-5{^VIM-lmoGz zG2Uxa7K=vz2!3J+TREYBNiJ+P>7xrffbp^|je2pxkJp`#>&ZL>A|Wq8IeYIKcZLH0 zSGgbQ;|*Lq9Gjjs(aQbDnuvuNztHpg@cYN|=l_uFf3!%R$CwQU#!h8nrXc^(m>w2( zD?&9iQY?`~PbBw7ln1>}IVEnEYbELl{>~)U9U(bC$+L-RMEh+`OEvIC7MIy-qKGL| zb4Ng)t!|JII&@&4f>8{ya>fYgoReJ&4vsQ1a5pax#_z^xd#9EnSV4x)&W^bgZoTQh z8D3vLB5T(x$h^iA(l4A_l4kF*B((4?TM_4@4+wwa;hluHBv+CUgvkKWYfFBV{7U%s%%fMH9)P>n0=BKOS_my=OP5hCGrLHdhc@GlR#pa7P+hB}_z471v& zDmSj~FiX6t`o`?R{n2!%7AOGge4E`@!^~gct#2-XI&r87SCxQqWS2EOvbIy!`nmNqCnCrEc*S52t{<_t2K0e>551cu3G(` za3=}Jv*{k0&;jt8NMAwFcRqYdKXQs?)9#aq{pJfq)?J|f9rFOipQ^7oR8HWy1A31# z@BHo>lmXI|0Z}G-h$-teQ5diUegp>e{#1d{%y@8~hUFZX-BCK=oRO-`Lb>|OvR`_I zv3u!77CQOXdHCK5*%{#yvVK4myJ3szBPn<@SYyQ3c^T?pYO&L0+R{1kZLaqmNVvgS zKP|hRMck7?*%J}OclKU5Ew}R1IjoyG)H0o&P>ZiwsDD|Tx1=WyLHIs2NxNnT*T@7c zFr%ZA^iacS-G*PwU^ra$CSX;M{WV1)mnbAzLkXp->HQA>b_*LN_Q_xg7T<$#NNJG3 zls?=yCo)v8TBRZQJ2D$VDxnmy0wov)rFu^8{8^U3V7d$xH!TX|ntDYVwQ_NV(0Io- zdOMh=b^h-{>X0M@dz2+ZXVY(Z`e%>fkT`$dbEN`r@u1t*qb2dY`@n2tna-lue-DFd zQP%9*?1^LuNfA9ci)iG4z4$lmM*FiA<(wx@Volk)FR&sC%yTi=-=DNUrC3oCV5pHW z0nkmP>d3^v$(te|L|KNeZGJxr#W4Hs{uzxugd!X{jXfNwV(3Gd@e8B4j2%~DOT)LR z2)_tdCv|23KV=_*bpG0eS&Pvtp@i9=G)EsjmuI{f0BNhEaIw?F)c^;H(_4k@_*}-s z{H>d@2gM!>oMRT>E0(65kwqL~%s5zP=0UcA2#cms&R9kciF!sRdylRTI}q60!a&6L zN)Mf15zb-Mr2T0is7LLpegpMUL?bX~K!zBZ+DGrC;inp`AgP`hx<%%eG0LWKV-r@_ zjwe_Y(a@KPt=Iz<7o8ezXu9shGSYbc!z29IWh+Hj8}ec*0C#DBEC(aS@zC^B6bkI! z*4IH@xf!~(S{1I=+2oDyp!_w(khJM^ts}uEsB?<;f>sK=e1KSd%kE;TH?-Dj`30ra zADk_wKu>qb-nghAWOhev7GKlWeFSYCf+DvFac{s&_wgf2{Zai2`m;>vjn!aNlthg) zCKn2rc;9Unc z9(@GZ!97LB=$+mVb>9YZrmTNg Jjadbc=f)Q24w`K{=1u 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 + + + + + + + + + + + + + + + + + + + + + diff --git a/org.moflon.core.ui.packageregistration/src/org/moflon/core/ui/packageregistration/EmfRegistryManager.java b/org.moflon.core.ui.packageregistration/src/org/moflon/core/ui/packageregistration/EmfRegistryManager.java new file mode 100644 index 00000000..d281179e --- /dev/null +++ b/org.moflon.core.ui.packageregistration/src/org/moflon/core/ui/packageregistration/EmfRegistryManager.java @@ -0,0 +1,166 @@ +/******************************************************************************* + * Copyright (c) 2008 The University of York. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dimitrios Kolovos - initial API and implementation + ******************************************************************************/ +package org.moflon.core.ui.packageregistration; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EDataType; +import org.eclipse.emf.ecore.EEnum; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EcorePackage; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; +import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl; + +/** + * This class allows to register and manage EMF metamodels + * + * @author Roland Kluge - Initial implementation + * + */ +public class EmfRegistryManager { + private static EmfRegistryManager instance = null; + + private HashMap> managedMetamodels = new HashMap<>(); + + public static EmfRegistryManager getInstance() { + if (instance == null) { + instance = new EmfRegistryManager(); + } + return instance; + } + + public void registerMetamodel(String fileName) throws Exception { + List ePackages = register(URI.createPlatformResourceURI(fileName, true), EPackage.Registry.INSTANCE); + managedMetamodels.put(fileName, ePackages); + } + + // The following methods are taken from org.eclipse.epsilon.emc.emf.EmfUtil + + public static List register(URI uri, EPackage.Registry registry) throws Exception { + return register(uri, registry, true); + } + + /** + * Register all the packages in the metamodel specified by the uri in the + * registry. + * + * @param uri + * The URI of the metamodel + * @param registry + * The registry in which the metamodel's packages are registered + * @param useUriForResource + * If True, the URI of the resource created for the metamodel would + * be overwritten with the URI of the last EPackage in the metamodel. + * @return A list of the EPackages registered. + * @throws Exception + * If there is an error accessing the resources. + */ + public static List register(URI uri, EPackage.Registry registry, boolean useUriForResource) + throws Exception { + + List ePackages = new ArrayList(); + + initialiseResourceFactoryRegistry(); + + ResourceSet resourceSet = new ResourceSetImpl(); + resourceSet.getPackageRegistry().put(EcorePackage.eINSTANCE.getNsURI(), EcorePackage.eINSTANCE); + + Resource metamodel = resourceSet.createResource(uri); + metamodel.load(Collections.EMPTY_MAP); + + setDataTypesInstanceClasses(metamodel); + + Iterator it = metamodel.getAllContents(); + while (it.hasNext()) { + Object next = it.next(); + if (next instanceof EPackage) { + EPackage p = (EPackage) next; + + adjustNsAndPrefix(metamodel, p, useUriForResource); + registry.put(p.getNsURI(), p); + ePackages.add(p); + } + } + + return ePackages; + + } + + private static void initialiseResourceFactoryRegistry() { + final Map etfm = Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap(); + + if (!etfm.containsKey("*")) { + etfm.put("*", new XMIResourceFactoryImpl()); + } + + } + + private static void adjustNsAndPrefix(Resource metamodel, EPackage p, boolean useUriForResource) { + if (p.getNsURI() == null || p.getNsURI().trim().length() == 0) { + if (p.getESuperPackage() == null) { + p.setNsURI(p.getName()); + } else { + p.setNsURI(p.getESuperPackage().getNsURI() + "/" + p.getName()); + } + } + + if (p.getNsPrefix() == null || p.getNsPrefix().trim().length() == 0) { + if (p.getESuperPackage() != null) { + if (p.getESuperPackage().getNsPrefix() != null) { + p.setNsPrefix(p.getESuperPackage().getNsPrefix() + "." + p.getName()); + } else { + p.setNsPrefix(p.getName()); + } + } + } + + if (p.getNsPrefix() == null) + p.setNsPrefix(p.getName()); + if (useUriForResource) + metamodel.setURI(URI.createURI(p.getNsURI())); + } + + protected static void setDataTypesInstanceClasses(Resource metamodel) { + Iterator it = metamodel.getAllContents(); + while (it.hasNext()) { + EObject eObject = (EObject) it.next(); + if (eObject instanceof EEnum) { + // ((EEnum) eObject).setInstanceClassName("java.lang.Integer"); + } else if (eObject instanceof EDataType) { + EDataType eDataType = (EDataType) eObject; + String instanceClass = ""; + if (eDataType.getName().equals("String")) { + instanceClass = "java.lang.String"; + } else if (eDataType.getName().equals("Boolean")) { + instanceClass = "java.lang.Boolean"; + } else if (eDataType.getName().equals("Integer")) { + instanceClass = "java.lang.Integer"; + } else if (eDataType.getName().equals("Float")) { + instanceClass = "java.lang.Float"; + } else if (eDataType.getName().equals("Double")) { + instanceClass = "java.lang.Double"; + } + if (instanceClass.trim().length() > 0) { + eDataType.setInstanceClassName(instanceClass); + } + } + } + } +} diff --git a/org.moflon.core.ui.packageregistration/src/org/moflon/core/ui/packageregistration/RegisterMetamodelHandler.java b/org.moflon.core.ui.packageregistration/src/org/moflon/core/ui/packageregistration/RegisterMetamodelHandler.java new file mode 100644 index 00000000..2e3d59fd --- /dev/null +++ b/org.moflon.core.ui.packageregistration/src/org/moflon/core/ui/packageregistration/RegisterMetamodelHandler.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2008 The University of York. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dimitrios Kolovos - initial API and implementation + ******************************************************************************/ +package org.moflon.core.ui.packageregistration; + +import java.util.Iterator; + +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.resources.IFile; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.handlers.HandlerUtil; +import org.moflon.core.ui.AbstractCommandHandler; + +/** + * This class provides the front-end capabilities for registering an EMF model. + * + * @author Roland Kluge - Initial implementation + * + */ +public class RegisterMetamodelHandler extends AbstractCommandHandler { + + @Override + public Object execute(final ExecutionEvent event) throws ExecutionException { + final ISelection selection = HandlerUtil.getCurrentSelectionChecked(event); + + if (selection instanceof IStructuredSelection) { + final IStructuredSelection structuredSelection = (IStructuredSelection) selection; + final Iterator it = structuredSelection.iterator(); + while (it.hasNext()) { + final IFile file = (IFile) it.next(); + registerMetamodelInFile(file); + } + } + + return AbstractCommandHandler.DEFAULT_HANDLER_RESULT; + } + + private void registerMetamodelInFile(final IFile file) { + final String fileName = file.getFullPath().toOSString(); + try { + EmfRegistryManager.getInstance().registerMetamodel(fileName); + logger.info("Metamodel " + fileName + " registered successfully"); + } catch (final Exception ex) { + logger.info("Metamodel " + fileName + " could not be registered", ex); + } + } + +} diff --git a/org.moflon.core.ui/plugin.xml b/org.moflon.core.ui/plugin.xml index dea43fb5..f7744c01 100644 --- a/org.moflon.core.ui/plugin.xml +++ b/org.moflon.core.ui/plugin.xml @@ -27,7 +27,7 @@ - + diff --git a/projectSet.psf b/projectSet.psf index 8bead36b..e60911be 100644 --- a/projectSet.psf +++ b/projectSet.psf @@ -16,6 +16,7 @@ + @@ -46,6 +47,7 @@ + From fd4672181d1bead1db15c57c0243c0e716466879 Mon Sep 17 00:00:00 2001 From: Roland Kluge Date: Thu, 9 Aug 2018 14:21:47 +0200 Subject: [PATCH 33/37] Clean up duplicate code --- .../strategy/ObjectDiagramStrategies.java | 53 ++++--- .../xtext/scoping/ScopeProviderHelper.java | 4 +- .../xtext/scoping/utils/MOSLScopeUtil.java | 145 +++++------------- 3 files changed, 70 insertions(+), 132 deletions(-) diff --git a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/strategy/ObjectDiagramStrategies.java b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/strategy/ObjectDiagramStrategies.java index abcb34d6..aca9d260 100644 --- a/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/strategy/ObjectDiagramStrategies.java +++ b/org.moflon.core.ui/src/org/moflon/core/ui/visualisation/strategy/ObjectDiagramStrategies.java @@ -1,10 +1,12 @@ package org.moflon.core.ui.visualisation.strategy; +import java.util.Arrays; import java.util.Collection; import java.util.HashSet; 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.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.util.EContentsEList; @@ -14,18 +16,18 @@ /** * Contains various methods for manipulating {@link ObjectDiagram} instances. - * + * * @author Johannes Brandt * */ public class ObjectDiagramStrategies { - + /** * Computes all edges between selected objects in the given object diagram. - * + * * This method will add each computed edge to the edges stored with the diagram. * Only edges between selected objects will be computed and added. - * + * * @param diagram * The object diagram containing the selection, for which the edges * are to be computed. @@ -39,21 +41,21 @@ public static ObjectDiagram determineEdgesForSelection(ObjectDiagram diagram) { return diagram; } - + /** * Expands the given {@link ObjectDiagram}'s neighbourhood by one degree, * bidirectional. - * + * * The given diagram's neighbourhood is expanded, by adding all neighbors of the * current neighbourhood. The direction of the associations between objects is * irrelevant. If no neighbourhood is defined with the given diagram, then the * selection's neighbours are added to the neighbourhood. - * + * *

* Note: If a neighbourhood expansion of a degree greater than one is * wished, this method can simple be chained. *

- * + * * @param diagram * The diagram, of which the neighbourhood is to be increased by a * degree of one. @@ -90,7 +92,7 @@ public static ObjectDiagram expandNeighbourhoodBidirectional(ObjectDiagram diagr /** * Determines all outbound edges from objects in sourceElements to * objects in targetElements. - * + * * @param sourceElements * The set of objects for which all outbound edges shall be * determined. @@ -98,26 +100,23 @@ public static ObjectDiagram expandNeighbourhoodBidirectional(ObjectDiagram diagr * The set of objects which represent targets of all outbound edges * from the set of source elements. * @param edges - * All edges with an object from sourceElements as source, - * and an object from targetElements as target. + * All edges with an object from sourceElements as + * source, and an object from targetElements as target. */ - @SuppressWarnings("rawtypes") - private static void determineOutboundEdgesBetween(Collection sourceElements, Collection targetElements, - Collection edges) { + private static void determineOutboundEdgesBetween(Collection sourceElements, + Collection targetElements, Collection edges) { for (EObject obj : sourceElements) { - for (EContentsEList.FeatureIterator featureIterator = // - (EContentsEList.FeatureIterator) obj.eCrossReferences().iterator(); featureIterator.hasNext();) { - EObject trg = (EObject) featureIterator.next(); - EReference eReference = (EReference) featureIterator.feature(); - if (targetElements.contains(trg)) - edges.add(new VisualEdge(eReference, EdgeType.LINK, obj, trg)); - } - for (EContentsEList.FeatureIterator featureIterator = // - (EContentsEList.FeatureIterator) obj.eContents().iterator(); featureIterator.hasNext();) { - EObject trg = (EObject) featureIterator.next(); - EReference eReference = (EReference) featureIterator.feature(); - if (targetElements.contains(trg)) - edges.add(new VisualEdge(eReference, EdgeType.LINK, obj, trg)); + final EList eCrossReferences = obj.eCrossReferences(); + final EList eContents = obj.eContents(); + for (final EList featureList : Arrays.asList(eCrossReferences, eContents)) { + for (final EContentsEList.FeatureIterator featureIterator = // + (EContentsEList.FeatureIterator) featureList.iterator(); // + featureIterator.hasNext();) { + EObject trg = (EObject) featureIterator.next(); + EReference eReference = (EReference) featureIterator.feature(); + if (targetElements.contains(trg)) + edges.add(new VisualEdge(eReference, EdgeType.LINK, obj, trg)); + } } } } diff --git a/org.moflon.core.xtext/src/org/moflon/core/xtext/scoping/ScopeProviderHelper.java b/org.moflon.core.xtext/src/org/moflon/core/xtext/scoping/ScopeProviderHelper.java index 016644e0..885b2855 100644 --- a/org.moflon.core.xtext/src/org/moflon/core/xtext/scoping/ScopeProviderHelper.java +++ b/org.moflon.core.xtext/src/org/moflon/core/xtext/scoping/ScopeProviderHelper.java @@ -15,7 +15,7 @@ import org.eclipse.xtext.EcoreUtil2; import org.eclipse.xtext.scoping.IScope; import org.moflon.core.xtext.exceptions.CannotFindScopeException; -import org.moflon.core.xtext.scoping.utils.MOSLScopeUtil; +import org.moflon.core.xtext.utils.ResourceUtil; public class ScopeProviderHelper { private Map existingScopingRoots; @@ -29,7 +29,7 @@ public ScopeProviderHelper(ResourceSet resSet) { public ScopeProviderHelper() { init(); - resourceSet = MOSLScopeUtil.getResourceSet("ecore"); + resourceSet = ResourceUtil.getResourceSet("ecore"); } private void init(){ diff --git a/org.moflon.core.xtext/src/org/moflon/core/xtext/scoping/utils/MOSLScopeUtil.java b/org.moflon.core.xtext/src/org/moflon/core/xtext/scoping/utils/MOSLScopeUtil.java index cca3e36a..fbc4d0cd 100644 --- a/org.moflon.core.xtext/src/org/moflon/core/xtext/scoping/utils/MOSLScopeUtil.java +++ b/org.moflon.core.xtext/src/org/moflon/core/xtext/scoping/utils/MOSLScopeUtil.java @@ -4,113 +4,52 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.Stack; import java.util.stream.Collectors; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; -import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; -import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl; - -public class MOSLScopeUtil -{ - @Deprecated // Since 2018-08-09 - private static MOSLScopeUtil instance; - - /** - * @deprecated All methods are now static, so we do not need the synthetic singleton - */ - @Deprecated // Since 2018-08-09 - public static MOSLScopeUtil getInstance() - { - if (instance == null) - instance = new MOSLScopeUtil(); - return instance; - } - - public static R getRootObject(EObject context, Class clazz) - { - Stack stack = new Stack(); - stack.push(context); - while (!stack.isEmpty()) - { - EObject element = stack.pop(); - if (element == null) - { - return null; - } else if (clazz.isInstance(element)) - return clazz.cast(element); - stack.push(element.eContainer()); - } - return null; - } - - public static List getObjectsFromResource(Resource resource, Class clazz){ - List allContent = new ArrayList<>(); - resource.getAllContents().forEachRemaining(allContent::add); - return allContent.parallelStream().filter(clazz::isInstance).map(clazz::cast).collect(Collectors.toList()); - } - - public static E getObjectFromResourceSet(URI uri, ResourceSet resourceSet, Class clazz) - { - Resource res = getResource(uri, resourceSet, true); - E scopingRoot = clazz.cast(res.getContents().get(0)); - return scopingRoot; - } - - public static ResourceSet getResourceSet() - { - return getResourceSet("xmi"); - } - - private static Resource getResource(URI uri, ResourceSet resourceSet, boolean load) - { - try - { - Resource res = resourceSet.getResource(uri, false); - if (res == null) - { - res = resourceSet.createResource(uri); - } - if (load) - res.load(Collections.EMPTY_MAP); - return res; - } catch (IOException e) - { - throw new RuntimeException(e); - } - } - - public static Resource addToResource(URI uri, ResourceSet resourceSet, EObject obj) - { - Resource resource = getResource(uri, resourceSet, false); - resource.getContents().clear(); - resource.getContents().add(obj); - return resource; - } - - public static void saveToResource(URI uri, ResourceSet resourceSet, EObject obj) - { - try - { - Resource resource = addToResource(uri, resourceSet, obj); - resource.save(Collections.EMPTY_MAP); - } catch (IOException e) - { - throw new RuntimeException(e); - } - } - - public static ResourceSet getResourceSet(String ext) - { - ResourceSet resourceSet = new ResourceSetImpl(); - Resource.Factory.Registry reg = Resource.Factory.Registry.INSTANCE; - Map m = reg.getExtensionToFactoryMap(); - Object factory = m.getOrDefault(ext, new XMIResourceFactoryImpl()); - m.put(ext, factory); - return resourceSet; - } +import org.moflon.core.xtext.utils.ResourceUtil; + +public class MOSLScopeUtil { + @Deprecated // Since 2018-08-09 + private static MOSLScopeUtil instance; + + /** + * @deprecated All methods are now static, so we do not need the synthetic + * singleton + */ + @Deprecated // Since 2018-08-09 + public static MOSLScopeUtil getInstance() { + if (instance == null) + instance = new MOSLScopeUtil(); + return instance; + } + + public static List getObjectsFromResource(Resource resource, Class clazz) { + List allContent = new ArrayList<>(); + resource.getAllContents().forEachRemaining(allContent::add); + return allContent.parallelStream().filter(clazz::isInstance).map(clazz::cast).collect(Collectors.toList()); + } + + public static ResourceSet getResourceSet() { + return ResourceUtil.getResourceSet("xmi"); + } + + public static Resource addToResource(URI uri, ResourceSet resourceSet, EObject obj) { + Resource resource = ResourceUtil.getResource(uri, resourceSet, false); + resource.getContents().clear(); + resource.getContents().add(obj); + return resource; + } + + public static void saveToResource(URI uri, ResourceSet resourceSet, EObject obj) { + try { + Resource resource = addToResource(uri, resourceSet, obj); + resource.save(Collections.EMPTY_MAP); + } catch (IOException e) { + throw new RuntimeException(e); + } + } } From 72661617ba70f4d2c64e0b3cea11f4008c1512c7 Mon Sep 17 00:00:00 2001 From: Roland Kluge Date: Thu, 9 Aug 2018 14:55:05 +0200 Subject: [PATCH 34/37] Improve API reusability of MoflonEmfCodeGenerator --- org.moflon.emf.build/META-INF/MANIFEST.MF | 2 +- .../emf/build/MoflonEmfCodeGenerator.java | 41 +++++++++++++++---- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/org.moflon.emf.build/META-INF/MANIFEST.MF b/org.moflon.emf.build/META-INF/MANIFEST.MF index bc755031..8b6193e1 100644 --- a/org.moflon.emf.build/META-INF/MANIFEST.MF +++ b/org.moflon.emf.build/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Basic EMF build infrastructure Bundle-SymbolicName: org.moflon.emf.build;singleton:=true -Bundle-Version: 1.1.0.qualifier +Bundle-Version: 1.2.0.qualifier Bundle-Vendor: eMoflon developers Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Require-Bundle: org.eclipse.core.runtime;bundle-version="3.0.0", diff --git a/org.moflon.emf.build/src/org/moflon/emf/build/MoflonEmfCodeGenerator.java b/org.moflon.emf.build/src/org/moflon/emf/build/MoflonEmfCodeGenerator.java index f07e29d2..44c672a2 100644 --- a/org.moflon.emf.build/src/org/moflon/emf/build/MoflonEmfCodeGenerator.java +++ b/org.moflon.emf.build/src/org/moflon/emf/build/MoflonEmfCodeGenerator.java @@ -64,12 +64,14 @@ public IStatus processResource(final IProgressMonitor monitor) { if (genModelBuilderStatus.matches(IStatus.ERROR)) { return genModelBuilderStatus; } - this.genModel = genModelBuilderJob.getGenModel(); + this.setGenModel(genModelBuilderJob.getGenModel()); // Load injections final IProject project = getEcoreFile().getProject(); - final IStatus injectionStatus = createInjections(project, genModel); + final InjectionManager injectionManager = createInjectionManager(project); + this.setInjectorManager(injectionManager); + final IStatus injectionStatus = createInjections(project); if (subMon.isCanceled()) { return Status.CANCEL_STATUS; } @@ -105,17 +107,25 @@ public IStatus processResource(final IProgressMonitor monitor) { } } + protected final void setGenModel(final GenModel genModel) { + this.genModel = genModel; + } + public final GenModel getGenModel() { return genModel; } + protected void setInjectorManager(final InjectionManager injectionManager) { + this.injectionManager = injectionManager; + } + public final InjectionManager getInjectorManager() { return injectionManager; } /** * Returns the project name to be displayed - * + * * @param moflonProperties * the properties container to consult * @return the project name @@ -125,17 +135,30 @@ protected String getFullProjectName(final MoflonPropertiesContainer moflonProper } /** - * Loads the injections from the /injection folder + * Loads the injections from the /injection folder using the injection manager returned from {@link #getInjectorManager()} */ - private IStatus createInjections(final IProject project, final GenModel genModel) throws CoreException { + protected IStatus createInjections(final IProject project) throws CoreException { + final IStatus extractionStatus = getInjectorManager().extractInjections(); + return extractionStatus; + } + + /** + * Creates the injection manager to be used for this build process + * + * The resulting injection manager still needs to be set using {@link #setInjectorManager(InjectionManager)} + * @param project the current project + * @return + * @throws CoreException + */ + protected InjectionManager createInjectionManager(final IProject project) + throws CoreException { final IFolder injectionFolder = WorkspaceHelper.addFolder(project, WorkspaceHelper.INJECTION_FOLDER, new NullProgressMonitor()); final CodeInjector injector = new CodeInjectorImpl(project.getLocation().toOSString()); - final InjectionExtractor injectionExtractor = new XTextInjectionExtractor(injectionFolder, genModel); + final InjectionExtractor injectionExtractor = new XTextInjectionExtractor(injectionFolder, this.getGenModel()); - injectionManager = new InjectionManager(injectionExtractor, injector); - final IStatus extractionStatus = injectionManager.extractInjections(); - return extractionStatus; + InjectionManager injectionManager = new InjectionManager(injectionExtractor, injector); + return injectionManager; } } From e841b4aefe85f7c279f65b61dd1045139b5ca641 Mon Sep 17 00:00:00 2001 From: Roland Kluge Date: Thu, 9 Aug 2018 18:13:16 +0200 Subject: [PATCH 35/37] Update version number --- org.moflon.core.feature/feature.xml | 2 +- org.moflon.core.releng.updatesite/changelog.txt | 6 ++++++ org.moflon.core.releng.updatesite/site.xml | 2 +- org.moflon.core.ui/plugin.xml | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/org.moflon.core.feature/feature.xml b/org.moflon.core.feature/feature.xml index 376948d1..9fb9564c 100644 --- a/org.moflon.core.feature/feature.xml +++ b/org.moflon.core.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/org.moflon.core.releng.updatesite/changelog.txt b/org.moflon.core.releng.updatesite/changelog.txt index e33f30a3..2737ddcd 100644 --- a/org.moflon.core.releng.updatesite/changelog.txt +++ b/org.moflon.core.releng.updatesite/changelog.txt @@ -1,3 +1,9 @@ +2018-08-09 eMoflon Core 1.3.0 released + +Integrate support for XCore and improve visualization of metamodels + +* See also https://github.com/eMoflon/emoflon-core/issues?utf8=✓&q=is%3Aissue closed%3A2018-04-10..2018-08-09 + 2018-04-10 eMoflon Core 1.2.0 released Minor improvements: diff --git a/org.moflon.core.releng.updatesite/site.xml b/org.moflon.core.releng.updatesite/site.xml index 038b3f67..475c76a8 100644 --- a/org.moflon.core.releng.updatesite/site.xml +++ b/org.moflon.core.releng.updatesite/site.xml @@ -1,6 +1,6 @@ - + diff --git a/org.moflon.core.ui/plugin.xml b/org.moflon.core.ui/plugin.xml index f7744c01..5bc724e9 100644 --- a/org.moflon.core.ui/plugin.xml +++ b/org.moflon.core.ui/plugin.xml @@ -209,7 +209,7 @@ - + From bcf802fa8d373c71892191c4a5106703fdf9760f Mon Sep 17 00:00:00 2001 From: Roland Kluge Date: Thu, 9 Aug 2018 18:13:50 +0200 Subject: [PATCH 36/37] Update version number --- org.moflon.core.releng.updatesite/category.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.moflon.core.releng.updatesite/category.xml b/org.moflon.core.releng.updatesite/category.xml index 1391d423..52c7854f 100644 --- a/org.moflon.core.releng.updatesite/category.xml +++ b/org.moflon.core.releng.updatesite/category.xml @@ -1,6 +1,6 @@ - + From 77ed5f904d24e0e59d7778980236f1afe6b24769 Mon Sep 17 00:00:00 2001 From: Roland Kluge Date: Thu, 9 Aug 2018 18:29:42 +0200 Subject: [PATCH 37/37] Update pom.xml with new project --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index ce5d5ca0..461f55a3 100644 --- a/pom.xml +++ b/pom.xml @@ -17,6 +17,7 @@ org.moflon.core.ui org.moflon.core.ui.autosetup org.moflon.core.ui.autosetup.tests + org.moflon.core.ui.packageregistration org.moflon.core.releng.updatesite org.moflon.core.releng.target org.moflon.core.utilities