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

Last change on this file since 2215 was 2215, checked in by pharms, 7 years ago
  • java doc issues removal
  • 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 sequences
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 component
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     * </p>
692     * <ul>
693     * <li>If no testContextName is provided, the first test context found is returned.</li>
694     * <li>In case no test context is found, null is returned.</li>
695     * </ul>
696     *
697     * @param pkg
698     *            package where the test context is being locked for
699     * @param testContextName
700     *            name of the test context; in case no test name is specified, use null and not the
701     *            empty String.
702     * @return {@link Component} to which the TestContext stereotype is applied
703     */
704    public static Component fetchTestContext(final Package pkg, final String testContextName) {
705        List<Component> testContexts = fetchAllTestContexts(pkg);
706        if (testContexts.isEmpty()) {
707            return null;
708        }
709        if (testContextName != null) {
710            for (Component testContext : testContexts) {
711                if (testContextName.equals(testContext.getName())) {
712                    return testContext;
713                }
714            }
715            return null;
716        }
717        else {
718            return testContexts.get(0);
719        }
720    }
721
722    /**
723     * <p>
724     * Retrieves all UML {@link Component}s to which the UTP TestContext stereotype is applied from
725     * a package. This method calls itself recursively to include all components contained in
726     * sub-packages.
727     * </p>
728     * <p>
729     * In case no test context is found, an empty list is returned.
730     * </p>
731     *
732     * @param pkg
733     *            package from which the test contexts are retrieved
734     * @return {@link List} of test contexts
735     */
736    public static List<Component> fetchAllTestContexts(final Package pkg) {
737        final Stereotype utpTestContext = UTPUtils.getTestContextStereotype(pkg.getModel());
738        final List<Component> testContexts = new LinkedList<>();
739        for (Element element : pkg.getOwnedElements()) {
740            if (element instanceof Package) {
741                testContexts.addAll(fetchAllTestContexts((Package) element));
742            }
743            if (element instanceof Component &&
744                element.getAppliedStereotypes().contains(utpTestContext))
745            {
746                testContexts.add((Component) element);
747            }
748        }
749        return testContexts;
750    }
751
752    /**
753     * <p>
754     * Retrieves all properties that represent a UTP TestComponent from a test context.
755     * </p>
756     *
757     * @param testContext
758     *            test context from which the properties are retrieved
759     * @return properties that represent test components
760     */
761    public static Set<Property> fetchAllTestComponentProperties(final Component testContext) {
762        // fetch all SUTs and TestComponents
763        final Stereotype utpTestComponent =
764            UTPUtils.getTestComponentStereotype(testContext.getModel());
765        final Set<Property> properties = new HashSet<>();
766        for (Property property : testContext.getAllAttributes()) {
767            // TODO once all models are update the first condition should be removed
768            if (property.getType().getAppliedStereotypes().contains(utpTestComponent) ||
769                property.getType().getApplicableStereotypes().contains(utpTestComponent)) {
770                properties.add(property);
771            }
772        }
773        return properties;
774    }
775
776    /**
777     * <p>
778     * Retrieves all properties that represent a UTP SUT from a test context.
779     * </p>
780     *
781     * @param testContext
782     *            test context from which the properties are retrieved
783     * @return properties that represent the SUTs
784     */
785    public static Set<Property> fetchAllSUTProperties(final Component testContext) {
786        // fetch all SUTs and TestComponents
787        final Stereotype utpSUT = UTPUtils.getSUTStereotype(testContext.getModel());
788        final Set<Property> properties = new HashSet<>();
789        for (Property property : testContext.getAllAttributes()) {
790            if (property.getAppliedStereotypes().contains(utpSUT)) {
791                properties.add(property);
792            }
793        }
794        return properties;
795    }
796
797    /**
798     * <p>
799     * Infers connector between two lifelines.
800     * </p>
801     *
802     * @param msgSourceLifeline
803     *            source lifeline of the message
804     * @param msgTargetLifeline
805     *            target lifeline of the message
806     */
807    public static Connector inferConnector(Lifeline msgSourceLifeline,
808                                            Lifeline msgTargetLifeline,
809                                            Interface targetInterface)
810    {
811        EList<Property> userAttributes =
812            ((Component) msgSourceLifeline.getRepresents().getType()).getAllAttributes();
813        EList<Property> targetAttributes =
814            ((Component) msgTargetLifeline.getRepresents().getType()).getAllAttributes();
815        for (Property userAttribute : userAttributes) {
816            if (userAttribute instanceof Port) {
817                EList<ConnectorEnd> userEnds = ((Port) userAttribute).getEnds();
818                for (ConnectorEnd userEnd : userEnds) {
819                    Connector userConnector = (Connector) userEnd.eContainer();
820                    for (Property targetAttribute : targetAttributes) {
821                        if (targetAttribute instanceof Port) {
822                            if (((Port) targetAttribute).getProvideds().contains(targetInterface)) {
823                                EList<ConnectorEnd> targetEnds = ((Port) targetAttribute).getEnds();
824                                for (ConnectorEnd targetEnd : targetEnds) {
825                                    Connector targetConnector = (Connector) targetEnd.eContainer();
826                                    if (targetConnector == userConnector) {
827                                        return targetConnector;
828                                    }
829                                }
830                            }
831                        }
832                    }
833                }
834            }
835        }
836        return null;
837    }
838
839    /**
840     * <p>
841     * Creates a map that maps the interfaces to the properties, i.e., services that they are
842     * represented by.
843     * </p>
844     * <p>
845     * TODO: currently assumes that each interfaces is only realized by one property
846     * </p>
847     *
848     * @param model
849     *            model for which the interface->service map is created
850     * @return the map
851     */
852    private static Map<Interface, String> createInterfaceServiceMap(Model model) {
853        Map<Interface, String> interfaceServiceMap = new HashMap<>();
854        List<Component> testContexts = fetchAllTestContexts(model.getModel());
855        for (Component testContext : testContexts) {
856            for (Property property : fetchAllSUTProperties(testContext)) {
857                for (Interface intface : getRealizedInterfacesFromProperty(property)) {
858                    interfaceServiceMap.put(intface, property.getName());
859                }
860            }
861            for (Property property : fetchAllTestComponentProperties(testContext)) {
862                for (Interface intface : getRealizedInterfacesFromProperty(property)) {
863                    interfaceServiceMap.put(intface, property.getName());
864                }
865            }
866        }
867        return interfaceServiceMap;
868    }
869
870   
871
872    /**
873     * <p>
874     * Creates an operand that defines a {@link PrimitiveType}.
875     * </p>
876     * <p>
877     * TODO: Currently does nothing in case of multiplicity 0. I am not sure if, in that case, one
878     * has to define LiteralNull instead.
879     * </p>
880     *
881     * @param param
882     *            parameter for which the operand is created
883     * @param argument
884     *            argument to which the operand is added
885     * @param currentNode
886     *            DOM node from which is value for the operand is inferred
887     * @param path
888     *            used for warnings and debug information
889     */
890   
891    @SuppressWarnings("unused")
892    @Deprecated
893    private static void createOperandPrimitiveType(Parameter param,
894                                                   Expression argument,
895                                                   org.w3c.dom.Element currentNode,
896                                                   String path)
897    {
898        List<String> attributeValues = SOAPUtils.getValuesFromElement(param.getName(), currentNode);
899
900        if (attributeValues.isEmpty()) {
901            if (param.getLower() == 0) {
902                // ignoring optional attribute
903                return;
904            }
905            else {
906                if (currentNode != null) {
907                    Console.traceln(Level.WARNING,
908                                    "required attribute not found in SOAP message: " + path + "." +
909                                        param.getName());
910                    Console.traceln(Level.WARNING, "setting default values for this attribute");
911                    Console.traceln(Level.FINE, "XML structure of path:" + StringTools.ENDLINE +
912                        SOAPUtils.getSerialization(currentNode));
913                }
914                attributeValues.add(null);
915            }
916        }
917        for (String attributeValue : attributeValues) {
918            if ("String".equals(param.getType().getName())) {
919                LiteralString spec =
920                    (LiteralString) argument.createOperand(param.getName(), null,
921                                                           UMLPackage.Literals.LITERAL_STRING);
922                if (attributeValue != null) {
923                    spec.setValue(attributeValue);
924                }
925                else {
926                    spec.setValue("foobar");
927                }
928            }
929            else if ("Integer".equals(param.getType().getName())) {
930                LiteralInteger spec =
931                    (LiteralInteger) argument.createOperand(param.getName(), null,
932                                                            UMLPackage.Literals.LITERAL_INTEGER);
933                if (attributeValue != null) {
934                    spec.setValue(Integer.parseInt(attributeValue));
935                }
936                else {
937                    spec.setValue(42);
938                }
939            }
940            else if ("Boolean".equals(param.getType().getName())) {
941                LiteralBoolean spec =
942                    (LiteralBoolean) argument.createOperand(param.getName(), null,
943                                                            UMLPackage.Literals.LITERAL_BOOLEAN);
944                if (attributeValue != null) {
945                    spec.setValue(Boolean.parseBoolean(attributeValue));
946                }
947                else {
948                    spec.setValue(true);
949                }
950            }
951            else if ("Real".equals(param.getType().getName())) {
952                LiteralReal spec =
953                    (LiteralReal) argument.createOperand(param.getName(), null,
954                                                         UMLPackage.Literals.LITERAL_REAL);
955                if (attributeValue != null) {
956                    spec.setValue(Double.parseDouble(attributeValue));
957                }
958                else {
959                    spec.setValue(3.14);
960                }
961            }
962        }
963    }
964
965   
966
967    /**
968     * <p>
969     * Checks if a parameter has the direction IN or INOUT
970     * </p>
971     *
972     * @param parameter
973     *            parameter that is checked
974     * @return true if the direction is IN or INOUT; false otherwise
975     */
976    public static boolean isInParameter(Parameter parameter) {
977        return parameter.getDirection() == ParameterDirectionKind.IN_LITERAL ||
978            parameter.getDirection() == ParameterDirectionKind.INOUT_LITERAL;
979    }
980
981    /**
982     * <p>
983     * Checks if a parameter has the direction RETURN, OUT or INOUT
984     * </p>
985     *
986     * @param parameter
987     *            parameter that is checked
988     * @return true if the direction is RETURN, OUT, or INOUT; false otherwise
989     */
990    public static boolean isOutParameter(Parameter parameter) {
991        return parameter.getDirection() == ParameterDirectionKind.RETURN_LITERAL ||
992            parameter.getDirection() == ParameterDirectionKind.OUT_LITERAL ||
993            parameter.getDirection() == ParameterDirectionKind.INOUT_LITERAL;
994    }
995
996    /**
997     * <p>
998     * Checks if the {@link MessageSort} of a message is a call message, i.e., ASYNCH_CALL or
999     * SYNCH_CALL.
1000     * </p>
1001     *
1002     * @param message
1003     *            message that is checked
1004     * @return true if the message is a call message; false otherwise
1005     */
1006    public static boolean isCallMessage(Message message) {
1007        if (message == null) {
1008            return false;
1009        }
1010        MessageSort msgSort = message.getMessageSort();
1011        return msgSort == MessageSort.ASYNCH_CALL_LITERAL ||
1012            msgSort == MessageSort.SYNCH_CALL_LITERAL;
1013    }
1014
1015    /**
1016     * <p>
1017     * inverse-sorts the values of a map. Has been adapted from <a href=
1018     * "http://stackoverflow.com/questions/109383/how-to-sort-a-mapkey-value-on-the-values-in-java"
1019     * >this</a> StackOverflow post.
1020     * </p>
1021     *
1022     * @param map
1023     *            map whose values are sorted
1024     * @return sorted version of the map
1025     */
1026    private static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
1027        List<Map.Entry<K, V>> list = new LinkedList<>(map.entrySet());
1028        Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
1029            @Override
1030            public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
1031                return -1 * (o1.getValue()).compareTo(o2.getValue());
1032            }
1033        });
1034
1035        Map<K, V> result = new LinkedHashMap<>();
1036        for (Map.Entry<K, V> entry : list) {
1037            result.put(entry.getKey(), entry.getValue());
1038        }
1039        return result;
1040    }
1041}
Note: See TracBrowser for help on using the repository browser.