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

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