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

Last change on this file since 1897 was 1897, checked in by sherbold, 9 years ago
  • refactorings
  • Property svn:mime-type set to text/plain
File size: 35.2 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.BehaviorExecutionSpecification;
33import org.eclipse.uml2.uml.CallOperationAction;
34import org.eclipse.uml2.uml.Comment;
35import org.eclipse.uml2.uml.Component;
36import org.eclipse.uml2.uml.Connector;
37import org.eclipse.uml2.uml.ConnectorEnd;
38import org.eclipse.uml2.uml.Element;
39import org.eclipse.uml2.uml.Interaction;
40import org.eclipse.uml2.uml.InteractionFragment;
41import org.eclipse.uml2.uml.Interface;
42import org.eclipse.uml2.uml.Lifeline;
43import org.eclipse.uml2.uml.Message;
44import org.eclipse.uml2.uml.MessageOccurrenceSpecification;
45import org.eclipse.uml2.uml.MessageSort;
46import org.eclipse.uml2.uml.Model;
47import org.eclipse.uml2.uml.Operation;
48import org.eclipse.uml2.uml.Package;
49import org.eclipse.uml2.uml.Port;
50import org.eclipse.uml2.uml.Profile;
51import org.eclipse.uml2.uml.Property;
52import org.eclipse.uml2.uml.Region;
53import org.eclipse.uml2.uml.StateMachine;
54import org.eclipse.uml2.uml.Stereotype;
55import org.eclipse.uml2.uml.Transition;
56import org.eclipse.uml2.uml.UMLPackage;
57import org.eclipse.uml2.uml.Vertex;
58
59import de.ugoe.cs.autoquest.eventcore.Event;
60import de.ugoe.cs.autoquest.plugin.http.eventcore.SOAPEventType;
61import de.ugoe.cs.autoquest.plugin.http.eventcore.SimpleSOAPEventType;
62import de.ugoe.cs.autoquest.plugin.uml.eventcore.UMLTransitionType;
63import de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess;
64
65/**
66 * <p>
67 * Utilities for working with UML.
68 * </p>
69 *
70 * @author Steffen Herbold
71 */
72public class UMLUtils {
73
74    /**
75     * <p>
76     * Creates a sequence of events with {@link UMLTransitionType} as event type from a given
77     * sequence of events with the {@link SOAPEventType}, by matching the sequences to a state
78     * machine.
79     * </p>
80     *
81     * @param sequence
82     *            SOAP sequences
83     * @param stateMachine
84     *            the state machine
85     * @return create UML sequences
86     */
87    public static List<Event> createUMLTransitionSequence(List<Event> sequence,
88                                                          StateMachine stateMachine)
89    {
90        List<List<Transition>> matchingSequences =
91            determineMatchingTransitionSequences(sequence, stateMachine);
92
93        if (matchingSequences.size() != 1) {
94            throw new RuntimeException("no unique match found; " + matchingSequences.size() +
95                " matches");
96        }
97        List<Event> umlEventSequence = new LinkedList<>();
98        for (Transition transition : matchingSequences.get(0)) {
99            umlEventSequence.add(new Event(new UMLTransitionType(transition)));
100        }
101        return umlEventSequence;
102    }
103
104    /**
105     * <p>
106     * Uses a sequences of events with the {@link UMLTransitionType} to determine the transition
107     * probabilities for the state machine.
108     * </p>
109     *
110     * @param sequences
111     *            UML sequences
112     * @param stateMachine
113     *            state machine to be converted to a usage profile
114     */
115    public static void convertStateMachineToUsageProfile(Collection<List<Event>> sequences,
116                                                         StateMachine stateMachine)
117    {
118        // create state->outgoings hashmap
119        Map<Vertex, Map<Transition, Integer>> stateMap = new HashMap<>();
120        for (Region region : stateMachine.getRegions()) {
121            for (Vertex state : region.getSubvertices()) {
122                stateMap.put(state, new HashMap<Transition, Integer>());
123            }
124        }
125
126        // create counters for each transition
127        for (List<Event> sequence : sequences) {
128            for (Event event : sequence) {
129                if (event.getType() instanceof UMLTransitionType) {
130                    Transition transition = ((UMLTransitionType) event.getType()).getTransition();
131                    Map<Transition, Integer> transitionMap = stateMap.get(transition.getSource());
132                    Integer value = transitionMap.get(transition);
133                    if (value == null) {
134                        value = 0;
135                    }
136                    transitionMap.put(transition, value + 1);
137                }
138                else {
139                    throw new RuntimeException(
140                                               "Wrong event type. Only UMLTransitionType supported but was: " +
141                                                   event.getType().getClass().getName());
142                }
143            }
144        }
145
146        // calculate probabilities
147        for (Region region : stateMachine.getRegions()) {
148            for (Vertex state : region.getSubvertices()) {
149                Map<Transition, Integer> transitionMap = stateMap.get(state);
150                int totalCount = 0;
151                for (Entry<Transition, Integer> entry : transitionMap.entrySet()) {
152                    totalCount += entry.getValue();
153                }
154                if (totalCount != 0) {
155                    for (Transition transition : state.getOutgoings()) {
156                        double prob = 0.0d;
157                        if (transitionMap.containsKey(transition)) {
158                            prob = ((double) transitionMap.get(transition)) / totalCount;
159                        }
160                        Comment comment = transition.createOwnedComment();
161                        comment.setBody("" + prob);
162                    }
163                }
164                else {
165                    // system has never been in this state, all transitions equally likely
166                    int numOutgoings = state.getOutgoings().size();
167                    for (Transition transition : state.getOutgoings()) {
168                        Comment comment = transition.createOwnedComment();
169                        comment.setBody("" + (1.0d / numOutgoings));
170                    }
171                }
172            }
173        }
174    }
175
176    /**
177     * <p>
178     * Determines all matching {@link Transition} sequences in a state machine for a given sequence
179     * of SOAP events.
180     * </p>
181     *
182     * @param sequence
183     *            SOAP sequence
184     * @param stateMachine
185     *            the state machine
186     * @return all matching {@link Transition} sequences
187     */
188    public static List<List<Transition>> determineMatchingTransitionSequences(List<Event> sequence,
189                                                                              StateMachine stateMachine)
190    {
191        EList<Region> regions = stateMachine.getRegions();
192        EList<Vertex> states = null;
193        for (Region region : regions) {
194            if (states == null) {
195                states = region.getSubvertices();
196            }
197            else {
198                states.addAll(region.getSubvertices());
199            }
200        }
201        List<Transition> allTransitions = new LinkedList<>();
202        for (Vertex state : states) {
203            allTransitions.addAll(state.getOutgoings());
204        }
205
206        List<List<Transition>> matchingSequences = null;
207        List<Transition> currentTransitions = null;
208
209        // first, we try to find a single unique transition that we can match using the method name
210        for (Iterator<Event> eventIterator = sequence.iterator(); eventIterator.hasNext();) {
211            Event event = eventIterator.next();
212            if (event.getType() instanceof SOAPEventType) {
213                SOAPEventType eventType = (SOAPEventType) event.getType();
214                if (matchingSequences == null) {
215                    matchingSequences = new LinkedList<>();
216                    List<Transition> initialMatches = matchTransitions(allTransitions, eventType);
217                    for (Transition transition : initialMatches) {
218                        List<Transition> candidate = new LinkedList<>();
219                        candidate.add(transition);
220                        matchingSequences.add(candidate);
221                    }
222                    currentTransitions = initialMatches;
223                }
224                else {
225                    List<List<Transition>> nextMatchingSequences = new LinkedList<>();
226                    List<Transition> nextCurrentTransitions = new LinkedList<>();
227                    Iterator<Transition> currentTransitionIterator = currentTransitions.iterator();
228                    Iterator<List<Transition>> currentMatchingSequencesIterator =
229                        matchingSequences.iterator();
230                    while (currentTransitionIterator.hasNext()) {
231                        Transition currentTransition = currentTransitionIterator.next();
232                        List<Transition> currentMatch = currentMatchingSequencesIterator.next();
233
234                        List<Transition> matches =
235                            matchTransitions(currentTransition.getTarget().getOutgoings(),
236                                             eventType);
237                        if (matches.isEmpty()) {
238                            throw new RuntimeException("no matches found");
239                        }
240                        for (Transition matchingTransition : matches) {
241                            List<Transition> candidate = new LinkedList<>(currentMatch);
242                            candidate.add(matchingTransition);
243                            nextMatchingSequences.add(candidate);
244                            nextCurrentTransitions.add(matchingTransition);
245                        }
246                    }
247                    matchingSequences = nextMatchingSequences;
248                    currentTransitions = nextCurrentTransitions;
249                }
250            }
251            else {
252                throw new RuntimeException(
253                                           "Wrong event type. Only UMLTransitionType supported but was: " +
254                                               event.getType().getClass().getName());
255            }
256        }
257        return matchingSequences;
258    }
259
260    /**
261     * <p>
262     * Extends a given model with an interaction that represents an observed sequence.
263     * </p>
264     *
265     * @param sequence
266     *            sequence that is added as sequence diagram
267     * @param model
268     *            UML model to which the interaction is added
269     * @param interactionName
270     *            name of the interaction
271     * @param testContextName
272     *            Name of the test context that should be used. If this value is null, the first
273     *            test context found is used.
274     */
275    public static void createInteractionFromEventSequence(List<Event> sequence,
276                                                          Model model,
277                                                          String interactionName,
278                                                          String testContextName)
279    {
280        final Profile utpProfile = model.getAppliedProfile("utp");
281        final Stereotype utpTestCase = (Stereotype) utpProfile.getOwnedMember("TestCase");
282        final Stereotype utpTestComponent = (Stereotype) utpProfile.getOwnedMember("TestComponent");
283        final Stereotype utpSUT = (Stereotype) utpProfile.getOwnedMember("SUT");
284        final Stereotype utpTestContext = (Stereotype) utpProfile.getOwnedMember("TestContext");
285
286        Component testContext = fetchTestContext(model, utpTestContext, testContextName);
287        if (testContext == null) {
288            throw new RuntimeException("Could not find any test context in the model");
289        }
290
291        Operation operation = testContext.createOwnedOperation(interactionName, null, null);
292        operation.applyStereotype(utpTestCase);
293
294        Interaction interaction =
295            (Interaction) testContext.createPackagedElement(interactionName + "_Impl",
296                                                            UMLPackage.Literals.INTERACTION);
297        operation.getMethods().add(interaction);
298
299        // create lifelines
300        Lifeline userLifeline = null;
301       
302        for (Property property : testContext.getAllAttributes()) {
303            if (property.getAppliedStereotypes().contains(utpSUT)) {
304                String serviceName = property.getName();
305                Lifeline targetLifeline = interaction.createLifeline(serviceName);
306                targetLifeline.setRepresents(property);
307            }
308            else if (property.getType().getAppliedStereotypes().contains(utpTestComponent)) {
309                if( userLifeline!=null ) {
310                    throw new RuntimeException("TestContext must only have one TestComponent for the application of usage-based testing.");
311                }
312                userLifeline = interaction.createLifeline(property.getName());
313                userLifeline.setRepresents(property);
314            }
315        }
316        if( userLifeline==null ) {
317            throw new RuntimeException("No TestComponent found, could not create user lifeline.");
318        }
319        if( interaction.getLifelines().size()<2 ) {
320            throw new RuntimeException("Fewer than two lifelines created. No SUT found.");
321        }
322       
323        int i = 0;
324        for (Event event : sequence) {
325            if (!(event.equals(Event.STARTEVENT) || event.equals(Event.ENDEVENT))) {
326                String serviceName = getServiceNameFromEvent(event);
327                String methodName = getCalledMethodFromEvent(event);
328                // determine lifelines
329                Lifeline msgTargetLifeline;
330                Lifeline msgSourceLifeline;
331
332                if (serviceName.equals(userLifeline.getName())) {
333                    // message being send to user
334                    // currently we just select the first lifeline that is not the user
335                    // this, obviously, has to be replaced with the real service.
336                    // however, identification of the source of a message is still an open issue
337                    msgSourceLifeline = null;
338                    for (Lifeline lifeline : interaction.getLifelines()) {
339                        if (!lifeline.equals(userLifeline)) {
340                            msgSourceLifeline = lifeline;
341                            break;
342                        }
343                    }
344                    msgTargetLifeline = userLifeline;
345                }
346                else {
347                    msgSourceLifeline = userLifeline;
348                    msgTargetLifeline = interaction.getLifeline(serviceName);
349                }
350                if( msgSourceLifeline==null ) {
351                    throw new RuntimeException("Error creating message: could not determine source lifeline.");
352                }
353                if( msgTargetLifeline==null ) {
354                    throw new RuntimeException("Error creating message: could not determine target lifeline.");
355                }
356                // determine correct target interface
357                List<Interface> targetInterfaces =
358                    getRealizedInterfacesFromProperty((Property) msgTargetLifeline.getRepresents());
359                if (targetInterfaces.isEmpty()) {
360                    throw new RuntimeException("no interface associated with the property " +
361                        msgTargetLifeline.getRepresents().getName());
362                }
363                Interface targetInterface = null;
364                for (Interface intface : targetInterfaces) {
365                    System.out.println(intface.getOperations());
366                    if (getOperationFromName(intface.getOperations(), methodName) != null) {
367                        // interface found
368                        targetInterface = intface;
369                        break;
370                    }
371                }
372                if (targetInterface == null) {
373                    StringBuilder errStrBuilder = new StringBuilder();
374                    errStrBuilder.append("Error creating message: operation not found in the implementing interfaces (");
375                    Iterator<Interface> iter = targetInterfaces.iterator();
376                    while (iter.hasNext()) {
377                        String interfaceName = iter.next().getName();
378                        errStrBuilder.append(interfaceName);
379                        if (iter.hasNext()) {
380                            errStrBuilder.append(",");
381                        }
382                        else {
383                            errStrBuilder.append("): " + methodName);
384                        }
385                    }
386                    throw new RuntimeException(errStrBuilder.toString());
387                }
388
389                Operation calledOperation =
390                    getOperationFromName(targetInterface.getOperations(), methodName);
391
392                // setup for both SYNCH and ASYNCH calls
393                MessageOccurrenceSpecification callSendFragment =
394                    (MessageOccurrenceSpecification) interaction
395                        .createFragment(i + ":" + methodName + "_callSendFragment",
396                                        UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
397                MessageOccurrenceSpecification callRecvFragment =
398                    (MessageOccurrenceSpecification) interaction
399                        .createFragment(i + ":" + methodName + "_callRecvFragment",
400                                        UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
401
402                callSendFragment.setCovered(msgSourceLifeline);
403                callRecvFragment.setCovered(msgTargetLifeline);
404
405                // create call
406                Message callMessage = interaction.createMessage(methodName);
407                callMessage.setSignature(calledOperation);
408                callMessage.setConnector(inferConnector(msgSourceLifeline, msgTargetLifeline));
409                callMessage.setSendEvent(callSendFragment);
410                callMessage.setReceiveEvent(callRecvFragment);
411                callSendFragment.setMessage(callMessage);
412                callRecvFragment.setMessage(callMessage);
413
414                // TODO somehow infer if called operation is SYNCH or ASYNCH
415                // possibly requires additional stereotype
416                boolean asynch = false;
417                if (asynch) {
418                    // Create ASYNCH call
419                    callMessage.setMessageSort(MessageSort.ASYNCH_CALL_LITERAL);
420                }
421                else {
422                    // SYNCH call
423                    callMessage.setMessageSort(MessageSort.SYNCH_CALL_LITERAL);
424
425                    // setup reply and behavior execution specifications
426                    MessageOccurrenceSpecification replySendFragment =
427                        (MessageOccurrenceSpecification) interaction
428                            .createFragment(i + ":" + methodName + "_replySendFragment",
429                                            UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
430                    MessageOccurrenceSpecification replyRecvFragment =
431                        (MessageOccurrenceSpecification) interaction
432                            .createFragment(i + ":" + methodName + "_replyRecvFragment",
433                                            UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
434
435                    replySendFragment.setCovered(msgTargetLifeline);
436                    replyRecvFragment.setCovered(msgSourceLifeline);
437                   
438                    /*BehaviorExecutionSpecification sourceBehaviorExecutionSpecification =
439                        (BehaviorExecutionSpecification) interaction
440                            .createFragment(":" + methodName + "_sourceBhvExecSpec",
441                                            UMLPackage.Literals.BEHAVIOR_EXECUTION_SPECIFICATION);
442                    BehaviorExecutionSpecification targetBehaviorExecutionSpecification =
443                        (BehaviorExecutionSpecification) interaction
444                            .createFragment(":" + methodName + "_targetBhvExecSpec",
445                                            UMLPackage.Literals.BEHAVIOR_EXECUTION_SPECIFICATION);
446
447                    sourceBehaviorExecutionSpecification.setStart(callSendFragment);
448                    sourceBehaviorExecutionSpecification.setFinish(replyRecvFragment);
449                    targetBehaviorExecutionSpecification.setStart(callRecvFragment);
450                    targetBehaviorExecutionSpecification.setFinish(replySendFragment);*/
451
452                    // create reply
453                    Message replyMessage = interaction.createMessage(methodName + "_reply");
454                    replyMessage.setMessageSort(MessageSort.REPLY_LITERAL);
455                    replyMessage.setSignature(calledOperation);
456                    replyMessage.setSendEvent(replySendFragment);
457                    replyMessage.setReceiveEvent(replyRecvFragment);
458                    replySendFragment.setMessage(replyMessage);
459                    replyRecvFragment.setMessage(replyMessage);
460                }
461
462                i++;
463            }
464        }
465    }
466
467    /**
468     * <p>
469     * Calculates the usage score of an interaction as the logsum of the event probabilities
470     * multiplied with the length of the interaction.
471     * </p>
472     *
473     * @param interaction
474     *            interaction for which the score is calculated
475     * @param usageProfile
476     *            usage profile used for the calculation
477     * @return calculated usage score
478     */
479    public static double calculateUsageScore(Interaction interaction,
480                                             IStochasticProcess usageProfile)
481    {
482        double usageScore = 0.0d;
483
484        EList<InteractionFragment> interactionFragments = interaction.getFragments();
485        List<Event> eventSequence = new LinkedList<>();
486        eventSequence.add(Event.STARTEVENT);
487        for (InteractionFragment interactionFragment : interactionFragments) {
488            if (interactionFragment.getName() != null &&
489                interactionFragment.getName().endsWith("_recvFragment")) // TODO must be more
490                                                                         // generic
491            {
492                String serviceName =
493                    interactionFragment.getCovereds().get(0).getRepresents().getName().split("_")[0];
494                String methodName = "UNKNOWN";
495                if (interactionFragment instanceof MessageOccurrenceSpecification) {
496                    methodName =
497                        ((MessageOccurrenceSpecification) interactionFragment).getMessage()
498                            .getName();
499                }
500                eventSequence.add(new Event(new SimpleSOAPEventType(methodName, serviceName)));
501            }
502        }
503        eventSequence.add(Event.ENDEVENT);
504        double prob = usageProfile.getLogSum(eventSequence);
505        usageScore = eventSequence.size() * prob;
506
507        return usageScore;
508    }
509
510    /**
511     * <p>
512     * Extends the given model with an activity for usage-based scheduling of the test cases.
513     * </p>
514     *
515     * @param model
516     *            model to be extended
517     * @param usageProfile
518     *            usage profile used as foundation
519     */
520    public static void createScheduling(Model model,
521                                        IStochasticProcess usageProfile,
522                                        String testContextName)
523    {
524
525        final Profile utpProfile = model.getAppliedProfile("utp");
526        final Stereotype utpTestCase = (Stereotype) utpProfile.getOwnedMember("TestCase");
527        final Stereotype utpTestContext = (Stereotype) utpProfile.getOwnedMember("TestContext");
528
529        Component testContext = fetchTestContext(model, utpTestContext, testContextName);
530        if (testContext == null) {
531            throw new RuntimeException("Could not find any test context in the model");
532        }
533
534        Map<Operation, Double> usageScoreMapUnsorted = new HashMap<>();
535
536        // first, we determine all test cases and calculate their usage scores
537        for (Operation operation : testContext.getAllOperations()) {
538            if (operation.getAppliedStereotypes().contains(utpTestCase)) {
539                Interaction interaction = (Interaction) operation.getMethods().get(0);
540                usageScoreMapUnsorted
541                    .put(operation, calculateUsageScore(interaction, usageProfile));
542            }
543        }
544        Map<Operation, Double> usageScoreMapSorted = sortByValue(usageScoreMapUnsorted);
545
546        // now we create the scheduling
547        Activity schedulingActivity =
548            (Activity) testContext.createOwnedBehavior("UsageBasedScheduling",
549                                                       UMLPackage.Literals.ACTIVITY);
550        testContext.setClassifierBehavior(schedulingActivity);
551
552        ActivityNode startNode =
553            schedulingActivity.createOwnedNode("final", UMLPackage.Literals.INITIAL_NODE);
554        ActivityNode finalNode =
555            schedulingActivity.createOwnedNode("final", UMLPackage.Literals.ACTIVITY_FINAL_NODE);
556
557        ActivityNode currentOperationNode = startNode;
558
559        for (Entry<Operation, Double> entry : usageScoreMapSorted.entrySet()) {
560            Operation operation = entry.getKey();
561            CallOperationAction nextOperationNode =
562                (CallOperationAction) schedulingActivity
563                    .createOwnedNode(operation.getName(), UMLPackage.Literals.CALL_OPERATION_ACTION);
564            nextOperationNode.setOperation(operation);
565
566            ActivityEdge edge =
567                schedulingActivity.createEdge(currentOperationNode.getName() + "_to_" +
568                    nextOperationNode.getName(), UMLPackage.Literals.CONTROL_FLOW);
569            edge.setSource(currentOperationNode);
570            edge.setTarget(nextOperationNode);
571
572            currentOperationNode = nextOperationNode;
573        }
574
575        ActivityEdge edge =
576            schedulingActivity
577                .createEdge(currentOperationNode.getName() + "_to_" + finalNode.getName(),
578                            UMLPackage.Literals.CONTROL_FLOW);
579        edge.setSource(currentOperationNode);
580        edge.setTarget(finalNode);
581    }
582
583    /**
584     * From
585     * http://stackoverflow.com/questions/109383/how-to-sort-a-mapkey-value-on-the-values-in-java
586     * and adapted to do an inverse sorting
587     */
588    public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
589        List<Map.Entry<K, V>> list = new LinkedList<>(map.entrySet());
590        Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
591            @Override
592            public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
593                return -1 * (o1.getValue()).compareTo(o2.getValue());
594            }
595        });
596
597        Map<K, V> result = new LinkedHashMap<>();
598        for (Map.Entry<K, V> entry : list) {
599            result.put(entry.getKey(), entry.getValue());
600        }
601        return result;
602    }
603
604    /**
605     * <p>
606     * Helper function to get the name of a service from a SOAP event.
607     * </p>
608     *
609     * @param event
610     *            event for which the service name is retrieved
611     * @return service name
612     */
613    protected static String getServiceNameFromEvent(Event event) {
614        if (event.getType() instanceof SOAPEventType) {
615            return ((SOAPEventType) event.getType()).getServiceName();
616        }
617        else if (event.getType() instanceof SimpleSOAPEventType) {
618            return ((SimpleSOAPEventType) event.getType()).getServiceName();
619        }
620        else {
621            throw new RuntimeException(
622                                       "Wrong event type. Only SOAPEventType and SimpleSOAPEventType supported but was: " +
623                                           event.getType().getClass().getName());
624        }
625    }
626
627    /**
628     *
629     * <p>
630     * Helper function to get the called method from a SOAP event
631     * </p>
632     *
633     * @param event
634     *            event for which the called method is retrieved
635     * @return called method
636     */
637    private static String getCalledMethodFromEvent(Event event) {
638        if (event.getType() instanceof SOAPEventType) {
639            return ((SOAPEventType) event.getType()).getCalledMethod();
640        }
641        else if (event.getType() instanceof SimpleSOAPEventType) {
642            return ((SimpleSOAPEventType) event.getType()).getCalledMethod();
643        }
644        else {
645            throw new RuntimeException(
646                                       "Wrong event type. Only SOAPEventType and SimpleSOAPEventType supported but was: " +
647                                           event.getType().getClass().getName());
648        }
649    }
650
651    /**
652     * <p>
653     * Fetches an operation using only its name from a list of operations. Returns the first match
654     * found or null if no match is found.
655     * </p>
656     *
657     * @param operations
658     *            list of operations
659     * @param name
660     *            name of the operation
661     * @return first matching operation; null if no match is found
662     */
663    private static Operation getOperationFromName(EList<Operation> operations, String name) {
664        if (name == null) {
665            throw new IllegalArgumentException("name of the operation must not be null");
666        }
667        if (operations != null) {
668            for (Operation operation : operations) {
669                if (operation.getName() != null && operation.getName().equals(name)) {
670                    return operation;
671                }
672            }
673        }
674        return null;
675    }
676
677    /**
678     * <p>
679     * Determines which transitions match a given {@link SOAPEventType}.
680     * </p>
681     *
682     * @param transitions
683     *            the transitions
684     * @param eventType
685     *            the SOAP event
686     * @return matching transitions
687     */
688    private static List<Transition> matchTransitions(List<Transition> transitions,
689                                                     SOAPEventType eventType)
690    {
691        List<Transition> matching = new LinkedList<>();
692        for (Transition transition : transitions) {
693            // String serviceName = transition.getName().split("\\.")[0]; // TODO service name check
694            String methodName = transition.getName().split("\\.")[1];
695            if (methodName.equals(eventType.getCalledMethod())) {
696                matching.add(transition);
697            }
698        }
699        return matching;
700    }
701
702    private static List<Interface> getRealizedInterfacesFromProperty(Property property) {
703        return getRealizedInterfaceFromComponent((Component) property.getType());
704    }
705
706    private static List<Interface> getRealizedInterfaceFromComponent(Component comp) {
707        List<Interface> interfaces = new LinkedList<>();
708        // Interface myInterface = null;
709        for (Property property : comp.getAttributes()) {
710            if (property instanceof Port) {
711                Port port = (Port) property;
712                if (!port.isConjugated()) {
713                    interfaces.addAll(port.getProvideds());                 
714                }
715            }
716        }
717        return interfaces;
718    }
719
720    private static Component fetchTestContext(Package pkg,
721                                              Stereotype utpTestContext,
722                                              String testContextName)
723    {
724        List<Component> testContexts = fetchTestContextRecursively(pkg, utpTestContext);
725        if (testContexts.isEmpty()) {
726            return null;
727        }
728        if (testContextName != null) {
729            for (Component testContext : testContexts) {
730                if (testContextName.equals(testContext.getName())) {
731                    return testContext;
732                }
733            }
734            return null;
735        }
736        else {
737            return testContexts.get(0);
738        }
739    }
740
741    private static List<Component> fetchTestContextRecursively(Package pkg,
742                                                               Stereotype utpTestContext)
743    {
744        List<Component> testContexts = new LinkedList<>();
745        for (Element element : pkg.getOwnedElements()) {
746            if (element instanceof Package) {
747                testContexts.addAll(fetchTestContextRecursively((Package) element, utpTestContext));
748            }
749            if (element instanceof Component &&
750                element.getAppliedStereotypes().contains(utpTestContext))
751            {
752                testContexts.add((Component) element);
753            }
754        }
755        return testContexts;
756    }
757
758    /**
759     * <p>
760     * Infers connector between two lifelines.
761     * TODO: currently assumes only one connector between two lifelines possible. I need to make sure this assumption is valid.
762     * </p>
763     *
764     * @param userAttributes
765     * @param targetAttributes
766     */
767    private static Connector inferConnector(Lifeline msgSourceLifeline, Lifeline msgTargetLifeline)
768    {
769        EList<Property> userAttributes =
770            ((Component) msgSourceLifeline.getRepresents().getType()).getAttributes();
771        EList<Property> targetAttributes =
772            ((Component) msgTargetLifeline.getRepresents().getType()).getAttributes();
773        for (Property userAttribute : userAttributes) {
774            if (userAttribute instanceof Port) {
775                EList<ConnectorEnd> userEnds = ((Port) userAttribute).getEnds();
776                for (ConnectorEnd userEnd : userEnds) {
777                    Connector userConnector = (Connector) userEnd.eContainer();
778                    for (Property targetAttribute : targetAttributes) {
779                        if (targetAttribute instanceof Port) {
780                            EList<ConnectorEnd> targetEnds = ((Port) targetAttribute).getEnds();
781                            for (ConnectorEnd targetEnd : targetEnds) {
782                                Connector targetConnector = (Connector) targetEnd.eContainer();
783                                if (targetConnector == userConnector) {
784                                    return targetConnector;
785                                }
786                            }
787                        }
788                    }
789                }
790            }
791        }
792        return null;
793    }
794}
Note: See TracBrowser for help on using the repository browser.