source: trunk/autoquest-plugin-uml/src/main/java/de/ugoe/cs/autoquest/plugin/uml/UMLInteractionCreator.java @ 2010

Last change on this file since 2010 was 2010, checked in by sherbold, 9 years ago
  • updated interaction generation to be able to handle anonymous sequence and choice elements in the model that represent XSD sequences and choices to some degree. This is just a workaround at the current stage
  • interaction generation now supports enumeration data types
  • Property svn:mime-type set to text/plain
File size: 43.8 KB
Line 
1//   Copyright 2012 Georg-August-Universität Göttingen, Germany
2//
3//   Licensed under the Apache License, Version 2.0 (the "License");
4//   you may not use this file except in compliance with the License.
5//   You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9//   Unless required by applicable law or agreed to in writing, software
10//   distributed under the License is distributed on an "AS IS" BASIS,
11//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//   See the License for the specific language governing permissions and
13//   limitations under the License.
14
15package de.ugoe.cs.autoquest.plugin.uml;
16
17import java.util.HashMap;
18import java.util.Iterator;
19import java.util.List;
20import java.util.Map;
21import java.util.Set;
22import java.util.logging.Level;
23
24import org.apache.commons.lang.mutable.MutableInt;
25import org.eclipse.emf.common.util.EList;
26import org.eclipse.uml2.uml.CallConcurrencyKind;
27import org.eclipse.uml2.uml.Component;
28import org.eclipse.uml2.uml.Connector;
29import org.eclipse.uml2.uml.DataType;
30import org.eclipse.uml2.uml.Element;
31import org.eclipse.uml2.uml.Enumeration;
32import org.eclipse.uml2.uml.Generalization;
33import org.eclipse.uml2.uml.InstanceSpecification;
34import org.eclipse.uml2.uml.InstanceValue;
35import org.eclipse.uml2.uml.Interaction;
36import org.eclipse.uml2.uml.Interface;
37import org.eclipse.uml2.uml.Lifeline;
38import org.eclipse.uml2.uml.LiteralBoolean;
39import org.eclipse.uml2.uml.LiteralInteger;
40import org.eclipse.uml2.uml.LiteralReal;
41import org.eclipse.uml2.uml.LiteralString;
42import org.eclipse.uml2.uml.Message;
43import org.eclipse.uml2.uml.MessageOccurrenceSpecification;
44import org.eclipse.uml2.uml.MessageSort;
45import org.eclipse.uml2.uml.Model;
46import org.eclipse.uml2.uml.Operation;
47import org.eclipse.uml2.uml.Package;
48import org.eclipse.uml2.uml.Parameter;
49import org.eclipse.uml2.uml.PrimitiveType;
50import org.eclipse.uml2.uml.Property;
51import org.eclipse.uml2.uml.Relationship;
52import org.eclipse.uml2.uml.Slot;
53import org.eclipse.uml2.uml.Type;
54import org.eclipse.uml2.uml.UMLPackage;
55
56import de.ugoe.cs.autoquest.eventcore.Event;
57import de.ugoe.cs.autoquest.plugin.http.SOAPUtils;
58import de.ugoe.cs.autoquest.plugin.http.eventcore.SimpleSOAPEventType.CallType;
59import de.ugoe.cs.util.StringTools;
60import de.ugoe.cs.util.console.Console;
61
62/**
63 * <p>
64 * Utility class to create UML Interactions from event sequences
65 * </p>
66 *
67 * @author Steffen Herbold
68 */
69class UMLInteractionCreator {
70
71    /**
72     * model for which the interactions are created
73     */
74    private final Model model;
75
76    /**
77     * name of the test context in which the interactions are created
78     */
79    private final String testContextName;
80
81    /**
82     * defines whether random message bodies are used or the ones associated with the SOAP events
83     */
84    private final boolean useRandomMsgBodies;
85
86    /**
87     * a cache of the already created instance specification; used to create the overall number of generated specifications
88     */
89    private final Map<String, InstanceSpecification> instanceSpecificationCache;
90
91    /**
92     * <p>
93     * Creates a new UMLInteractionCreator
94     * </p>
95     *
96     * @param model
97     *            UML model to which the interaction is added
98     * @param testContextName
99     *            Name of the test context that should be used. If this value is null, the first
100     *            test context found is used.
101     * @param useRandomRequestBodies
102     *            defines is random request bodies are used or the body of the associated event
103     */
104    public UMLInteractionCreator(Model model, String testContextName, boolean useRandomMsgBodies) {
105        this.model = model;
106        this.testContextName = testContextName;
107        this.useRandomMsgBodies = useRandomMsgBodies;
108        instanceSpecificationCache = new HashMap<String, InstanceSpecification>();
109    }
110
111    /**
112     * <p>
113     * Creates the UML Interaction for the given sequence
114     * </p>
115     *
116     * @param sequence
117     *            for which the interaction is created
118     * @param interactionName
119     *            name of the interaction
120     * @return created interaction
121     */
122    public Interaction createInteraction(List<Event> sequence, String interactionName) {
123        Console.traceln(Level.FINEST, "creating interaction: " + interactionName);
124        final Component testContext = UMLUtils.fetchTestContext(model, testContextName);
125        if (testContext == null) {
126            throw new RuntimeException("Could not find any test context in the model");
127        }
128
129        final Operation operation = testContext.createOwnedOperation(interactionName, null, null);
130        operation.applyStereotype(UTPUtils.getTestCaseStereotype(model));
131
132        final Interaction interaction =
133            (Interaction) testContext.createPackagedElement(interactionName + "_Impl",
134                                                            UMLPackage.Literals.INTERACTION);
135        operation.getMethods().add(interaction);
136
137        // create lifelines
138        Lifeline userLifeline = null;
139
140        for (Property property : UMLUtils.fetchAllSUTProperties(testContext)) {
141            String serviceName = property.getName();
142            Lifeline targetLifeline = interaction.createLifeline(serviceName);
143            targetLifeline.setRepresents(property);
144        }
145        for (Property property : UMLUtils.fetchAllTestComponentProperties(testContext)) {
146            userLifeline = interaction.createLifeline(property.getName());
147            userLifeline.setRepresents(property);
148        }
149
150        if (userLifeline == null) {
151            throw new RuntimeException("No TestComponent found, could not create user lifeline.");
152        }
153        if (interaction.getLifelines().size() < 2) {
154            throw new RuntimeException("Fewer than two lifelines created. No SUT found.");
155        }
156
157        int i = 0;
158        for (Event event : sequence) {
159            Console.traceln(Level.FINEST, "creating message for event: " + event.toString());
160            if (!(event.equals(Event.STARTEVENT) || event.equals(Event.ENDEVENT))) {
161                String serviceName = SOAPUtils.getServiceNameFromEvent(event);
162                String methodName = SOAPUtils.getCalledMethodFromEvent(event);
163                String clientName = SOAPUtils.getClientNameFromEvent(event);
164                String prefix = interactionName + "_" + i + "_" + methodName + "_";
165                // determine lifelines
166                Lifeline msgTargetLifeline;
167                Lifeline msgSourceLifeline;
168
169                msgSourceLifeline = interaction.getLifeline(clientName);
170                msgTargetLifeline = interaction.getLifeline(serviceName);
171
172                if (msgSourceLifeline == null) {
173                    throw new RuntimeException(
174                                               "Error creating message: could not determine source lifeline for component: " +
175                                                   clientName);
176                }
177                if (msgTargetLifeline == null) {
178                    throw new RuntimeException(
179                                               "Error creating message: could not determine target lifeline for component: " +
180                                                   serviceName);
181                }
182                // determine correct target interface
183                Set<Interface> targetInterfaces =
184                    UMLUtils.getRealizedInterfacesFromProperty((Property) msgTargetLifeline
185                        .getRepresents());
186                if (targetInterfaces.isEmpty()) {
187                    throw new RuntimeException("no interface associated with the property " +
188                        msgTargetLifeline.getRepresents().getName());
189                }
190                Interface targetInterface = null;
191                for (Interface intface : targetInterfaces) {
192                    if (UMLUtils.getOperationFromName(intface.getOperations(), methodName) != null)
193                    {
194                        // interface found
195                        targetInterface = intface;
196                        break;
197                    }
198                }
199                if (targetInterface == null) {
200                    StringBuilder errStrBuilder = new StringBuilder();
201                    errStrBuilder
202                        .append("Error creating message: operation not found in the implementing interfaces (");
203                    Iterator<Interface> iter = targetInterfaces.iterator();
204                    while (iter.hasNext()) {
205                        String interfaceName = iter.next().getName();
206                        errStrBuilder.append(interfaceName);
207                        if (iter.hasNext()) {
208                            errStrBuilder.append(",");
209                        }
210                        else {
211                            errStrBuilder.append("): " + methodName);
212                        }
213                    }
214                    throw new RuntimeException(errStrBuilder.toString());
215                }
216
217                Operation calledOperation =
218                    UMLUtils.getOperationFromName(targetInterface.getOperations(), methodName);
219                // get connector
220                Connector connector =
221                    UMLUtils.inferConnector(msgSourceLifeline, msgTargetLifeline, targetInterface);
222                if (connector == null) {
223                    throw new RuntimeException(
224                                               "Error creating message: could not find connector between the two life lines that supports the target interface at the target lifeline");
225                }
226
227                boolean asynch = false;
228                if (calledOperation.getConcurrency() == CallConcurrencyKind.CONCURRENT_LITERAL) {
229                    asynch = true;
230                }
231
232                if (SOAPUtils.isSOAPRequest(event)) {
233                    // setup for both SYNCH and ASYNCH calls
234                    MessageOccurrenceSpecification callSendFragment =
235                        (MessageOccurrenceSpecification) interaction
236                            .createFragment(prefix + "callSendFragment",
237                                            UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
238                    MessageOccurrenceSpecification callRecvFragment =
239                        (MessageOccurrenceSpecification) interaction
240                            .createFragment(prefix + "callRecvFragment",
241                                            UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
242
243                    callSendFragment.setCovered(msgSourceLifeline);
244                    callRecvFragment.setCovered(msgTargetLifeline);
245
246                    // create call
247                    Message callMessage = interaction.createMessage(prefix + "call");
248                    callMessage.setSignature(calledOperation);
249                    setMessageParameters(callMessage, calledOperation, event, prefix);
250                    callMessage.setConnector(connector);
251                    callMessage.setSendEvent(callSendFragment);
252                    callMessage.setReceiveEvent(callRecvFragment);
253                    callSendFragment.setMessage(callMessage);
254                    callRecvFragment.setMessage(callMessage);
255
256                    if (asynch) {
257                        // Create ASYNCH call
258                        callMessage.setMessageSort(MessageSort.ASYNCH_CALL_LITERAL);
259                    }
260                    else {
261                        // SYNCH call
262                        callMessage.setMessageSort(MessageSort.SYNCH_CALL_LITERAL);
263                    }
264                }
265                if (!asynch && SOAPUtils.isSOAPResponse(event)) {
266                    // setup reply and behavior execution specifications
267                    MessageOccurrenceSpecification replySendFragment =
268                        (MessageOccurrenceSpecification) interaction
269                            .createFragment(prefix + "replySendFragment",
270                                            UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
271                    MessageOccurrenceSpecification replyRecvFragment =
272                        (MessageOccurrenceSpecification) interaction
273                            .createFragment(prefix + "replyRecvFragment",
274                                            UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
275
276                    replySendFragment.setCovered(msgTargetLifeline);
277                    replyRecvFragment.setCovered(msgSourceLifeline);
278
279                    /*
280                     * BehaviorExecutionSpecification sourceBehaviorExecutionSpecification =
281                     * (BehaviorExecutionSpecification) interaction .createFragment(":" + methodName
282                     * + "_sourceBhvExecSpec",
283                     * UMLPackage.Literals.BEHAVIOR_EXECUTION_SPECIFICATION);
284                     * BehaviorExecutionSpecification targetBehaviorExecutionSpecification =
285                     * (BehaviorExecutionSpecification) interaction .createFragment(":" + methodName
286                     * + "_targetBhvExecSpec",
287                     * UMLPackage.Literals.BEHAVIOR_EXECUTION_SPECIFICATION);
288                     *
289                     * sourceBehaviorExecutionSpecification.setStart(callSendFragment);
290                     * sourceBehaviorExecutionSpecification.setFinish(replyRecvFragment);
291                     * targetBehaviorExecutionSpecification.setStart(callRecvFragment);
292                     * targetBehaviorExecutionSpecification.setFinish(replySendFragment);
293                     */
294
295                    // create reply
296                    Message replyMessage = interaction.createMessage(prefix + "_reply");
297                    replyMessage.setMessageSort(MessageSort.REPLY_LITERAL);
298                    replyMessage.setSignature(calledOperation);
299                    // setReplyMessageParameters(replyMessage, calledOperation);
300                    setMessageParameters(replyMessage, calledOperation, event, prefix);
301                    replyMessage.setConnector(connector);
302                    replyMessage.setSendEvent(replySendFragment);
303                    replyMessage.setReceiveEvent(replyRecvFragment);
304                    replySendFragment.setMessage(replyMessage);
305                    replyRecvFragment.setMessage(replyMessage);
306                }
307
308                i++;
309            }
310        }
311        return interaction;
312    }
313
314    /**
315     * <p>
316     * Sets values for the parameters of a call message. The values are, if possible, inferred from
317     * the event that is provided.
318     * </p>
319     *
320     * @param message
321     *            call message for which the parameters are set
322     * @param calledOperation
323     *            operation that is called by the message
324     * @param event
325     *            event that provides the parameters; in case of null, default values are assumed
326     * @param prefix
327     *            prefix of the call message; used to create good warnings and debugging information
328     */
329    private void setMessageParameters(Message message,
330                                      Operation calledOperation,
331                                      Event event,
332                                      String prefix)
333    {
334        // printParameters(calledOperation); // FOR DEBUGGING
335        org.w3c.dom.Element requestBody;
336        if (SOAPUtils.isSOAPRequest(event)) {
337            requestBody =
338                SOAPUtils.getSoapBodyFromEvent(event, useRandomMsgBodies, CallType.REQUEST);
339        }
340        else {
341            requestBody =
342                SOAPUtils.getSoapBodyFromEvent(event, useRandomMsgBodies, CallType.RESPONSE);
343        }
344        Package instSpecPkg = null;
345        MutableInt instSpecNumber = new MutableInt(0);
346
347        // Set parameters of operation
348        for (Parameter param : calledOperation.getOwnedParameters()) {
349            if (instSpecPkg == null) {
350                instSpecPkg = getOrCreateInstanceSpecificationPackage(event);
351            }
352
353            String path = calledOperation.getName() + ":" + getNormalizedTypeName(param.getType());
354            if ((UMLUtils.isInParameter(param) && SOAPUtils.isSOAPRequest(event)) ||
355                (UMLUtils.isOutParameter(param) && SOAPUtils.isSOAPResponse(event)))
356            {
357
358                // create parameters node
359                if (!(param.getType() instanceof DataType)) {
360                    throw new RuntimeException("TODO error handling; parameters missing");
361                }
362                DataType parametersNode = (DataType) param.getType();
363                InstanceSpecification instSpecParameters =
364                    (InstanceSpecification) instSpecPkg
365                        .createPackagedElement(prefix + "instspec" + instSpecNumber.intValue() +
366                                                   "_" + getNormalizedTypeName(param.getType()),
367                                               UMLPackage.Literals.INSTANCE_SPECIFICATION);
368                instSpecParameters.getClassifiers().add((DataType) param.getType());
369                instSpecNumber.setValue(instSpecNumber.intValue() + 1);
370                InstanceValue instanceValue =
371                    (InstanceValue) message.createArgument(param.getName(), param.getType(),
372                                                           UMLPackage.Literals.INSTANCE_VALUE);
373                instanceValue.setInstance(instSpecParameters);
374
375                for (Property internalParameter : parametersNode.getAllAttributes()) {
376                    if (internalParameter.getType() instanceof DataType) {
377                        List<org.w3c.dom.Element> paramNodes =
378                            SOAPUtils.getMatchingChildNode(getNormalizedTypeName(internalParameter
379                                .getType()), requestBody);
380                        int multiplicityChosen = paramNodes.size();
381
382                        if (multiplicityChosen == 0 && internalParameter.getLower() > 0) {
383                            Console
384                                .traceln(Level.WARNING,
385                                         "required attribute not found in SOAP message: " + path);
386                            Console
387                                .traceln(Level.WARNING,
388                                         "setting default values for this attribute and all its children");
389                            Console.traceln(Level.FINE, "XML structure of path:" +
390                                StringTools.ENDLINE + SOAPUtils.getSerialization(requestBody));
391                            multiplicityChosen = internalParameter.getLower();
392                        }
393                        for (int i = 0; i < multiplicityChosen; i++) {
394                            org.w3c.dom.Element paramNode = null;
395                            if (!paramNodes.isEmpty()) {
396                                paramNode = paramNodes.get(i);
397                            }
398
399                            Slot slot = instSpecParameters.createSlot();
400                            slot.setDefiningFeature(internalParameter);
401
402                            InstanceValue value =
403                                (InstanceValue) slot
404                                    .createValue(internalParameter.getName() + "_" + i,
405                                                 internalParameter.getType(),
406                                                 UMLPackage.Literals.INSTANCE_VALUE);
407                            InstanceSpecification instSpec =
408                                createInstanceSpecification((DataType) internalParameter.getType(),
409                                                            instSpecPkg, prefix, instSpecNumber,
410                                                            paramNode, path);
411                            value.setInstance(instSpec);
412                            /*
413                             * InstanceValue value = (InstanceValue) argument .createOperand(null,
414                             * internalParameter.getType(), UMLPackage.Literals.INSTANCE_VALUE);
415                             * value.setInstance(instSpec);
416                             */
417                        }
418                    }
419                    else if (internalParameter.getType() instanceof PrimitiveType) {
420                        createSlotPrimitiveType(instSpecParameters, internalParameter, requestBody,
421                                                path);
422                    }
423                }
424            }
425            else {
426                // set literalNull for out and return parameters
427                // argument.createOperand(null, param.getType(), UMLPackage.Literals.LITERAL_NULL);
428                message.createArgument(param.getName(), param.getType(),
429                                       UMLPackage.Literals.LITERAL_NULL);
430            }
431        }
432    }
433
434    /**
435     * <p>
436     * Creates an {@link InstanceSpecification} for a data type in the given package. The values are
437     * inferred, if possible, from the DOM node. The prefix and the path are used for naming the
438     * instance specification and to provide good warnings and debug information in case of
439     * problems.
440     * </p>
441     *
442     * @param type
443     *            DataType for which the {@link InstanceSpecification} is created
444     * @param pkg
445     *            package in which the {@link InstanceSpecification} is created
446     * @param prefix
447     *            prefix used for naming the {@link InstanceSpecification}
448     * @param currentNode
449     *            node of a DOM from which values are inferred
450     * @param path
451     *            used for warnings and debug information
452     * @return {@link InstanceSpecification} for the given type
453     */
454    private InstanceSpecification createInstanceSpecification(DataType type,
455                                                              Package pkg,
456                                                              String prefix,
457                                                              MutableInt instSpecNumber,
458                                                              org.w3c.dom.Element currentNode,
459                                                              String path)
460    {
461        if (instanceSpecificationCache.containsKey(SOAPUtils.getSerialization(currentNode))) {
462            return instanceSpecificationCache.get(SOAPUtils.getSerialization(currentNode));
463        }
464        if ("".equals(path)) {
465            path = type.getName();
466        }
467
468        InstanceSpecification instSpec =
469            (InstanceSpecification) pkg
470                .createPackagedElement(prefix + "instspec" + instSpecNumber.intValue() + "_" +
471                                           type.getName(),
472                                       UMLPackage.Literals.INSTANCE_SPECIFICATION);
473        instSpec.getClassifiers().add(type);
474        instSpecNumber.setValue(instSpecNumber.intValue() + 1);
475        for (Property prop : type.getAllAttributes()) {
476            if (prop.getType() instanceof PrimitiveType) {
477                createSlotPrimitiveType(instSpec, prop, currentNode, path);
478            }
479            else if (prop.getType() instanceof Enumeration ) {
480                createSlotEnumeration(instSpec, prop, currentNode, path);
481            }
482            else if (prop.getType() instanceof DataType) {
483                if( isXSDSequence(prop.getType()) ) { // XSD sequence, no real type
484                    System.out.println(SOAPUtils.getChildNodeNames(currentNode));
485                    List<String> childNames = SOAPUtils.getChildNodeNames(currentNode);
486                    List<org.w3c.dom.Element> childNodes = SOAPUtils.getChildElements(currentNode);
487                    EList<Property> sequenceProperties = ((DataType) prop.getType()).getAllAttributes();
488                    boolean sequenceIsMatch = true;
489                    if( sequenceProperties.size()==childNames.size()) {
490                        for(int i=0; sequenceIsMatch && i<sequenceProperties.size() ; i++) {
491                            Property propSeq = sequenceProperties.get(i);
492                            String currentChildName = childNames.get(i);
493                            if( isXSDChoice(propSeq.getType() ) ) {
494                                boolean choiceMatchFound = false;
495                                for( Property propChoice : ((DataType) propSeq.getType()).getAllAttributes() ) {
496                                    if( currentChildName.equals(propChoice.getName()) ) {
497                                        choiceMatchFound = true;
498                                    }
499                                }
500                                sequenceIsMatch &= choiceMatchFound;
501                            } else {
502                                sequenceIsMatch &= currentChildName.equals(propSeq.getName());
503                            }
504                        }
505                        if( sequenceIsMatch ) {
506                            // this is the correct sequence, it matches; now appropriate data must be created
507                           
508                            // first we create the slot and instance specification for the sequence
509                            Slot slot = instSpec.createSlot();
510                            slot.setDefiningFeature(prop);
511                            InstanceValue value =
512                                (InstanceValue) slot.createValue(prop.getName(), prop.getType(), UMLPackage.Literals.INSTANCE_VALUE);
513                            InstanceSpecification instSpecSequence =
514                                    (InstanceSpecification) pkg
515                                        .createPackagedElement(prefix + prop.getName() + "_instspec" + instSpecNumber.intValue() + "_" +
516                                                                   type.getName(),
517                                                               UMLPackage.Literals.INSTANCE_SPECIFICATION);
518                            instSpecSequence.getClassifiers().add((DataType) prop.getType());
519                            instSpecNumber.setValue(instSpecNumber.intValue() + 1);
520                            value.setInstance(instSpecSequence);
521                           
522                            // now we create the slots and instance specifications for the elements of the sequence
523                            for(int i=0; i<sequenceProperties.size() ; i++) {
524                                Property propSeq = sequenceProperties.get(i);
525                                String currentChildName = childNames.get(i);
526                                slot = instSpecSequence.createSlot();
527                                slot.setDefiningFeature(propSeq);
528                                value = (InstanceValue) slot.createValue(propSeq.getName(), propSeq.getType(), UMLPackage.Literals.INSTANCE_VALUE);
529                                if( isXSDChoice(propSeq.getType() ) ) {
530                                    // create the inner choice instance spec
531                                    InstanceSpecification instSpecSequenceChoice = (InstanceSpecification) pkg
532                                            .createPackagedElement(prefix + propSeq.getName() + "_instspec" + instSpecNumber.intValue() + "_" +
533                                                    propSeq.getType().getName(),
534                                                UMLPackage.Literals.INSTANCE_SPECIFICATION);
535                                    instSpecSequenceChoice.getClassifiers().add((DataType) propSeq.getType());
536                                    instSpecNumber.setValue(instSpecNumber.intValue()+1);
537                                    value.setInstance(instSpecSequenceChoice);
538                                    for( Property propChoice : ((DataType) propSeq.getType()).getAllAttributes() ) {
539                                        if( currentChildName.equals(propChoice.getName()) ) {
540                                            slot = instSpecSequenceChoice.createSlot();
541                                            slot.setDefiningFeature(propChoice);
542                                            value = (InstanceValue) slot.createValue(propChoice.getName(), propChoice.getType(), UMLPackage.Literals.INSTANCE_VALUE);
543                                            value.setInstance(createInstanceSpecification((DataType) propChoice.getType(), pkg, prefix, instSpecNumber, childNodes.get(i), path+"."+propChoice.getName()));
544                                            break;
545                                        }
546                                    }
547                                } else {
548                                    value.setInstance(createInstanceSpecification((DataType) propSeq.getType(), pkg, prefix, instSpecNumber, childNodes.get(i), path+"."+propSeq.getName()));
549                                }
550                            }
551                        }
552                        System.out.println(prop.getName() + " " + sequenceIsMatch);
553                    }
554                }
555                else if( isXSDChoice(prop.getType()) ) {
556                    // TODO implement handling of choices, if required for the MIDAS pilots
557                    Console.traceln(Level.SEVERE, "cannot handle choices that are the child elements of normal data types yet!");
558                    Console.traceln(Level.SEVERE, "choice is ignored and no data is created.");
559                } else {
560                    List<org.w3c.dom.Element> attributeNodes = null;
561                    int multiplicityChosen = 0;
562                    if (currentNode != null) {
563                        attributeNodes = SOAPUtils.getMatchingChildNode(prop.getName(), currentNode);
564                        multiplicityChosen = attributeNodes.size();
565                    }
566   
567                    if (multiplicityChosen == 0 && prop.getLower() > 0) {
568                        if (currentNode != null) {
569                            Console.traceln(Level.WARNING,
570                                            "required attribute not found in SOAP message: " + path +
571                                                "." + prop.getName());
572                            Console
573                                .traceln(Level.WARNING,
574                                         "setting default values for this attribute and all its children");
575                            Console.traceln(Level.FINE, "XML structure of path:" + StringTools.ENDLINE +
576                                SOAPUtils.getSerialization(currentNode));
577                        }
578                        multiplicityChosen = prop.getLower();
579                    }
580                    for (int i = 0; i < multiplicityChosen; i++) {
581                        org.w3c.dom.Element attributeNode = null;
582                        if (attributeNodes != null && !attributeNodes.isEmpty()) {
583                            attributeNode = attributeNodes.get(i);
584                        }
585   
586                        Slot slot = instSpec.createSlot();
587                        slot.setDefiningFeature(prop);
588   
589                        InstanceValue value =
590                            (InstanceValue) slot.createValue(prop.getName() + "_" + i, prop.getType(),
591                                                             UMLPackage.Literals.INSTANCE_VALUE);
592                        value.setInstance(createInstanceSpecification((DataType) prop.getType(), pkg,
593                                                                      prefix, instSpecNumber,
594                                                                      attributeNode,
595                                                                      path + "." + prop.getName()));
596                    }
597                }
598            }
599            else {
600                Console.traceln(Level.SEVERE, "property neither DataType nor PrimitiveType: " +
601                    prop.getType());
602                throw new RuntimeException(
603                                           "can only handle DataType and PrimitiveType properties but was: " +
604                                               prop.getType().getClass().getName());
605            }
606        }
607        instanceSpecificationCache.put(SOAPUtils.getSerialization(currentNode), instSpec);
608        return instSpec;
609    }
610
611    /**
612     * <p>
613     * Creates a {@link Slot} in an {@link InstanceSpecification} for a primitive type.
614     * </p>
615     *
616     * @param instSpec
617     *            instance specification to which the slot is added
618     * @param prop
619     *            property that describes the slot
620     * @param currentNode
621     *            DOM node from which is value for the slot is inferred
622     * @param path
623     *            used for warnings and debug information
624     */
625    private void createSlotPrimitiveType(InstanceSpecification instSpec,
626                                         Property prop,
627                                         org.w3c.dom.Element currentNode,
628                                         String path)
629    {
630        List<String> attributeValues = SOAPUtils.getValuesFromElement(prop.getName(), currentNode);
631
632        if (attributeValues.isEmpty()) {
633            if (prop.getLower() == 0) {
634                // ignoring optional attribute
635                return;
636            }
637            else {
638                if (currentNode != null) {
639                    Console.traceln(Level.WARNING,
640                                    "required attribute not found in SOAP message: " + path + "." +
641                                        prop.getName());
642                    Console.traceln(Level.WARNING, "setting default values for this attribute");
643                    Console.traceln(Level.FINE, "XML structure of path:" + StringTools.ENDLINE +
644                        SOAPUtils.getSerialization(currentNode));
645                }
646                attributeValues.add(null);
647            }
648        }
649        for (String attributeValue : attributeValues) {
650            Slot slot = instSpec.createSlot();
651            slot.setDefiningFeature(prop);
652            if (isPrimitiveTypeOf(prop.getType(), "String")) {
653                LiteralString value =
654                    (LiteralString) slot.createValue(prop.getName(), null,
655                                                     UMLPackage.Literals.LITERAL_STRING);
656                if (attributeValue != null) {
657                    value.setValue(attributeValue);
658                }
659                else {
660                    value.setValue("foobar");
661                }
662            }
663            else if (isPrimitiveTypeOf(prop.getType(), "Integer")) {
664                LiteralInteger value =
665                    (LiteralInteger) slot.createValue(prop.getName(), null,
666                                                      UMLPackage.Literals.LITERAL_INTEGER);
667                if (attributeValue != null) {
668                    value.setValue(Integer.parseInt(attributeValue));
669                }
670                else {
671                    value.setValue(42);
672                }
673            }
674            else if (isPrimitiveTypeOf(prop.getType(), "Boolean")) {
675                LiteralBoolean value =
676                    (LiteralBoolean) slot.createValue(prop.getName(), null,
677                                                      UMLPackage.Literals.LITERAL_BOOLEAN);
678                if (attributeValue != null) {
679                    value.setValue(Boolean.parseBoolean(attributeValue));
680                }
681                else {
682                    value.setValue(true);
683                }
684            }
685            else if (isPrimitiveTypeOf(prop.getType(), "Real")) {
686                LiteralReal value =
687                    (LiteralReal) slot.createValue(prop.getName(), null,
688                                                   UMLPackage.Literals.LITERAL_REAL);
689                if (attributeValue != null) {
690                    value.setValue(Double.parseDouble(attributeValue));
691                }
692                else {
693                    value.setValue(3.14);
694                }
695            }
696            else {
697                Console.traceln(Level.SEVERE, "could not create literal for primitive type: " +
698                    prop.getType().getName());
699                throw new RuntimeException("unknown primitive type: " + prop.getType().getName());
700            }
701        }
702    }
703   
704    /**
705     * <p>
706     * Creates a {@link Slot} in an {@link InstanceSpecification} for an enumeration.
707     * </p>
708     *
709     * @param instSpec
710     *            instance specification to which the slot is added
711     * @param prop
712     *            property that describes the slot
713     * @param currentNode
714     *            DOM node from which is value for the slot is inferred
715     * @param path
716     *            used for warnings and debug information
717     */
718    private void createSlotEnumeration(InstanceSpecification instSpec,
719                                         Property prop,
720                                         org.w3c.dom.Element currentNode,
721                                         String path)
722    {
723        List<String> attributeValues = SOAPUtils.getValuesFromElement(prop.getName(), currentNode);
724
725        if (attributeValues.isEmpty()) {
726            if (prop.getLower() == 0) {
727                // ignoring optional attribute
728                return;
729            }
730            else {
731                if (currentNode != null) {
732                    Console.traceln(Level.WARNING,
733                                    "required attribute not found in SOAP message: " + path + "." +
734                                        prop.getName());
735                    Console.traceln(Level.WARNING, "setting default values for this attribute");
736                    Console.traceln(Level.FINE, "XML structure of path:" + StringTools.ENDLINE +
737                        SOAPUtils.getSerialization(currentNode));
738                }
739                attributeValues.add(null);
740            }
741        }
742        for (String attributeValue : attributeValues) {
743            Slot slot = instSpec.createSlot();
744            slot.setDefiningFeature(prop);
745           
746            InstanceValue value = (InstanceValue) slot.createValue(prop.getName(), null, UMLPackage.Literals.INSTANCE_VALUE);
747            if( attributeValue!=null ) {
748                value.setInstance(((Enumeration) prop.getType()).getOwnedLiteral(attributeValue));
749                System.out.println("foo");
750            } else {
751                throw new RuntimeException("cannot create enumeration literal with dummy value");
752            }
753        }
754    }
755
756    /**
757     * <p>
758     * Gets or creates a {@link Package} for {@link InstanceSpecification} created by the
759     * usage-based testing. Each service gets its own sub-package within a package called
760     * UBT_InstanceSpecifications. "
761     * </p>
762     *
763     * @param event
764     *            event from which the service name is inferred
765     * @return package for the {@link InstanceSpecification}s
766     */
767    private Package getOrCreateInstanceSpecificationPackage(Event event) {
768        String pkgUBTInstSpecs = "UBT_InstanceSpecifications";
769        Package ubtInstSpecPkg = (Package) model.getOwnedMember(pkgUBTInstSpecs);
770        if (ubtInstSpecPkg == null) {
771            ubtInstSpecPkg =
772                (Package) model.createPackagedElement(pkgUBTInstSpecs, UMLPackage.Literals.PACKAGE);
773        }
774        String serviceName = SOAPUtils.getServiceNameFromEvent(event);
775        Package serviceInstSpecPkg = (Package) ubtInstSpecPkg.getOwnedMember(serviceName);
776        if (serviceInstSpecPkg == null) {
777            serviceInstSpecPkg =
778                (Package) ubtInstSpecPkg.createPackagedElement(serviceName,
779                                                               UMLPackage.Literals.PACKAGE);
780        }
781        return serviceInstSpecPkg;
782    }
783
784    /**
785     * <p>
786     * Checks if a type is a instance of a primitive type defined by its name, e.g., String,
787     * Integer, or Boolean. The functions also checks whether the type is a generalization.
788     * </p>
789     *
790     * @param type
791     *            type that is checked
792     * @param typeNam
793     *            name of the primitive type
794     * @return true if the type is of the specified primitive type; false otherwise
795     */
796    private boolean isPrimitiveTypeOf(Type type, String typeName) {
797        if (typeName == null) {
798            throw new RuntimeException("typename must not be null");
799        }
800        if (!(type instanceof PrimitiveType)) {
801            return false;
802        }
803        if (typeName.equals(type.getName())) {
804            return true;
805        }
806        for (Relationship relationship : type.getRelationships()) {
807            if (relationship instanceof Generalization) {
808                Element target = ((Generalization) relationship).getTargets().get(0);
809                if (target instanceof PrimitiveType) {
810                    return typeName.equals(((PrimitiveType) target).getName());
811                }
812            }
813        }
814        return false;
815    }
816   
817    /**
818     * <p>
819     * Checks if a type is an anonymous inner type that represents an XSD sequence
820     * </p>
821     *
822     * @param type type that is checked
823     * @return true if XSD sequence; false otherwise
824     */
825    private boolean isXSDSequence(Type type) {
826        if( type==null || type.getName()==null ) {
827            return false;
828        }
829        return type.getName().matches("sequence\\d*");
830    }
831   
832    /**
833     * <p>
834     * Checks if a type is an anonymous inner type that represents an XSD choice
835     * </p>
836     *
837     * @param type type that is checked
838     * @return true if XSD choice; false otherwise
839     */
840    private boolean isXSDChoice(Type type) {
841        if( type==null || type.getName()==null ) {
842            return false;
843        }
844        return type.getName().matches("choice\\d*");
845    }
846
847    /**
848     * <p>
849     * Returns the name of a type and removes the suffix _._type.
850     * </p>
851     *
852     * @param the
853     *            type
854     * @return type name of the type without the possible suffix
855     */
856    private String getNormalizedTypeName(Type type) {
857        if (type == null) {
858            throw new RuntimeException("type must not be null");
859        }
860        String typeName = type.getName();
861        if (typeName != null && typeName.endsWith("_._type")) {
862            typeName = typeName.substring(0, typeName.length() - 7);
863        }
864        return typeName;
865    }
866
867    // /////////////////////
868    // DEBUGGING METHODS //
869    // /////////////////////
870
871    @SuppressWarnings("unused")
872    private void printParameters(Operation operation) {
873        System.out.println("operation name: " + operation.getName());
874        for (Parameter param : operation.getOwnedParameters()) {
875            printParameters(param, 0);
876        }
877    }
878
879    private void printParameters(Parameter param, int depth) {
880        for (int i = 0; i < depth; i++) {
881            System.out.print(" ");
882        }
883        System.out.println(param.getName() + " - " + getNormalizedTypeName(param.getType()));
884        if (param.getType() instanceof DataType) {
885            for (Property prop : ((DataType) param.getType()).getAllAttributes()) {
886                printParameters(prop, depth + 2);
887            }
888        }
889    }
890
891    private void printParameters(Property prop, int depth) {
892        if (depth > 10)
893            return;
894        for (int i = 0; i < depth; i++) {
895            System.out.print(" ");
896        }
897        System.out.println(prop.getName() + " - " + getNormalizedTypeName(prop.getType()));
898        if (prop.getType() instanceof DataType) {
899            for (Property prop2 : ((DataType) prop.getType()).getAllAttributes()) {
900                printParameters(prop2, depth + 2);
901            }
902        }
903    }
904
905}
Note: See TracBrowser for help on using the repository browser.