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

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