// 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.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import org.apache.commons.lang.mutable.MutableInt; import org.eclipse.emf.common.util.EList; import org.eclipse.uml2.uml.CallConcurrencyKind; import org.eclipse.uml2.uml.Component; import org.eclipse.uml2.uml.Connector; import org.eclipse.uml2.uml.DataType; import org.eclipse.uml2.uml.Element; import org.eclipse.uml2.uml.Enumeration; import org.eclipse.uml2.uml.Generalization; import org.eclipse.uml2.uml.InstanceSpecification; import org.eclipse.uml2.uml.InstanceValue; import org.eclipse.uml2.uml.Interaction; 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.PrimitiveType; import org.eclipse.uml2.uml.Property; import org.eclipse.uml2.uml.Relationship; import org.eclipse.uml2.uml.Slot; import org.eclipse.uml2.uml.Type; import org.eclipse.uml2.uml.UMLPackage; import de.ugoe.cs.autoquest.eventcore.Event; import de.ugoe.cs.autoquest.plugin.http.SOAPUtils; import de.ugoe.cs.autoquest.plugin.http.eventcore.SimpleSOAPEventType.CallType; import de.ugoe.cs.util.StringTools; import de.ugoe.cs.util.console.Console; /** *
* Utility class to create UML Interactions from event sequences *
* * @author Steffen Herbold */ class UMLInteractionCreator { /** * model for which the interactions are created */ private final Model model; /** * name of the test context in which the interactions are created */ private final String testContextName; /** * defines whether random message bodies are used or the ones associated with the SOAP events */ private final boolean useRandomMsgBodies; /** * a cache of the already created instance specification; used to create the overall number of generated specifications */ private final Map* Creates a new UMLInteractionCreator *
* * @param model * UML model to which the interaction is added * @param testContextName * Name of the test context that should be used. If this value is null, the first * test context found is used. * @param useRandomRequestBodies * defines is random request bodies are used or the body of the associated event */ public UMLInteractionCreator(Model model, String testContextName, boolean useRandomMsgBodies) { this.model = model; this.testContextName = testContextName; this.useRandomMsgBodies = useRandomMsgBodies; instanceSpecificationCache = new HashMap* Creates the UML Interaction for the given sequence *
* * @param sequence * for which the interaction is created * @param interactionName * name of the interaction * @return created interaction */ public Interaction createInteraction(List* Sets values for the parameters of a call message. The values are, if possible, inferred from * the event that is provided. *
* * @param message * call message for which the parameters are set * @param calledOperation * operation that is called by the message * @param event * event that provides the parameters; in case of null, default values are assumed * @param prefix * prefix of the call message; used to create good warnings and debugging information */ private void setMessageParameters(Message message, Operation calledOperation, Event event, String prefix) { // printParameters(calledOperation); // FOR DEBUGGING org.w3c.dom.Element requestBody; if (SOAPUtils.isSOAPRequest(event)) { requestBody = SOAPUtils.getSoapBodyFromEvent(event, useRandomMsgBodies, CallType.REQUEST); } else { requestBody = SOAPUtils.getSoapBodyFromEvent(event, useRandomMsgBodies, CallType.RESPONSE); } Package instSpecPkg = null; MutableInt instSpecNumber = new MutableInt(0); // Set parameters of operation for (Parameter param : calledOperation.getOwnedParameters()) { if (instSpecPkg == null) { instSpecPkg = getOrCreateInstanceSpecificationPackage(event); } String path = calledOperation.getName() + ":" + getNormalizedTypeName(param.getType()); if ((UMLUtils.isInParameter(param) && SOAPUtils.isSOAPRequest(event)) || (UMLUtils.isOutParameter(param) && SOAPUtils.isSOAPResponse(event))) { // create parameters node if (!(param.getType() instanceof DataType)) { throw new RuntimeException("TODO error handling; parameters missing"); } DataType parametersNode = (DataType) param.getType(); InstanceSpecification instSpecParameters = (InstanceSpecification) instSpecPkg .createPackagedElement(prefix + "instspec" + instSpecNumber.intValue() + "_" + getNormalizedTypeName(param.getType()), UMLPackage.Literals.INSTANCE_SPECIFICATION); instSpecParameters.getClassifiers().add((DataType) param.getType()); instSpecNumber.setValue(instSpecNumber.intValue() + 1); InstanceValue instanceValue = (InstanceValue) message.createArgument(param.getName(), param.getType(), UMLPackage.Literals.INSTANCE_VALUE); instanceValue.setInstance(instSpecParameters); for (Property internalParameter : parametersNode.getAllAttributes()) { if (internalParameter.getType() instanceof DataType) { List* Creates an {@link InstanceSpecification} for a data type in the given package. The values are * inferred, if possible, from the DOM node. The prefix and the path are used for naming the * instance specification and to provide good warnings and debug information in case of * problems. *
* * @param type * DataType for which the {@link InstanceSpecification} is created * @param pkg * package in which the {@link InstanceSpecification} is created * @param prefix * prefix used for naming the {@link InstanceSpecification} * @param currentNode * node of a DOM from which values are inferred * @param path * used for warnings and debug information * @return {@link InstanceSpecification} for the given type */ private InstanceSpecification createInstanceSpecification(DataType type, Package pkg, String prefix, MutableInt instSpecNumber, org.w3c.dom.Element currentNode, String path) { if (instanceSpecificationCache.containsKey(SOAPUtils.getSerialization(currentNode))) { return instanceSpecificationCache.get(SOAPUtils.getSerialization(currentNode)); } if ("".equals(path)) { path = type.getName(); } InstanceSpecification instSpec = (InstanceSpecification) pkg .createPackagedElement(prefix + "instspec" + instSpecNumber.intValue() + "_" + type.getName(), UMLPackage.Literals.INSTANCE_SPECIFICATION); instSpec.getClassifiers().add(type); instSpecNumber.setValue(instSpecNumber.intValue() + 1); for (Property prop : type.getAllAttributes()) { if (prop.getType() instanceof PrimitiveType) { createSlotPrimitiveType(instSpec, prop, currentNode, path); } else if (prop.getType() instanceof Enumeration ) { createSlotEnumeration(instSpec, prop, currentNode, path); } else if (prop.getType() instanceof DataType) { if( isXSDSequence(prop.getType()) ) { // XSD sequence, no real type List* Creates a {@link Slot} in an {@link InstanceSpecification} for a primitive type. *
* * @param instSpec * instance specification to which the slot is added * @param prop * property that describes the slot * @param currentNode * DOM node from which is value for the slot is inferred * @param path * used for warnings and debug information */ private void createSlotPrimitiveType(InstanceSpecification instSpec, Property prop, org.w3c.dom.Element currentNode, String path) { List* Creates a {@link Slot} in an {@link InstanceSpecification} for an enumeration. *
* * @param instSpec * instance specification to which the slot is added * @param prop * property that describes the slot * @param currentNode * DOM node from which is value for the slot is inferred * @param path * used for warnings and debug information */ private void createSlotEnumeration(InstanceSpecification instSpec, Property prop, org.w3c.dom.Element currentNode, String path) { List* Gets or creates a {@link Package} for {@link InstanceSpecification} created by the * usage-based testing. Each service gets its own sub-package within a package called * UBT_InstanceSpecifications. " *
* * @param event * event from which the service name is inferred * @return package for the {@link InstanceSpecification}s */ private Package getOrCreateInstanceSpecificationPackage(Event event) { String pkgUBTInstSpecs = "UBT_InstanceSpecifications"; Package ubtInstSpecPkg = (Package) model.getOwnedMember(pkgUBTInstSpecs); if (ubtInstSpecPkg == null) { ubtInstSpecPkg = (Package) model.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); } return serviceInstSpecPkg; } /** ** Checks if a type is a instance of a primitive type defined by its name, e.g., String, * Integer, or Boolean. The functions also checks whether the type is a generalization. *
* * @param type * type that is checked * @param typeNam * name of the primitive type * @return true if the type is of the specified primitive type; false otherwise */ private boolean isPrimitiveTypeOf(Type type, String typeName) { if (typeName == null) { throw new RuntimeException("typename must not be null"); } if (!(type instanceof PrimitiveType)) { return false; } if (typeName.equals(type.getName())) { return true; } for (Relationship relationship : type.getRelationships()) { if (relationship instanceof Generalization) { Element target = ((Generalization) relationship).getTargets().get(0); if (target instanceof PrimitiveType) { return typeName.equals(((PrimitiveType) target).getName()); } } } return false; } /** ** Checks if a type is an anonymous inner type that represents an XSD sequence *
* * @param type type that is checked * @return true if XSD sequence; false otherwise */ private boolean isXSDSequence(Type type) { if( type==null || type.getName()==null ) { return false; } return type.getName().matches("sequence\\d*"); } /** ** Checks if a type is an anonymous inner type that represents an XSD choice *
* * @param type type that is checked * @return true if XSD choice; false otherwise */ private boolean isXSDChoice(Type type) { if( type==null || type.getName()==null ) { return false; } return type.getName().matches("choice\\d*"); } /** ** Returns the name of a type and removes the suffix _._type. *
* * @param the * type * @return type name of the type without the possible suffix */ private String getNormalizedTypeName(Type type) { if (type == null) { throw new RuntimeException("type must not be null"); } String typeName = type.getName(); if (typeName != null && typeName.endsWith("_._type")) { typeName = typeName.substring(0, typeName.length() - 7); } return typeName; } // ///////////////////// // DEBUGGING METHODS // // ///////////////////// @SuppressWarnings("unused") private void printParameters(Operation operation) { System.out.println("operation name: " + operation.getName()); for (Parameter param : operation.getOwnedParameters()) { printParameters(param, 0); } } private void printParameters(Parameter param, int depth) { for (int i = 0; i < depth; i++) { System.out.print(" "); } System.out.println(param.getName() + " - " + getNormalizedTypeName(param.getType())); if (param.getType() instanceof DataType) { for (Property prop : ((DataType) param.getType()).getAllAttributes()) { printParameters(prop, depth + 2); } } } private void printParameters(Property prop, int depth) { if (depth > 10) return; for (int i = 0; i < depth; i++) { System.out.print(" "); } System.out.println(prop.getName() + " - " + getNormalizedTypeName(prop.getType())); if (prop.getType() instanceof DataType) { for (Property prop2 : ((DataType) prop.getType()).getAllAttributes()) { printParameters(prop2, depth + 2); } } } }