blob: 1d25cb0e9f6c0ee8225ecd067e858e31b1de6725 [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2013 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:
* Ansgar Radermacher ansgar.radermacher@cea.fr
* Shuai Li (CEA LIST) <shuai.li@cea.fr> - Bug 530651
*
*****************************************************************************/
package org.eclipse.papyrus.designer.components.transformation;
import java.util.List;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.papyrus.designer.components.FCM.PortKind;
import org.eclipse.papyrus.designer.components.FCM.TemplatePort;
import org.eclipse.papyrus.designer.languages.common.base.ElementUtils;
import org.eclipse.papyrus.designer.transformation.core.utils.ComparisonUtils;
import org.eclipse.papyrus.uml.tools.utils.StereotypeUtil;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.EncapsulatedClassifier;
import org.eclipse.uml2.uml.Interface;
import org.eclipse.uml2.uml.Port;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Type;
import org.eclipse.uml2.uml.util.UMLUtil;
public class PortUtils {
/**
* Return the provided interface associated with the UML port, i.e.
* the derived attribute of the FCM profile
*
* @param port
* the UML port
* @return the provided interface
*/
public static Interface getProvided(Port port) {
if (port.getProvideds().size() > 0) {
// return first standard UML provided port
return port.getProvideds().get(0);
}
return null;
}
/**
* Return all provided interfaces associated with the UML port, i.e.
* the derived attribute of the FCM profile
*
* @param port
* the UML port
* @return the provided interface
*/
public static List<Interface> getProvideds(Port port) {
if (port.getProvideds().size() > 0) {
// return all provided interfaces
return port.getProvideds();
}
return null;
}
/**
* Return the required interface associated with the UML port, i.e.
* the derived attribute of the FCM profile
*
* @param port
* the UML port
* @return the required interface
*/
public static Interface getRequired(Port port) {
if (port.getRequireds().size() > 0) {
// return first standard UML required port
return port.getRequireds().get(0);
}
return null;
}
/**
* Return all required interfaces associated with the UML port, i.e.
* the derived attribute of the FCM profile
*
* @param port
* the UML port
* @return the required interface
*/
public static List<Interface> getRequireds(Port port) {
if (port.getRequireds().size() > 0) {
// return all required interfaces
return port.getRequireds();
}
return null;
}
/**
* Return the FCM port (static profile) from a given UML port
*
* @param port
* a port
* @return the associated FCM port
*/
public static org.eclipse.papyrus.designer.components.FCM.Port getFCMport(Port port) {
return UMLUtil.getStereotypeApplication(port, org.eclipse.papyrus.designer.components.FCM.Port.class);
}
/**
* Return the value of the FCM stereotype attribute "type"
*
* @param port
* a UML port
* @return the value of the type attribute
*/
public static Type getFCMType(Port port) {
org.eclipse.papyrus.designer.components.FCM.Port fcmPort = getFCMport(port);
if (fcmPort != null) {
return fcmPort.getType();
}
return null;
}
/**
* Returns all ports (including inherited ones) for an encapsulated classifier
* It will also flatten extended ports
*
* @param ec
* an encapsulated classifier
* @return the list of ports, including inherited ones
*/
public static EList<Port> getAllPorts(EncapsulatedClassifier ec) {
EList<Port> ports = new BasicEList<Port>();
for (Property attribute : ec.getAllAttributes()) {
if (attribute instanceof Port) {
ports.add((Port) attribute);
}
}
return ports;
}
/**
* Returns all ports for an encapsulated classifier. Inherited ports are
* included, except if the superclass is already a component implementation.
* The motivation for this function is that ports inherited by a
* component implementation have already corresponding operations/attributes,
* only ports inherited by types need these definitions in subclasses.
* TODO: support for abstract implementations that partially implement ports
*
* @param ec
* an encapsulated classifier
* @return the list of ports, (partly) including inherited ones
*/
public static EList<Port> getAllPorts2(EncapsulatedClassifier ec) {
EList<Port> ports = new BasicEList<Port>();
ports.addAll(ec.getOwnedPorts());
for (Classifier general : ec.getGenerals()) {
if ((general instanceof EncapsulatedClassifier) && (general.isAbstract())) {
ports.addAll(getAllPorts2((EncapsulatedClassifier) general));
}
}
return ports;
}
/**
* When given a list of ports, flatten the extended ports within this list
* and return a list of port-infos, i.e. information about ports.
*
* @param ports
* A list of ports
* @return A list of port-infos
*/
public static EList<PortInfo> flattenExtendedPorts(EList<Port> ports) {
EList<PortInfo> portInfos = new BasicEList<PortInfo>();
for (Port port : ports) {
portInfos.addAll(flattenExtendedPort(port));
}
return portInfos;
}
/**
* Flatten the given extended port and return a list of port-infos.
*
* @param port
* @return A list of port-infos
*/
public static EList<PortInfo> flattenExtendedPort(Port port) {
EList<PortInfo> portInfos = new BasicEList<PortInfo>();
if (isExtendedPort(port)) {
org.eclipse.papyrus.designer.components.FCM.Port fcmPort = getFCMport(port);
org.eclipse.uml2.uml.Class cl;
if (isTemplatePort(port)) {
TemplatePort tp = UMLUtil.getStereotypeApplication(port, TemplatePort.class);
if (tp.getBoundType() == null) {
Activator.log.debug("Bound type of template port is null, choosing base class of kind instead"); //$NON-NLS-1$
cl = fcmPort.getKind().getBase_Class();
} else {
cl = tp.getBoundType().getBase_Class();
}
} else {
cl = fcmPort.getKind().getBase_Class();
}
if ((cl != null) && (getAllPorts(cl).size() > 0)) {
EList<Port> extendedPorts = getAllPorts(cl);
for (Port extendedPort : extendedPorts) {
portInfos.add(new PortInfo(extendedPort, port));
}
}
} else {
portInfos.add(new PortInfo(port, null));
}
return portInfos;
}
/**
* Return true, if the passed port is an extended port
*
* @param port
* a UML port
* @return true, if port is an extended port
*/
public static boolean isExtendedPort(Port port) {
org.eclipse.papyrus.designer.components.FCM.Port fcmPort = getFCMport(port);
if ((fcmPort != null) && (fcmPort.getKind() != null)) {
org.eclipse.uml2.uml.Class cl = fcmPort.getKind().getBase_Class();
return ((cl != null) && (getAllPorts(cl).size() > 0));
}
return false;
}
/**
* Return true, if the passed port is a template port
*
* @param port
* a UML port
* @return true, if port is a template port
*/
public static boolean isTemplatePort(Port port) {
return StereotypeUtil.isApplied(port, TemplatePort.class);
}
/**
* Return the port kind, an element of the static profile
*
* @param port
* a UML port
* @return the port kind
*/
public static PortKind getKind(Port port) {
org.eclipse.papyrus.designer.components.FCM.Port fcmPort = getFCMport(port);
if (fcmPort != null) {
return fcmPort.getKind();
}
return null;
}
/**
* Check whether two ports have the same port kind. Since different models apparently
* use different Java instances for the same port kind, the check is therefore based
* on the equality of full qualified name.
*
* @param portA
* @param portB
* @return true, if port kinds are identical
*/
public static boolean sameKinds(Port portA, Port portB) {
PortKind kindA = getKind(portA);
PortKind kindB = getKind(portB);
return ComparisonUtils.sameObject(kindA, kindB);
}
/**
* Check whether two ports match, i.e. have the same type & kind but different conjugation (assembly)
* or same type, kind and conjugation (delegation). The ports must have the FCM port stereotype.
*
* @param portA
* first port
* @param portB
* second port
* @param isAssembly
* true, if the ports should be connected by an assembly connector (i.e. no delegation)
* @return true, if ports match
*/
public static boolean matches(Port portA, Port portB, boolean isAssembly) {
org.eclipse.papyrus.designer.components.FCM.Port fcmPortA = UMLUtil.getStereotypeApplication(portA, org.eclipse.papyrus.designer.components.FCM.Port.class);
org.eclipse.papyrus.designer.components.FCM.Port fcmPortB = UMLUtil.getStereotypeApplication(portB, org.eclipse.papyrus.designer.components.FCM.Port.class);
if ((fcmPortA == null) || (fcmPortB == null)) {
return false;
}
// require strict identity for type (unlike the kind, both must be in the same resource set)
boolean sameTypeAndKind = (fcmPortA.getType() == fcmPortB.getType()) &&
ComparisonUtils.sameObject(fcmPortA.getKind(), fcmPortB.getKind());
if (isAssembly) {
return (sameTypeAndKind && portA.isConjugated() != portB.isConjugated());
} else {
// delegation
return (sameTypeAndKind && portA.isConjugated() == portB.isConjugated());
}
}
/**
* Check whether two ports are compatible. i.e. either match or are compatible interface wise
*
* @param portA
* @param portB
* @param isAssembly
* true, if the ports should be connected by an assembly connector (i.e. no delegation)
* @return true, if the two ports are compatible
*/
public static boolean isCompatible(Port portA, Port portB, boolean isAssembly) {
if (matches(portA, portB, isAssembly)) {
return true;
}
// no match found, try weaker condition: find 1st match for provided ...
if (isAssembly) {
Interface reqA = PortUtils.getRequired(portA);
Interface reqB = PortUtils.getRequired(portB);
Interface prodA = PortUtils.getProvided(portA);
Interface prodB = PortUtils.getProvided(portB);
return (((reqA == null) && isSubInterface(prodA, reqB))
|| ((reqB == null) && isSubInterface(prodB, reqA))
|| (isSubInterface(prodA, reqB) && isSubInterface(prodB, reqA)));
} else {
return (PortUtils.getProvided(portA) == PortUtils.getProvided(portB)) &&
(PortUtils.getRequired(portB) == PortUtils.getRequired(portA));
}
}
/**
* return true, if intfA is a sub-interface of intfB, i.e. either both interfaces are identical or one of the
* super-classes (generalizations of intfA) is identical to B.
* more general than interfaceB.
*
* @param intfA
* @param intfB
* @return true, if intfA is a sub-interface of intfB
*/
public static boolean isSubInterface(Interface intfA, Interface intfB) {
return (intfA == intfB) || (intfA != null && intfA.getGenerals().contains(intfB));
}
/**
* Return a port with a given name and type
* TODO: Currently unused. Would be useful for bootloader generation, but
* bootloader generation is not specific for component based approach, i.e.
* cannot access PortUtils
*
* @param component
* the name of a component
* @param portName
* the name of a potential port
* @param qTypeName
* the required type of that port
* @return a port of the component with the given name and type combination or null
*/
public static Port getPortWithType(Class component, String portName, String qTypeName) {
if (component != null) {
Element portElem = ElementUtils.getNamedElementFromList(component.getAllAttributes(), portName);
if (portElem instanceof Port) {
Port port = (Port) portElem;
// check, if port has expected type
if (qTypeName.equals(port.getType().getQualifiedName())) {
return port;
}
}
}
return null;
}
}