diff --git a/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/FlowNodeService.java b/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/FlowNodeService.java index 7baddcdc..ddf3bc4e 100644 --- a/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/FlowNodeService.java +++ b/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/FlowNodeService.java @@ -22,16 +22,18 @@ import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.sirius.diagram.AbstractDNode; +import org.eclipse.sirius.diagram.DDiagram; +import org.eclipse.sirius.diagram.DNode; import org.eclipse.sirius.diagram.description.AbstractNodeMapping; import org.eclipse.sirius.diagram.description.ContainerMapping; import org.eclipse.sirius.diagram.description.Layer; import org.eclipse.sirius.diagram.description.NodeMapping; +import org.obeonetwork.bpmn2.design.refactoring.SiriusElementRefactor; import org.obeonetwork.bpmn2.design.ui.PopupChoiceSelector; import org.obeonetwork.bpmn2.design.ui.UiConstants; import org.obeonetwork.dsl.bpmn2.Bpmn2Package; import org.obeonetwork.dsl.bpmn2.Gateway; - /** * Conversion services to operate on FlowNode objects. * @@ -39,14 +41,12 @@ * */ public class FlowNodeService { - + private static final Bpmn2Package PKG = Bpmn2Package.eINSTANCE; private static final List GATEWAY_CLASSES = Arrays.asList( // Order is the same to Gateways Toolsection of VSM - PKG.getParallelGateway(), PKG.getExclusiveGateway(), - PKG.getInclusiveGateway(), PKG.getComplexGateway(), - PKG.getEventBasedGateway() - ); + PKG.getParallelGateway(), PKG.getExclusiveGateway(), PKG.getInclusiveGateway(), PKG.getComplexGateway(), + PKG.getEventBasedGateway()); /** * Converts a Gateway into another kind. @@ -54,7 +54,7 @@ public class FlowNodeService { * Most of related data are restored. *

* - * @param view of Gateway + * @param view of Gateway * @param eClass class to convert in * @return created Gateway */ @@ -63,46 +63,57 @@ public static Gateway convertToSpecificGateway(AbstractDNode view, EClass eClass if (eClass.equals(previous.eClass())) { return previous; } - return (Gateway) new SiriusElementRefactor(view) { @Override protected boolean isTransferable(EStructuralFeature feature, EClass targetType) { return Bpmn2Package.eINSTANCE.getBaseElement_Id() != feature; } - + @Override protected AbstractNodeMapping getApplicableNodeMapping(AbstractNodeMapping previous, EObject current) { - // Gateways have several mappings. (For no good reason: only image) - return current instanceof Gateway - ? getGatewayMapping(previous.eContainer(), current) - : previous; + // Gateways have several mappings. + return current instanceof Gateway ? getGatewayMapping(previous, current) : previous; + } + + @Override + protected void postCreateNewNode(AbstractDNode newDNode, boolean isExternalLabel) { + if (newDNode instanceof DNode && isExternalLabel) { + ServiceHelper.setExternalLabel((DNode) newDNode); + } } }.transformInto(eClass); } - - private static AbstractNodeMapping getGatewayMapping(EObject mappingOwner, EObject target) { + + private static AbstractNodeMapping getGatewayMapping(AbstractNodeMapping previous, EObject target) { + if (previous.getName().endsWith("Gateway")) { + String classname = "bpmn2." + target.eClass().getName(); // Notation used in ODesign + return getMapping(previous.eContainer(), classname); + } else { + String classname = "bpmn2.BaseElement"; // Notation used in ODesign + return getMapping(previous.eContainer(), classname); + } + } + + private static AbstractNodeMapping getMapping(EObject mappingOwner, String className) { List siblingMappings = Collections.emptyList(); if (mappingOwner instanceof ContainerMapping) { siblingMappings = ((ContainerMapping) mappingOwner).getSubNodeMappings(); } else if (mappingOwner instanceof Layer) { siblingMappings = ((Layer) mappingOwner).getNodeMappings(); } - String classname = "bpmn2." + target.eClass().getName(); // Notation used in ODesign for (NodeMapping mapping : siblingMappings) { - if (Objects.equals(mapping.getDomainClass(), classname)) { + if (Objects.equals(mapping.getDomainClass(), className)) { return mapping; } } return null; } - - /** * Applies a function with a type choosen by user. * - * @param it context - * @param types to choose from + * @param it context + * @param types to choose from * @param transform to apply */ public static T applyToChoosableClass(EObject it, List types, Function transform) { @@ -118,20 +129,19 @@ public static T applyToChoosableClass(EObject it, List types, Functi } return transform.apply(target.get()); } - + /** * Converts a gateway in a new type. *

* If no gateway is selected, whole operation is aborted. *

* - * @param it gateway to convert + * @param it gateway to convert * @param view on which the task is triggered * @return new gateway */ public static Gateway convertToChoosableGateway(Gateway it, AbstractDNode view) { - return FlowNodeService.applyToChoosableClass(it, GATEWAY_CLASSES, - type -> convertToSpecificGateway(view, type)); + return FlowNodeService.applyToChoosableClass(it, GATEWAY_CLASSES, type -> convertToSpecificGateway(view, type)); } } diff --git a/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/ServiceHelper.java b/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/ServiceHelper.java index 6de831ff..a9a8ea36 100644 --- a/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/ServiceHelper.java +++ b/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/ServiceHelper.java @@ -51,7 +51,6 @@ import org.obeonetwork.dsl.bpmn2.SubProcess; import org.obeonetwork.dsl.bpmn2.Task; - /** * Services for BPMN Viewpoints. * @@ -82,7 +81,8 @@ public static ECrossReferenceAdapter getCrossReferenceAdapter(EObject eo) { /** * Logs a message with called message. *

- * This method should only be used will editing VSM. It must be used any released version. + * This method should only be used will editing VSM. It must be used any + * released version. *

* * @param eObject @@ -113,12 +113,10 @@ public static Definitions getDefinitionsObject(EObject it) { public static Process getProcess(EObject it) { return getAncestor(Process.class, it); } - + @SuppressWarnings("unchecked") private static T getAncestor(Class type, EObject it) { - return it == null || type.isInstance(it) - ? (T) it - : getAncestor(type, it.eContainer()); + return it == null || type.isInstance(it) ? (T) it : getAncestor(type, it.eContainer()); } public static List getElementsWithExternalLabel(DNodeContainer dNodeContainer) { @@ -128,14 +126,12 @@ public static List getElementsWithExternalLabel(DNodeContainer dNod while (it.hasNext()) { DDiagramElement dde = it.next(); Object bpmnElement = dde.getTarget(); - if ((bpmnElement instanceof Event) - || (bpmnElement instanceof Gateway) + if ((bpmnElement instanceof Event) || (bpmnElement instanceof Gateway) || (bpmnElement instanceof ItemAwareElement)) { if (!(bpmnElement instanceof BoundaryEvent) && isExternalLabel((DNode) dde)) { result.add((BaseElement) bpmnElement); } - } else if ((bpmnElement instanceof Task) - || (bpmnElement instanceof SubProcess) + } else if ((bpmnElement instanceof Task) || (bpmnElement instanceof SubProcess) || (bpmnElement instanceof CallActivity)) { DNodeContainer dNodeTask = (DNodeContainer) dde; for (DDiagramElement subDDE : dNodeTask.getElements()) { @@ -152,18 +148,35 @@ public static List getElementsWithExternalLabel(DNodeContainer dNod } public static boolean isExternalLabel(DNode dNode) { + if (dNode != null && ServiceHelper.IS_EXTERNAL_LABEL.equals(dNode.getTooltipText())) { + //Crapy code used when the type of a Task or a Gateway is changed. + if (dNode.getStyle() != null || dNode.getOwnedStyle().getCustomFeatures() != null) { + setExternalLabel(dNode); + } else { + return true; + } + } if (dNode == null || dNode.getOwnedStyle() == null || dNode.getOwnedStyle().getCustomFeatures() == null) { return false; } return dNode.getOwnedStyle().getCustomFeatures().contains(ServiceHelper.IS_EXTERNAL_LABEL); } + public static void setExternalLabelInTooltip(DNode dNode) { + //Crapy code used when the type of a Task or a Gateway is changed. + dNode.setTooltipText(IS_EXTERNAL_LABEL); + } + public static void setExternalLabel(DNode dNode) { dNode.getStyle().getCustomFeatures().add(IS_EXTERNAL_LABEL); + //Crapy code used when the type of a Task or a Gateway is changed. + setExternalLabelInTooltip(dNode); } public static void setInternalLabel(DNode dNode) { dNode.getStyle().getCustomFeatures().remove(IS_EXTERNAL_LABEL); + //Crapy code used when the type of a Task or a Gateway is changed. + dNode.setTooltipText(""); } public static boolean isDefaultPath(DEdge dEdge) { @@ -176,7 +189,7 @@ public static boolean isDefaultPath(DEdge dEdge) { } return result; } - + private static SequenceFlow getGatewayDefault(Gateway it) { SequenceFlow result = null; if (it instanceof InclusiveGateway) { @@ -192,7 +205,7 @@ private static SequenceFlow getGatewayDefault(Gateway it) { /** * Moves a flow node from a container to another container. * - * @param element to mode + * @param element to mode * @param oldSemanticContainer old container * @param newSemanticContainer new container */ @@ -214,7 +227,7 @@ private List getFlowNodeContainment(EObject container) { // no empty list to indicate containment is not possible return null; } - + public List getFlowNodeElements(EObject container, String className) { Class clazz = null; try { @@ -223,30 +236,22 @@ public List getFlowNodeElements(EObject container, String className) { Activator.log(IStatus.ERROR, e.getMessage(), e); return Collections.emptyList(); } - + List elements = getFlowNodeContainment(container); - return elements == null - ? Collections.emptyList() - : elements.stream() - .filter(clazz::isInstance) - .map(FlowNode.class::cast) - .collect(Collectors.toList()); + return elements == null ? Collections.emptyList() + : elements.stream().filter(clazz::isInstance).map(FlowNode.class::cast).collect(Collectors.toList()); } public List getDataInputs(EObject container) { InputOutputSpecification ioSpec = getIoSpecification(container); - return ioSpec != null - ? ioSpec.getDataInputs() - : Collections.emptyList(); + return ioSpec != null ? ioSpec.getDataInputs() : Collections.emptyList(); } public List getDataOutputs(EObject container) { InputOutputSpecification ioSpec = getIoSpecification(container); - return ioSpec != null - ? ioSpec.getDataOutputs() - : Collections.emptyList(); + return ioSpec != null ? ioSpec.getDataOutputs() : Collections.emptyList(); } - + private InputOutputSpecification getIoSpecification(EObject container) { if (container instanceof Lane && ((Lane) container).getPartitionElement() instanceof InputOutputSpecification) { return (InputOutputSpecification) ((Lane) container).getPartitionElement(); @@ -255,7 +260,7 @@ private InputOutputSpecification getIoSpecification(EObject container) { } return null; } - + /** * Aborts a Sirius operation. * @@ -264,7 +269,7 @@ private InputOutputSpecification getIoSpecification(EObject container) { */ public static void abortOperation(EObject context) throws OperationCanceledException { TransactionalEditingDomain edt = Session.of(context).get().getTransactionalEditingDomain(); - // Cancel Operation does not cancel !! + // Cancel Operation does not cancel !! // ChangeContext catches them all !! // What a trainer ... ((InternalTransactionalEditingDomain) edt).getActiveTransaction().abort(Status.CANCEL_STATUS); diff --git a/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/TaskService.java b/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/TaskService.java index 765de8fe..87330f3f 100644 --- a/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/TaskService.java +++ b/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/TaskService.java @@ -20,6 +20,7 @@ import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.sirius.diagram.DDiagramElementContainer; import org.eclipse.sirius.diagram.DNodeContainer; +import org.obeonetwork.bpmn2.design.refactoring.SiriusElementRefactor; import org.obeonetwork.dsl.bpmn2.Bpmn2Package; import org.obeonetwork.dsl.bpmn2.BusinessRuleTask; import org.obeonetwork.dsl.bpmn2.CallActivity; diff --git a/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/refactoring/DiagramModifier.java b/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/refactoring/DiagramModifier.java new file mode 100644 index 00000000..fca14e52 --- /dev/null +++ b/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/refactoring/DiagramModifier.java @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2024 Obeo. + * 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: + * Obeo - initial API and implementation + * + */ +package org.obeonetwork.bpmn2.design.refactoring; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.sirius.business.api.session.Session; +import org.eclipse.sirius.common.tools.api.interpreter.IInterpreter; +import org.eclipse.sirius.common.tools.api.util.RefreshIdsHolder; +import org.eclipse.sirius.diagram.DSemanticDiagram; +import org.eclipse.sirius.diagram.EdgeTarget; +import org.eclipse.sirius.diagram.business.api.componentization.DiagramMappingsManager; +import org.eclipse.sirius.diagram.business.api.componentization.DiagramMappingsManagerRegistry; +import org.eclipse.sirius.diagram.business.internal.helper.decoration.DecorationHelperInternal; +import org.eclipse.sirius.diagram.business.internal.sync.DDiagramElementSynchronizer; +import org.eclipse.sirius.diagram.description.DiagramElementMapping; +import org.eclipse.sirius.diagram.description.EdgeMapping; +import org.eclipse.sirius.diagram.description.MappingBasedDecoration; +import org.eclipse.sirius.ecore.extender.business.api.accessor.ModelAccessor; +import org.eclipse.sirius.tools.api.SiriusPlugin; +import org.eclipse.sirius.tools.api.interpreter.InterpreterUtil; +import org.eclipse.sirius.viewpoint.description.SemanticBasedDecoration; + +/** + * + * @author nperansin + */ +@SuppressWarnings("restriction") +class DiagramModifier { + + final Session session; + final DSemanticDiagram parent; + final ModelAccessor accessor; + final IInterpreter interpreter; + final DDiagramElementSynchronizer dsync; + final DiagramMappingsManager mmapping; + final RefreshIdsHolder idFactory; + + Map> mappingsToEdgeTargets = null; + final Map> edgeToMappings = new HashMap<>(); + final Map> edgeToSemantics = new HashMap<>(); + + protected DiagramModifier(DSemanticDiagram diagram, Session session) { + this.session = session; + parent = diagram; + accessor = SiriusPlugin.getDefault().getModelAccessorRegistry().getModelAccessor(diagram); + mmapping = DiagramMappingsManagerRegistry.INSTANCE.getDiagramMappingsManager(session, diagram); + interpreter = InterpreterUtil.getInterpreter(diagram); + dsync = new DDiagramElementSynchronizer(diagram, interpreter, accessor); + idFactory = RefreshIdsHolder.getOrCreateHolder(diagram); + } + + protected void prepareEdges() { + if (mappingsToEdgeTargets != null) { + return; + } + mappingsToEdgeTargets = dsync.computeMappingsToEdgeTargets(session.getSelectedViewpoints(false)); + // Initialize cache + new DecorationHelperInternal(parent, interpreter, accessor).computeDecorations(mappingsToEdgeTargets, + edgeToSemantics, edgeToMappings); + // TODO update with new nodes ? + } + +} diff --git a/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/refactoring/EdgeLayout.java b/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/refactoring/EdgeLayout.java new file mode 100644 index 00000000..9f4fb073 --- /dev/null +++ b/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/refactoring/EdgeLayout.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2024 Obeo. + * 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: + * Obeo - initial API and implementation + * + */ +package org.obeonetwork.bpmn2.design.refactoring; + +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.View; +import org.eclipse.sirius.business.api.session.Session; +import org.eclipse.sirius.diagram.DEdge; +import org.eclipse.sirius.diagram.description.EdgeMapping; +import org.eclipse.sirius.diagram.ui.business.api.view.SiriusGMFHelper; + +/** + * + * @author nperansin + */ +class EdgeLayout extends RepresentationLayout { + + final EdgeLayoutData data; + + protected EdgeLayout(DEdge src, Session session) { + super(src, src.getParentDiagram(), session); + View view = SiriusGMFHelper.getGmfView(src, session); + data = view instanceof Edge ? new EdgeLayoutData(src, (Edge) view) : null; + } + + @Override + protected void update(DEdge target) { + View gmfView = SiriusGMFHelper.getGmfView(target, session); + if (gmfView instanceof Edge) { + data.update(target, (Edge) gmfView); + } + } + +} \ No newline at end of file diff --git a/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/refactoring/EdgeLayoutData.java b/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/refactoring/EdgeLayoutData.java new file mode 100644 index 00000000..d04d6f4a --- /dev/null +++ b/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/refactoring/EdgeLayoutData.java @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2024 Obeo. + * 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: + * Obeo - initial API and implementation + * + */ +package org.obeonetwork.bpmn2.design.refactoring; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.gmf.runtime.notation.Anchor; +import org.eclipse.gmf.runtime.notation.Bendpoints; +import org.eclipse.gmf.runtime.notation.ConnectorStyle; +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.FontStyle; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.sirius.diagram.DEdge; +import org.eclipse.sirius.diagram.EdgeStyle; + +/** + * + * @author nperansin + */ +class EdgeLayoutData { + + final Bendpoints bendPoints; + final Anchor sourceAnchor; + final Anchor targetAnchor; + final List labels; + final EdgeStyle oldSiriusStyle; + final EList oldGMFStyles; + + protected EdgeLayoutData(DEdge siriusSrc, Edge src) { + bendPoints = src.getBendpoints(); + sourceAnchor = src.getSourceAnchor(); + targetAnchor = src.getTargetAnchor(); + this.oldSiriusStyle = (siriusSrc instanceof DEdge) ? (EdgeStyle) siriusSrc.getStyle() : null; + this.oldGMFStyles = src.getStyles(); + + labels = ((List) src.getPersistedChildren()).stream() + .filter(it -> SiriusElementRefactorHelper.isGmfLabelNode(it)).map(Node.class::cast) + .collect(Collectors.toList()); + } + + /** + * Updates edge with layout + * + * @param edge to update + */ + protected void update(DEdge targetSiriusEdge, Edge edge) { + edge.setBendpoints(bendPoints); + edge.setSourceAnchor(sourceAnchor); + edge.setTargetAnchor(targetAnchor); + updateEdgeStyle(targetSiriusEdge, edge); + if (!labels.isEmpty()) { + updateLabels(edge); + } + } + + @SuppressWarnings("unchecked") + private void updateEdgeStyle(DEdge targetSiriusEdge, Edge targetEdge) { + if (oldSiriusStyle != null) { + SiriusElementRefactorHelper.updateSiriusEdgeStyle(oldSiriusStyle, (EdgeStyle) targetSiriusEdge.getStyle()); + } + + targetEdge.getStyles().forEach(newStyle -> { + Optional oldStyleOpt = oldGMFStyles.stream() + .filter(oldStyle -> oldStyle.getClass().equals(newStyle.getClass())).findFirst(); + + oldStyleOpt.ifPresent(oldStyle -> { + if (oldStyle instanceof FontStyle && newStyle instanceof FontStyle) { + SiriusElementRefactorHelper.updateFontStyle((FontStyle) oldStyle, (FontStyle) newStyle); + } else if (oldStyle instanceof ConnectorStyle && newStyle instanceof ConnectorStyle) { + SiriusElementRefactorHelper.updateConnectorStyle((ConnectorStyle) oldStyle, + (ConnectorStyle) newStyle); + } + }); + }); + } + + private void updateLabels(Edge edge) { + ((List) edge.getPersistedChildren()).stream().filter(it -> SiriusElementRefactorHelper.isGmfLabelNode(it)) + .map(Node.class::cast).forEach(it -> updateLabel(it)); + + } + + private void updateLabel(Node it) { + Node old = findLabel(it.getType()); + if (old != null) { + it.setLayoutConstraint(old.getLayoutConstraint()); + } + } + + private Node findLabel(String type) { + return labels.stream().filter(it -> Objects.equals(it.getType(), type)).findFirst().orElse(null); + } + +} diff --git a/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/refactoring/EndLayout.java b/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/refactoring/EndLayout.java new file mode 100644 index 00000000..aa548ed8 --- /dev/null +++ b/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/refactoring/EndLayout.java @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2024 Obeo. + * 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: + * Obeo - initial API and implementation + * + */ +package org.obeonetwork.bpmn2.design.refactoring; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.sirius.business.api.session.Session; +import org.eclipse.sirius.diagram.AbstractDNode; +import org.eclipse.sirius.diagram.DEdge; +import org.eclipse.sirius.diagram.EdgeTarget; + +/** + * + * @author nperansin + */ +class EndLayout extends EdgeLayout { + final boolean targetChange; + final EdgeTarget end; + final boolean outEnd; + final boolean endChange; + + protected EndLayout(DEdge src, AbstractDNode targeting, boolean out, Session session) { + super(src, session); + targetChange = src.getTarget() == targeting.getTarget(); + outEnd = out; + end = SiriusElementRefactorHelper.getEnd(src, out); + endChange = end == targeting; + } + + protected boolean match(DEdge it, AbstractDNode previous, AbstractDNode current) { + EObject expectedTarget = targetChange ? current.getTarget() : target.getTarget(); + EdgeTarget actualEnd = SiriusElementRefactorHelper.getEnd(it, outEnd); + EdgeTarget expectedEnd = endChange ? (EdgeTarget) current : end; + + return it.getTarget() == expectedTarget // proper target + && actualEnd == expectedEnd // proper end + && it.getMapping() == mapping; // proper mapping + } + +} \ No newline at end of file diff --git a/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/refactoring/NodeLayout.java b/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/refactoring/NodeLayout.java new file mode 100644 index 00000000..24c1ff46 --- /dev/null +++ b/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/refactoring/NodeLayout.java @@ -0,0 +1,208 @@ +/** + * Copyright (c) 2024 Obeo. + * 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: + * Obeo - initial API and implementation + * + */ +package org.obeonetwork.bpmn2.design.refactoring; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.emf.common.util.EList; +import org.eclipse.gmf.runtime.notation.Bounds; +import org.eclipse.gmf.runtime.notation.FontStyle; +import org.eclipse.gmf.runtime.notation.Location; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.View; +import org.eclipse.sirius.business.api.session.Session; +import org.eclipse.sirius.diagram.AbstractDNode; +import org.eclipse.sirius.diagram.DEdge; +import org.eclipse.sirius.diagram.DNode; +import org.eclipse.sirius.diagram.EdgeTarget; +import org.eclipse.sirius.diagram.FlatContainerStyle; +import org.eclipse.sirius.diagram.Square; +import org.eclipse.sirius.diagram.WorkspaceImage; +import org.eclipse.sirius.diagram.description.AbstractNodeMapping; +import org.eclipse.sirius.diagram.ui.business.api.view.SiriusGMFHelper; +import org.eclipse.sirius.viewpoint.Style; +import org.obeonetwork.bpmn2.design.Activator; +import org.obeonetwork.bpmn2.design.ServiceHelper; + +/** + * + * @author nperansin + */ +class NodeLayout extends RepresentationLayout { + + private static final String LABEL_NODE_TYPE = "5003"; + + final Bounds bounds; + final Location labelLocation; + final List borders; + final List outEdges; + final List inEdges; + final boolean isExternalLabel; + final Style siriusStyle; + final EList gmfStyles; + + protected NodeLayout(AbstractDNode src, Session session) { + super(src, src.eContainer(), session); + this.isExternalLabel = src instanceof DNode ? ServiceHelper.isExternalLabel((DNode) src) : false; + + Bounds bounds = null; + Location labelLocation = null; + EList gmfStyles = null; + View gmfView = SiriusGMFHelper.getGmfView(src, session); + if (gmfView instanceof Node) { + Node node = (Node) gmfView; + if (node.getLayoutConstraint() instanceof Bounds) { + bounds = (Bounds) node.getLayoutConstraint(); + for (Object o : node.getChildren()) { + if (o instanceof Node) { + Node subNode = (Node) o; + if (LABEL_NODE_TYPE.equals(subNode.getType()) + && subNode.getLayoutConstraint() instanceof Location) { + labelLocation = (Location) subNode.getLayoutConstraint(); + } + } + } + } + gmfStyles = node.getStyles(); + } + this.bounds = bounds; + this.labelLocation = labelLocation; + this.gmfStyles = gmfStyles; + this.siriusStyle = src.getStyle(); + + borders = src.getOwnedBorderedNodes().stream().map(it -> new NodeLayout(it, session)) + .collect(Collectors.toList()); + + if (src instanceof EdgeTarget) { + outEdges = ((EdgeTarget) src).getOutgoingEdges().stream().map(it -> new EndLayout(it, src, true, session)) + .collect(Collectors.toList()); + inEdges = ((EdgeTarget) src).getIncomingEdges().stream().map(it -> new EndLayout(it, src, false, session)) + .collect(Collectors.toList()); + } else { + outEdges = Collections.emptyList(); + inEdges = Collections.emptyList(); + } + } + + @Override + protected void update(AbstractDNode target) { + // Update bounds + View gmfView = SiriusGMFHelper.getGmfView(target, session); + if (gmfView instanceof Node) { + Node node = (Node) gmfView; + node.setLayoutConstraint(bounds); + for (Object o : node.getChildren()) { + if (o instanceof Node) { + Node subNode = (Node) o; + if (LABEL_NODE_TYPE.equals(subNode.getType())) { + subNode.setLayoutConstraint(labelLocation); + } + } + } + } + + updateNodeContainerStyle(target, gmfView instanceof Node ? (Node) gmfView : null); + + if (target instanceof DNode && isExternalLabel) { + //Crapy code to set that the DNode has an external label. + ServiceHelper.setExternalLabelInTooltip((DNode) target); + } + + // Update border + newView.getOwnedBorderedNodes().forEach(it -> updateBorder(it)); + + if (target instanceof EdgeTarget) { + ((EdgeTarget) target).getOutgoingEdges().stream().forEach(it -> updateEdge(it, outEdges, target)); + ((EdgeTarget) target).getIncomingEdges().stream().forEach(it -> updateEdge(it, inEdges, target)); + } + } + + @SuppressWarnings("unchecked") + private void updateNodeContainerStyle(AbstractDNode target, Node gmfNode) { + if (target != null && (target.getStyle() instanceof FlatContainerStyle) + && (siriusStyle instanceof FlatContainerStyle)) { + SiriusElementRefactorHelper.updateSiriusNodeContainerFlatContainerStyle((FlatContainerStyle) siriusStyle, + (FlatContainerStyle) target.getStyle()); + } else if (target != null && (target.getStyle() instanceof WorkspaceImage) + && (siriusStyle instanceof WorkspaceImage)) { + SiriusElementRefactorHelper.updateSiriusNodeContainerWorkspaceImageStyle((WorkspaceImage) siriusStyle, + (WorkspaceImage) target.getStyle()); + } else if (target != null && (target.getStyle() instanceof Square) && (siriusStyle instanceof Square)) { + SiriusElementRefactorHelper.updateSiriusNodeContainerSquareStyle((Square) siriusStyle, + (Square) target.getStyle()); + } else { + if (target == null || ((DNode) target).getStyle() == null) { + Activator.log(IStatus.ERROR, "Target or style is null: " + target.getStyle().getClass().getName() + "/" + + siriusStyle.getClass().getName(), null); + } else { + Activator.log(IStatus.ERROR, "Unknown types: " + target.getStyle().getClass().getName() + "/" + + siriusStyle.getClass().getName(), null); + } + } + + if (gmfNode != null) { + gmfNode.getStyles().forEach(newStyle -> { + Optional oldStyleOpt = gmfStyles.stream() + .filter(oldStyle -> oldStyle.getClass().equals(newStyle.getClass())).findFirst(); + + oldStyleOpt.ifPresent(oldStyle -> { + if (oldStyle instanceof FontStyle && newStyle instanceof FontStyle) { + SiriusElementRefactorHelper.updateFontStyle((FontStyle) oldStyle, (FontStyle) newStyle); + } + }); + }); + } + } + + protected void updateEdge(DEdge current, List layouts, AbstractDNode newNode) { + EndLayout layout = findEdgeLayout(current, layouts, newNode); + if (layout != null) { + layout.update(current); + } + } + + protected EndLayout findEdgeLayout(DEdge current, List layouts, AbstractDNode newNode) { + for (EndLayout layout : layouts) { + if (layout.match(current, target, newNode)) { + return layout; + } + } + return null; + } + + protected void updateBorder(DNode border) { + NodeLayout layout = SiriusElementRefactorHelper.findLayout(borders, border); + if (layout != null) { + layout.updateBorderStyle(border); + } + } + + protected void updateBorderStyle(AbstractDNode target) { + // Update bounds + View gmfView = SiriusGMFHelper.getGmfView(target, session); + if (gmfView instanceof Node) { + ((Node) gmfView).setLayoutConstraint(bounds); + } + + updateNodeContainerStyle(target, gmfView instanceof Node ? (Node) gmfView : null); + + if (target instanceof EdgeTarget) { + ((EdgeTarget) target).getOutgoingEdges().stream().forEach(it -> updateEdge(it, outEdges, target)); + ((EdgeTarget) target).getIncomingEdges().stream().forEach(it -> updateEdge(it, inEdges, target)); + } + } + +} diff --git a/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/refactoring/RepresentationLayout.java b/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/refactoring/RepresentationLayout.java new file mode 100644 index 00000000..2f872de8 --- /dev/null +++ b/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/refactoring/RepresentationLayout.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2024 Obeo. + * 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: + * Obeo - initial API and implementation + * + */ +package org.obeonetwork.bpmn2.design.refactoring; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.sirius.business.api.session.Session; +import org.eclipse.sirius.diagram.DDiagramElement; +import org.eclipse.sirius.diagram.description.DiagramElementMapping; + +/** + * + * @author nperansin + */ +abstract class RepresentationLayout { + + final Session session; + + final D target; + final EObject container; // DDiagram, AbstractDNode{border}, DNodeContainer, DNodeList + final M mapping; + D newView; + + @SuppressWarnings("unchecked") + protected RepresentationLayout(D src, EObject parent, Session session) { + + this.session = session; + target = src; + this.container = parent; + mapping = (M) src.getDiagramElementMapping(); + } + + protected void bind(D view) { + newView = view; + } + + protected void update() { + if (newView != null) { + update(newView); + } + } + + protected abstract void update(D newView); +} diff --git a/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/SiriusElementRefactor.java b/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/refactoring/SiriusElementRefactor.java similarity index 50% rename from plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/SiriusElementRefactor.java rename to plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/refactoring/SiriusElementRefactor.java index 68200172..90a6cfeb 100644 --- a/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/SiriusElementRefactor.java +++ b/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/refactoring/SiriusElementRefactor.java @@ -9,11 +9,9 @@ * Obeo - initial API and implementation * */ -package org.obeonetwork.bpmn2.design; +package org.obeonetwork.bpmn2.design.refactoring; import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -23,6 +21,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EClass; @@ -30,18 +29,10 @@ import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; -import org.eclipse.gmf.runtime.notation.Anchor; -import org.eclipse.gmf.runtime.notation.Bendpoints; -import org.eclipse.gmf.runtime.notation.Bounds; import org.eclipse.gmf.runtime.notation.Diagram; -import org.eclipse.gmf.runtime.notation.Edge; -import org.eclipse.gmf.runtime.notation.Node; -import org.eclipse.gmf.runtime.notation.View; import org.eclipse.sirius.business.api.dialect.DialectManager; import org.eclipse.sirius.business.api.session.CustomDataConstants; import org.eclipse.sirius.business.api.session.Session; -import org.eclipse.sirius.common.tools.api.interpreter.IInterpreter; -import org.eclipse.sirius.common.tools.api.util.RefreshIdsHolder; import org.eclipse.sirius.diagram.AbstractDNode; import org.eclipse.sirius.diagram.DDiagram; import org.eclipse.sirius.diagram.DDiagramElement; @@ -52,33 +43,23 @@ import org.eclipse.sirius.diagram.DSemanticDiagram; import org.eclipse.sirius.diagram.DragAndDropTarget; import org.eclipse.sirius.diagram.EdgeTarget; -import org.eclipse.sirius.diagram.business.api.componentization.DiagramMappingsManager; -import org.eclipse.sirius.diagram.business.api.componentization.DiagramMappingsManagerRegistry; -import org.eclipse.sirius.diagram.business.internal.helper.decoration.DecorationHelperInternal; -import org.eclipse.sirius.diagram.business.internal.sync.DDiagramElementSynchronizer; import org.eclipse.sirius.diagram.business.internal.sync.DEdgeCandidate; import org.eclipse.sirius.diagram.business.internal.sync.DNodeCandidate; import org.eclipse.sirius.diagram.description.AbstractNodeMapping; import org.eclipse.sirius.diagram.description.ContainerMapping; -import org.eclipse.sirius.diagram.description.DiagramElementMapping; import org.eclipse.sirius.diagram.description.EdgeMapping; import org.eclipse.sirius.diagram.description.IEdgeMapping; -import org.eclipse.sirius.diagram.description.MappingBasedDecoration; import org.eclipse.sirius.diagram.description.NodeMapping; -import org.eclipse.sirius.diagram.ui.business.api.view.SiriusGMFHelper; import org.eclipse.sirius.diagram.ui.internal.refresh.diagram.DDiagramCanonicalSynchronizer; import org.eclipse.sirius.diagram.ui.part.SiriusDiagramEditor; -import org.eclipse.sirius.ecore.extender.business.api.accessor.ModelAccessor; -import org.eclipse.sirius.tools.api.SiriusPlugin; -import org.eclipse.sirius.tools.api.interpreter.InterpreterUtil; import org.eclipse.sirius.ui.business.api.session.SessionEditorInput; import org.eclipse.sirius.viewpoint.DRepresentationDescriptor; import org.eclipse.sirius.viewpoint.DRepresentationElement; import org.eclipse.sirius.viewpoint.ViewpointPackage; import org.eclipse.sirius.viewpoint.description.AnnotationEntry; -import org.eclipse.sirius.viewpoint.description.SemanticBasedDecoration; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.PlatformUI; +import org.obeonetwork.bpmn2.design.Activator; /** * Refactor an element into a Sirius Session. @@ -86,9 +67,11 @@ * This class is generic but only tested for nodes. (no case for edge in BPMN) *

*

- * Simple approach (replacing a Representation target) does not work for diagrams. + * Simple approach (replacing a Representation target) does not work for + * diagrams. *

    - *
  • Editor contains a map from Semantic element to Representation that is not refreshed on simple change.
  • + *
  • Editor contains a map from Semantic element to Representation that is not + * refreshed on simple change.
  • *
  • Unedited diagrams may be impacted and positions are lost.
  • *
  • A specific mapping translation may be required.
  • *
@@ -100,327 +83,30 @@ */ @SuppressWarnings("restriction") // DDiagramElementSynchronizer, DNodeCandidate public class SiriusElementRefactor { - + private static final EReference DTARGET_FEATURE = ViewpointPackage.eINSTANCE.getDSemanticDecorator_Target(); - + private final EObject origin; - + private final Session session; private final List editedDiagrams; - - protected abstract class RepresentationLayout { - final D target; - final EObject container; // DDiagram, AbstractDNode{border}, DNodeContainer, DNodeList - final M mapping; - D newView; - - @SuppressWarnings("unchecked") - protected RepresentationLayout(D src, EObject parent) { - target = src; - this.container = parent; - mapping = (M) src.getDiagramElementMapping(); - } - - protected void bind(D view) { - newView = view; - } - - protected void update() { - if (newView != null) { - update(newView); - } - } - protected abstract void update(D newView); - } - - private static > T findLayout(Collection layouts, DDiagramElement element) { - return layouts.stream() - .filter(it -> it.target == element.getTarget() - && it.mapping == element.getDiagramElementMapping()) - .findFirst() - .orElse(null); - } - - protected class NodeLayout extends RepresentationLayout { - final Bounds bounds; - final List borders; - final List outEdges; - final List inEdges; - - protected NodeLayout(AbstractDNode src) { - super(src, src.eContainer()); - - Bounds bounds = null; - View gmfView = SiriusGMFHelper.getGmfView(src, session); - if (gmfView instanceof Node) { - Node node = (Node) gmfView; // gmfNode.get(); - if (node.getLayoutConstraint() instanceof Bounds) { - bounds = (Bounds) node.getLayoutConstraint(); - } - } - this.bounds = bounds; - // TODO copy style ? - - borders = src.getOwnedBorderedNodes() - .stream() - .map(it -> new NodeLayout(it)) - .collect(Collectors.toList()); - - if (src instanceof EdgeTarget) { - outEdges = ((EdgeTarget) src).getOutgoingEdges() - .stream() - .map(it -> new EndLayout(it, src, true)) - .collect(Collectors.toList()); - inEdges = ((EdgeTarget) src).getIncomingEdges() - .stream() - .map(it -> new EndLayout(it, src, false)) - .collect(Collectors.toList()); - } else { - outEdges = Collections.emptyList(); - inEdges = Collections.emptyList(); - } - } - - @Override - protected void update(AbstractDNode target) { - // Update bounds - View gmfView = SiriusGMFHelper.getGmfView(target, session); - if (gmfView instanceof Node) { - ((Node) gmfView).setLayoutConstraint(bounds); - } - // Update border - newView.getOwnedBorderedNodes().forEach(it -> updateBorder(it)); - - if (target instanceof EdgeTarget) { - ((EdgeTarget) target).getOutgoingEdges() - .stream() - .forEach(it -> updateEdge(it, outEdges, target)); - ((EdgeTarget) target).getIncomingEdges() - .stream() - .forEach(it -> updateEdge(it, inEdges, target)); - } - } - - protected void updateEdge(DEdge current, List layouts, AbstractDNode newNode) { - EndLayout layout = findEdgeLayout(current, layouts, newNode); - if (layout != null) { - layout.update(current); - } - } - - protected EndLayout findEdgeLayout(DEdge current, List layouts, AbstractDNode newNode) { - for (EndLayout layout : layouts) { - if (layout.match(current, target, newNode)) { - return layout; - } - } - return null; - } - - protected void updateBorder(DNode border) { - NodeLayout layout = findLayout(borders, border); - if (layout != null) { - layout.update(border); - } - } - - } - - protected class EdgeLayout extends RepresentationLayout { - final EdgeLayoutData data; - - protected EdgeLayout(DEdge src) { - super(src, src.getParentDiagram()); - View view = SiriusGMFHelper.getGmfView(src, session); - data = view instanceof Edge - ? new EdgeLayoutData((Edge) view) - : null; - } - - @Override - protected void update(DEdge target) { - View gmfView = SiriusGMFHelper.getGmfView(target, session); - if (gmfView instanceof Edge) { - data.update((Edge) gmfView); - } - } - - } - - protected static EdgeTarget getEnd(DEdge src, boolean out) { - return out ? src.getTargetNode() : src.getSourceNode(); - } - - protected class EndLayout extends EdgeLayout { - final boolean targetChange; - final EdgeTarget end; - final boolean outEnd; - final boolean endChange; - - protected EndLayout(DEdge src, AbstractDNode targeting, boolean out) { - super(src); - targetChange = src.getTarget() == targeting.getTarget(); - outEnd = out; - end = getEnd(src, out); - endChange = end == targeting; - } - - protected boolean match(DEdge it, AbstractDNode previous, AbstractDNode current) { - EObject expectedTarget = targetChange - ? current.getTarget() - : target.getTarget(); - EdgeTarget actualEnd = getEnd(it, outEnd); - EdgeTarget expectedEnd = endChange - ? (EdgeTarget) current - : end; - - return it.getTarget() == expectedTarget // proper target - && actualEnd == expectedEnd // proper end - && it.getMapping() == mapping; // proper mapping - } - - } - - - private static boolean isGmfLabelNode(Object it) { - if (!(it instanceof Node)) { - return false; - } - Node node = (Node) it; - return node.getPersistedChildren().isEmpty() && node.getType() != null; - } - - protected class EdgeLayoutData { - - final Bendpoints bendPoints; - final Anchor sourceAnchor; - final Anchor targetAnchor; - final List labels; - - protected EdgeLayoutData(Edge src) { - bendPoints = src.getBendpoints(); - sourceAnchor = src.getSourceAnchor(); - targetAnchor = src.getTargetAnchor(); - - labels = ((List) src.getPersistedChildren()) - .stream() - .filter(it -> isGmfLabelNode(it)) - .map(Node.class::cast) - .collect(Collectors.toList()); - } - - /** - * Updates edge with layout - * - * @param edge - * to update - */ - private void update(Edge edge) { - edge.setBendpoints(bendPoints); - edge.setSourceAnchor(sourceAnchor); - edge.setTargetAnchor(targetAnchor); - if (!labels.isEmpty()) { - updateLabels(edge); - } - } - - private void updateLabels(Edge edge) { - ((List) edge.getPersistedChildren()) - .stream() - .filter(it -> isGmfLabelNode(it)) - .map(Node.class::cast) - .forEach(it -> updateLabel(it)); - - } - - private void updateLabel(Node it) { - Node old = findLabel(it.getType()); - if (old != null) { - it.setLayoutConstraint(old.getLayoutConstraint()); - } - } - - private Node findLabel(String type) { - return labels.stream() - .filter(it -> Objects.equals(it.getType(), type)) - .findFirst().orElse(null); - } - - } - - class DiagramModifier { - final DSemanticDiagram parent; - final ModelAccessor accessor; - final IInterpreter interpreter; - final DDiagramElementSynchronizer dsync; - final DiagramMappingsManager mmapping; - final RefreshIdsHolder idFactory; - - Map> mappingsToEdgeTargets = null; - final Map> edgeToMappings = new HashMap<>(); - final Map> edgeToSemantics = new HashMap<>(); - - private DiagramModifier(DSemanticDiagram diagram) { - parent = diagram; - accessor = SiriusPlugin.getDefault() - .getModelAccessorRegistry() - .getModelAccessor(diagram); - mmapping = DiagramMappingsManagerRegistry.INSTANCE.getDiagramMappingsManager(session, diagram); - interpreter = InterpreterUtil.getInterpreter(diagram); - dsync = new DDiagramElementSynchronizer(diagram, interpreter, accessor) - /* Should we skip a PermissionAuthority ? - { - - @Override - public void refresh(final DDiagramElement newNode) { - if (newNode instanceof DNode) { - refresh((DNode) newNode); - } else if (newNode instanceof DNodeListElement) { - refresh((DNodeListElement) newNode); - } else if (newNode instanceof DDiagramElementContainer) { - refresh((DDiagramElementContainer) newNode); - } else if (newNode instanceof DEdge) { - refresh((DEdge) newNode); - } - } - }*/; - idFactory = RefreshIdsHolder.getOrCreateHolder(diagram); - } - - private void prepareEdges() { - if (mappingsToEdgeTargets != null) { - return; - } - mappingsToEdgeTargets = dsync.computeMappingsToEdgeTargets(session.getSelectedViewpoints(false)); - // Initialize cache - new DecorationHelperInternal(parent, interpreter, accessor) - .computeDecorations(mappingsToEdgeTargets, edgeToSemantics, edgeToMappings); - // TODO update with new nodes ? - } - - } - // Transformation content private List transferableRefs = new ArrayList<>(); private List nodeLayouts = new ArrayList<>(); private List edgeLayouts = new ArrayList<>(); private Map synchs = new HashMap<>(); - + public SiriusElementRefactor(DRepresentationElement context) { origin = Objects.requireNonNull(context.getTarget()); session = Session.of(context).get(); - + editedDiagrams = Stream.of(PlatformUI.getWorkbench().getWorkbenchWindows()) - .flatMap(it -> Stream.of(it.getPages())) - .flatMap(it -> Stream.of(it.getEditorReferences())) - .map(it -> it.getEditor(false)) - .filter(this::isApplicableEditor) - .map(this::toDiagram) - .filter(Objects::nonNull) - .collect(Collectors.toList()); + .flatMap(it -> Stream.of(it.getPages())).flatMap(it -> Stream.of(it.getEditorReferences())) + .map(it -> it.getEditor(false)).filter(this::isApplicableEditor).map(this::toDiagram) + .filter(Objects::nonNull).collect(Collectors.toList()); } - + private DDiagram toDiagram(IEditorPart editor) { URI repUri = ((SessionEditorInput) editor.getEditorInput()).getRepDescUri(); EObject rep = session.getTransactionalEditingDomain().getResourceSet().getEObject(repUri, false); @@ -428,66 +114,63 @@ private DDiagram toDiagram(IEditorPart editor) { if (rep instanceof DRepresentationDescriptor) { // regular case rep = ((DRepresentationDescriptor) rep).getRepresentation(); } - return rep instanceof DDiagram - ? (DDiagram) rep - : null; + return rep instanceof DDiagram ? (DDiagram) rep : null; } - + private boolean isApplicableEditor(IEditorPart it) { - return session != null - && it instanceof SiriusDiagramEditor - && it.getEditorInput() instanceof SessionEditorInput + return session != null && it instanceof SiriusDiagramEditor && it.getEditorInput() instanceof SessionEditorInput && ((SessionEditorInput) it.getEditorInput()).getSession() == session; } - + // Open diagram -> listeners requires a proper update public EObject transformInto(EClass targetType) { EFactory fct = targetType.getEPackage().getEFactoryInstance(); return replaceWith(fct.create(targetType)); } - - + public EObject replaceWith(R target) { try { copyContent(target); - + EStructuralFeature.Setting containment = analyseReferences(target); - + // Change containment first for all listeners to be triggered. replaceReference(containment.getEObject(), containment.getEStructuralFeature(), target); for (EStructuralFeature.Setting ref : transferableRefs) { - if (ref.getEStructuralFeature() == DTARGET_FEATURE - && ref.getEObject() instanceof DDiagramElement) { + if (ref.getEStructuralFeature() == DTARGET_FEATURE && ref.getEObject() instanceof DDiagramElement) { replaceDElementTarget((DDiagramElement) ref.getEObject(), target); } else { replaceReference(ref.getEObject(), ref.getEStructuralFeature(), target); - }; + } } - + // Recreate Views (Do not use refresh to associate old and new representations). Set toRefresh = new HashSet<>(); nodeLayouts.forEach(it -> toRefresh.add(createNewNode(target, it))); edgeLayouts.forEach(it -> toRefresh.add(createNewEdge(target, it))); - + // Refresh Diagrams to create GMF notation toRefresh.forEach(it -> refresh(it)); - + // Restore layout nodes nodeLayouts.forEach(it -> it.update()); - // TODO restore - // - edges - + return target; + } catch (Exception e) { + // Capture the exception to add information to the log because the exception is + // caught silently. + Activator.log(IStatus.ERROR, "Error during refactoring", e); + throw e; } finally { cleanCache(); } } - + /** * Replaces the element target and mapping. * * @param dElement to update - * @param target new target + * @param target new target */ protected void replaceDElementTarget(DDiagramElement dElement, EObject target) { // Update mapping if possible. @@ -504,7 +187,7 @@ protected void replaceDElementTarget(DDiagramElement dElement, EObject target) { AbstractNodeMapping newMapping = getApplicableNodeMapping((AbstractNodeMapping) node.getMapping(), target); updateNodeMapping(node, newMapping); } - dElement.setTarget(target); + dElement.setTarget(target); } private static void updateNodeMapping(AbstractDNode node, AbstractNodeMapping newMapping) { @@ -531,8 +214,7 @@ private void refresh(DSemanticDiagram it) { return; } // Create proper location - DDiagramCanonicalSynchronizer gmfSynch = - new DDiagramCanonicalSynchronizer(gmfDiagram); + DDiagramCanonicalSynchronizer gmfSynch = new DDiagramCanonicalSynchronizer(gmfDiagram); gmfSynch.storeViewsToArrange(false); // Clean diagram and related edges @@ -541,7 +223,7 @@ private void refresh(DSemanticDiagram it) { // Create gmf nodes of new elements. This is performed in pre-commit. gmfSynch.synchronize(); } - + private static Diagram getGMFDiagram(DDiagram it) { for (AnnotationEntry anno : it.getOwnedAnnotationEntries()) { if (Objects.equals(anno.getSource(), CustomDataConstants.GMF_DIAGRAMS)) { @@ -553,80 +235,76 @@ private static Diagram getGMFDiagram(DDiagram it) { private DSemanticDiagram createNewNode(EObject target, NodeLayout save) { DSemanticDiagram diagram = getAncestor(DSemanticDiagram.class, save.container); - DiagramModifier sync = synchs.computeIfAbsent(diagram, DiagramModifier::new); + DiagramModifier sync = synchs.computeIfAbsent(diagram, d -> new DiagramModifier(d, session)); - AbstractNodeMapping targetMapping = getApplicableNodeMapping( - (AbstractNodeMapping) save.target.getMapping(), target); + AbstractNodeMapping targetMapping = getApplicableNodeMapping((AbstractNodeMapping) save.target.getMapping(), + target); if (targetMapping == null) { return diagram; } - DNodeCandidate candidate = new DNodeCandidate( - targetMapping, - target, - (DragAndDropTarget) save.container, + DNodeCandidate candidate = new DNodeCandidate(targetMapping, target, (DragAndDropTarget) save.container, sync.idFactory); - + // Should we keep node order in container ? (Probably not). - save.bind(sync.dsync.createNewNode(sync.mmapping, candidate, false)); + AbstractDNode newDNode = sync.dsync.createNewNode(sync.mmapping, candidate, false); + postCreateNewNode(newDNode, save.isExternalLabel); + save.bind(newDNode); return diagram; } + protected void postCreateNewNode(AbstractDNode newDNode, boolean isExternalLabel) { + // Nada + } + /** * Returns the mapping expected for a transformed node. * * @param previous mapping - * @param current transformed target + * @param current transformed target * @return mapping or null if not displayed anymore */ protected AbstractNodeMapping getApplicableNodeMapping(AbstractNodeMapping previous, EObject current) { return previous; } - + private DSemanticDiagram createNewEdge(EObject target, EdgeLayout save) { DSemanticDiagram diagram = (DSemanticDiagram) save.container; - DiagramModifier writer = synchs.computeIfAbsent(diagram, DiagramModifier::new); - - EdgeMapping targetMapping = getApplicableEdgeMapping( - (IEdgeMapping) save.target.getMapping(), target); - DEdgeCandidate candidate = new DEdgeCandidate( - targetMapping, - target, - getAccurateEgdeTarget(save.target.getSourceNode()), - getAccurateEgdeTarget(save.target.getTargetNode()), + DiagramModifier writer = synchs.computeIfAbsent(diagram, d -> new DiagramModifier(d, session)); + + EdgeMapping targetMapping = getApplicableEdgeMapping((IEdgeMapping) save.target.getMapping(), target); + DEdgeCandidate candidate = new DEdgeCandidate(targetMapping, target, + getAccurateEgdeTarget(save.target.getSourceNode()), getAccurateEgdeTarget(save.target.getTargetNode()), writer.idFactory); - - // See org.eclipse.sirius.diagram.business.internal.helper.task.operations.CreateViewTask + + // See + // org.eclipse.sirius.diagram.business.internal.helper.task.operations.CreateViewTask // #createEdgeView(DSemanticDiagram, CreateEdgeView, Session) writer.prepareEdges(); - save.bind(writer.dsync.createNewEdge(writer.mmapping, candidate, - writer.mappingsToEdgeTargets, - writer.edgeToMappings, - writer.edgeToSemantics)); + save.bind(writer.dsync.createNewEdge(writer.mmapping, candidate, writer.mappingsToEdgeTargets, + writer.edgeToMappings, writer.edgeToSemantics)); return diagram; } - + /** * Returns the mapping expected for a transformed edge. * * @param previous mapping - * @param current transformed target + * @param current transformed target * @return mapping or null if not displayed anymore */ protected EdgeMapping getApplicableEdgeMapping(IEdgeMapping previous, EObject current) { return (EdgeMapping) previous; } - - + private EdgeTarget getAccurateEgdeTarget(EdgeTarget previous) { - for(NodeLayout node : nodeLayouts) { + for (NodeLayout node : nodeLayouts) { if (node.target == previous && node.newView != null) { return (EdgeTarget) node.newView; } } return previous; } - - + private void cleanCache() { transferableRefs.clear(); nodeLayouts.clear(); @@ -638,15 +316,14 @@ private EStructuralFeature.Setting analyseReferences(EObject target) { EStructuralFeature.Setting containment = null; for (EStructuralFeature.Setting ref : session.getSemanticCrossReferencer().getInverseReferences(origin)) { - // Refactor + // Refactor // - split reference and get relationship EStructuralFeature feat = ref.getEStructuralFeature(); if (feat instanceof EReference && ((EReference) feat).isContainment()) { containment = ref; } else if (isEditable(ref.getEStructuralFeature())) { - if (isAssignable(ref.getEStructuralFeature(), target) - && shouldRecreateView(ref)) { - + if (isAssignable(ref.getEStructuralFeature(), target) && shouldRecreateView(ref)) { + if (DTARGET_FEATURE == ref.getEStructuralFeature()) { DDiagramElement dElement = (DDiagramElement) ref.getEObject(); saveLayoutRepresentation(dElement); @@ -661,12 +338,12 @@ && shouldRecreateView(ref)) { protected void saveLayoutRepresentation(DDiagramElement dElement) { if (dElement instanceof AbstractDNode) { - nodeLayouts.add(new NodeLayout((AbstractDNode) dElement)); + nodeLayouts.add(new NodeLayout((AbstractDNode) dElement, session)); } else if (dElement instanceof DEdge) { - edgeLayouts.add(new EdgeLayout((DEdge) dElement)); + edgeLayouts.add(new EdgeLayout((DEdge) dElement, session)); } // else no other cases for diagram } - + private boolean shouldRecreateView(EStructuralFeature.Setting ref) { if (!(ref.getEObject() instanceof DDiagramElement)) { return false; @@ -674,9 +351,8 @@ private boolean shouldRecreateView(EStructuralFeature.Setting ref) { DDiagram container = getAncestor(DDiagram.class, ref.getEObject()); return container != null && editedDiagrams.contains(container); } - - - private void replaceReference(EObject caller, EStructuralFeature feat, EObject target) { + + private void replaceReference(EObject caller, EStructuralFeature feat, EObject target) { if (feat.isMany()) { // remove reference @SuppressWarnings("unchecked") List values = ((List) caller.eGet(feat)); @@ -689,24 +365,21 @@ private void replaceReference(EObject caller, EStructuralFeature feat, EObject t } else { values.remove(origin); } - } else /*single*/ if (isAssignable(feat, target)) { + } else /* single */ if (isAssignable(feat, target)) { caller.eSet(feat, target); } else { caller.eUnset(feat); - } + } } - + @SuppressWarnings("unchecked") private static T getAncestor(Class type, EObject it) { - return it == null || type.isInstance(it) - ? (T) it - : getAncestor(type, it.eContainer()); + return it == null || type.isInstance(it) ? (T) it : getAncestor(type, it.eContainer()); } - + private void copyContent(EObject target) { for (EStructuralFeature feature : origin.eClass().getEAllStructuralFeatures()) { - if (origin.eIsSet(feature) - && isApplicable(feature, target.eClass()) + if (origin.eIsSet(feature) && isApplicable(feature, target.eClass()) && isTransferable(feature, target.eClass())) { if (feature.isMany()) { List values = (List) origin.eGet(feature); @@ -719,34 +392,31 @@ && isTransferable(feature, target.eClass())) { } } } - + private boolean isEditable(EStructuralFeature feature) { - return !feature.isDerived() - && !feature.isTransient() - && feature.isChangeable(); + return !feature.isDerived() && !feature.isTransient() && feature.isChangeable(); } - + private boolean isApplicable(EStructuralFeature feature, EClass targetType) { - return isEditable(feature) - && feature.getEContainingClass().isSuperTypeOf(targetType); + return isEditable(feature) && feature.getEContainingClass().isSuperTypeOf(targetType); } - + /** * Evaluates if a feature should be copied to new element. *

* Dynamic Id should not be copied for EMF-compare to work. *

* - * @param feature to copy + * @param feature to copy * @param targetType type of element to write in * @return true if transferable */ protected boolean isTransferable(EStructuralFeature feature, EClass targetType) { return true; } - + private boolean isAssignable(EStructuralFeature feature, EObject target) { return feature.getEType().isInstance(target); } - + } diff --git a/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/refactoring/SiriusElementRefactorHelper.java b/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/refactoring/SiriusElementRefactorHelper.java new file mode 100644 index 00000000..057067b7 --- /dev/null +++ b/plugins/org.obeonetwork.dsl.bpmn2.design/src/org/obeonetwork/bpmn2/design/refactoring/SiriusElementRefactorHelper.java @@ -0,0 +1,151 @@ +/** + * Copyright (c) 2024 Obeo. + * 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: + * Obeo - initial API and implementation + * + */ +package org.obeonetwork.bpmn2.design.refactoring; + +import java.util.Collection; + +import org.eclipse.gmf.runtime.notation.ConnectorStyle; +import org.eclipse.gmf.runtime.notation.FontStyle; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.sirius.diagram.DDiagramElement; +import org.eclipse.sirius.diagram.DEdge; +import org.eclipse.sirius.diagram.EdgeStyle; +import org.eclipse.sirius.diagram.EdgeTarget; +import org.eclipse.sirius.diagram.FlatContainerStyle; +import org.eclipse.sirius.diagram.Square; +import org.eclipse.sirius.diagram.WorkspaceImage; + +public class SiriusElementRefactorHelper { + + static boolean isGmfLabelNode(Object it) { + if (!(it instanceof Node)) { + return false; + } + Node node = (Node) it; + return node.getPersistedChildren().isEmpty() && node.getType() != null; + } + + static > T findLayout(Collection layouts, DDiagramElement element) { + return layouts.stream().filter( + it -> it.target.getTarget() == element.getTarget() && it.mapping == element.getDiagramElementMapping()) + .findFirst().orElse(null); + } + + static EdgeTarget getEnd(DEdge src, boolean out) { + return out ? src.getTargetNode() : src.getSourceNode(); + } + + static void updateFontStyle(FontStyle oldStyle, FontStyle newStyle) { + newStyle.setBold(oldStyle.isBold()); + newStyle.setFontColor(oldStyle.getFontColor()); + newStyle.setFontHeight(oldStyle.getFontHeight()); + newStyle.setFontName(oldStyle.getFontName()); + newStyle.setItalic(oldStyle.isItalic()); + newStyle.setStrikeThrough(oldStyle.isStrikeThrough()); + newStyle.setUnderline(oldStyle.isUnderline()); + } + + static void updateConnectorStyle(ConnectorStyle oldStyle, ConnectorStyle newStyle) { + newStyle.setAvoidObstructions(oldStyle.isAvoidObstructions()); + newStyle.setClosestDistance(oldStyle.isClosestDistance()); + newStyle.setJumpLinksReverse(oldStyle.isJumpLinksReverse()); + newStyle.setJumpLinkStatus(oldStyle.getJumpLinkStatus()); + newStyle.setJumpLinkType(oldStyle.getJumpLinkType()); + newStyle.setLineColor(oldStyle.getLineColor()); + newStyle.setLineWidth(oldStyle.getLineWidth()); + newStyle.setRoundedBendpointsRadius(oldStyle.getRoundedBendpointsRadius()); + newStyle.setRouting(oldStyle.getRouting()); + newStyle.setSmoothness(oldStyle.getSmoothness()); + } + + static void updateSiriusNodeContainerFlatContainerStyle(FlatContainerStyle oldContainerStyle, + FlatContainerStyle containerStyle) { + containerStyle.setDescription(oldContainerStyle.getDescription()); + containerStyle.setUid(oldContainerStyle.getUid()); + containerStyle.getCustomFeatures().clear(); + containerStyle.getCustomFeatures().addAll(oldContainerStyle.getCustomFeatures()); + containerStyle.setBackgroundColor(oldContainerStyle.getBackgroundColor()); + containerStyle.setBackgroundStyle(oldContainerStyle.getBackgroundStyle()); + containerStyle.setBorderColor(oldContainerStyle.getBorderColor()); + containerStyle.setBorderLineStyle(oldContainerStyle.getBorderLineStyle()); + containerStyle.setBorderSize(oldContainerStyle.getBorderSize()); + containerStyle.setBorderSizeComputationExpression(oldContainerStyle.getBorderSizeComputationExpression()); + containerStyle.setForegroundColor(oldContainerStyle.getForegroundColor()); + containerStyle.setHideLabelByDefault(oldContainerStyle.isHideLabelByDefault()); + containerStyle.setIconPath(oldContainerStyle.getIconPath()); + containerStyle.setLabelAlignment(oldContainerStyle.getLabelAlignment()); + containerStyle.setLabelColor(oldContainerStyle.getLabelColor()); + containerStyle.setLabelSize(oldContainerStyle.getLabelSize()); + containerStyle.getLabelFormat().addAll(oldContainerStyle.getLabelFormat()); + containerStyle.setShowIcon(oldContainerStyle.isShowIcon()); + } + + static void updateSiriusEdgeStyle(EdgeStyle siriusStyle, EdgeStyle newSiriusStyle) { + newSiriusStyle.setBeginLabelStyle(siriusStyle.getBeginLabelStyle()); + newSiriusStyle.setCentered(siriusStyle.getCentered()); + newSiriusStyle.setCenterLabelStyle(siriusStyle.getCenterLabelStyle()); + newSiriusStyle.setDescription(siriusStyle.getDescription()); + newSiriusStyle.setEndLabelStyle(siriusStyle.getEndLabelStyle()); + newSiriusStyle.setFoldingStyle(siriusStyle.getFoldingStyle()); + newSiriusStyle.setLineStyle(siriusStyle.getLineStyle()); + newSiriusStyle.setRoutingStyle(siriusStyle.getRoutingStyle()); + newSiriusStyle.setSize(siriusStyle.getSize()); + newSiriusStyle.setSourceArrow(siriusStyle.getSourceArrow()); + newSiriusStyle.setStrokeColor(siriusStyle.getStrokeColor()); + newSiriusStyle.setTargetArrow(siriusStyle.getTargetArrow()); + newSiriusStyle.setUid(siriusStyle.getUid()); + newSiriusStyle.getCustomFeatures().clear(); + newSiriusStyle.getCustomFeatures().addAll(siriusStyle.getCustomFeatures()); + } + + public static void updateSiriusNodeContainerWorkspaceImageStyle(WorkspaceImage oldContainerStyle, + WorkspaceImage containerStyle) { + containerStyle.setDescription(oldContainerStyle.getDescription()); + containerStyle.setUid(oldContainerStyle.getUid()); + containerStyle.getCustomFeatures().clear(); + containerStyle.getCustomFeatures().addAll(oldContainerStyle.getCustomFeatures()); + containerStyle.setBorderColor(oldContainerStyle.getBorderColor()); + containerStyle.setBorderLineStyle(oldContainerStyle.getBorderLineStyle()); + containerStyle.setBorderSize(oldContainerStyle.getBorderSize()); + containerStyle.setBorderSizeComputationExpression(oldContainerStyle.getBorderSizeComputationExpression()); + containerStyle.setHideLabelByDefault(oldContainerStyle.isHideLabelByDefault()); + containerStyle.setIconPath(oldContainerStyle.getIconPath()); + containerStyle.setLabelAlignment(oldContainerStyle.getLabelAlignment()); + containerStyle.setLabelColor(oldContainerStyle.getLabelColor()); + containerStyle.setLabelSize(oldContainerStyle.getLabelSize()); + containerStyle.getLabelFormat().addAll(oldContainerStyle.getLabelFormat()); + containerStyle.setLabelPosition(oldContainerStyle.getLabelPosition()); + containerStyle.setShowIcon(oldContainerStyle.isShowIcon()); + } + + public static void updateSiriusNodeContainerSquareStyle(Square oldContainerStyle, Square containerStyle) { + containerStyle.setDescription(oldContainerStyle.getDescription()); + containerStyle.setUid(oldContainerStyle.getUid()); + containerStyle.getCustomFeatures().clear(); + containerStyle.getCustomFeatures().addAll(oldContainerStyle.getCustomFeatures()); + containerStyle.setBorderColor(oldContainerStyle.getBorderColor()); + containerStyle.setBorderLineStyle(oldContainerStyle.getBorderLineStyle()); + containerStyle.setBorderSize(oldContainerStyle.getBorderSize()); + containerStyle.setBorderSizeComputationExpression(oldContainerStyle.getBorderSizeComputationExpression()); + containerStyle.setColor(oldContainerStyle.getColor()); + containerStyle.setHeight(oldContainerStyle.getHeight()); + containerStyle.setHideLabelByDefault(oldContainerStyle.isHideLabelByDefault()); + containerStyle.setIconPath(oldContainerStyle.getIconPath()); + containerStyle.setLabelAlignment(oldContainerStyle.getLabelAlignment()); + containerStyle.setLabelColor(oldContainerStyle.getLabelColor()); + containerStyle.setLabelSize(oldContainerStyle.getLabelSize()); + containerStyle.getLabelFormat().addAll(oldContainerStyle.getLabelFormat()); + containerStyle.setShowIcon(oldContainerStyle.isShowIcon()); + containerStyle.setWidth(oldContainerStyle.getWidth()); + } + +}