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

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