Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 246945ce50fca7062230e35336adef335b3934bf (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
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
package org.eclipse.papyrus.qompass.designer.core.deployment;

import java.util.Iterator;
import java.util.Stack;

import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.UniqueEList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature.Setting;
import org.eclipse.papyrus.FCM.CodeGenOptions;
import org.eclipse.papyrus.FCM.DeploymentPlan;
import org.eclipse.papyrus.FCM.ImplementationGroup;
import org.eclipse.papyrus.FCM.ImplementationProperties;
import org.eclipse.papyrus.FCM.InteractionComponent;
import org.eclipse.papyrus.FCM.Target;
import org.eclipse.papyrus.qompass.designer.core.ElementFilter;
import org.eclipse.papyrus.qompass.designer.core.Utils;
import org.eclipse.papyrus.uml.tools.utils.StereotypeUtil;
import org.eclipse.uml2.common.util.UML2Util;
import org.eclipse.uml2.uml.AggregationKind;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.DirectedRelationship;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Generalization;
import org.eclipse.uml2.uml.InstanceSpecification;
import org.eclipse.uml2.uml.InstanceValue;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.PackageableElement;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Slot;
import org.eclipse.uml2.uml.StructuralFeature;
import org.eclipse.uml2.uml.Type;
import org.eclipse.uml2.uml.ValueSpecification;
import org.eclipse.uml2.uml.util.UMLUtil;

/**
 * Utilities around instances (within deployment plan)
 * [DepPlanUtils?]
 * [but missing: creation, ..., allocation?]
 * Structuration ??
 * 
 * @author ansgar
 * 
 */
public class DepUtils {

	/**
	 * Check whether a class is an eligible implementation for a certain node, i.e.
	 * has compatible requirements.
	 * Requires that setCurrentNode has been called earlier
	 * TODO: how does that work with connector reification between distributeToNode has been
	 * called??!
	 * 
	 * @param implemCandidate
	 * @return
	 */
	public static boolean isImplEligible(Class implemCandidate, EList<InstanceSpecification> nodes) {
		if(!Utils.isCompImpl(implemCandidate)) {
			return false;
		}
		if(nodes != null) {
			// now check properties
			if(nodes.size() > 1) {
				// indicates distribution
				InteractionComponent connImpl = UMLUtil.getStereotypeApplication(implemCandidate, InteractionComponent.class);
				// if a connector implementation, it must support distribution (in case of multiple nodes)
				// TODO: criteria is not optimal, since a composite may be deployed on multiple nodes,
				//   but a contained connector might still only connect local parts.
				if(connImpl != null) {
					if(!connImpl.isForDistribution()) {
						return false;
					}
				}
			}
			// must fit requirements of all nodes
			for(InstanceSpecification nodeInstance : nodes) {
				Target target = UMLUtil.getStereotypeApplication(nodeInstance, Target.class);
				if(target == null) {
					// no target information on instance => try to get this
					// information from the node referenced by the instance
					target = UMLUtil.getStereotypeApplication(DepUtils.getClassifier(nodeInstance), Target.class);
				}
				if(target != null) {
					ImplementationProperties implProps = UMLUtil.getStereotypeApplication(implemCandidate, ImplementationProperties.class);
					if(implProps != null) {
						if(!implProps.getArch().contains(target.getTargetArch())) {
							return false;
						}
						// TODO: check OS and size as well!
					}
				}
			}
		}
		return true;
	}

	/**
	 * Find a sub instance via its name. This is in particular useful for connectors that cannot be
	 * found via a slot, since UML only supports structural features (a connector is only a feature)
	 * in the definingFeature attribute of a slot.
	 * 
	 * @param owningInstance
	 *        an owning instance
	 * @param name
	 *        name of the sub-element (unqualified)
	 * @return the found sub-instance or null
	 */
	public static InstanceSpecification getNamedSubInstance(InstanceSpecification owningInstance, String name) {
		Element cdp = owningInstance.getOwner();
		String candidateName = owningInstance.getName() + "." + name; //$NON-NLS-1$
		if(cdp instanceof Package) {
			for(PackageableElement instance : ((Package)cdp).getPackagedElements()) {
				if(instance instanceof InstanceSpecification) {
					InstanceSpecification candidate = (InstanceSpecification)instance;

					if(candidateName != null) {
						if(candidateName.equals(candidate.getName())) {
							return candidate;
						}
					}
				}
			}
		}
		return null;
	}

	/**
	 * Automatically choose an implementation, i.e. if the passed classifier
	 * (1) is already an implementation, simply return it
	 * (2) is an implementation group, choose the first implementation that fits the requirements
	 * (3) is a type: choose the first implementation among the heirs that fits the requirements
	 * 
	 * @param componentType a component type or implementation (class, optionally abstract)
	 * @param nodes a set of instance specification representing nodes on which this component will be allocated
	 * @param interactive boolean indicating whether the choice should be done interactively
	 * @return a suitable implementation
	 */
	public static Class chooseImplementation(Class componentType, EList<InstanceSpecification> nodes, ImplementationChooser chooser) {
		// choose implementation automatically: get the first one that implements the passed type
		// get reference to component model, then search all classes contained in it.
		// TODO: assumption that implementations are in same package as type;

		EList<Class> implList = new BasicEList<Class>();
		if(StereotypeUtil.isApplied(componentType, ImplementationGroup.class)) {
			for(Property groupAttribute : componentType.getAttributes()) {
				Type implClass = groupAttribute.getType();
				if((implClass instanceof Class) && isImplEligible((Class)implClass, nodes)) {
					InteractionComponent connImpl = UMLUtil.getStereotypeApplication(implClass, InteractionComponent.class);
					if((connImpl != null) && connImpl.isForDistribution()) {
						// only add distributed connector, if distributed
						// don't put check into 
						if(nodes.size() > 1) {
							implList.add((Class)implClass);
						}
					}
					else {
						implList.add((Class)implClass);
					}
				}
			}
		} else if(Utils.isCompImpl(componentType)) {
			// check this after implementation group, since the latter inherits from component implementation
			return componentType;
		} else if(Utils.isCompType(componentType)) {
			for(DirectedRelationship relship : componentType.getTargetDirectedRelationships()) {
				if(relship instanceof Generalization) {
					Classifier source = ((Generalization)relship).getSpecific();
					if(source instanceof Class) {
						Class implClass = (Class)source;
						if(isImplEligible(implClass, nodes)) {
							implList.add(implClass);
						}
					}
				}
			}
		}
		if(implList.size() == 0) {
			return null;
		} else if(implList.size() == 1) {
			return implList.get(0);
		} else if(chooser != null) {
			Class impl = chooser.chooseImplementation(componentType, implList);
			if (impl != null) {
				return impl;
			}
		} else if(implList.size() > 0) {
			return implList.get(0);
		}
		return null;
	}

	/**
	 * return an instance specification for the main instance within
	 * a package.
	 * 
	 * @param cdp
	 *        the deployment plan
	 */
	public static InstanceSpecification getMainInstance(Package cdp) {
		DeploymentPlan dp = UMLUtil.getStereotypeApplication(cdp, DeploymentPlan.class);
		return dp.getMainInstance();
	}

	/**
	 * Apply the stereotype deployment plan and set the mainInstance value
	 * 
	 * @param cdp
	 *        the deployment plan
	 * @param main
	 *        instance the top-level instance specification of the plan
	 */
	public static void setMainInstance(Package cdp, InstanceSpecification mainInstance) {
		StereotypeUtil.apply(cdp, DeploymentPlan.class);
		DeploymentPlan dp = UMLUtil.getStereotypeApplication(cdp, DeploymentPlan.class);
		dp.setMainInstance(mainInstance);
	}

	/**
	 * return the implementation associated with an instance specification, i.e. a
	 * Class.
	 * 
	 * @param instance
	 * @return
	 */
	public static Class getImplementation(InstanceSpecification instance) {
		Classifier cl = getClassifier(instance);
		if(cl instanceof Class) {
			return (Class)cl;
		}
		return null;
	}

	/**
	 * Small helper function
	 * 
	 * @param instance
	 *        an instance specification
	 * @return returns true, if the stereotype ConnectorComp
	 *         is applied to the classifier associated with an instance specification
	 */
	public static boolean isConnector(InstanceSpecification instance) {
		Classifier cl = getClassifier(instance);
		return StereotypeUtil.isApplied(cl, InteractionComponent.class);
	}

	/**
	 * Return the first classifier referenced by an instance specification. Whereas UML supports
	 * a set of classifiers, we assume that that an instance specification has only one.
	 * 
	 * @param instance
	 *        the instance, for which we are interested in type information
	 */
	public static Classifier getClassifier(InstanceSpecification instance) {
		Iterator<Classifier> classifierIt = instance.getClassifiers().iterator();
		// simply return the first element (if there is any)
		if(classifierIt.hasNext()) {
			return classifierIt.next();
		}
		return null;
	}

	/**
	 * Return the first instance specification within a deployment plan that instantiates a given
	 * classifier
	 * 
	 * @param cdp
	 *        the deployment plan
	 * @param cl
	 *        the classifier
	 * @return
	 */
	public static InstanceSpecification getInstanceForClassifier(Package cdp, Classifier cl) {
		for(PackageableElement pe : cdp.getPackagedElements()) {
			if(pe instanceof InstanceSpecification) {
				InstanceSpecification is = (InstanceSpecification)pe;
				if(getClassifier(is) == cl) {
					return is;
				}
			}
		}
		return null;
	}

	/**
	 * Return the (unique) list of implementations that are contained within an
	 * instance specification
	 */
	public static EList<Classifier> getContainedImplementations(InstanceSpecification is) {
		Iterator<InstanceSpecification> instances = getContainedInstances(is).iterator();
		EList<Classifier> list = new UniqueEList<Classifier>();
		while(instances.hasNext()) {
			Classifier implementation = getClassifier(instances.next());
			list.add(implementation);
		}
		return list;
	}

	/**
	 * Return the slot that is associated with a property
	 * 
	 * @param is
	 *        an instance specification (of a class having properties)
	 * @param property
	 *        A property of the classifier associated with the passed instance specification
	 * @return the associated slot or null, if it does not exist
	 */
	public static Slot getSlot(InstanceSpecification is, Property property) {
		for (Slot slot : is.getSlots()) {
			if(slot.getDefiningFeature() == property) {
				return slot;
			}
		}
		return null;
	}

	/**
	 * Return the instance referenced by a slot value, i.e. the first instance value associated
	 * with a slot
	 * 
	 * @param slot
	 * @return
	 */
	public static InstanceSpecification getInstance(Slot slot) {
		for(ValueSpecification value : slot.getValues()) {
			// instances are accessible via ValueSpecification subclass InstanceValue
			if(value instanceof InstanceValue) {
				return ((InstanceValue)value).getInstance();
			}
		}
		return null;
	}

	/**
	 * This method returns the instances contained within a composite instance
	 * specification for an assembly.
	 */
	public static EList<InstanceSpecification> getContainedInstances(InstanceSpecification is) {
		EList<InstanceSpecification> contained = new BasicEList<InstanceSpecification>();
		for(Slot slot : is.getSlots()) {
			InstanceSpecification instance = getInstance(slot);
			if(instance != null) {
				contained.add(instance);
			}
		}
		return contained;
	}

	/**
	 * return all slots that reference an instance specification
	 * 
	 * @param is
	 * @return
	 */
	public static EList<Slot> getReferencingSlots(InstanceSpecification is) {
		EList<Slot> list = new BasicEList<Slot>();
		for(Setting setting : UML2Util.getNonNavigableInverseReferences(is)) {
			// no trigger is referencing the event any more, delete call event
			EObject eObj = setting.getEObject();
			if(eObj instanceof ValueSpecification) {
				ValueSpecification vs = (ValueSpecification)eObj;
				Element owner = vs.getOwner();
				if(owner instanceof Slot)
					list.add((Slot)owner);
			}
		}
		return list;
	}

	/**
	 * Return a slot for a given instance specification. The slot is the first one in a list of slots
	 * whose value points to the passed instance.
	 * 
	 * @param is
	 *        an instance that is contained within an composite (i.e. that
	 *        belongs to a part of this composite).
	 * @return
	 */
	public static Slot getParentSlot(InstanceSpecification is) {
		for(Slot slot : getReferencingSlots(is)) {
			if(slot.getDefiningFeature() instanceof Property) {
				if(((Property)slot.getDefiningFeature()).getAggregation() == AggregationKind.COMPOSITE_LITERAL) {
					return slot;
				}
			}
		}
		return null;
	}

	/**
	 * Return an instance specification that refers to the composite in which the
	 * passed instance is contained
	 * 
	 * @param is
	 *        an instance that is contained within an composite (i.e. that
	 *        belongs to a part of this composite).
	 * @return
	 */
	public static InstanceSpecification getParentIS(InstanceSpecification is) {
		Slot parentSlot = getParentSlot(is);
		if(parentSlot != null) {
			return parentSlot.getOwningInstance();
		}
		return null;
	}

	/**
	 * Return the access path in terms of slots to an instance specification, i.e. the
	 * set of slots starting with the slot within the main instance that identifies the next
	 * instance until arriving at the passed instance.
	 * 
	 * @param is
	 * @return
	 */
	public static Stack<Slot> getAccessPath(InstanceSpecification is) {
		Stack<Slot> path = new Stack<Slot>();
		while(is != null) {
			Slot parentSlot = getParentSlot(is);
			if(parentSlot == null) {
				break;
			}
			path.insertElementAt(parentSlot, 0);
			is = parentSlot.getOwningInstance();
		}
		return path;
	}

	/**
	 * Return true, if an instance is shared
	 * @param slot
	 * @return
	 */
	public static boolean isShared(Slot slot) {
		StructuralFeature df = slot.getDefiningFeature();
		if(df instanceof Property) {
			return ((Property)df).getAggregation() == AggregationKind.SHARED_LITERAL;
		}
		return false;
	}
	
	/**
	 * Determine which programming language should be generated for a classifier. The
	 * stereotype CodeGenOptions (which could be on any owning package) is evaluated.
	 *
	 * @param pkg a classifier
	 * @return the programming language
	 */
	public static String getLanguageFromPackage(Package pkg) {
		CodeGenOptions codeGenOpt = UMLUtil.getStereotypeApplication(pkg, CodeGenOptions.class);
		if ((codeGenOpt != null) && (codeGenOpt.getProgLanguage() != null)) {
			return codeGenOpt.getProgLanguage().getBase_Class().getName();
		}
		else if (pkg.getOwner() instanceof Package) {
			return getLanguageFromPackage((Package) pkg.getOwner());
		}
		else {
			return null;
		}
	}
	
	/**
	 * Get all instances within a package that comply with a filter criterion. Recurse into sub-packages.
	 * @param pkg Starting package for search
	 * @param instanceList list of instances
	 * @param filter filter criterion.
	 */
	public static void getAllInstances(Package pkg, EList<InstanceSpecification> instanceList, ElementFilter filter) {
		for(PackageableElement el : pkg.getPackagedElements()) {
			if(el instanceof Package) {
				getAllInstances((Package)el, instanceList, filter);
			}
			else if(el instanceof InstanceSpecification) {
				InstanceSpecification instance = (InstanceSpecification)el;
				if (filter.acceptElement(instance)) { 
					instanceList.add(instance);
				}
			}
		}
	}
	
	/**
	 * Return the first value for a slot.
	 * @param slot the slot for which the first value should be returned.
	 * @return
	 */
	public static ValueSpecification firstValue(Slot slot) {
		if (slot.getValues().size() > 0) {
			return slot.getValues().get(0);
		}
		return null;
	}
}

Back to the top