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

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