Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 342830d705a944ad2303b7a255cde6b4436327ed (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
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
/*****************************************************************************
 * 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
 *
 *****************************************************************************/

package org.eclipse.papyrus.qompass.designer.core.transformations.container;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.papyrus.FCM.BindTemplate;
import org.eclipse.papyrus.FCM.ContainerRule;
import org.eclipse.papyrus.FCM.ContainerRuleKind;
import org.eclipse.papyrus.FCM.InteractionComponent;
import org.eclipse.papyrus.FCM.InterceptionKind;
import org.eclipse.papyrus.FCM.InterceptionRule;
import org.eclipse.papyrus.FCM.Singleton;
import org.eclipse.papyrus.FCM.util.FCMUtil;
import org.eclipse.papyrus.FCM.util.MapUtil;
import org.eclipse.papyrus.qompass.designer.core.Messages;
import org.eclipse.papyrus.qompass.designer.core.PortUtils;
import org.eclipse.papyrus.qompass.designer.core.StUtils;
import org.eclipse.papyrus.qompass.designer.core.UMLTool;
import org.eclipse.papyrus.qompass.designer.core.Utils;
import org.eclipse.papyrus.qompass.designer.core.deployment.DepCreation;
import org.eclipse.papyrus.qompass.designer.core.deployment.DepUtils;
import org.eclipse.papyrus.qompass.designer.core.templates.TemplateInstantiation;
import org.eclipse.papyrus.qompass.designer.core.templates.TemplateUtils;
import org.eclipse.papyrus.qompass.designer.core.transformations.AbstractContainerTrafo;
import org.eclipse.papyrus.qompass.designer.core.transformations.LazyCopier;
import org.eclipse.papyrus.qompass.designer.core.transformations.RuleManagement;
import org.eclipse.papyrus.qompass.designer.core.transformations.TransformationContext;
import org.eclipse.papyrus.qompass.designer.core.transformations.TransformationException;
import org.eclipse.papyrus.qompass.designer.core.transformations.connector.ConnectorReification;
import org.eclipse.papyrus.uml.tools.utils.ConnectorUtil;
import org.eclipse.papyrus.uml.tools.utils.StereotypeUtil;
import org.eclipse.uml2.uml.Association;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Connector;
import org.eclipse.uml2.uml.ConnectorEnd;
import org.eclipse.uml2.uml.EncapsulatedClassifier;
import org.eclipse.uml2.uml.Feature;
import org.eclipse.uml2.uml.InstanceSpecification;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.Port;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Slot;
import org.eclipse.uml2.uml.TemplateBinding;
import org.eclipse.uml2.uml.TemplateParameter;
import org.eclipse.uml2.uml.TemplateSignature;
import org.eclipse.uml2.uml.Type;
import org.eclipse.uml2.uml.UMLPackage;
import org.eclipse.uml2.uml.ValueSpecification;
import org.eclipse.uml2.uml.VisibilityKind;
import org.eclipse.uml2.uml.util.UMLUtil;

/**
 * Make the container transformation, i.e. add elements into a container
 *
 */
public class ContainerTrafo extends AbstractContainerTrafo {

	public static final String executorPartName = "_executor"; //$NON-NLS-1$

	public static final String interceptorName = "intercept "; //$NON-NLS-1$

	public static final String containerPostfix = "_cc"; //$NON-NLS-1$

	public static final String hwContainerPostfix = "_hwcc"; //$NON-NLS-1$

	/**
	 * Constructor
	 *
	 * @param sat
	 *            information about source and target model
	 * @param tmCDP
	 *            deployment plan within target model
	 */
	public ContainerTrafo(LazyCopier copier, Package tmCDP, InstanceSpecification executorIS) {
		this.copier = copier;
		this.tmCDP = tmCDP;
		this.executorIS = executorIS;
		portInfo = new HashMap<Property, Port>();
	}

	/**
	 * creates the executor. Needs to be called *before* the other operations of
	 * this class.
	 *
	 * @param tmComponent
	 *            the implementation of a component
	 *
	 * @throws TransformationException
	 */
	@Override
	public void createContainer(Class smComponent, Class tmComponent) throws TransformationException {
		Package tmPkgOwner;
		if (tmComponent.eResource() == tmCDP.eResource()) {
			// tmComponent is in same model as tmContainer
			tmPkgOwner = tmComponent.getNearestPackage();
		}
		else {
			// tmComponent is not a member of the target model. This is the case, if tmComponent is
			// in an imported model that has not been copied during the transformation towards an intermediate
			// model.
			tmPkgOwner = MapUtil.getAndCreate(Utils.getTop(tmCDP), tmComponent.allNamespaces(), true);
		}
		// create a container with the suitable postfix
		tmContainerImpl = tmPkgOwner.createOwnedClass(tmComponent.getName() + containerPostfix, false);

		// Apply singleton to container, if original class is a singleton
		boolean isSingleton = StereotypeUtil.isApplied(smComponent, Singleton.class);
		if (isSingleton) {
			StereotypeUtil.apply(tmContainerImpl, Singleton.class);
		}
		LazyCopier.copyID(tmComponent, tmContainerImpl, containerPostfix);

		// add part and slot corresponding to component;
		executorPart = tmContainerImpl.createOwnedAttribute(executorPartName, tmComponent);
		LazyCopier.copyID(tmComponent, executorPart, "e"); //$NON-NLS-1$

		executorPart.setIsComposite(true);

		// copy ports from the executor (tmComponent) to the container
		// (tmContainerImpl)
		// TODO: connectors still point to the ports of the executor, implying a non-well-formed
		// model. Yet code generation works, as port names are identical
		for (Port port : PortUtils.getAllPorts(tmComponent)) {
			// copy port
			if (port.getVisibility() == VisibilityKind.PUBLIC_LITERAL) {
				Port newPort = EcoreUtil.copy(port); // don't use Qompass copy, since this is not a copy from source to target model
				tmContainerImpl.getOwnedAttributes().add(newPort);
				StUtils.copyStereotypes(port, newPort);

				// create delegation for application port
				Connector containerDelegation = tmContainerImpl.createOwnedConnector("delegation " + port.getName()); //$NON-NLS-1$
				LazyCopier.copyID(tmContainerImpl, containerDelegation);
				ConnectorEnd end1 = containerDelegation.createEnd();
				end1.setRole(newPort);
				ConnectorEnd end2 = containerDelegation.createEnd();
				end2.setPartWithPort(executorPart);
				end2.setRole(port);
			}
		}

		// Inheritance issues:
		// currently: container never copies inheritance relationships, but copies inherited ports into container
		// Problem: polymorphism would stop working.
		// Objective: use inheritance, if possible, but avoid that container inherits from an original non-abstract class (avoid duplication of attributes, ...)
		// Option: container inherits from fully abstract super-classes (might have to consider indirect super-classes).
		// All connectors and container extension remain within container (i.e. no inheritance of connectors and container extensions)
		// Remaining problems: Imagine that component A inherits from component B and both have state-charts.
		// => containers of A and B would define a state-Machine and an event pool.
		// => No shared pool (but anyway: semantics of this construct is not very clear).
		containers.put(tmComponent, this);
	}

	/**
	 * Create a container for a HW class. This is an additional software class
	 * within the system. There is no executor part, i.e. executorPart remains
	 *
	 * @param tmComponent
	 *            the implementation of a component
	 *
	 * @throws TransformationException
	 */
	public void createHwContainer(Class tmComponent) throws TransformationException {
		Package tmPkgOwner = (Package) tmComponent.getOwner();
		tmContainerImpl = tmPkgOwner.createOwnedClass(tmComponent.getName() + hwContainerPostfix, false);
		LazyCopier.copyID(tmComponent, tmContainerImpl, hwContainerPostfix);

		// register created container
		containers.put(tmComponent, this);
	}

	/**
	 * Create an instance of the container (UML instance specification)
	 *
	 * @param tmComponent
	 *            the component within the target model for which we want to create an instance
	 *
	 * @param tmIS
	 *            the existing instance specification for the component before container expansion. This
	 *            instance specification becomes the new instance specification of the executor.
	 * @param context
	 *            Additional information about the container that is used by instance configurators
	 */
	@Override
	public InstanceSpecification createContainerInstance(Class tmComponent, InstanceSpecification tmIS) throws TransformationException {
		// create an instance specification for the container
		InstanceSpecification containerIS =
				(InstanceSpecification) tmCDP.createPackagedElement(tmIS.getName(), UMLPackage.eINSTANCE.getInstanceSpecification());
		// assign new name to original instance specification which reflects
		// role within containment hierarchy
		tmIS.setName(tmIS.getName() + "." + executorPartName); //$NON-NLS-1$
		containerIS.getClassifiers().add(tmContainerImpl);
		// existing instance specification (tmIS) is the executor instance specification
		DepCreation.createSlot(containerIS, tmIS, executorPart);
		counter = 0;
		// containers.put(tmComponent, this);
		EList<Slot> connectorSlots = new BasicEList<Slot>();

		executorIS = tmIS;
		// now create instances for the contained elements
		for (Property extensionPart : tmContainerImpl.getAttributes()) {
			Type tmContainerExtImpl = extensionPart.getType();
			if (tmContainerExtImpl instanceof Class) {
				if (DepUtils.getSlot(containerIS, extensionPart) == null) {
					// no slot for part exists => assume that the part has been added by the container and create an instance specification for it.
					InstanceSpecification containerExtIS = null;

					String isName = containerIS.getName() + "." + extensionPart.getName(); //$NON-NLS-1$

					// create sub-instance and slot for extensions
					if (containerExtIS == null) {
						containerExtIS = DepCreation.createDepPlan(tmCDP, (Class) tmContainerExtImpl, isName, false);
					}
					Slot partSlot = DepCreation.createSlot(containerIS, containerExtIS, extensionPart);
					if (StereotypeUtil.isApplied(tmContainerExtImpl, InteractionComponent.class)) {
						connectorSlots.add(partSlot);
					}
				}
			}
		}

		// handle propagation of node allocation for connectors. Do that in 2nd loop, since the allocation
		// of parts might be incomplete before.
		for (Slot connectorSlot : connectorSlots) {
			ConnectorReification.propagateNodeAllocation(tmContainerImpl, containerIS, connectorSlot);
		}
		moveSlots(executorIS);
		return containerIS;
	}

	/**
	 * Create an instance of the container (UML instance specification)
	 *
	 * @param tmComponent
	 * @param tmIS
	 */
	public InstanceSpecification createHwContainerInstance(Class tmComponent, InstanceSpecification tmNode) {
		// create an instance specification for the container
		InstanceSpecification containerIS =
				(InstanceSpecification) tmCDP.createPackagedElement(tmNode.getName() + hwContainerPostfix, UMLPackage.eINSTANCE.getInstanceSpecification());
		// assign new name to original instance specification which reflects
		// role within containment hierarchy
		containerIS.getClassifiers().add(tmContainerImpl);
		// containers.put(tmComponent, this);
		counter = 0;
		executorIS = tmNode;
		// copy slots from HW instance specification
		for (Slot slot : tmNode.getSlots()) {
			Slot slotCopy = containerIS.createSlot();
			slotCopy.setDefiningFeature(slot.getDefiningFeature());
			// copy values (use CopyTo.copyTo(slot, containerIS) instead?)
			for (ValueSpecification value : slot.getValues()) {
				LazyCopier.copyValue(value, slotCopy);
			}
		}
		return containerIS;
	}

	/**
	 * return the reference of the created container class
	 *
	 * @return
	 */
	public Class getContainer() {
		return tmContainerImpl;
	}

	/**
	 * apply a container rule, i.e. add either a container extension or an
	 * interceptor to the container
	 *
	 * @param smContainerRule
	 *            An container rule
	 * @param smComponent
	 *            the application component in the source model
	 * @param tmComponent
	 *            the application component in the target model
	 * @param tmIS
	 *            the instance specification for the application component in the target model
	 * @throws TransformationException
	 */
	@Override
	public void applyRule(ContainerRule smContainerRule, Class smComponent, Class tmComponent)
			throws TransformationException
	{
		Map<Property, EList<Property>> interceptorPartsMap = new HashMap<Property, EList<Property>>();

		for (Property part : smContainerRule.getBase_Class().getAllAttributes()) {
			Type type = part.getType();
			if (type == null) {
				String ruleName = (smContainerRule.getBase_Class() != null) ? smContainerRule.getBase_Class().getName() : "undefined"; //$NON-NLS-1$
				throw new TransformationException(String.format(
						Messages.ContainerTrafo_CannotApplyRule, ruleName));
			}
			if (part instanceof Port) {
				Port newPort = tmContainerImpl.createOwnedPort(part.getName(), part.getType());
				StUtils.copyStereotypes(part, newPort);
			}
			else if (type instanceof Class) {
				Class extOrInterceptor = (Class) type;
				// DepUtils.chooseImplementation((Class) type,
				// new BasicEList<InstanceSpecification>(), false);

				if (StereotypeUtil.isApplied(part, InterceptionRule.class)) {
					if (StereotypeUtil.isApplied(extOrInterceptor, InteractionComponent.class)) {

						InterceptionRule interceptionRule = UMLUtil.getStereotypeApplication(part, InterceptionRule.class);
						InterceptionKind interceptionKind = interceptionRule.getInterceptionKind();
						EList<Feature> interceptFeatures = interceptionRule.getInterceptionSet();
						EList<Property> interceptorParts =
								expandInterceptorExtension(interceptionKind, interceptFeatures, extOrInterceptor, tmComponent);
						interceptorPartsMap.put(part, interceptorParts);
					}
					else {
						throw new TransformationException(String.format(
								Messages.ContainerTrafo_InterceptionRuleButNoInterceptor,
								part.getName(), smContainerRule.getBase_Class().getName()));
					}
				}
				else {
					Property extensionPart =
							expandAggregationExtension(part, extOrInterceptor, tmComponent);
					// register relation to facilitate connector copy
					copier.setPackageTemplate(smContainerRule.getBase_Class(), tmContainerImpl);
					copier.putPair(part, extensionPart);
					copier.setPackageTemplate(null, null);
				}
			}
		}
		createConnectorForAssociations();

		// tell copier that tmcontainerImpl is associated with the smContainerRule
		// register a package template (although it is not a template) to assure that the connectors
		// get copied, although they are in a different resource (only the connectors are copied, not
		// the types of the referenced parts).
		// [main issue here: properties of container rule are not copies in the sense of identical
		// copies]
		copier.setPackageTemplate(smContainerRule.getBase_Class(), tmContainerImpl);

		for (Connector connector : smContainerRule.getBase_Class().getOwnedConnectors()) {
			Property ruleInterceptorPart = null;
			for (Property part : interceptorPartsMap.keySet()) {
				if (ConnectorUtil.connectsPart(connector, part)) {
					// this connector is a connection between an interceptor (and another part which may not
					// be an interceptor => TODO: validation rule yet to write
					// we need to copy this connector multiple times, once for each associated interceptor part
					ruleInterceptorPart = part;
					break;
				}
			}
			if (ruleInterceptorPart != null) {
				for (Property interceptorPart : interceptorPartsMap.get(ruleInterceptorPart)) {
					// map an interceptor part within rule successively to to an interceptor part
					// within the container
					copier.putPair(ruleInterceptorPart, interceptorPart);
					copier.removeForCopy(connector);
					copier.getCopy(connector);
				}
			}
			else {
				// check whether FCM connector
				org.eclipse.papyrus.FCM.Connector fcmConn = StUtils.getConnector(connector);
				if (fcmConn != null) {
					ConnectorReification.reifyConnector(copier, tmContainerImpl,
							UMLTool.varName(connector), connector, executorIS, null);
				}
				else {
					copier.remove(connector);
					copier.getCopy(connector);
				}
			}
		}
		copier.setPackageTemplate(null, null);

		TemplateUtils.retargetConnectors(tmContainerImpl);
	}

	/**
	 * This container expansion does not create a new composite. Instead, it
	 * adds the extension as a part to the copied application component. The
	 * application component also inherits from the type of the container
	 * extension in order to avoid copying ports.
	 */
	Property expandAggregationExtension(Property smExtensionPart, Class smContainerExtImpl, Class tmComponent) throws TransformationException {
		Property extensionPart;
		String name = smExtensionPart.getName();
		Class tmContainerExtImpl = null;

		TemplateSignature signature = TemplateUtils.getSignature(smContainerExtImpl);
		if (signature == null) {
			// no template signature, just copy the container extension into the target model
			tmContainerExtImpl = copier.getCopy(smContainerExtImpl);
		}
		else {
			// template signature found, instantiate container extension via template binding mechanism
			Classifier actual;
			if (StereotypeUtil.isApplied(smExtensionPart, BindTemplate.class)) {
				BindTemplate bt = UMLUtil.getStereotypeApplication(smExtensionPart, BindTemplate.class);
				// use explicit binding from stereotype
				actual = (Classifier) bt.getActual().get(0);
			}
			else {
				// use executor component as actual
				actual = tmComponent;
			}
			// template signature and instantiate container extension via the
			// template binding mechanism, use executor component as actual
			TemplateBinding binding = TemplateUtils.fixedBinding(copier.target, smContainerExtImpl, actual);
			Object[] args = new Object[] {};
			TemplateInstantiation ti = new TemplateInstantiation(copier, binding, args);
			tmContainerExtImpl = ti.bindNamedElement(smContainerExtImpl);
		}

		// --------------------------------------------------
		// a container extension could be subject to container rules as well, i.e. need
		// to handle recursive rule application.
		EList<ContainerRule> rules = FCMUtil.getAllContainerRules(smContainerExtImpl);

		// get container trafo instance, if already existing
		AbstractContainerTrafo containerTrafo = AbstractContainerTrafo.get(tmContainerExtImpl);

		if (containerTrafo == null) {
			// no container exists, check rules and create eventually
			for (ContainerRule rule : rules) {
				if (RuleManagement.isRuleActive(rule)) {
					// at least one active rule => create container (or get previously instantiated))
					if (containerTrafo == null) {
						if (rule.getKind() == ContainerRuleKind.LIGHT_WEIGHT_OO_RULE) {
							throw new TransformationException(Messages.ContainerTrafo_RecursiveLWnotSupported);
						}
						else {
							containerTrafo = new ContainerTrafo(copier, tmCDP, null);
						}
						containerTrafo.createContainer(smContainerExtImpl, tmContainerExtImpl);
					}
					else {
						// configure only??
					}
					containerTrafo.applyRule(rule, smContainerExtImpl, tmContainerExtImpl);
				}
			}
			if (containerTrafo != null) {
				containerTrafo.finalize();
				tmContainerExtImpl = ((ContainerTrafo) containerTrafo).getContainer();
			}
		}
		// --------------------------------------------------

		// add part associated with the extension to the container
		extensionPart = tmContainerImpl.createOwnedAttribute(name, tmContainerExtImpl);

		// Copy.copyID(tmComponent, extensionPart, "a");
		extensionPart.setAggregation(smExtensionPart.getAggregation());
		LazyCopier.copyMultElemModifiers(smExtensionPart, extensionPart);
		LazyCopier.copyFeatureModifiers(smExtensionPart, extensionPart);

		return extensionPart;
	}

	/**
	 * Expand an interceptor definition between the container and the executor.
	 *
	 * @param extKind
	 *            the interception kind (intercept which ports: all, some, out, ...)
	 * @param featureList
	 *            The set of ports to intercept (In case of "some" above)
	 * @param smContainerConnImpl
	 *            The connector (interceptor) implementation in the source model
	 * @param tmComponent
	 *            the application component (executor) in the target model
	 * @return
	 * @throws TransformationException
	 */
	EList<Property> expandInterceptorExtension(InterceptionKind extKind, EList<Feature> featureList, Class smContainerConnImpl, Class tmComponent)
			throws TransformationException
	{
		EList<Property> connectorParts = new BasicEList<Property>();
		// replace delegation connectors with extension connector
		for (Port port : PortUtils.getAllPorts(tmComponent)) {
			// delegate to component via connector
			boolean match = true;
			if (extKind == InterceptionKind.INTERCEPT_ALL_IN) {
				// IN-PORT = provided port
				match = (PortUtils.getProvided(port) != null);
			}
			else if (extKind == InterceptionKind.INTERCEPT_ALL_OUT) {
				// IN-PORT = provided port
				match = (PortUtils.getRequired(port) != null);
			}
			else if (extKind == InterceptionKind.INTERCEPT_SOME) {
				// comparison based on name, since in different models
				match = (Utils.getNamedElementFromList(featureList, port.getName()) != null);
			}
			else if (extKind == InterceptionKind.INTERCEPT_MATCHING) {
				EList<Port> interceptorPorts = PortUtils.getAllPorts(smContainerConnImpl);
				match = false;
				// get first port from interception connector that is typed with a template parameter
				for (Port interceptorPort : interceptorPorts) {
					if (interceptorPort.getType().getOwner() instanceof TemplateParameter) {
						match = (PortUtils.getKind(port) == PortUtils.getKind(interceptorPort));
						break;
					}
				}
			}

			// else INTERCEPT_ALL_PORT => match remains true
			if (!match) {
				// port does not match criterion, continue with next port
				continue;
			}

			Property connectorPart;
			String interceptionName = interceptorName + port.getName() + counter;
			Connector interceptionConnector = null;
			// get delegation connector
			for (Connector connector : tmContainerImpl.getOwnedConnectors()) {
				if (ConnectorUtil.connectsPort(connector, port)) {
					interceptionConnector = connector;
					break;
				}
			}
			// interceptionConnector = tmContainerImpl.getOwnedConnector
			// ("delegation " + port.getName ());
			if (interceptionConnector == null) {
				throw new TransformationException(Messages.ContainerTrafo_CannotFindDelegationConn);
			}

			interceptionConnector.setName(interceptionName);
			org.eclipse.papyrus.FCM.Connector fcmConn = StereotypeUtil.applyApp(interceptionConnector, org.eclipse.papyrus.FCM.Connector.class);
			InteractionComponent fcmConnType = UMLUtil.getStereotypeApplication(smContainerConnImpl, InteractionComponent.class);
			fcmConn.setIc(fcmConnType);

			// pass target component and port to interceptor (not clean, define
			// suitable template signature as for instance in methodCall_comp
			Object[] args = new Object[] { executorIS, port };
			TransformationContext.instance = executorIS;
			TransformationContext.port = port;
			connectorPart = ConnectorReification.reifyConnector(copier, tmContainerImpl, UMLTool.varName(interceptionConnector), interceptionConnector, executorIS, args);
			connectorParts.add(connectorPart);
			TransformationContext.port = null;
			portInfo.put(connectorPart, port);
			// delete intermediate connector (has been replaced by two
			// connections to the reified connector)
			interceptionConnector.destroy();
		}
		counter++;
		return connectorParts;
	}

	/**
	 * Move a slot from the executor instance specification to an instance
	 * specification of an extension/interceptor. This is required, since
	 * users cannot configure containers directly. The modification is done in
	 * the target model, i.e. the user model is not affected.
	 *
	 * Configuration is based on the idea that we can have multiple classifiers for an
	 * instance specification and that we add additional slots for attributes of the 2nd
	 * classifier which becomes "visible" after container transformation.
	 * Main use: configure priority of thread within container.
	 *
	 * TODO: Not clear, whether we should keep this operation, since configuration of elements that are not
	 * (directly) part of the user model is confusing. Application was: configuration
	 * of thread priorities within container.
	 */
	public void moveSlots(InstanceSpecification containerIS) {
		// Log.log(Status.INFO, Log.TRAFO_CONTAINER, String.format(Messages.ContainerTrafo_InfoMoveSlots, executorIS.getQualifiedName()));
		Classifier mainCl = DepUtils.getClassifier(executorIS);
		Iterator<Slot> slotIt = executorIS.getSlots().iterator();
		while (slotIt.hasNext()) {
			Slot slot = slotIt.next();
			String featureName = slot.getDefiningFeature().getName();
			if (!mainCl.getAllAttributes().contains(slot.getDefiningFeature())) {
				// defining feature does not belong to main classifier
				// => move it to first matching instance specification
				for (InstanceSpecification is : DepUtils.getContainedInstances(containerIS)) {
					Classifier containedCl = DepUtils.getClassifier(is);
					if (Utils.getNamedElementFromList(containedCl.getAllAttributes(), featureName) != null) {
						if (executorIS != is) {
							// remove slot first from iterator, as addition below removes it from the list (slots are owned)
							slotIt.remove();
							is.getSlots().add(slot);
							break;
						}
					}
				}
			}
		}
	}

	/**
	 * Create an connector for parts that have an association. The rationale
	 * behind this is support connections between the state machine in the container
	 * and the executor.
	 *
	 */
	public void createConnectorForAssociations() {
		// TODO: keep list of added parts, only re-check those!

		for (Property part : Utils.getParts(tmContainerImpl)) {
			if (part.getType() == null) {
				continue;
			}
			for (Association association : part.getType().getAssociations()) {
				// Part has an association. Check if the other association end
				// targets one of
				// other parts within the container
				for (Property end : association.getMemberEnds()) {
					Type type = end.getType();
					if ((type != null) && (type != part.getType())) {
						// end is not typed with the part we are currently
						// examining
						// thus, it might belong to another part of the
						// composite
						for (Property checkPart : Utils.getParts(tmContainerImpl)) {
							if (type == checkPart.getType()) {
								// found an association between two parts of the
								// container => create connection,
								// unless already existing.
								if (!ConnectorUtil.existsConnector(tmContainerImpl, part, checkPart)) {
									Connector conn = tmContainerImpl.createOwnedConnector(part.getName() + "_" + checkPart.getName()); //$NON-NLS-1$
									conn.setType(association);
									conn.createEnd().setRole(part);
									conn.createEnd().setRole(checkPart);
								}
							}
						}
					}
				}
			}
		}
	}

	@Override
	public void finalize() {
		discoverServices();
	}

	/**
	 * Connector ports of executor with compatible ports offered by container extensions
	 * (runtime system)
	 */
	public void discoverServices() {
		Type executorType = executorPart.getType();
		if (!(executorType instanceof EncapsulatedClassifier)) {
			return;
		}
		EncapsulatedClassifier ec = (EncapsulatedClassifier) executorType;
		for (Port executorPort : ec.getOwnedPorts()) {
			for (Property svcPart : tmContainerImpl.getOwnedAttributes()) {
				if (svcPart == executorPart) {
					continue;
				}
				Type containerSvcType = svcPart.getType();
				if (containerSvcType instanceof EncapsulatedClassifier) {
					EncapsulatedClassifier containerSvc = (EncapsulatedClassifier) containerSvcType;
					for (Port svcPort : containerSvc.getOwnedPorts()) {
						if (PortUtils.matches(executorPort, svcPort, true)) {
							// create connector
							Connector c = tmContainerImpl.createOwnedConnector(
									String.format("auto from %s to %s", executorPart.getName(), svcPart.getName())); //$NON-NLS-1$
							ConnectorEnd ce1 = c.createEnd();
							ConnectorEnd ce2 = c.createEnd();
							ce1.setPartWithPort(executorPart);
							ce1.setRole(executorPort);
							ce2.setPartWithPort(svcPart);
							ce2.setRole(svcPort);
						}
					}
				}
			}
		}
	}

	/**
	 * Return the containerTrafo associated with a container. This operation is useful for instance
	 * configurators that need more information about the container context, in particular to associate
	 * a part with a port that is intercepted.
	 *
	 * @param containerInstance
	 *            the container instance (the parent instance of an instance of a container extension)ight be a container instance)
	 * @return the containerTrafo info, if found.
	 */
	public static ContainerTrafo getContainerTrafo(InstanceSpecification containerInstance) {
		Classifier containerCandidate = DepUtils.getClassifier(containerInstance);
		Property executorPart = containerCandidate.getAttribute(executorPartName, null);
		if ((executorPart != null) && executorPart.getType() instanceof Class) {
			AbstractContainerTrafo containerTrafo = AbstractContainerTrafo.get((Class) executorPart.getType());
			if (containerTrafo instanceof ContainerTrafo) {
				return (ContainerTrafo) containerTrafo;
			}
		}
		return null;
	}

	/**
	 * Utility function to retrieve the executor slot, when given the container instance.
	 * Needs to be called, after a container instance has been created.
	 *
	 * @param containerInstance
	 *            the instance of a container specification
	 * @return the associated executor slot, or null, if it cannot be found
	 */
	public static Slot getExecutorSlot(InstanceSpecification containerInstance) {
		for (Slot slot : containerInstance.getSlots()) {
			if (slot.getDefiningFeature().getName().equals(ContainerTrafo.executorPartName)) {
				return slot;
			}
		}
		return null;
	}

	/**
	 * Return the port that is intercepted when given a part
	 *
	 * @param containerPart
	 *            a part that participates in a port interception
	 * @return intercepted port
	 */
	public Port getInterceptedPort(Property containerPart) {
		return portInfo.get(containerPart);
	}

	/**
	 * The attribute within the container that holds the executor, i.e. the
	 * original component
	 */
	protected Property executorPart;

	/**
	 * An instance specification of the executor in the target model (todo: should be source model instead?), i.e.
	 * of the component before container expansion
	 */
	protected InstanceSpecification executorIS;

	/**
	 * The created container implementation (prefixed with tm, since part of
	 * target model)
	 */
	protected Class tmContainerImpl;

	/**
	 * A counter which is used to manage unique names for interceptors (if
	 * multiple interceptors are applied)
	 */
	protected int counter;

	/**
	 * Store information about intercepted port. The map contains a mapping from the part representing
	 * a reified connector to the port that was intercepted. It is stored, since it might be used by instance
	 * configurators that configure the container instance.
	 */
	protected Map<Property, Port> portInfo;
}

Back to the top