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

Last change on this file since 1988 was 1988, checked in by sherbold, 9 years ago
  • updated EqualSOAPDataMap: not a singleton anymore, now each event has a reference to the map. the map is created when the SOAPEvents are converted to SimpleSOAPEvents using the appropriate method from the SOAPUtils
  • the logic for deciding if random SOAP requests are selected or the one of the event itself was changed within the SimpleSOAPEvents. Now methods for both are offered and the caller has to decide which method to choose
  • the test case generation in the model utils now has a parameter to decide if random data or the data of the SimpleSOAPEvents is used
  • Property svn:mime-type set to text/plain
File size: 70.3 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.logging.Level;
29import java.util.Set;
30import java.util.TreeSet;
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.CallConcurrencyKind;
37import org.eclipse.uml2.uml.CallEvent;
38import org.eclipse.uml2.uml.CallOperationAction;
39import org.eclipse.uml2.uml.Comment;
40import org.eclipse.uml2.uml.Component;
41import org.eclipse.uml2.uml.Connector;
42import org.eclipse.uml2.uml.ConnectorEnd;
43import org.eclipse.uml2.uml.DataType;
44import org.eclipse.uml2.uml.Element;
45import org.eclipse.uml2.uml.Expression;
46import org.eclipse.uml2.uml.InstanceSpecification;
47import org.eclipse.uml2.uml.InstanceValue;
48import org.eclipse.uml2.uml.Interaction;
49import org.eclipse.uml2.uml.InteractionFragment;
50import org.eclipse.uml2.uml.Interface;
51import org.eclipse.uml2.uml.Lifeline;
52import org.eclipse.uml2.uml.LiteralBoolean;
53import org.eclipse.uml2.uml.LiteralInteger;
54import org.eclipse.uml2.uml.LiteralReal;
55import org.eclipse.uml2.uml.LiteralString;
56import org.eclipse.uml2.uml.Message;
57import org.eclipse.uml2.uml.MessageOccurrenceSpecification;
58import org.eclipse.uml2.uml.MessageSort;
59import org.eclipse.uml2.uml.Model;
60import org.eclipse.uml2.uml.Operation;
61import org.eclipse.uml2.uml.Package;
62import org.eclipse.uml2.uml.Parameter;
63import org.eclipse.uml2.uml.ParameterDirectionKind;
64import org.eclipse.uml2.uml.Port;
65import org.eclipse.uml2.uml.PrimitiveType;
66import org.eclipse.uml2.uml.Property;
67import org.eclipse.uml2.uml.Region;
68import org.eclipse.uml2.uml.Slot;
69import org.eclipse.uml2.uml.StateMachine;
70import org.eclipse.uml2.uml.Stereotype;
71import org.eclipse.uml2.uml.Transition;
72import org.eclipse.uml2.uml.Trigger;
73import org.eclipse.uml2.uml.UMLPackage;
74import org.eclipse.uml2.uml.ValueSpecification;
75import org.eclipse.uml2.uml.Vertex;
76
77import de.ugoe.cs.autoquest.eventcore.Event;
78import de.ugoe.cs.autoquest.plugin.http.SOAPUtils;
79import de.ugoe.cs.autoquest.plugin.http.eventcore.SOAPEventType;
80import de.ugoe.cs.autoquest.plugin.http.eventcore.SimpleSOAPEventType;
81import de.ugoe.cs.autoquest.plugin.uml.eventcore.UMLTransitionType;
82import de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess;
83import de.ugoe.cs.util.StringTools;
84import de.ugoe.cs.util.console.Console;
85
86/**
87 * <p>
88 * Utilities for working with UML.
89 * </p>
90 *
91 * @author Steffen Herbold
92 */
93public class UMLUtils {
94
95    /**
96     * In case a multiplicity is defined as *, this value defines the highest one that can be picked
97     */
98    final static int MAX_MULTIPLICITY = 10;
99
100    /**
101     * <p>
102     * Method for checking if the information in a usage journal can be mapped to the SUT model. In
103     * case this is not possible, the violations are reported.
104     * </p>
105     *
106     * @param sequences
107     *            sequences of the usage journal
108     * @param model
109     *            SUT model that is validated
110     * @param testContextName
111     *            name of the test context to be used; if null, the first test context found is used
112     * @return number of violations
113     */
114    public static int validateModelWithLog(Collection<List<Event>> sequences,
115                                           Model model,
116                                           String testContextName)
117    {
118        int violationCount = 0;
119        Component testContext = fetchTestContext(model, testContextName);
120        if (testContext == null) {
121            violationCount++;
122            if (testContextName == null) {
123                Console.traceln(Level.SEVERE, "Could not find any TestContext in the model.");
124
125            }
126            else {
127                Console.traceln(Level.SEVERE, "Could not find TestContext in the model: " +
128                    testContextName);
129            }
130            Console
131                .traceln(Level.SEVERE,
132                         "Hint: Check if you have applied the TestContext stereotype correctly in the model.");
133            Console.traceln(Level.SEVERE, "Aborting");
134            return violationCount;
135        }
136
137        // Create list of unique methods calls
138        HashMap<String, Set<String>> calledMethods = new HashMap<>();
139        for (List<Event> sequence : sequences) {
140            for (Event event : sequence) {
141                String serviceName = SOAPUtils.getServiceNameFromEvent(event);
142                String calledMethod = SOAPUtils.getCalledMethodFromEvent(event);
143                if (serviceName != null) {
144                    Set<String> curCalledMethods = calledMethods.get(serviceName);
145                    if (curCalledMethods == null) {
146                        curCalledMethods = new TreeSet<>();
147                        calledMethods.put(serviceName, curCalledMethods);
148                    }
149                    curCalledMethods.add(calledMethod);
150                }
151            }
152        }
153
154        Console.traceln(Level.INFO,
155                        "Found the following services and operations in the usage data: ");
156        for (Entry<String, Set<String>> entry : calledMethods.entrySet()) {
157            Console.traceln(Level.INFO, "\tService \"" + entry.getKey() + "\": ");
158            for (String method : entry.getValue()) {
159                Console.traceln(Level.INFO, "\t\t" + method);
160            }
161        }
162
163        // fetch all SUTs and TestComponents
164        HashMap<String, Property> properties = new HashMap<>();
165        for (Property property : fetchAllSUTProperties(testContext)) {
166            properties.put(property.getName(), property);
167        }
168        for (Property property : fetchAllTestComponentProperties(testContext)) {
169            properties.put(property.getName(), property);
170        }
171        Console.traceln(Level.INFO, "Found the following services in the TestConfiguration:");
172        for (Entry<String, Property> entry : properties.entrySet()) {
173            Console.traceln(Level.INFO, "\t" + entry.getKey());
174        }
175
176        for (Entry<String, Set<String>> entry : calledMethods.entrySet()) {
177            String serviceName = entry.getKey();
178            Console.traceln(Level.INFO, "Checking service: " + serviceName);
179            Set<String> methodNames = entry.getValue();
180            Property property = properties.get(serviceName);
181            if (property == null) {
182                violationCount++;
183                Console.traceln(Level.SEVERE, "\tCould not find property for service: " +
184                    serviceName);
185                Console
186                    .traceln(Level.SEVERE,
187                             "\tHint: Check service name map and/or model if the service is present and spelled correctly.");
188                Console
189                    .traceln(Level.SEVERE,
190                             "\tHint: Check if the SUT/TestComponent stereotype has been applied correctly in this TestContext.");
191            }
192            else {
193                Set<Interface> interfaces = getRealizedInterfacesFromProperty(property);
194                if (interfaces.isEmpty()) {
195                    violationCount++;
196                    Console
197                        .traceln(Level.SEVERE,
198                                 "\tCould not find any interfaces implementing the property for service: " +
199                                     serviceName);
200                    Console
201                        .traceln(Level.SEVERE,
202                                 "\tHint: Check if the property correctly realizes the interfaces in the model.");
203                }
204                else {
205                    Console.traceln(Level.INFO,
206                                    "\tFound the following realized interfaces for the service \"" +
207                                        serviceName + "\": ");
208                    for (Interface intface : interfaces) {
209                        Console.traceln(Level.INFO, "\t" + intface.getName());
210                        for (Operation operation : intface.getAllOperations()) {
211                            Console.traceln(Level.INFO, "\t\t" + operation.getName());
212                        }
213                    }
214                    for (String methodName : methodNames) {
215                        boolean methodFound = false;
216                        for (Interface intface : interfaces) {
217                            if (getOperationFromName(intface.getOperations(), methodName) != null) {
218                                // interface found
219                                Console.traceln(Level.INFO, "\tMethod " + methodName +
220                                    " found in interface " + intface.getName());
221                                methodFound = true;
222                            }
223                        }
224                        if (!methodFound) {
225                            violationCount++;
226                            Console.traceln(Level.SEVERE, "\tCould not find operation: " +
227                                methodName);
228                        }
229                    }
230                }
231            }
232        }
233        return violationCount;
234    }
235
236    /**
237     * <p>
238     * Creates a sequence of events with {@link UMLTransitionType} as event type from a given
239     * sequence of events with the {@link SOAPEventType} or {@link SimpleSOAPEventType}, by matching
240     * the sequences to a state machine.
241     * </p>
242     *
243     * @param sequence
244     *            SOAP sequences
245     * @param stateMachine
246     *            the state machine
247     * @return create UML sequences
248     */
249    public static List<Event> createUMLTransitionSequence(List<Event> sequence,
250                                                          StateMachine stateMachine)
251    {
252        List<List<Transition>> matchingSequences =
253            determineMatchingTransitionSequences(sequence, stateMachine);
254
255        if (matchingSequences.size() != 1) {
256            throw new RuntimeException("no unique match found; " + matchingSequences.size() +
257                " matches");
258        }
259        List<Event> umlEventSequence = new LinkedList<>();
260        for (Transition transition : matchingSequences.get(0)) {
261            umlEventSequence.add(new Event(new UMLTransitionType(transition)));
262        }
263        return umlEventSequence;
264    }
265
266    /**
267     * <p>
268     * Uses a sequences of events with the {@link UMLTransitionType} to determine the transition
269     * probabilities for the state machine.
270     * </p>
271     *
272     * @param sequences
273     *            UML sequences
274     * @param stateMachine
275     *            state machine to be converted to a usage profile
276     */
277    public static void convertStateMachineToUsageProfile(Collection<List<Event>> sequences,
278                                                         StateMachine stateMachine)
279    {
280        // create state->outgoings hashmap
281        Map<Vertex, Map<Transition, Integer>> stateMap = new HashMap<>();
282        for (Region region : stateMachine.getRegions()) {
283            for (Vertex state : region.getSubvertices()) {
284                stateMap.put(state, new HashMap<Transition, Integer>());
285            }
286        }
287
288        // create counters for each transition
289        for (List<Event> sequence : sequences) {
290            for (Event event : sequence) {
291                if (event.getType() instanceof UMLTransitionType) {
292                    Transition transition = ((UMLTransitionType) event.getType()).getTransition();
293                    Map<Transition, Integer> transitionMap = stateMap.get(transition.getSource());
294                    Integer value = transitionMap.get(transition);
295                    if (value == null) {
296                        value = 0;
297                    }
298                    transitionMap.put(transition, value + 1);
299                }
300                else {
301                    throw new RuntimeException(
302                                               "Wrong event type. Only UMLTransitionType supported but was: " +
303                                                   event.getType().getClass().getName());
304                }
305            }
306        }
307
308        // calculate probabilities
309        for (Region region : stateMachine.getRegions()) {
310            for (Vertex state : region.getSubvertices()) {
311                Map<Transition, Integer> transitionMap = stateMap.get(state);
312                int totalCount = 0;
313                for (Entry<Transition, Integer> entry : transitionMap.entrySet()) {
314                    totalCount += entry.getValue();
315                }
316                if (totalCount != 0) {
317                    for (Transition transition : state.getOutgoings()) {
318                        double prob = 0.0d;
319                        if (transitionMap.containsKey(transition)) {
320                            prob = ((double) transitionMap.get(transition)) / totalCount;
321                        }
322                        Comment comment = transition.createOwnedComment();
323                        comment.setBody("" + prob);
324                    }
325                }
326                else {
327                    // system has never been in this state, all transitions equally likely
328                    int numOutgoings = state.getOutgoings().size();
329                    for (Transition transition : state.getOutgoings()) {
330                        Comment comment = transition.createOwnedComment();
331                        comment.setBody("" + (1.0d / numOutgoings));
332                    }
333                }
334            }
335        }
336    }
337
338    /**
339     * <p>
340     * Determines all matching {@link Transition} sequences in a state machine for a given sequence
341     * of SOAP events.
342     * </p>
343     *
344     * @param sequence
345     *            SOAP sequence
346     * @param stateMachine
347     *            the state machine
348     * @return all matching {@link Transition} sequences
349     */
350    public static List<List<Transition>> determineMatchingTransitionSequences(List<Event> sequence,
351                                                                              StateMachine stateMachine)
352    {
353        EList<Region> regions = stateMachine.getRegions();
354        EList<Vertex> states = null;
355        for (Region region : regions) {
356            if (states == null) {
357                states = region.getSubvertices();
358            }
359            else {
360                states.addAll(region.getSubvertices());
361            }
362        }
363        List<Transition> allTransitions = new LinkedList<>();
364        for (Vertex state : states) {
365            allTransitions.addAll(state.getOutgoings());
366        }
367
368        List<List<Transition>> matchingSequences = null;
369        List<Transition> currentTransitions = null;
370
371        // first, we try to find a single unique transition that we can match using the method name
372        for (Iterator<Event> eventIterator = sequence.iterator(); eventIterator.hasNext();) {
373            Event event = eventIterator.next();
374            if (matchingSequences == null) {
375                matchingSequences = new LinkedList<>();
376                List<Transition> initialMatches = matchTransitions(allTransitions, event);
377                for (Transition transition : initialMatches) {
378                    List<Transition> candidate = new LinkedList<>();
379                    candidate.add(transition);
380                    matchingSequences.add(candidate);
381                }
382                currentTransitions = initialMatches;
383            }
384            else {
385                List<List<Transition>> nextMatchingSequences = new LinkedList<>();
386                List<Transition> nextCurrentTransitions = new LinkedList<>();
387                Iterator<Transition> currentTransitionIterator = currentTransitions.iterator();
388                Iterator<List<Transition>> currentMatchingSequencesIterator =
389                    matchingSequences.iterator();
390                while (currentTransitionIterator.hasNext()) {
391                    Transition currentTransition = currentTransitionIterator.next();
392                    List<Transition> currentMatch = currentMatchingSequencesIterator.next();
393
394                    List<Transition> matches =
395                        matchTransitions(currentTransition.getTarget().getOutgoings(), event);
396                    if (matches.isEmpty()) {
397                        throw new RuntimeException("no matches found");
398                    }
399                    for (Transition matchingTransition : matches) {
400                        List<Transition> candidate = new LinkedList<>(currentMatch);
401                        candidate.add(matchingTransition);
402                        nextMatchingSequences.add(candidate);
403                        nextCurrentTransitions.add(matchingTransition);
404                    }
405                }
406                matchingSequences = nextMatchingSequences;
407                currentTransitions = nextCurrentTransitions;
408            }
409        }
410        return matchingSequences;
411    }
412
413    /**
414     * <p>
415     * Extends a given model with an interaction that represents an observed sequence.
416     * </p>
417     *
418     * @param sequence
419     *            sequence that is added as sequence diagram
420     * @param model
421     *            UML model to which the interaction is added
422     * @param interactionName
423     *            name of the interaction
424     * @param testContextName
425     *            Name of the test context that should be used. If this value is null, the first
426     *            test context found is used.
427     * @param useRandomRequestBodies
428     *            defines is random request bodies are used or the body of the associated event
429     */
430    public static Interaction createInteractionFromEventSequence(List<Event> sequence,
431                                                                 Model model,
432                                                                 String interactionName,
433                                                                 String testContextName,
434                                                                 boolean useRandomRequestBodies)
435    {
436        final Component testContext = fetchTestContext(model, testContextName);
437        if (testContext == null) {
438            throw new RuntimeException("Could not find any test context in the model");
439        }
440
441        final Operation operation = testContext.createOwnedOperation(interactionName, null, null);
442        operation.applyStereotype(UTPUtils.getTestCaseStereotype(model));
443
444        final Interaction interaction =
445            (Interaction) testContext.createPackagedElement(interactionName + "_Impl",
446                                                            UMLPackage.Literals.INTERACTION);
447        operation.getMethods().add(interaction);
448
449        // create lifelines
450        Lifeline userLifeline = null;
451
452        for (Property property : fetchAllSUTProperties(testContext)) {
453            String serviceName = property.getName();
454            Lifeline targetLifeline = interaction.createLifeline(serviceName);
455            targetLifeline.setRepresents(property);
456        }
457        for (Property property : fetchAllTestComponentProperties(testContext)) {
458            // TODO check if this is still required
459            /*if (userLifeline != null) {
460                throw new RuntimeException(
461                                           "TestContext must only have one TestComponent for the application of usage-based testing.");
462            }*/
463            userLifeline = interaction.createLifeline(property.getName());
464            userLifeline.setRepresents(property);
465        }
466
467        if (userLifeline == null) {
468            throw new RuntimeException("No TestComponent found, could not create user lifeline.");
469        }
470        if (interaction.getLifelines().size() < 2) {
471            throw new RuntimeException("Fewer than two lifelines created. No SUT found.");
472        }
473
474        int i = 0;
475        for (Event event : sequence) {
476            if (!(event.equals(Event.STARTEVENT) || event.equals(Event.ENDEVENT))) {
477                String serviceName = SOAPUtils.getServiceNameFromEvent(event);
478                String methodName = SOAPUtils.getCalledMethodFromEvent(event);
479                String clientName = SOAPUtils.getClientNameFromEvent(event);
480                String prefix = interactionName + "_" + i + "_" + methodName + "_";
481                // determine lifelines
482                Lifeline msgTargetLifeline;
483                Lifeline msgSourceLifeline;
484
485                msgSourceLifeline = interaction.getLifeline(clientName);
486                msgTargetLifeline = interaction.getLifeline(serviceName);
487
488                if (msgSourceLifeline == null) {
489                    throw new RuntimeException(
490                                               "Error creating message: could not determine source lifeline for component: " +
491                                                   clientName);
492                }
493                if (msgTargetLifeline == null) {
494                    throw new RuntimeException(
495                                               "Error creating message: could not determine target lifeline for component: " +
496                                                   serviceName);
497                }
498                // determine correct target interface
499                Set<Interface> targetInterfaces =
500                    getRealizedInterfacesFromProperty((Property) msgTargetLifeline.getRepresents());
501                if (targetInterfaces.isEmpty()) {
502                    throw new RuntimeException("no interface associated with the property " +
503                        msgTargetLifeline.getRepresents().getName());
504                }
505                Interface targetInterface = null;
506                for (Interface intface : targetInterfaces) {
507                    if (getOperationFromName(intface.getOperations(), methodName) != null) {
508                        // interface found
509                        targetInterface = intface;
510                        break;
511                    }
512                }
513                if (targetInterface == null) {
514                    StringBuilder errStrBuilder = new StringBuilder();
515                    errStrBuilder
516                        .append("Error creating message: operation not found in the implementing interfaces (");
517                    Iterator<Interface> iter = targetInterfaces.iterator();
518                    while (iter.hasNext()) {
519                        String interfaceName = iter.next().getName();
520                        errStrBuilder.append(interfaceName);
521                        if (iter.hasNext()) {
522                            errStrBuilder.append(",");
523                        }
524                        else {
525                            errStrBuilder.append("): " + methodName);
526                        }
527                    }
528                    throw new RuntimeException(errStrBuilder.toString());
529                }
530
531                Operation calledOperation =
532                    getOperationFromName(targetInterface.getOperations(), methodName);
533
534                // setup for both SYNCH and ASYNCH calls
535                MessageOccurrenceSpecification callSendFragment =
536                    (MessageOccurrenceSpecification) interaction.createFragment(prefix +
537                        "callSendFragment", UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
538                MessageOccurrenceSpecification callRecvFragment =
539                    (MessageOccurrenceSpecification) interaction.createFragment(prefix +
540                        "callRecvFragment", UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
541
542                callSendFragment.setCovered(msgSourceLifeline);
543                callRecvFragment.setCovered(msgTargetLifeline);
544
545                // get connector
546                Connector connector = inferConnector(msgSourceLifeline, msgTargetLifeline);
547
548                // create call
549                Message callMessage = interaction.createMessage(prefix + "call");
550                callMessage.setSignature(calledOperation);
551                setCallMessageParameters(callMessage, calledOperation, event, useRandomRequestBodies, prefix);
552                callMessage.setConnector(connector);
553                callMessage.setSendEvent(callSendFragment);
554                callMessage.setReceiveEvent(callRecvFragment);
555                callSendFragment.setMessage(callMessage);
556                callRecvFragment.setMessage(callMessage);
557
558                boolean asynch = false;
559                if (calledOperation.getConcurrency() == CallConcurrencyKind.CONCURRENT_LITERAL) {
560                    asynch = true;
561                }
562                if (asynch) {
563                    // Create ASYNCH call
564                    callMessage.setMessageSort(MessageSort.ASYNCH_CALL_LITERAL);
565                }
566                else {
567                    // SYNCH call
568                    callMessage.setMessageSort(MessageSort.SYNCH_CALL_LITERAL);
569
570                    // setup reply and behavior execution specifications
571                    MessageOccurrenceSpecification replySendFragment =
572                        (MessageOccurrenceSpecification) interaction
573                            .createFragment(prefix + "replySendFragment",
574                                            UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
575                    MessageOccurrenceSpecification replyRecvFragment =
576                        (MessageOccurrenceSpecification) interaction
577                            .createFragment(prefix + "replyRecvFragment",
578                                            UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
579
580                    replySendFragment.setCovered(msgTargetLifeline);
581                    replyRecvFragment.setCovered(msgSourceLifeline);
582
583                    /*
584                     * BehaviorExecutionSpecification sourceBehaviorExecutionSpecification =
585                     * (BehaviorExecutionSpecification) interaction .createFragment(":" + methodName
586                     * + "_sourceBhvExecSpec",
587                     * UMLPackage.Literals.BEHAVIOR_EXECUTION_SPECIFICATION);
588                     * BehaviorExecutionSpecification targetBehaviorExecutionSpecification =
589                     * (BehaviorExecutionSpecification) interaction .createFragment(":" + methodName
590                     * + "_targetBhvExecSpec",
591                     * UMLPackage.Literals.BEHAVIOR_EXECUTION_SPECIFICATION);
592                     *
593                     * sourceBehaviorExecutionSpecification.setStart(callSendFragment);
594                     * sourceBehaviorExecutionSpecification.setFinish(replyRecvFragment);
595                     * targetBehaviorExecutionSpecification.setStart(callRecvFragment);
596                     * targetBehaviorExecutionSpecification.setFinish(replySendFragment);
597                     */
598
599                    // create reply
600                    Message replyMessage = interaction.createMessage(prefix + "_reply");
601                    replyMessage.setMessageSort(MessageSort.REPLY_LITERAL);
602                    replyMessage.setSignature(calledOperation);
603                    setReplyMessageParameters(replyMessage, calledOperation);
604                    replyMessage.setConnector(connector);
605                    replyMessage.setSendEvent(replySendFragment);
606                    replyMessage.setReceiveEvent(replyRecvFragment);
607                    replySendFragment.setMessage(replyMessage);
608                    replyRecvFragment.setMessage(replyMessage);
609                }
610
611                i++;
612            }
613        }
614        return interaction;
615    }
616
617    /**
618     * <p>
619     * Calculates the usage score of an interaction as the logsum of the event probabilities
620     * multiplied with the length of the interaction.
621     * </p>
622     *
623     * @param interaction
624     *            interaction for which the score is calculated
625     * @param usageProfile
626     *            usage profile used for the calculation
627     * @return calculated usage score
628     */
629    public static double calculateUsageScore(Interaction interaction,
630                                             IStochasticProcess usageProfile)
631    {
632        double usageScore = 0.0d;
633        EList<InteractionFragment> interactionFragments = interaction.getFragments();
634        List<Event> eventSequence = new LinkedList<>();
635        eventSequence.add(Event.STARTEVENT);
636        for (InteractionFragment interactionFragment : interactionFragments) {
637            if (interactionFragment instanceof MessageOccurrenceSpecification) {
638                Message message =
639                    ((MessageOccurrenceSpecification) interactionFragment).getMessage();
640                if (message.getReceiveEvent().equals(interactionFragment) && isCallMessage(message))
641                {
642                    String clientName =
643                        ((MessageOccurrenceSpecification) message.getSendEvent()).getCovereds()
644                            .get(0).getName();
645                    String serviceName =
646                        ((MessageOccurrenceSpecification) message.getReceiveEvent()).getCovereds()
647                            .get(0).getName();
648                    String methodName = message.getSignature().getName();
649                    eventSequence.add(new Event(new SimpleSOAPEventType(methodName, serviceName,
650                                                                        clientName, null)));
651                }
652            }
653        }
654        eventSequence.add(Event.ENDEVENT);
655        double prob = usageProfile.getLogSum(eventSequence);
656        usageScore = eventSequence.size() * prob;
657
658        return usageScore;
659    }
660
661    /**
662     * <p>
663     * Extends the given model with an activity for usage-based scheduling of the test cases.
664     * </p>
665     *
666     * @param model
667     *            model to be extended
668     * @param usageProfile
669     *            usage profile used as foundation
670     */
671    public static void createScheduling(Model model,
672                                        IStochasticProcess usageProfile,
673                                        String testContextName)
674    {
675        final Component testContext = fetchTestContext(model, testContextName);
676        if (testContext == null) {
677            throw new RuntimeException("Could not find any test context in the model");
678        }
679
680        Map<Operation, Double> usageScoreMapUnsorted = new HashMap<>();
681
682        // first, we determine all test cases and calculate their usage scores
683        final Stereotype utpTestCase = UTPUtils.getTestCaseStereotype(model);
684        for (Operation operation : testContext.getAllOperations()) {
685            if (operation.getAppliedStereotypes().contains(utpTestCase) &&
686                !operation.getMethods().isEmpty())
687            {
688                Interaction interaction = (Interaction) operation.getMethods().get(0);
689                usageScoreMapUnsorted
690                    .put(operation, calculateUsageScore(interaction, usageProfile));
691            }
692        }
693        Map<Operation, Double> usageScoreMapSorted = sortByValue(usageScoreMapUnsorted);
694
695        // now we create the scheduling
696        Activity schedulingActivity =
697            (Activity) testContext.createOwnedBehavior("UsageBasedScheduling",
698                                                       UMLPackage.Literals.ACTIVITY);
699        testContext.setClassifierBehavior(schedulingActivity);
700
701        ActivityNode startNode =
702            schedulingActivity.createOwnedNode("final", UMLPackage.Literals.INITIAL_NODE);
703        ActivityNode finalNode =
704            schedulingActivity.createOwnedNode("final", UMLPackage.Literals.ACTIVITY_FINAL_NODE);
705
706        ActivityNode currentOperationNode = startNode;
707
708        for (Entry<Operation, Double> entry : usageScoreMapSorted.entrySet()) {
709            Operation operation = entry.getKey();
710            CallOperationAction nextOperationNode =
711                (CallOperationAction) schedulingActivity
712                    .createOwnedNode(operation.getName(), UMLPackage.Literals.CALL_OPERATION_ACTION);
713            nextOperationNode.setOperation(operation);
714
715            ActivityEdge edge =
716                schedulingActivity.createEdge(currentOperationNode.getName() + "_to_" +
717                    nextOperationNode.getName(), UMLPackage.Literals.CONTROL_FLOW);
718            edge.setSource(currentOperationNode);
719            edge.setTarget(nextOperationNode);
720
721            currentOperationNode = nextOperationNode;
722        }
723
724        ActivityEdge edge =
725            schedulingActivity
726                .createEdge(currentOperationNode.getName() + "_to_" + finalNode.getName(),
727                            UMLPackage.Literals.CONTROL_FLOW);
728        edge.setSource(currentOperationNode);
729        edge.setTarget(finalNode);
730    }
731
732    /**
733     * <p>
734     * Fetches an operation using only its name from a list of operations. Returns the first match
735     * found or null if no match is found.
736     * </p>
737     *
738     * @param operations
739     *            list of operations
740     * @param name
741     *            name of the operation
742     * @return first matching operation; null if no match is found
743     */
744    private static Operation getOperationFromName(EList<Operation> operations, String name) {
745        if (name == null) {
746            throw new IllegalArgumentException("name of the operation must not be null");
747        }
748        if (operations != null) {
749            for (Operation operation : operations) {
750                if (operation.getName() != null && operation.getName().equals(name)) {
751                    return operation;
752                }
753            }
754        }
755        return null;
756    }
757
758    /**
759     * <p>
760     * Determines which transitions match a given {@link SOAPEventType}.
761     * </p>
762     *
763     * @param transitions
764     *            the transitions
765     * @param eventType
766     *            the SOAP event
767     * @return matching transitions
768     */
769    private static List<Transition> matchTransitions(List<Transition> transitions, Event event) {
770        String eventService = SOAPUtils.getServiceNameFromEvent(event);
771        String eventMethod = SOAPUtils.getCalledMethodFromEvent(event);
772
773        Map<Interface, String> interfaceServiceMap =
774            createInterfaceServiceMap(transitions.get(0).getModel());
775
776        List<Transition> matching = new LinkedList<>();
777        for (Transition transition : transitions) {
778            EList<Trigger> triggers = transition.getTriggers();
779            if (triggers.size() == 1) {
780                if (triggers.get(0).getEvent() instanceof CallEvent) {
781                    CallEvent callEvent = (CallEvent) triggers.get(0).getEvent();
782                    String transitionMethod = callEvent.getOperation().getName();
783                    String transitionService =
784                        interfaceServiceMap.get(callEvent.getOperation().getInterface());
785                   
786                    if (eventMethod.equals(transitionMethod) &&
787                        eventService.equals(transitionService))
788                    {
789                        matching.add(transition);
790                    }
791                }
792            } else {
793                throw new RuntimeException("only one trigger of type CallEvent per transition allowed: " + transition.getName());
794            }
795
796        }
797        return matching;
798    }
799
800    /**
801     * <p>
802     * Fetches all realized interfaces from the type of a UML {@link Property} (i.e.,
803     * property.getType()). If no interfaces are realized, an empty set is returned.
804     * </p>
805     *
806     * @param property
807     *            property, of the whose realized interfaces of the type are determined
808     * @return realized interfaces
809     */
810    private static Set<Interface> getRealizedInterfacesFromProperty(Property property) {
811        return getRealizedInterfaceFromComponent((Component) property.getType());
812    }
813
814    /**
815     * <p>
816     * Fetches all realized interfaces from a UML {@link Component}. If no interfaces are realized,
817     * an empty set is returned.
818     * </p>
819     *
820     * @param comp
821     *            component whose realized interfaces are determined
822     * @return realized interfaces
823     */
824    private static Set<Interface> getRealizedInterfaceFromComponent(Component component) {
825        Set<Interface> interfaces = new HashSet<>();
826        // Interface myInterface = null;
827        for (Property property : component.getAttributes()) {
828            if (property instanceof Port) {
829                Port port = (Port) property;
830                if (!port.isConjugated()) {
831                    interfaces.addAll(port.getProvideds());
832                }
833            }
834        }
835        return interfaces;
836    }
837
838    /**
839     * <p>
840     * Determines searches within a {@link Package} for a component to which the UTP TestContext
841     * stereotype is applied.
842     * <ul>
843     * <li>If no testContextName is provided, the first test context found is returned.</li>
844     * <li>In case no test context is found, null is returned.</li>
845     * </p>
846     *
847     * @param pkg
848     *            package where the test context is being locked for
849     * @param testContextName
850     *            name of the test context; in case no test name is specified, use null and not the
851     *            empty String.
852     * @return {@link Component} to which the TestContext stereotype is applied
853     */
854    private static Component fetchTestContext(final Package pkg, final String testContextName) {
855        List<Component> testContexts = fetchAllTestContexts(pkg);
856        if (testContexts.isEmpty()) {
857            return null;
858        }
859        if (testContextName != null) {
860            for (Component testContext : testContexts) {
861                if (testContextName.equals(testContext.getName())) {
862                    return testContext;
863                }
864            }
865            return null;
866        }
867        else {
868            return testContexts.get(0);
869        }
870    }
871
872    /**
873     * <p>
874     * Retrieves all UML {@link Component}s to which the UTP TestContext stereotype is applied from
875     * a package. This method calls itself recursively to include all components contained in
876     * sub-packages.
877     * </p>
878     * <p>
879     * In case no test context is found, an empty list is returned.
880     * </p>
881     *
882     * @param pkg
883     *            package from which the test contexts are retrieved
884     * @return {@link List} of test contexts
885     */
886    private static List<Component> fetchAllTestContexts(final Package pkg) {
887        final Stereotype utpTestContext = UTPUtils.getTestContextStereotype(pkg.getModel());
888        final List<Component> testContexts = new LinkedList<>();
889        for (Element element : pkg.getOwnedElements()) {
890            if (element instanceof Package) {
891                testContexts.addAll(fetchAllTestContexts((Package) element));
892            }
893            if (element instanceof Component &&
894                element.getAppliedStereotypes().contains(utpTestContext))
895            {
896                testContexts.add((Component) element);
897            }
898        }
899        return testContexts;
900    }
901
902    /**
903     * <p>
904     * Retrieves all properties that represent a UTP TestComponent from a test context.
905     * </p>
906     *
907     * @param testContext
908     *            test context from which the properties are retrieved
909     * @return properties that represent test components
910     */
911    private static Set<Property> fetchAllTestComponentProperties(final Component testContext) {
912        // fetch all SUTs and TestComponents
913        final Stereotype utpTestComponent =
914            UTPUtils.getTestComponentStereotype(testContext.getModel());
915        final Set<Property> properties = new HashSet<>();
916        for (Property property : testContext.getAllAttributes()) {
917            if (property.getType().getAppliedStereotypes().contains(utpTestComponent)) {
918                properties.add(property);
919            }
920        }
921        return properties;
922    }
923
924    /**
925     * <p>
926     * Retrieves all properties that represent a UTP SUT from a test context.
927     * </p>
928     *
929     * @param testContext
930     *            test context from which the properties are retrieved
931     * @return properties that represent the SUTs
932     */
933    private static Set<Property> fetchAllSUTProperties(final Component testContext) {
934        // fetch all SUTs and TestComponents
935        final Stereotype utpSUT = UTPUtils.getSUTStereotype(testContext.getModel());
936        final Set<Property> properties = new HashSet<>();
937        for (Property property : testContext.getAllAttributes()) {
938            if (property.getAppliedStereotypes().contains(utpSUT)) {
939                properties.add(property);
940            }
941        }
942        return properties;
943    }
944
945    /**
946     * <p>
947     * Infers connector between two lifelines.
948     * </p>
949     * <p>
950     * TODO: Currently assumes only one connector between two lifelines possible. This assumption is
951     * invalid as soon as there are two ports that connect the same two properties.
952     * </p>
953     *
954     * @param msgSourceLifeline
955     *            source lifeline of the message
956     * @param targetAttributes
957     *            target lifeline of the message
958     */
959    private static Connector inferConnector(Lifeline msgSourceLifeline, Lifeline msgTargetLifeline)
960    {
961        EList<Property> userAttributes =
962            ((Component) msgSourceLifeline.getRepresents().getType()).getAttributes();
963        EList<Property> targetAttributes =
964            ((Component) msgTargetLifeline.getRepresents().getType()).getAttributes();
965        for (Property userAttribute : userAttributes) {
966            if (userAttribute instanceof Port) {
967                EList<ConnectorEnd> userEnds = ((Port) userAttribute).getEnds();
968                for (ConnectorEnd userEnd : userEnds) {
969                    Connector userConnector = (Connector) userEnd.eContainer();
970                    for (Property targetAttribute : targetAttributes) {
971                        if (targetAttribute instanceof Port) {
972                            EList<ConnectorEnd> targetEnds = ((Port) targetAttribute).getEnds();
973                            for (ConnectorEnd targetEnd : targetEnds) {
974                                Connector targetConnector = (Connector) targetEnd.eContainer();
975                                if (targetConnector == userConnector) {
976                                    return targetConnector;
977                                }
978                            }
979                        }
980                    }
981                }
982            }
983        }
984        return null;
985    }
986
987    /**
988     * <p>
989     * Creates a map that maps the interfaces to the properties, i.e., services that they are
990     * represented by.
991     * </p>
992     * <p>
993     * TODO: currently assumes that each interfaces is only realized by one property
994     * </p>
995     *
996     * @param model
997     *            model for which the interface->service map is created
998     * @return the map
999     */
1000    private static Map<Interface, String> createInterfaceServiceMap(Model model) {
1001        Map<Interface, String> interfaceServiceMap = new HashMap<>();
1002        List<Component> testContexts = fetchAllTestContexts(model.getModel());
1003        for (Component testContext : testContexts) {
1004            for (Property property : fetchAllSUTProperties(testContext)) {
1005                for (Interface intface : getRealizedInterfacesFromProperty(property)) {
1006                    interfaceServiceMap.put(intface, property.getName());
1007                }
1008            }
1009            for (Property property : fetchAllTestComponentProperties(testContext)) {
1010                for (Interface intface : getRealizedInterfacesFromProperty(property)) {
1011                    interfaceServiceMap.put(intface, property.getName());
1012                }
1013            }
1014        }
1015        return interfaceServiceMap;
1016    }
1017
1018    /**
1019     * <p>
1020     * Sets values for the parameters of a call message. The values are, if possible, inferred from
1021     * the event that is provided.
1022     * </p>
1023     *
1024     * @param callMessage
1025     *            call message for which the parameters are set
1026     * @param calledOperation
1027     *            operation that is called by the message
1028     * @param event
1029     *            event that provides the parameters; in case of null, default values are assumed
1030     * @param useRandomRequestBodies
1031     *            defines is random request bodies are used or the body of the associated event
1032     * @param prefix
1033     *            prefix of the call message; used to create good warnings and debugging information
1034     */
1035    private static void setCallMessageParameters(Message callMessage,
1036                                                 Operation calledOperation,
1037                                                 Event event,
1038                                                 boolean useRandomRequestBodies,
1039                                                 String prefix)
1040    {
1041        org.w3c.dom.Element requestBody = SOAPUtils.getSoapRequestBodyFromEvent(event, useRandomRequestBodies);
1042        Package instSpecPkg = null;
1043
1044        // FOR DEBUGGING
1045//        Console.traceln(Level.FINE, "XML structure of path:" + StringTools.ENDLINE +
1046//                        SOAPUtils.getSerialization(requestBody));
1047//        for( Parameter param : calledOperation.getOwnedParameters() ) {
1048//            System.out.println(param.getName());
1049//            if( param.getType() instanceof DataType ) {
1050//                for( Property prop1 : ((DataType) param.getType()).getAllAttributes() ) {
1051//                    System.out.println("  " + prop1.getName());
1052//                    if( prop1.getType() instanceof DataType ) {
1053//                        for( Property prop2 : ((DataType) prop1.getType()).getAllAttributes() ) {
1054//                            System.out.println("    " + prop2.getName());
1055//                            if( prop2.getType() instanceof DataType ) {
1056//                                for( Property prop3 : ((DataType) prop2.getType()).getAllAttributes() ) {
1057//                                    System.out.println("      " + prop3.getName());
1058//                                    if( prop3.getType() instanceof DataType ) {
1059//                                        for( Property prop4 : ((DataType) prop3.getType()).getAllAttributes() ) {
1060//                                            System.out.println("        " + prop4.getName());
1061//                                        }
1062//                                    }
1063//                                }
1064//                            }
1065//                        }
1066//                    }
1067//                }
1068//            }
1069//        }
1070       
1071        // Set parameters of operation
1072        for (Parameter param : calledOperation.getOwnedParameters()) {
1073            if (instSpecPkg == null) {
1074                instSpecPkg = getOrCreateInstanceSpecificationPackage(callMessage.getModel(), event);
1075            }
1076
1077            // TODO String path = calledOperation.getName() + ":" + param.getName();
1078            String path = calledOperation.getName() + ":" + param.getType().getName();
1079            // create param node
1080//            Expression argument =
1081//                (Expression) callMessage.createArgument(param.getName(), param.getType(),
1082//                                                        UMLPackage.Literals.EXPRESSION);
1083            if (isInParameter(param)) {
1084               
1085                // create parameters node
1086                if (!(param.getType() instanceof DataType) ) {
1087                    throw new RuntimeException("TODO error handling; parameters missing");
1088                }
1089                DataType parametersNode = (DataType) param.getType();
1090                InstanceSpecification instSpecParameters = (InstanceSpecification)
1091                        instSpecPkg.createPackagedElement(prefix + "instspec_" + param.getType().getName(),
1092                                                          UMLPackage.Literals.INSTANCE_SPECIFICATION);
1093                instSpecParameters.getClassifiers().add((DataType) param.getType());
1094//                InstanceValue parametersValue =
1095//                        (InstanceValue) argument
1096//                            .createOperand(param.getType().getName(), param.getType(),
1097//                                           UMLPackage.Literals.INSTANCE_VALUE);
1098//                parametersValue.setInstance(instSpecParameters);
1099                InstanceValue instanceValue = (InstanceValue) callMessage.createArgument(param.getName(), param.getType(), UMLPackage.Literals.INSTANCE_VALUE);
1100                instanceValue.setInstance(instSpecParameters);
1101               
1102                for( Property internalParameter : parametersNode.getAllAttributes() ) {
1103                    if (internalParameter.getType() instanceof DataType) {
1104                        List<org.w3c.dom.Element> paramNodes =
1105                            SOAPUtils.getMatchingChildNode(internalParameter.getType().getName(), requestBody);
1106                        // TODO the mistake is somewhere around here ... probably
1107                        //List<org.w3c.dom.Element> paramNodes =
1108                        //    SOAPUtils.getMatchingChildNode(param.getName(), requestBody);
1109                        int multiplicityChosen = paramNodes.size();
1110   
1111                        if (multiplicityChosen == 0 && internalParameter.getLower() > 0) {
1112                            Console.traceln(Level.WARNING,
1113                                            "required attribute not found in SOAP message: " + path);
1114                            Console
1115                                .traceln(Level.WARNING,
1116                                         "setting default values for this attribute and all its children");
1117                            Console.traceln(Level.FINE, "XML structure of path:" + StringTools.ENDLINE +
1118                                SOAPUtils.getSerialization(requestBody));
1119                            multiplicityChosen = internalParameter.getLower();
1120                        }
1121                        for (int i = 0; i < multiplicityChosen; i++) {
1122                            org.w3c.dom.Element paramNode = null;
1123                            if (!paramNodes.isEmpty()) {
1124                                paramNode = paramNodes.get(i);
1125                            }
1126                           
1127                            Slot slot = instSpecParameters.createSlot();
1128                            slot.setDefiningFeature(internalParameter);
1129
1130                            InstanceValue value =
1131                                (InstanceValue) slot.createValue(internalParameter.getName() + "_" + i, internalParameter.getType(),
1132                                                                 UMLPackage.Literals.INSTANCE_VALUE);
1133                            value.setInstance(createInstanceSpecification((DataType) internalParameter.getType(), instSpecPkg,
1134                                                                          prefix, paramNode, path));
1135                            /*
1136                            InstanceValue value =
1137                                (InstanceValue) argument
1138                                    .createOperand(null, internalParameter.getType(),
1139                                                   UMLPackage.Literals.INSTANCE_VALUE);
1140                            value.setInstance(instSpec);*/
1141                        }
1142                    }
1143                    else if (internalParameter.getType() instanceof PrimitiveType) {
1144                        createSlotPrimitiveType(instSpecParameters, internalParameter, requestBody, path);
1145                    }
1146                }
1147            }
1148            else {
1149                // set literalNull for out and return parameters
1150//                argument.createOperand(null, param.getType(), UMLPackage.Literals.LITERAL_NULL);
1151                callMessage.createArgument(param.getName(), param.getType(), UMLPackage.Literals.LITERAL_NULL);
1152            }
1153        }
1154    }
1155
1156    /**
1157     * <p>
1158     * Creates an {@link InstanceSpecification} for a data type in the given package. The values are
1159     * inferred, if possible, from the DOM node. The prefix and the path are used for naming the
1160     * instance specification and to provide good warnings and debug information in case of
1161     * problems.
1162     * </p>
1163     *
1164     * @param type
1165     *            DataType for which the {@link InstanceSpecification} is created
1166     * @param pkg
1167     *            package in which the {@link InstanceSpecification} is created
1168     * @param prefix
1169     *            prefix used for naming the {@link InstanceSpecification}
1170     * @param currentNode
1171     *            node of a DOM from which values are inferred
1172     * @param path
1173     *            used for warnings and debug information
1174     * @return {@link InstanceSpecification} for the given type
1175     */
1176    private static InstanceSpecification createInstanceSpecification(DataType type,
1177                                                                     Package pkg,
1178                                                                     String prefix,
1179                                                                     org.w3c.dom.Element currentNode,
1180                                                                     String path)
1181    {
1182        if ("".equals(path)) {
1183            path = type.getName();
1184        }
1185
1186        InstanceSpecification instSpec =
1187            (InstanceSpecification) pkg
1188                .createPackagedElement(prefix + "instspec_" + type.getName(),
1189                                       UMLPackage.Literals.INSTANCE_SPECIFICATION);
1190        instSpec.getClassifiers().add(type);
1191        for (Property prop : type.getAllAttributes()) {
1192            if (prop.getType() instanceof PrimitiveType) {
1193                createSlotPrimitiveType(instSpec, prop, currentNode, path);
1194            }
1195            else if (prop.getType() instanceof DataType) {
1196                List<org.w3c.dom.Element> attributeNodes = null;
1197                int multiplicityChosen = 0;
1198                if (currentNode != null) {
1199                    // TODO attributeNodes = SOAPUtils.getMatchingChildNode(prop.getName(), currentNode);
1200                    attributeNodes = SOAPUtils.getMatchingChildNode(prop.getName(), currentNode);
1201                    multiplicityChosen = attributeNodes.size();
1202                }
1203
1204                if (multiplicityChosen == 0 && prop.getLower() > 0) {
1205                    if (currentNode != null) {
1206                        Console.traceln(Level.WARNING,
1207                                        "required attribute not found in SOAP message: " + path +
1208                                            "." + prop.getName());
1209                        Console
1210                            .traceln(Level.WARNING,
1211                                     "setting default values for this attribute and all its children");
1212                        Console.traceln(Level.FINE, "XML structure of path:" + StringTools.ENDLINE +
1213                            SOAPUtils.getSerialization(currentNode));
1214                    }
1215                    multiplicityChosen = prop.getLower();
1216                }
1217                for (int i = 0; i < multiplicityChosen; i++) {
1218                    org.w3c.dom.Element attributeNode = null;
1219                    if (attributeNodes != null && !attributeNodes.isEmpty()) {
1220                        attributeNode = attributeNodes.get(i);
1221                    }
1222
1223                    Slot slot = instSpec.createSlot();
1224                    slot.setDefiningFeature(prop);
1225
1226                    InstanceValue value =
1227                        (InstanceValue) slot.createValue(prop.getName() + "_" + i, prop.getType(),
1228                                                         UMLPackage.Literals.INSTANCE_VALUE);
1229                    value.setInstance(createInstanceSpecification((DataType) prop.getType(), pkg,
1230                                                                  prefix, attributeNode, path +
1231                                                                      "." + prop.getName()));
1232                }
1233            }
1234            else {
1235                Console.traceln(Level.SEVERE, "property neither DataType nor PrimitiveType: " +
1236                    prop.getType());
1237                // TODO abort?
1238            }
1239        }
1240        return instSpec;
1241    }
1242
1243    /**
1244     * <p>
1245     * Gets or creates a {@link Package} for {@link InstanceSpecification} created by the
1246     * usage-based testing. Each service gets its own sub-package within a package called
1247     * UBT_InstanceSpecifications. "
1248     * </p>
1249     *
1250     * @param model
1251     *            model in which the package is generated
1252     * @param event
1253     *            event from which the service name is inferred
1254     * @return package for the {@link InstanceSpecification}s
1255     */
1256    private static Package getOrCreateInstanceSpecificationPackage(Model model, Event event) {
1257        String pkgUBTInstSpecs = "UBT_InstanceSpecifications";
1258        Package ubtInstSpecPkg = (Package) model.getOwnedMember(pkgUBTInstSpecs);
1259        if (ubtInstSpecPkg == null) {
1260            ubtInstSpecPkg =
1261                (Package) model.createPackagedElement(pkgUBTInstSpecs, UMLPackage.Literals.PACKAGE);
1262        }
1263        String serviceName = SOAPUtils.getServiceNameFromEvent(event);
1264        Package serviceInstSpecPkg = (Package) ubtInstSpecPkg.getOwnedMember(serviceName);
1265        if (serviceInstSpecPkg == null) {
1266            serviceInstSpecPkg =
1267                (Package) ubtInstSpecPkg.createPackagedElement(serviceName,
1268                                                               UMLPackage.Literals.PACKAGE);
1269        }
1270        return serviceInstSpecPkg;
1271    }
1272
1273    /**
1274     * <p>
1275     * Creates an operand that defines a {@link PrimitiveType}.
1276     * </p>
1277     * <p>
1278     * TODO: Currently does nothing in case of multiplicity 0. I am not sure if, in that case, one
1279     * has to define LiteralNull instead.
1280     * </p>
1281     *
1282     * @param param
1283     *            parameter for which the operand is created
1284     * @param argument
1285     *            argument to which the operand is added
1286     * @param currentNode
1287     *            DOM node from which is value for the operand is inferred
1288     * @param path
1289     *            used for warnings and debug information
1290     */
1291    private static void createOperandPrimitiveType(Parameter param,
1292                                                   Expression argument,
1293                                                   org.w3c.dom.Element currentNode,
1294                                                   String path)
1295    {
1296        List<String> attributeValues = SOAPUtils.getValuesFromElement(param.getName(), currentNode);
1297
1298        if (attributeValues.isEmpty()) {
1299            if (param.getLower() == 0) {
1300                // ignoring optional attribute
1301                return;
1302            }
1303            else {
1304                if (currentNode != null) {
1305                    Console.traceln(Level.WARNING,
1306                                    "required attribute not found in SOAP message: " + path + "." +
1307                                        param.getName());
1308                    Console.traceln(Level.WARNING, "setting default values for this attribute");
1309                    Console.traceln(Level.FINE, "XML structure of path:" + StringTools.ENDLINE +
1310                        SOAPUtils.getSerialization(currentNode));
1311                }
1312                attributeValues.add(null);
1313            }
1314        }
1315        for (String attributeValue : attributeValues) {
1316            if ("String".equals(param.getType().getName())) {
1317                LiteralString spec =
1318                    (LiteralString) argument.createOperand(param.getName(), null,
1319                                                           UMLPackage.Literals.LITERAL_STRING);
1320                if (attributeValue != null) {
1321                    spec.setValue(attributeValue);
1322                }
1323                else {
1324                    spec.setValue("foobar");
1325                }
1326            }
1327            else if ("Integer".equals(param.getType().getName())) {
1328                LiteralInteger spec =
1329                    (LiteralInteger) argument.createOperand(param.getName(), null,
1330                                                            UMLPackage.Literals.LITERAL_INTEGER);
1331                if (attributeValue != null) {
1332                    spec.setValue(Integer.parseInt(attributeValue));
1333                }
1334                else {
1335                    spec.setValue(42);
1336                }
1337            }
1338            else if ("Boolean".equals(param.getType().getName())) {
1339                LiteralBoolean spec =
1340                    (LiteralBoolean) argument.createOperand(param.getName(), null,
1341                                                            UMLPackage.Literals.LITERAL_BOOLEAN);
1342                if (attributeValue != null) {
1343                    spec.setValue(Boolean.parseBoolean(attributeValue));
1344                }
1345                else {
1346                    spec.setValue(true);
1347                }
1348            }
1349            else if ("Real".equals(param.getType().getName())) {
1350                LiteralReal spec =
1351                    (LiteralReal) argument.createOperand(param.getName(), null,
1352                                                         UMLPackage.Literals.LITERAL_REAL);
1353                if (attributeValue != null) {
1354                    spec.setValue(Double.parseDouble(attributeValue));
1355                }
1356                else {
1357                    spec.setValue(3.14);
1358                }
1359            }
1360        }
1361    }
1362
1363    /**
1364     * <p>
1365     * Creates a {@link Slot} in an {@link InstanceSpecification} for a primitive type.
1366     * </p>
1367     *
1368     * @param instSpec
1369     *            instance specification to which the slot is added
1370     * @param prop
1371     *            property that describes the slot
1372     * @param currentNode
1373     *            DOM node from which is value for the slot is inferred
1374     * @param path
1375     *            used for warnings and debug information
1376     */
1377    private static void createSlotPrimitiveType(InstanceSpecification instSpec,
1378                                                Property prop,
1379                                                org.w3c.dom.Element currentNode,
1380                                                String path)
1381    {
1382        List<String> attributeValues = SOAPUtils.getValuesFromElement(prop.getName(), currentNode);
1383
1384        if (attributeValues.isEmpty()) {
1385            if (prop.getLower() == 0) {
1386                // ignoring optional attribute
1387                return;
1388            }
1389            else {
1390                if (currentNode != null) {
1391                    Console.traceln(Level.WARNING,
1392                                    "required attribute not found in SOAP message: " + path + "." +
1393                                        prop.getName());
1394                    Console.traceln(Level.WARNING, "setting default values for this attribute");
1395                    Console.traceln(Level.FINE, "XML structure of path:" + StringTools.ENDLINE +
1396                        SOAPUtils.getSerialization(currentNode));
1397                }
1398                attributeValues.add(null);
1399            }
1400        }
1401        for (String attributeValue : attributeValues) {
1402            Slot slot = instSpec.createSlot();
1403            slot.setDefiningFeature(prop);
1404            if ("String".equals(prop.getType().getName())) {
1405                LiteralString value =
1406                    (LiteralString) slot.createValue(prop.getName(), null,
1407                                                     UMLPackage.Literals.LITERAL_STRING);
1408                if (attributeValue != null) {
1409                    value.setValue(attributeValue);
1410                }
1411                else {
1412                    value.setValue("foobar");
1413                }
1414            }
1415            else if ("Integer".equals(prop.getType().getName())) {
1416                LiteralInteger value =
1417                    (LiteralInteger) slot.createValue(prop.getName(), null,
1418                                                      UMLPackage.Literals.LITERAL_INTEGER);
1419                if (attributeValue != null) {
1420                    value.setValue(Integer.parseInt(attributeValue));
1421                }
1422                else {
1423                    value.setValue(42);
1424                }
1425            }
1426            else if ("Boolean".equals(prop.getType().getName())) {
1427                LiteralBoolean value =
1428                    (LiteralBoolean) slot.createValue(prop.getName(), null,
1429                                                      UMLPackage.Literals.LITERAL_BOOLEAN);
1430                if (attributeValue != null) {
1431                    value.setValue(Boolean.parseBoolean(attributeValue));
1432                }
1433                else {
1434                    value.setValue(true);
1435                }
1436            }
1437            else if ("Real".equals(prop.getType().getName())) {
1438                LiteralReal value =
1439                    (LiteralReal) slot.createValue(prop.getName(), null,
1440                                                   UMLPackage.Literals.LITERAL_REAL);
1441                if (attributeValue != null) {
1442                    value.setValue(Double.parseDouble(attributeValue));
1443                }
1444                else {
1445                    value.setValue(3.14);
1446                }
1447            }
1448            else {
1449                Console.traceln(Level.SEVERE, "could not create literal for primitive type: " +
1450                    prop.getType().getName());
1451                // TODO abort?
1452            }
1453        }
1454    }
1455
1456    /**
1457     * <p>
1458     * Sets values for the parameters of a reply message. The values are, all LiterealNull and to
1459     * the INOUT, OUT and REPLY parameters, the UTP stereotype LiteralAny is applied.
1460     * </p>
1461     *
1462     * @param replyMessage
1463     *            reply message for which the parameters are set
1464     * @param calledOperation
1465     *            operation that is replied for by the message
1466     */
1467    private static void setReplyMessageParameters(Message replyMessage, Operation calledOperation) {
1468        for (Parameter param : calledOperation.getOwnedParameters()) {
1469            Expression argument =
1470                (Expression) replyMessage.createArgument(param.getName(), param.getType(),
1471                                                         UMLPackage.Literals.EXPRESSION);
1472            ValueSpecification operand =
1473                    argument.createOperand(null, param.getType(), UMLPackage.Literals.LITERAL_NULL);
1474            if (isOutParameter(param)) {
1475                operand.applyStereotype(UTPUtils.getLiteralAnyStereotype(replyMessage.getModel()));
1476            }
1477        }
1478    }
1479
1480    /**
1481     * <p>
1482     * Checks if a parameter has the direction IN or INOUT
1483     * </p>
1484     *
1485     * @param parameter
1486     *            parameter that is checked
1487     * @return true if the direction is IN or INOUT; false otherwise
1488     */
1489    private static boolean isInParameter(Parameter parameter) {
1490        return parameter.getDirection() == ParameterDirectionKind.IN_LITERAL ||
1491            parameter.getDirection() == ParameterDirectionKind.INOUT_LITERAL;
1492    }
1493
1494    /**
1495     * <p>
1496     * Checks if a parameter has the direction RETURN, OUT or INOUT
1497     * </p>
1498     *
1499     * @param parameter
1500     *            parameter that is checked
1501     * @return true if the direction is RETURN, OUT, or INOUT; false otherwise
1502     */
1503    private static boolean isOutParameter(Parameter parameter) {
1504        return parameter.getDirection() == ParameterDirectionKind.RETURN_LITERAL ||
1505            parameter.getDirection() == ParameterDirectionKind.OUT_LITERAL ||
1506            parameter.getDirection() == ParameterDirectionKind.INOUT_LITERAL;
1507    }
1508
1509    /**
1510     * <p>
1511     * Checks if the {@link MessageSort} of a message is a call message, i.e., ASYNCH_CALL or
1512     * SYNCH_CALL.
1513     * </p>
1514     *
1515     * @param message
1516     *            message that is checked
1517     * @return true if the message is a call message; false otherwise
1518     */
1519    private static boolean isCallMessage(Message message) {
1520        if (message == null) {
1521            return false;
1522        }
1523        MessageSort msgSort = message.getMessageSort();
1524        return msgSort == MessageSort.ASYNCH_CALL_LITERAL ||
1525            msgSort == MessageSort.SYNCH_CALL_LITERAL;
1526    }
1527
1528    /**
1529     * <p>
1530     * inverse-sorts the values of a map. Has been adapted from <a href=
1531     * "http://stackoverflow.com/questions/109383/how-to-sort-a-mapkey-value-on-the-values-in-java"
1532     * >this</a> StackOverflow post.
1533     * </p>
1534     *
1535     * @param map
1536     *            map whose values are sorted
1537     * @return sorted version of the map
1538     */
1539    private static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
1540        // TODO possibly move to another class
1541        List<Map.Entry<K, V>> list = new LinkedList<>(map.entrySet());
1542        Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
1543            @Override
1544            public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
1545                return -1 * (o1.getValue()).compareTo(o2.getValue());
1546            }
1547        });
1548
1549        Map<K, V> result = new LinkedHashMap<>();
1550        for (Map.Entry<K, V> entry : list) {
1551            result.put(entry.getKey(), entry.getValue());
1552        }
1553        return result;
1554    }
1555}
Note: See TracBrowser for help on using the repository browser.