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...
+ *
+ *
... the editor is not null
+ *
... the editor input is not null
+ *
... the editor input is of type {@link IFileEditorInput}
+ *
... there is a {@link IFile} opened in the editor
+ *
... the {@link IFile}'s extension is not null
+ *
... the {@link IFile}'s extension equals the given one
+ *
+ * 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