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

Last change on this file since 1759 was 1759, checked in by sherbold, 10 years ago
  • added new third-party repo for FF tooling
  • now using UML modeling tools from FF
  • generated sequence diagrams are now the implementation of an operation with the TestCase? stereotype. In case the loaded model knows UTP, the UTP TestCase? stereotype is set.
  • Property svn:mime-type set to text/plain
File size: 21.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.HashMap;
19import java.util.Iterator;
20import java.util.LinkedList;
21import java.util.List;
22import java.util.Map;
23import java.util.Map.Entry;
24
25import org.eclipse.emf.common.util.EList;
26import org.eclipse.uml2.uml.Comment;
27import org.eclipse.uml2.uml.Component;
28import org.eclipse.uml2.uml.Connector;
29import org.eclipse.uml2.uml.ConnectorEnd;
30import org.eclipse.uml2.uml.Element;
31import org.eclipse.uml2.uml.Interaction;
32import org.eclipse.uml2.uml.InteractionFragment;
33import org.eclipse.uml2.uml.Interface;
34import org.eclipse.uml2.uml.Lifeline;
35import org.eclipse.uml2.uml.Message;
36import org.eclipse.uml2.uml.MessageOccurrenceSpecification;
37import org.eclipse.uml2.uml.MessageSort;
38import org.eclipse.uml2.uml.Model;
39import org.eclipse.uml2.uml.Operation;
40import org.eclipse.uml2.uml.Port;
41import org.eclipse.uml2.uml.Profile;
42import org.eclipse.uml2.uml.Region;
43import org.eclipse.uml2.uml.StateMachine;
44import org.eclipse.uml2.uml.Stereotype;
45import org.eclipse.uml2.uml.Transition;
46import org.eclipse.uml2.uml.UMLPackage;
47import org.eclipse.uml2.uml.Vertex;
48
49import de.ugoe.cs.autoquest.eventcore.Event;
50import de.ugoe.cs.autoquest.plugin.http.eventcore.SOAPEventType;
51import de.ugoe.cs.autoquest.plugin.http.eventcore.SimpleSOAPEventType;
52import de.ugoe.cs.autoquest.plugin.uml.eventcore.UMLTransitionType;
53import de.ugoe.cs.autoquest.usageprofiles.TrieBasedModel;
54
55/**
56 * <p>
57 * Utilities for working with UML.
58 * </p>
59 *
60 * @author Steffen Herbold
61 */
62public class UMLUtils {
63
64    /**
65     * <p>
66     * Creates a sequence of events with {@link UMLTransitionType} as event type from a given
67     * sequence of events with the {@link SOAPEventType}, by matching the sequences to a state
68     * machine.
69     * </p>
70     *
71     * @param sequence
72     *            SOAP sequences
73     * @param stateMachine
74     *            the state machine
75     * @return create UML sequences
76     */
77    public static List<Event> createUMLTransitionSequence(List<Event> sequence,
78                                                          StateMachine stateMachine)
79    {
80        List<List<Transition>> matchingSequences =
81            determineMatchingTransitionSequences(sequence, stateMachine);
82
83        if (matchingSequences.size() != 1) {
84            throw new RuntimeException("no unique match found; " + matchingSequences.size() +
85                " matches");
86        }
87        List<Event> umlEventSequence = new LinkedList<>();
88        for (Transition transition : matchingSequences.get(0)) {
89            umlEventSequence.add(new Event(new UMLTransitionType(transition)));
90        }
91        return umlEventSequence;
92    }
93
94    /**
95     * <p>
96     * Uses a sequences of events with the {@link UMLTransitionType} to determine the transition
97     * probabilities for the state machine.
98     * </p>
99     *
100     * @param sequences
101     *            UML sequences
102     * @param stateMachine
103     *            state machine to be converted to a usage profile
104     */
105    public static void convertStateMachineToUsageProfile(Collection<List<Event>> sequences,
106                                                         StateMachine stateMachine)
107    {
108        // create state->outgoings hashmap
109        Map<Vertex, Map<Transition, Integer>> stateMap = new HashMap<>();
110        for (Region region : stateMachine.getRegions()) {
111            for (Vertex state : region.getSubvertices()) {
112                stateMap.put(state, new HashMap<Transition, Integer>());
113            }
114        }
115
116        // create counters for each transition
117        for (List<Event> sequence : sequences) {
118            for (Event event : sequence) {
119                if (event.getType() instanceof UMLTransitionType) {
120                    Transition transition = ((UMLTransitionType) event.getType()).getTransition();
121                    Map<Transition, Integer> transitionMap = stateMap.get(transition.getSource());
122                    Integer value = transitionMap.get(transition);
123                    if (value == null) {
124                        value = 0;
125                    }
126                    transitionMap.put(transition, value + 1);
127                }
128                else {
129                    throw new RuntimeException(
130                                               "Wrong event type. Only UMLTransitionType supported but was: " +
131                                                   event.getType().getClass().getName());
132                }
133            }
134        }
135
136        // calculate probabilities
137        for (Region region : stateMachine.getRegions()) {
138            for (Vertex state : region.getSubvertices()) {
139                Map<Transition, Integer> transitionMap = stateMap.get(state);
140                int totalCount = 0;
141                for (Entry<Transition, Integer> entry : transitionMap.entrySet()) {
142                    totalCount += entry.getValue();
143                }
144                if (totalCount != 0) {
145                    for (Transition transition : state.getOutgoings()) {
146                        double prob = 0.0d;
147                        if (transitionMap.containsKey(transition)) {
148                            prob = ((double) transitionMap.get(transition)) / totalCount;
149                        }
150                        Comment comment = transition.createOwnedComment();
151                        comment.setBody("" + prob);
152                    }
153                }
154                else {
155                    // system has never been in this state, all transitions equally likely
156                    int numOutgoings = state.getOutgoings().size();
157                    for (Transition transition : state.getOutgoings()) {
158                        Comment comment = transition.createOwnedComment();
159                        comment.setBody("" + (1.0d / numOutgoings));
160                    }
161                }
162            }
163        }
164    }
165
166    /**
167     * <p>
168     * Determines all matching {@link Transition} sequences in a state machine for a given sequence
169     * of SOAP events.
170     * </p>
171     *
172     * @param sequence
173     *            SOAP sequence
174     * @param stateMachine
175     *            the state machine
176     * @return all matching {@link Transition} sequences
177     */
178    public static List<List<Transition>> determineMatchingTransitionSequences(List<Event> sequence,
179                                                                              StateMachine stateMachine)
180    {
181        EList<Region> regions = stateMachine.getRegions();
182        EList<Vertex> states = null;
183        for (Region region : regions) {
184            if (states == null) {
185                states = region.getSubvertices();
186            }
187            else {
188                states.addAll(region.getSubvertices());
189            }
190        }
191        List<Transition> allTransitions = new LinkedList<>();
192        for (Vertex state : states) {
193            allTransitions.addAll(state.getOutgoings());
194        }
195
196        List<List<Transition>> matchingSequences = null;
197        List<Transition> currentTransitions = null;
198
199        // first, we try to find a single unique transition that we can match using the method name
200        for (Iterator<Event> eventIterator = sequence.iterator(); eventIterator.hasNext();) {
201            Event event = eventIterator.next();
202            if (event.getType() instanceof SOAPEventType) {
203                SOAPEventType eventType = (SOAPEventType) event.getType();
204                if (matchingSequences == null) {
205                    matchingSequences = new LinkedList<>();
206                    List<Transition> initialMatches = matchTransitions(allTransitions, eventType);
207                    for (Transition transition : initialMatches) {
208                        List<Transition> candidate = new LinkedList<>();
209                        candidate.add(transition);
210                        matchingSequences.add(candidate);
211                    }
212                    currentTransitions = initialMatches;
213                }
214                else {
215                    List<List<Transition>> nextMatchingSequences = new LinkedList<>();
216                    List<Transition> nextCurrentTransitions = new LinkedList<>();
217                    Iterator<Transition> currentTransitionIterator = currentTransitions.iterator();
218                    Iterator<List<Transition>> currentMatchingSequencesIterator =
219                        matchingSequences.iterator();
220                    while (currentTransitionIterator.hasNext()) {
221                        Transition currentTransition = currentTransitionIterator.next();
222                        List<Transition> currentMatch = currentMatchingSequencesIterator.next();
223
224                        List<Transition> matches =
225                            matchTransitions(currentTransition.getTarget().getOutgoings(),
226                                             eventType);
227                        if (matches.isEmpty()) {
228                            throw new RuntimeException("no matches found");
229                        }
230                        for (Transition matchingTransition : matches) {
231                            List<Transition> candidate = new LinkedList<>(currentMatch);
232                            candidate.add(matchingTransition);
233                            nextMatchingSequences.add(candidate);
234                            nextCurrentTransitions.add(matchingTransition);
235                        }
236                    }
237                    matchingSequences = nextMatchingSequences;
238                    currentTransitions = nextCurrentTransitions;
239                }
240            }
241            else {
242                throw new RuntimeException(
243                                           "Wrong event type. Only UMLTransitionType supported but was: " +
244                                               event.getType().getClass().getName());
245            }
246        }
247        return matchingSequences;
248    }
249
250    /**
251     * <p>
252     * Extends a given model with an interaction that represents an observed sequence.
253     * </p>
254     *
255     * @param sequence
256     *            sequence that is added as sequence diagram
257     * @param model
258     *            UML model to which the interaction is added
259     * @param interactionName
260     *            name of the interaction
261     */
262    public static void createInteractionFromEventSequence(List<Event> sequence,
263                                                          Model model,
264                                                          String interactionName)
265    {
266        Map<String, Interface> interfaceMap = new HashMap<>();
267        Map<String, Port> portMap = new HashMap<>();
268
269        EList<Element> elements = model.getOwnedElements();
270        for (Element element : elements) {
271            if (element instanceof Interface) {
272                interfaceMap.put(((Interface) element).getName(), (Interface) element);
273            }
274        }
275
276        Component testContext =
277            (Component) model.getPackagedElement("TestContext", true,
278                                                 UMLPackage.Literals.COMPONENT, true);
279
280        final Profile utpProfile = model.getAppliedProfile("utp");
281        final Stereotype utpTestCase = (Stereotype) utpProfile.getOwnedMember("TestCase");
282        Operation operation = testContext.createOwnedOperation(interactionName, null, null);
283        operation.applyStereotype(utpTestCase);
284
285        Interaction interaction =
286            (Interaction) testContext.createPackagedElement(interactionName + "_Impl",
287                                                            UMLPackage.Literals.INTERACTION);
288
289        Lifeline userLifeline = interaction.createLifeline("user");
290
291        userLifeline.setRepresents(testContext.getAttribute("user", null));
292
293        Component userComponent =
294            (Component) model.getPackagedElement("User", true, UMLPackage.Literals.COMPONENT, true);
295        Port userPort = (Port) userComponent.getAttribute("p_user_rlus", null);
296
297        int i = 0;
298        for (Event event : sequence) {
299            if (!(event.equals(Event.STARTEVENT) || event.equals(Event.ENDEVENT))) {
300                String serviceName = getServiceNameFromEvent(event);
301                String methodName = getCalledMethodFromEvent(event);
302
303                Interface targetInterface = interfaceMap.get(serviceName);
304                if (targetInterface == null) {
305                    throw new RuntimeException(
306                                               "Could not find interface in the UML model that belong to the service: " +
307                                                   serviceName);
308                }
309
310                Lifeline targetLifeline = interaction.getLifeline(serviceName);
311                if (targetLifeline == null) {
312                    targetLifeline = interaction.createLifeline(serviceName);
313
314                    targetLifeline.setRepresents(testContext
315                        .getAttribute(serviceName + "_property", null));
316
317                    Component component =
318                        (Component) model.getPackagedElement(serviceName, true,
319                                                             UMLPackage.Literals.COMPONENT, true);
320                    portMap.put(serviceName,
321                                (Port) component.getAttribute("p_" + serviceName, null));
322                }
323                MessageOccurrenceSpecification sendFragment =
324                    (MessageOccurrenceSpecification) interaction
325                        .createFragment(i + ":" + methodName + "_sendFragment",
326                                        UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
327                MessageOccurrenceSpecification recvFragment =
328                    (MessageOccurrenceSpecification) interaction
329                        .createFragment(i + ":" + methodName + "_recvFragment",
330                                        UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
331
332                sendFragment.setCovered(userLifeline);
333                recvFragment.setCovered(targetLifeline);
334
335                Message message = interaction.createMessage(methodName);
336                if (getOperationFromName(targetInterface.getOperations(), methodName) == null) {
337                    System.out.println("operation not found: " + methodName);
338                }
339                message.setSignature(getOperationFromName(targetInterface.getOperations(),
340                                                          methodName));
341                message.setMessageSort(MessageSort.ASYNCH_CALL_LITERAL);
342                message.setSendEvent(sendFragment);
343                message.setReceiveEvent(recvFragment);
344
345                EList<ConnectorEnd> sourceEnds = userPort.getEnds();
346                EList<ConnectorEnd> targetEnds = portMap.get(serviceName).getEnds();
347
348                for (ConnectorEnd sourceEnd : sourceEnds) {
349                    Connector sourceConnector = (Connector) sourceEnd.eContainer();
350                    for (ConnectorEnd targetEnd : targetEnds) {
351                        Connector targetConnector = (Connector) targetEnd.eContainer();
352                        if (targetConnector == sourceConnector) {
353                            message.setConnector(targetConnector);
354                        }
355                    }
356                }
357
358                sendFragment.setMessage(message);
359                recvFragment.setMessage(message);
360
361                i++;
362            }
363        }
364    }
365
366    /**
367     * <p>
368     * Calculates the usage score of an interaction as the logsum of the event probabilities
369     * multiplied with the length of the interaction.
370     * </p>
371     *
372     * @param interaction
373     *            interaction for which the score is calculated
374     * @param usageProfile
375     *            usage profile used for the calculation
376     * @return calculated usage score
377     */
378    public static double calculateUsageScore(Interaction interaction, TrieBasedModel usageProfile) {
379        double usageScore = 0.0d;
380
381        EList<InteractionFragment> interactionFragments = interaction.getFragments();
382        List<Event> eventSequence = new LinkedList<>();
383        eventSequence.add(Event.STARTEVENT);
384        for (InteractionFragment interactionFragment : interactionFragments) {
385            if (interactionFragment.getName() != null &&
386                interactionFragment.getName().endsWith("_recvFragment"))
387            {
388                String serviceName =
389                    interactionFragment.getCovereds().get(0).getRepresents().getName();
390                String methodName = "UNKNOWN";
391                if (interactionFragment instanceof MessageOccurrenceSpecification) {
392                    methodName =
393                        ((MessageOccurrenceSpecification) interactionFragment).getMessage()
394                            .getName();
395                }
396                eventSequence.add(new Event(new SimpleSOAPEventType(methodName, serviceName)));
397            }
398        }
399        double prob = usageProfile.getLogSum(eventSequence);
400        usageScore = prob * eventSequence.size();
401
402        return usageScore;
403    }
404
405    /**
406     * <p>
407     * Helper function to get the name of a service from a SOAP event.
408     * </p>
409     *
410     * @param event
411     *            event for which the service name is retrieved
412     * @return service name
413     */
414    private static String getServiceNameFromEvent(Event event) {
415        if (event.getType() instanceof SOAPEventType) {
416            return ((SOAPEventType) event.getType()).getServiceName();
417        }
418        else if (event.getType() instanceof SimpleSOAPEventType) {
419            return ((SimpleSOAPEventType) event.getType()).getServiceName();
420        }
421        else {
422            throw new RuntimeException(
423                                       "Wrong event type. Only SOAPEventType and SimpleSOAPEventType supported but was: " +
424                                           event.getType().getClass().getName());
425        }
426    }
427
428    /**
429     *
430     * <p>
431     * Helper function to get the called method from a SOAP event
432     * </p>
433     *
434     * @param event
435     *            event for which the called method is retrieved
436     * @return called method
437     */
438    private static String getCalledMethodFromEvent(Event event) {
439        if (event.getType() instanceof SOAPEventType) {
440            return ((SOAPEventType) event.getType()).getCalledMethod();
441        }
442        else if (event.getType() instanceof SimpleSOAPEventType) {
443            return ((SimpleSOAPEventType) event.getType()).getCalledMethod();
444        }
445        else {
446            throw new RuntimeException(
447                                       "Wrong event type. Only SOAPEventType and SimpleSOAPEventType supported but was: " +
448                                           event.getType().getClass().getName());
449        }
450    }
451
452    /**
453     * <p>
454     * Fetches an operation using only its name from a list of operations. Returns the first match
455     * found or null if no match is found.
456     * </p>
457     *
458     * @param operations
459     *            list of operations
460     * @param name
461     *            name of the operation
462     * @return first matching operation; null if no match is found
463     */
464    private static Operation getOperationFromName(EList<Operation> operations, String name) {
465        if (name == null) {
466            throw new IllegalArgumentException("name of the operation must not be null");
467        }
468        if (operations != null) {
469            for (Operation operation : operations) {
470                if (operation.getName() != null && operation.getName().equals(name)) {
471                    return operation;
472                }
473            }
474        }
475        return null;
476    }
477
478    /**
479     * <p>
480     * Determines which transitions match a given {@link SOAPEventType}.
481     * </p>
482     *
483     * @param transitions
484     *            the transitions
485     * @param eventType
486     *            the SOAP event
487     * @return matching transitions
488     */
489    private static List<Transition> matchTransitions(List<Transition> transitions,
490                                                     SOAPEventType eventType)
491    {
492        List<Transition> matching = new LinkedList<>();
493        for (Transition transition : transitions) {
494            // String serviceName = transition.getName().split("\\.")[0]; // TODO service name check
495            String methodName = transition.getName().split("\\.")[1];
496            if (methodName.equals(eventType.getCalledMethod())) {
497                matching.add(transition);
498            }
499        }
500        return matching;
501    }
502
503}
Note: See TracBrowser for help on using the repository browser.