blob: 2e3ba8f91356c29a86246a19d31b554b68a4259c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006 - 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:
* CEA LIST - initial API and implementation
*******************************************************************************/
package org.eclipse.papyrus.designer.languages.c.codegen.utils;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.papyrus.designer.languages.common.base.GenUtils;
import org.eclipse.papyrus.designer.languages.common.profile.Codegen.NoCodeGen;
import org.eclipse.papyrus.designer.languages.cpp.profile.C_Cpp.ExternLibrary;
import org.eclipse.papyrus.designer.languages.cpp.profile.C_Cpp.External;
import org.eclipse.papyrus.designer.languages.cpp.profile.C_Cpp.Typedef;
import org.eclipse.papyrus.designer.languages.cpp.profile.C_Cpp.Visibility;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.ClassifierTemplateParameter;
import org.eclipse.uml2.uml.ConnectableElementTemplateParameter;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.LiteralBoolean;
import org.eclipse.uml2.uml.LiteralInteger;
import org.eclipse.uml2.uml.LiteralString;
import org.eclipse.uml2.uml.LiteralUnlimitedNatural;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.Namespace;
import org.eclipse.uml2.uml.OperationTemplateParameter;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.ParameterableElement;
import org.eclipse.uml2.uml.PrimitiveType;
import org.eclipse.uml2.uml.TemplateParameter;
import org.eclipse.uml2.uml.VisibilityKind;
import org.eclipse.uml2.uml.util.UMLUtil;
/**
* Some utilities specific to C code generation: a set of static methods that is intended
* for use from xtend templates.
*
* @author wassim, ansgar
* TODO: Unify with C++ GenUtils?
*/
public class CGenUtils {
public static final String ANSI_C_LIB = "AnsiCLibrary"; //$NON-NLS-1$
/**
* If true, qualified names use all namespace elements.
* If false, the top-level package is skipped
*/
public static boolean USE_ALL_NS = true;
/**
*
* @param classifier
* @return a list of template parameters without type
*/
public static String getTemplateParametersWoType(Classifier classifier) {
String tparamWoType = ""; //$NON-NLS-1$
Iterator<TemplateParameter> it = GenUtils.getTemplateParameters(classifier).iterator();
while (it.hasNext()) {
TemplateParameter currentTParam = it.next();
tparamWoType = tparamWoType + GenUtils.getTemplateName(currentTParam);
if (it.hasNext()) {
tparamWoType = tparamWoType + ", "; //$NON-NLS-1$
}
}
return tparamWoType;
}
/**
* @param type
* a primitive type
* @return The standard UML and MARTE libraries do not apply the stereotype "Typedef". Yet, we want to treat these
* types in an identical way, i.e. we use a typedef to the associated primitive C++ type
*/
public static String getStdtypes(PrimitiveType type) {
Object owner = type.getOwner();
String owningPkgName = ""; //$NON-NLS-1$
if (owner instanceof Package) {
owningPkgName = ((Package) owner).getName();
}
if (owningPkgName.equals("PrimitiveTypes") || // used in UML >= 2.4 //$NON-NLS-1$
owningPkgName.equals("UMLPrimitiveTypes") || // used in UML < 2.4 //$NON-NLS-1$
owningPkgName.equals("MARTE_PrimitivesTypes")) { //$NON-NLS-1$
String td = null;
String name = type.getName();
if (name.equals("Boolean")) { //$NON-NLS-1$
td = "bool"; //$NON-NLS-1$
} else if (name.equals("Integer")) { //$NON-NLS-1$
td = "int"; //$NON-NLS-1$
} else if (name.equals("Unlimited Natural")) { //$NON-NLS-1$
td = "unsigned long"; //$NON-NLS-1$
} else if (name.equals("Real")) { //$NON-NLS-1$
td = "float"; //$NON-NLS-1$
} else if (name.equals("String")) { //$NON-NLS-1$
td = "const char *"; //$NON-NLS-1$
} else if (name.equals("Unlimited Natural")) { //$NON-NLS-1$
td = "unsigned long"; //$NON-NLS-1$
}
if (td != null) {
return "typedef " + td + " " + name + ";"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
// else unknown within UMLPrimitiveTypes, treat in standard way
}
return ""; //$NON-NLS-1$
}
/**
* Specific handling of CORBA basic types. These have the property that the
* type name itself differs from the declared primitive type (in the C++ mapping)
* TODO: This should be handled by a modular extension defined in CORBA modules.
*
* @param type
* A primitive type
* @return a new type name if for CORBA, null otherwise
*/
public static String transformCORBABaseTypes(PrimitiveType type) {
Object owner = type.getOwner();
String owningPkgName = ""; //$NON-NLS-1$
if (owner instanceof Package) {
owningPkgName = ((Package) owner).getName();
}
if (owningPkgName.equals("CORBA")) { //$NON-NLS-1$
String cppName = "CORBA::"; //$NON-NLS-1$
boolean nextUpper = true; // first char is upper
for (char ch : type.getName().toCharArray()) {
if (ch == ' ') { // after " " is upper
nextUpper = true;
} else {
if (nextUpper) {
cppName += Character.toUpperCase(ch);
} else {
cppName += ch;
}
nextUpper = false;
}
}
return cppName;
}
return null;
}
/**
* Return a kind of qualifiedName, except if
* - The named element has the stereotype External or NoCodeGen
* - The named element is part of the ANSI C library
* - The named element is a primitive type that has no further definition via a stereotype (TODO: why is this required/useful?)
*
* @param ne
* @return a qualified name
*/
public static String cppQualifiedName(NamedElement ne) {
if (ne == null) {
return "undefined"; //$NON-NLS-1$
}
Object owner = ne.getOwner();
String owningPkgName = ""; //$NON-NLS-1$
if (owner instanceof Package) {
owningPkgName = ((Package) owner).getName();
}
if (ne instanceof PrimitiveType) {
// is a standard type
String cppType = transformCORBABaseTypes((PrimitiveType) ne);
if (cppType != null) {
return cppType;
}
}
if (GenUtils.hasStereotypeTree(ne, NoCodeGen.class)) {
return ne.getName();
} else if (GenUtils.hasStereotypeTree(ne, ExternLibrary.class)) {
ExternLibrary extLibrary = GenUtils.getApplicationTree(ne, ExternLibrary.class);
// handle potential prefix defined for all types within the external library
return GenUtils.maskNull(extLibrary.getPrefix()) + ne.getName();
} else if (GenUtils.hasStereotype(ne, External.class)) {
// check External after ExternLibrary stereotype, in order to take an
// eventual prefix defined by the latter into account
return ne.getName();
} else if (owningPkgName.equals(ANSI_C_LIB)) {
// always use the short name for types within the ANSI C library
return ne.getName();
} else if (owner instanceof ClassifierTemplateParameter) {
// return short name for template in Type
return ne.getName();
}
String qName = ne.getName();
if (currentNS == ne.getNamespace()) {
// return simple name, if in current namespace
return qName;
}
if (ne instanceof PrimitiveType) {
if (!GenUtils.hasStereotype(ne, Typedef.class) && (getStdtypes((PrimitiveType) ne).length() == 0)) {
// is a primitive type without further definition and not a standard primitive type
// => assume that it is a external type without namespace
return qName;
}
}
for (Namespace ns : ne.allNamespaces()) {
if (ns.getOwner() != null || USE_ALL_NS) {
qName = ns.getName() + NamedElement.SEPARATOR + qName;
}
}
if (qName == null) {
GenUtils.checkProxy(ne);
return "undefined"; //$NON-NLS-1$
}
if (qName.contains(NamedElement.SEPARATOR)) {
// is a qualified name => make path absolute
return NamedElement.SEPARATOR + qName;
} else {
return qName;
}
}
/**
* Returns the string that is used within a C++ template declaration, e.g. the "Class XY" in template<class XY>.
*
* @return the template type formated as string
*/
public static String getTemplateTypeName(TemplateParameter templateParam) {
String prefix = ""; //$NON-NLS-1$
String name = ""; //$NON-NLS-1$
// Retrieve name of the ParameteredElement (when possible = it is a NamedElement
ParameterableElement pElt = templateParam.getParameteredElement();
if ((pElt != null) && (pElt instanceof NamedElement)) {
name = ((NamedElement) pElt).getName();
if (templateParam instanceof ClassifierTemplateParameter) {
// prefix name with "class" to indicate that the following type is a classifier
prefix = "class "; //$NON-NLS-1$
} else if (templateParam instanceof OperationTemplateParameter) {
// no prefix
} else if (templateParam instanceof ConnectableElementTemplateParameter) {
// no prefix
} else {// uml2TemplateParameter instanceof TemplateParameter
// TODO: literal integer is a value specification, but we use the fact that is also a named element
// e.g. would produce an <int N> parameter and ignore the value behind (which is ok for the
// declaration of the template, but not for its use.
if (pElt instanceof LiteralInteger) {
prefix = "int "; //$NON-NLS-1$
} else if (pElt instanceof LiteralString) {
prefix = "String "; //$NON-NLS-1$
} else if (pElt instanceof LiteralBoolean) {
prefix = "Boolean "; //$NON-NLS-1$
} else if (pElt instanceof LiteralUnlimitedNatural) {
prefix = "UnlimitedNatural "; //$NON-NLS-1$
} else {
prefix = pElt.eClass().getName() + " "; //$NON-NLS-1$
}
}
} else {
name = "undefined"; //$NON-NLS-1$
}
return (prefix + name);
}
/**
* Return a C++ namespace definition for a named element
*
* @param ne
* a named element
* @return a C++ namespace definition for a named element
*/
public static String getNamespace(NamedElement ne) {
String namespace = ""; //$NON-NLS-1$
for (Namespace ns : ne.allNamespaces()) {
if (ns.getOwner() != null) {
String nsName = ns.getName();
if (!namespace.equals("")) { //$NON-NLS-1$
nsName += NamedElement.SEPARATOR;
}
namespace = nsName + namespace;
}
}
if (!namespace.equals("")) { //$NON-NLS-1$
namespace = "\n" + "using namespace " + namespace + ";\n"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
return namespace;
}
/**
* Return all namespaces, with specific case of a package (which needs to
* include itself as a namespace unlike other elements (e.g. classes)
*/
public static EList<Namespace> getAllNamespaces(NamedElement ne) {
if (ne instanceof Package) {
EList<Namespace> allNamespaces = new BasicEList<Namespace>();
allNamespaces.add((Package) ne);
allNamespaces.addAll(ne.allNamespaces());
return allNamespaces;
} else {
return ne.allNamespaces();
}
}
/**
* Return a C++ open-namespace definition for a named element
*
* @param ne
* a named element
* @return a C++ open-namespace definition for a named element
*/
public static String openNS(NamedElement ne) {
String openNS = ""; //$NON-NLS-1$
currentNS = ne.getNamespace();
for (Namespace ns : getAllNamespaces(ne)) {
if (ns.getOwner() != null || USE_ALL_NS) {
openNS = "namespace " + ns.getName() + " {\n" + openNS; //$NON-NLS-1$ //$NON-NLS-2$
}
}
return openNS;
}
/**
* Return a C++ open-namespace definition for a named element, without spaces and line breaks
*
* @param ne
* a named element
* @return a C++ open-namespace definition for a named element, without spaces and line breaks
*/
public static String openNSMinimal(NamedElement ne) {
String openNS = ""; //$NON-NLS-1$
currentNS = ne.getNamespace();
for (Namespace ns : getAllNamespaces(ne)) {
if (ns.getOwner() != null || USE_ALL_NS) {
openNS = "namespace " + ns.getName() + " {" + openNS; //$NON-NLS-1$ //$NON-NLS-2$
}
}
return openNS;
}
/**
* Return a C++ close-namespace definition for a named element
*
* @param ne
* a named element
* @return a C++ close-namespace definition for a named element
*/
public static String closeNS(NamedElement ne) {
String closeNS = ""; //$NON-NLS-1$
for (Namespace ns : getAllNamespaces(ne)) {
if (ns.getOwner() != null || USE_ALL_NS) {
closeNS += "} // of namespace " + ns.getName() + "\n"; //$NON-NLS-1$ //$NON-NLS-2$
}
}
return closeNS;
}
/**
* Return a C++ close-namespace definition for a named element, without spaces and line breaks
*
* @param ne
* a named element
* @return a C++ close-namespace definition for a named element, without spaces and line breaks
*/
public static String closeNSMinimal(NamedElement ne) {
String closeNS = ""; //$NON-NLS-1$
for (Namespace ns : getAllNamespaces(ne)) {
if (ns.getOwner() != null || USE_ALL_NS) {
closeNS += "}"; //$NON-NLS-1$
}
}
return closeNS;
}
/**
* @return Return the C visibility (on generalizations) in text form. Return public, if no stereotype
* visibility exists
*
* @param element
*/
public static String getGeneralizationVisibility(Element element) {
// get visibility and target name
Visibility cppVisibility = UMLUtil.getStereotypeApplication(element, Visibility.class);
if (cppVisibility != null) {
return cppVisibility.getValue();
} else {
return "public"; //$NON-NLS-1$
}
}
private static Namespace currentNS;
private static VisibilityKind currVisibility = null;
private static final Pattern EmptySectionRegex = Pattern.compile("^\\s*$"); //$NON-NLS-1$
/**
* Update the current visibility to the specified value without writing this value to
* the output. This is to be used when setting the default visibility of a class/struct.
*/
public static void resetVisibility(VisibilityKind v) {
currVisibility = v;
}
/**
* Get the current visibility so we can restore it when generating nested classes
*/
public static VisibilityKind getCurrentVisibility() {
return currVisibility;
}
/**
* Create a section of code with the appropriate visibility. Merges the content with
* the previously declared visibility (if appropriate). Ignore empty content.
*/
public static String getSection(VisibilityKind visibility, String content) {
// Bug 425208: Don't update the visibility until we know for sure that it
// will be written to the output.
VisibilityKind newVisibility = null;
if (!visibility.equals(currVisibility)) {
newVisibility = visibility;
}
// Filter out empty sections.
Matcher m = EmptySectionRegex.matcher(content);
if (content.isEmpty() || m.matches()) {
return ""; //$NON-NLS-1$
}
// Don't write duplicate visibility modifiers.
if (newVisibility == null) {
return content;
}
currVisibility = newVisibility;
return currVisibility.toString() + ":\n" + content; //$NON-NLS-1$
}
}