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

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