| /***************************************************************************** |
| * 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)); |
| } |
| |
| } |