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

Last change on this file since 2039 was 2039, checked in by sherbold, 9 years ago
  • minor improvement to the feedback in case of failures
  • Property svn:mime-type set to text/plain
File size: 43.6 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 List<Interaction> createInteractionFromEventSequence(Collection<List<Event>> sequences,
426                                                                 Model model,
427                                                                 String interactionName,
428                                                                 String testContextName,
429                                                                 boolean useRandomMsgBodies)
430    {
431        List<Interaction> interactions = new LinkedList<>();
432        UMLInteractionCreator interactionCreator = new UMLInteractionCreator(model, testContextName, useRandomMsgBodies);
433        int i=0;
434        for( List<Event> sequence : sequences ) {
435            try {
436                interactions.add(interactionCreator.createInteraction(sequence, interactionName+"_"+i));
437                i++;
438            } catch(Exception e) {
439                Console.traceln(Level.SEVERE, "failure for " + interactionName+"_"+i + ": " + e.getMessage());
440                Console.logException(e);
441            }
442        }
443        return interactions;
444    }
445
446    /**
447     * <p>
448     * Calculates the usage score of an interaction as the logsum of the event probabilities
449     * multiplied with the length of the interaction.
450     * </p>
451     *
452     * @param interaction
453     *            interaction for which the score is calculated
454     * @param usageProfile
455     *            usage profile used for the calculation
456     * @return calculated usage score
457     */
458    public static double calculateUsageScore(Interaction interaction,
459                                             IStochasticProcess usageProfile)
460    {
461        double usageScore = 0.0d;
462        EList<InteractionFragment> interactionFragments = interaction.getFragments();
463        List<Event> eventSequence = new LinkedList<>();
464        eventSequence.add(Event.STARTEVENT);
465        for (InteractionFragment interactionFragment : interactionFragments) {
466            if (interactionFragment instanceof MessageOccurrenceSpecification) {
467                Message message =
468                    ((MessageOccurrenceSpecification) interactionFragment).getMessage();
469                // if (message.getReceiveEvent().equals(interactionFragment) &&
470                // isCallMessage(message))
471                if (message.getReceiveEvent().equals(interactionFragment)) {
472                    String clientName;
473                    String serviceName;
474                    String methodName = message.getSignature().getName();
475                    CallType callType;
476                    if (isCallMessage(message)) {
477                        clientName =
478                            ((MessageOccurrenceSpecification) message.getSendEvent()).getCovereds()
479                                .get(0).getName();
480                        serviceName =
481                            ((MessageOccurrenceSpecification) message.getReceiveEvent())
482                                .getCovereds().get(0).getName();
483                        callType = CallType.REQUEST;
484                    }
485                    else {
486                        clientName =
487                            ((MessageOccurrenceSpecification) message.getReceiveEvent())
488                                .getCovereds().get(0).getName();
489                        serviceName =
490                            ((MessageOccurrenceSpecification) message.getSendEvent()).getCovereds()
491                                .get(0).getName();
492                        callType = CallType.RESPONSE;
493                    }
494                    eventSequence.add(new Event(new SimpleSOAPEventType(methodName, serviceName,
495                                                                        clientName, null, null,
496                                                                        callType)));
497                }
498            }
499        }
500        eventSequence.add(Event.ENDEVENT);
501        double prob = usageProfile.getLogSum(eventSequence);
502        usageScore = eventSequence.size() * prob;
503
504        return usageScore;
505    }
506
507    /**
508     * <p>
509     * Extends the given model with an activity for usage-based scheduling of the test cases.
510     * </p>
511     *
512     * @param model
513     *            model to be extended
514     * @param usageProfile
515     *            usage profile used as foundation
516     */
517    public static void createScheduling(Model model,
518                                        IStochasticProcess usageProfile,
519                                        String testContextName)
520    {
521        final Component testContext = fetchTestContext(model, testContextName);
522        if (testContext == null) {
523            throw new RuntimeException("Could not find any test context in the model");
524        }
525
526        Map<Operation, Double> usageScoreMapUnsorted = new HashMap<>();
527
528        // first, we determine all test cases and calculate their usage scores
529        final Stereotype utpTestCase = UTPUtils.getTestCaseStereotype(model);
530        for (Operation operation : testContext.getAllOperations()) {
531            if (operation.getAppliedStereotypes().contains(utpTestCase) &&
532                !operation.getMethods().isEmpty())
533            {
534                Interaction interaction = (Interaction) operation.getMethods().get(0);
535                usageScoreMapUnsorted
536                    .put(operation, calculateUsageScore(interaction, usageProfile));
537            }
538        }
539        Map<Operation, Double> usageScoreMapSorted = sortByValue(usageScoreMapUnsorted);
540
541        // now we create the scheduling
542        Activity schedulingActivity =
543            (Activity) testContext.createOwnedBehavior("UsageBasedScheduling",
544                                                       UMLPackage.Literals.ACTIVITY);
545        testContext.setClassifierBehavior(schedulingActivity);
546
547        ActivityNode startNode =
548            schedulingActivity.createOwnedNode("final", UMLPackage.Literals.INITIAL_NODE);
549        ActivityNode finalNode =
550            schedulingActivity.createOwnedNode("final", UMLPackage.Literals.ACTIVITY_FINAL_NODE);
551
552        ActivityNode currentOperationNode = startNode;
553
554        for (Entry<Operation, Double> entry : usageScoreMapSorted.entrySet()) {
555            Operation operation = entry.getKey();
556            CallOperationAction nextOperationNode =
557                (CallOperationAction) schedulingActivity
558                    .createOwnedNode(operation.getName(), UMLPackage.Literals.CALL_OPERATION_ACTION);
559            nextOperationNode.setOperation(operation);
560
561            ActivityEdge edge =
562                schedulingActivity.createEdge(currentOperationNode.getName() + "_to_" +
563                    nextOperationNode.getName(), UMLPackage.Literals.CONTROL_FLOW);
564            edge.setSource(currentOperationNode);
565            edge.setTarget(nextOperationNode);
566
567            currentOperationNode = nextOperationNode;
568        }
569
570        ActivityEdge edge =
571            schedulingActivity
572                .createEdge(currentOperationNode.getName() + "_to_" + finalNode.getName(),
573                            UMLPackage.Literals.CONTROL_FLOW);
574        edge.setSource(currentOperationNode);
575        edge.setTarget(finalNode);
576    }
577
578    /**
579     * <p>
580     * Fetches an operation using only its name from a list of operations. Returns the first match
581     * found or null if no match is found.
582     * </p>
583     *
584     * @param operations
585     *            list of operations
586     * @param name
587     *            name of the operation
588     * @return first matching operation; null if no match is found
589     */
590    public static Operation getOperationFromName(EList<Operation> operations, String name) {
591        if (name == null) {
592            throw new IllegalArgumentException("name of the operation must not be null");
593        }
594        if (operations != null) {
595            for (Operation operation : operations) {
596                if (operation.getName() != null && operation.getName().equals(name)) {
597                    return operation;
598                }
599            }
600        }
601        return null;
602    }
603
604    /**
605     * <p>
606     * Determines which transitions match a given {@link SOAPEventType}.
607     * </p>
608     *
609     * @param transitions
610     *            the transitions
611     * @param eventType
612     *            the SOAP event
613     * @return matching transitions
614     */
615    private static List<Transition> matchTransitions(List<Transition> transitions, Event event) {
616        String eventService = SOAPUtils.getServiceNameFromEvent(event);
617        String eventMethod = SOAPUtils.getCalledMethodFromEvent(event);
618
619        Map<Interface, String> interfaceServiceMap =
620            createInterfaceServiceMap(transitions.get(0).getModel());
621
622        List<Transition> matching = new LinkedList<>();
623        for (Transition transition : transitions) {
624            EList<Trigger> triggers = transition.getTriggers();
625            if (triggers.size() == 1) {
626                if (triggers.get(0).getEvent() instanceof CallEvent) {
627                    CallEvent callEvent = (CallEvent) triggers.get(0).getEvent();
628                    String transitionMethod = callEvent.getOperation().getName();
629                    String transitionService =
630                        interfaceServiceMap.get(callEvent.getOperation().getInterface());
631
632                    if (eventMethod.equals(transitionMethod) &&
633                        eventService.equals(transitionService))
634                    {
635                        matching.add(transition);
636                    }
637                }
638            }
639            else {
640                throw new RuntimeException(
641                                           "only one trigger of type CallEvent per transition allowed: " +
642                                               transition.getName());
643            }
644
645        }
646        return matching;
647    }
648
649    /**
650     * <p>
651     * Fetches all realized interfaces from the type of a UML {@link Property} (i.e.,
652     * property.getType()). If no interfaces are realized, an empty set is returned.
653     * </p>
654     *
655     * @param property
656     *            property, of the whose realized interfaces of the type are determined
657     * @return realized interfaces
658     */
659    public static Set<Interface> getRealizedInterfacesFromProperty(Property property) {
660        return getRealizedInterfaceFromComponent((Component) property.getType());
661    }
662
663    /**
664     * <p>
665     * Fetches all realized interfaces from a UML {@link Component}. If no interfaces are realized,
666     * an empty set is returned.
667     * </p>
668     *
669     * @param comp
670     *            component whose realized interfaces are determined
671     * @return realized interfaces
672     */
673    public static Set<Interface> getRealizedInterfaceFromComponent(Component component) {
674        Set<Interface> interfaces = new HashSet<>();
675        // Interface myInterface = null;
676        for (Property property : component.getAllAttributes()) {
677            if (property instanceof Port) {
678                Port port = (Port) property;
679                if (!port.isConjugated()) {
680                    interfaces.addAll(port.getProvideds());
681                }
682            }
683        }
684        return interfaces;
685    }
686
687    /**
688     * <p>
689     * Determines searches within a {@link Package} for a component to which the UTP TestContext
690     * stereotype is applied.
691     * <ul>
692     * <li>If no testContextName is provided, the first test context found is returned.</li>
693     * <li>In case no test context is found, null is returned.</li>
694     * </p>
695     *
696     * @param pkg
697     *            package where the test context is being locked for
698     * @param testContextName
699     *            name of the test context; in case no test name is specified, use null and not the
700     *            empty String.
701     * @return {@link Component} to which the TestContext stereotype is applied
702     */
703    public static Component fetchTestContext(final Package pkg, final String testContextName) {
704        List<Component> testContexts = fetchAllTestContexts(pkg);
705        if (testContexts.isEmpty()) {
706            return null;
707        }
708        if (testContextName != null) {
709            for (Component testContext : testContexts) {
710                if (testContextName.equals(testContext.getName())) {
711                    return testContext;
712                }
713            }
714            return null;
715        }
716        else {
717            return testContexts.get(0);
718        }
719    }
720
721    /**
722     * <p>
723     * Retrieves all UML {@link Component}s to which the UTP TestContext stereotype is applied from
724     * a package. This method calls itself recursively to include all components contained in
725     * sub-packages.
726     * </p>
727     * <p>
728     * In case no test context is found, an empty list is returned.
729     * </p>
730     *
731     * @param pkg
732     *            package from which the test contexts are retrieved
733     * @return {@link List} of test contexts
734     */
735    public static List<Component> fetchAllTestContexts(final Package pkg) {
736        final Stereotype utpTestContext = UTPUtils.getTestContextStereotype(pkg.getModel());
737        final List<Component> testContexts = new LinkedList<>();
738        for (Element element : pkg.getOwnedElements()) {
739            if (element instanceof Package) {
740                testContexts.addAll(fetchAllTestContexts((Package) element));
741            }
742            if (element instanceof Component &&
743                element.getAppliedStereotypes().contains(utpTestContext))
744            {
745                testContexts.add((Component) element);
746            }
747        }
748        return testContexts;
749    }
750
751    /**
752     * <p>
753     * Retrieves all properties that represent a UTP TestComponent from a test context.
754     * </p>
755     *
756     * @param testContext
757     *            test context from which the properties are retrieved
758     * @return properties that represent test components
759     */
760    public static Set<Property> fetchAllTestComponentProperties(final Component testContext) {
761        // fetch all SUTs and TestComponents
762        final Stereotype utpTestComponent =
763            UTPUtils.getTestComponentStereotype(testContext.getModel());
764        final Set<Property> properties = new HashSet<>();
765        for (Property property : testContext.getAllAttributes()) {
766            // TODO once all models are update the first condition should be removed
767            if (property.getType().getAppliedStereotypes().contains(utpTestComponent) ||
768                property.getType().getApplicableStereotypes().contains(utpTestComponent)) {
769                properties.add(property);
770            }
771        }
772        return properties;
773    }
774
775    /**
776     * <p>
777     * Retrieves all properties that represent a UTP SUT from a test context.
778     * </p>
779     *
780     * @param testContext
781     *            test context from which the properties are retrieved
782     * @return properties that represent the SUTs
783     */
784    public static Set<Property> fetchAllSUTProperties(final Component testContext) {
785        // fetch all SUTs and TestComponents
786        final Stereotype utpSUT = UTPUtils.getSUTStereotype(testContext.getModel());
787        final Set<Property> properties = new HashSet<>();
788        for (Property property : testContext.getAllAttributes()) {
789            if (property.getAppliedStereotypes().contains(utpSUT)) {
790                properties.add(property);
791            }
792        }
793        return properties;
794    }
795
796    /**
797     * <p>
798     * Infers connector between two lifelines.
799     * </p>
800     *
801     * @param msgSourceLifeline
802     *            source lifeline of the message
803     * @param targetAttributes
804     *            target lifeline of the message
805     */
806    public static Connector inferConnector(Lifeline msgSourceLifeline,
807                                            Lifeline msgTargetLifeline,
808                                            Interface targetInterface)
809    {
810        EList<Property> userAttributes =
811            ((Component) msgSourceLifeline.getRepresents().getType()).getAllAttributes();
812        EList<Property> targetAttributes =
813            ((Component) msgTargetLifeline.getRepresents().getType()).getAllAttributes();
814        for (Property userAttribute : userAttributes) {
815            if (userAttribute instanceof Port) {
816                EList<ConnectorEnd> userEnds = ((Port) userAttribute).getEnds();
817                for (ConnectorEnd userEnd : userEnds) {
818                    Connector userConnector = (Connector) userEnd.eContainer();
819                    for (Property targetAttribute : targetAttributes) {
820                        if (targetAttribute instanceof Port) {
821                            if (((Port) targetAttribute).getProvideds().contains(targetInterface)) {
822                                EList<ConnectorEnd> targetEnds = ((Port) targetAttribute).getEnds();
823                                for (ConnectorEnd targetEnd : targetEnds) {
824                                    Connector targetConnector = (Connector) targetEnd.eContainer();
825                                    if (targetConnector == userConnector) {
826                                        return targetConnector;
827                                    }
828                                }
829                            }
830                        }
831                    }
832                }
833            }
834        }
835        return null;
836    }
837
838    /**
839     * <p>
840     * Creates a map that maps the interfaces to the properties, i.e., services that they are
841     * represented by.
842     * </p>
843     * <p>
844     * TODO: currently assumes that each interfaces is only realized by one property
845     * </p>
846     *
847     * @param model
848     *            model for which the interface->service map is created
849     * @return the map
850     */
851    private static Map<Interface, String> createInterfaceServiceMap(Model model) {
852        Map<Interface, String> interfaceServiceMap = new HashMap<>();
853        List<Component> testContexts = fetchAllTestContexts(model.getModel());
854        for (Component testContext : testContexts) {
855            for (Property property : fetchAllSUTProperties(testContext)) {
856                for (Interface intface : getRealizedInterfacesFromProperty(property)) {
857                    interfaceServiceMap.put(intface, property.getName());
858                }
859            }
860            for (Property property : fetchAllTestComponentProperties(testContext)) {
861                for (Interface intface : getRealizedInterfacesFromProperty(property)) {
862                    interfaceServiceMap.put(intface, property.getName());
863                }
864            }
865        }
866        return interfaceServiceMap;
867    }
868
869   
870
871    /**
872     * <p>
873     * Creates an operand that defines a {@link PrimitiveType}.
874     * </p>
875     * <p>
876     * TODO: Currently does nothing in case of multiplicity 0. I am not sure if, in that case, one
877     * has to define LiteralNull instead.
878     * </p>
879     *
880     * @param param
881     *            parameter for which the operand is created
882     * @param argument
883     *            argument to which the operand is added
884     * @param currentNode
885     *            DOM node from which is value for the operand is inferred
886     * @param path
887     *            used for warnings and debug information
888     */
889   
890    @SuppressWarnings("unused")
891    @Deprecated
892    private static void createOperandPrimitiveType(Parameter param,
893                                                   Expression argument,
894                                                   org.w3c.dom.Element currentNode,
895                                                   String path)
896    {
897        List<String> attributeValues = SOAPUtils.getValuesFromElement(param.getName(), currentNode);
898
899        if (attributeValues.isEmpty()) {
900            if (param.getLower() == 0) {
901                // ignoring optional attribute
902                return;
903            }
904            else {
905                if (currentNode != null) {
906                    Console.traceln(Level.WARNING,
907                                    "required attribute not found in SOAP message: " + path + "." +
908                                        param.getName());
909                    Console.traceln(Level.WARNING, "setting default values for this attribute");
910                    Console.traceln(Level.FINE, "XML structure of path:" + StringTools.ENDLINE +
911                        SOAPUtils.getSerialization(currentNode));
912                }
913                attributeValues.add(null);
914            }
915        }
916        for (String attributeValue : attributeValues) {
917            if ("String".equals(param.getType().getName())) {
918                LiteralString spec =
919                    (LiteralString) argument.createOperand(param.getName(), null,
920                                                           UMLPackage.Literals.LITERAL_STRING);
921                if (attributeValue != null) {
922                    spec.setValue(attributeValue);
923                }
924                else {
925                    spec.setValue("foobar");
926                }
927            }
928            else if ("Integer".equals(param.getType().getName())) {
929                LiteralInteger spec =
930                    (LiteralInteger) argument.createOperand(param.getName(), null,
931                                                            UMLPackage.Literals.LITERAL_INTEGER);
932                if (attributeValue != null) {
933                    spec.setValue(Integer.parseInt(attributeValue));
934                }
935                else {
936                    spec.setValue(42);
937                }
938            }
939            else if ("Boolean".equals(param.getType().getName())) {
940                LiteralBoolean spec =
941                    (LiteralBoolean) argument.createOperand(param.getName(), null,
942                                                            UMLPackage.Literals.LITERAL_BOOLEAN);
943                if (attributeValue != null) {
944                    spec.setValue(Boolean.parseBoolean(attributeValue));
945                }
946                else {
947                    spec.setValue(true);
948                }
949            }
950            else if ("Real".equals(param.getType().getName())) {
951                LiteralReal spec =
952                    (LiteralReal) argument.createOperand(param.getName(), null,
953                                                         UMLPackage.Literals.LITERAL_REAL);
954                if (attributeValue != null) {
955                    spec.setValue(Double.parseDouble(attributeValue));
956                }
957                else {
958                    spec.setValue(3.14);
959                }
960            }
961        }
962    }
963
964   
965
966    /**
967     * <p>
968     * Checks if a parameter has the direction IN or INOUT
969     * </p>
970     *
971     * @param parameter
972     *            parameter that is checked
973     * @return true if the direction is IN or INOUT; false otherwise
974     */
975    public static boolean isInParameter(Parameter parameter) {
976        return parameter.getDirection() == ParameterDirectionKind.IN_LITERAL ||
977            parameter.getDirection() == ParameterDirectionKind.INOUT_LITERAL;
978    }
979
980    /**
981     * <p>
982     * Checks if a parameter has the direction RETURN, OUT or INOUT
983     * </p>
984     *
985     * @param parameter
986     *            parameter that is checked
987     * @return true if the direction is RETURN, OUT, or INOUT; false otherwise
988     */
989    public static boolean isOutParameter(Parameter parameter) {
990        return parameter.getDirection() == ParameterDirectionKind.RETURN_LITERAL ||
991            parameter.getDirection() == ParameterDirectionKind.OUT_LITERAL ||
992            parameter.getDirection() == ParameterDirectionKind.INOUT_LITERAL;
993    }
994
995    /**
996     * <p>
997     * Checks if the {@link MessageSort} of a message is a call message, i.e., ASYNCH_CALL or
998     * SYNCH_CALL.
999     * </p>
1000     *
1001     * @param message
1002     *            message that is checked
1003     * @return true if the message is a call message; false otherwise
1004     */
1005    public static boolean isCallMessage(Message message) {
1006        if (message == null) {
1007            return false;
1008        }
1009        MessageSort msgSort = message.getMessageSort();
1010        return msgSort == MessageSort.ASYNCH_CALL_LITERAL ||
1011            msgSort == MessageSort.SYNCH_CALL_LITERAL;
1012    }
1013
1014    /**
1015     * <p>
1016     * inverse-sorts the values of a map. Has been adapted from <a href=
1017     * "http://stackoverflow.com/questions/109383/how-to-sort-a-mapkey-value-on-the-values-in-java"
1018     * >this</a> StackOverflow post.
1019     * </p>
1020     *
1021     * @param map
1022     *            map whose values are sorted
1023     * @return sorted version of the map
1024     */
1025    private static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
1026        List<Map.Entry<K, V>> list = new LinkedList<>(map.entrySet());
1027        Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
1028            @Override
1029            public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
1030                return -1 * (o1.getValue()).compareTo(o2.getValue());
1031            }
1032        });
1033
1034        Map<K, V> result = new LinkedHashMap<>();
1035        for (Map.Entry<K, V> entry : list) {
1036            result.put(entry.getKey(), entry.getValue());
1037        }
1038        return result;
1039    }
1040}
Note: See TracBrowser for help on using the repository browser.