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

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