// Copyright 2012 Georg-August-Universität Göttingen, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package de.ugoe.cs.autoquest.plugin.uml; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.eclipse.emf.common.util.EList; import org.eclipse.uml2.uml.Association; import org.eclipse.uml2.uml.Class; import org.eclipse.uml2.uml.Comment; import org.eclipse.uml2.uml.Element; import org.eclipse.uml2.uml.Interaction; import org.eclipse.uml2.uml.Lifeline; import org.eclipse.uml2.uml.Message; import org.eclipse.uml2.uml.MessageOccurrenceSpecification; import org.eclipse.uml2.uml.Model; import org.eclipse.uml2.uml.Operation; import org.eclipse.uml2.uml.Region; import org.eclipse.uml2.uml.StateMachine; import org.eclipse.uml2.uml.Transition; import org.eclipse.uml2.uml.UMLPackage; import org.eclipse.uml2.uml.Vertex; import de.ugoe.cs.autoquest.eventcore.Event; import de.ugoe.cs.autoquest.plugin.http.eventcore.SOAPEventType; import de.ugoe.cs.autoquest.plugin.uml.eventcore.UMLTransitionType; /** *

* Utilities for working with UML. *

* * @author Steffen Herbold */ public class UMLUtils { /** *

* Creates a sequence of events with {@link UMLTransitionType} as event type from a given * sequence of events with the {@link SOAPEventType}, by matching the sequences to a state * machine. *

* * @param sequence * SOAP sequences * @param stateMachine * the state machine * @return create UML sequences */ public static List createUMLTransitionSequence(List sequence, StateMachine stateMachine) { List> matchingSequences = determineMatchingTransitionSequences(sequence, stateMachine); if (matchingSequences.size() != 1) { throw new RuntimeException("no unique match found; " + matchingSequences.size() + " matches"); } List umlEventSequence = new LinkedList<>(); for (Transition transition : matchingSequences.get(0)) { umlEventSequence.add(new Event(new UMLTransitionType(transition))); } return umlEventSequence; } /** *

* Uses a sequences of events with the {@link UMLTransitionType} to determine the transition * probabilities for the state machine. *

* * @param sequences * UML sequences * @param stateMachine * state machine to be converted to a usage profile */ public static void convertStateMachineToUsageProfile(Collection> sequences, StateMachine stateMachine) { // create state->outgoings hashmap Map> stateMap = new HashMap<>(); for (Region region : stateMachine.getRegions()) { for (Vertex state : region.getSubvertices()) { stateMap.put(state, new HashMap()); } } // create counters for each transition for (List sequence : sequences) { for (Event event : sequence) { if (event.getType() instanceof UMLTransitionType) { Transition transition = ((UMLTransitionType) event.getType()).getTransition(); Map transitionMap = stateMap.get(transition.getSource()); Integer value = transitionMap.get(transition); if (value == null) { value = 0; } transitionMap.put(transition, value + 1); } else { throw new RuntimeException( "Wrong event type. Only UMLTransitionType supported but was: " + event.getType().getClass().getName()); } } } // calculate probabilities for (Region region : stateMachine.getRegions()) { for (Vertex state : region.getSubvertices()) { Map transitionMap = stateMap.get(state); int totalCount = 0; for (Entry entry : transitionMap.entrySet()) { totalCount += entry.getValue(); } if (totalCount != 0) { for (Transition transition : state.getOutgoings()) { double prob = 0.0d; if (transitionMap.containsKey(transition)) { prob = ((double) transitionMap.get(transition)) / totalCount; } Comment comment = transition.createOwnedComment(); comment.setBody("" + prob); } } else { // system has never been in this state, all transitions equally likely int numOutgoings = state.getOutgoings().size(); for (Transition transition : state.getOutgoings()) { Comment comment = transition.createOwnedComment(); comment.setBody("" + (1.0d / numOutgoings)); } } } } } /** *

* Determines all matching {@link Transition} sequences in a state machine for a given sequence * of SOAP events. *

* * @param sequence * SOAP sequence * @param stateMachine * the state machine * @return all matching {@link Transition} sequences */ public static List> determineMatchingTransitionSequences(List sequence, StateMachine stateMachine) { EList regions = stateMachine.getRegions(); EList states = null; for (Region region : regions) { if (states == null) { states = region.getSubvertices(); } else { states.addAll(region.getSubvertices()); } } List allTransitions = new LinkedList<>(); for (Vertex state : states) { allTransitions.addAll(state.getOutgoings()); } List> matchingSequences = null; List currentTransitions = null; // first, we try to find a single unique transition that we can match using the method name for (Iterator eventIterator = sequence.iterator(); eventIterator.hasNext();) { Event event = eventIterator.next(); if (event.getType() instanceof SOAPEventType) { SOAPEventType eventType = (SOAPEventType) event.getType(); if (matchingSequences == null) { matchingSequences = new LinkedList<>(); List initialMatches = matchTransitions(allTransitions, eventType); for (Transition transition : initialMatches) { List candidate = new LinkedList<>(); candidate.add(transition); matchingSequences.add(candidate); } currentTransitions = initialMatches; } else { List> nextMatchingSequences = new LinkedList<>(); List nextCurrentTransitions = new LinkedList<>(); Iterator currentTransitionIterator = currentTransitions.iterator(); Iterator> currentMatchingSequencesIterator = matchingSequences.iterator(); while (currentTransitionIterator.hasNext()) { Transition currentTransition = currentTransitionIterator.next(); List currentMatch = currentMatchingSequencesIterator.next(); List matches = matchTransitions(currentTransition.getTarget().getOutgoings(), eventType); if (matches.isEmpty()) { throw new RuntimeException("no matches found"); } for (Transition matchingTransition : matches) { List candidate = new LinkedList<>(currentMatch); candidate.add(matchingTransition); nextMatchingSequences.add(candidate); nextCurrentTransitions.add(matchingTransition); } } matchingSequences = nextMatchingSequences; currentTransitions = nextCurrentTransitions; } } else { throw new RuntimeException( "Wrong event type. Only UMLTransitionType supported but was: " + event.getType().getClass().getName()); } } return matchingSequences; } /** *

* Extends a given model with an interaction that represents an observed sequence. *

* * @param sequence sequence that is added as sequence diagram * @param model UML model to which the interaction is added * @param interactionName name of the interaction */ public static void createInteractionFromEventSequence(List sequence, Model model, String interactionName) { Map classMap = new HashMap<>(); EList elements = model.getOwnedElements(); for (Element element : elements) { if (element instanceof Class) { classMap.put(((Class) element).getName(), (Class) element); } } Interaction interaction = (Interaction) model .createPackagedElement(interactionName, UMLPackage.Literals.INTERACTION); Lifeline userLifeline = interaction.createLifeline("user"); int i = 0; for (Event event : sequence) { if (event.getType() instanceof SOAPEventType) { SOAPEventType eventType = (SOAPEventType) event.getType(); String serviceName = eventType.getServiceName(); String methodName = eventType.getCalledMethod(); Class targetClass = classMap.get(serviceName); if (targetClass == null) { throw new RuntimeException( "Could not find class in the UML model that belong to the service: " + serviceName); } Lifeline targetLifeline = interaction.getLifeline(serviceName); if (targetLifeline == null) { targetLifeline = interaction.createLifeline(serviceName); Association association = (Association) model.getPackagedElement("user_" + serviceName, true, UMLPackage.Literals.ASSOCIATION, true); targetLifeline .setRepresents(association.getMemberEnd(serviceName, classMap.get(serviceName))); } MessageOccurrenceSpecification sendFragment = (MessageOccurrenceSpecification) interaction .createFragment(i + ":" + methodName + "_sendFragment", UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION); MessageOccurrenceSpecification recvFragment = (MessageOccurrenceSpecification) interaction .createFragment(i + ":" + methodName + "_recvFragment", UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION); sendFragment.setCovered(userLifeline); recvFragment.setCovered(targetLifeline); Message message = interaction.createMessage(methodName); message.setSignature(getOperationFromName(targetClass.getOperations(), methodName)); message.setSendEvent(sendFragment); message.setReceiveEvent(recvFragment); sendFragment.setMessage(message); recvFragment.setMessage(message); } else { throw new RuntimeException( "Wrong event type. Only SOAPEventType supported but was: " + event.getType().getClass().getName()); } i++; } } /** *

* Fetches an operation using only its name from a list of operations. Returns the first match * found or null if no match is found. *

* * @param operations * list of operations * @param name * name of the operation * @return first matching operation; null if no match is found */ private static Operation getOperationFromName(EList operations, String name) { if (name == null) { throw new IllegalArgumentException("name of the operation must not be null"); } if (operations != null) { for (Operation operation : operations) { if (operation.getName() != null && operation.getName().equals(name)) { return operation; } } } return null; } /** *

* Determines which transitions match a given {@link SOAPEventType}. *

* * @param transitions * the transitions * @param eventType * the SOAP event * @return matching transitions */ private static List matchTransitions(List transitions, SOAPEventType eventType) { List matching = new LinkedList<>(); for (Transition transition : transitions) { // String serviceName = transition.getName().split("\\.")[0]; // TODO service name check String methodName = transition.getName().split("\\.")[1]; if (methodName.equals(eventType.getCalledMethod())) { matching.add(transition); } } return matching; } }