// 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.Set;
import java.util.TreeSet;
import java.util.logging.Level;
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.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.Element;
import org.eclipse.uml2.uml.Expression;
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.Property;
import org.eclipse.uml2.uml.Region;
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.UMLPackage;
import org.eclipse.uml2.uml.Vertex;
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.http.eventcore.SimpleSOAPEventType.CallType;
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;
/**
*
* 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)
{
int violationCount = 0;
Component testContext = fetchTestContext(model, 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 : fetchAllSUTProperties(testContext)) {
properties.put(property.getName(), property);
}
for (Property property : fetchAllTestComponentProperties(testContext)) {
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)
{
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 (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.
* @param useRandomMsgBodies
* defines is random request bodies are used or the body of the associated event
*/
public static List createInteractionFromEventSequence(Collection> sequences,
Model model,
String interactionName,
String testContextName,
boolean useRandomMsgBodies)
{
List interactions = new LinkedList<>();
UMLInteractionCreator interactionCreator = new UMLInteractionCreator(model, testContextName, useRandomMsgBodies);
int i=0;
for( List sequence : sequences ) {
try {
interactions.add(interactionCreator.createInteraction(sequence, interactionName+"_"+i));
i++;
} catch(Exception e) {
Console.traceln(Level.SEVERE, "failure for " + interactionName+"_"+i + ": " + e.getMessage());
}
}
return interactions;
}
/**
*
* 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 instanceof MessageOccurrenceSpecification) {
Message message =
((MessageOccurrenceSpecification) interactionFragment).getMessage();
// if (message.getReceiveEvent().equals(interactionFragment) &&
// isCallMessage(message))
if (message.getReceiveEvent().equals(interactionFragment)) {
String clientName;
String serviceName;
String methodName = message.getSignature().getName();
CallType callType;
if (isCallMessage(message)) {
clientName =
((MessageOccurrenceSpecification) message.getSendEvent()).getCovereds()
.get(0).getName();
serviceName =
((MessageOccurrenceSpecification) message.getReceiveEvent())
.getCovereds().get(0).getName();
callType = CallType.REQUEST;
}
else {
clientName =
((MessageOccurrenceSpecification) message.getReceiveEvent())
.getCovereds().get(0).getName();
serviceName =
((MessageOccurrenceSpecification) message.getSendEvent()).getCovereds()
.get(0).getName();
callType = CallType.RESPONSE;
}
eventSequence.add(new Event(new SimpleSOAPEventType(methodName, serviceName,
clientName, null, null,
callType)));
}
}
}
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 Component testContext = fetchTestContext(model, 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
final Stereotype utpTestCase = UTPUtils.getTestCaseStereotype(model);
for (Operation operation : testContext.getAllOperations()) {
if (operation.getAppliedStereotypes().contains(utpTestCase) &&
!operation.getMethods().isEmpty())
{
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);
}
/**
*
* 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
*/
public 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();
String transitionService =
interfaceServiceMap.get(callEvent.getOperation().getInterface());
if (eventMethod.equals(transitionMethod) &&
eventService.equals(transitionService))
{
matching.add(transition);
}
}
}
else {
throw new RuntimeException(
"only one trigger of type CallEvent per transition allowed: " +
transition.getName());
}
}
return matching;
}
/**
*
* Fetches all realized interfaces from the type of a UML {@link Property} (i.e.,
* property.getType()). If no interfaces are realized, an empty set is returned.
*
*
* @param property
* property, of the whose realized interfaces of the type are determined
* @return realized interfaces
*/
public static Set getRealizedInterfacesFromProperty(Property property) {
return getRealizedInterfaceFromComponent((Component) property.getType());
}
/**
*
* Fetches all realized interfaces from a UML {@link Component}. If no interfaces are realized,
* an empty set is returned.
*
*
* @param comp
* component whose realized interfaces are determined
* @return realized interfaces
*/
public static Set getRealizedInterfaceFromComponent(Component component) {
Set interfaces = new HashSet<>();
// Interface myInterface = null;
for (Property property : component.getAllAttributes()) {
if (property instanceof Port) {
Port port = (Port) property;
if (!port.isConjugated()) {
interfaces.addAll(port.getProvideds());
}
}
}
return interfaces;
}
/**
*
* Determines searches within a {@link Package} for a component to which the UTP TestContext
* stereotype is applied.
*
* - If no testContextName is provided, the first test context found is returned.
* - In case no test context is found, null is returned.
*
*
* @param pkg
* package where the test context is being locked for
* @param testContextName
* name of the test context; in case no test name is specified, use null and not the
* empty String.
* @return {@link Component} to which the TestContext stereotype is applied
*/
public static Component fetchTestContext(final Package pkg, final String testContextName) {
List testContexts = fetchAllTestContexts(pkg);
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);
}
}
/**
*
* Retrieves all UML {@link Component}s to which the UTP TestContext stereotype is applied from
* a package. This method calls itself recursively to include all components contained in
* sub-packages.
*
*
* In case no test context is found, an empty list is returned.
*
*
* @param pkg
* package from which the test contexts are retrieved
* @return {@link List} of test contexts
*/
public static List fetchAllTestContexts(final Package pkg) {
final Stereotype utpTestContext = UTPUtils.getTestContextStereotype(pkg.getModel());
final List testContexts = new LinkedList<>();
for (Element element : pkg.getOwnedElements()) {
if (element instanceof Package) {
testContexts.addAll(fetchAllTestContexts((Package) element));
}
if (element instanceof Component &&
element.getAppliedStereotypes().contains(utpTestContext))
{
testContexts.add((Component) element);
}
}
return testContexts;
}
/**
*
* Retrieves all properties that represent a UTP TestComponent from a test context.
*
*
* @param testContext
* test context from which the properties are retrieved
* @return properties that represent test components
*/
public static Set fetchAllTestComponentProperties(final Component testContext) {
// fetch all SUTs and TestComponents
final Stereotype utpTestComponent =
UTPUtils.getTestComponentStereotype(testContext.getModel());
final Set properties = new HashSet<>();
for (Property property : testContext.getAllAttributes()) {
// TODO once all models are update the first condition should be removed
if (property.getType().getAppliedStereotypes().contains(utpTestComponent) ||
property.getType().getApplicableStereotypes().contains(utpTestComponent)) {
properties.add(property);
}
}
return properties;
}
/**
*
* Retrieves all properties that represent a UTP SUT from a test context.
*
*
* @param testContext
* test context from which the properties are retrieved
* @return properties that represent the SUTs
*/
public static Set fetchAllSUTProperties(final Component testContext) {
// fetch all SUTs and TestComponents
final Stereotype utpSUT = UTPUtils.getSUTStereotype(testContext.getModel());
final Set properties = new HashSet<>();
for (Property property : testContext.getAllAttributes()) {
if (property.getAppliedStereotypes().contains(utpSUT)) {
properties.add(property);
}
}
return properties;
}
/**
*
* Infers connector between two lifelines.
*
*
* @param msgSourceLifeline
* source lifeline of the message
* @param targetAttributes
* target lifeline of the message
*/
public static Connector inferConnector(Lifeline msgSourceLifeline,
Lifeline msgTargetLifeline,
Interface targetInterface)
{
EList userAttributes =
((Component) msgSourceLifeline.getRepresents().getType()).getAllAttributes();
EList targetAttributes =
((Component) msgTargetLifeline.getRepresents().getType()).getAllAttributes();
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) {
if (((Port) targetAttribute).getProvideds().contains(targetInterface)) {
EList targetEnds = ((Port) targetAttribute).getEnds();
for (ConnectorEnd targetEnd : targetEnds) {
Connector targetConnector = (Connector) targetEnd.eContainer();
if (targetConnector == userConnector) {
return targetConnector;
}
}
}
}
}
}
}
}
return null;
}
/**
*
* Creates a map that maps the interfaces to the properties, i.e., services that they are
* represented by.
*
*
* TODO: currently assumes that each interfaces is only realized by one property
*
*
* @param model
* model for which the interface->service map is created
* @return the map
*/
private static Map createInterfaceServiceMap(Model model) {
Map interfaceServiceMap = new HashMap<>();
List testContexts = fetchAllTestContexts(model.getModel());
for (Component testContext : testContexts) {
for (Property property : fetchAllSUTProperties(testContext)) {
for (Interface intface : getRealizedInterfacesFromProperty(property)) {
interfaceServiceMap.put(intface, property.getName());
}
}
for (Property property : fetchAllTestComponentProperties(testContext)) {
for (Interface intface : getRealizedInterfacesFromProperty(property)) {
interfaceServiceMap.put(intface, property.getName());
}
}
}
return interfaceServiceMap;
}
/**
*
* Creates an operand that defines a {@link PrimitiveType}.
*
*
* TODO: Currently does nothing in case of multiplicity 0. I am not sure if, in that case, one
* has to define LiteralNull instead.
*
*
* @param param
* parameter for which the operand is created
* @param argument
* argument to which the operand is added
* @param currentNode
* DOM node from which is value for the operand is inferred
* @param path
* used for warnings and debug information
*/
@SuppressWarnings("unused")
@Deprecated
private static void createOperandPrimitiveType(Parameter param,
Expression argument,
org.w3c.dom.Element currentNode,
String path)
{
List attributeValues = SOAPUtils.getValuesFromElement(param.getName(), currentNode);
if (attributeValues.isEmpty()) {
if (param.getLower() == 0) {
// ignoring optional attribute
return;
}
else {
if (currentNode != null) {
Console.traceln(Level.WARNING,
"required attribute not found in SOAP message: " + path + "." +
param.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) {
if ("String".equals(param.getType().getName())) {
LiteralString spec =
(LiteralString) argument.createOperand(param.getName(), null,
UMLPackage.Literals.LITERAL_STRING);
if (attributeValue != null) {
spec.setValue(attributeValue);
}
else {
spec.setValue("foobar");
}
}
else if ("Integer".equals(param.getType().getName())) {
LiteralInteger spec =
(LiteralInteger) argument.createOperand(param.getName(), null,
UMLPackage.Literals.LITERAL_INTEGER);
if (attributeValue != null) {
spec.setValue(Integer.parseInt(attributeValue));
}
else {
spec.setValue(42);
}
}
else if ("Boolean".equals(param.getType().getName())) {
LiteralBoolean spec =
(LiteralBoolean) argument.createOperand(param.getName(), null,
UMLPackage.Literals.LITERAL_BOOLEAN);
if (attributeValue != null) {
spec.setValue(Boolean.parseBoolean(attributeValue));
}
else {
spec.setValue(true);
}
}
else if ("Real".equals(param.getType().getName())) {
LiteralReal spec =
(LiteralReal) argument.createOperand(param.getName(), null,
UMLPackage.Literals.LITERAL_REAL);
if (attributeValue != null) {
spec.setValue(Double.parseDouble(attributeValue));
}
else {
spec.setValue(3.14);
}
}
}
}
/**
*
* Checks if a parameter has the direction IN or INOUT
*
*
* @param parameter
* parameter that is checked
* @return true if the direction is IN or INOUT; false otherwise
*/
public static boolean isInParameter(Parameter parameter) {
return parameter.getDirection() == ParameterDirectionKind.IN_LITERAL ||
parameter.getDirection() == ParameterDirectionKind.INOUT_LITERAL;
}
/**
*
* Checks if a parameter has the direction RETURN, OUT or INOUT
*
*
* @param parameter
* parameter that is checked
* @return true if the direction is RETURN, OUT, or INOUT; false otherwise
*/
public static boolean isOutParameter(Parameter parameter) {
return parameter.getDirection() == ParameterDirectionKind.RETURN_LITERAL ||
parameter.getDirection() == ParameterDirectionKind.OUT_LITERAL ||
parameter.getDirection() == ParameterDirectionKind.INOUT_LITERAL;
}
/**
*
* Checks if the {@link MessageSort} of a message is a call message, i.e., ASYNCH_CALL or
* SYNCH_CALL.
*
*
* @param message
* message that is checked
* @return true if the message is a call message; false otherwise
*/
public static boolean isCallMessage(Message message) {
if (message == null) {
return false;
}
MessageSort msgSort = message.getMessageSort();
return msgSort == MessageSort.ASYNCH_CALL_LITERAL ||
msgSort == MessageSort.SYNCH_CALL_LITERAL;
}
/**
*
* inverse-sorts the values of a map. Has been adapted from this StackOverflow post.
*
*
* @param map
* map whose values are sorted
* @return sorted version of the map
*/
private 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;
}
}