blob: 2564ecf184c0e1c32d4e9302f0ef46fdc100c8e5 [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2020 CEA LIST.
*
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* Contributors:
* Ansgar Radermacher ansgar.radermacher@cea.fr
*
*****************************************************************************/
package org.eclipse.papyrus.robotics.ros2.reverse.fromfile;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTFieldReference;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTFunctionCallExpression;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTIdExpression;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTTypeId;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.ITypedef;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamedTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.emf.common.util.URI;
import org.eclipse.papyrus.designer.languages.common.base.StringUtils;
import org.eclipse.papyrus.robotics.ros2.reverse.Activator;
import org.eclipse.papyrus.robotics.ros2.reverse.PortInfo;
import org.eclipse.papyrus.robotics.ros2.reverse.PortInfo.PortKind;
import org.eclipse.papyrus.robotics.ros2.reverse.utils.CreatePortUtils;
import org.eclipse.papyrus.robotics.ros2.reverse.utils.ServiceDefUtils;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Interface;
import org.eclipse.uml2.uml.Namespace;
public class ReversePortsFromSource {
private static final String CREATE_PUBLISHER = "create_publisher"; //$NON-NLS-1$
private static final String CREATE_SUBSCRIPTION = "create_subscription"; //$NON-NLS-1$
private static final String CREATE_SERVICE = "create_service"; //$NON-NLS-1$
private static final String CREATE_CLIENT = "create_client"; //$NON-NLS-1$
private static final String CREATE_ACTION_SRV = "rclcpp_action::create_server"; //$NON-NLS-1$
private static final String CREATE_ACTION_CLI = "rclcpp_action::create_client"; //$NON-NLS-1$
protected Class clazz;
protected List<URI> pathMapURIs;
protected ITranslationUnit itu;
List<PortInfo> portInfoList;
public ReversePortsFromSource(Class clazz, List<URI> pathMapURIs, ITranslationUnit itu) {
this.clazz = clazz;
this.pathMapURIs = pathMapURIs;
this.itu = itu;
portInfoList = new ArrayList<PortInfo>();
}
public List<PortInfo> getPortInfoList() {
return portInfoList;
}
public void updatePorts(IASTNode node) {
scanFunctions(node);
for (PortInfo pi : portInfoList) {
String nameArray[] = pi.dtQName.split(Namespace.SEPARATOR);
String pkgName;
String sdName;
// we expect that the name is a fully qualified message name <pkgName::[msg|srv]::name>
if (nameArray.length > 2) {
pkgName = nameArray[0];
sdName = nameArray[2];
}
else {
// rudimentary handling of other cases (e.g. if project setup is incorrect)
pkgName = "*"; //$NON-NLS-1$
sdName = pi.dtQName;
}
Interface sd = ServiceDefUtils.getServiceDef(clazz, pathMapURIs, pi.pk, pkgName, sdName);
if (sd != null) {
CreatePortUtils.createPort(clazz, pi, sd);
}
else {
Activator.log.debug(String.format("Cannot find service <%s>", pi.dtQName)); //$NON-NLS-1$
}
}
}
public void scanFunctions(IASTNode node) {
for (IASTNode child : node.getChildren()) {
if (child instanceof IASTFunctionDefinition) {
IASTFunctionDefinition definition = (IASTFunctionDefinition) child;
scanBody(definition.getBody());
}
if (child instanceof ICPPASTNamespaceDefinition) {
// recurse into namespaces
scanFunctions(child);
}
}
}
public static String getASTName(IASTExpression expr) {
if (expr instanceof IASTFieldReference) {
IASTName fieldName = ((IASTFieldReference) expr).getFieldName();
if (fieldName != null) {
return fieldName.toString();
}
return ""; //$NON-NLS-1$
} else {
return expr.toString();
}
}
public static PortKind getProviderFromCall(String fctName) {
if (fctName.equals(CREATE_CLIENT)) {
return PortKind.CLIENT;
} else if (fctName.equals(CREATE_SERVICE)) {
return PortKind.SERVER;
} else if (fctName.equals(CREATE_SUBSCRIPTION)) {
return PortKind.SUBSCRIBER;
} else if (fctName.equals(CREATE_PUBLISHER)) {
return PortKind.PUBLISHER;
} else if (fctName.equals(CREATE_ACTION_SRV)) {
return PortKind.ACTION_SRV;
} else if (fctName.equals(CREATE_ACTION_CLI)) {
return PortKind.ACTION_CLI;
}
return null;
}
/**
* This function obtains the PortInfo from AST data
*
* @param pk
* @param fCallExpr
* the function call expression
* @param templateId
* the template-id
*/
public PortInfo obtainCallDetails(PortKind pk, IASTFunctionCallExpression fCallExpr, ICPPASTTemplateId templateId) {
if (fCallExpr.getArguments().length >= 2) {
String topicName = fCallExpr.getArguments()[0].toString();
IASTFileLocation qosLoc = fCallExpr.getArguments()[1].getFileLocation();
char contents[] = itu.getContents();
String qos = new String(contents, qosLoc.getNodeOffset(), qosLoc.getNodeLength());
PortInfo portInfo = new PortInfo();
portInfo.pk = pk;
IASTNode argument = templateId.getTemplateArguments()[0];
if (argument instanceof IASTTypeId) {
IASTDeclSpecifier declSpecifier = ((IASTTypeId) argument).getDeclSpecifier();
if (declSpecifier instanceof ICPPASTNamedTypeSpecifier) {
// handle case that type is defined via "using" statement
IBinding binding = ((ICPPASTNamedTypeSpecifier) declSpecifier).getName().resolveBinding();
// but exclude typedef (as all messages or services are typedefs
if (binding != null && !(binding instanceof ITypedef)) {
portInfo.dtQName = binding.toString();
}
}
if (portInfo.dtQName == null) {
portInfo.dtQName = ((IASTTypeId) argument).getDeclSpecifier().toString();
}
} else {
Activator.log.debug("Unexpected template argument"); //$NON-NLS-1$
}
portInfo.topic = StringUtils.unquote(topicName.trim());
portInfo.qos = qos.trim();
return portInfo;
}
return null;
}
public void scanBody(IASTNode node) {
if (node instanceof IASTFunctionCallExpression) {
IASTFunctionCallExpression fCallExpr = (IASTFunctionCallExpression) node;
IASTExpression nameExpr = fCallExpr.getFunctionNameExpression();
if (nameExpr instanceof IASTIdExpression) {
IASTIdExpression nameId = (IASTIdExpression) nameExpr;
IASTName astName = nameId.getName();
if (astName instanceof ICPPASTTemplateId) {
// all ROS2 "ports" are created by binding one or more concrete message types
// e.g. create_publisher<geometry_msgs::msg::PoseArray>(...)
ICPPASTTemplateId templateId = (ICPPASTTemplateId) astName;
String fctName = templateId.getTemplateName().toString();
PortKind pk = getProviderFromCall(fctName);
if (pk != null) {
PortInfo portInfo = obtainCallDetails(pk, fCallExpr, templateId);
if (portInfo != null) {
portInfoList.add(portInfo);
}
}
}
}
}
for (IASTNode child : node.getChildren()) {
// recurse into all children
scanBody(child);
}
}
}