// 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.io.StringWriter;
import java.util.ArrayList;
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 javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
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.CallEvent;
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.Relationship;
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.Trigger;
import org.eclipse.uml2.uml.Type;
import org.eclipse.uml2.uml.UMLPackage;
import org.eclipse.uml2.uml.Vertex;
import org.w3c.dom.Attr;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import de.ugoe.cs.autoquest.eventcore.Event;
import de.ugoe.cs.autoquest.plugin.http.SOAPUtils;
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.StringTools;
import de.ugoe.cs.util.console.Console;
/**
*
* Utilities for working with UML.
*
*
* @author Steffen Herbold
*/
public class UMLUtils {
/**
* In case a multiplicity is defined as *, this value defines the highest one that can be picked
*/
final static int MAX_MULTIPLICITY = 10;
final public static URI UML_PRIMITIVE_TYPES_URI = URI
.createURI("pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml", true);
/**
*
* 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) {
String serviceName = SOAPUtils.getServiceNameFromEvent(event);
String calledMethod = SOAPUtils.getCalledMethodFromEvent(event);
if (serviceName != null) {
Set curCalledMethods = calledMethods.get(serviceName);
if (curCalledMethods == null) {
curCalledMethods = new TreeSet<>();
calledMethods.put(serviceName, curCalledMethods);
}
curCalledMethods.add(calledMethod);
}
}
}
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} or {@link SimpleSOAPEventType}, 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)
{
System.out.println("foo");
List> matchingSequences =
determineMatchingTransitionSequences(sequence, stateMachine);
System.out.println(matchingSequences.size());
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();
System.out.println(event);
System.out.println(matchingSequences);
if (matchingSequences == null) {
matchingSequences = new LinkedList<>();
List initialMatches = matchTransitions(allTransitions, event);
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(), event);
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;
}
}
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");
// add UML Primitive types
// final UMLResource umlRes = (UMLResource)
// model.eResource().getResourceSet().getResource(UML_PRIMITIVE_TYPES_URI, true);
// model = (Model) umlRes.getContents().get(0);
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 = SOAPUtils.getServiceNameFromEvent(event);
String methodName = SOAPUtils.getCalledMethodFromEvent(event);
String clientName = SOAPUtils.getClientNameFromEvent(event);
String prefix = interactionName + ":" + i + ":" + methodName + "_";
// 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(prefix +
"callSendFragment", UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
MessageOccurrenceSpecification callRecvFragment =
(MessageOccurrenceSpecification) interaction.createFragment(prefix +
"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, prefix);
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(prefix + "replySendFragment",
UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
MessageOccurrenceSpecification replyRecvFragment =
(MessageOccurrenceSpecification) interaction
.createFragment(prefix + "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;
}
/**
*
* 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, Event event) {
String eventService = SOAPUtils.getServiceNameFromEvent(event);
String eventMethod = SOAPUtils.getCalledMethodFromEvent(event);
Map interfaceServiceMap =
createInterfaceServiceMap(transitions.get(0).getModel());
List matching = new LinkedList<>();
for (Transition transition : transitions) {
EList triggers = transition.getTriggers();
if (triggers.size() == 1) {
if (triggers.get(0).getEvent() instanceof CallEvent) {
CallEvent callEvent = (CallEvent) triggers.get(0).getEvent();
String transitionMethod = callEvent.getOperation().getName();
Interface intface = callEvent.getOperation().getInterface();
for (Relationship relationship : intface.getRelationships()) {
for (Element element : relationship.getRelatedElements()) {
if (element instanceof Component) {
}
}
}
String transitionService =
interfaceServiceMap.get(callEvent.getOperation().getInterface());
if (eventMethod.equals(transitionMethod) &&
eventService.equals(transitionService))
{
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 Map createInterfaceServiceMap(Model model) {
Map interfaceServiceMap = new HashMap<>();
final Profile utpProfile = model.getModel().getAppliedProfile("utp");
final Stereotype utpTestComponent = (Stereotype) utpProfile.getOwnedMember("TestComponent");
final Stereotype utpSUT = (Stereotype) utpProfile.getOwnedMember("SUT");
final Stereotype utpTestContext = (Stereotype) utpProfile.getOwnedMember("TestContext");
List testContexts =
fetchTestContextRecursively(model.getModel(), utpTestContext);
for (Component testContext : testContexts) {
for (Property property : testContext.getAllAttributes()) {
if (property.getAppliedStereotypes().contains(utpSUT) ||
property.getType().getAppliedStereotypes().contains(utpTestComponent))
{
for (Interface intface : getRealizedInterfacesFromProperty(property)) {
interfaceServiceMap.put(intface, property.getName());
}
}
}
}
return interfaceServiceMap;
}
private static void setMessageParameters(Message callMessage,
Operation calledOperation,
Event event,
String prefix)
{
org.w3c.dom.Element requestBody = SOAPUtils.getSoapRequestBodyFromEvent(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) {
List paramNodes =
getMatchingChildNode((DataType) param.getType(), requestBody);
for (org.w3c.dom.Element paramNode : paramNodes) {
InstanceSpecification instSpec =
createInstanceSpecification((DataType) param.getType(), event, prefix,
paramNode, "");
InstanceValue value =
(InstanceValue) argument
.createOperand(null, param.getType(),
UMLPackage.Literals.INSTANCE_VALUE);
value.setInstance(instSpec);
}
}
else if (param.getType() instanceof PrimitiveType) {
createOperandPrimitiveType(param, argument, requestBody);
}
}
else {
// set literalNull for out and return parameters
argument.createOperand(null, param.getType(), UMLPackage.Literals.LITERAL_NULL);
}
}
}
private static InstanceSpecification createInstanceSpecification(DataType type,
Event event,
String prefix,
org.w3c.dom.Element currentNode,
String path)
{
if ("".equals(path)) {
path = type.getName();
}
// System.out.println(path);
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 = SOAPUtils.getServiceNameFromEvent(event);
Package serviceInstSpecPkg = (Package) ubtInstSpecPkg.getOwnedMember(serviceName);
if (serviceInstSpecPkg == null) {
serviceInstSpecPkg =
(Package) ubtInstSpecPkg.createPackagedElement(serviceName,
UMLPackage.Literals.PACKAGE);
}
InstanceSpecification instSpec =
(InstanceSpecification) serviceInstSpecPkg.createPackagedElement(prefix + "instspec_" +
type.getName(), UMLPackage.Literals.INSTANCE_SPECIFICATION);
instSpec.getClassifiers().add(type);
for (Property prop : type.getAllAttributes()) {
if (prop.getType() instanceof PrimitiveType) {
createSlotPrimitiveType(instSpec, prop, currentNode, path);
}
else if (prop.getType() instanceof DataType) {
List attributeNodes = null;
int multiplicityChosen = 0;
if (currentNode != null) {
attributeNodes = getMatchingChildNode(prop, currentNode);
multiplicityChosen = attributeNodes.size();
}
if (multiplicityChosen == 0 && prop.getLower() > 0) {
if (currentNode != null) {
Console.traceln(Level.WARNING,
"required attribute not found in SOAP message: " + path +
"." + prop.getName());
Console
.traceln(Level.WARNING,
"setting default values for this attribute and all its children");
Console.traceln(Level.FINE, "XML structure of path:" + StringTools.ENDLINE +
SOAPUtils.getSerialization(currentNode));
}
multiplicityChosen = prop.getLower();
}
for (int i = 0; i < multiplicityChosen; i++) {
org.w3c.dom.Element attributeNode = null;
if (attributeNodes != null && !attributeNodes.isEmpty()) {
attributeNode = attributeNodes.get(i);
}
Slot slot = instSpec.createSlot();
slot.setDefiningFeature(prop);
InstanceValue value =
(InstanceValue) slot.createValue(prop.getName() + "_" + i, prop.getType(),
UMLPackage.Literals.INSTANCE_VALUE);
value.setInstance(createInstanceSpecification((DataType) prop.getType(), event,
prefix, attributeNode, path +
"." + prop.getName()));
}
}
else {
Console.traceln(Level.SEVERE, "property neither DataType nor PrimitiveType: " +
prop.getType());
// TODO abort?
}
}
return instSpec;
}
private static void createOperandPrimitiveType(Parameter param,
Expression argument,
org.w3c.dom.Element currentNode)
{
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
}
}
private static void createSlotPrimitiveType(InstanceSpecification instSpec,
Property prop,
org.w3c.dom.Element currentNode,
String path)
{
List attributeValues = getPrimitiveTypeValuesFromElement(prop, currentNode);
if (attributeValues.isEmpty()) {
if (prop.getLower() == 0) {
// ignoring optional attribute
return;
}
else {
if (currentNode != null) {
Console.traceln(Level.WARNING,
"required attribute not found in SOAP message: " + path + "." +
prop.getName());
Console.traceln(Level.WARNING, "setting default values for this attribute");
}
Console.traceln(Level.FINE, "XML structure of path:" + StringTools.ENDLINE +
SOAPUtils.getSerialization(currentNode));
attributeValues.add(null);
}
}
for (String attributeValue : attributeValues) {
Slot slot = instSpec.createSlot();
slot.setDefiningFeature(prop);
if ("String".equals(prop.getType().getName())) {
LiteralString value =
(LiteralString) slot.createValue(prop.getName(), null,
UMLPackage.Literals.LITERAL_STRING);
if (attributeValue != null) {
value.setValue(attributeValue);
}
else {
value.setValue("foobar");
}
}
else if ("Integer".equals(prop.getType().getName())) {
LiteralInteger value =
(LiteralInteger) slot.createValue(prop.getName(), null,
UMLPackage.Literals.LITERAL_INTEGER);
if (attributeValue != null) {
value.setValue(Integer.parseInt(attributeValue));
}
else {
value.setValue(42);
}
}
else if ("Boolean".equals(prop.getType().getName())) {
LiteralBoolean value =
(LiteralBoolean) slot.createValue(prop.getName(), null,
UMLPackage.Literals.LITERAL_BOOLEAN);
if (attributeValue != null) {
value.setValue(Boolean.parseBoolean(attributeValue));
}
else {
value.setValue(true);
}
}
else if ("Real".equals(prop.getType().getName())) {
LiteralReal value =
(LiteralReal) slot.createValue(prop.getName(), null,
UMLPackage.Literals.LITERAL_REAL);
if (attributeValue != null) {
value.setValue(Double.parseDouble(attributeValue));
}
else {
value.setValue(3.14); // TODO needs to be real value
}
}
else {
Console.traceln(Level.SEVERE, "could not create literal for primitive type: " +
prop.getType().getName());
// TODO abort?
}
}
}
// TODO comment
private static List getMatchingChildNode(Type type,
org.w3c.dom.Element parentNode)
{
return getMachingChildNode(type.getName(), parentNode);
}
// TODO comment
private static List getMatchingChildNode(Property prop,
org.w3c.dom.Element parentNode)
{
return getMachingChildNode(prop.getName(), parentNode);
}
// TODO comment
private static List getMachingChildNode(String typeNameRaw,
org.w3c.dom.Element parentNode)
{
List matchingNodes = new ArrayList<>();
Node parameterNode = null;
if (parentNode != null) {
NodeList parameterNodes = parentNode.getChildNodes();
String[] typeNameSplit = typeNameRaw.split(":");
String typeName = typeNameSplit[typeNameSplit.length - 1];
for (int i = 0; i < parameterNodes.getLength(); i++) {
parameterNode = parameterNodes.item(i);
if (parameterNode.getNodeType() == Node.ELEMENT_NODE) {
String[] parameterNodeSplit = parameterNode.getNodeName().split(":");
String parameterNodeName = parameterNodeSplit[parameterNodeSplit.length - 1];
if (typeName.equals(parameterNodeName)) {
matchingNodes.add((org.w3c.dom.Element) parameterNode);
}
}
}
/*
* if( !matchingSOAPFound) { Console.traceln(Level.WARNING,
* "could not look up name of parameter in SOAP request: " + typeName); if(
* elementCount==0 ) { Console.traceln(Level.INFO, "\tno parameters found"); } else {
* Console.traceln(Level.INFO, "\tparameters found:"); for( int i=0 ;
* i getPrimitiveTypeValuesFromElement(Property prop,
org.w3c.dom.Element currentNode)
{
List attributeValues = new LinkedList<>();
if (currentNode != null) {
// first check attributes of the node
Attr attribute = currentNode.getAttributeNode(prop.getName());
if (attribute != null) {
attributeValues.add(attribute.getValue());
}
else {
// now check elements
List elements = getMatchingChildNode(prop, currentNode);
for (org.w3c.dom.Element element : elements) {
attributeValues.add(element.getTextContent());
}
}
}
return attributeValues;
}
}