blob: 05ba33927550647978fc72e14dae01528384a780 [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2019 CEA LIST.
*
*
* All rights reserved. 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:
* CEA LIST - Initial API and implementation
*
*****************************************************************************/
package org.eclipse.papyrus.moka.fmi.ui.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gmf.runtime.diagram.ui.commands.SetBoundsCommand;
import org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest.ViewDescriptor;
import org.eclipse.gmf.runtime.emf.core.util.EObjectAdapter;
import org.eclipse.gmf.runtime.emf.type.core.IHintedType;
import org.eclipse.gmf.runtime.notation.Bounds;
import org.eclipse.gmf.runtime.notation.Node;
import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.papyrus.ease.module.RequestUtils;
import org.eclipse.papyrus.moka.fmi.fmiprofile.FMIPort;
import org.eclipse.papyrus.moka.ssp.omsimulatorprofile.OMSimulatorBus;
import org.eclipse.papyrus.moka.ssp.omsimulatorprofile.util.OMSimulatorProfileUtil;
import org.eclipse.papyrus.uml.diagram.composite.custom.edit.command.CreateViewCommand;
import org.eclipse.papyrus.uml.diagram.composite.edit.parts.PortEditPart;
import org.eclipse.papyrus.uml.diagram.composite.edit.parts.PropertyPartEditPartCN;
import org.eclipse.papyrus.uml.diagram.composite.part.UMLVisualIDRegistry;
import org.eclipse.papyrus.uml.diagram.composite.providers.UMLElementTypes;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.ConnectableElement;
import org.eclipse.uml2.uml.Connector;
import org.eclipse.uml2.uml.ConnectorEnd;
import org.eclipse.uml2.uml.Port;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.util.UMLUtil;
public class FMUViewUtil {
public static final int PORT_DEFAULT_HEIGHT = 20;
protected static final int WIDTH_FACTOR = 11;
protected static final int MIN_WIDTH = 150;
protected static final int MIN_HEIGHT = 150;
protected static final double X_FONT_HEIGHT_FACTOR = 0.9;
protected static final double Y_FONT_HEIGHT_FACTOR = 0.85;
protected static final double X_WEST_OFFSET = 8;
protected static final int Y_LABEL_OFFSET = 20;
protected enum PositionKind {
NORTH, SOUTH, EAST, WEST
}
public static Node createGraphicalViews(Property newPart, GraphicalEditPart anEditPartInDiagram, View parentView, Point location,
Dimension dimension, List<Port> portsToHide) {
FMUPortHelper portHelper = new FMUPortHelper(newPart);
List<Port> inPorts = new ArrayList<>();
inPorts.addAll(portHelper.getInputBusses());
inPorts.addAll(portHelper.getInputsNotInBus());
inPorts.removeAll(portsToHide);
List<Port> outPorts = new ArrayList<>();
outPorts.addAll(portHelper.getOutputorBidiBusses());
outPorts.addAll(portHelper.getOutputsNotInBus());
outPorts.removeAll(portsToHide);
if (dimension == null) {
int figureWidth = Math.max(MIN_WIDTH, WIDTH_FACTOR * newPart.getName().length());
int maxPortOnOneSide = Math.max(inPorts.size(), outPorts.size());
int figureHeight = Math.max(MIN_HEIGHT, (2 * maxPortOnOneSide + 1) * (PORT_DEFAULT_HEIGHT+3));
dimension = new Dimension(figureWidth, figureHeight);
}
String elementType = PropertyPartEditPartCN.VISUAL_ID;
ViewDescriptor descriptor = new ViewDescriptor(new EObjectAdapter(newPart), Node.class,
((IHintedType) UMLElementTypes.getElementType(elementType)).getSemanticHint(),
anEditPartInDiagram.getDiagramPreferencesHint());
CreateViewCommand createCommand = new CreateViewCommand(anEditPartInDiagram.getEditingDomain(), descriptor,
parentView);
try {
createCommand.execute(null, null);
} catch (ExecutionException e) {
e.printStackTrace();
}
Rectangle partRectangle = new Rectangle(location, dimension);
SetBoundsCommand setBoundsCommand = new SetBoundsCommand(anEditPartInDiagram.getEditingDomain(), "move",
(IAdaptable) createCommand.getCommandResult().getReturnValue(), partRectangle);
try {
setBoundsCommand.execute(null, null);
} catch (ExecutionException e) {
e.printStackTrace();
}
Node fmuNode = ((IAdaptable) createCommand.getCommandResult().getReturnValue()).getAdapter(Node.class);
createPorts(inPorts, PositionKind.WEST, outPorts, PositionKind.EAST, fmuNode, anEditPartInDiagram);
return fmuNode;
}
protected static void createPorts(List<Port> inPorts, PositionKind inPosition, List<Port> outPorts,
PositionKind outPosition, Node fmuNode, GraphicalEditPart diagramEditPart) {
Bounds fmuBounds = (Bounds) fmuNode.getLayoutConstraint();
Rectangle viewRectangle = new Rectangle(fmuBounds.getX(), fmuBounds.getY(), fmuBounds.getWidth(),
fmuBounds.getHeight());
for (int index = 0; index < inPorts.size(); index++) {
Port port = inPorts.get(index);
Point proposedLocation = getPortLocation(inPosition, viewRectangle, index, inPorts.size());
createPortView(port, proposedLocation, fmuNode, diagramEditPart);
}
for (int index = 0; index < outPorts.size(); index++) {
Port port = outPorts.get(index);
Point proposedLocation = getPortLocation(outPosition, viewRectangle, index, outPorts.size());
createPortView(port, proposedLocation, fmuNode, diagramEditPart);
}
}
private static Point getPortLocation(PositionKind position, Rectangle viewRectangle, int currentPortIndex,
int numberOfPorts) {
switch (position) {
case WEST:
return new Point(0, (currentPortIndex + 1) * viewRectangle.height / (numberOfPorts + 1));
case EAST:
return new Point(viewRectangle.width, (currentPortIndex + 1) * viewRectangle.height / (numberOfPorts + 1));
case NORTH:
return new Point((currentPortIndex + 1) * viewRectangle.width / (numberOfPorts + 1), 0);
case SOUTH:
return new Point((currentPortIndex + 1) * viewRectangle.width / (numberOfPorts + 1), viewRectangle.height);
}
return null;
}
protected static void createPortView(Port port, Point location, View parentView,
GraphicalEditPart anEditPartInDiagram) {
ViewDescriptor descriptor = new ViewDescriptor(new EObjectAdapter(port), Node.class,
((IHintedType) UMLElementTypes.getElementType(UMLVisualIDRegistry.getNodeVisualID(parentView, port)))
.getSemanticHint(),
anEditPartInDiagram.getDiagramPreferencesHint());
CreateViewCommand createCommand = new CreateViewCommand(anEditPartInDiagram.getEditingDomain(), descriptor,
parentView);
try {
createCommand.execute(null, null);
} catch (ExecutionException e) {
e.printStackTrace();
}
SetBoundsCommand setBoundsCommand = new SetBoundsCommand(anEditPartInDiagram.getEditingDomain(), "move",
(IAdaptable) createCommand.getCommandResult().getReturnValue(), location);
try {
setBoundsCommand.execute(null, null);
} catch (ExecutionException e) {
e.printStackTrace();
}
}
public static void updateFMUType(Property fmuPartToUpdate, Class newType,
GraphicalEditPart targetGraphicalEditPart) {
List<Port> portsToRemove = new ArrayList<>();
List<Port> newPorts = new ArrayList<>();
Map<Port, Port> portMapping = new HashMap<>();
FMUPortHelper oldPortHelper = new FMUPortHelper(fmuPartToUpdate);
FMUPortHelper newPortHelper = new FMUPortHelper(newType);
newPortHelper.copyMissingBusses(oldPortHelper);
// we first look at all the previously existing ports
for (Port oldPort : oldPortHelper.getAllPorts()) {
Port correspondingPort = newPortHelper.getPortByName(oldPort.getName());
// if there is not a port with the same name we will have to remove the
// connections to this port
if (correspondingPort == null) {
portsToRemove.add(oldPort);
} else {
// we also remove the connections if the port is transformed into a bus (or vice
// versa)
if (UMLUtil.getStereotypeApplication(oldPort, FMIPort.class) != null
&& UMLUtil.getStereotypeApplication(correspondingPort, FMIPort.class) == null) {
portsToRemove.add(oldPort);
} else if (UMLUtil.getStereotypeApplication(oldPort, OMSimulatorBus.class) != null
&& UMLUtil.getStereotypeApplication(correspondingPort, OMSimulatorBus.class) == null) {
portsToRemove.add(oldPort);
} else {
// we keep a mapping between old ports and new ports, will be used to update
// connectors ends end graphical views
portMapping.put(oldPort, correspondingPort);
}
}
}
// the new ports in the new fmu also have to be identified
// they are in the newPortHelper but not in the mapping created with the old fmu
// it also includes the ports which have been changed to busses (and vice versa)
for (Port newPort : newPortHelper.getAllPorts()) {
if (!portMapping.containsValue(newPort)) {
newPorts.add(newPort);
}
}
fmuPartToUpdate.setType(newType);
updateConnectorEnds(fmuPartToUpdate.getClass_(), portMapping);
// if we execute the function without a graphical representation, we explicitely
// destroy connectors for port which are not anymore in the new fmu
if (targetGraphicalEditPart == null) {
destroyConnectors(fmuPartToUpdate.getClass_(), portsToRemove);
} else {
// if we have the diagram
List<View> viewToDestroy = new ArrayList<>();
for (PortEditPart portEditPart : getPortEditPartsMap(targetGraphicalEditPart).values()) {
View portView = portEditPart.getNotationView();
Port oldPort = (Port) portView.getElement();
Port newPort = portMapping.get(oldPort);
// if there is still a port with the same name in the new FMU, the existing
// graphical
// view has now to point to the new port
if (newPort != null) {
portView.setElement(newPort);
} else {
// else the graphical view has to be removed.
// It will also atomatically remove the associated connectors (view + semantic
// element)
viewToDestroy.add(portView);
}
}
RequestUtils.deleteObjectsWithRequest(new ArrayList<>(viewToDestroy));
displayNewPorts(newPorts, newPortHelper, portMapping.isEmpty(), targetGraphicalEditPart);
}
}
public static Map<Port, PortEditPart> getPortEditPartsMap(GraphicalEditPart fmuEditPart) {
Map<Port, PortEditPart> result = new HashMap<>();
for (Object childObject : fmuEditPart.getChildren()) {
if (childObject instanceof PortEditPart) {
View portView = ((PortEditPart) childObject).getNotationView();
result.put((Port) portView.getElement(), (PortEditPart) childObject);
}
}
return result;
}
private static void displayNewPorts(List<Port> newPorts, FMUPortHelper newPortHelper, boolean noPreservedPorts,
GraphicalEditPart targetGraphicalEditPart) {
List<Port> inputPorts = new ArrayList<>();
List<Port> outputOrBidiPorts = new ArrayList<>();
for (Port newPort : newPorts) {
// we display new ports which are not mapped in a bus and new busses
if (newPortHelper.getOwningBusses(newPort).isEmpty() || OMSimulatorProfileUtil.isBus(newPort)) {
if (newPortHelper.isInput(newPort)) {
inputPorts.add(newPort);
} else {
outputOrBidiPorts.add(newPort);
}
}
}
PositionKind inPosition = noPreservedPorts ? PositionKind.WEST : PositionKind.NORTH;
PositionKind outPosition = noPreservedPorts ? PositionKind.EAST : PositionKind.SOUTH;
createPorts(inputPorts, inPosition, outputOrBidiPorts, outPosition, (Node) targetGraphicalEditPart.getModel(),
targetGraphicalEditPart);
}
private static void updateConnectorEnds(Class owningClass, Map<Port, Port> portMapping) {
for (Connector connector : owningClass.getOwnedConnectors()) {
for (ConnectorEnd end : connector.getEnds()) {
ConnectableElement oldRole = end.getRole();
if (oldRole instanceof Port) {
Port newPort = portMapping.get(oldRole);
if (newPort != null) {
end.setRole(newPort);
}
}
}
}
}
private static void destroyConnectors(Class owningClass, List<Port> portsToRemove) {
Set<Connector> connectorToDestroy = new HashSet<>();
for (Connector connector : owningClass.getOwnedConnectors()) {
for (ConnectorEnd end : connector.getEnds()) {
if (portsToRemove.contains(end.getRole())) {
connectorToDestroy.add(connector);
}
}
}
RequestUtils.deleteObjectsWithRequest(new ArrayList<>(connectorToDestroy));
}
}