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

Last change on this file since 2008 was 2008, checked in by sherbold, 9 years ago
  • moved logic for the creation of Interactions from event sequences from the UML utils to a separate class
  • Property svn:mime-type set to text/plain
File size: 42.9 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.HashSet;
22import java.util.Iterator;
23import java.util.LinkedHashMap;
24import java.util.LinkedList;
25import java.util.List;
26import java.util.Map;
27import java.util.Map.Entry;
28import java.util.Set;
29import java.util.TreeSet;
30import java.util.logging.Level;
31
32import org.eclipse.emf.common.util.EList;
33import org.eclipse.uml2.uml.Activity;
34import org.eclipse.uml2.uml.ActivityEdge;
35import org.eclipse.uml2.uml.ActivityNode;
36import org.eclipse.uml2.uml.CallEvent;
37import org.eclipse.uml2.uml.CallOperationAction;
38import org.eclipse.uml2.uml.Comment;
39import org.eclipse.uml2.uml.Component;
40import org.eclipse.uml2.uml.Connector;
41import org.eclipse.uml2.uml.ConnectorEnd;
42import org.eclipse.uml2.uml.Element;
43import org.eclipse.uml2.uml.Expression;
44import org.eclipse.uml2.uml.Interaction;
45import org.eclipse.uml2.uml.InteractionFragment;
46import org.eclipse.uml2.uml.Interface;
47import org.eclipse.uml2.uml.Lifeline;
48import org.eclipse.uml2.uml.LiteralBoolean;
49import org.eclipse.uml2.uml.LiteralInteger;
50import org.eclipse.uml2.uml.LiteralReal;
51import org.eclipse.uml2.uml.LiteralString;
52import org.eclipse.uml2.uml.Message;
53import org.eclipse.uml2.uml.MessageOccurrenceSpecification;
54import org.eclipse.uml2.uml.MessageSort;
55import org.eclipse.uml2.uml.Model;
56import org.eclipse.uml2.uml.Operation;
57import org.eclipse.uml2.uml.Package;
58import org.eclipse.uml2.uml.Parameter;
59import org.eclipse.uml2.uml.ParameterDirectionKind;
60import org.eclipse.uml2.uml.Port;
61import org.eclipse.uml2.uml.PrimitiveType;
62import org.eclipse.uml2.uml.Property;
63import org.eclipse.uml2.uml.Region;
64import org.eclipse.uml2.uml.StateMachine;
65import org.eclipse.uml2.uml.Stereotype;
66import org.eclipse.uml2.uml.Transition;
67import org.eclipse.uml2.uml.Trigger;
68import org.eclipse.uml2.uml.UMLPackage;
69import org.eclipse.uml2.uml.Vertex;
70
71import de.ugoe.cs.autoquest.eventcore.Event;
72import de.ugoe.cs.autoquest.plugin.http.SOAPUtils;
73import de.ugoe.cs.autoquest.plugin.http.eventcore.SOAPEventType;
74import de.ugoe.cs.autoquest.plugin.http.eventcore.SimpleSOAPEventType;
75import de.ugoe.cs.autoquest.plugin.http.eventcore.SimpleSOAPEventType.CallType;
76import de.ugoe.cs.autoquest.plugin.uml.eventcore.UMLTransitionType;
77import de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess;
78import de.ugoe.cs.util.StringTools;
79import de.ugoe.cs.util.console.Console;
80
81/**
82 * <p>
83 * Utilities for working with UML.
84 * </p>
85 *
86 * @author Steffen Herbold
87 */
88public class UMLUtils {
89
90    /**
91     * In case a multiplicity is defined as *, this value defines the highest one that can be picked
92     */
93    final static int MAX_MULTIPLICITY = 10;
94
95    /**
96     * <p>
97     * Method for checking if the information in a usage journal can be mapped to the SUT model. In
98     * case this is not possible, the violations are reported.
99     * </p>
100     *
101     * @param sequences
102     *            sequences of the usage journal
103     * @param model
104     *            SUT model that is validated
105     * @param testContextName
106     *            name of the test context to be used; if null, the first test context found is used
107     * @return number of violations
108     */
109    public static int validateModelWithLog(Collection<List<Event>> sequences,
110                                           Model model,
111                                           String testContextName)
112    {
113        int violationCount = 0;
114        Component testContext = fetchTestContext(model, testContextName);
115        if (testContext == null) {
116            violationCount++;
117            if (testContextName == null) {
118                Console.traceln(Level.SEVERE, "Could not find any TestContext in the model.");
119
120            }
121            else {
122                Console.traceln(Level.SEVERE, "Could not find TestContext in the model: " +
123                    testContextName);
124            }
125            Console
126                .traceln(Level.SEVERE,
127                         "Hint: Check if you have applied the TestContext stereotype correctly in the model.");
128            Console.traceln(Level.SEVERE, "Aborting");
129            return violationCount;
130        }
131
132        // Create list of unique methods calls
133        HashMap<String, Set<String>> calledMethods = new HashMap<>();
134        for (List<Event> sequence : sequences) {
135            for (Event event : sequence) {
136                String serviceName = SOAPUtils.getServiceNameFromEvent(event);
137                String calledMethod = SOAPUtils.getCalledMethodFromEvent(event);
138                if (serviceName != null) {
139                    Set<String> curCalledMethods = calledMethods.get(serviceName);
140                    if (curCalledMethods == null) {
141                        curCalledMethods = new TreeSet<>();
142                        calledMethods.put(serviceName, curCalledMethods);
143                    }
144                    curCalledMethods.add(calledMethod);
145                }
146            }
147        }
148
149        Console.traceln(Level.INFO,
150                        "Found the following services and operations in the usage data: ");
151        for (Entry<String, Set<String>> entry : calledMethods.entrySet()) {
152            Console.traceln(Level.INFO, "\tService \"" + entry.getKey() + "\": ");
153            for (String method : entry.getValue()) {
154                Console.traceln(Level.INFO, "\t\t" + method);
155            }
156        }
157
158        // fetch all SUTs and TestComponents
159        HashMap<String, Property> properties = new HashMap<>();
160        for (Property property : fetchAllSUTProperties(testContext)) {
161            properties.put(property.getName(), property);
162        }
163        for (Property property : fetchAllTestComponentProperties(testContext)) {
164            properties.put(property.getName(), property);
165        }
166        Console.traceln(Level.INFO, "Found the following services in the TestConfiguration:");
167        for (Entry<String, Property> entry : properties.entrySet()) {
168            Console.traceln(Level.INFO, "\t" + entry.getKey());
169        }
170
171        for (Entry<String, Set<String>> entry : calledMethods.entrySet()) {
172            String serviceName = entry.getKey();
173            Console.traceln(Level.INFO, "Checking service: " + serviceName);
174            Set<String> methodNames = entry.getValue();
175            Property property = properties.get(serviceName);
176            if (property == null) {
177                violationCount++;
178                Console.traceln(Level.SEVERE, "\tCould not find property for service: " +
179                    serviceName);
180                Console
181                    .traceln(Level.SEVERE,
182                             "\tHint: Check service name map and/or model if the service is present and spelled correctly.");
183                Console
184                    .traceln(Level.SEVERE,
185                             "\tHint: Check if the SUT/TestComponent stereotype has been applied correctly in this TestContext.");
186            }
187            else {
188                Set<Interface> interfaces = getRealizedInterfacesFromProperty(property);
189                if (interfaces.isEmpty()) {
190                    violationCount++;
191                    Console
192                        .traceln(Level.SEVERE,
193                                 "\tCould not find any interfaces implementing the property for service: " +
194                                     serviceName);
195                    Console
196                        .traceln(Level.SEVERE,
197                                 "\tHint: Check if the property correctly realizes the interfaces in the model.");
198                }
199                else {
200                    Console.traceln(Level.INFO,
201                                    "\tFound the following realized interfaces for the service \"" +
202                                        serviceName + "\": ");
203                    for (Interface intface : interfaces) {
204                        Console.traceln(Level.INFO, "\t" + intface.getName());
205                        for (Operation operation : intface.getAllOperations()) {
206                            Console.traceln(Level.INFO, "\t\t" + operation.getName());
207                        }
208                    }
209                    for (String methodName : methodNames) {
210                        boolean methodFound = false;
211                        for (Interface intface : interfaces) {
212                            if (getOperationFromName(intface.getOperations(), methodName) != null) {
213                                // interface found
214                                Console.traceln(Level.INFO, "\tMethod " + methodName +
215                                    " found in interface " + intface.getName());
216                                methodFound = true;
217                            }
218                        }
219                        if (!methodFound) {
220                            violationCount++;
221                            Console.traceln(Level.SEVERE, "\tCould not find operation: " +
222                                methodName);
223                        }
224                    }
225                }
226            }
227        }
228        return violationCount;
229    }
230
231    /**
232     * <p>
233     * Creates a sequence of events with {@link UMLTransitionType} as event type from a given
234     * sequence of events with the {@link SOAPEventType} or {@link SimpleSOAPEventType}, by matching
235     * the sequences to a state machine.
236     * </p>
237     *
238     * @param sequence
239     *            SOAP sequences
240     * @param stateMachine
241     *            the state machine
242     * @return create UML sequences
243     */
244    public static List<Event> createUMLTransitionSequence(List<Event> sequence,
245                                                          StateMachine stateMachine)
246    {
247        List<List<Transition>> matchingSequences =
248            determineMatchingTransitionSequences(sequence, stateMachine);
249
250        if (matchingSequences.size() != 1) {
251            throw new RuntimeException("no unique match found; " + matchingSequences.size() +
252                " matches");
253        }
254        List<Event> umlEventSequence = new LinkedList<>();
255        for (Transition transition : matchingSequences.get(0)) {
256            umlEventSequence.add(new Event(new UMLTransitionType(transition)));
257        }
258        return umlEventSequence;
259    }
260
261    /**
262     * <p>
263     * Uses a sequences of events with the {@link UMLTransitionType} to determine the transition
264     * probabilities for the state machine.
265     * </p>
266     *
267     * @param sequences
268     *            UML sequences
269     * @param stateMachine
270     *            state machine to be converted to a usage profile
271     */
272    public static void convertStateMachineToUsageProfile(Collection<List<Event>> sequences,
273                                                         StateMachine stateMachine)
274    {
275        // create state->outgoings hashmap
276        Map<Vertex, Map<Transition, Integer>> stateMap = new HashMap<>();
277        for (Region region : stateMachine.getRegions()) {
278            for (Vertex state : region.getSubvertices()) {
279                stateMap.put(state, new HashMap<Transition, Integer>());
280            }
281        }
282
283        // create counters for each transition
284        for (List<Event> sequence : sequences) {
285            for (Event event : sequence) {
286                if (event.getType() instanceof UMLTransitionType) {
287                    Transition transition = ((UMLTransitionType) event.getType()).getTransition();
288                    Map<Transition, Integer> transitionMap = stateMap.get(transition.getSource());
289                    Integer value = transitionMap.get(transition);
290                    if (value == null) {
291                        value = 0;
292                    }
293                    transitionMap.put(transition, value + 1);
294                }
295                else {
296                    throw new RuntimeException(
297                                               "Wrong event type. Only UMLTransitionType supported but was: " +
298                                                   event.getType().getClass().getName());
299                }
300            }
301        }
302
303        // calculate probabilities
304        for (Region region : stateMachine.getRegions()) {
305            for (Vertex state : region.getSubvertices()) {
306                Map<Transition, Integer> transitionMap = stateMap.get(state);
307                int totalCount = 0;
308                for (Entry<Transition, Integer> entry : transitionMap.entrySet()) {
309                    totalCount += entry.getValue();
310                }
311                if (totalCount != 0) {
312                    for (Transition transition : state.getOutgoings()) {
313                        double prob = 0.0d;
314                        if (transitionMap.containsKey(transition)) {
315                            prob = ((double) transitionMap.get(transition)) / totalCount;
316                        }
317                        Comment comment = transition.createOwnedComment();
318                        comment.setBody("" + prob);
319                    }
320                }
321                else {
322                    // system has never been in this state, all transitions equally likely
323                    int numOutgoings = state.getOutgoings().size();
324                    for (Transition transition : state.getOutgoings()) {
325                        Comment comment = transition.createOwnedComment();
326                        comment.setBody("" + (1.0d / numOutgoings));
327                    }
328                }
329            }
330        }
331    }
332
333    /**
334     * <p>
335     * Determines all matching {@link Transition} sequences in a state machine for a given sequence
336     * of SOAP events.
337     * </p>
338     *
339     * @param sequence
340     *            SOAP sequence
341     * @param stateMachine
342     *            the state machine
343     * @return all matching {@link Transition} sequences
344     */
345    public static List<List<Transition>> determineMatchingTransitionSequences(List<Event> sequence,
346                                                                              StateMachine stateMachine)
347    {
348        EList<Region> regions = stateMachine.getRegions();
349        EList<Vertex> states = null;
350        for (Region region : regions) {
351            if (states == null) {
352                states = region.getSubvertices();
353            }
354            else {
355                states.addAll(region.getSubvertices());
356            }
357        }
358        List<Transition> allTransitions = new LinkedList<>();
359        for (Vertex state : states) {
360            allTransitions.addAll(state.getOutgoings());
361        }
362
363        List<List<Transition>> matchingSequences = null;
364        List<Transition> currentTransitions = null;
365
366        // first, we try to find a single unique transition that we can match using the method name
367        for (Iterator<Event> eventIterator = sequence.iterator(); eventIterator.hasNext();) {
368            Event event = eventIterator.next();
369            if (matchingSequences == null) {
370                matchingSequences = new LinkedList<>();
371                List<Transition> initialMatches = matchTransitions(allTransitions, event);
372                for (Transition transition : initialMatches) {
373                    List<Transition> candidate = new LinkedList<>();
374                    candidate.add(transition);
375                    matchingSequences.add(candidate);
376                }
377                currentTransitions = initialMatches;
378            }
379            else {
380                List<List<Transition>> nextMatchingSequences = new LinkedList<>();
381                List<Transition> nextCurrentTransitions = new LinkedList<>();
382                Iterator<Transition> currentTransitionIterator = currentTransitions.iterator();
383                Iterator<List<Transition>> currentMatchingSequencesIterator =
384                    matchingSequences.iterator();
385                while (currentTransitionIterator.hasNext()) {
386                    Transition currentTransition = currentTransitionIterator.next();
387                    List<Transition> currentMatch = currentMatchingSequencesIterator.next();
388
389                    List<Transition> matches =
390                        matchTransitions(currentTransition.getTarget().getOutgoings(), event);
391                    if (matches.isEmpty()) {
392                        throw new RuntimeException("no matches found");
393                    }
394                    for (Transition matchingTransition : matches) {
395                        List<Transition> candidate = new LinkedList<>(currentMatch);
396                        candidate.add(matchingTransition);
397                        nextMatchingSequences.add(candidate);
398                        nextCurrentTransitions.add(matchingTransition);
399                    }
400                }
401                matchingSequences = nextMatchingSequences;
402                currentTransitions = nextCurrentTransitions;
403            }
404        }
405        return matchingSequences;
406    }
407
408    /**
409     * <p>
410     * Extends a given model with an interaction that represents an observed sequence.
411     * </p>
412     *
413     * @param sequence
414     *            sequence that is added as sequence diagram
415     * @param model
416     *            UML model to which the interaction is added
417     * @param interactionName
418     *            name of the interaction
419     * @param testContextName
420     *            Name of the test context that should be used. If this value is null, the first
421     *            test context found is used.
422     * @param useRandomMsgBodies
423     *            defines is random request bodies are used or the body of the associated event
424     */
425    public static Interaction createInteractionFromEventSequence(List<Event> sequence,
426                                                                 Model model,
427                                                                 String interactionName,
428                                                                 String testContextName,
429                                                                 boolean useRandomMsgBodies)
430    {
431        UMLInteractionCreator interactionCreator = new UMLInteractionCreator(model, testContextName, useRandomMsgBodies);
432        return interactionCreator.createInteraction(sequence, interactionName);
433    }
434
435    /**
436     * <p>
437     * Calculates the usage score of an interaction as the logsum of the event probabilities
438     * multiplied with the length of the interaction.
439     * </p>
440     *
441     * @param interaction
442     *            interaction for which the score is calculated
443     * @param usageProfile
444     *            usage profile used for the calculation
445     * @return calculated usage score
446     */
447    public static double calculateUsageScore(Interaction interaction,
448                                             IStochasticProcess usageProfile)
449    {
450        double usageScore = 0.0d;
451        EList<InteractionFragment> interactionFragments = interaction.getFragments();
452        List<Event> eventSequence = new LinkedList<>();
453        eventSequence.add(Event.STARTEVENT);
454        for (InteractionFragment interactionFragment : interactionFragments) {
455            if (interactionFragment instanceof MessageOccurrenceSpecification) {
456                Message message =
457                    ((MessageOccurrenceSpecification) interactionFragment).getMessage();
458                // if (message.getReceiveEvent().equals(interactionFragment) &&
459                // isCallMessage(message))
460                if (message.getReceiveEvent().equals(interactionFragment)) {
461                    String clientName;
462                    String serviceName;
463                    String methodName = message.getSignature().getName();
464                    CallType callType;
465                    if (isCallMessage(message)) {
466                        clientName =
467                            ((MessageOccurrenceSpecification) message.getSendEvent()).getCovereds()
468                                .get(0).getName();
469                        serviceName =
470                            ((MessageOccurrenceSpecification) message.getReceiveEvent())
471                                .getCovereds().get(0).getName();
472                        callType = CallType.REQUEST;
473                    }
474                    else {
475                        clientName =
476                            ((MessageOccurrenceSpecification) message.getReceiveEvent())
477                                .getCovereds().get(0).getName();
478                        serviceName =
479                            ((MessageOccurrenceSpecification) message.getSendEvent()).getCovereds()
480                                .get(0).getName();
481                        callType = CallType.RESPONSE;
482                    }
483                    eventSequence.add(new Event(new SimpleSOAPEventType(methodName, serviceName,
484                                                                        clientName, null, null,
485                                                                        callType)));
486                }
487            }
488        }
489        eventSequence.add(Event.ENDEVENT);
490        double prob = usageProfile.getLogSum(eventSequence);
491        usageScore = eventSequence.size() * prob;
492
493        return usageScore;
494    }
495
496    /**
497     * <p>
498     * Extends the given model with an activity for usage-based scheduling of the test cases.
499     * </p>
500     *
501     * @param model
502     *            model to be extended
503     * @param usageProfile
504     *            usage profile used as foundation
505     */
506    public static void createScheduling(Model model,
507                                        IStochasticProcess usageProfile,
508                                        String testContextName)
509    {
510        final Component testContext = fetchTestContext(model, testContextName);
511        if (testContext == null) {
512            throw new RuntimeException("Could not find any test context in the model");
513        }
514
515        Map<Operation, Double> usageScoreMapUnsorted = new HashMap<>();
516
517        // first, we determine all test cases and calculate their usage scores
518        final Stereotype utpTestCase = UTPUtils.getTestCaseStereotype(model);
519        for (Operation operation : testContext.getAllOperations()) {
520            if (operation.getAppliedStereotypes().contains(utpTestCase) &&
521                !operation.getMethods().isEmpty())
522            {
523                Interaction interaction = (Interaction) operation.getMethods().get(0);
524                usageScoreMapUnsorted
525                    .put(operation, calculateUsageScore(interaction, usageProfile));
526            }
527        }
528        Map<Operation, Double> usageScoreMapSorted = sortByValue(usageScoreMapUnsorted);
529
530        // now we create the scheduling
531        Activity schedulingActivity =
532            (Activity) testContext.createOwnedBehavior("UsageBasedScheduling",
533                                                       UMLPackage.Literals.ACTIVITY);
534        testContext.setClassifierBehavior(schedulingActivity);
535
536        ActivityNode startNode =
537            schedulingActivity.createOwnedNode("final", UMLPackage.Literals.INITIAL_NODE);
538        ActivityNode finalNode =
539            schedulingActivity.createOwnedNode("final", UMLPackage.Literals.ACTIVITY_FINAL_NODE);
540
541        ActivityNode currentOperationNode = startNode;
542
543        for (Entry<Operation, Double> entry : usageScoreMapSorted.entrySet()) {
544            Operation operation = entry.getKey();
545            CallOperationAction nextOperationNode =
546                (CallOperationAction) schedulingActivity
547                    .createOwnedNode(operation.getName(), UMLPackage.Literals.CALL_OPERATION_ACTION);
548            nextOperationNode.setOperation(operation);
549
550            ActivityEdge edge =
551                schedulingActivity.createEdge(currentOperationNode.getName() + "_to_" +
552                    nextOperationNode.getName(), UMLPackage.Literals.CONTROL_FLOW);
553            edge.setSource(currentOperationNode);
554            edge.setTarget(nextOperationNode);
555
556            currentOperationNode = nextOperationNode;
557        }
558
559        ActivityEdge edge =
560            schedulingActivity
561                .createEdge(currentOperationNode.getName() + "_to_" + finalNode.getName(),
562                            UMLPackage.Literals.CONTROL_FLOW);
563        edge.setSource(currentOperationNode);
564        edge.setTarget(finalNode);
565    }
566
567    /**
568     * <p>
569     * Fetches an operation using only its name from a list of operations. Returns the first match
570     * found or null if no match is found.
571     * </p>
572     *
573     * @param operations
574     *            list of operations
575     * @param name
576     *            name of the operation
577     * @return first matching operation; null if no match is found
578     */
579    public static Operation getOperationFromName(EList<Operation> operations, String name) {
580        if (name == null) {
581            throw new IllegalArgumentException("name of the operation must not be null");
582        }
583        if (operations != null) {
584            for (Operation operation : operations) {
585                if (operation.getName() != null && operation.getName().equals(name)) {
586                    return operation;
587                }
588            }
589        }
590        return null;
591    }
592
593    /**
594     * <p>
595     * Determines which transitions match a given {@link SOAPEventType}.
596     * </p>
597     *
598     * @param transitions
599     *            the transitions
600     * @param eventType
601     *            the SOAP event
602     * @return matching transitions
603     */
604    private static List<Transition> matchTransitions(List<Transition> transitions, Event event) {
605        String eventService = SOAPUtils.getServiceNameFromEvent(event);
606        String eventMethod = SOAPUtils.getCalledMethodFromEvent(event);
607
608        Map<Interface, String> interfaceServiceMap =
609            createInterfaceServiceMap(transitions.get(0).getModel());
610
611        List<Transition> matching = new LinkedList<>();
612        for (Transition transition : transitions) {
613            EList<Trigger> triggers = transition.getTriggers();
614            if (triggers.size() == 1) {
615                if (triggers.get(0).getEvent() instanceof CallEvent) {
616                    CallEvent callEvent = (CallEvent) triggers.get(0).getEvent();
617                    String transitionMethod = callEvent.getOperation().getName();
618                    String transitionService =
619                        interfaceServiceMap.get(callEvent.getOperation().getInterface());
620
621                    if (eventMethod.equals(transitionMethod) &&
622                        eventService.equals(transitionService))
623                    {
624                        matching.add(transition);
625                    }
626                }
627            }
628            else {
629                throw new RuntimeException(
630                                           "only one trigger of type CallEvent per transition allowed: " +
631                                               transition.getName());
632            }
633
634        }
635        return matching;
636    }
637
638    /**
639     * <p>
640     * Fetches all realized interfaces from the type of a UML {@link Property} (i.e.,
641     * property.getType()). If no interfaces are realized, an empty set is returned.
642     * </p>
643     *
644     * @param property
645     *            property, of the whose realized interfaces of the type are determined
646     * @return realized interfaces
647     */
648    public static Set<Interface> getRealizedInterfacesFromProperty(Property property) {
649        return getRealizedInterfaceFromComponent((Component) property.getType());
650    }
651
652    /**
653     * <p>
654     * Fetches all realized interfaces from a UML {@link Component}. If no interfaces are realized,
655     * an empty set is returned.
656     * </p>
657     *
658     * @param comp
659     *            component whose realized interfaces are determined
660     * @return realized interfaces
661     */
662    public static Set<Interface> getRealizedInterfaceFromComponent(Component component) {
663        Set<Interface> interfaces = new HashSet<>();
664        // Interface myInterface = null;
665        for (Property property : component.getAllAttributes()) {
666            if (property instanceof Port) {
667                Port port = (Port) property;
668                if (!port.isConjugated()) {
669                    interfaces.addAll(port.getProvideds());
670                }
671            }
672        }
673        return interfaces;
674    }
675
676    /**
677     * <p>
678     * Determines searches within a {@link Package} for a component to which the UTP TestContext
679     * stereotype is applied.
680     * <ul>
681     * <li>If no testContextName is provided, the first test context found is returned.</li>
682     * <li>In case no test context is found, null is returned.</li>
683     * </p>
684     *
685     * @param pkg
686     *            package where the test context is being locked for
687     * @param testContextName
688     *            name of the test context; in case no test name is specified, use null and not the
689     *            empty String.
690     * @return {@link Component} to which the TestContext stereotype is applied
691     */
692    public static Component fetchTestContext(final Package pkg, final String testContextName) {
693        List<Component> testContexts = fetchAllTestContexts(pkg);
694        if (testContexts.isEmpty()) {
695            return null;
696        }
697        if (testContextName != null) {
698            for (Component testContext : testContexts) {
699                if (testContextName.equals(testContext.getName())) {
700                    return testContext;
701                }
702            }
703            return null;
704        }
705        else {
706            return testContexts.get(0);
707        }
708    }
709
710    /**
711     * <p>
712     * Retrieves all UML {@link Component}s to which the UTP TestContext stereotype is applied from
713     * a package. This method calls itself recursively to include all components contained in
714     * sub-packages.
715     * </p>
716     * <p>
717     * In case no test context is found, an empty list is returned.
718     * </p>
719     *
720     * @param pkg
721     *            package from which the test contexts are retrieved
722     * @return {@link List} of test contexts
723     */
724    public static List<Component> fetchAllTestContexts(final Package pkg) {
725        final Stereotype utpTestContext = UTPUtils.getTestContextStereotype(pkg.getModel());
726        final List<Component> testContexts = new LinkedList<>();
727        for (Element element : pkg.getOwnedElements()) {
728            if (element instanceof Package) {
729                testContexts.addAll(fetchAllTestContexts((Package) element));
730            }
731            if (element instanceof Component &&
732                element.getAppliedStereotypes().contains(utpTestContext))
733            {
734                testContexts.add((Component) element);
735            }
736        }
737        return testContexts;
738    }
739
740    /**
741     * <p>
742     * Retrieves all properties that represent a UTP TestComponent from a test context.
743     * </p>
744     *
745     * @param testContext
746     *            test context from which the properties are retrieved
747     * @return properties that represent test components
748     */
749    public static Set<Property> fetchAllTestComponentProperties(final Component testContext) {
750        // fetch all SUTs and TestComponents
751        final Stereotype utpTestComponent =
752            UTPUtils.getTestComponentStereotype(testContext.getModel());
753        final Set<Property> properties = new HashSet<>();
754        for (Property property : testContext.getAllAttributes()) {
755            if (property.getType().getAppliedStereotypes().contains(utpTestComponent)) {
756                properties.add(property);
757            }
758        }
759        return properties;
760    }
761
762    /**
763     * <p>
764     * Retrieves all properties that represent a UTP SUT from a test context.
765     * </p>
766     *
767     * @param testContext
768     *            test context from which the properties are retrieved
769     * @return properties that represent the SUTs
770     */
771    public static Set<Property> fetchAllSUTProperties(final Component testContext) {
772        // fetch all SUTs and TestComponents
773        final Stereotype utpSUT = UTPUtils.getSUTStereotype(testContext.getModel());
774        final Set<Property> properties = new HashSet<>();
775        for (Property property : testContext.getAllAttributes()) {
776            if (property.getAppliedStereotypes().contains(utpSUT)) {
777                properties.add(property);
778            }
779        }
780        return properties;
781    }
782
783    /**
784     * <p>
785     * Infers connector between two lifelines.
786     * </p>
787     *
788     * @param msgSourceLifeline
789     *            source lifeline of the message
790     * @param targetAttributes
791     *            target lifeline of the message
792     */
793    public static Connector inferConnector(Lifeline msgSourceLifeline,
794                                            Lifeline msgTargetLifeline,
795                                            Interface targetInterface)
796    {
797        EList<Property> userAttributes =
798            ((Component) msgSourceLifeline.getRepresents().getType()).getAllAttributes();
799        EList<Property> targetAttributes =
800            ((Component) msgTargetLifeline.getRepresents().getType()).getAllAttributes();
801        for (Property userAttribute : userAttributes) {
802            if (userAttribute instanceof Port) {
803                EList<ConnectorEnd> userEnds = ((Port) userAttribute).getEnds();
804                for (ConnectorEnd userEnd : userEnds) {
805                    Connector userConnector = (Connector) userEnd.eContainer();
806                    for (Property targetAttribute : targetAttributes) {
807                        if (targetAttribute instanceof Port) {
808                            if (((Port) targetAttribute).getProvideds().contains(targetInterface)) {
809                                EList<ConnectorEnd> targetEnds = ((Port) targetAttribute).getEnds();
810                                for (ConnectorEnd targetEnd : targetEnds) {
811                                    Connector targetConnector = (Connector) targetEnd.eContainer();
812                                    if (targetConnector == userConnector) {
813                                        return targetConnector;
814                                    }
815                                }
816                            }
817                        }
818                    }
819                }
820            }
821        }
822        return null;
823    }
824
825    /**
826     * <p>
827     * Creates a map that maps the interfaces to the properties, i.e., services that they are
828     * represented by.
829     * </p>
830     * <p>
831     * TODO: currently assumes that each interfaces is only realized by one property
832     * </p>
833     *
834     * @param model
835     *            model for which the interface->service map is created
836     * @return the map
837     */
838    private static Map<Interface, String> createInterfaceServiceMap(Model model) {
839        Map<Interface, String> interfaceServiceMap = new HashMap<>();
840        List<Component> testContexts = fetchAllTestContexts(model.getModel());
841        for (Component testContext : testContexts) {
842            for (Property property : fetchAllSUTProperties(testContext)) {
843                for (Interface intface : getRealizedInterfacesFromProperty(property)) {
844                    interfaceServiceMap.put(intface, property.getName());
845                }
846            }
847            for (Property property : fetchAllTestComponentProperties(testContext)) {
848                for (Interface intface : getRealizedInterfacesFromProperty(property)) {
849                    interfaceServiceMap.put(intface, property.getName());
850                }
851            }
852        }
853        return interfaceServiceMap;
854    }
855
856   
857
858    /**
859     * <p>
860     * Creates an operand that defines a {@link PrimitiveType}.
861     * </p>
862     * <p>
863     * TODO: Currently does nothing in case of multiplicity 0. I am not sure if, in that case, one
864     * has to define LiteralNull instead.
865     * </p>
866     *
867     * @param param
868     *            parameter for which the operand is created
869     * @param argument
870     *            argument to which the operand is added
871     * @param currentNode
872     *            DOM node from which is value for the operand is inferred
873     * @param path
874     *            used for warnings and debug information
875     */
876   
877    @SuppressWarnings("unused")
878    @Deprecated
879    private static void createOperandPrimitiveType(Parameter param,
880                                                   Expression argument,
881                                                   org.w3c.dom.Element currentNode,
882                                                   String path)
883    {
884        List<String> attributeValues = SOAPUtils.getValuesFromElement(param.getName(), currentNode);
885
886        if (attributeValues.isEmpty()) {
887            if (param.getLower() == 0) {
888                // ignoring optional attribute
889                return;
890            }
891            else {
892                if (currentNode != null) {
893                    Console.traceln(Level.WARNING,
894                                    "required attribute not found in SOAP message: " + path + "." +
895                                        param.getName());
896                    Console.traceln(Level.WARNING, "setting default values for this attribute");
897                    Console.traceln(Level.FINE, "XML structure of path:" + StringTools.ENDLINE +
898                        SOAPUtils.getSerialization(currentNode));
899                }
900                attributeValues.add(null);
901            }
902        }
903        for (String attributeValue : attributeValues) {
904            if ("String".equals(param.getType().getName())) {
905                LiteralString spec =
906                    (LiteralString) argument.createOperand(param.getName(), null,
907                                                           UMLPackage.Literals.LITERAL_STRING);
908                if (attributeValue != null) {
909                    spec.setValue(attributeValue);
910                }
911                else {
912                    spec.setValue("foobar");
913                }
914            }
915            else if ("Integer".equals(param.getType().getName())) {
916                LiteralInteger spec =
917                    (LiteralInteger) argument.createOperand(param.getName(), null,
918                                                            UMLPackage.Literals.LITERAL_INTEGER);
919                if (attributeValue != null) {
920                    spec.setValue(Integer.parseInt(attributeValue));
921                }
922                else {
923                    spec.setValue(42);
924                }
925            }
926            else if ("Boolean".equals(param.getType().getName())) {
927                LiteralBoolean spec =
928                    (LiteralBoolean) argument.createOperand(param.getName(), null,
929                                                            UMLPackage.Literals.LITERAL_BOOLEAN);
930                if (attributeValue != null) {
931                    spec.setValue(Boolean.parseBoolean(attributeValue));
932                }
933                else {
934                    spec.setValue(true);
935                }
936            }
937            else if ("Real".equals(param.getType().getName())) {
938                LiteralReal spec =
939                    (LiteralReal) argument.createOperand(param.getName(), null,
940                                                         UMLPackage.Literals.LITERAL_REAL);
941                if (attributeValue != null) {
942                    spec.setValue(Double.parseDouble(attributeValue));
943                }
944                else {
945                    spec.setValue(3.14);
946                }
947            }
948        }
949    }
950
951   
952
953    /**
954     * <p>
955     * Checks if a parameter has the direction IN or INOUT
956     * </p>
957     *
958     * @param parameter
959     *            parameter that is checked
960     * @return true if the direction is IN or INOUT; false otherwise
961     */
962    public static boolean isInParameter(Parameter parameter) {
963        return parameter.getDirection() == ParameterDirectionKind.IN_LITERAL ||
964            parameter.getDirection() == ParameterDirectionKind.INOUT_LITERAL;
965    }
966
967    /**
968     * <p>
969     * Checks if a parameter has the direction RETURN, OUT or INOUT
970     * </p>
971     *
972     * @param parameter
973     *            parameter that is checked
974     * @return true if the direction is RETURN, OUT, or INOUT; false otherwise
975     */
976    public static boolean isOutParameter(Parameter parameter) {
977        return parameter.getDirection() == ParameterDirectionKind.RETURN_LITERAL ||
978            parameter.getDirection() == ParameterDirectionKind.OUT_LITERAL ||
979            parameter.getDirection() == ParameterDirectionKind.INOUT_LITERAL;
980    }
981
982    /**
983     * <p>
984     * Checks if the {@link MessageSort} of a message is a call message, i.e., ASYNCH_CALL or
985     * SYNCH_CALL.
986     * </p>
987     *
988     * @param message
989     *            message that is checked
990     * @return true if the message is a call message; false otherwise
991     */
992    public static boolean isCallMessage(Message message) {
993        if (message == null) {
994            return false;
995        }
996        MessageSort msgSort = message.getMessageSort();
997        return msgSort == MessageSort.ASYNCH_CALL_LITERAL ||
998            msgSort == MessageSort.SYNCH_CALL_LITERAL;
999    }
1000
1001    /**
1002     * <p>
1003     * inverse-sorts the values of a map. Has been adapted from <a href=
1004     * "http://stackoverflow.com/questions/109383/how-to-sort-a-mapkey-value-on-the-values-in-java"
1005     * >this</a> StackOverflow post.
1006     * </p>
1007     *
1008     * @param map
1009     *            map whose values are sorted
1010     * @return sorted version of the map
1011     */
1012    private static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
1013        List<Map.Entry<K, V>> list = new LinkedList<>(map.entrySet());
1014        Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
1015            @Override
1016            public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
1017                return -1 * (o1.getValue()).compareTo(o2.getValue());
1018            }
1019        });
1020
1021        Map<K, V> result = new LinkedHashMap<>();
1022        for (Map.Entry<K, V> entry : list) {
1023            result.put(entry.getKey(), entry.getValue());
1024        }
1025        return result;
1026    }
1027}
Note: See TracBrowser for help on using the repository browser.