// 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.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.logging.Level; import java.util.Set; import java.util.TreeSet; import org.eclipse.emf.common.util.EList; import org.eclipse.uml2.uml.Activity; import org.eclipse.uml2.uml.ActivityEdge; import org.eclipse.uml2.uml.ActivityNode; import org.eclipse.uml2.uml.CallConcurrencyKind; import org.eclipse.uml2.uml.CallOperationAction; import org.eclipse.uml2.uml.Comment; import org.eclipse.uml2.uml.Component; import org.eclipse.uml2.uml.Connector; import org.eclipse.uml2.uml.ConnectorEnd; import org.eclipse.uml2.uml.DataType; import org.eclipse.uml2.uml.Element; import org.eclipse.uml2.uml.Expression; import org.eclipse.uml2.uml.InstanceSpecification; import org.eclipse.uml2.uml.InstanceValue; import org.eclipse.uml2.uml.Interaction; import org.eclipse.uml2.uml.InteractionFragment; import org.eclipse.uml2.uml.Interface; import org.eclipse.uml2.uml.Lifeline; import org.eclipse.uml2.uml.LiteralBoolean; import org.eclipse.uml2.uml.LiteralInteger; import org.eclipse.uml2.uml.LiteralReal; import org.eclipse.uml2.uml.LiteralString; import org.eclipse.uml2.uml.Message; import org.eclipse.uml2.uml.MessageOccurrenceSpecification; import org.eclipse.uml2.uml.MessageSort; import org.eclipse.uml2.uml.Model; import org.eclipse.uml2.uml.Operation; import org.eclipse.uml2.uml.Package; import org.eclipse.uml2.uml.Parameter; import org.eclipse.uml2.uml.ParameterDirectionKind; import org.eclipse.uml2.uml.Port; import org.eclipse.uml2.uml.PrimitiveType; import org.eclipse.uml2.uml.Profile; import org.eclipse.uml2.uml.Property; import org.eclipse.uml2.uml.Region; import org.eclipse.uml2.uml.Slot; import org.eclipse.uml2.uml.StateMachine; import org.eclipse.uml2.uml.Stereotype; 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.http.eventcore.SimpleSOAPEventType; import de.ugoe.cs.autoquest.plugin.uml.eventcore.UMLTransitionType; import de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess; import de.ugoe.cs.util.console.Console; /** *

* Utilities for working with UML. *

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

* Method for checking if the information in a usage journal can be mapped to the SUT model. In * case this is not possible, the violations are reported. *

* * @param sequences * sequences of the usage journal * @param model * SUT model that is validated * @param testContextName * name of the test context to be used; if null, the first test context found is used * @return number of violations */ public static int validateModelWithLog(Collection> sequences, Model model, String testContextName) { final Profile utpProfile = model.getAppliedProfile("utp"); final Stereotype utpTestComponent = (Stereotype) utpProfile.getOwnedMember("TestComponent"); final Stereotype utpSUT = (Stereotype) utpProfile.getOwnedMember("SUT"); final Stereotype utpTestContext = (Stereotype) utpProfile.getOwnedMember("TestContext"); int violationCount = 0; Component testContext = fetchTestContext(model, utpTestContext, testContextName); if (testContext == null) { violationCount++; if (testContextName == null) { Console.traceln(Level.SEVERE, "Could not find any TestContext in the model."); } else { Console.traceln(Level.SEVERE, "Could not find TestContext in the model: " + testContextName); } Console .traceln(Level.SEVERE, "Hint: Check if you have applied the TestContext stereotype correctly in the model."); Console.traceln(Level.SEVERE, "Aborting"); return violationCount; } // Create list of unique methods calls HashMap> calledMethods = new HashMap<>(); for (List sequence : sequences) { for (Event event : sequence) { if (event.getType() instanceof SOAPEventType) { SOAPEventType eventType = (SOAPEventType) event.getType(); Set curCalledMethods = calledMethods.get(eventType.getServiceName()); if (curCalledMethods == null) { curCalledMethods = new TreeSet<>(); calledMethods.put(eventType.getServiceName(), curCalledMethods); } curCalledMethods.add(eventType.getCalledMethod()); } } } Console.traceln(Level.INFO, "Found the following services and operations in the usage data: "); for (Entry> entry : calledMethods.entrySet()) { Console.traceln(Level.INFO, "\tService \"" + entry.getKey() + "\": "); for(String method : entry.getValue()) { Console.traceln(Level.INFO, "\t\t"+method); } } // fetch all SUTs and TestComponents HashMap properties = new HashMap<>(); for (Property property : testContext.getAllAttributes()) { if (property.getAppliedStereotypes().contains(utpSUT)) { properties.put(property.getName(), property); } else if (property.getType().getAppliedStereotypes().contains(utpTestComponent)) { properties.put(property.getName(), property); } } Console.traceln(Level.INFO, "Found the following services in the TestConfiguration:"); for (Entry entry : properties.entrySet()) { Console.traceln(Level.INFO, "\t" + entry.getKey()); } for (Entry> entry : calledMethods.entrySet()) { String serviceName = entry.getKey(); Console.traceln(Level.INFO, "Checking service: " + serviceName); Set methodNames = entry.getValue(); Property property = properties.get(serviceName); if (property == null) { violationCount++; Console.traceln(Level.SEVERE, "\tCould not find property for service: " + serviceName); Console .traceln(Level.SEVERE, "\tHint: Check service name map and/or model if the service is present and spelled correctly."); Console .traceln(Level.SEVERE, "\tHint: Check if the SUT/TestComponent stereotype has been applied correctly in this TestContext."); } else { Set interfaces = getRealizedInterfacesFromProperty(property); if (interfaces.isEmpty()) { violationCount++; Console .traceln(Level.SEVERE, "\tCould not find any interfaces implementing the property for service: " + serviceName); Console .traceln(Level.SEVERE, "\tHint: Check if the property correctly realizes the interfaces in the model."); } else { Console.traceln(Level.INFO, "\tFound the following realized interfaces for the service \"" + serviceName + "\": "); for( Interface intface : interfaces ) { Console.traceln(Level.INFO, "\t" + intface.getName()); for( Operation operation : intface.getAllOperations() ) { Console.traceln(Level.INFO, "\t\t" + operation.getName()); } } for (String methodName : methodNames) { boolean methodFound = false; for (Interface intface : interfaces) { if (getOperationFromName(intface.getOperations(), methodName) != null) { // interface found Console.traceln(Level.INFO, "\tMethod " + methodName + " found in interface " + intface.getName()); methodFound = true; } } if (!methodFound) { violationCount++; Console.traceln(Level.SEVERE, "\tCould not find operation: " + methodName); } } } } } return violationCount; } /** *

* 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 * @param testContextName * Name of the test context that should be used. If this value is null, the first * test context found is used. */ public static void createInteractionFromEventSequence(List sequence, Model model, String interactionName, String testContextName) { final Profile utpProfile = model.getAppliedProfile("utp"); final Stereotype utpTestCase = (Stereotype) utpProfile.getOwnedMember("TestCase"); final Stereotype utpTestComponent = (Stereotype) utpProfile.getOwnedMember("TestComponent"); final Stereotype utpSUT = (Stereotype) utpProfile.getOwnedMember("SUT"); final Stereotype utpTestContext = (Stereotype) utpProfile.getOwnedMember("TestContext"); Component testContext = fetchTestContext(model, utpTestContext, testContextName); if (testContext == null) { throw new RuntimeException("Could not find any test context in the model"); } Operation operation = testContext.createOwnedOperation(interactionName, null, null); operation.applyStereotype(utpTestCase); Interaction interaction = (Interaction) testContext.createPackagedElement(interactionName + "_Impl", UMLPackage.Literals.INTERACTION); operation.getMethods().add(interaction); // create lifelines Lifeline userLifeline = null; for (Property property : testContext.getAllAttributes()) { if (property.getAppliedStereotypes().contains(utpSUT)) { String serviceName = property.getName(); Lifeline targetLifeline = interaction.createLifeline(serviceName); targetLifeline.setRepresents(property); } else if (property.getType().getAppliedStereotypes().contains(utpTestComponent)) { if (userLifeline != null) { throw new RuntimeException( "TestContext must only have one TestComponent for the application of usage-based testing."); } userLifeline = interaction.createLifeline(property.getName()); userLifeline.setRepresents(property); } } if (userLifeline == null) { throw new RuntimeException("No TestComponent found, could not create user lifeline."); } if (interaction.getLifelines().size() < 2) { throw new RuntimeException("Fewer than two lifelines created. No SUT found."); } int i = 0; for (Event event : sequence) { if (!(event.equals(Event.STARTEVENT) || event.equals(Event.ENDEVENT))) { String serviceName = getServiceNameFromEvent(event); String methodName = getCalledMethodFromEvent(event); String clientName = getClientNameFromEvent(event); // determine lifelines Lifeline msgTargetLifeline; Lifeline msgSourceLifeline; msgSourceLifeline = interaction.getLifeline(clientName); msgTargetLifeline = interaction.getLifeline(serviceName); if (msgSourceLifeline == null) { throw new RuntimeException( "Error creating message: could not determine source lifeline for component: " + clientName); } if (msgTargetLifeline == null) { throw new RuntimeException( "Error creating message: could not determine target lifeline for component: " + serviceName); } // determine correct target interface Set targetInterfaces = getRealizedInterfacesFromProperty((Property) msgTargetLifeline.getRepresents()); if (targetInterfaces.isEmpty()) { throw new RuntimeException("no interface associated with the property " + msgTargetLifeline.getRepresents().getName()); } Interface targetInterface = null; for (Interface intface : targetInterfaces) { if (getOperationFromName(intface.getOperations(), methodName) != null) { // interface found targetInterface = intface; break; } } if (targetInterface == null) { StringBuilder errStrBuilder = new StringBuilder(); errStrBuilder .append("Error creating message: operation not found in the implementing interfaces ("); Iterator iter = targetInterfaces.iterator(); while (iter.hasNext()) { String interfaceName = iter.next().getName(); errStrBuilder.append(interfaceName); if (iter.hasNext()) { errStrBuilder.append(","); } else { errStrBuilder.append("): " + methodName); } } throw new RuntimeException(errStrBuilder.toString()); } Operation calledOperation = getOperationFromName(targetInterface.getOperations(), methodName); // setup for both SYNCH and ASYNCH calls MessageOccurrenceSpecification callSendFragment = (MessageOccurrenceSpecification) interaction .createFragment(i + ":" + methodName + "_callSendFragment", UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION); MessageOccurrenceSpecification callRecvFragment = (MessageOccurrenceSpecification) interaction .createFragment(i + ":" + methodName + "_callRecvFragment", UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION); callSendFragment.setCovered(msgSourceLifeline); callRecvFragment.setCovered(msgTargetLifeline); // create call Message callMessage = interaction.createMessage(methodName); callMessage.setSignature(calledOperation); setMessageParameters(callMessage, calledOperation, event); callMessage.setConnector(inferConnector(msgSourceLifeline, msgTargetLifeline)); callMessage.setSendEvent(callSendFragment); callMessage.setReceiveEvent(callRecvFragment); callSendFragment.setMessage(callMessage); callRecvFragment.setMessage(callMessage); boolean asynch = false; if (calledOperation.getConcurrency() == CallConcurrencyKind.CONCURRENT_LITERAL) { asynch = true; } if (asynch) { // Create ASYNCH call callMessage.setMessageSort(MessageSort.ASYNCH_CALL_LITERAL); } else { // SYNCH call callMessage.setMessageSort(MessageSort.SYNCH_CALL_LITERAL); // setup reply and behavior execution specifications MessageOccurrenceSpecification replySendFragment = (MessageOccurrenceSpecification) interaction .createFragment(i + ":" + methodName + "_replySendFragment", UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION); MessageOccurrenceSpecification replyRecvFragment = (MessageOccurrenceSpecification) interaction .createFragment(i + ":" + methodName + "_replyRecvFragment", UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION); replySendFragment.setCovered(msgTargetLifeline); replyRecvFragment.setCovered(msgSourceLifeline); /* * BehaviorExecutionSpecification sourceBehaviorExecutionSpecification = * (BehaviorExecutionSpecification) interaction .createFragment(":" + methodName * + "_sourceBhvExecSpec", * UMLPackage.Literals.BEHAVIOR_EXECUTION_SPECIFICATION); * BehaviorExecutionSpecification targetBehaviorExecutionSpecification = * (BehaviorExecutionSpecification) interaction .createFragment(":" + methodName * + "_targetBhvExecSpec", * UMLPackage.Literals.BEHAVIOR_EXECUTION_SPECIFICATION); * * sourceBehaviorExecutionSpecification.setStart(callSendFragment); * sourceBehaviorExecutionSpecification.setFinish(replyRecvFragment); * targetBehaviorExecutionSpecification.setStart(callRecvFragment); * targetBehaviorExecutionSpecification.setFinish(replySendFragment); */ // create reply Message replyMessage = interaction.createMessage(methodName + "_reply"); replyMessage.setMessageSort(MessageSort.REPLY_LITERAL); replyMessage.setSignature(calledOperation); replyMessage.setSendEvent(replySendFragment); replyMessage.setReceiveEvent(replyRecvFragment); replySendFragment.setMessage(replyMessage); replyRecvFragment.setMessage(replyMessage); } i++; } } } /** *

* Calculates the usage score of an interaction as the logsum of the event probabilities * multiplied with the length of the interaction. *

* * @param interaction * interaction for which the score is calculated * @param usageProfile * usage profile used for the calculation * @return calculated usage score */ public static double calculateUsageScore(Interaction interaction, IStochasticProcess usageProfile) { double usageScore = 0.0d; EList interactionFragments = interaction.getFragments(); List eventSequence = new LinkedList<>(); eventSequence.add(Event.STARTEVENT); for (InteractionFragment interactionFragment : interactionFragments) { if (interactionFragment.getName() != null && interactionFragment.getName().endsWith("_recvFragment")) // TODO must be more // generic { String serviceName = interactionFragment.getCovereds().get(0).getRepresents().getName().split("_")[0]; String methodName = "UNKNOWN"; if (interactionFragment instanceof MessageOccurrenceSpecification) { methodName = ((MessageOccurrenceSpecification) interactionFragment).getMessage() .getName(); } eventSequence.add(new Event(new SimpleSOAPEventType(methodName, serviceName,""))); // TODO add client name } } eventSequence.add(Event.ENDEVENT); double prob = usageProfile.getLogSum(eventSequence); usageScore = eventSequence.size() * prob; return usageScore; } /** *

* Extends the given model with an activity for usage-based scheduling of the test cases. *

* * @param model * model to be extended * @param usageProfile * usage profile used as foundation */ public static void createScheduling(Model model, IStochasticProcess usageProfile, String testContextName) { final Profile utpProfile = model.getAppliedProfile("utp"); final Stereotype utpTestCase = (Stereotype) utpProfile.getOwnedMember("TestCase"); final Stereotype utpTestContext = (Stereotype) utpProfile.getOwnedMember("TestContext"); Component testContext = fetchTestContext(model, utpTestContext, testContextName); if (testContext == null) { throw new RuntimeException("Could not find any test context in the model"); } Map usageScoreMapUnsorted = new HashMap<>(); // first, we determine all test cases and calculate their usage scores for (Operation operation : testContext.getAllOperations()) { if (operation.getAppliedStereotypes().contains(utpTestCase)) { Interaction interaction = (Interaction) operation.getMethods().get(0); usageScoreMapUnsorted .put(operation, calculateUsageScore(interaction, usageProfile)); } } Map usageScoreMapSorted = sortByValue(usageScoreMapUnsorted); // now we create the scheduling Activity schedulingActivity = (Activity) testContext.createOwnedBehavior("UsageBasedScheduling", UMLPackage.Literals.ACTIVITY); testContext.setClassifierBehavior(schedulingActivity); ActivityNode startNode = schedulingActivity.createOwnedNode("final", UMLPackage.Literals.INITIAL_NODE); ActivityNode finalNode = schedulingActivity.createOwnedNode("final", UMLPackage.Literals.ACTIVITY_FINAL_NODE); ActivityNode currentOperationNode = startNode; for (Entry entry : usageScoreMapSorted.entrySet()) { Operation operation = entry.getKey(); CallOperationAction nextOperationNode = (CallOperationAction) schedulingActivity .createOwnedNode(operation.getName(), UMLPackage.Literals.CALL_OPERATION_ACTION); nextOperationNode.setOperation(operation); ActivityEdge edge = schedulingActivity.createEdge(currentOperationNode.getName() + "_to_" + nextOperationNode.getName(), UMLPackage.Literals.CONTROL_FLOW); edge.setSource(currentOperationNode); edge.setTarget(nextOperationNode); currentOperationNode = nextOperationNode; } ActivityEdge edge = schedulingActivity .createEdge(currentOperationNode.getName() + "_to_" + finalNode.getName(), UMLPackage.Literals.CONTROL_FLOW); edge.setSource(currentOperationNode); edge.setTarget(finalNode); } /** * From * http://stackoverflow.com/questions/109383/how-to-sort-a-mapkey-value-on-the-values-in-java * and adapted to do an inverse sorting */ public static > Map sortByValue(Map map) { List> list = new LinkedList<>(map.entrySet()); Collections.sort(list, new Comparator>() { @Override public int compare(Map.Entry o1, Map.Entry o2) { return -1 * (o1.getValue()).compareTo(o2.getValue()); } }); Map result = new LinkedHashMap<>(); for (Map.Entry entry : list) { result.put(entry.getKey(), entry.getValue()); } return result; } /** *

* Helper function to get the name of a service from a SOAP event. *

* * @param event * event for which the service name is retrieved * @return service name */ protected static String getServiceNameFromEvent(Event event) { if (event.getType() instanceof SOAPEventType) { return ((SOAPEventType) event.getType()).getServiceName(); } else if (event.getType() instanceof SimpleSOAPEventType) { return ((SimpleSOAPEventType) event.getType()).getServiceName(); } else { throw new RuntimeException( "Wrong event type. Only SOAPEventType and SimpleSOAPEventType supported but was: " + event.getType().getClass().getName()); } } /** * *

* Helper function to get the called method from a SOAP event *

* * @param event * event for which the called method is retrieved * @return called method */ private static String getCalledMethodFromEvent(Event event) { if (event.getType() instanceof SOAPEventType) { return ((SOAPEventType) event.getType()).getCalledMethod(); } else if (event.getType() instanceof SimpleSOAPEventType) { return ((SimpleSOAPEventType) event.getType()).getCalledMethod(); } else { throw new RuntimeException( "Wrong event type. Only SOAPEventType and SimpleSOAPEventType supported but was: " + event.getType().getClass().getName()); } } /** *

* Helper function to get the name of a client from a SOAP event. *

* * @param event * event for which the client name is retrieved * @return service name */ protected static String getClientNameFromEvent(Event event) { if (event.getType() instanceof SOAPEventType) { return ((SOAPEventType) event.getType()).getClientName(); } else if (event.getType() instanceof SimpleSOAPEventType) { return ((SimpleSOAPEventType) event.getType()).getClientName(); } else { throw new RuntimeException( "Wrong event type. Only SOAPEventType and SimpleSOAPEventType supported but was: " + event.getType().getClass().getName()); } } /** *

* 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; } private static Set getRealizedInterfacesFromProperty(Property property) { return getRealizedInterfaceFromComponent((Component) property.getType()); } private static Set getRealizedInterfaceFromComponent(Component comp) { Set interfaces = new HashSet<>(); // Interface myInterface = null; for (Property property : comp.getAttributes()) { if (property instanceof Port) { Port port = (Port) property; if (!port.isConjugated()) { interfaces.addAll(port.getProvideds()); } } } return interfaces; } private static Component fetchTestContext(Package pkg, Stereotype utpTestContext, String testContextName) { List testContexts = fetchTestContextRecursively(pkg, utpTestContext); if (testContexts.isEmpty()) { return null; } if (testContextName != null) { for (Component testContext : testContexts) { if (testContextName.equals(testContext.getName())) { return testContext; } } return null; } else { return testContexts.get(0); } } private static List fetchTestContextRecursively(Package pkg, Stereotype utpTestContext) { List testContexts = new LinkedList<>(); for (Element element : pkg.getOwnedElements()) { if (element instanceof Package) { testContexts.addAll(fetchTestContextRecursively((Package) element, utpTestContext)); } if (element instanceof Component && element.getAppliedStereotypes().contains(utpTestContext)) { testContexts.add((Component) element); } } return testContexts; } /** *

* Infers connector between two lifelines. TODO: currently assumes only one connector between * two lifelines possible. I need to make sure this assumption is valid. *

* * @param userAttributes * @param targetAttributes */ private static Connector inferConnector(Lifeline msgSourceLifeline, Lifeline msgTargetLifeline) { EList userAttributes = ((Component) msgSourceLifeline.getRepresents().getType()).getAttributes(); EList targetAttributes = ((Component) msgTargetLifeline.getRepresents().getType()).getAttributes(); for (Property userAttribute : userAttributes) { if (userAttribute instanceof Port) { EList userEnds = ((Port) userAttribute).getEnds(); for (ConnectorEnd userEnd : userEnds) { Connector userConnector = (Connector) userEnd.eContainer(); for (Property targetAttribute : targetAttributes) { if (targetAttribute instanceof Port) { EList targetEnds = ((Port) targetAttribute).getEnds(); for (ConnectorEnd targetEnd : targetEnds) { Connector targetConnector = (Connector) targetEnd.eContainer(); if (targetConnector == userConnector) { return targetConnector; } } } } } } } return null; } private static void setMessageParameters(Message callMessage, Operation calledOperation, Event event) { // Set parameters of operation for( Parameter param : calledOperation.getOwnedParameters() ) { Expression argument = (Expression) callMessage.createArgument(param.getName(), param.getType(), UMLPackage.Literals.EXPRESSION); if( param.getDirection()==ParameterDirectionKind.IN_LITERAL || param.getDirection()==ParameterDirectionKind.INOUT_LITERAL) { if( param.getType() instanceof DataType ) { InstanceSpecification instSpec = createInstanceSpecification((DataType) param.getType(), event); InstanceValue value = (InstanceValue) argument.createOperand(null, param.getType(), UMLPackage.Literals.INSTANCE_VALUE); value.setInstance(instSpec); } else if( param.getType() instanceof PrimitiveType ) { if( "String".equals(param.getType().getName()) ) { LiteralString spec = (LiteralString) argument.createOperand(param.getName(), null, UMLPackage.Literals.LITERAL_STRING); spec.setValue("foobar"); // TODO needs to be real value } else if( "Integer".equals(param.getType().getName()) ) { LiteralInteger spec = (LiteralInteger) argument.createOperand(param.getName(), null, UMLPackage.Literals.LITERAL_INTEGER); spec.setValue(42); // TODO needs to be real value } else if( "Boolean".equals(param.getType().getName()) ) { LiteralBoolean spec = (LiteralBoolean) argument.createOperand(param.getName(), null, UMLPackage.Literals.LITERAL_BOOLEAN); spec.setValue(true); // TODO needs to be real value } else if( "Real".equals(param.getType().getName()) ) { LiteralReal spec = (LiteralReal) argument.createOperand(param.getName(), null, UMLPackage.Literals.LITERAL_REAL); spec.setValue(3.14); // TODO needs to be real value } } } else { // set literalNull for out and return parameters argument.createOperand(null, param.getType(), UMLPackage.Literals.LITERAL_NULL); } } } public static InstanceSpecification createInstanceSpecification(DataType type, Event event) { String pkgUBTInstSpecs = "UBT_InstanceSpecifications"; Model model = type.getModel(); Package ubtInstSpecPkg = (Package) model.getOwnedMember(pkgUBTInstSpecs); if( ubtInstSpecPkg==null ) { ubtInstSpecPkg = (Package) type.getModel().createPackagedElement(pkgUBTInstSpecs, UMLPackage.Literals.PACKAGE); } String serviceName = ((SOAPEventType) event.getType()).getServiceName(); Package serviceInstSpecPkg = (Package) ubtInstSpecPkg.getOwnedMember(serviceName); if( serviceInstSpecPkg==null ) { serviceInstSpecPkg = (Package) ubtInstSpecPkg.createPackagedElement(serviceName, UMLPackage.Literals.PACKAGE); } InstanceSpecification instSpec = (InstanceSpecification) serviceInstSpecPkg.createPackagedElement("instspec_"+type.getName(), UMLPackage.Literals.INSTANCE_SPECIFICATION); instSpec.getClassifiers().add(type); for( Property prop : type.getAllAttributes() ) { // TODO handle multiplicity //int lowerBound = prop.getLower(); //int upperBound = prop.getUpper(); Slot slot = instSpec.createSlot(); slot.setDefiningFeature(prop); if( prop.getType() instanceof PrimitiveType ) { if( "String".equals(prop.getType().getName()) ) { LiteralString value = (LiteralString) slot.createValue(prop.getName(), null, UMLPackage.Literals.LITERAL_STRING); value.setValue("foobar"); // TODO needs to be real value } else if( "Integer".equals(prop.getType().getName()) ) { LiteralInteger value = (LiteralInteger) slot.createValue(prop.getName(), null, UMLPackage.Literals.LITERAL_INTEGER); value.setValue(42); // TODO needs to be real value } else if( "Boolean".equals(prop.getType().getName()) ) { LiteralBoolean value = (LiteralBoolean) slot.createValue(prop.getName(), null, UMLPackage.Literals.LITERAL_BOOLEAN); value.setValue(true); // TODO needs to be real value } else if( "Real".equals(prop.getType().getName()) ) { LiteralReal value = (LiteralReal) slot.createValue(prop.getName(), null, UMLPackage.Literals.LITERAL_REAL); value.setValue(3.14); // TODO needs to be real value } } else if( prop.getType() instanceof DataType ) { InstanceValue value = (InstanceValue) slot.createValue(prop.getName(), prop.getType(), UMLPackage.Literals.INSTANCE_VALUE); value.setInstance(createInstanceSpecification((DataType) prop.getType(), event)); } } return instSpec; } }