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

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