blob: a6e81523a97edbc275f6f1d22112303bbc09142f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2019 THALES GLOBAL SERVICES.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.diagram.business.internal.metamodel.helper;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EStructuralFeature.Setting;
import org.eclipse.sirius.business.api.componentization.ViewpointRegistry;
import org.eclipse.sirius.common.tools.api.util.EqualityHelper;
import org.eclipse.sirius.diagram.DDiagram;
import org.eclipse.sirius.diagram.DDiagramElement;
import org.eclipse.sirius.diagram.DDiagramElementContainer;
import org.eclipse.sirius.diagram.DNode;
import org.eclipse.sirius.diagram.business.api.componentization.DiagramComponentizationManager;
import org.eclipse.sirius.diagram.business.api.componentization.DiagramMappingsManager;
import org.eclipse.sirius.diagram.business.internal.metamodel.description.operations.EdgeMappingImportWrapper;
import org.eclipse.sirius.diagram.description.AdditionalLayer;
import org.eclipse.sirius.diagram.description.ContainerMapping;
import org.eclipse.sirius.diagram.description.DescriptionPackage;
import org.eclipse.sirius.diagram.description.DiagramDescription;
import org.eclipse.sirius.diagram.description.DiagramElementMapping;
import org.eclipse.sirius.diagram.description.EdgeMapping;
import org.eclipse.sirius.diagram.description.EdgeMappingImport;
import org.eclipse.sirius.diagram.description.IEdgeMapping;
import org.eclipse.sirius.diagram.description.Layer;
import org.eclipse.sirius.diagram.description.NodeMapping;
import org.eclipse.sirius.viewpoint.description.AbstractMappingImport;
import org.eclipse.sirius.viewpoint.description.DecorationDescription;
import org.eclipse.sirius.viewpoint.description.Viewpoint;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
/**
* An helper to deal with layers stuffs.
*
* @author mchauvin
*/
public final class LayerHelper {
private static final Map<DiagramMappingsManager, Map<DiagramElementMapping, Collection<Layer>>> ACTIVE_PARENT_LAYER_CACHE = new ConcurrentHashMap<>();
/**
* Avoid instantiation.
*/
private LayerHelper() {
}
/**
* Get all the layers of a diagram description.
*
* @param description
* the diagram description
* @return all the layers
*/
public static EList<Layer> getAllLayers(final DiagramDescription description) {
final Collection<Layer> layers = new ArrayList<Layer>();
if (description.getDefaultLayer() != null) {
layers.add(description.getDefaultLayer());
}
layers.addAll(description.getAdditionalLayers());
return new BasicEList<>(layers);
}
/**
* return all Layers which use this mapping if available.
*
* @param mapping
* any {@link DiagramElementMapping}.
* @return a containing Layer if available.
*/
public static Collection<Layer> getParentLayers(final DiagramElementMapping mapping) {
final Set<Layer> layers = new LinkedHashSet<Layer>();
/* add containing layer */
final Layer parentLayer = LayerHelper.getContainingLayer(mapping);
if (parentLayer != null) {
layers.add(parentLayer);
}
/* add layers which reuse this mapping */
final EObject registryMappingInstance = ViewpointRegistry.getInstance().find(mapping);
final Collection<Setting> settings = ViewpointRegistry.getInstance().getCrossReferencer().getInverseReferences(registryMappingInstance);
for (final Setting setting : settings) {
final EObject eReferencer = setting.getEObject();
final EStructuralFeature eFeature = setting.getEStructuralFeature();
if (eReferencer instanceof Layer && eFeature.equals(DescriptionPackage.eINSTANCE.getLayer_ReusedMappings())) {
layers.add((Layer) eReferencer);
} else if (eFeature.equals(DescriptionPackage.eINSTANCE.getContainerMapping_ReusedNodeMappings())
|| eFeature.equals(DescriptionPackage.eINSTANCE.getContainerMapping_ReusedContainerMappings())
|| eFeature.equals(DescriptionPackage.eINSTANCE.getAbstractNodeMapping_ReusedBorderedNodeMappings())) {
final Layer eReferencerLayer = LayerHelper.getContainingLayer((DiagramElementMapping) eReferencer);
final Layer mappingSourceLayer = LayerHelper.getContainingLayer(mapping);
// add eReferencerLayer if reusedMapping and its re-user are not
// in the same diagram.
if (eReferencerLayer != null && !layers.contains(eReferencerLayer) && !EqualityHelper.areEquals(mappingSourceLayer.eContainer(), eReferencerLayer.eContainer())) {
layers.add(eReferencerLayer);
}
}
}
/* add layers which reuse a container of this mapping */
if (mapping.eContainer() instanceof ContainerMapping) {
layers.addAll(LayerHelper.getParentLayers((ContainerMapping) mapping.eContainer()));
}
return layers;
}
/**
* return the layer which contains this mapping if available.
*
* @param mapping
* the diagram element mapping
* @return the layer containing if there is one, <code>null</code> otherwise
*/
public static Layer getContainingLayer(final DiagramElementMapping mapping) {
EObject current = mapping;
while (current != null) {
current = current.eContainer();
if (current instanceof Layer) {
return (Layer) current;
}
}
return null;
}
/**
* return a containing Layer if available.
*
* @param decorationDescription
* any {@link DecorationDescription}.
* @return a containing Layer if available.
*/
public static Layer getParentLayer(final DecorationDescription decorationDescription) {
EObject current = decorationDescription;
while (current != null) {
current = current.eContainer();
if (current instanceof Layer) {
return (Layer) current;
}
}
return null;
}
/**
* Check if a diagram element is in an activated layer or not and visible.
*
* @param mappingsManager
* the DiagramMappingsManager of the current diagram.
* @param element
* the diagram element.
* @return <code>true</code> if it is, <code>false</code> otherwise
*/
public static boolean isInActivatedLayer(DiagramMappingsManager mappingsManager, final DDiagramElement element) {
return isInActivatedLayer(mappingsManager, element, element.getParentDiagram());
}
/**
* Check if a diagram element is in an activated layer or not and visible.
*
* @param mappingsManager
* the DiagramMappingsManager of the current diagram.
* @param element
* the diagram element.
* @param parentDiagram
* the parent diagram of the diagram element. This information can be retrieved from the diagram element
* but sometimes it is already known by the caller or it can be null (during drag'n'drop of element with
* bordered nodes for example : PortLocationAfterDragAndDropTest.
* testPortLocationFromParentDnDFromModelExplorerView()) this method is called before setting all parents
* hierarchy of diagram element.
* @return <code>true</code> if it is, <code>false</code> otherwise
*/
public static boolean isInActivatedLayer(DiagramMappingsManager mappingsManager, final DDiagramElement element, final DDiagram parentDiagram) {
final DiagramElementMapping mapping = element.getDiagramElementMapping();
if (!LayerHelper.withoutLayersMode(mapping)) {
final DDiagram diagram;
if (parentDiagram != null) {
diagram = parentDiagram;
} else {
diagram = element.getParentDiagram();
}
boolean visible = false;
if (diagram != null && getActiveParentLayers(mappingsManager, mapping).size() > 0) {
/*
* We are visible in the following cases: 1. the mapping is in active layer and not hidden by owner
* mapping in an active layer and container is diagram 2- the mapping is in active layer and not hidden
* by owner mapping in an active layer and container is element and element.mapping contains mapping and
* element is visible
*/
/*
* Check that mapping is not hidden by an importer mapping
*/
final EObject registryMappingInstance = ViewpointRegistry.getInstance().find(mapping);
final Collection<Setting> settings = ViewpointRegistry.getInstance().getCrossReferencer().getInverseReferences(registryMappingInstance, true);
if (!LayerHelper.hideSubMappingsInImporters(mappingsManager, diagram, settings, mapping)) {
final EObject container = element.eContainer();
/*
* Case 2 The mapping should be imported by another mapping owned by a visible element.
*/
if (container instanceof DDiagramElement && LayerHelper.isInActivatedLayer(mappingsManager, (DDiagramElement) container, parentDiagram)) {
visible = LayerHelper.caseDiagramElementContainer((DDiagramElement) container, mapping);
}
/*
* Case 1 The mapping should be in visible mappings set
*/
else if (container instanceof DDiagram) {
visible = LayerHelper.caseDiagramContainer(diagram, mapping);
} else if (container == null) {
// We consider the diagram as future container.
visible = LayerHelper.caseDiagramContainer(parentDiagram, mapping);
}
}
}
return visible;
}
return true;
}
private static boolean caseDiagramElementContainer(final DDiagramElement container, final DiagramElementMapping mapping) {
final DiagramElementMapping containerMapping = container.getDiagramElementMapping();
if (MappingHelper.getAllMappings(containerMapping).contains(mapping)) {
return true;
}
return false;
}
private static boolean caseDiagramContainer(final DDiagram diagram, final DiagramElementMapping mapping) {
for (Layer activatedLayer : diagram.getActivatedLayers()) {
if (EqualityHelper.contains(LayerHelper.getAllLayerMappings(activatedLayer), mapping)) {
return true;
}
}
return false;
}
private static boolean hideSubMappingsInImporters(final DiagramMappingsManager session, final DDiagram diagram, final Collection<Setting> settings, final DiagramElementMapping mapping) {
boolean hide = false;
for (final Setting setting : settings) {
final EObject eObject = setting.getEObject();
final EStructuralFeature eFeature = setting.getEStructuralFeature();
if (eObject instanceof AbstractMappingImport && eObject instanceof DiagramElementMapping) {
if (eFeature == DescriptionPackage.eINSTANCE.getContainerMappingImport_ImportedMapping() || eFeature == DescriptionPackage.eINSTANCE.getNodeMappingImport_ImportedMapping()) {
final DiagramElementMapping importerMapping = (DiagramElementMapping) eObject;
if (((AbstractMappingImport) importerMapping).isHideSubMappings() && LayerHelper.isInActivatedLayer(session, diagram, importerMapping)) {
hide = true;
break;
} else {
final Collection<Setting> importerSettings = ViewpointRegistry.getInstance().getCrossReferencer().getInverseReferences(importerMapping);
if (LayerHelper.hideSubMappingsInImporters(session, diagram, importerSettings, importerMapping)) {
hide = true;
break;
}
}
}
}
}
return hide;
}
private static Collection<DiagramElementMapping> getAllLayerMappings(final Layer layer) {
final Collection<DiagramElementMapping> result = new HashSet<DiagramElementMapping>();
result.addAll(layer.getNodeMappings());
result.addAll(layer.getContainerMappings());
result.addAll(layer.getEdgeMappings());
// Add the wrapper of EdgeMappingImport
final Iterator<EdgeMappingImport> iterMappingImport = layer.getEdgeMappingImports().iterator();
while (iterMappingImport.hasNext()) {
result.add(EdgeMappingImportWrapper.getWrapper(iterMappingImport.next()));
}
result.addAll(ContentLayerHelper.getReuseMappings(layer));
return result;
}
/**
* Check if a diagram element mapping is in an activated layer or not.
*
* @param mappingsManager
* the DiagramMappingsManager of the current diagram.
* @param mapping
* the diagram element mapping.
* @param diagram
* the diagram.
* @return <code>true</code> if it is, <code>false</code> otherwise
*/
public static boolean isInActivatedLayer(DiagramMappingsManager mappingsManager, final DDiagram diagram, final DiagramElementMapping mapping) {
if (!LayerHelper.withoutLayersMode(mapping)) {
boolean visible = false;
final Collection<Layer> layers = getActiveParentLayers(mappingsManager, mapping);
for (final Layer layer : layers) {
if (EqualityHelper.contains(diagram.getActivatedLayers(), layer)) {
final EObject registryMappingInstance = ViewpointRegistry.getInstance().find(mapping);
final Collection<Setting> settings = ViewpointRegistry.getInstance().getCrossReferencer().getInverseReferences(registryMappingInstance);
if (!LayerHelper.hideSubMappingsInImporters(mappingsManager, diagram, settings, mapping)) {
visible = true;
break;
}
}
}
return visible;
}
return true;
}
/**
* Enable or disable the ability to cache the computed parent layers for the given DiagramMappingManager. The cache
* is cleared when this method is called to disable the cache.
*
* @param mappingsManager
* DiagramMappingsManager of the current diagram.
* @param enable
* <code>true</code> to allow this helper to put the computed active parent layers in a cache,
* <code>false</code> otherwise.
*/
public static synchronized void setActiveParentLayersCacheEnabled(DiagramMappingsManager mappingsManager, boolean enable) {
if (enable && !ACTIVE_PARENT_LAYER_CACHE.containsKey(mappingsManager)) {
ACTIVE_PARENT_LAYER_CACHE.put(mappingsManager, new ConcurrentHashMap<>());
}
if (!enable) {
ACTIVE_PARENT_LAYER_CACHE.remove(mappingsManager);
}
}
private static Collection<Layer> getActiveParentLayers(DiagramMappingsManager mappingsManager, final DiagramElementMapping mapping) {
Map<DiagramElementMapping, Collection<Layer>> managerCache = ACTIVE_PARENT_LAYER_CACHE.get(mappingsManager);
if (managerCache != null) {
Collection<Layer> cachedValue = managerCache.get(mapping);
if (cachedValue == null) {
cachedValue = mappingsManager.getActiveParentLayers(mapping);
managerCache.put(mapping, cachedValue);
}
return cachedValue;
}
return mappingsManager.getActiveParentLayers(mapping);
}
/**
* Check if a diagram element mapping is in an activated layer or not.
*
* @param mapping
* the diagram element mapping.
* @param diagram
* the diagram.
* @return <code>true</code> if it is, <code>false</code> otherwise
*/
public static boolean isInActivatedLayer(final DDiagram diagram, final IEdgeMapping mapping) {
boolean found = false;
if (!found && (diagram.getDescription().getEdgeMappings().contains(mapping) || diagram.getDescription().getEdgeMappingImports().contains(mapping)
|| LayerHelper.containsWrapped(diagram.getDescription().getEdgeMappingImports(), mapping))) {
found = true;
}
for (Layer layer : diagram.getActivatedLayers()) {
if (layer.getEdgeMappings().contains(mapping) || layer.getEdgeMappingImports().contains(mapping) || LayerHelper.containsWrapped(layer.getEdgeMappingImports(), mapping)) {
found = true;
break;
}
}
return found;
}
/**
* @param edgeMappingImports
* @param mapping
* @return
*/
private static boolean containsWrapped(final EList<EdgeMappingImport> edgeMappingImports, final IEdgeMapping mapping) {
for (EdgeMappingImport edgeMappingImport : edgeMappingImports) {
if (mapping.equals(EdgeMappingImportWrapper.getWrapper(edgeMappingImport))) {
return true;
}
}
return false;
}
/*
* mch : may be useful do not delete elements
*/
/*
* public static boolean areOnSameLayers(final DiagramElementMapping mapping1, final DiagramElementMapping mapping2)
* { if (!LayerHelper.withoutLayersMode(mapping1)) { final Layer layer1 = LayerHelper.getParentLayer(mapping1);
* final Layer layer2 = LayerHelper.getParentLayer(mapping1); } return true; }
*/
/**
* Check if are in the mode without layers.
*
* @param mapping
* any {@link DiagramElementMapping}
* @return <code>true</code> if we are in the without layer mode, <code>false</code> otherwise
*/
public static boolean withoutLayersMode(final DiagramElementMapping mapping) {
return LayerHelper.getContainingLayer(mapping) == null;
}
/**
* Update the actual mapping of the element with mapping given as parameter.
*
* @param element
* the diagram element to update
* @param mapping
* the mapping to use
*/
public static void updateActualMapping(final DDiagramElement element, final DiagramElementMapping mapping) {
if (element instanceof DNode && mapping instanceof NodeMapping) {
DNode node = (DNode) element;
if (node.getActualMapping() != mapping) {
node.setActualMapping((NodeMapping) mapping);
}
} else if (element instanceof DDiagramElementContainer && mapping instanceof ContainerMapping) {
DDiagramElementContainer ddec = (DDiagramElementContainer) element;
if (ddec.getActualMapping() != mapping) {
ddec.setActualMapping((ContainerMapping) mapping);
}
}
}
/**
* Search in this diagram (or in activated layer of this diagram) if there is one EdgeMappingImport which import
* edgeMapping.
*
* @param edgeMapping
* the edgeMapping to possibly refine (with importMapping)
* @param diagram
* the diagram in which search the list of EdgeMappingImport to check
* @return the best EdgeMapping
*/
public static EdgeMapping getBestMapping(final EdgeMapping edgeMapping, final DDiagram diagram) {
final EList<EdgeMappingImportWrapper> edgeMappingImportWrappers = new BasicEList<EdgeMappingImportWrapper>();
// Add wrapper form EdgeMappingImport of DiagramDescription
for (EdgeMappingImport edgeMappingImport : diagram.getDescription().getEdgeMappingImports()) {
edgeMappingImportWrappers.add(EdgeMappingImportWrapper.getWrapper(edgeMappingImport));
}
// Add wrapper from EdgeMappingImport of activated layers
for (final Layer layer : diagram.getActivatedLayers()) {
for (final EdgeMapping otherEdgeMapping : ContentLayerHelper.getAllEdgeMappings(layer)) {
if (otherEdgeMapping instanceof EdgeMappingImportWrapper) {
edgeMappingImportWrappers.add((EdgeMappingImportWrapper) otherEdgeMapping);
}
}
}
// If the edgeMapping is a EdgeMappingImportWrapper, verify if it's
// available.
EdgeMapping result;
if (edgeMapping instanceof EdgeMappingImportWrapper && !edgeMappingImportWrappers.contains(edgeMapping)) {
result = null;
} else {
result = LayerHelper.getBestMapping(edgeMapping, edgeMappingImportWrappers);
}
return result;
}
/**
* Search in the list of EdgeMappingImportWrapper if there is one which import edgeMapping.
*
* @param edgeMapping
* the edgeMapping to possibly refine (with importMapping)
* @param edgeMappingImportWrappers
* List of EdgeMappingImport to check
* @return the best EdgeMapping
*/
public static EdgeMapping getBestMapping(final EdgeMapping edgeMapping, final EList<EdgeMappingImportWrapper> edgeMappingImportWrappers) {
EdgeMapping result = edgeMapping;
for (EdgeMappingImportWrapper edgeMappingImportWrapper : edgeMappingImportWrappers) {
if (LayerHelper.isImported(edgeMapping, edgeMappingImportWrapper.getWrappedEdgeMappingImport())) {
result = LayerHelper.getBestMapping(edgeMappingImportWrapper, edgeMappingImportWrappers);
break;
}
}
return result;
}
/**
* Check if an edgeMappingImport imports another or not.
*
* @param searchImportedMapping
* The potential imported edge mapping.
* @param edgeMappingImport
* The importer
* @return true is the EdgeMappingImport imports the searchImportedMapping.
*/
public static boolean isImported(final IEdgeMapping searchImportedMapping, final EdgeMappingImport edgeMappingImport) {
boolean result = false;
final IEdgeMapping importedMapping = edgeMappingImport.getImportedMapping();
IEdgeMapping mappingToCompare = searchImportedMapping;
if (searchImportedMapping instanceof EdgeMappingImportWrapper) {
mappingToCompare = ((EdgeMappingImportWrapper) searchImportedMapping).getWrappedEdgeMappingImport();
}
if (importedMapping == null) {
result = false;
} else if (importedMapping.equals(mappingToCompare)) {
result = true;
} else if (importedMapping instanceof EdgeMappingImport) {
result = LayerHelper.isImported(searchImportedMapping, (EdgeMappingImport) importedMapping);
} else {
result = importedMapping.equals(searchImportedMapping);
}
return result;
}
/**
* Check if this layer contains only tools or not.
*
* @param layer
* the layer to check
* @return <code>true</code> if it contains only tools, <code>false</code> otherwise
*/
public static boolean containsOnlyTools(final Layer layer) {
final boolean containsMappings = containsMappings(layer);
final boolean isNoDecorationDescritionSet = layer.getDecorationDescriptionsSet() == null || layer.getDecorationDescriptionsSet().getDecorationDescriptions().isEmpty();
return !containsMappings && isNoDecorationDescritionSet;
}
private static boolean containsMappings(final Layer layer) {
final boolean isNoMapping = layer.getContainerMappings().isEmpty() && layer.getEdgeMappings().isEmpty() && layer.getNodeMappings().isEmpty();
final boolean isNoImportOrReusedMapping = layer.getEdgeMappingImports().isEmpty() && layer.getReusedMappings().isEmpty();
return !(isNoMapping && isNoImportOrReusedMapping);
}
/**
* Check if this layer is considered as Transient. A transient layer is an additional layer that contains at most
* tools or decorationDescription.
*
* @param layer
* the layer to check
* @return <code>true</code> if it is transient, <code>false</code> otherwise
*/
public static boolean isTransientLayer(final Layer layer) {
if (layer instanceof AdditionalLayer) {
boolean containsMappings = containsMappings(layer);
boolean containsCusto = layer.getCustomization() != null && !layer.getCustomization().getVsmElementCustomizations().isEmpty();
return !containsMappings && !containsCusto;
}
return false;
}
/**
* Get the transient or non transient layers to activate.
*
* @param diagDescription
* the {@link DiagramDescription} to get the layers from
* @param viewpointsFilter
* viewpoints to consider
* @param layersToActivate
* returned non transient layers to activate
* @param transientLayersToActivate
* returned transient layers to activate
*/
public static void getInitialActiveLayers(DiagramDescription diagDescription, Collection<Viewpoint> viewpointsFilter, List<Layer> layersToActivate,
List<AdditionalLayer> transientLayersToActivate) {
final Predicate<Layer> isActiveByDefault = new Predicate<Layer>() {
@Override
public boolean apply(final Layer layer) {
boolean result = true;
if (layer instanceof AdditionalLayer) {
AdditionalLayer additionalLayer = (AdditionalLayer) layer;
result = additionalLayer.isActiveByDefault() || !additionalLayer.isOptional();
}
return result;
}
};
getFilteredLayers(diagDescription, viewpointsFilter, layersToActivate, transientLayersToActivate, isActiveByDefault);
}
private static void getFilteredLayers(DiagramDescription diagDescription, Collection<Viewpoint> viewpointsFilter, List<Layer> layers, List<AdditionalLayer> transientLayers,
Predicate<Layer> predicate) {
Collection<Layer> allLayers = new ArrayList<Layer>(new DiagramComponentizationManager().getAllLayers(viewpointsFilter, diagDescription));
Collection<Layer> allActivatedLayers = Collections2.filter(allLayers, predicate);
allActivatedLayers.addAll(Collections2.filter(DiagramComponentizationHelper.getContributedLayers(diagDescription, viewpointsFilter), predicate));
for (Layer layer : allActivatedLayers) {
if (LayerHelper.isTransientLayer(layer)) {
transientLayers.add((AdditionalLayer) layer);
} else {
layers.add(layer);
}
}
}
/**
* Get, from descriptions, the list of mandatories layers.
*
* @param diagDescription
* the {@link DiagramDescription} to get the layers from
* @param viewpointsFilter
* viewpoints to consider
* @param layers
* returned non transient layers to activate
* @param transientLayers
* returned transient layers to activate
*/
public static void getMandatoriesAdditionalLayers(DiagramDescription diagDescription, Collection<Viewpoint> viewpointsFilter, List<Layer> layers, List<AdditionalLayer> transientLayers) {
final Predicate<Layer> isMandatory = new Predicate<Layer>() {
@Override
public boolean apply(final Layer layer) {
return (layer instanceof AdditionalLayer) && !((AdditionalLayer) layer).isOptional();
}
};
getFilteredLayers(diagDescription, viewpointsFilter, layers, transientLayers, isMandatory);
}
}