Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: d63fd1b36557ba84d1e88aa83183ad4af7c5bb42 (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
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
/*****************************************************************************
 * Copyright (c) 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:
 *  Ansgar Radermacher  ansgar.radermacher@cea.fr  
 *  Christophe JOUVRAY
 *
 *****************************************************************************/

package org.eclipse.papyrus.qompass.designer.core.acceleo;

import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.UniqueEList;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.xmi.XMLResource;
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.uml.tools.utils.StereotypeUtil;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Dependency;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Interface;
import org.eclipse.uml2.uml.InterfaceRealization;
import org.eclipse.uml2.uml.Model;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.Namespace;
import org.eclipse.uml2.uml.Operation;
import org.eclipse.uml2.uml.Parameter;
import org.eclipse.uml2.uml.ParameterDirectionKind;
import org.eclipse.uml2.uml.PrimitiveType;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Relationship;
import org.eclipse.uml2.uml.Type;
import org.eclipse.uml2.uml.UMLPackage;
import org.eclipse.uml2.uml.util.UMLUtil;


/**
 * Collection of utility functions. In contrast to core.Utils, it is chiefly used by Acceleo
 * scripts
 * 
 * @author ansgar
 * 
 */

public class UMLTool {

	private static final String UNDERSCORE = "_"; //$NON-NLS-1$

	/**
	 * @param operation
	 * @return all in and inout parameters of an operation
	 */
	public static EList<Parameter> parametersInInout(Operation operation) {
		EList<Parameter> list = new BasicEList<Parameter>();
		for(Parameter parameter : operation.getOwnedParameters()) {
			if((parameter.getDirection().getValue() == ParameterDirectionKind.IN) ||
				(parameter.getDirection().getValue() == ParameterDirectionKind.INOUT)) {
				list.add(parameter);
			}
		}
		return list;
	}

	/**
	 * @param operation
	 * @return all out, inout and return parameters of an operation
	 */
	public static EList<Parameter> parametersOutInout(Operation operation) {
		EList<Parameter> list = new BasicEList<Parameter>();
		for(Parameter parameter : operation.getOwnedParameters()) {
			if((parameter.getDirection().getValue() == ParameterDirectionKind.OUT) ||
				(parameter.getDirection().getValue() == ParameterDirectionKind.RETURN) ||
				(parameter.getDirection().getValue() == ParameterDirectionKind.INOUT)) {
				list.add(parameter);
			}
		}
		return list;
	}

	/**
	 * @param operation
	 * @return all non-return parameters of an operation
	 */
	public static EList<Parameter> parametersNonRet(Operation operation) {
		EList<Parameter> list = new BasicEList<Parameter>();
		for(Parameter parameter : operation.getOwnedParameters()) {
			if(parameter.getDirection().getValue() != ParameterDirectionKind.RETURN) {
				list.add(parameter);
			}
		}
		return list;
	}

	/**
	 * A small helper function that makes names compliant with variable
	 * names in programming languages such as C++ or Java
	 * Unlike varName2, replace scoping signs as well
	 */
	public static String varName(NamedElement element) {
		return varName(element.getName());
	}

	/**
	 * A small helper function that makes names compliant with variable
	 * names in programming languages such as C++ or Java
	 * Unlike varName2, replace scoping signs as well
	 */
	public static String varName(String umlName) {
		umlName = umlName.replace(".", UNDERSCORE);  //$NON-NLS-1$
		umlName = umlName.replace(Namespace.SEPARATOR, UNDERSCORE);
		return varName2(umlName);
	}

	/**
	 * A small helper function that makes names compliant with variable
	 * names in programming languages such as C++ or Java
	 * TODO: obviously, it is not complete (e.g. in case of "$", national characters ("ä", "é", ...) , ...)
	 */
	public static String varName2(NamedElement element) {
		String umlName = element.getName();
		return varName2(umlName);
	}

	/**
	 * Like varName, but does not replace "." with "_"
	 * 
	 * @param umlName
	 * @return
	 */
	public static String varName2(String umlName) {
		umlName = umlName.replace(" ", UNDERSCORE); //$NON-NLS-1$
		umlName = umlName.replace("-", UNDERSCORE); //$NON-NLS-1$
		umlName = umlName.replace("+", UNDERSCORE); //$NON-NLS-1$
		umlName = umlName.replace("?", UNDERSCORE); //$NON-NLS-1$
		return umlName;
	}

	public static EList<Namespace> usedNamespaces(NamedElement element) {
		EList<Namespace> list = new BasicEList<Namespace>(element.allNamespaces());

		if(list.size() < 1) {
			return null;
		}
		list.remove(list.size() - 1); // remove last element (top-level)
		return list;
	}

	/**
	 * Return a sequence of namespaces for a given element, starting from the "bottom"
	 * one, i.e. the one in which the element is contained. It will end before the
	 * searchNS namespace is reached. Returns null, if the element is not contained
	 * within the search namespace.
	 * 
	 * @param element
	 * @param searchNS
	 * @return
	 */
	public static EList<Namespace> relativePath(Element element, Namespace searchNS) {
		EList<Namespace> pathList = new BasicEList<Namespace>();
		Element owner = element.getOwner();
		if(!(owner instanceof Namespace)) {
			// happens, if element is contained in a template signature
			return null;
		}
		Namespace ns = (Namespace)owner;
		while(ns != null) {
			if(ns == searchNS) {
				return pathList;
			}
			pathList.add(ns);

			ns = (Namespace)ns.getOwner();
		}
		return null;
	}

	/**
	 * This method returns all types that are referenced by a classifier. This includes
	 * attribute types, types within operations as well as inherited types.
	 * This is useful to generate the #includes
	 * (which is used by the Acceleo code within the packaging plugin)
	 */
	public static EList<Classifier> getReferencedClassifiers(Classifier classifier) {
		EList<Classifier> list = new UniqueEList<Classifier>();
		list.addAll(classifier.parents());

		if(classifier instanceof Class) {
			// get classifiers referenced by attributes
			for(Operation operation : ((Class)classifier).getOwnedOperations()) {
				for(Parameter parameter : operation.getOwnedParameters()) {
					Type type = parameter.getType();
					if(type instanceof Classifier) {
						list.add((Classifier)type);
					}
				}
			}

			// get classifiers referenced by attributes
			for(Property attribute : ((Class)classifier).getOwnedAttributes()) {
				Type type = attribute.getType();
				if(type instanceof Classifier) {
					list.add((Classifier)type);
				}
			}
		}
		return list;
	}

	/**
	 * TODO: copy&paste from C++ generator (& specific for C++)
	 * 
	 * @param ne
	 * @return
	 */
	public static String cppQName(NamedElement ne) {
		if((StereotypeUtil.isApplied(ne, External.class)) || (StereotypeUtil.isApplied(ne, NoCodeGen.class))) {
			return ne.getName();
		} else {
			String qName = ne.getName();
			for(Namespace ns : ne.allNamespaces()) {
				if(!(ns instanceof Model)) {
					qName = ns.getName() + "::" + qName; //$NON-NLS-1$
				}
			}
			return qName;
		}
	}

	/**
	 * Return the name of a parameter. In case of a return parameter, always return the name
	 * "retValue". This is quite useful for marshalling operations (which need to assign a
	 * value to the return parameter, which is sometimes not initialized)
	 * 
	 * @param parameter
	 * @return
	 */
	public static String paramName(Parameter parameter) {
		if(parameter.getDirection().getValue() == ParameterDirectionKind.RETURN) {
			return "retValue"; //$NON-NLS-1$
		} else {
			return parameter.getName();
		}
	}

	/**
	 * Return the interface which owns an operation that is implemented by a class.
	 * Context: a class might implement several interfaces by defining their operations.
	 * The operation is useful in the context of state-machines: when a transition is triggered by
	 * the call of an operation of the class, we'd like to know which interceptor (for which interface)
	 * belongs to it (since the operations are enumerated within each interface).
	 * TOOD: move operation into state-chart java code
	 * 
	 * @param operation
	 * @return the interface which the operation belongs
	 */
	public static Interface implementsInterface(Operation operation) {
		Element owner = operation.getOwner();
		if(owner instanceof Class) {
			String name = operation.getName();
			EList<Type> types = new BasicEList<Type>();
			for(Parameter parameter : operation.getOwnedParameters()) {
				types.add(parameter.getType());
			}
			// loop over implemented realizations. Do not rely on FCM derivedElement information
			// as it might be missing on some models (it would point from an operation of the class
			// to the associated operation of the interface)
			for(InterfaceRealization ir : ((Class)owner).getInterfaceRealizations()) {
				// check for types to allow for overloading
				Operation candidate = ir.getContract().getOwnedOperation(name, null, types);
				if(candidate != null) {
					return ir.getContract();
				}
			}
		}
		else if(owner instanceof Interface) {
			return (Interface)owner;
		}
		return null;
	}

	/**
	 * Convenience function: Declare a dependency from source to destination. The function checks,
	 * if a dependency already exists to avoid double dependencies.
	 * 
	 * @param source
	 *        source type of the dependency
	 * @param dest
	 *        destination of the dependency
	 */
	public static void declareDependency(Type source, Type dest) {
		// check, if a relationship already exists
		for(Relationship dependency : source.getRelationships(UMLPackage.eINSTANCE.getDependency())) {
			if(((Dependency)dependency).getSuppliers().contains(dest)) {
				return;
			}
		}
		source.createDependency(dest);
	}

	/**
	 * Get the URI fragment of an element within your model
	 * Useful for transmitting model references
	 * 
	 * @param element
	 *        a UML element
	 * @return
	 */
	public static String fragment(Element element) {
		Resource resource = element.eResource();
		// TODO: use EcoreUtil getURI (InternalEObject) instead?

		if(resource instanceof XMLResource) {
			XMLResource xmlResource = (XMLResource)resource;
			return "\"" + xmlResource.getURIFragment(element) + "\"";  //$NON-NLS-1$//$NON-NLS-2$
		}
		return null;
	}

	/**
	 * Get the XML (URI) Id of an element within your model
	 * Useful for transmitting model references
	 * 
	 * @param element
	 *        a UML element
	 * @return
	 */
	public static String xmlID(Element element) {
		Resource resource = element.eResource();
		// TODO: use EcoreUtil getURI (InternalEObject) instead?

		if(resource instanceof XMLResource) {
			XMLResource xmlResource = (XMLResource)resource;
			return xmlResource.getID(element);
		}
		return null;
	}

	public static String getURI(Element element) {
		Resource resource = element.eResource();
		if(resource != null) {
			URI uri = resource.getURI();
			return uri.toString();
		}
		return null;
	}

	/**
	 * 
	 * TODO: Specific to C++
	 * 
	 * @param type
	 *        a type
	 * @return return the definition of a typedef, if the type has been defined via
	 *         the stereotype CppType of the Cpp profile
	 */
	public static String dereferenceTypedef(Type type) {
		if(type instanceof PrimitiveType) {
			Typedef cppType = UMLUtil.getStereotypeApplication(type, Typedef.class);
			if(cppType != null) {
				return cppType.getDefinition();
			}
		}
		return type.getQualifiedName();

	}

	public static String encodeID(String uri) {
		// _ becomes escape character. original _ is __, '-' becomes _M
		return uri.
				replace(UNDERSCORE, UNDERSCORE + UNDERSCORE).
				replace("-", "_M"); //$NON-NLS-1$ //$NON-NLS-2$
	}

	public static String decodeID(String encodedURI) {
		String result = ""; //$NON-NLS-1$
		for(int i = 0; i < encodedURI.length(); i++) {
			char c = encodedURI.charAt(i);
			if(c == '_') {
				char next = encodedURI.charAt(i + 1);
				if(next == 'M')
					result += '-';
				else if(next == '_')
					result += '_';
				i++;
			}
			else {
				result += c;
			}
		}
		return result;
	}
}

Back to the top