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

Last change on this file since 2215 was 2041, checked in by sherbold, 9 years ago
  • workaround due to bug in TTwb that does not handle synch messages without replys correctly.
  • Property svn:mime-type set to text/plain
File size: 52.4 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                if (!hasOutParameters(operation)) {
234                    asynch = true;
235                }
236
237                if (SOAPUtils.isSOAPRequest(event)) {
238                    // setup for both SYNCH and ASYNCH calls
239                    MessageOccurrenceSpecification callSendFragment =
240                        (MessageOccurrenceSpecification) interaction
241                            .createFragment(prefix + "callSendFragment",
242                                            UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
243                    MessageOccurrenceSpecification callRecvFragment =
244                        (MessageOccurrenceSpecification) interaction
245                            .createFragment(prefix + "callRecvFragment",
246                                            UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
247
248                    callSendFragment.setCovered(msgSourceLifeline);
249                    callRecvFragment.setCovered(msgTargetLifeline);
250
251                    // create call
252                    Message callMessage = interaction.createMessage(prefix + "call");
253                    callMessage.setSignature(calledOperation);
254                    setMessageParameters(callMessage, calledOperation, event, prefix);
255                    callMessage.setConnector(connector);
256                    callMessage.setSendEvent(callSendFragment);
257                    callMessage.setReceiveEvent(callRecvFragment);
258                    callSendFragment.setMessage(callMessage);
259                    callRecvFragment.setMessage(callMessage);
260
261                    if (asynch) {
262                        // Create ASYNCH call
263                        callMessage.setMessageSort(MessageSort.ASYNCH_CALL_LITERAL);
264                    }
265                    else {
266                        // SYNCH call
267                        callMessage.setMessageSort(MessageSort.SYNCH_CALL_LITERAL);
268                    }
269                }
270                if (!asynch && SOAPUtils.isSOAPResponse(event)) {
271                    if (hasOutParameters(calledOperation)) {
272                        // setup reply and behavior execution specifications
273                        MessageOccurrenceSpecification replySendFragment =
274                            (MessageOccurrenceSpecification) interaction
275                                .createFragment(prefix + "replySendFragment",
276                                                UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
277                        MessageOccurrenceSpecification replyRecvFragment =
278                            (MessageOccurrenceSpecification) interaction
279                                .createFragment(prefix + "replyRecvFragment",
280                                                UMLPackage.Literals.MESSAGE_OCCURRENCE_SPECIFICATION);
281
282                        replySendFragment.setCovered(msgTargetLifeline);
283                        replyRecvFragment.setCovered(msgSourceLifeline);
284
285                        /*
286                         * BehaviorExecutionSpecification sourceBehaviorExecutionSpecification =
287                         * (BehaviorExecutionSpecification) interaction .createFragment(":" +
288                         * methodName + "_sourceBhvExecSpec",
289                         * UMLPackage.Literals.BEHAVIOR_EXECUTION_SPECIFICATION);
290                         * BehaviorExecutionSpecification targetBehaviorExecutionSpecification =
291                         * (BehaviorExecutionSpecification) interaction .createFragment(":" +
292                         * methodName + "_targetBhvExecSpec",
293                         * UMLPackage.Literals.BEHAVIOR_EXECUTION_SPECIFICATION);
294                         *
295                         * sourceBehaviorExecutionSpecification.setStart(callSendFragment);
296                         * sourceBehaviorExecutionSpecification.setFinish(replyRecvFragment);
297                         * targetBehaviorExecutionSpecification.setStart(callRecvFragment);
298                         * targetBehaviorExecutionSpecification.setFinish(replySendFragment);
299                         */
300
301                        // create reply
302                        Message replyMessage = interaction.createMessage(prefix + "_reply");
303                        replyMessage.setMessageSort(MessageSort.REPLY_LITERAL);
304                        replyMessage.setSignature(calledOperation);
305                        // setReplyMessageParameters(replyMessage, calledOperation);
306                        setMessageParameters(replyMessage, calledOperation, event, prefix);
307                        replyMessage.setConnector(connector);
308                        replyMessage.setSendEvent(replySendFragment);
309                        replyMessage.setReceiveEvent(replyRecvFragment);
310                        replySendFragment.setMessage(replyMessage);
311                        replyRecvFragment.setMessage(replyMessage);
312                    }
313                }
314
315                i++;
316            }
317        }
318        return interaction;
319    }
320
321    /**
322     * <p>
323     * Sets values for the parameters of a call message. The values are, if possible, inferred from
324     * the event that is provided.
325     * </p>
326     *
327     * @param message
328     *            call message for which the parameters are set
329     * @param calledOperation
330     *            operation that is called by the message
331     * @param event
332     *            event that provides the parameters; in case of null, default values are assumed
333     * @param prefix
334     *            prefix of the call message; used to create good warnings and debugging information
335     */
336    private void setMessageParameters(Message message,
337                                      Operation calledOperation,
338                                      Event event,
339                                      String prefix)
340    {
341        // printParameters(calledOperation); // FOR DEBUGGING
342        org.w3c.dom.Element requestBody;
343        if (SOAPUtils.isSOAPRequest(event)) {
344            requestBody =
345                SOAPUtils.getSoapBodyFromEvent(event, useRandomMsgBodies, CallType.REQUEST);
346        }
347        else {
348            requestBody =
349                SOAPUtils.getSoapBodyFromEvent(event, useRandomMsgBodies, CallType.RESPONSE);
350        }
351        Package instSpecPkg = null;
352        MutableInt instSpecNumber = new MutableInt(0);
353
354        // Set parameters of operation
355        for (Parameter param : calledOperation.getOwnedParameters()) {
356            if (instSpecPkg == null) {
357                instSpecPkg = getOrCreateInstanceSpecificationPackage(event);
358            }
359
360            String path = calledOperation.getName() + ":" + getNormalizedTypeName(param.getType());
361            if ((UMLUtils.isInParameter(param) && SOAPUtils.isSOAPRequest(event)) ||
362                (UMLUtils.isOutParameter(param) && SOAPUtils.isSOAPResponse(event)))
363            {
364
365                // create parameters node
366                if (!(param.getType() instanceof DataType)) {
367                    throw new RuntimeException("TODO error handling; parameters missing");
368                }
369                DataType parametersNode = (DataType) param.getType();
370                InstanceSpecification instSpecParameters =
371                    (InstanceSpecification) instSpecPkg
372                        .createPackagedElement(prefix + "instspec" + instSpecNumber.intValue() +
373                                                   "_" + getNormalizedTypeName(param.getType()),
374                                               UMLPackage.Literals.INSTANCE_SPECIFICATION);
375                instSpecParameters.getClassifiers().add((DataType) param.getType());
376                instSpecNumber.setValue(instSpecNumber.intValue() + 1);
377                InstanceValue instanceValue =
378                    (InstanceValue) message.createArgument(param.getName(), param.getType(),
379                                                           UMLPackage.Literals.INSTANCE_VALUE);
380                instanceValue.setInstance(instSpecParameters);
381
382                for (Property internalParameter : parametersNode.getAllAttributes()) {
383                    if (internalParameter.getType() instanceof DataType) {
384                        List<org.w3c.dom.Element> paramNodes =
385                            SOAPUtils.getMatchingChildNode(getNormalizedTypeName(internalParameter
386                                .getType()), requestBody);
387                        int multiplicityChosen = paramNodes.size();
388
389                        if (multiplicityChosen == 0 && internalParameter.getLower() > 0 &&
390                            requestBody != null)
391                        {
392                            Console
393                                .traceln(Level.WARNING,
394                                         "required attribute not found in SOAP message: " + path);
395                            Console
396                                .traceln(Level.WARNING,
397                                         "setting default values for this attribute and all its children");
398                            Console.traceln(Level.FINE, "XML structure of path:" +
399                                StringTools.ENDLINE + SOAPUtils.getSerialization(requestBody));
400                            multiplicityChosen = internalParameter.getLower();
401                        }
402                        if (multiplicityChosen == 0 && requestBody == null) {
403                            // create parameters node with empty instance specification as value
404                            Slot slot = instSpecParameters.createSlot();
405                            slot.setDefiningFeature(internalParameter);
406
407                            InstanceValue value =
408                                (InstanceValue) slot
409                                    .createValue(internalParameter.getName() + "_" + 0,
410                                                 internalParameter.getType(),
411                                                 UMLPackage.Literals.INSTANCE_VALUE);
412                            InstanceSpecification instSpec =
413                                (InstanceSpecification) instSpecPkg
414                                    .createPackagedElement(prefix +
415                                                               "instspec" +
416                                                               instSpecNumber.intValue() +
417                                                               "_" +
418                                                               internalParameter.getType()
419                                                                   .getName(),
420                                                           UMLPackage.Literals.INSTANCE_SPECIFICATION);
421                            instSpec.getClassifiers().add((DataType) internalParameter.getType());
422                            instSpecNumber.setValue(instSpecNumber.intValue() + 1);
423                            value.setInstance(instSpec);
424                        }
425                        else if (multiplicityChosen > 0) {
426                            Slot slot = instSpecParameters.createSlot();
427                            slot.setDefiningFeature(internalParameter);
428                            for (int i = 0; i < multiplicityChosen; i++) {
429                                org.w3c.dom.Element paramNode = null;
430                                if (!paramNodes.isEmpty()) {
431                                    paramNode = paramNodes.get(i);
432                                }
433
434                                InstanceValue value =
435                                    (InstanceValue) slot
436                                        .createValue(internalParameter.getName() + "_" + i,
437                                                     internalParameter.getType(),
438                                                     UMLPackage.Literals.INSTANCE_VALUE);
439                                InstanceSpecification instSpec =
440                                    createInstanceSpecification((DataType) internalParameter
441                                                                    .getType(),
442                                                                instSpecPkg, prefix,
443                                                                instSpecNumber, paramNode, path);
444                                value.setInstance(instSpec);
445                                /*
446                                 * InstanceValue value = (InstanceValue) argument
447                                 * .createOperand(null, internalParameter.getType(),
448                                 * UMLPackage.Literals.INSTANCE_VALUE); value.setInstance(instSpec);
449                                 */
450                            }
451                        }
452                    }
453                    else if (internalParameter.getType() instanceof PrimitiveType) {
454                        createSlotPrimitiveType(instSpecParameters, internalParameter, requestBody,
455                                                path);
456                    }
457                }
458            }
459            else {
460                // set literalNull for out and return parameters
461                // argument.createOperand(null, param.getType(), UMLPackage.Literals.LITERAL_NULL);
462                message.createArgument(param.getName(), param.getType(),
463                                       UMLPackage.Literals.LITERAL_NULL);
464            }
465        }
466    }
467
468    /**
469     * <p>
470     * Creates an {@link InstanceSpecification} for a data type in the given package. The values are
471     * inferred, if possible, from the DOM node. The prefix and the path are used for naming the
472     * instance specification and to provide good warnings and debug information in case of
473     * problems.
474     * </p>
475     *
476     * @param type
477     *            DataType for which the {@link InstanceSpecification} is created
478     * @param pkg
479     *            package in which the {@link InstanceSpecification} is created
480     * @param prefix
481     *            prefix used for naming the {@link InstanceSpecification}
482     * @param currentNode
483     *            node of a DOM from which values are inferred
484     * @param path
485     *            used for warnings and debug information
486     * @return {@link InstanceSpecification} for the given type
487     */
488    private InstanceSpecification createInstanceSpecification(DataType type,
489                                                              Package pkg,
490                                                              String prefix,
491                                                              MutableInt instSpecNumber,
492                                                              org.w3c.dom.Element currentNode,
493                                                              String path)
494    {
495        if (instanceSpecificationCache.containsKey(SOAPUtils.getSerialization(currentNode))) {
496            return instanceSpecificationCache.get(SOAPUtils.getSerialization(currentNode));
497        }
498        if ("".equals(path)) {
499            path = type.getName();
500        }
501
502        InstanceSpecification instSpec =
503            (InstanceSpecification) pkg
504                .createPackagedElement(prefix + "instspec" + instSpecNumber.intValue() + "_" +
505                                           type.getName(),
506                                       UMLPackage.Literals.INSTANCE_SPECIFICATION);
507        instSpec.getClassifiers().add(type);
508        instSpecNumber.setValue(instSpecNumber.intValue() + 1);
509        for (Property prop : type.getAllAttributes()) {
510            if (prop.getType() instanceof PrimitiveType) {
511                createSlotPrimitiveType(instSpec, prop, currentNode, path);
512            }
513            else if (prop.getType() instanceof Enumeration) {
514                createSlotEnumeration(instSpec, prop, currentNode, path);
515            }
516            else if (prop.getType() instanceof DataType) {
517                if (isXSDSequence(prop.getType())) { // XSD sequence, no real type
518                    List<String> childNames = SOAPUtils.getChildNodeNames(currentNode);
519                    List<org.w3c.dom.Element> childNodes = SOAPUtils.getChildElements(currentNode);
520                    EList<Property> sequenceProperties =
521                        ((DataType) prop.getType()).getAllAttributes();
522                    boolean sequenceIsMatch = true;
523                    // can currently only work with exactly one sequence and no optional elements
524                    if (sequenceProperties.size() == childNames.size()) {
525                        for (int i = 0; sequenceIsMatch && i < sequenceProperties.size(); i++) {
526                            Property propSeq = sequenceProperties.get(i);
527                            String currentChildName = childNames.get(i);
528                            if (isXSDChoice(propSeq.getType())) {
529                                boolean choiceMatchFound = false;
530                                for (Property propChoice : ((DataType) propSeq.getType())
531                                    .getAllAttributes())
532                                {
533                                    if (currentChildName.equals(propChoice.getName())) {
534                                        choiceMatchFound = true;
535                                    }
536                                }
537                                sequenceIsMatch &= choiceMatchFound;
538                            }
539                            else {
540                                sequenceIsMatch &= currentChildName.equals(propSeq.getName());
541                            }
542                        }
543                        if (sequenceIsMatch) {
544                            // this is the correct sequence, it matches; now appropriate data must
545                            // be created
546
547                            // first we create the slot and instance specification for the sequence
548                            Slot slot = instSpec.createSlot();
549                            slot.setDefiningFeature(prop);
550                            InstanceValue value =
551                                (InstanceValue) slot
552                                    .createValue(prop.getName(), prop.getType(),
553                                                 UMLPackage.Literals.INSTANCE_VALUE);
554                            InstanceSpecification instSpecSequence =
555                                (InstanceSpecification) pkg
556                                    .createPackagedElement(prefix + prop.getName() + "_instspec" +
557                                                               instSpecNumber.intValue() + "_" +
558                                                               type.getName(),
559                                                           UMLPackage.Literals.INSTANCE_SPECIFICATION);
560                            instSpecSequence.getClassifiers().add((DataType) prop.getType());
561                            instSpecNumber.setValue(instSpecNumber.intValue() + 1);
562                            value.setInstance(instSpecSequence);
563
564                            // now we create the slots and instance specifications for the elements
565                            // of the sequence
566                            for (int i = 0; i < sequenceProperties.size(); i++) {
567                                Property propSeq = sequenceProperties.get(i);
568                                String currentChildName = childNames.get(i);
569                                slot = instSpecSequence.createSlot();
570                                slot.setDefiningFeature(propSeq);
571                                value =
572                                    (InstanceValue) slot
573                                        .createValue(propSeq.getName(), propSeq.getType(),
574                                                     UMLPackage.Literals.INSTANCE_VALUE);
575                                if (isXSDChoice(propSeq.getType())) {
576                                    // create the inner choice instance spec
577                                    InstanceSpecification instSpecSequenceChoice =
578                                        (InstanceSpecification) pkg
579                                            .createPackagedElement(prefix + propSeq.getName() +
580                                                                       "_instspec" +
581                                                                       instSpecNumber.intValue() +
582                                                                       "_" +
583                                                                       propSeq.getType().getName(),
584                                                                   UMLPackage.Literals.INSTANCE_SPECIFICATION);
585                                    instSpecSequenceChoice.getClassifiers().add((DataType) propSeq
586                                                                                    .getType());
587                                    instSpecNumber.setValue(instSpecNumber.intValue() + 1);
588                                    value.setInstance(instSpecSequenceChoice);
589                                    for (Property propChoice : ((DataType) propSeq.getType())
590                                        .getAllAttributes())
591                                    {
592                                        if (currentChildName.equals(propChoice.getName())) {
593                                            slot = instSpecSequenceChoice.createSlot();
594                                            slot.setDefiningFeature(propChoice);
595                                            value =
596                                                (InstanceValue) slot
597                                                    .createValue(propChoice.getName(),
598                                                                 propChoice.getType(),
599                                                                 UMLPackage.Literals.INSTANCE_VALUE);
600                                            value
601                                                .setInstance(createInstanceSpecification((DataType) propChoice
602                                                                                             .getType(),
603                                                                                         pkg,
604                                                                                         prefix,
605                                                                                         instSpecNumber,
606                                                                                         childNodes
607                                                                                             .get(i),
608                                                                                         path +
609                                                                                             "." +
610                                                                                             propChoice
611                                                                                                 .getName()));
612                                            break;
613                                        }
614                                    }
615                                }
616                                else {
617                                    value
618                                        .setInstance(createInstanceSpecification((DataType) propSeq
619                                            .getType(), pkg, prefix, instSpecNumber, childNodes
620                                            .get(i), path + "." + propSeq.getName()));
621                                }
622                            }
623                        }
624                    }
625                }
626                else if (isXSDChoice(prop.getType())) {
627                    for (Property propChoice : ((DataType) prop.getType()).getAllAttributes()) {
628                        List<org.w3c.dom.Element> matchingChildren =
629                            SOAPUtils.getMatchingChildNode(propChoice.getName(), currentNode);
630                        if (!matchingChildren.isEmpty()) {
631                            // create instance specification for the choice
632                            Slot slot = instSpec.createSlot();
633                            slot.setDefiningFeature(prop);
634                            InstanceValue value =
635                                (InstanceValue) slot
636                                    .createValue(prop.getName(), prop.getType(),
637                                                 UMLPackage.Literals.INSTANCE_VALUE);
638                            InstanceSpecification instSpecSequence =
639                                (InstanceSpecification) pkg
640                                    .createPackagedElement(prefix + prop.getName() + "_instspec" +
641                                                               instSpecNumber.intValue() + "_" +
642                                                               type.getName(),
643                                                           UMLPackage.Literals.INSTANCE_SPECIFICATION);
644                            instSpecSequence.getClassifiers().add((DataType) prop.getType());
645                            instSpecNumber.setValue(instSpecNumber.intValue() + 1);
646                            value.setInstance(instSpecSequence);
647
648                            // now the slot and instance specification for the matching choice
649                            slot = instSpecSequence.createSlot();
650                            slot.setDefiningFeature(propChoice);
651                            value =
652                                (InstanceValue) slot
653                                    .createValue(propChoice.getName(), propChoice.getType(),
654                                                 UMLPackage.Literals.INSTANCE_VALUE);
655                            value
656                                .setInstance(createInstanceSpecification((DataType) propChoice
657                                    .getType(), pkg, prefix, instSpecNumber, matchingChildren
658                                    .get(0), path + "." + propChoice.getName()));
659                        }
660                    }
661                }
662                else {
663                    // normal child, neither choice nor sequence
664                    List<org.w3c.dom.Element> attributeNodes = null;
665                    int multiplicityChosen = 0;
666                    if (currentNode != null) {
667                        attributeNodes =
668                            SOAPUtils.getMatchingChildNode(prop.getName(), currentNode);
669                        multiplicityChosen = attributeNodes.size();
670                    }
671
672                    if (multiplicityChosen == 0 && prop.getLower() > 0) {
673                        if (currentNode != null) {
674                            Console.traceln(Level.WARNING,
675                                            "required attribute not found in SOAP message: " +
676                                                path + "." + prop.getName());
677                            Console
678                                .traceln(Level.WARNING,
679                                         "setting default values for this attribute and all its children");
680                            Console.traceln(Level.FINE, "XML structure of path:" +
681                                StringTools.ENDLINE + SOAPUtils.getSerialization(currentNode));
682                        }
683                        multiplicityChosen = prop.getLower();
684                    }
685                    if (multiplicityChosen == 1) {
686                        org.w3c.dom.Element attributeNode = null;
687                        if (attributeNodes != null && !attributeNodes.isEmpty()) {
688                            attributeNode = attributeNodes.get(0);
689                        }
690
691                        Slot slot = instSpec.createSlot();
692                        slot.setDefiningFeature(prop);
693
694                        InstanceValue value =
695                            (InstanceValue) slot.createValue(prop.getName() + "_" + 1,
696                                                             prop.getType(),
697                                                             UMLPackage.Literals.INSTANCE_VALUE);
698                        value.setInstance(createInstanceSpecification((DataType) prop.getType(),
699                                                                      pkg, prefix, instSpecNumber,
700                                                                      attributeNode, path + "." +
701                                                                          prop.getName()));
702                    }
703                    else if (multiplicityChosen > 1) {
704                        Slot slot = instSpec.createSlot();
705                        slot.setDefiningFeature(prop);
706                        for (int i = 0; i < multiplicityChosen; i++) {
707                            org.w3c.dom.Element attributeNode = null;
708                            if (attributeNodes != null && !attributeNodes.isEmpty()) {
709                                attributeNode = attributeNodes.get(i);
710                            }
711
712                            InstanceValue value = UMLFactory.eINSTANCE.createInstanceValue();
713                            value
714                                .setInstance(createInstanceSpecification((DataType) prop.getType(),
715                                                                         pkg, prefix,
716                                                                         instSpecNumber,
717                                                                         attributeNode, path + "." +
718                                                                             prop.getName()));
719                            slot.getValues().add(value);
720                        }
721                    }
722                }
723            }
724            else {
725                Console.traceln(Level.SEVERE, "property neither DataType nor PrimitiveType: " +
726                    prop.getType());
727                throw new RuntimeException(
728                                           "can only handle DataType and PrimitiveType properties but was: " +
729                                               prop.getType().getClass().getName());
730            }
731        }
732        instanceSpecificationCache.put(SOAPUtils.getSerialization(currentNode), instSpec);
733        return instSpec;
734    }
735
736    /**
737     * <p>
738     * Creates a {@link Slot} in an {@link InstanceSpecification} for a primitive type.
739     * </p>
740     *
741     * @param instSpec
742     *            instance specification to which the slot is added
743     * @param prop
744     *            property that describes the slot
745     * @param currentNode
746     *            DOM node from which is value for the slot is inferred
747     * @param path
748     *            used for warnings and debug information
749     */
750    private void createSlotPrimitiveType(InstanceSpecification instSpec,
751                                         Property prop,
752                                         org.w3c.dom.Element currentNode,
753                                         String path)
754    {
755        List<String> attributeValues = SOAPUtils.getValuesFromElement(prop.getName(), currentNode);
756
757        if (attributeValues.isEmpty()) {
758            if (prop.getLower() == 0) {
759                // ignoring optional attribute
760                return;
761            }
762            else {
763                if (currentNode != null) {
764                    Console.traceln(Level.WARNING,
765                                    "required attribute not found in SOAP message: " + path + "." +
766                                        prop.getName());
767                    Console.traceln(Level.WARNING, "setting default values for this attribute");
768                    Console.traceln(Level.FINE, "XML structure of path:" + StringTools.ENDLINE +
769                        SOAPUtils.getSerialization(currentNode));
770                }
771                attributeValues.add(null);
772            }
773        }
774        for (String attributeValue : attributeValues) {
775            Slot slot = instSpec.createSlot();
776            slot.setDefiningFeature(prop);
777            if (isPrimitiveTypeOf(prop.getType(), "String")) {
778                LiteralString value =
779                    (LiteralString) slot.createValue(prop.getName(), null,
780                                                     UMLPackage.Literals.LITERAL_STRING);
781                if (attributeValue != null) {
782                    value.setValue(attributeValue);
783                }
784                else {
785                    value.setValue("foobar");
786                }
787            }
788            else if (isPrimitiveTypeOf(prop.getType(), "Integer")) {
789                LiteralInteger value =
790                    (LiteralInteger) slot.createValue(prop.getName(), null,
791                                                      UMLPackage.Literals.LITERAL_INTEGER);
792                if (attributeValue != null) {
793                    value.setValue(Integer.parseInt(attributeValue));
794                }
795                else {
796                    value.setValue(42);
797                }
798            }
799            else if (isPrimitiveTypeOf(prop.getType(), "Boolean")) {
800                LiteralBoolean value =
801                    (LiteralBoolean) slot.createValue(prop.getName(), null,
802                                                      UMLPackage.Literals.LITERAL_BOOLEAN);
803                if (attributeValue != null) {
804                    value.setValue(Boolean.parseBoolean(attributeValue));
805                }
806                else {
807                    value.setValue(true);
808                }
809            }
810            else if (isPrimitiveTypeOf(prop.getType(), "Real")) {
811                LiteralReal value =
812                    (LiteralReal) slot.createValue(prop.getName(), null,
813                                                   UMLPackage.Literals.LITERAL_REAL);
814                if (attributeValue != null) {
815                    System.out.println(attributeValue);
816                    value.setValue(Double.parseDouble(attributeValue));
817                }
818                else {
819                    value.setValue(3.14);
820                }
821            }
822            else {
823                Console.traceln(Level.SEVERE, "could not create literal for primitive type: " +
824                    prop.getType().getName());
825                Console.traceln(Level.SEVERE, "attribute is ignored!");
826            }
827        }
828    }
829
830    /**
831     * <p>
832     * Creates a {@link Slot} in an {@link InstanceSpecification} for an enumeration.
833     * </p>
834     *
835     * @param instSpec
836     *            instance specification to which the slot is added
837     * @param prop
838     *            property that describes the slot
839     * @param currentNode
840     *            DOM node from which is value for the slot is inferred
841     * @param path
842     *            used for warnings and debug information
843     */
844    private void createSlotEnumeration(InstanceSpecification instSpec,
845                                       Property prop,
846                                       org.w3c.dom.Element currentNode,
847                                       String path)
848    {
849        List<String> attributeValues = SOAPUtils.getValuesFromElement(prop.getName(), currentNode);
850
851        if (attributeValues.isEmpty()) {
852            if (prop.getLower() == 0) {
853                // ignoring optional attribute
854                return;
855            }
856            else {
857                if (currentNode != null) {
858                    Console.traceln(Level.WARNING,
859                                    "required attribute not found in SOAP message: " + path + "." +
860                                        prop.getName());
861                    Console.traceln(Level.WARNING, "setting default values for this attribute");
862                    Console.traceln(Level.FINE, "XML structure of path:" + StringTools.ENDLINE +
863                        SOAPUtils.getSerialization(currentNode));
864                }
865                attributeValues.add(null);
866            }
867        }
868        for (String attributeValue : attributeValues) {
869            Slot slot = instSpec.createSlot();
870            slot.setDefiningFeature(prop);
871
872            InstanceValue value =
873                (InstanceValue) slot.createValue(prop.getName(), null,
874                                                 UMLPackage.Literals.INSTANCE_VALUE);
875            if (attributeValue != null) {
876                value.setInstance(((Enumeration) prop.getType()).getOwnedLiteral(attributeValue));
877            }
878            else {
879                throw new RuntimeException("cannot create enumeration literal with dummy value");
880            }
881        }
882    }
883
884    /**
885     * <p>
886     * Gets or creates a {@link Package} for {@link InstanceSpecification} created by the
887     * usage-based testing. Each service gets its own sub-package within a package called
888     * UBT_InstanceSpecifications. "
889     * </p>
890     *
891     * @param event
892     *            event from which the service name is inferred
893     * @return package for the {@link InstanceSpecification}s
894     */
895    private Package getOrCreateInstanceSpecificationPackage(Event event) {
896        String pkgUBTInstSpecs = "UBT_InstanceSpecifications";
897        Package ubtInstSpecPkg = (Package) model.getOwnedMember(pkgUBTInstSpecs);
898        if (ubtInstSpecPkg == null) {
899            ubtInstSpecPkg =
900                (Package) model.createPackagedElement(pkgUBTInstSpecs, UMLPackage.Literals.PACKAGE);
901        }
902        String serviceName = SOAPUtils.getServiceNameFromEvent(event);
903        Package serviceInstSpecPkg = (Package) ubtInstSpecPkg.getOwnedMember(serviceName);
904        if (serviceInstSpecPkg == null) {
905            serviceInstSpecPkg =
906                (Package) ubtInstSpecPkg.createPackagedElement(serviceName,
907                                                               UMLPackage.Literals.PACKAGE);
908        }
909        return serviceInstSpecPkg;
910    }
911
912    /**
913     * <p>
914     * Checks if a type is a instance of a primitive type defined by its name, e.g., String,
915     * Integer, or Boolean. The functions also checks whether the type is a generalization.
916     * </p>
917     *
918     * @param type
919     *            type that is checked
920     * @param typeNam
921     *            name of the primitive type
922     * @return true if the type is of the specified primitive type; false otherwise
923     */
924    private boolean isPrimitiveTypeOf(Type type, String typeName) {
925        if (typeName == null) {
926            throw new RuntimeException("typename must not be null");
927        }
928        if (!(type instanceof PrimitiveType)) {
929            return false;
930        }
931        if (typeName.equals(type.getName())) {
932            return true;
933        }
934        for (Relationship relationship : type.getRelationships()) {
935            if (relationship instanceof Generalization) {
936                Element target = ((Generalization) relationship).getTargets().get(0);
937                if (target instanceof PrimitiveType) {
938                    if (typeName.equals(((PrimitiveType) target).getName())) {
939                        return true;
940                    }
941                }
942            }
943        }
944        return false;
945    }
946
947    /**
948     * <p>
949     * Checks if a type is an anonymous inner type that represents an XSD sequence
950     * </p>
951     *
952     * @param type
953     *            type that is checked
954     * @return true if XSD sequence; false otherwise
955     */
956    private boolean isXSDSequence(Type type) {
957        if (type == null || type.getName() == null) {
958            return false;
959        }
960        return type.getName().matches("sequence\\d*");
961    }
962
963    /**
964     * <p>
965     * Checks if a type is an anonymous inner type that represents an XSD choice
966     * </p>
967     *
968     * @param type
969     *            type that is checked
970     * @return true if XSD choice; false otherwise
971     */
972    private boolean isXSDChoice(Type type) {
973        if (type == null || type.getName() == null) {
974            return false;
975        }
976        return type.getName().matches("choice\\d*");
977    }
978
979    /**
980     * <p>
981     * Returns the name of a type and removes the suffix _._type.
982     * </p>
983     *
984     * @param the
985     *            type
986     * @return type name of the type without the possible suffix
987     */
988    private String getNormalizedTypeName(Type type) {
989        if (type == null) {
990            throw new RuntimeException("type must not be null");
991        }
992        String typeName = type.getName();
993        if (typeName != null && typeName.endsWith("_._type")) {
994            typeName = typeName.substring(0, typeName.length() - 7);
995        }
996        return typeName;
997    }
998
999    /**
1000     * <p>
1001     * Checks if a operation has an output parameter.
1002     * </p>
1003     *
1004     * @param operation
1005     *            operation that is checked
1006     * @return true if pilot has an operation
1007     */
1008    private boolean hasOutParameters(Operation operation) {
1009        if (operation == null) {
1010            throw new RuntimeException("operation must not be null");
1011        }
1012        for (Parameter param : operation.getOwnedParameters()) {
1013            if (UMLUtils.isOutParameter(param)) {
1014                return true;
1015            }
1016        }
1017        return false;
1018    }
1019
1020    // /////////////////////
1021    // DEBUGGING METHODS //
1022    // /////////////////////
1023
1024    @SuppressWarnings("unused")
1025    private void printParameters(Operation operation) {
1026        System.out.println("operation name: " + operation.getName());
1027        for (Parameter param : operation.getOwnedParameters()) {
1028            printParameters(param, 0);
1029        }
1030    }
1031
1032    private void printParameters(Parameter param, int depth) {
1033        for (int i = 0; i < depth; i++) {
1034            System.out.print(" ");
1035        }
1036        System.out.println(param.getName() + " - " + getNormalizedTypeName(param.getType()));
1037        if (param.getType() instanceof DataType) {
1038            for (Property prop : ((DataType) param.getType()).getAllAttributes()) {
1039                printParameters(prop, depth + 2);
1040            }
1041        }
1042    }
1043
1044    private void printParameters(Property prop, int depth) {
1045        if (depth > 10)
1046            return;
1047        for (int i = 0; i < depth; i++) {
1048            System.out.print(" ");
1049        }
1050        System.out.println(prop.getName() + " - " + getNormalizedTypeName(prop.getType()));
1051        if (prop.getType() instanceof DataType) {
1052            for (Property prop2 : ((DataType) prop.getType()).getAllAttributes()) {
1053                printParameters(prop2, depth + 2);
1054            }
1055        }
1056    }
1057
1058}
Note: See TracBrowser for help on using the repository browser.