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

Last change on this file since 1633 was 1633, checked in by sherbold, 10 years ago
  • sequence diagram generation now ignores STARTEVENT and ENDEVENT
  • Property svn:mime-type set to text/plain
File size: 16.2 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.Association;
27import org.eclipse.uml2.uml.Class;
28import org.eclipse.uml2.uml.Comment;
29import org.eclipse.uml2.uml.Element;
30import org.eclipse.uml2.uml.Interaction;
31import org.eclipse.uml2.uml.Lifeline;
32import org.eclipse.uml2.uml.Message;
33import org.eclipse.uml2.uml.MessageOccurrenceSpecification;
34import org.eclipse.uml2.uml.Model;
35import org.eclipse.uml2.uml.Operation;
36import org.eclipse.uml2.uml.Region;
37import org.eclipse.uml2.uml.StateMachine;
38import org.eclipse.uml2.uml.Transition;
39import org.eclipse.uml2.uml.UMLPackage;
40import org.eclipse.uml2.uml.Vertex;
41
42import de.ugoe.cs.autoquest.eventcore.Event;
43import de.ugoe.cs.autoquest.plugin.http.eventcore.SOAPEventType;
44import de.ugoe.cs.autoquest.plugin.uml.eventcore.UMLTransitionType;
45
46/**
47 * <p>
48 * Utilities for working with UML.
49 * </p>
50 *
51 * @author Steffen Herbold
52 */
53public class UMLUtils {
54
55    /**
56     * <p>
57     * Creates a sequence of events with {@link UMLTransitionType} as event type from a given
58     * sequence of events with the {@link SOAPEventType}, by matching the sequences to a state
59     * machine.
60     * </p>
61     *
62     * @param sequence
63     *            SOAP sequences
64     * @param stateMachine
65     *            the state machine
66     * @return create UML sequences
67     */
68    public static List<Event> createUMLTransitionSequence(List<Event> sequence,
69                                                          StateMachine stateMachine)
70    {
71        List<List<Transition>> matchingSequences =
72            determineMatchingTransitionSequences(sequence, stateMachine);
73
74        if (matchingSequences.size() != 1) {
75            throw new RuntimeException("no unique match found; " + matchingSequences.size() +
76                " matches");
77        }
78        List<Event> umlEventSequence = new LinkedList<>();
79        for (Transition transition : matchingSequences.get(0)) {
80            umlEventSequence.add(new Event(new UMLTransitionType(transition)));
81        }
82        return umlEventSequence;
83    }
84
85    /**
86     * <p>
87     * Uses a sequences of events with the {@link UMLTransitionType} to determine the transition
88     * probabilities for the state machine.
89     * </p>
90     *
91     * @param sequences
92     *            UML sequences
93     * @param stateMachine
94     *            state machine to be converted to a usage profile
95     */
96    public static void convertStateMachineToUsageProfile(Collection<List<Event>> sequences,
97                                                         StateMachine stateMachine)
98    {
99        // create state->outgoings hashmap
100        Map<Vertex, Map<Transition, Integer>> stateMap = new HashMap<>();
101        for (Region region : stateMachine.getRegions()) {
102            for (Vertex state : region.getSubvertices()) {
103                stateMap.put(state, new HashMap<Transition, Integer>());
104            }
105        }
106
107        // create counters for each transition
108        for (List<Event> sequence : sequences) {
109            for (Event event : sequence) {
110                if (event.getType() instanceof UMLTransitionType) {
111                    Transition transition = ((UMLTransitionType) event.getType()).getTransition();
112                    Map<Transition, Integer> transitionMap = stateMap.get(transition.getSource());
113                    Integer value = transitionMap.get(transition);
114                    if (value == null) {
115                        value = 0;
116                    }
117                    transitionMap.put(transition, value + 1);
118                }
119                else {
120                    throw new RuntimeException(
121                                               "Wrong event type. Only UMLTransitionType supported but was: " +
122                                                   event.getType().getClass().getName());
123                }
124            }
125        }
126
127        // calculate probabilities
128        for (Region region : stateMachine.getRegions()) {
129            for (Vertex state : region.getSubvertices()) {
130                Map<Transition, Integer> transitionMap = stateMap.get(state);
131                int totalCount = 0;
132                for (Entry<Transition, Integer> entry : transitionMap.entrySet()) {
133                    totalCount += entry.getValue();
134                }
135                if (totalCount != 0) {
136                    for (Transition transition : state.getOutgoings()) {
137                        double prob = 0.0d;
138                        if (transitionMap.containsKey(transition)) {
139                            prob = ((double) transitionMap.get(transition)) / totalCount;
140                        }
141                        Comment comment = transition.createOwnedComment();
142                        comment.setBody("" + prob);
143                    }
144                }
145                else {
146                    // system has never been in this state, all transitions equally likely
147                    int numOutgoings = state.getOutgoings().size();
148                    for (Transition transition : state.getOutgoings()) {
149                        Comment comment = transition.createOwnedComment();
150                        comment.setBody("" + (1.0d / numOutgoings));
151                    }
152                }
153            }
154        }
155    }
156
157    /**
158     * <p>
159     * Determines all matching {@link Transition} sequences in a state machine for a given sequence
160     * of SOAP events.
161     * </p>
162     *
163     * @param sequence
164     *            SOAP sequence
165     * @param stateMachine
166     *            the state machine
167     * @return all matching {@link Transition} sequences
168     */
169    public static List<List<Transition>> determineMatchingTransitionSequences(List<Event> sequence,
170                                                                              StateMachine stateMachine)
171    {
172        EList<Region> regions = stateMachine.getRegions();
173        EList<Vertex> states = null;
174        for (Region region : regions) {
175            if (states == null) {
176                states = region.getSubvertices();
177            }
178            else {
179                states.addAll(region.getSubvertices());
180            }
181        }
182        List<Transition> allTransitions = new LinkedList<>();
183        for (Vertex state : states) {
184            allTransitions.addAll(state.getOutgoings());
185        }
186
187        List<List<Transition>> matchingSequences = null;
188        List<Transition> currentTransitions = null;
189
190        // first, we try to find a single unique transition that we can match using the method name
191        for (Iterator<Event> eventIterator = sequence.iterator(); eventIterator.hasNext();) {
192            Event event = eventIterator.next();
193            if (event.getType() instanceof SOAPEventType) {
194                SOAPEventType eventType = (SOAPEventType) event.getType();
195                if (matchingSequences == null) {
196                    matchingSequences = new LinkedList<>();
197                    List<Transition> initialMatches = matchTransitions(allTransitions, eventType);
198                    for (Transition transition : initialMatches) {
199                        List<Transition> candidate = new LinkedList<>();
200                        candidate.add(transition);
201                        matchingSequences.add(candidate);
202                    }
203                    currentTransitions = initialMatches;
204                }
205                else {
206                    List<List<Transition>> nextMatchingSequences = new LinkedList<>();
207                    List<Transition> nextCurrentTransitions = new LinkedList<>();
208                    Iterator<Transition> currentTransitionIterator = currentTransitions.iterator();
209                    Iterator<List<Transition>> currentMatchingSequencesIterator =
210                        matchingSequences.iterator();
211                    while (currentTransitionIterator.hasNext()) {
212                        Transition currentTransition = currentTransitionIterator.next();
213                        List<Transition> currentMatch = currentMatchingSequencesIterator.next();
214
215                        List<Transition> matches =
216                            matchTransitions(currentTransition.getTarget().getOutgoings(),
217                                             eventType);
218                        if (matches.isEmpty()) {
219                            throw new RuntimeException("no matches found");
220                        }
221                        for (Transition matchingTransition : matches) {
222                            List<Transition> candidate = new LinkedList<>(currentMatch);
223                            candidate.add(matchingTransition);
224                            nextMatchingSequences.add(candidate);
225                            nextCurrentTransitions.add(matchingTransition);
226                        }
227                    }
228                    matchingSequences = nextMatchingSequences;
229                    currentTransitions = nextCurrentTransitions;
230                }
231            }
232            else {
233                throw new RuntimeException(
234                                           "Wrong event type. Only UMLTransitionType supported but was: " +
235                                               event.getType().getClass().getName());
236            }
237        }
238        return matchingSequences;
239    }
240
241    /**
242     * <p>
243     * Extends a given model with an interaction that represents an observed sequence.
244     * </p>
245     *
246     * @param sequence
247     *            sequence that is added as sequence diagram
248     * @param model
249     *            UML model to which the interaction is added
250     * @param interactionName
251     *            name of the interaction
252     */
253    public static void createInteractionFromEventSequence(List<Event> sequence,
254                                                          Model model,
255                                                          String interactionName)
256    {
257        Map<String, Class> classMap = new HashMap<>();
258
259        EList<Element> elements = model.getOwnedElements();
260        for (Element element : elements) {
261            if (element instanceof Class) {
262                classMap.put(((Class) element).getName(), (Class) element);
263            }
264        }
265
266        Interaction interaction =
267            (Interaction) model.createPackagedElement(interactionName,
268                                                      UMLPackage.Literals.INTERACTION);
269
270        Lifeline userLifeline = interaction.createLifeline("user");
271
272        int i = 0;
273        for (Event event : sequence) {
274            if (!(event.equals(Event.STARTEVENT) || event.equals(Event.ENDEVENT))) {
275                if (event.getType() instanceof SOAPEventType) {
276                    SOAPEventType eventType = (SOAPEventType) event.getType();
277                    String serviceName = eventType.getServiceName();
278                    String methodName = eventType.getCalledMethod();
279                    Class targetClass = classMap.get(serviceName);
280                    if (targetClass == null) {
281                        throw new RuntimeException(
282                                                   "Could not find class in the UML model that belong to the service: " +
283                                                       serviceName);
284                    }
285
286                    Lifeline targetLifeline = interaction.getLifeline(serviceName);
287                    if (targetLifeline == null) {
288                        targetLifeline = interaction.createLifeline(serviceName);
289                        Association association =
290                            (Association) model.getPackagedElement("user_" + serviceName, true,
291                                                                   UMLPackage.Literals.ASSOCIATION,
292                                                                   true);
293                        targetLifeline.setRepresents(association.getMemberEnd(serviceName, classMap
294                            .get(serviceName)));
295                    }
296                    MessageOccurrenceSpecification sendFragment =
297                        (MessageOccurrenceSpecification) interaction
298                            .createFragment(i + ":" + methodName + "_sendFragment",
299                                            UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
300                    MessageOccurrenceSpecification recvFragment =
301                        (MessageOccurrenceSpecification) interaction
302                            .createFragment(i + ":" + methodName + "_recvFragment",
303                                            UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
304
305                    sendFragment.setCovered(userLifeline);
306                    recvFragment.setCovered(targetLifeline);
307
308                    Message message = interaction.createMessage(methodName);
309                    message.setSignature(getOperationFromName(targetClass.getOperations(),
310                                                              methodName));
311                    message.setSendEvent(sendFragment);
312                    message.setReceiveEvent(recvFragment);
313
314                    sendFragment.setMessage(message);
315                    recvFragment.setMessage(message);
316                }
317                else {
318                    throw new RuntimeException(
319                                               "Wrong event type. Only SOAPEventType supported but was: " +
320                                                   event.getType().getClass().getName());
321                }
322                i++;
323            }
324        }
325    }
326
327    /**
328     * <p>
329     * Fetches an operation using only its name from a list of operations. Returns the first match
330     * found or null if no match is found.
331     * </p>
332     *
333     * @param operations
334     *            list of operations
335     * @param name
336     *            name of the operation
337     * @return first matching operation; null if no match is found
338     */
339    private static Operation getOperationFromName(EList<Operation> operations, String name) {
340        if (name == null) {
341            throw new IllegalArgumentException("name of the operation must not be null");
342        }
343        if (operations != null) {
344            for (Operation operation : operations) {
345                if (operation.getName() != null && operation.getName().equals(name)) {
346                    return operation;
347                }
348            }
349        }
350        return null;
351    }
352
353    /**
354     * <p>
355     * Determines which transitions match a given {@link SOAPEventType}.
356     * </p>
357     *
358     * @param transitions
359     *            the transitions
360     * @param eventType
361     *            the SOAP event
362     * @return matching transitions
363     */
364    private static List<Transition> matchTransitions(List<Transition> transitions,
365                                                     SOAPEventType eventType)
366    {
367        List<Transition> matching = new LinkedList<>();
368        for (Transition transition : transitions) {
369            // String serviceName = transition.getName().split("\\.")[0]; // TODO service name check
370            String methodName = transition.getName().split("\\.")[1];
371            if (methodName.equals(eventType.getCalledMethod())) {
372                matching.add(transition);
373            }
374        }
375        return matching;
376    }
377
378}
Note: See TracBrowser for help on using the repository browser.