blob: 1dd06e93f46765a26b8e0b4ed6a17cefc643d465 [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2015-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:
* Ansgar Radermacher ansgar.radermacher@cea.fr
* Shuai Li (CEA LIST) <shuai.li@cea.fr> - Bug 530651
* Shuai Li (CEA LIST) <shuai.li@cea.fr> - Bug 531771
* Yoann Farre (CIL4Sys) <yoann.farre@cil4sys.com> - Bug 543072, Bug 559033
*
*****************************************************************************/
package org.eclipse.papyrus.designer.components.transformation.cpp.xtend
import org.eclipse.papyrus.designer.components.modellibs.core.transformations.AbstractCompToOO
import org.eclipse.papyrus.designer.components.modellibs.core.transformations.Constants
import org.eclipse.papyrus.designer.components.transformation.PortInfo
import org.eclipse.papyrus.designer.components.transformation.PortUtils
import org.eclipse.papyrus.designer.components.transformation.component.PrefixConstants
import org.eclipse.papyrus.designer.components.transformation.extensions.IOOTrafo
import org.eclipse.papyrus.designer.languages.common.profile.Codegen.NoCodeGen
import org.eclipse.papyrus.designer.languages.cpp.profile.C_Cpp.Array
import org.eclipse.papyrus.designer.languages.cpp.profile.C_Cpp.Const
import org.eclipse.papyrus.designer.languages.cpp.profile.C_Cpp.ConstInit
import org.eclipse.papyrus.designer.languages.cpp.profile.C_Cpp.Friend
import org.eclipse.papyrus.designer.languages.cpp.profile.C_Cpp.Inline
import org.eclipse.papyrus.designer.languages.cpp.profile.C_Cpp.Ptr
import org.eclipse.papyrus.designer.languages.cpp.profile.C_Cpp.Ref
import org.eclipse.papyrus.designer.languages.cpp.profile.C_Cpp.Typedef
import org.eclipse.papyrus.designer.languages.cpp.profile.C_Cpp.Variadic
import org.eclipse.papyrus.designer.languages.cpp.profile.C_Cpp.Virtual
import org.eclipse.papyrus.designer.languages.cpp.profile.C_Cpp.Volatile
import org.eclipse.papyrus.designer.transformation.base.utils.CopyUtils
import org.eclipse.papyrus.designer.transformation.base.utils.PartsUtil
import org.eclipse.papyrus.designer.transformation.base.utils.TransformationException
import org.eclipse.papyrus.designer.transformation.core.transformations.LazyCopier
import org.eclipse.papyrus.uml.tools.utils.ConnectorUtil
import org.eclipse.papyrus.uml.tools.utils.StereotypeUtil
import org.eclipse.uml2.uml.AggregationKind
import org.eclipse.uml2.uml.BehavioredClassifier
import org.eclipse.uml2.uml.Class
import org.eclipse.uml2.uml.Classifier
import org.eclipse.uml2.uml.Connector
import org.eclipse.uml2.uml.ConnectorEnd
import org.eclipse.uml2.uml.Element
import org.eclipse.uml2.uml.Interface
import org.eclipse.uml2.uml.OpaqueBehavior
import org.eclipse.uml2.uml.Operation
import org.eclipse.uml2.uml.Parameter
import org.eclipse.uml2.uml.Port
import org.eclipse.uml2.uml.PrimitiveType
import org.eclipse.uml2.uml.Property
import org.eclipse.uml2.uml.UMLPackage
import org.eclipse.uml2.uml.util.UMLUtil
import org.eclipse.uml2.uml.Type
/**
* This class realizes the transformation from component-based to object-oriented
* models for C++. It refines the abstract component to OO transformation
*/
class StaticCppToOO extends AbstractCompToOO implements IOOTrafo {
override init(LazyCopier copier, Class bootloader) {
super.init(copier, bootloader)
progLang = "C/C++";
}
override nameRef(Property attribute) {
CppUtils.nameRef(attribute)
}
/**
* Apply a pointer to the (typed) element
*/
override applyRef(Element element) {
StereotypeUtil.apply(element, Ptr)
}
/**
* Create a ptr_type for the given typed element
* This is required, in order to create a vector of pointers
* instead of a pointer towards a vector
*/
def createPtrType(Type type) {
// check, if an "_ptr" type already exists
if (!type.name.endsWith("_ptr")) {
val pkg = type.nearestPackage
val name = type.name + "_ptr"
var ptrType = pkg.getPackagedElement(name) as PrimitiveType
if (ptrType === null) {
ptrType = pkg.createOwnedPrimitiveType(name)
val typedef = StereotypeUtil.applyApp(ptrType, Typedef)
// dependencies do not work for primitive types, as a
// workaround, prefix typedef with class (ok for pointer types)
typedef.definition = '''class «type.name» *'''
}
return ptrType;
}
else {
return type;
}
}
override getRef(Property attribute) {
CppUtils.getRef(attribute)
}
/**
* Transform parts if necessary.
*
* If the bootloader is responsible for creating an instance (if it is a
* abstract type), mark the associated part as a C++ pointer. We do not want
* to change the aggregation kind, since it remains logically a composition,
* it is merely an implementation issue that it must be a pointer for C++ if
* the concrete type is not yet known.
*
* @param compositeImplementation
* a (composite) component
*/
override transformParts(Class compositeImplementation) {
for (Property attribute : PartsUtil.getParts(compositeImplementation)) {
val type = attribute.type
if (type instanceof Class) {
// => requires adaptations of boot-loader which is then only
// responsible for creating instances corresponding to types
if (instantiateViaBootloader(type)) {
StereotypeUtil.apply(attribute, Ptr)
}
}
}
}
override addGetPortOperation(Class implementation, PortInfo portInfo, Interface providedIntf, String portName) {
// port provides an interface, add "get_p" operation and implementation
val opName = PrefixConstants.getP_Prefix + portName
var op = implementation.getOwnedOperation(opName, null, null)
if (op !== null) {
// operation already exists. Assume that user wants to
// override standard delegation
if (op.type != providedIntf) {
op.createOwnedParameter(Constants.retParamName, providedIntf)
}
} else {
op = implementation.createOwnedOperation(opName, null, null, providedIntf)
val retParam = op.getOwnedParameters().get(0)
retParam.setName(Constants.retParamName)
applyRef(retParam)
val behavior = implementation.createOwnedBehavior(opName,
UMLPackage.eINSTANCE.getOpaqueBehavior()) as OpaqueBehavior
op.getMethods().add(behavior)
val ces = ConnectorUtil.getDelegations(implementation, portInfo.getModelPort())
var connectedDelegations = 0
for (ce : ces) {
val role = ce.role
if (role instanceof Port) {
val rolePort = role as Port
if (rolePort.provideds.contains(providedIntf)) {
connectedDelegations++
}
} else {
if (role instanceof Property) {
val roleType = (role as Property).type;
if (roleType instanceof BehavioredClassifier &&
(roleType as BehavioredClassifier).getInterfaceRealization(null, providedIntf) !== null) {
connectedDelegations++
}
}
}
}
val isMultipleDelegations = connectedDelegations > 1
var String body
if (isMultipleDelegations) {
val dc = new CreateMultiRefClass(this, copier)
body = dc.createDelegationProvided(implementation, ces, portInfo.port, portName, providedIntf);
} else if (!ces.empty) {
// If there is an delegation to one, and one only, inner property, delegate to it
// Make distinction between delegation to a port of the class typing an inner property or the property itself
body = "return "
var i = 0
while (i < ces.size && body.equals("return ")) {
val part = ces.get(i).partWithPort
val role = ces.get(i).role
if (role instanceof Port) {
val rolePort = role as Port
if (rolePort.provideds.contains(providedIntf)) {
if (rolePort.provideds.size > 1 || !(rolePort.type instanceof Interface)) {
body +=
'''«part.nameRef»«PrefixConstants.getP_Prefix»«role.name»«providedIntf.name»();'''
} else {
body += '''«part.nameRef»«PrefixConstants.getP_Prefix»«role.name»();'''
}
}
} else {
// role is not a port: connector connects directly to a
// structural feature without passing via a port
if (role instanceof Property) {
val roleType = (role as Property).type;
if (roleType instanceof BehavioredClassifier &&
(roleType as BehavioredClassifier).getInterfaceRealization(null, providedIntf) !==
null) {
body += role.name
}
}
}
i++
}
if (body.equals("return ")) {
body += "NULL;"
}
} else {
// no delegation, check whether port implements provided interface
var implementsIntf = implementation.getInterfaceRealization(null, providedIntf) !== null
if (!implementsIntf) {
// The extended port itself is not copied to the target
// model (since referenced via a stereotype). Therefore,
// a port of an extended port still points to the
// original model. We try whether the providedIntf
// within the target model is within the interface
// realizations.
val providedIntfInCopy = copier.getCopy(providedIntf)
implementsIntf = implementation.getInterfaceRealization(null, providedIntfInCopy) !== null
}
if (implementsIntf) {
body = "return this;"
} else {
throw new RuntimeException(
String.format(
"Interface <%s> provided by port <%s> of class <%s> is not implemented by the component itself nor does the port delegate to a part",
providedIntf.name, portName, implementation.name))
}
}
behavior.getLanguages().add(progLang)
behavior.getBodies().add(body)
}
}
override addConnectPortOperation(Class implementation, PortInfo portInfo, Interface requiredIntf, String portName) {
// port requires an interface, add "connect_p" operation and implementation
val opName = PrefixConstants.connectQ_Prefix + portName
if (implementation.getOwnedOperation(opName, null, null) !== null) {
// do not add the operation, if it already exists. This means that the
// user wants to override it with custom behavior. In case of extended
// ports, we may have to do that.
} else {
var op = implementation.createOwnedOperation(opName, null, null)
val refParam = op.createOwnedParameter("ref", requiredIntf)
applyRef(refParam)
val behavior = implementation.createOwnedBehavior(opName,
UMLPackage.eINSTANCE.getOpaqueBehavior()) as OpaqueBehavior
op.getMethods().add(behavior)
val ces = ConnectorUtil.getDelegations(implementation, portInfo.getModelPort())
// if there is an delegation to an inner property, delegate to it
// Make distinction between delegation to component (with a port) or
// "normal" class (without).
var String body = ""
if (!ces.empty) {
var i = 0
while (i < ces.size) {
val part = ces.get(i).partWithPort
val role = ces.get(i).role
var bodyConstruction = ""
if (role instanceof Port) {
val rolePort = role as Port
if (rolePort.requireds.contains(requiredIntf)) {
// in case of a delegation, use name of target port which might be different
var targetOpName = PrefixConstants.connectQ_Prefix + role.name
if (rolePort.requireds.size > 1 || !(rolePort.type instanceof Interface)) {
targetOpName += requiredIntf.name
}
if (part.upper > 1) {
for (var j = 0; j < part.upper; j++) {
bodyConstruction += '''«part.name»[«i»].«targetOpName»(ref);'''
}
} else {
bodyConstruction += '''«part.nameRef»«targetOpName»(ref);'''
}
}
} else {
if (part.type instanceof Classifier &&
(part.type as Classifier).getAllUsedInterfaces().contains(requiredIntf)) {
if (part.upper > 1) {
for (var j = 0; j < part.upper; j++) {
bodyConstruction += '''«part.name»[«i»];'''
}
} else {
bodyConstruction += '''«part.name»;'''
}
}
}
if (!bodyConstruction.empty) {
body += bodyConstruction
if (i < ces.size - 1) {
body += NL
}
}
i++
}
} else {
val attributeName = PrefixConstants.attributePrefix + portName
val port = portInfo.port
if (port.upper != 1) {
val dc = new CreateMultiRefClass(this, copier)
body += dc.createDelegationRequired(implementation, portName, requiredIntf);
} else {
// No delegation and no multiple providers
var attr = implementation.getOwnedAttribute(attributeName, null)
if (attr === null || attr instanceof Port) {
attr = implementation.createOwnedAttribute(attributeName, requiredIntf)
CopyUtils.copyMultElemModifiers(portInfo.port, attr)
// is shared (should store a reference)
attr.setAggregation(AggregationKind.SHARED_LITERAL)
}
body += '''«attributeName» = ref;'''
}
}
behavior.getLanguages().add(progLang)
behavior.getBodies().add(body)
// -------------------------
// add body to get-connection operation (which exists already if the port is also
// owned, since it is synchronized automatically during model edit)
// getConnQ prefix may be empty to indicate that the port is accessed directly
// TODO: reconsider optimization that delegated required ports do not have a
// local attribute and associated operation (an inner class may delegate, but the
// composite may be using it as well).
if ((PrefixConstants.getConnQ_Prefix.length() > 0) && (!ces.empty)) {
val getConnOpName = PrefixConstants.getConnQ_Prefix + portName
var getConnOp = implementation.getOwnedOperation(getConnOpName, null, null)
if (getConnOp === null) {
getConnOp = implementation.createOwnedOperation(getConnOpName, null, null, requiredIntf)
val retParam = op.getOwnedParameters().get(0)
retParam.setName(Constants.retParamName)
applyRef(retParam)
}
val getConnBehavior = implementation.createOwnedBehavior(getConnOpName,
UMLPackage.eINSTANCE.getOpaqueBehavior()) as OpaqueBehavior
getConnOp.getMethods().add(getConnBehavior)
// no delegation
val String name = PrefixConstants.attributePrefix + portName
body = '''return «name»;'''
behavior.getLanguages().add(progLang)
behavior.getBodies().add(body.trim)
} else {
}
}
}
override connectPorts(Connector connector, ConnectorEnd receptacleEnd,
ConnectorEnd facetEnd, Port subPort) throws TransformationException {
val association = connector.type
if ((receptacleEnd.role instanceof Port) && (facetEnd.role instanceof Port)) {
val facetPort = facetEnd.role as Port
val receptaclePort = receptacleEnd.role as Port
val facetPI = PortInfo.fromSubPort(facetPort, subPort)
val receptaclePI = PortInfo.fromSubPort(receptaclePort, subPort)
if ((facetPI.getProvided() !== null) && (receptaclePI.getRequired() !== null)) {
val facetPart = facetEnd.partWithPort
val receptaclePart = receptacleEnd.partWithPort
var subPortName = ""
if(subPort !== null) subPortName += "_" + subPort.name
var result = ""
if (receptaclePI.getRequireds().size == 1 && receptaclePI.port.type instanceof Interface) { // receptaclePort requires only 1 interface
var facetPortName = ""
if (facetPI.getProvideds().size == 1 && facetPI.port.type instanceof Interface) { // Original behavior in case of single interface, where we always connect without check of interface consistency
facetPortName = facetPI.name
} else if (facetPI.getProvideds().size >= 1 &&
facetPI.getProvideds().contains(receptaclePI.getRequired())) {
facetPortName += facetPI.name + receptaclePI.getRequired().name
}
if (!facetPortName.empty) {
val setter = '''«receptaclePart.nameRef»connect_«receptaclePort.name»«subPortName»'''
if (facetPart.upper > 1) {
for (var i = 0; i < facetPart.upper; i++) {
val getter = '''«facetPart.name»[«i»].get_«facetPortName»«subPortName»()'''
result += '''«setter»(«getter»);''' + NL
}
} else {
val getter = '''«facetPart.nameRef»get_«facetPortName»«subPortName»()'''
result += '''«setter»(«getter»);''' + NL
}
}
} else { // receptaclePort requires several interfaces or one interface through a class
for (requiredInterface : receptaclePI.getRequireds()) {
var receptaclePortName = receptaclePI.name + requiredInterface.name
var facetPortName = ""
if (facetPI.getProvideds().contains(requiredInterface)) {
facetPortName += facetPI.name
if (facetPI.getProvideds().size > 1 || !(facetPI.port.type instanceof Interface)) {
facetPortName += requiredInterface.name
}
}
if (!facetPortName.empty) {
val setter = '''«receptaclePart.nameRef»connect_«receptaclePortName»«subPortName»'''
if (facetPart.upper > 1) {
for (var i = 0; i < facetPart.upper; i++) {
val getter = '''«facetPart.name»[«i»].get_«facetPortName»«subPortName»()'''
result += '''«setter»(«getter»);''' + NL
}
} else {
val getter = '''«facetPart.nameRef»get_«facetPortName»«subPortName»()'''
result += '''«setter»(«getter»);''' + NL
}
}
}
}
return result
}
} else if (receptacleEnd.role instanceof Port) {
// only the receptacle end is of type port.
val Port receptaclePort = receptacleEnd.role as Port
if (PortUtils.getRequired(receptaclePort) !== null) {
val facetPart = facetEnd.role as Property
val receptaclePart = facetEnd.partWithPort
var result = ""
if (receptaclePort.getRequireds().size > 1) { // receptaclePort requires several interfaces
for (requiredInterface : receptaclePort.getRequireds()) {
var receptaclePortName = receptaclePort.name + requiredInterface.name
val setter = '''«receptaclePart.nameRef»connect_«receptaclePortName»'''
if (facetPart.upper > 1) {
for (var i = 0; i < facetPart.upper; i++) {
val getter = '''«facetPart.name»[«i»]'''
result += '''«setter»(«getter»);''' + NL
}
} else {
val getter = '''«facetPart.getRef»'''
result += '''«setter»(«getter»);''' + NL
}
}
} else {
val setter = '''«receptaclePart.nameRef»connect_«receptaclePort.name»'''
if (facetPart.upper > 1) {
for (var i = 0; i < facetPart.upper; i++) {
val getter = '''«facetPart.name»[«i»]'''
result += '''«setter»(«getter»);''' + NL
}
} else {
val getter = '''«facetPart.getRef»'''
result += '''«setter»(«getter»);''' + NL
}
}
return result
}
} else if (facetEnd.role instanceof Port) {
// only the facet end is of type port. Unsupported combination
val facetPort = facetEnd.role as Port
if (PortUtils.getProvided(facetPort) !== null) {
val facetPart = facetEnd.partWithPort
val receptaclePart = facetEnd.role as Property
var result = ""
if (facetPort.getProvideds().size > 1) { // facetPort provides several interfaces
for (providedInterface : facetPort.getProvideds()) {
var facetPortName = facetPort.name + providedInterface.name
val setter = receptaclePart.name
if (facetPart.upper > 1) {
for (var i = 0; i < facetPart.upper; i++) {
val getter = '''«facetPart.name»[«i»].get_«facetPortName»();'''
result += '''«setter» = «getter»;''' + NL
}
} else {
val getter = '''«facetPart.nameRef»get_«facetPortName»();'''
result += '''«setter» = «getter»;''' + NL
}
}
} else {
val setter = receptaclePart.name
if (facetPart.upper > 1) {
for (var i = 0; i < facetPart.upper; i++) {
val getter = '''«facetPart.name»[«i»].get_«facetPort.name»();'''
result += '''«setter» = «getter»;''' + NL
}
} else {
val getter = '''«facetPart.nameRef»get_«facetPort.name»();'''
result += '''«setter» = «getter»;''' + NL
}
}
return result
}
} else if (association !== null) {
// both connector ends do not target ports. In this case, we require that the connector is typed
// with an association. We use this association to find out which end is navigable and assume that
// the part pointed to by the other end is a pointer that gets initialized with the part of the
// navigable end.
val facetPart = facetEnd.role as Property
val receptaclePart = receptacleEnd.role as Property
val assocProp1 = association.getMemberEnd(null, facetPart.type)
if ((assocProp1 !== null) && assocProp1.isNavigable) {
var setter = '''«receptaclePart.name».«assocProp1.name»'''
val getter = '''«facetPart.name»'''
return '''«setter» = «getter»;''' + NL
}
} else {
// not handled (a connector not targeting a port must be typed)
throw new TransformationException("Connector <" + connector.name +
"> does not use ports, but it is not typed (only connectors between ports should not be typed)")
}
return ""
}
def void copyCppOperationAndParameterStereotypes(Element source, Element target) {
if ((source instanceof Operation && target instanceof Operation) ||
(source instanceof Parameter && target instanceof Parameter)) {
if (UMLUtil.getStereotypeApplication(source, Array) !== null) {
StereotypeUtil.apply(target, Array)
}
if (UMLUtil.getStereotypeApplication(source, Const) !== null) {
StereotypeUtil.apply(target, Const)
}
if (UMLUtil.getStereotypeApplication(source, Ptr) !== null) {
val sourceStereotype = UMLUtil.getStereotypeApplication(source, Ptr)
StereotypeUtil.apply(target, Ptr)
var stereotype = UMLUtil.getStereotypeApplication(target, Ptr)
if (stereotype !== null) {
if (sourceStereotype.declaration !== null) {
stereotype.declaration = new String(sourceStereotype.declaration)
}
}
}
if (UMLUtil.getStereotypeApplication(source, Ref) !== null) {
val sourceStereotype = UMLUtil.getStereotypeApplication(source, Ref)
StereotypeUtil.apply(target, Ref)
var stereotype = UMLUtil.getStereotypeApplication(target, Ref)
if (stereotype !== null) {
if (sourceStereotype.declaration !== null) {
stereotype.declaration = new String(sourceStereotype.declaration)
}
}
}
if (UMLUtil.getStereotypeApplication(source, NoCodeGen) !== null) {
StereotypeUtil.apply(target, NoCodeGen)
}
if (UMLUtil.getStereotypeApplication(source, Volatile) !== null) {
StereotypeUtil.apply(target, Volatile)
}
if (UMLUtil.getStereotypeApplication(source, ConstInit) !== null) {
val sourceStereotype = UMLUtil.getStereotypeApplication(source, ConstInit)
StereotypeUtil.apply(target, Array)
var stereotype = UMLUtil.getStereotypeApplication(target, ConstInit)
if (stereotype !== null) {
if (sourceStereotype.initialisation !== null) {
stereotype.initialisation = new String(sourceStereotype.initialisation)
}
}
}
if (UMLUtil.getStereotypeApplication(source, Friend) !== null) {
StereotypeUtil.apply(target, Friend)
}
if (UMLUtil.getStereotypeApplication(source, Inline) !== null) {
StereotypeUtil.apply(target, Inline)
}
if (UMLUtil.getStereotypeApplication(source, Virtual) !== null) {
StereotypeUtil.apply(target, Virtual)
}
if (UMLUtil.getStereotypeApplication(source, Variadic) !== null) {
StereotypeUtil.apply(target, Variadic)
}
if (source instanceof Operation && target instanceof Operation) {
if ((source as Operation).ownedParameters.size == (target as Operation).ownedParameters.size) {
var i = 0
while (i < (source as Operation).ownedParameters.size) {
copyCppOperationAndParameterStereotypes((source as Operation).ownedParameters.get(i),
(target as Operation).ownedParameters.get(i))
i++
}
}
}
}
}
}