blob: d2a713f23ea596bdcf943dea92464a6029ac4b30 [file] [log] [blame]
package org.eclipse.papyrus.designer.components.transformation.cpp.xtend
import java.util.List
import org.eclipse.emf.common.util.BasicEList
import org.eclipse.emf.ecore.util.EcoreUtil
import org.eclipse.papyrus.designer.components.transformation.component.PrefixConstants
import org.eclipse.papyrus.designer.languages.common.base.ElementUtils
import org.eclipse.papyrus.designer.transformation.core.transformations.LazyCopier
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.ConnectorEnd
import org.eclipse.uml2.uml.Interface
import org.eclipse.uml2.uml.OpaqueBehavior
import org.eclipse.uml2.uml.ParameterDirectionKind
import org.eclipse.uml2.uml.Port
import org.eclipse.uml2.uml.Property
import org.eclipse.uml2.uml.Type
import org.eclipse.uml2.uml.UMLPackage
import org.eclipse.papyrus.designer.languages.common.profile.Codegen.TemplateBinding
import org.eclipse.papyrus.uml.tools.utils.PackageUtil
import org.eclipse.papyrus.designer.languages.cpp.library.CppUriConstants
/**
* Enable ports that delegate to several inner parts/ports, see bug 531771
*
* This delegation is bidirectional, i.e. applies to provided and required
* ports (methods being called and methods that are calling, respectively)
*
* For provided ports: return the reference to a broadcast class in the
* get_XXX methods.
* The user has to verify that the port of the composite has the right
* multiplicity.
*
* For required ports: call all connect_XXX of inner parts in the connect_XXX
* of the class that owns inner parts.
*
* Finally the createConnection method should handle connecting to a port that
* returns a vector. In this case we expect the receptacle of the connection
* to be of multiplicity several.
*/
class CreateMultiRefClass {
StaticCppToOO cppToOO
LazyCopier copier
static String progLang = "C++"
new(StaticCppToOO cppToOO, LazyCopier copier) {
this.cppToOO = cppToOO
this.copier = copier
}
def String createDelegationProvided(Class implementation, List<ConnectorEnd> ces, Port port, String portName,
Interface providedIntf) {
// We need to create a port representing the delegating port
val attributeName = PrefixConstants.attributePrefix + portName
var attr = implementation.getOwnedAttribute(attributeName, null)
if (attr === null || attr instanceof Port) {
val broadcastClass = getOrCreateBroadcastClass(providedIntf)
// Create the attribute in implementation for the port and type it with the broadcast class
attr = implementation.createOwnedAttribute(attributeName, broadcastClass)
attr.setAggregation(AggregationKind.SHARED_LITERAL)
// Connect delegated parts (either properties or ports) to the broadcast class
return createDelegationConnCode(ces, broadcastClass, portName, providedIntf)
}
return ""
}
def String createDelegationConnCode(List<ConnectorEnd> ces, Class broadcastClass, String portName,
Interface providedIntf) '''
«val attributeName = PrefixConstants.attributePrefix + portName»
// generated broadcast class uses generic port name
«val connectOpName = PrefixConstants.connectQ_Prefix + "port"»
if («attributeName» == NULL) {
«attributeName» = new «broadcastClass.qualifiedName»();
}
«FOR ce : ces»
«val part = ce.partWithPort»
«val role = ce.role»
«IF role instanceof Port»
«val rolePort = role as Port»
«IF rolePort.provideds.contains(providedIntf)»
«IF rolePort.provideds.size > 1 ||
((rolePort.provideds.size == 1) && !(rolePort.type instanceof Interface))»
«IF part.upper > 1»
«FOR i : 0 ..< part.upper»
«attributeName»->«connectOpName»(«part.name»[«i»].«PrefixConstants.getP_Prefix»«role.name»«providedIntf.name»());
«ENDFOR»
«ELSE»
«attributeName»->«connectOpName»(«cppToOO.nameRef(part)»«PrefixConstants.getP_Prefix»«role.name»«providedIntf.name»());
«ENDIF»
«ELSE»
«IF part.upper > 1»
«FOR i : 0 ..< part.upper»
«attributeName»->«connectOpName»(«part.name»[«i»].«PrefixConstants.getP_Prefix»«role.name»());
«ENDFOR»
«ELSE»
«attributeName»->«connectOpName»(«cppToOO.nameRef(part)»«PrefixConstants.getP_Prefix»«role.name»());
«ENDIF»
«ENDIF»
«ENDIF»
«ELSE»
«IF role instanceof Property»
«val roleType = (role as Property).type»
«IF roleType instanceof BehavioredClassifier &&
(roleType as BehavioredClassifier).getInterfaceRealization(null, providedIntf) !== null»
«IF (role as Property).upper > 1»
«FOR i : 0 ..< (role as Property).upper»
«attributeName»->«connectOpName»(«role.name»[«i»]);
«ENDFOR»
«ELSE»
«attributeName»->«connectOpName»(«role.name»);
«ENDIF»
«ENDIF»
«ENDIF»
«ENDIF»
«ENDFOR»
return «attributeName»;
'''
def String createDelegationRequired(Class implementation, String portName, Interface requiredIntf) {
val attributeName = PrefixConstants.attributePrefix + portName
// generated broadcast class uses generic port name
val opName = PrefixConstants.connectQ_Prefix + "port"
// We need to create a port representing the delegating port
var attr = implementation.getOwnedAttribute(attributeName, null)
if (attr === null || attr instanceof Port) {
val broadcastClass = getOrCreateBroadcastClass(requiredIntf)
// Create the attribute in implementation for the port and type it with the broadcast class
attr = implementation.createOwnedAttribute(attributeName, broadcastClass)
cppToOO.applyRef(attr);
// CopyUtils.copyMultElemModifiers(port, attr)
// Delegate reference management to broadcastCass
return '''
if (!«attributeName») {
«attributeName» = new «broadcastClass.qualifiedName»();
}
«attributeName»->«opName»(ref);
'''
}
return "";
}
/**
* Create a new broadcast class or create an existing
*/
def Class getOrCreateBroadcastClass(Interface intf) {
val broadcastClassName = '''Broadcast_«intf.name»'''
var broadcastClass = intf.nearestPackage.getPackagedElement(broadcastClassName)
if (broadcastClass instanceof Class) {
return broadcastClass as Class
} else {
return createBroadcastClass(intf)
}
}
/**
* Create a broadcast class for a given interface. This class will manage multiple references for the
* given interface and provide an API for calling each method of the interface.
* The called operation is called for each stored reference. If there is a return value, only the
* return value of the last call is returned.
*/
def Class createBroadcastClass(Interface intf) {
// Create the broadcast class
// use generic name port, since the implementation is not specific to the port using it
val opName = PrefixConstants.connectQ_Prefix + "port"
val broadcastClassName = '''Broadcast_«intf.name»'''
val broadcastClass = intf.nearestPackage.createOwnedClass(broadcastClassName, false)
// Set its interface realization
broadcastClass.createInterfaceRealization("broadcast" + intf, intf)
// Create the references vector (based on std::vector from STL)
PackageUtil.loadPackage(CppUriConstants.STL_LIB, copier.source.eResource.resourceSet)
var vector = ElementUtils.getQualifiedElementFromRS(copier.source, PrefixConstants.TYPE_FOR_MULTI_RECEPTACLE) as Type;
if (vector !== null) {
vector = copier.getCopy(vector);
} else {
throw new RuntimeException(
String.format(
"Can not find type %s. Thus, unable to create suitable connect operation in component to OO transformation",
PrefixConstants.TYPE_FOR_MULTI_RECEPTACLE));
}
var references = broadcastClass.createOwnedAttribute("references", vector)
val tBinding = StereotypeUtil.applyApp(references, TemplateBinding)
tBinding.actuals.add(cppToOO.createPtrType(intf))
// Create the connect method
val connectOperation = broadcastClass.createOwnedOperation(opName, null, null)
val connectRefParam = connectOperation.createOwnedParameter("ref", intf)
cppToOO.applyRef(connectRefParam)
val connectBehavior = broadcastClass.createOwnedBehavior(opName,
UMLPackage.eINSTANCE.getOpaqueBehavior()) as OpaqueBehavior
connectOperation.getMethods().add(connectBehavior)
connectBehavior.getLanguages().add(progLang)
connectBehavior.getBodies.add(
'''
references.push_back(ref);
'''
)
// Create operations of realized interface in broadcast class
for (operation : intf.operations) {
val parameterNames = new BasicEList<String>();
val parameterTypes = new BasicEList<Type>();
val parameterNamesWithReturn = new BasicEList<String>();
val parameterTypesWithReturn = new BasicEList<Type>();
for (parameter : operation.ownedParameters) {
if (parameter.direction != ParameterDirectionKind.RETURN_LITERAL) {
parameterNames.add(parameter.name)
parameterTypes.add(parameter.type)
}
parameterNamesWithReturn.add(parameter.name)
parameterTypesWithReturn.add(parameter.type)
}
// Copy operation of interface in class
val copiedOperation = EcoreUtil.copy(operation);
// val copiedOperation = copier.getCopy(operation);
broadcastClass.ownedOperations.add(copiedOperation);
var delegationOperation = copiedOperation;
// Copy stereotype applications
cppToOO.copyCppOperationAndParameterStereotypes(operation, copiedOperation)
// Create body
var delegateOperationCall = '''
«operation.name»(
«FOR parameterName : parameterNames SEPARATOR ", "»
«parameterName»
«ENDFOR»)
'''.toString.trim
val hasReturn = parameterNamesWithReturn.size > parameterNames.size
val delegationOperationBody = '''
for (unsigned int i = 0; i < references.size()«IF hasReturn» - 1«ENDIF»; i++) {
references[i]->«delegateOperationCall»;
}
«IF hasReturn»
return references[references.size() - 1]->«delegateOperationCall»;
«ENDIF»
'''
val delegationOperationBehavior = broadcastClass.createOwnedBehavior(opName,
UMLPackage.eINSTANCE.getOpaqueBehavior()) as OpaqueBehavior
delegationOperation.getMethods().add(delegationOperationBehavior)
delegationOperationBehavior.getLanguages().add(progLang)
delegationOperationBehavior.getBodies().add(delegationOperationBody)
}
return broadcastClass
}
}