Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 36043a363ad7268b6c1bf699f56c39ac4b25841e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
/*******************************************************************************
 * 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 v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA LIST - initial API and implementation
 *******************************************************************************/

package org.eclipse.papyrus.cpp.codegen.utils;

import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.papyrus.C_Cpp.ExternLibrary;
import org.eclipse.papyrus.C_Cpp.External;
import org.eclipse.papyrus.C_Cpp.NoCodeGen;
import org.eclipse.papyrus.C_Cpp.Typedef;
import org.eclipse.papyrus.C_Cpp.Visibility;
import org.eclipse.papyrus.codegen.base.GenUtils;
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 Acceleo templates.
 *
 * @author wassim, ansgar
 *
 */
public class CppGenUtils {

	public static final String ANSI_C_LIB = "AnsiCLibrary"; //$NON-NLS-1$

	/**
	 * Return a list of template parameters without type
	 *
	 * @param classifier
	 * @return
	 */
	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;
	}

	/**
	 * 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
	 *
	 * @param type
	 * @return
	 */
	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$
	}

	/**
	 * 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
	 */
	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 (GenUtils.hasStereotype(ne, External.class) ||
				GenUtils.hasStereotypeTree(ne, NoCodeGen.class) ||
				GenUtils.hasStereotypeTree(ne, ExternLibrary.class)) {
			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()) {
			// don't add qualified name for specific top-level namespace "root".
			// TODO: specific workaround for the way Qompass creates its target model. Needs to be removed.
			if (!((ns.getOwner() == null) && ns.getName().equals("root"))) { //$NON-NLS-1$
				qName = ns.getName() + "::" + qName; //$NON-NLS-1$
			}
		}
		if (qName.contains("::")) { //$NON-NLS-1$
			// is a qualified name => make path absolute
			return "::" + qName; //$NON-NLS-1$
		} 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 += "::"; //$NON-NLS-1$
				}
				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 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();
		if (ne instanceof Package) {
			openNS = "namespace " + ne.getName() + " {\n"; //$NON-NLS-1$ //$NON-NLS-2$
		}
		for (Namespace ns : ne.allNamespaces()) {
			if (ns.getOwner() != null) {	// skip top-level package (useful?)
				openNS = "namespace " + ns.getName() + " {\n" + 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$
		if (ne instanceof Package) {
			closeNS = "} // of namespace " + ne.getName() + "\n"; //$NON-NLS-1$ //$NON-NLS-2$
		}
		for (Namespace ns : ne.allNamespaces()) {
			if (ns.getOwner() != null) {
				closeNS += "} // of namespace " + ns.getName() + "\n"; //$NON-NLS-1$ //$NON-NLS-2$
			}
		}
		return closeNS;
	}


	/**
	 * Return the C++ visibility (on generalizations) in text form. Return public, if no stereotype
	 * visibility exists
	 *
	 * @param element
	 * @return
	 */
	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;
	}

	/**
	 * 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$
	}
}

Back to the top