// 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()); Console.logException(e); } } 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; } }