source: trunk/autoquest-plugin-uml/src/main/java/de/ugoe/cs/autoquest/plugin/uml/UMLUtils.java @ 1835

Last change on this file since 1835 was 1835, checked in by sherbold, 10 years ago
  • updated UMLUtils and UMLUtilsTest to work with a first version of the MIDAS logistics pilot. This also required changes in the service name maps. They now must point to the component for a service, not the interface.
  • Property svn:mime-type set to text/plain
File size: 28.4 KB
Line 
1//   Copyright 2012 Georg-August-Universität Göttingen, Germany
2//
3//   Licensed under the Apache License, Version 2.0 (the "License");
4//   you may not use this file except in compliance with the License.
5//   You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9//   Unless required by applicable law or agreed to in writing, software
10//   distributed under the License is distributed on an "AS IS" BASIS,
11//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//   See the License for the specific language governing permissions and
13//   limitations under the License.
14
15package de.ugoe.cs.autoquest.plugin.uml;
16
17import java.util.Collection;
18import java.util.Collections;
19import java.util.Comparator;
20import java.util.HashMap;
21import java.util.Iterator;
22import java.util.LinkedHashMap;
23import java.util.LinkedList;
24import java.util.List;
25import java.util.Map;
26import java.util.Map.Entry;
27
28import org.eclipse.emf.common.util.EList;
29import org.eclipse.uml2.uml.Activity;
30import org.eclipse.uml2.uml.ActivityEdge;
31import org.eclipse.uml2.uml.ActivityNode;
32import org.eclipse.uml2.uml.CallOperationAction;
33import org.eclipse.uml2.uml.Comment;
34import org.eclipse.uml2.uml.Component;
35import org.eclipse.uml2.uml.Connector;
36import org.eclipse.uml2.uml.ConnectorEnd;
37import org.eclipse.uml2.uml.Element;
38import org.eclipse.uml2.uml.Interaction;
39import org.eclipse.uml2.uml.InteractionFragment;
40import org.eclipse.uml2.uml.Interface;
41import org.eclipse.uml2.uml.Lifeline;
42import org.eclipse.uml2.uml.Message;
43import org.eclipse.uml2.uml.MessageOccurrenceSpecification;
44import org.eclipse.uml2.uml.MessageSort;
45import org.eclipse.uml2.uml.Model;
46import org.eclipse.uml2.uml.Operation;
47import org.eclipse.uml2.uml.Port;
48import org.eclipse.uml2.uml.Profile;
49import org.eclipse.uml2.uml.Property;
50import org.eclipse.uml2.uml.Region;
51import org.eclipse.uml2.uml.StateMachine;
52import org.eclipse.uml2.uml.Stereotype;
53import org.eclipse.uml2.uml.Transition;
54import org.eclipse.uml2.uml.UMLPackage;
55import org.eclipse.uml2.uml.Vertex;
56
57import de.ugoe.cs.autoquest.eventcore.Event;
58import de.ugoe.cs.autoquest.plugin.http.eventcore.SOAPEventType;
59import de.ugoe.cs.autoquest.plugin.http.eventcore.SimpleSOAPEventType;
60import de.ugoe.cs.autoquest.plugin.uml.eventcore.UMLTransitionType;
61import de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess;
62
63/**
64 * <p>
65 * Utilities for working with UML.
66 * </p>
67 *
68 * @author Steffen Herbold
69 */
70public class UMLUtils {
71
72    /**
73     * <p>
74     * Creates a sequence of events with {@link UMLTransitionType} as event type from a given
75     * sequence of events with the {@link SOAPEventType}, by matching the sequences to a state
76     * machine.
77     * </p>
78     *
79     * @param sequence
80     *            SOAP sequences
81     * @param stateMachine
82     *            the state machine
83     * @return create UML sequences
84     */
85    public static List<Event> createUMLTransitionSequence(List<Event> sequence,
86                                                          StateMachine stateMachine)
87    {
88        List<List<Transition>> matchingSequences =
89            determineMatchingTransitionSequences(sequence, stateMachine);
90
91        if (matchingSequences.size() != 1) {
92            throw new RuntimeException("no unique match found; " + matchingSequences.size() +
93                " matches");
94        }
95        List<Event> umlEventSequence = new LinkedList<>();
96        for (Transition transition : matchingSequences.get(0)) {
97            umlEventSequence.add(new Event(new UMLTransitionType(transition)));
98        }
99        return umlEventSequence;
100    }
101
102    /**
103     * <p>
104     * Uses a sequences of events with the {@link UMLTransitionType} to determine the transition
105     * probabilities for the state machine.
106     * </p>
107     *
108     * @param sequences
109     *            UML sequences
110     * @param stateMachine
111     *            state machine to be converted to a usage profile
112     */
113    public static void convertStateMachineToUsageProfile(Collection<List<Event>> sequences,
114                                                         StateMachine stateMachine)
115    {
116        // create state->outgoings hashmap
117        Map<Vertex, Map<Transition, Integer>> stateMap = new HashMap<>();
118        for (Region region : stateMachine.getRegions()) {
119            for (Vertex state : region.getSubvertices()) {
120                stateMap.put(state, new HashMap<Transition, Integer>());
121            }
122        }
123
124        // create counters for each transition
125        for (List<Event> sequence : sequences) {
126            for (Event event : sequence) {
127                if (event.getType() instanceof UMLTransitionType) {
128                    Transition transition = ((UMLTransitionType) event.getType()).getTransition();
129                    Map<Transition, Integer> transitionMap = stateMap.get(transition.getSource());
130                    Integer value = transitionMap.get(transition);
131                    if (value == null) {
132                        value = 0;
133                    }
134                    transitionMap.put(transition, value + 1);
135                }
136                else {
137                    throw new RuntimeException(
138                                               "Wrong event type. Only UMLTransitionType supported but was: " +
139                                                   event.getType().getClass().getName());
140                }
141            }
142        }
143
144        // calculate probabilities
145        for (Region region : stateMachine.getRegions()) {
146            for (Vertex state : region.getSubvertices()) {
147                Map<Transition, Integer> transitionMap = stateMap.get(state);
148                int totalCount = 0;
149                for (Entry<Transition, Integer> entry : transitionMap.entrySet()) {
150                    totalCount += entry.getValue();
151                }
152                if (totalCount != 0) {
153                    for (Transition transition : state.getOutgoings()) {
154                        double prob = 0.0d;
155                        if (transitionMap.containsKey(transition)) {
156                            prob = ((double) transitionMap.get(transition)) / totalCount;
157                        }
158                        Comment comment = transition.createOwnedComment();
159                        comment.setBody("" + prob);
160                    }
161                }
162                else {
163                    // system has never been in this state, all transitions equally likely
164                    int numOutgoings = state.getOutgoings().size();
165                    for (Transition transition : state.getOutgoings()) {
166                        Comment comment = transition.createOwnedComment();
167                        comment.setBody("" + (1.0d / numOutgoings));
168                    }
169                }
170            }
171        }
172    }
173
174    /**
175     * <p>
176     * Determines all matching {@link Transition} sequences in a state machine for a given sequence
177     * of SOAP events.
178     * </p>
179     *
180     * @param sequence
181     *            SOAP sequence
182     * @param stateMachine
183     *            the state machine
184     * @return all matching {@link Transition} sequences
185     */
186    public static List<List<Transition>> determineMatchingTransitionSequences(List<Event> sequence,
187                                                                              StateMachine stateMachine)
188    {
189        EList<Region> regions = stateMachine.getRegions();
190        EList<Vertex> states = null;
191        for (Region region : regions) {
192            if (states == null) {
193                states = region.getSubvertices();
194            }
195            else {
196                states.addAll(region.getSubvertices());
197            }
198        }
199        List<Transition> allTransitions = new LinkedList<>();
200        for (Vertex state : states) {
201            allTransitions.addAll(state.getOutgoings());
202        }
203
204        List<List<Transition>> matchingSequences = null;
205        List<Transition> currentTransitions = null;
206
207        // first, we try to find a single unique transition that we can match using the method name
208        for (Iterator<Event> eventIterator = sequence.iterator(); eventIterator.hasNext();) {
209            Event event = eventIterator.next();
210            if (event.getType() instanceof SOAPEventType) {
211                SOAPEventType eventType = (SOAPEventType) event.getType();
212                if (matchingSequences == null) {
213                    matchingSequences = new LinkedList<>();
214                    List<Transition> initialMatches = matchTransitions(allTransitions, eventType);
215                    for (Transition transition : initialMatches) {
216                        List<Transition> candidate = new LinkedList<>();
217                        candidate.add(transition);
218                        matchingSequences.add(candidate);
219                    }
220                    currentTransitions = initialMatches;
221                }
222                else {
223                    List<List<Transition>> nextMatchingSequences = new LinkedList<>();
224                    List<Transition> nextCurrentTransitions = new LinkedList<>();
225                    Iterator<Transition> currentTransitionIterator = currentTransitions.iterator();
226                    Iterator<List<Transition>> currentMatchingSequencesIterator =
227                        matchingSequences.iterator();
228                    while (currentTransitionIterator.hasNext()) {
229                        Transition currentTransition = currentTransitionIterator.next();
230                        List<Transition> currentMatch = currentMatchingSequencesIterator.next();
231
232                        List<Transition> matches =
233                            matchTransitions(currentTransition.getTarget().getOutgoings(),
234                                             eventType);
235                        if (matches.isEmpty()) {
236                            throw new RuntimeException("no matches found");
237                        }
238                        for (Transition matchingTransition : matches) {
239                            List<Transition> candidate = new LinkedList<>(currentMatch);
240                            candidate.add(matchingTransition);
241                            nextMatchingSequences.add(candidate);
242                            nextCurrentTransitions.add(matchingTransition);
243                        }
244                    }
245                    matchingSequences = nextMatchingSequences;
246                    currentTransitions = nextCurrentTransitions;
247                }
248            }
249            else {
250                throw new RuntimeException(
251                                           "Wrong event type. Only UMLTransitionType supported but was: " +
252                                               event.getType().getClass().getName());
253            }
254        }
255        return matchingSequences;
256    }
257
258    /**
259     * <p>
260     * Extends a given model with an interaction that represents an observed sequence.
261     * </p>
262     *
263     * @param sequence
264     *            sequence that is added as sequence diagram
265     * @param model
266     *            UML model to which the interaction is added
267     * @param interactionName
268     *            name of the interaction
269     */
270    public static void createInteractionFromEventSequence(List<Event> sequence,
271                                                          Model model,
272                                                          String interactionName)
273    {
274
275        Component testContext = fetchTestContext(model);
276
277        final Profile utpProfile = model.getAppliedProfile("utp");
278        final Stereotype utpTestCase = (Stereotype) utpProfile.getOwnedMember("TestCase");
279        final Stereotype utpTestComponent = (Stereotype) utpProfile.getOwnedMember("TestComponent");
280        final Stereotype utpSUT = (Stereotype) utpProfile.getOwnedMember("SUT");
281       
282        Operation operation = testContext.createOwnedOperation(interactionName, null, null);
283        operation.applyStereotype(utpTestCase);
284
285        Interaction interaction =
286            (Interaction) testContext.createPackagedElement(interactionName + "_Impl",
287                                                            UMLPackage.Literals.INTERACTION);
288        operation.getMethods().add(interaction);
289
290        // create lifelines
291        Lifeline userLifeline = null;
292        // List<Port> userPorts = new LinkedList<>();
293        for (Property property : testContext.getAllAttributes()) {
294            if (property.getAppliedStereotypes().contains(utpSUT)) {
295                String serviceName = property.getName();
296                Lifeline targetLifeline = interaction.createLifeline(serviceName);
297                targetLifeline.setRepresents(property);
298            }
299            else if (property.getType().getAppliedStereotypes().contains(utpTestComponent)) {
300                userLifeline = interaction.createLifeline(property.getName());
301                userLifeline.setRepresents(property);
302            }   
303        }
304       
305        // TODO sanity checks for userLifeline!=null, etc.
306
307        int i = 0;
308        for (Event event : sequence) {
309            if (!(event.equals(Event.STARTEVENT) || event.equals(Event.ENDEVENT))) {
310                String serviceName = getServiceNameFromEvent(event);
311                String methodName = getCalledMethodFromEvent(event);
312               
313                // determine lifelines
314                Lifeline msgTargetLifeline;
315                Lifeline msgSourceLifeline;
316               
317                if( serviceName.equals(userLifeline.getName()) ) {
318                    // message being send to user
319                    // currently we just select the first lifeline that is not the user
320                    // this, obviously, has to be replaced with the real service.
321                    // however, identification of the source of a message is still an open issue
322                    msgSourceLifeline = null;
323                    for( Lifeline lifeline : interaction.getLifelines() ) {
324                        if(!lifeline.equals(userLifeline)){
325                            msgSourceLifeline = lifeline;
326                            break;
327                        }
328                    }
329                    msgTargetLifeline = userLifeline;
330                } else {
331                    msgSourceLifeline = userLifeline;
332                    msgTargetLifeline = interaction.getLifeline(serviceName);
333                }
334
335                // determine target interface
336                Interface targetInterface = getRealizedInterfaceFromProperty((Property) msgTargetLifeline.getRepresents());
337               
338                // create message
339                MessageOccurrenceSpecification sendFragment =
340                    (MessageOccurrenceSpecification) interaction
341                        .createFragment(i + ":" + methodName + "_sendFragment",
342                                        UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
343                MessageOccurrenceSpecification recvFragment =
344                    (MessageOccurrenceSpecification) interaction
345                        .createFragment(i + ":" + methodName + "_recvFragment",
346                                        UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
347
348                sendFragment.setCovered(msgSourceLifeline);
349                recvFragment.setCovered(msgTargetLifeline);
350
351                Message message = interaction.createMessage(methodName);
352                if (getOperationFromName(targetInterface.getOperations(), methodName) == null) {
353                    System.out.println("operation not found in the " + targetInterface.getName() + " interface: " + methodName);
354                }
355                message.setSignature(getOperationFromName(targetInterface.getOperations(),
356                                                          methodName));
357                message.setMessageSort(MessageSort.ASYNCH_CALL_LITERAL);
358                message.setSendEvent(sendFragment);
359                message.setReceiveEvent(recvFragment);
360
361                // now the connector needs to be determined
362                EList<Property> userAttributes = ((Component) msgSourceLifeline.getRepresents().getType()).getAttributes();
363                EList<Property> targetAttributes = ((Component) msgTargetLifeline.getRepresents().getType()).getAttributes();
364               
365                for( Property userAttribute : userAttributes ) {
366                    if( userAttribute instanceof Port ) {
367                        EList<ConnectorEnd> userEnds = ((Port) userAttribute).getEnds();
368                        for( ConnectorEnd userEnd : userEnds ) {
369                            Connector userConnector = (Connector) userEnd.eContainer();
370                            for( Property targetAttribute : targetAttributes ) {
371                                if( targetAttribute instanceof Port ) {
372                                    EList<ConnectorEnd> targetEnds = ((Port) targetAttribute).getEnds();
373                                    for( ConnectorEnd targetEnd : targetEnds ) {
374                                        Connector targetConnector = (Connector) targetEnd.eContainer();
375                                        if( targetConnector==userConnector ) {
376                                            message.setConnector(targetConnector);
377                                        }
378                                    }
379                                }
380                            }
381                        }
382                    }
383                }
384               
385                sendFragment.setMessage(message);
386                recvFragment.setMessage(message);
387
388                i++;
389            }
390        }
391    }
392
393    /**
394     * <p>
395     * Calculates the usage score of an interaction as the logsum of the event probabilities
396     * multiplied with the length of the interaction.
397     * </p>
398     *
399     * @param interaction
400     *            interaction for which the score is calculated
401     * @param usageProfile
402     *            usage profile used for the calculation
403     * @return calculated usage score
404     */
405    public static double calculateUsageScore(Interaction interaction,
406                                             IStochasticProcess usageProfile)
407    {
408        double usageScore = 0.0d;
409
410        EList<InteractionFragment> interactionFragments = interaction.getFragments();
411        List<Event> eventSequence = new LinkedList<>();
412        eventSequence.add(Event.STARTEVENT);
413        for (InteractionFragment interactionFragment : interactionFragments) {
414            if (interactionFragment.getName() != null &&
415                interactionFragment.getName().endsWith("_recvFragment"))
416            {
417                String serviceName =
418                    interactionFragment.getCovereds().get(0).getRepresents().getName().split("_")[0];
419                String methodName = "UNKNOWN";
420                if (interactionFragment instanceof MessageOccurrenceSpecification) {
421                    methodName =
422                        ((MessageOccurrenceSpecification) interactionFragment).getMessage()
423                            .getName();
424                }
425                eventSequence.add(new Event(new SimpleSOAPEventType(methodName, serviceName)));
426            }
427        }
428        eventSequence.add(Event.ENDEVENT);
429        double prob = usageProfile.getLogSum(eventSequence);
430        usageScore = eventSequence.size() * prob;
431
432        return usageScore;
433    }
434
435    /**
436     * <p>
437     * Extends the given model with an activity for usage-based scheduling of the test cases.
438     * </p>
439     *
440     * @param model
441     *            model to be extended
442     * @param usageProfile
443     *            usage profile used as foundation
444     */
445    public static void createScheduling(Model model, IStochasticProcess usageProfile) {
446
447        final Profile utpProfile = model.getAppliedProfile("utp");
448        final Stereotype utpTestCase = (Stereotype) utpProfile.getOwnedMember("TestCase");
449
450        Component testContext = fetchTestContext(model);
451
452        Map<Operation, Double> usageScoreMapUnsorted = new HashMap<>();
453
454        // first, we determine all test cases and calculate their usage scores
455        for (Operation operation : testContext.getAllOperations()) {
456            if (operation.getAppliedStereotypes().contains(utpTestCase)) {
457                Interaction interaction = (Interaction) operation.getMethods().get(0);
458                usageScoreMapUnsorted
459                    .put(operation, calculateUsageScore(interaction, usageProfile));
460            }
461        }
462        Map<Operation, Double> usageScoreMapSorted = sortByValue(usageScoreMapUnsorted);
463
464        // now we create the scheduling
465        Activity schedulingActivity =
466            (Activity) testContext.createOwnedBehavior("UsageBasedScheduling",
467                                                       UMLPackage.Literals.ACTIVITY);
468        testContext.setClassifierBehavior(schedulingActivity);
469
470        ActivityNode startNode =
471            schedulingActivity.createOwnedNode("final", UMLPackage.Literals.INITIAL_NODE);
472        ActivityNode finalNode =
473            schedulingActivity.createOwnedNode("final", UMLPackage.Literals.ACTIVITY_FINAL_NODE);
474
475        ActivityNode currentOperationNode = startNode;
476
477        for (Entry<Operation, Double> entry : usageScoreMapSorted.entrySet()) {
478            Operation operation = entry.getKey();
479            CallOperationAction nextOperationNode =
480                (CallOperationAction) schedulingActivity
481                    .createOwnedNode(operation.getName(), UMLPackage.Literals.CALL_OPERATION_ACTION);
482            nextOperationNode.setOperation(operation);
483
484            ActivityEdge edge =
485                schedulingActivity.createEdge(currentOperationNode.getName() + "_to_" +
486                    nextOperationNode.getName(), UMLPackage.Literals.CONTROL_FLOW);
487            edge.setSource(currentOperationNode);
488            edge.setTarget(nextOperationNode);
489
490            currentOperationNode = nextOperationNode;
491        }
492
493        ActivityEdge edge =
494            schedulingActivity
495                .createEdge(currentOperationNode.getName() + "_to_" + finalNode.getName(),
496                            UMLPackage.Literals.CONTROL_FLOW);
497        edge.setSource(currentOperationNode);
498        edge.setTarget(finalNode);
499    }
500
501    /**
502     * From
503     * http://stackoverflow.com/questions/109383/how-to-sort-a-mapkey-value-on-the-values-in-java
504     * and adapted to do an inverse sorting
505     */
506    public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
507        List<Map.Entry<K, V>> list = new LinkedList<>(map.entrySet());
508        Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
509            @Override
510            public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
511                return -1 * (o1.getValue()).compareTo(o2.getValue());
512            }
513        });
514
515        Map<K, V> result = new LinkedHashMap<>();
516        for (Map.Entry<K, V> entry : list) {
517            result.put(entry.getKey(), entry.getValue());
518        }
519        return result;
520    }
521
522    /**
523     * <p>
524     * Helper function to get the name of a service from a SOAP event.
525     * </p>
526     *
527     * @param event
528     *            event for which the service name is retrieved
529     * @return service name
530     */
531    protected static String getServiceNameFromEvent(Event event) {
532        if (event.getType() instanceof SOAPEventType) {
533            return ((SOAPEventType) event.getType()).getServiceName();
534        }
535        else if (event.getType() instanceof SimpleSOAPEventType) {
536            return ((SimpleSOAPEventType) event.getType()).getServiceName();
537        }
538        else {
539            throw new RuntimeException(
540                                       "Wrong event type. Only SOAPEventType and SimpleSOAPEventType supported but was: " +
541                                           event.getType().getClass().getName());
542        }
543    }
544
545    /**
546     *
547     * <p>
548     * Helper function to get the called method from a SOAP event
549     * </p>
550     *
551     * @param event
552     *            event for which the called method is retrieved
553     * @return called method
554     */
555    private static String getCalledMethodFromEvent(Event event) {
556        if (event.getType() instanceof SOAPEventType) {
557            return ((SOAPEventType) event.getType()).getCalledMethod();
558        }
559        else if (event.getType() instanceof SimpleSOAPEventType) {
560            return ((SimpleSOAPEventType) event.getType()).getCalledMethod();
561        }
562        else {
563            throw new RuntimeException(
564                                       "Wrong event type. Only SOAPEventType and SimpleSOAPEventType supported but was: " +
565                                           event.getType().getClass().getName());
566        }
567    }
568
569    /**
570     * <p>
571     * Fetches an operation using only its name from a list of operations. Returns the first match
572     * found or null if no match is found.
573     * </p>
574     *
575     * @param operations
576     *            list of operations
577     * @param name
578     *            name of the operation
579     * @return first matching operation; null if no match is found
580     */
581    private static Operation getOperationFromName(EList<Operation> operations, String name) {
582        if (name == null) {
583            throw new IllegalArgumentException("name of the operation must not be null");
584        }
585        if (operations != null) {
586            for (Operation operation : operations) {
587                if (operation.getName() != null && operation.getName().equals(name)) {
588                    return operation;
589                }
590            }
591        }
592        return null;
593    }
594
595    /**
596     * <p>
597     * Determines which transitions match a given {@link SOAPEventType}.
598     * </p>
599     *
600     * @param transitions
601     *            the transitions
602     * @param eventType
603     *            the SOAP event
604     * @return matching transitions
605     */
606    private static List<Transition> matchTransitions(List<Transition> transitions,
607                                                     SOAPEventType eventType)
608    {
609        List<Transition> matching = new LinkedList<>();
610        for (Transition transition : transitions) {
611            // String serviceName = transition.getName().split("\\.")[0]; // TODO service name check
612            String methodName = transition.getName().split("\\.")[1];
613            if (methodName.equals(eventType.getCalledMethod())) {
614                matching.add(transition);
615            }
616        }
617        return matching;
618    }
619   
620    private static Interface getRealizedInterfaceFromProperty(Property property) {
621        return getRealizedInterfaceFromComponent((Component) property.getType());
622    }
623   
624    private static Interface getRealizedInterfaceFromComponent(Component comp) {
625        Interface myInterface = null;
626        for( Property property : comp.getAttributes() ) {
627            if( property instanceof Port ) {
628                Port port = (Port) property;
629                if( !port.isConjugated() ) {
630                    if( myInterface==null ) {
631                        myInterface = port.getProvideds().get(0);
632                    }
633                    else if( myInterface!=port.getProvideds().get(0)) {
634                        System.err.println("multiple different interfaces found");
635                    }
636                }
637            }
638        }
639        return myInterface;
640        //return ((Port) comp.getAttributes().get(0)).getInterface();
641        //Realization realization = (Realization) comp.getNestedClassifiers().get(0).getRelationships(UMLPackage.Literals.REALIZATION).get(0);
642        //return (Interface) realization.getSuppliers().get(0);
643    }
644   
645    private static Component fetchTestContext(Model model) {
646        final Profile utpProfile = model.getAppliedProfile("utp");
647        final Stereotype utpTestContext = (Stereotype) utpProfile.getOwnedMember("TestContext");
648       
649        for( Element element : model.getOwnedElements() ) {
650            if( element instanceof Component && element.getApplicableStereotypes().contains(utpTestContext) ) {
651                return (Component) element;
652            }
653        }
654        return null;
655    }
656}
Note: See TracBrowser for help on using the repository browser.