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

Last change on this file since 1896 was 1896, checked in by sherbold, 9 years ago
  • UMLUtils can now find test contexts within packages
  • UMLUtils can now work with components that implement multiple interfaces
  • UMLUtils can now create synchronous and asynchronous messages
  • Property svn:mime-type set to text/plain
File size: 37.5 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 test context found is used.
273     */
274    public static void createInteractionFromEventSequence(List<Event> sequence,
275                                                          Model model,
276                                                          String interactionName,
277                                                          String testContextName)
278    {
279        final Profile utpProfile = model.getAppliedProfile("utp");
280        final Stereotype utpTestCase = (Stereotype) utpProfile.getOwnedMember("TestCase");
281        final Stereotype utpTestComponent = (Stereotype) utpProfile.getOwnedMember("TestComponent");
282        final Stereotype utpSUT = (Stereotype) utpProfile.getOwnedMember("SUT");
283        final Stereotype utpTestContext = (Stereotype) utpProfile.getOwnedMember("TestContext");
284
285        Component testContext = fetchTestContext(model, utpTestContext, testContextName);
286        if( testContext==null ) {
287            throw new RuntimeException("Could not find any test context in the model");
288        }
289       
290        Operation operation = testContext.createOwnedOperation(interactionName, null, null);
291        operation.applyStereotype(utpTestCase);
292
293        Interaction interaction =
294            (Interaction) testContext.createPackagedElement(interactionName + "_Impl",
295                                                            UMLPackage.Literals.INTERACTION);
296        operation.getMethods().add(interaction);
297
298        // create lifelines
299        Lifeline userLifeline = null;
300        // List<Port> userPorts = new LinkedList<>();
301        for (Property property : testContext.getAllAttributes()) {
302            if (property.getAppliedStereotypes().contains(utpSUT)) {
303                String serviceName = property.getName();
304                Lifeline targetLifeline = interaction.createLifeline(serviceName);
305                targetLifeline.setRepresents(property);
306            }
307            else if (property.getType().getAppliedStereotypes().contains(utpTestComponent)) {
308                userLifeline = interaction.createLifeline(property.getName());
309                userLifeline.setRepresents(property);
310            }   
311        }
312       
313        // TODO sanity checks for userLifeline!=null, etc.
314
315        int i = 0;
316        for (Event event : sequence) {
317            if (!(event.equals(Event.STARTEVENT) || event.equals(Event.ENDEVENT))) {
318                String serviceName = getServiceNameFromEvent(event);
319                String methodName = getCalledMethodFromEvent(event);
320                // determine lifelines
321                Lifeline msgTargetLifeline;
322                Lifeline msgSourceLifeline;
323               
324                if( serviceName.equals(userLifeline.getName()) ) {
325                    // message being send to user
326                    // currently we just select the first lifeline that is not the user
327                    // this, obviously, has to be replaced with the real service.
328                    // however, identification of the source of a message is still an open issue
329                    msgSourceLifeline = null;
330                    for( Lifeline lifeline : interaction.getLifelines() ) {
331                        if(!lifeline.equals(userLifeline)){
332                            msgSourceLifeline = lifeline;
333                            break;
334                        }
335                    }
336                    msgTargetLifeline = userLifeline;
337                } else {
338                    msgSourceLifeline = userLifeline;
339                    msgTargetLifeline = interaction.getLifeline(serviceName);
340                }
341               
342                // TODO null checks for lifelines
343
344                // determine target interface and be able to deal with multiple interfaces
345                List<Interface> targetInterfaces = getRealizedInterfacesFromProperty((Property) msgTargetLifeline.getRepresents());
346                if( targetInterfaces.isEmpty() ) {
347                    throw new RuntimeException("no interface associated with the property " + msgTargetLifeline.getRepresents().getName());
348                }
349                Interface targetInterface = null;
350                for( Interface intface : targetInterfaces ) {
351                    System.out.println(intface.getOperations());
352                    if (getOperationFromName(intface.getOperations(), methodName) != null) {
353                        // interface found
354                        targetInterface = intface;
355                        break;
356                    }
357                }
358                if( targetInterface == null ) {
359                    StringBuilder errStrBuilder = new StringBuilder();
360                    errStrBuilder.append("operation not found in the implementing interfaces (");
361                    Iterator<Interface> iter = targetInterfaces.iterator();
362                    while( iter.hasNext() ) {
363                        String interfaceName = iter.next().getName();
364                        errStrBuilder.append(interfaceName);
365                        if( iter.hasNext() ) {
366                            errStrBuilder.append(",");
367                        } else {
368                            errStrBuilder.append("): " + methodName);
369                        }
370                    }
371                    throw new RuntimeException(errStrBuilder.toString());
372                }
373               
374                // create message ASYNCH
375                boolean asynch = false;
376                if( asynch ) {
377                    MessageOccurrenceSpecification sendFragment =
378                        (MessageOccurrenceSpecification) interaction
379                            .createFragment(i + ":" + methodName + "_sendFragment",
380                                            UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
381                    MessageOccurrenceSpecification recvFragment =
382                        (MessageOccurrenceSpecification) interaction
383                            .createFragment(i + ":" + methodName + "_recvFragment",
384                                            UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
385   
386                    sendFragment.setCovered(msgSourceLifeline);
387                    recvFragment.setCovered(msgTargetLifeline);
388   
389                    Message message = interaction.createMessage(methodName);
390                    if (getOperationFromName(targetInterface.getOperations(), methodName) == null) {
391                        System.out.println("operation not found in the " + targetInterface.getName() + " interface: " + methodName);
392                    }
393                    message.setSignature(getOperationFromName(targetInterface.getOperations(),
394                                                              methodName));
395                    message.setMessageSort(MessageSort.ASYNCH_CALL_LITERAL);
396                    message.setSendEvent(sendFragment);
397                    message.setReceiveEvent(recvFragment);
398   
399                    // now the connector needs to be determined
400                    EList<Property> userAttributes = ((Component) msgSourceLifeline.getRepresents().getType()).getAttributes();
401                    EList<Property> targetAttributes = ((Component) msgTargetLifeline.getRepresents().getType()).getAttributes();
402                   
403                    for( Property userAttribute : userAttributes ) {
404                        if( userAttribute instanceof Port ) {
405                            EList<ConnectorEnd> userEnds = ((Port) userAttribute).getEnds();
406                            for( ConnectorEnd userEnd : userEnds ) {
407                                Connector userConnector = (Connector) userEnd.eContainer();
408                                for( Property targetAttribute : targetAttributes ) {
409                                    if( targetAttribute instanceof Port ) {
410                                        EList<ConnectorEnd> targetEnds = ((Port) targetAttribute).getEnds();
411                                        for( ConnectorEnd targetEnd : targetEnds ) {
412                                            Connector targetConnector = (Connector) targetEnd.eContainer();
413                                            if( targetConnector==userConnector ) {
414                                                message.setConnector(targetConnector);
415                                            }
416                                        }
417                                    }
418                                }
419                            }
420                        }
421                    }
422                    sendFragment.setMessage(message);
423                    recvFragment.setMessage(message);
424                } else {
425                    // SYNCH call
426                    MessageOccurrenceSpecification callSendFragment =
427                        (MessageOccurrenceSpecification) interaction
428                            .createFragment(i + ":" + methodName + "_callSendFragment",
429                                            UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
430                    MessageOccurrenceSpecification callRecvFragment =
431                        (MessageOccurrenceSpecification) interaction
432                            .createFragment(i + ":" + methodName + "_callRecvFragment",
433                                            UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
434                   
435                    MessageOccurrenceSpecification replySendFragment =
436                        (MessageOccurrenceSpecification) interaction
437                            .createFragment(i + ":" + methodName + "_replySendFragment",
438                                            UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
439                    MessageOccurrenceSpecification replyRecvFragment =
440                        (MessageOccurrenceSpecification) interaction
441                            .createFragment(i + ":" + methodName + "_replyRecvFragment",
442                                            UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
443   
444                    BehaviorExecutionSpecification sourceBehaviorExecutionSpecification =
445                        (BehaviorExecutionSpecification) interaction
446                            .createFragment(":" + methodName + "_sourceBhvExecSpec",
447                                            UMLPackage.Literals.BEHAVIOR_EXECUTION_SPECIFICATION);
448                    BehaviorExecutionSpecification targetBehaviorExecutionSpecification =
449                        (BehaviorExecutionSpecification) interaction
450                            .createFragment(":" + methodName + "_targetBhvExecSpec",
451                                            UMLPackage.Literals.BEHAVIOR_EXECUTION_SPECIFICATION);
452                   
453                   
454                    callSendFragment.setCovered(msgSourceLifeline);
455                    callRecvFragment.setCovered(msgTargetLifeline);
456                    replySendFragment.setCovered(msgTargetLifeline);
457                    replyRecvFragment.setCovered(msgSourceLifeline);
458                   
459                    sourceBehaviorExecutionSpecification.setStart(callSendFragment);
460                    sourceBehaviorExecutionSpecification.setFinish(replyRecvFragment);
461                    targetBehaviorExecutionSpecification.setStart(callRecvFragment);
462                    targetBehaviorExecutionSpecification.setFinish(replySendFragment);
463   
464                    Operation calledOperation = getOperationFromName(targetInterface.getOperations(), methodName);
465                   
466                    // create call
467                    Message callMessage = interaction.createMessage(methodName);
468                    callMessage.setSignature(calledOperation);
469                    callMessage.setMessageSort(MessageSort.SYNCH_CALL_LITERAL);
470                    callMessage.setSendEvent(callSendFragment);
471                    callMessage.setReceiveEvent(callRecvFragment);
472                    callSendFragment.setMessage(callMessage);
473                    callRecvFragment.setMessage(callMessage);
474   
475                    // now the connector needs to be determined
476                    EList<Property> userAttributes = ((Component) msgSourceLifeline.getRepresents().getType()).getAttributes();
477                    EList<Property> targetAttributes = ((Component) msgTargetLifeline.getRepresents().getType()).getAttributes();
478                   
479                    for( Property userAttribute : userAttributes ) {
480                        if( userAttribute instanceof Port ) {
481                            EList<ConnectorEnd> userEnds = ((Port) userAttribute).getEnds();
482                            for( ConnectorEnd userEnd : userEnds ) {
483                                Connector userConnector = (Connector) userEnd.eContainer();
484                                for( Property targetAttribute : targetAttributes ) {
485                                    if( targetAttribute instanceof Port ) {
486                                        EList<ConnectorEnd> targetEnds = ((Port) targetAttribute).getEnds();
487                                        for( ConnectorEnd targetEnd : targetEnds ) {
488                                            Connector targetConnector = (Connector) targetEnd.eContainer();
489                                            if( targetConnector==userConnector ) {
490                                                callMessage.setConnector(targetConnector);
491                                            }
492                                        }
493                                    }
494                                }
495                            }
496                        }
497                    }
498                   
499                    // create reply
500                    Message replyMessage = interaction.createMessage(methodName + "_reply");
501                    replyMessage.setMessageSort(MessageSort.REPLY_LITERAL);
502                    replyMessage.setSignature(calledOperation);
503                    replyMessage.setSendEvent(replySendFragment);
504                    replyMessage.setReceiveEvent(replyRecvFragment);
505                    replySendFragment.setMessage(replyMessage);
506                    replyRecvFragment.setMessage(replyMessage);
507                }
508               
509
510                i++;
511            }
512        }
513    }
514
515    /**
516     * <p>
517     * Calculates the usage score of an interaction as the logsum of the event probabilities
518     * multiplied with the length of the interaction.
519     * </p>
520     *
521     * @param interaction
522     *            interaction for which the score is calculated
523     * @param usageProfile
524     *            usage profile used for the calculation
525     * @return calculated usage score
526     */
527    public static double calculateUsageScore(Interaction interaction,
528                                             IStochasticProcess usageProfile)
529    {
530        double usageScore = 0.0d;
531
532        EList<InteractionFragment> interactionFragments = interaction.getFragments();
533        List<Event> eventSequence = new LinkedList<>();
534        eventSequence.add(Event.STARTEVENT);
535        for (InteractionFragment interactionFragment : interactionFragments) {
536            if (interactionFragment.getName() != null &&
537                interactionFragment.getName().endsWith("_recvFragment"))
538            {
539                String serviceName =
540                    interactionFragment.getCovereds().get(0).getRepresents().getName().split("_")[0];
541                String methodName = "UNKNOWN";
542                if (interactionFragment instanceof MessageOccurrenceSpecification) {
543                    methodName =
544                        ((MessageOccurrenceSpecification) interactionFragment).getMessage()
545                            .getName();
546                }
547                eventSequence.add(new Event(new SimpleSOAPEventType(methodName, serviceName)));
548            }
549        }
550        eventSequence.add(Event.ENDEVENT);
551        double prob = usageProfile.getLogSum(eventSequence);
552        usageScore = eventSequence.size() * prob;
553
554        return usageScore;
555    }
556
557    /**
558     * <p>
559     * Extends the given model with an activity for usage-based scheduling of the test cases.
560     * </p>
561     *
562     * @param model
563     *            model to be extended
564     * @param usageProfile
565     *            usage profile used as foundation
566     */
567    public static void createScheduling(Model model, IStochasticProcess usageProfile, String testContextName) {
568
569        final Profile utpProfile = model.getAppliedProfile("utp");
570        final Stereotype utpTestCase = (Stereotype) utpProfile.getOwnedMember("TestCase");
571        final Stereotype utpTestContext = (Stereotype) utpProfile.getOwnedMember("TestContext");
572
573        Component testContext = fetchTestContext(model, utpTestContext, testContextName);
574        if( testContext==null ) {
575            throw new RuntimeException("Could not find any test context in the model");
576        }
577
578        Map<Operation, Double> usageScoreMapUnsorted = new HashMap<>();
579
580        // first, we determine all test cases and calculate their usage scores
581        for (Operation operation : testContext.getAllOperations()) {
582            if (operation.getAppliedStereotypes().contains(utpTestCase)) {
583                Interaction interaction = (Interaction) operation.getMethods().get(0);
584                usageScoreMapUnsorted
585                    .put(operation, calculateUsageScore(interaction, usageProfile));
586            }
587        }
588        Map<Operation, Double> usageScoreMapSorted = sortByValue(usageScoreMapUnsorted);
589
590        // now we create the scheduling
591        Activity schedulingActivity =
592            (Activity) testContext.createOwnedBehavior("UsageBasedScheduling",
593                                                       UMLPackage.Literals.ACTIVITY);
594        testContext.setClassifierBehavior(schedulingActivity);
595
596        ActivityNode startNode =
597            schedulingActivity.createOwnedNode("final", UMLPackage.Literals.INITIAL_NODE);
598        ActivityNode finalNode =
599            schedulingActivity.createOwnedNode("final", UMLPackage.Literals.ACTIVITY_FINAL_NODE);
600
601        ActivityNode currentOperationNode = startNode;
602
603        for (Entry<Operation, Double> entry : usageScoreMapSorted.entrySet()) {
604            Operation operation = entry.getKey();
605            CallOperationAction nextOperationNode =
606                (CallOperationAction) schedulingActivity
607                    .createOwnedNode(operation.getName(), UMLPackage.Literals.CALL_OPERATION_ACTION);
608            nextOperationNode.setOperation(operation);
609
610            ActivityEdge edge =
611                schedulingActivity.createEdge(currentOperationNode.getName() + "_to_" +
612                    nextOperationNode.getName(), UMLPackage.Literals.CONTROL_FLOW);
613            edge.setSource(currentOperationNode);
614            edge.setTarget(nextOperationNode);
615
616            currentOperationNode = nextOperationNode;
617        }
618
619        ActivityEdge edge =
620            schedulingActivity
621                .createEdge(currentOperationNode.getName() + "_to_" + finalNode.getName(),
622                            UMLPackage.Literals.CONTROL_FLOW);
623        edge.setSource(currentOperationNode);
624        edge.setTarget(finalNode);
625    }
626
627    /**
628     * From
629     * http://stackoverflow.com/questions/109383/how-to-sort-a-mapkey-value-on-the-values-in-java
630     * and adapted to do an inverse sorting
631     */
632    public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
633        List<Map.Entry<K, V>> list = new LinkedList<>(map.entrySet());
634        Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
635            @Override
636            public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
637                return -1 * (o1.getValue()).compareTo(o2.getValue());
638            }
639        });
640
641        Map<K, V> result = new LinkedHashMap<>();
642        for (Map.Entry<K, V> entry : list) {
643            result.put(entry.getKey(), entry.getValue());
644        }
645        return result;
646    }
647
648    /**
649     * <p>
650     * Helper function to get the name of a service from a SOAP event.
651     * </p>
652     *
653     * @param event
654     *            event for which the service name is retrieved
655     * @return service name
656     */
657    protected static String getServiceNameFromEvent(Event event) {
658        if (event.getType() instanceof SOAPEventType) {
659            return ((SOAPEventType) event.getType()).getServiceName();
660        }
661        else if (event.getType() instanceof SimpleSOAPEventType) {
662            return ((SimpleSOAPEventType) event.getType()).getServiceName();
663        }
664        else {
665            throw new RuntimeException(
666                                       "Wrong event type. Only SOAPEventType and SimpleSOAPEventType supported but was: " +
667                                           event.getType().getClass().getName());
668        }
669    }
670
671    /**
672     *
673     * <p>
674     * Helper function to get the called method from a SOAP event
675     * </p>
676     *
677     * @param event
678     *            event for which the called method is retrieved
679     * @return called method
680     */
681    private static String getCalledMethodFromEvent(Event event) {
682        if (event.getType() instanceof SOAPEventType) {
683            return ((SOAPEventType) event.getType()).getCalledMethod();
684        }
685        else if (event.getType() instanceof SimpleSOAPEventType) {
686            return ((SimpleSOAPEventType) event.getType()).getCalledMethod();
687        }
688        else {
689            throw new RuntimeException(
690                                       "Wrong event type. Only SOAPEventType and SimpleSOAPEventType supported but was: " +
691                                           event.getType().getClass().getName());
692        }
693    }
694
695    /**
696     * <p>
697     * Fetches an operation using only its name from a list of operations. Returns the first match
698     * found or null if no match is found.
699     * </p>
700     *
701     * @param operations
702     *            list of operations
703     * @param name
704     *            name of the operation
705     * @return first matching operation; null if no match is found
706     */
707    private static Operation getOperationFromName(EList<Operation> operations, String name) {
708        if (name == null) {
709            throw new IllegalArgumentException("name of the operation must not be null");
710        }
711        if (operations != null) {
712            for (Operation operation : operations) {
713                if (operation.getName() != null && operation.getName().equals(name)) {
714                    return operation;
715                }
716            }
717        }
718        return null;
719    }
720
721    /**
722     * <p>
723     * Determines which transitions match a given {@link SOAPEventType}.
724     * </p>
725     *
726     * @param transitions
727     *            the transitions
728     * @param eventType
729     *            the SOAP event
730     * @return matching transitions
731     */
732    private static List<Transition> matchTransitions(List<Transition> transitions,
733                                                     SOAPEventType eventType)
734    {
735        List<Transition> matching = new LinkedList<>();
736        for (Transition transition : transitions) {
737            // String serviceName = transition.getName().split("\\.")[0]; // TODO service name check
738            String methodName = transition.getName().split("\\.")[1];
739            if (methodName.equals(eventType.getCalledMethod())) {
740                matching.add(transition);
741            }
742        }
743        return matching;
744    }
745   
746    private static List<Interface> getRealizedInterfacesFromProperty(Property property) {
747        return getRealizedInterfaceFromComponent((Component) property.getType());
748    }
749   
750    private static List<Interface> getRealizedInterfaceFromComponent(Component comp) {
751        List<Interface> interfaces = new LinkedList<>();
752        //Interface myInterface = null;
753        for( Property property : comp.getAttributes() ) {
754            if( property instanceof Port ) {
755                Port port = (Port) property;
756                if( !port.isConjugated() ) {
757                    interfaces.addAll(port.getProvideds());
758                    /*
759                    if( myInterface==null ) {
760                        myInterface = port.getProvideds().get(0);
761                    }
762                    else if( myInterface!=port.getProvideds().get(0)) {
763                        System.err.println("multiple different interfaces found");
764                    }
765                    */
766                }
767            }
768        }
769        return interfaces;
770        //return myInterface;
771        //return ((Port) comp.getAttributes().get(0)).getInterface();
772        //Realization realization = (Realization) comp.getNestedClassifiers().get(0).getRelationships(UMLPackage.Literals.REALIZATION).get(0);
773        //return (Interface) realization.getSuppliers().get(0);
774    }
775   
776    private static Component fetchTestContext(Package pkg, Stereotype utpTestContext, String testContextName) {
777        List<Component> testContexts = fetchTestContextRecursively(pkg, utpTestContext);
778        if( testContexts.isEmpty() ) {
779            return null;
780        }
781        if( testContextName!=null ) {
782            for( Component testContext : testContexts ) {
783                if( testContextName.equals(testContext.getName()) ) {
784                    return testContext;
785                }
786            }
787            return null;
788        } else {
789            return testContexts.get(0);
790        }
791    }
792   
793    private static List<Component> fetchTestContextRecursively(Package pkg, Stereotype utpTestContext) {
794        List<Component> testContexts = new LinkedList<>();
795        for( Element element : pkg.getOwnedElements() ) {
796            if( element instanceof Package ) {
797                testContexts.addAll(fetchTestContextRecursively((Package) element, utpTestContext));
798            }
799            if( element instanceof Component && element.getAppliedStereotypes().contains(utpTestContext) ) {
800                testContexts.add((Component) element);
801            }
802        }
803        return testContexts;
804    }
805}
Note: See TracBrowser for help on using the repository browser.