001package org.andromda.cartridges.webservice.metafacades;
002
003import java.text.Collator;
004import java.text.MessageFormat;
005import java.util.ArrayList;
006import java.util.Collection;
007import java.util.Collections;
008import java.util.Comparator;
009import java.util.HashMap;
010import java.util.HashSet;
011import java.util.Iterator;
012import java.util.LinkedHashSet;
013import java.util.List;
014import java.util.Map;
015import java.util.Set;
016import java.util.TreeMap;
017import java.util.TreeSet;
018import org.andromda.cartridges.webservice.WebServiceGlobals;
019import org.andromda.cartridges.webservice.WebServiceUtils;
020import org.andromda.core.common.ExceptionUtils;
021import org.andromda.core.common.Introspector;
022import org.andromda.core.metafacade.MetafacadeBase;
023import org.andromda.core.metafacade.MetafacadeException;
024import org.andromda.core.metafacade.ModelValidationMessage;
025import org.andromda.metafacades.uml.AssociationEndFacade;
026import org.andromda.metafacades.uml.AttributeFacade;
027import org.andromda.metafacades.uml.ClassifierFacade;
028import org.andromda.metafacades.uml.GeneralizableElementFacade;
029import org.andromda.metafacades.uml.ModelElementFacade;
030import org.andromda.metafacades.uml.OperationFacade;
031import org.andromda.metafacades.uml.PackageFacade;
032import org.andromda.metafacades.uml.ParameterFacade;
033import org.andromda.metafacades.uml.Role;
034import org.andromda.metafacades.uml.ServiceOperation;
035import org.andromda.metafacades.uml.TypeMappings;
036import org.andromda.metafacades.uml.UMLMetafacadeProperties;
037import org.andromda.metafacades.uml.UMLProfile;
038import org.andromda.translation.ocl.validation.OCLExpressions;
039import org.andromda.translation.ocl.validation.OCLIntrospector;
040import org.apache.commons.collections.Closure;
041import org.apache.commons.collections.CollectionUtils;
042import org.apache.commons.collections.Predicate;
043import org.apache.commons.lang.ObjectUtils;
044import org.apache.commons.lang.StringUtils;
045import org.apache.log4j.Logger;
046
047/**
048 * MetafacadeLogic implementation for org.andromda.cartridges.webservice.metafacades.WebService.
049 *
050 * @see org.andromda.cartridges.webservice.metafacades.WebService
051 * @author Bob Fields
052 */
053public class WebServiceLogicImpl
054    extends WebServiceLogic
055{
056    private static final long serialVersionUID = 34L;
057    // ---------------- constructor -------------------------------
058    /**
059     * @param metaObject
060     * @param context
061     */
062    public WebServiceLogicImpl(
063        Object metaObject,
064        String context)
065    {
066        super(metaObject, context);
067    }
068
069    /**
070     * The logger instance.
071     */
072    private static final Logger logger = Logger.getLogger(WebServiceLogicImpl.class);
073
074    private static final String DEFAULT = "default";
075
076    /**
077     * @return operations filtered by ((WebServiceOperation)object).isExposed()
078     * @see org.andromda.cartridges.webservice.metafacades.WebService#getAllowedOperations()
079     */
080    protected Collection<OperationFacade> handleGetAllowedOperations()
081    {
082        List<OperationFacade> operations = new ArrayList<OperationFacade>(this.getOperations());
083        CollectionUtils.filter(
084            operations,
085            new Predicate()
086            {
087                public boolean evaluate(Object object)
088                {
089                    boolean valid = WebServiceOperation.class.isAssignableFrom(object.getClass());
090                    if (valid)
091                    {
092                        valid = ((WebServiceOperation)object).isExposed();
093                    }
094                    return valid;
095                }
096            });
097        if (this.getWSDLOperationSortMode().equals(OPERATION_SORT_MODE_NAME))
098        {
099            Collections.sort(
100                operations,
101                new OperationNameComparator());
102        }
103        return operations;
104    }
105
106    /**
107     * @return this.getAllowedOperations() separated by " "
108     * @see org.andromda.cartridges.webservice.metafacades.WebService#getAllowedMethods()
109     */
110    protected String handleGetAllowedMethods()
111    {
112        Collection<String> methodNames = new ArrayList<String>();
113        Collection<WebServiceOperation> operations = this.getAllowedOperations();
114        if (operations != null && !operations.isEmpty())
115        {
116            for (WebServiceOperation operation : operations)
117            {
118                methodNames.add(StringUtils.trimToEmpty(operation.getName()));
119            }
120        }
121        return StringUtils.join(
122            methodNames.iterator(),
123            " ");
124    }
125
126    /**
127     * @return this.getName() formatted as this.getQualifiedNameLocalPartPattern()
128     * @see org.andromda.cartridges.webservice.metafacades.WebService#getQName()
129     */
130    protected String handleGetQName()
131    {
132        return MessageFormat.format(
133            this.getQualifiedNameLocalPartPattern(),
134                StringUtils.trimToEmpty(this.getName()));
135    }
136
137    /**
138     * @return this.getPackageName() reversed if this.isReverseNamespace()
139     * @see org.andromda.cartridges.webservice.metafacades.WebService#getNamespace()
140     */
141    protected String handleGetNamespace()
142    {
143        String packageName = this.getPackageName();
144        if (this.isReverseNamespace())
145        {
146            packageName = WebServiceUtils.reversePackage(packageName);
147        }
148        return MessageFormat.format(
149            this.getNamespacePattern(),
150                StringUtils.trimToEmpty(packageName));
151    }
152
153    /**
154     * The property defining the default style to give the web services.
155     */
156    private static final String PROPERTY_DEFAULT_STYLE = "defaultStyle";
157
158    /**
159     * @return UMLProfile.TAGGEDVALUE_WEBSERVICE_STYLE or this.getConfiguredProperty(PROPERTY_DEFAULT_STYLE)
160     * @see org.andromda.cartridges.webservice.metafacades.WebService#getStyle()
161     */
162    protected String handleGetStyle()
163    {
164        String style = (String)this.findTaggedValue(UMLProfile.TAGGEDVALUE_WEBSERVICE_STYLE);
165        if (StringUtils.isBlank(style) || style.equals(DEFAULT))
166        {
167            style = String.valueOf(this.getConfiguredProperty(PROPERTY_DEFAULT_STYLE));
168        }
169        return style;
170    }
171
172    /**
173     * The property defining the default style to give the web services.
174     */
175    private static final String PROPERTY_DEFAULT_USE = "defaultUse";
176
177    /**
178     * @return UMLProfile.TAGGEDVALUE_WEBSERVICE_USE or this.getConfiguredProperty(PROPERTY_DEFAULT_USE
179     * @see org.andromda.cartridges.webservice.metafacades.WebService#getUse()
180     */
181    protected String handleGetUse()
182    {
183        String use = (String)this.findTaggedValue(UMLProfile.TAGGEDVALUE_WEBSERVICE_USE);
184        if (StringUtils.isBlank(use) || use.equals(DEFAULT))
185        {
186            use = String.valueOf(this.getConfiguredProperty(PROPERTY_DEFAULT_USE));
187        }
188        return use;
189    }
190
191    /**
192     * Sorted list of all type mapping elements (package.class), used to iterate through all elements in a service
193     */
194    private Set<ModelElementFacade> elementSet = new TreeSet<ModelElementFacade>(new TypeComparator());
195
196    /**
197     * Keeps track of whether or not the type has been checked, keeps us from entering infinite loops when calling
198     * loadTypes.
199     */
200    private Collection<ModelElementFacade> checkedTypes = new ArrayList<ModelElementFacade>();
201
202    /**
203     * @return this.elementSet types
204     * @see org.andromda.cartridges.webservice.metafacades.WebService#getTypeMappingElements()
205     */
206    protected Collection<ModelElementFacade> handleGetTypeMappingElements()
207    {
208        final Collection<ParameterFacade> parameterTypes = new LinkedHashSet<ParameterFacade>();
209        for (final WebServiceOperation operation : this.getAllowedOperations())
210        {
211            parameterTypes.addAll(operation.getParameters());
212        }
213
214        final Set<ModelElementFacade> types = new TreeSet<ModelElementFacade>(new TypeComparator());
215        final Collection<ModelElementFacade> nonArrayTypes = new TreeSet<ModelElementFacade>(new TypeComparator());
216
217        // clear out the cache of checkedTypes, otherwise
218        // they'll be ignored the second time this method is
219        // called (if the instance is reused)
220        this.checkedTypes.clear();
221        for (final ParameterFacade parameter : parameterTypes)
222        {
223            this.loadTypes((ModelElementFacade)parameter, types, nonArrayTypes);
224        }
225
226        for (final WebServiceOperation operation : this.getAllowedOperations())
227        {
228            final Collection<ModelElementFacade> exceptions = operation.getExceptions();
229            exceptions.addAll(exceptions);
230            // Exceptions may have attributes too
231            for (final ModelElementFacade exception : exceptions)
232            {
233                this.loadTypes(exception, types, nonArrayTypes);
234            }
235        }
236
237        // now since we're at the end, and we know the
238        // non array types won't override any other types
239        // (such as association ends) we
240        // add the non array types to the types
241        types.addAll(nonArrayTypes);
242
243        this.elementSet = types;
244        //setPkgAbbr(types);
245        return types;
246    }
247
248    /**
249     * <p> Loads all <code>types</code> and <code>nonArrayTypes</code> for
250     * the specified <code>type</code>. For each array type we collect the
251     * <code>nonArrayType</code>. Non array types are loaded separately so
252     * that they are added at the end at the type collecting process. Since the
253     * types collection is a set (by the fullyQualifiedName) we don't want any
254     * non array types to override things such as association ends in the
255     * <code>types</code> collection.
256     * </p>
257     *
258     * @param type the type
259     * @param types the collection to load.
260     * @param nonArrayTypes the collection of non array types.
261     */
262    private void loadTypes(ModelElementFacade modelElement, Set<ModelElementFacade> types,
263        Collection<ModelElementFacade> nonArrayTypes)
264    {
265        ExceptionUtils.checkNull("types", types);
266        ExceptionUtils.checkNull("nonArrayTypes", nonArrayTypes);
267
268        try
269        {
270            if (modelElement != null && !this.checkedTypes.contains(modelElement))
271            {
272                final ClassifierFacade parameterType = this.getType(modelElement);
273
274                // only continue if the model element has a type
275                if (parameterType != null)
276                {
277                    final Set<ModelElementFacade> allTypes = new LinkedHashSet<ModelElementFacade>();
278                    allTypes.add(parameterType);
279
280                    // add all generalizations and specializations of the type
281                    final Collection<GeneralizableElementFacade> generalizations = parameterType.getAllGeneralizations();
282
283                    if (generalizations != null)
284                    {
285                        allTypes.addAll(generalizations);
286                    }
287
288                    final Collection<GeneralizableElementFacade> specializations = parameterType.getAllSpecializations();
289
290                    if (specializations != null)
291                    {
292                        allTypes.addAll(specializations);
293                    }
294
295                    if (!this.checkedTypes.contains(parameterType))
296                    {
297                        this.checkedTypes.add(parameterType);
298
299                        for (final Iterator allTypesIterator = allTypes.iterator(); allTypesIterator.hasNext();)
300                        {
301                            ClassifierFacade type = (ClassifierFacade) allTypesIterator.next();
302
303                            if (!this.containsManyType(types, modelElement))
304                            {
305                                ClassifierFacade nonArrayType = type;
306                                final boolean arrayType = type.isArrayType();
307
308                                if (arrayType || this.isValidAssociationEnd(modelElement))
309                                {
310                                    types.add(modelElement);
311
312                                    if (arrayType)
313                                    {
314                                        // convert to non-array type since we
315                                        // check if that one has the stereotype
316                                        nonArrayType = type.getNonArray();
317
318                                        // set the type to the non array type since
319                                        // that will have the attributes
320                                        type = nonArrayType;
321                                    }
322                                }
323
324                                if (nonArrayType != null)
325                                {
326                                    if (nonArrayType.hasStereotype(UMLProfile.STEREOTYPE_VALUE_OBJECT)
327                                       || nonArrayType.hasStereotype(UMLProfile.STEREOTYPE_APPLICATION_EXCEPTION)
328                                       || nonArrayType.isEnumeration())
329                                    {
330                                        // we add the type when it's a non array and
331                                        // has the correct stereotype (even if we have
332                                        // added the array type above) since we need to
333                                        // define both an array and non array in the WSDL
334                                        // if we are defining an array.
335                                        nonArrayTypes.add(nonArrayType);
336                                    }
337                                }
338                            }
339
340                            if (type != null)
341                            {
342                                final List<? extends ModelElementFacade> properties = type.getProperties();
343                                if (properties != null && !properties.isEmpty())
344                                {
345                                    for (final ModelElementFacade property : properties)
346                                    {
347                                        // Avoid StackOverflowError loading self-referenced types
348                                        if (!property.getFullyQualifiedName().equals(modelElement.getFullyQualifiedName()))
349                                        {
350                                            this.loadTypes(property, types, nonArrayTypes);
351                                        }
352                                    }
353                                }
354                            }
355                        }
356                    }
357                }
358            }
359        }
360        catch (final Throwable throwable)
361        {
362            final String message = "Error performing loadTypes";
363            logger.error(throwable);
364            throw new MetafacadeException(message, throwable);
365        }
366    }
367
368    /**
369     * Cross reference between package name and namespace abbreviation, used to annotate foreign schema elements
370     */
371    private Map<String, String> packageAbbr = new TreeMap<String, String>();
372    private static final String EMPTY_STRING = "";
373
374    /**
375     * Get a unique list of packages populated from the results of GetTypeMappingElements
376     * @return pkgAbbr TreeSet containing unique package list
377     */
378    protected Collection<String> handleGetPackages()
379    {
380        if (this.elementSet == null || this.elementSet.size()<1)
381        {
382            this.elementSet = (TreeSet<ModelElementFacade>)handleGetTypeMappingElements();
383        }
384        setPkgAbbr(this.elementSet);
385        @SuppressWarnings("unused")
386        String pkgList = EMPTY_STRING;
387        for (final String pkg : this.packageAbbr.keySet())
388        {
389            pkgList += pkg + ", ";
390        }
391        return this.packageAbbr.keySet();
392    }
393
394    /**
395     * @param pkgName
396     * @return this.packageAbbr.get(pkgName)
397     */
398    protected String handleGetPkgAbbr(String pkgName)
399    {
400        if (StringUtils.isBlank(pkgName) || pkgName.length()<1)
401        {
402            return EMPTY_STRING;
403        }
404        if (this.elementSet == null || this.elementSet.size()<1)
405        {
406            this.elementSet = (TreeSet<ModelElementFacade>)handleGetTypeMappingElements();
407        }
408        if (this.packageAbbr == null || this.packageAbbr.size()<1)
409        {
410            setPkgAbbr(this.elementSet);
411        }
412        String rtn = this.packageAbbr.get(pkgName);
413        if (StringUtils.isBlank(rtn))
414        {
415            // Package reference was never added originally - needs to be fixed
416            int namespaceCount = this.packageAbbr.size();
417            rtn = "ns" + namespaceCount;
418            this.packageAbbr.put(pkgName, rtn);
419            logger.info(this.getName() + " missing PkgAbbr for " + pkgName);
420        }
421        return rtn;
422    }
423
424    /**
425     * Creates a list of sorted unique package names and namespace abbreviations for each one.
426     * Run this after running getTypeMappingElements(), to populate the namespace Map.
427     * Namespaces are in order ns1 through x
428     * @param types
429     * @return pkgAbbr
430     */
431    private Map<String, String> setPkgAbbr(Set<ModelElementFacade> types)
432    {
433        Map<String, String> pkgAbbr = new TreeMap<String, String>();
434        int namespaceCount = 1;
435        // Copy package names and abbreviations to package list
436        for (final OperationFacade op : this.getOperations())
437        {
438            for (final Iterator opiterator = op.getExceptions().iterator(); opiterator.hasNext();)
439            {
440                ModelElementFacade arg = (ModelElementFacade)opiterator.next();
441                String pkg = arg.getPackageName();
442                if (!pkgAbbr.containsKey(pkg) && pkg != null && pkg.indexOf('.') > 0)
443                {
444                    pkgAbbr.put(pkg, "ns" + namespaceCount);
445                    namespaceCount++;
446                }
447            }
448            for (final ParameterFacade arg : op.getArguments())
449            {
450                String pkg = arg.getPackageName();
451                if (!pkgAbbr.containsKey(pkg) && pkg != null && pkg.indexOf('.') > 0)
452                {
453                    pkgAbbr.put(pkg, "ns" + namespaceCount);
454                    namespaceCount++;
455                }
456            }
457            if (op.getReturnType()!=null)
458            {
459                String pkg = op.getReturnType().getPackageName();
460                if (!pkgAbbr.containsKey(pkg) && pkg != null && pkg.indexOf('.') > 0)
461                {
462                    pkgAbbr.put(pkg, "ns" + namespaceCount);
463                    namespaceCount++;
464                }
465            }
466        }
467        for (final Iterator iterator = types.iterator(); iterator.hasNext();)
468        {
469            ModelElementFacade type = ((ModelElementFacade)iterator.next());
470            String pkg = type.getPackageName();
471            if (!pkgAbbr.containsKey(pkg) && pkg != null && pkg.indexOf('.') > 0)
472            {
473                pkgAbbr.put(pkg, "ns" + namespaceCount);
474                namespaceCount++;
475            }
476        }
477        this.packageAbbr = pkgAbbr;
478        return pkgAbbr;
479    }
480
481    /**
482     * Cross reference between package name and collection of foreign package referenced elements
483     */
484    private Map<String, Set<String>> packageRefs = new HashMap<String, Set<String>>();
485
486    /**
487     * Get a unique list of packages referenced by the referring package
488     * @param pkg PackageName to find related packages for xs:schema import
489     * @param follow Follow Inheritance references $extensionInheritanceDisabled
490     * @return Collection TreeSet containing referenced package list
491     */
492    protected Collection<String> handleGetPackageReferences(String pkg, boolean follow)
493    {
494        //if (this.elementSet == null || this.elementSet.size()<1)
495        //{
496            this.elementSet = (TreeSet<ModelElementFacade>)handleGetTypeMappingElements();
497        //}
498        //if (this.packageRefs == null || this.packageRefs.size()<1)
499        //{
500            setPkgRefs(this.elementSet, follow);
501        //}
502        return this.packageRefs.get(pkg);
503    }
504
505    /**
506     * Creates a list of referenced packages for each package.
507     * Run this after running getTypeMappingElements(), to populate the namespace Map.
508     * @param types TreeSet of unique packageNames referenced in each package
509     * @param follow Follow Inheritance references $extensionInheritanceDisabled
510     * @return pkgAbbr
511     */
512    private Map<String, Set<String>> setPkgRefs(Set<ModelElementFacade> types, boolean follow)
513    {
514        // Copy package names and collection of related packages to package references list
515        // Iterate through previously collected type references to find all packages referenced by each type
516        for (final Iterator<ModelElementFacade> iterator = types.iterator(); iterator.hasNext();)
517        {
518            try
519            {
520                MetafacadeBase element = (MetafacadeBase)iterator.next();
521                if (element instanceof WSDLTypeLogicImpl)
522                {
523                    WSDLTypeLogicImpl type = (WSDLTypeLogicImpl)element;
524                    String pkg = type.getPackageName();
525                    if (pkg != null && pkg.indexOf('.') > 0)
526                    {
527                        // Duplicates logic in wsdl.vsl so that referenced packages are the same.
528                        for (final Iterator<AttributeFacade> itAttr = type.getAttributes(follow).iterator(); itAttr.hasNext();)
529                        {
530                            try
531                            {
532                                ModelElementFacade attr = (itAttr.next());
533                                if (getType(attr) != null)
534                                {
535                                    attr = getType(attr);
536                                }
537                                addPkgRef(pkg, attr.getPackageName(), attr);
538                            }
539                            catch (Exception e)
540                            {
541                                logger.error("WebServiceLogicImpl.setPkgRefs getAttributes: " + e);
542                            }
543                        }
544                    }
545                }
546                else if (element instanceof WSDLTypeAssociationEndLogicImpl)
547                {
548                    WSDLTypeAssociationEndLogicImpl type = (WSDLTypeAssociationEndLogicImpl)element;
549                    String pkg = type.getPackageName();
550                    if (pkg != null && pkg.indexOf('.') > 0)
551                    {
552                        // Duplicates logic in wsdl.vsl so that referenced packages are the same.
553                        for (final Iterator<AssociationEndFacade> otherEnds = type.getType().getNavigableConnectingEnds(follow).iterator(); otherEnds.hasNext();)
554                        {
555                            try
556                            {
557                                ModelElementFacade otherEnd = ((ModelElementFacade)otherEnds.next());
558                                if (getType(otherEnd) != null)
559                                {
560                                    otherEnd = getType(otherEnd);
561                                }
562                                addPkgRef(pkg, otherEnd.getPackageName(), otherEnd);
563                            }
564                            catch (RuntimeException e)
565                            {
566                                logger.error("WebServiceLogicImpl.setPkgRefs getNavigableConnectingEnds: " + e);
567                            }
568                        }
569                    }
570                }
571                else
572                {
573                    // Log the type so we can extend this logic later...
574                    logger.error("Unexpected element type: " + element);
575                }
576            }
577            catch (Exception e)
578            {
579                logger.error("WebServiceLogicImpl.setPkgRefs types: " + e);
580            }
581        }
582        // Copy package names and collection of related packages to package references list
583        /* for (final Iterator iterator = types.iterator(); iterator.hasNext();)
584        {
585            TreeSet pkgRef;
586            ClassifierFacade element = ((ClassifierFacade)iterator.next());
587            String pkg = element.getPackageName();
588            if (!packageRefs.containsKey(pkg))
589            {
590                // TypeComparator disallows adding nonunique referenced packageNames
591                pkgRef = new TreeSet(new TypeComparator());
592            }
593            else
594            {
595                // Reference to Set contained in pkgAbbr already, can be changed dynamically
596                pkgRef = (TreeSet)packageRefs.get(pkg);
597            }
598            // Duplicates logic in wsdl.vsl so that referenced packages are the same.
599            for (final Iterator itAttr = element.getAttributes(follow).iterator(); itAttr.hasNext();)
600            {
601                ClassifierFacade attr = ((ClassifierFacade)itAttr.next());
602                if (getType(attr) != null)
603                {
604                    attr = getType(attr);
605                }
606                if (!pkgRef.contains(attr) && attr != null && attr.getPackageName().length() > 0)
607                {
608                    pkgRef.add(attr.getPackageName());
609                }
610            }
611            for (final Iterator otherEnds = element.getNavigableConnectingEnds(follow).iterator(); otherEnds.hasNext();)
612            {
613                ClassifierFacade otherEnd = ((ClassifierFacade)otherEnds.next());
614                if (getType(otherEnd) != null)
615                {
616                    otherEnd = getType(otherEnd);
617                }
618                if (!pkgRef.contains(otherEnd))
619                {
620                    pkgRef.add(otherEnd.getPackageName());
621                }
622            }
623            if (!packageRefs.containsKey(pkg))
624            {
625                packageRefs.put(pkg, pkgRef);
626            }
627        } */
628
629        // Add references from the operations of the service package itself
630        for (final OperationFacade op : this.getOperations())
631        {
632            for (final Object exception : op.getExceptions())
633            {
634                ModelElementFacade arg = (ModelElementFacade)exception;
635                addPkgRef(this.getPackageName(), arg.getPackageName(), arg);
636            }
637            for (final ParameterFacade arg : op.getArguments())
638            {
639                addPkgRef(this.getPackageName(), arg.getPackageName(), arg);
640            }
641            if (op.getReturnType()!=null)
642            {
643                String pkg = op.getReturnType().getPackageName();
644                addPkgRef(this.getPackageName(), pkg, op.getReturnType());
645            }
646        }
647        return packageRefs;
648    }
649
650    private void addPkgRef(String pkg, String pkgRef, ModelElementFacade type)
651    {
652        Set<String> pkgRefSet;
653        if (!packageRefs.containsKey(pkg))
654        {
655            // TypeComparator disallows adding nonunique referenced packageNames
656            pkgRefSet = new TreeSet<String>();
657            packageRefs.put(pkg, pkgRefSet);
658        }
659        else
660        {
661            // Reference to Set contained in pkgAbbr already, can be changed dynamically
662            pkgRefSet = packageRefs.get(pkg);
663        }
664        if (pkgRef!=null && pkg!=null &&  !pkgRef.equals(pkg) && pkgRef.indexOf('.') > 0 && !pkgRefSet.contains(pkgRef))
665        {
666            pkgRefSet.add(pkgRef);
667            logger.debug("Added pkgRef " + pkg + " references " + pkgRef + " in " + type.getName());
668        }
669    }
670
671    /**
672     * <p> Checks to see if the <code>types</code> collection contains the
673     * <code>modelElement</code>. It does this by checking to see if the
674     * model element is either an association end or some type of model element
675     * that has a type that's an array. If it's either an array <strong>OR
676     * </strong> an association end, then we check to see if the type is stored
677     * within the <code>types</code> collection. If so, we return true,
678     * otherwise we return false.
679     * </p>
680     *
681     * @param types the previously collected types.
682     * @param modelElement the model element to check to see if it represents a
683     *        <code>many</code> type
684     * @return true/false depending on whether or not the model element is a
685     *         many type.
686     */
687    private boolean containsManyType(
688        final Collection<ModelElementFacade> types,
689        final Object modelElement)
690    {
691        final ClassifierFacade compareType = this.getClassifier(modelElement);
692        boolean containsManyType = false;
693        if (compareType != null)
694        {
695            containsManyType =
696                CollectionUtils.find(
697                    types,
698                    new Predicate()
699                    {
700                        public boolean evaluate(Object object)
701                        {
702                            return compareType.equals(getClassifier(object));
703                        }
704                    }) != null;
705        }
706        return containsManyType;
707    }
708
709    /**
710     * Attempts to get the classifier attached to the given <code>element</code>.
711     *
712     * @param element the element from which to retrieve the classifier.
713     * @return the classifier if found, null otherwise
714     */
715    private ClassifierFacade getClassifier(final Object element)
716    {
717        ClassifierFacade type = null;
718        if (element instanceof AssociationEndFacade)
719        {
720            AssociationEndFacade end = (AssociationEndFacade)element;
721            if (end.isMany())
722            {
723                type = ((AssociationEndFacade)element).getType();
724            }
725        }
726        else if (element instanceof AttributeFacade)
727        {
728            type = ((AttributeFacade)element).getType();
729        }
730        else if (element instanceof ParameterFacade)
731        {
732            type = ((ParameterFacade)element).getType();
733        }
734        if (element instanceof ClassifierFacade)
735        {
736            type = (ClassifierFacade)element;
737        }
738        if (type != null)
739        {
740            if (type.isArrayType())
741            {
742                type = type.getNonArray();
743            }
744        }
745        return type;
746    }
747
748    /**
749     * Returns true/false depending on whether or not this class represents a valid association end (meaning it has a
750     * multiplicity of many)
751     *
752     * @param modelElement the model element to check.
753     * @return true/false
754     */
755    private boolean isValidAssociationEnd(Object modelElement)
756    {
757        return modelElement instanceof AssociationEndFacade && ((AssociationEndFacade)modelElement).isMany();
758    }
759
760    /**
761     * @return this.getConfiguredProperty("defaultProvider")
762     * @see org.andromda.cartridges.webservice.metafacades.WebService#getProvider()
763     */
764    protected String handleGetProvider()
765    {
766        String provider = (String)this.findTaggedValue(UMLProfile.TAGGEDVALUE_WEBSERVICE_PROVIDER);
767        if (StringUtils.isBlank(provider) || provider.equals(DEFAULT))
768        {
769            provider = (String)this.getConfiguredProperty("defaultProvider");
770        }
771        return provider;
772    }
773
774    /**
775     * @return this.getConfiguredProperty(UMLMetafacadeProperties.NAMESPACE_SEPARATOR)
776     * @see org.andromda.cartridges.webservice.metafacades.WebService#getWsdlFile()
777     */
778    protected String handleGetWsdlFile()
779    {
780        return StringUtils.replace(
781            this.getFullyQualifiedName(),
782            String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.NAMESPACE_SEPARATOR)),
783            "/") + ".wsdl";
784    }
785
786    /**
787     * We use this comparator to actually eliminate duplicates instead of sorting like a comparator is normally used.
788     */
789    public final class TypeComparator
790        implements Comparator
791    {
792        private final Collator collator = Collator.getInstance();
793
794        /**
795         * We use this comparator to actually eliminate duplicates instead of sorting like a comparator is normally used.
796         */
797        public TypeComparator()
798        {
799            collator.setStrength(Collator.PRIMARY);
800        }
801
802        /**
803         * @see java.util.Comparator#compare(Object, Object)
804         */
805        public int compare(
806            Object objectA,
807            Object objectB)
808        {
809            final ModelElementFacade a = (ModelElementFacade)objectA;
810            ModelElementFacade aType = getType(a);
811            if (aType == null)
812            {
813                aType = a;
814            }
815            final ModelElementFacade b = (ModelElementFacade)objectB;
816            ModelElementFacade bType = getType(b);
817            if (bType == null)
818            {
819                bType = b;
820            }
821            return collator.compare(
822                aType.getFullyQualifiedName(),
823                bType.getFullyQualifiedName());
824        }
825    }
826
827    /**
828     * Gets the <code>type</code> or <code>returnType</code> of the model element (if the model element has a type or
829     * returnType).
830     *
831     * @param modelElement the model element we'll retrieve the type of.
832     * @return ClassifierFacade Type of modelElement Object
833     */
834    public ClassifierFacade getType(Object modelElement)
835    {
836        try
837        {
838            final Introspector introspector = Introspector.instance();
839            ClassifierFacade type = null;
840            String typeProperty = "type";
841
842            // only continue if the model element has a type
843            if (introspector.isReadable(modelElement, typeProperty))
844            {
845                type = (ClassifierFacade)introspector.getProperty(modelElement, typeProperty);
846            }
847
848            // try for return type if type wasn't found
849            typeProperty = "returnType";
850            if (type == null && introspector.isReadable(modelElement, typeProperty))
851            {
852                type = (ClassifierFacade)introspector.getProperty(modelElement, typeProperty);
853            }
854            // Sometimes the type is sent to this method instead of the property
855            if (type == null && modelElement instanceof ClassifierFacade)
856            {
857                type = (ClassifierFacade)modelElement;
858            }
859            return type;
860        }
861        catch (final Throwable throwable)
862        {
863            String errMsg = "Error performing WebServiceLogicImpl.getType";
864            logger.error(errMsg, throwable);
865            throw new MetafacadeException(errMsg, throwable);
866        }
867    }
868
869    /**
870     * namespacePrefix
871     */
872    static final String NAMESPACE_PREFIX = "namespacePrefix";
873
874    /**
875     * @return this.getConfiguredProperty(NAMESPACE_PREFIX)
876     * @see org.andromda.cartridges.webservice.metafacades.WSDLType#getNamespacePrefix()
877     */
878    protected String handleGetNamespacePrefix()
879    {
880        String prefix = (String)this.getConfiguredProperty(NAMESPACE_PREFIX);
881        if (StringUtils.isBlank(prefix))
882        {
883            prefix = "impl";
884        }
885        return prefix;
886    }
887
888    /**
889     * qualifiedNameLocalPartPattern
890     */
891    static final String QNAME_LOCAL_PART_PATTERN = "qualifiedNameLocalPartPattern";
892
893    /**
894     * Gets the <code>qualifiedNameLocalPartPattern</code> for this service.
895     * @return this.getConfiguredProperty(QNAME_LOCAL_PART_PATTERN)
896     */
897    protected String getQualifiedNameLocalPartPattern()
898    {
899        String pattern = (String)this.getConfiguredProperty(QNAME_LOCAL_PART_PATTERN);
900        if (StringUtils.isBlank(pattern))
901        {
902            pattern = "{0}";
903        }
904        return pattern;
905    }
906
907    /**
908     * namespacePattern
909     */
910    static final String NAMESPACE_PATTERN = "namespacePattern";
911
912    /**
913     * Gets the <code>namespacePattern</code> for this service.
914     *
915     * @return String the namespace pattern to use.
916     */
917    protected String getNamespacePattern()
918    {
919        String pattern = (String)this.getConfiguredProperty(NAMESPACE_PATTERN);
920        if (StringUtils.isBlank(pattern))
921        {
922            pattern = "http://{0}/";
923        }
924        return pattern;
925    }
926
927    /**
928     * reverseNamespace
929     */
930    static final String REVERSE_NAMESPACE = "reverseNamespace";
931
932    /**
933     * Gets whether or not <code>reverseNamespace</code> is true/false for this type.
934     *
935     * @return boolean true/false
936     */
937    protected boolean isReverseNamespace()
938    {
939        return Boolean.valueOf(String.valueOf(this.getConfiguredProperty(REVERSE_NAMESPACE))).booleanValue();
940    }
941
942    /**
943     * @return this.getEjbJndiNamePrefix() + ejb/ + this.getFullyQualifiedName()
944     * @see org.andromda.cartridges.webservice.metafacades.WebService#getEjbJndiName()
945     */
946    protected String handleGetEjbJndiName()
947    {
948        StringBuilder jndiName = new StringBuilder();
949        String jndiNamePrefix = StringUtils.trimToEmpty(this.getEjbJndiNamePrefix());
950        if (StringUtils.isNotBlank(jndiNamePrefix))
951        {
952            jndiName.append(jndiNamePrefix);
953            jndiName.append('/');
954        }
955        jndiName.append("ejb/");
956        jndiName.append(this.getFullyQualifiedName());
957        return jndiName.toString();
958    }
959
960    /**
961     * Gets the <code>ejbJndiNamePrefix</code> for an EJB provider.
962     *
963     * @return the EJB Jndi name prefix.
964     */
965    protected String getEjbJndiNamePrefix()
966    {
967        final String property = "ejbJndiNamePrefix";
968        return this.isConfiguredProperty(property) ? ObjectUtils.toString(this.getConfiguredProperty(property)) : null;
969    }
970
971    /**
972     * @return this.getEjbHomeInterfacePattern() formatted as this.getPackageName() + this.getName()
973     * @see org.andromda.cartridges.webservice.metafacades.WebService#getEjbHomeInterface()
974     */
975    protected String handleGetEjbHomeInterface()
976    {
977        return MessageFormat.format(
978            this.getEjbHomeInterfacePattern(),
979                StringUtils.trimToEmpty(this.getPackageName()), StringUtils.trimToEmpty(this.getName()));
980    }
981
982    /**
983     * Gets the <code>ejbHomeInterfacePattern</code> for an EJB provider.
984     *
985     * @return the EJB Home interface pattern
986     */
987    protected String getEjbHomeInterfacePattern()
988    {
989        return (String)this.getConfiguredProperty("ejbHomeInterfacePattern");
990    }
991
992    /**
993     * @return this.getEjbInterfacePattern() formatted as this.getPackageName() + this.getName()
994     * @see org.andromda.cartridges.webservice.metafacades.WebService#getEjbInterface()
995     */
996    protected String handleGetEjbInterface()
997    {
998        return MessageFormat.format(
999            this.getEjbInterfacePattern(),
1000                StringUtils.trimToEmpty(this.getPackageName()), StringUtils.trimToEmpty(this.getName()));
1001    }
1002
1003    /**
1004     * Gets the <code>ejbInterfacePattern</code> for an EJB provider.
1005     *
1006     * @return the EJB interface pattern
1007     */
1008    protected String getEjbInterfacePattern()
1009    {
1010        return (String)this.getConfiguredProperty("ejbInterfacePattern");
1011    }
1012
1013    private static final String RPC_CLASS_NAME_PATTERN = "rpcClassNamePattern";
1014
1015    /**
1016     * Gets the <code>rpcClassNamePattern</code> for this service.
1017     * @return this.getConfiguredProperty(RPC_CLASS_NAME_PATTERN)
1018     */
1019    protected String getRpcClassNamePattern()
1020    {
1021        return (String)this.getConfiguredProperty(RPC_CLASS_NAME_PATTERN);
1022    }
1023
1024    /**
1025     * @return this.getRpcClassNamePattern() formatted as this.getPackageName() + this.getName()
1026     * @see org.andromda.cartridges.webservice.metafacades.WebService#getRpcClassName()
1027     */
1028    protected String handleGetRpcClassName()
1029    {
1030        return MessageFormat.format(
1031            this.getRpcClassNamePattern(),
1032                StringUtils.trimToEmpty(this.getPackageName()), StringUtils.trimToEmpty(this.getName()));
1033    }
1034
1035    private static final String WSDL_OPERATION_SORT_MODE = "wsdlOperationSortMode";
1036
1037    /**
1038     * Used to sort operations by <code>name</code>.
1039     */
1040    public static final class OperationNameComparator
1041        implements Comparator
1042    {
1043        private final Collator collator = Collator.getInstance();
1044
1045        /**
1046         *
1047         */
1048        public OperationNameComparator()
1049        {
1050            collator.setStrength(Collator.PRIMARY);
1051        }
1052
1053        /**
1054         * @see java.util.Comparator#compare(Object, Object)
1055         */
1056        public int compare(
1057            Object objectA,
1058            Object objectB)
1059        {
1060            ModelElementFacade a = (ModelElementFacade)objectA;
1061            ModelElementFacade b = (ModelElementFacade)objectB;
1062
1063            return collator.compare(
1064                a.getName(),
1065                b.getName());
1066        }
1067    }
1068
1069    /**
1070     * The model specifying operations should be sorted by name.
1071     */
1072    private static final String OPERATION_SORT_MODE_NAME = "name";
1073
1074    /**
1075     * The model specifying operations should NOT be sorted.
1076     */
1077    private static final String OPERATION_SORT_MODE_NONE = "none";
1078
1079    /**
1080     * Gets the sort mode WSDL operations.
1081     *
1082     * @return String
1083     */
1084    private String getWSDLOperationSortMode()
1085    {
1086        Object property = this.getConfiguredProperty(WSDL_OPERATION_SORT_MODE);
1087        return property != null ? (property.equals(OPERATION_SORT_MODE_NAME) ? (String)property : OPERATION_SORT_MODE_NONE) : OPERATION_SORT_MODE_NONE;
1088    }
1089
1090    /**
1091     * @return !this.getAllRoles().isEmpty()
1092     * @see org.andromda.cartridges.webservice.metafacades.WebService#isSecured()
1093     */
1094    protected boolean handleIsSecured()
1095    {
1096        Collection<Role> roles = this.getAllRoles();
1097        return roles != null && !roles.isEmpty();
1098    }
1099
1100    /**
1101     * Overridden to only allow the exposed operations in the returned roles collection.
1102     *
1103     * @see org.andromda.metafacades.uml.Service#getAllRoles()
1104     */
1105    public Collection<Role> getAllRoles()
1106    {
1107        final Collection<Role> roles = new LinkedHashSet<Role>(this.getRoles());
1108        CollectionUtils.forAllDo(
1109            this.getAllowedOperations(),
1110            new Closure()
1111            {
1112                public void execute(Object object)
1113                {
1114                    if (object != null && ServiceOperation.class.isAssignableFrom(object.getClass()))
1115                    {
1116                        roles.addAll(((ServiceOperation)object).getRoles());
1117                    }
1118                }
1119            });
1120        return roles;
1121    }
1122
1123    /**
1124     * The pattern used to construct the test package name.
1125     */
1126    private static final String TEST_PACKAGE_NAME_PATTERN = "testPackageNamePattern";
1127
1128    /**
1129     * @return this.getPackageName() formatted as this.getConfiguredProperty(TEST_PACKAGE_NAME_PATTERN)
1130     * @see org.andromda.cartridges.webservice.metafacades.WebService#getTestPackageName()
1131     */
1132    protected String handleGetTestPackageName()
1133    {
1134        return String.valueOf(this.getConfiguredProperty(TEST_PACKAGE_NAME_PATTERN)).replaceAll(
1135            "\\{0\\}",
1136            this.getPackageName());
1137    }
1138
1139    /**
1140     * @return this.getTestPackageName() + '.' + this.getTestName()
1141     * @see org.andromda.cartridges.webservice.metafacades.WebService#getFullyQualifiedTestName()
1142     */
1143    protected String handleGetFullyQualifiedTestName()
1144    {
1145        return this.getTestPackageName() + '.' + this.getTestName();
1146    }
1147
1148    /**
1149     * The pattern used to construct the test name.
1150     */
1151    private static final String TEST_NAME_PATTERN = "testNamePattern";
1152
1153    /**
1154     * @return this.getName() formatted with this.getConfiguredProperty(TEST_NAME_PATTERN)
1155     * @see org.andromda.cartridges.webservice.metafacades.WebService#getTestName()
1156     */
1157    protected String handleGetTestName()
1158    {
1159        return String.valueOf(this.getConfiguredProperty(TEST_NAME_PATTERN)).replaceAll(
1160            "\\{0\\}",
1161            this.getName());
1162    }
1163
1164    /**
1165     * Represents a "wrapped" style.
1166     */
1167    private static final String STYLE_WRAPPED = "wrapped";
1168
1169    /**
1170     * @return this.getStyle().equalsIgnoreCase(STYLE_WRAPPED)
1171     * @see org.andromda.cartridges.webservice.metafacades.WebService#isWrappedStyle()
1172     */
1173    protected boolean handleIsWrappedStyle()
1174    {
1175        return this.getStyle().equalsIgnoreCase(STYLE_WRAPPED);
1176    }
1177
1178    /**
1179     * Represents a "document" style.
1180     */
1181    private static final String STYLE_DOCUMENT = "document";
1182
1183    /**
1184     * @return this.getStyle().equalsIgnoreCase("document")
1185     * @see org.andromda.cartridges.webservice.metafacades.WebService#isDocumentStyle()
1186     */
1187    protected boolean handleIsDocumentStyle()
1188    {
1189        return this.getStyle().equalsIgnoreCase(STYLE_DOCUMENT);
1190    }
1191
1192    /**
1193     * Represents a "rpc" style.
1194     */
1195    private static final String STYLE_RPC = "rpc";
1196
1197    /**
1198     * @return this.getStyle().equalsIgnoreCase("rpc")
1199     * @see org.andromda.cartridges.webservice.metafacades.WebService#isRpcStyle()
1200     */
1201    protected boolean handleIsRpcStyle()
1202    {
1203        return this.getStyle().equalsIgnoreCase(STYLE_RPC);
1204    }
1205
1206    /**
1207     * Represents an "literal" use.
1208     */
1209    private static final String USE_LITERAL = "literal";
1210
1211    /**
1212     * @return this.getStyle().equalsIgnoreCase("literal")
1213     * @see org.andromda.cartridges.webservice.metafacades.WebService#isLiteralUse()
1214     */
1215    protected boolean handleIsLiteralUse()
1216    {
1217        return this.getStyle().equalsIgnoreCase(USE_LITERAL);
1218    }
1219
1220    /**
1221     * Represents an "encoded" use.
1222     */
1223    private static final String USE_ENCODED = "encoded";
1224
1225    /**
1226     * @return this.getStyle().equalsIgnoreCase("encoded")
1227     * @see org.andromda.cartridges.webservice.metafacades.WebService#isEncodedUse()
1228     */
1229    protected boolean handleIsEncodedUse()
1230    {
1231        return this.getStyle().equalsIgnoreCase(USE_ENCODED);
1232    }
1233
1234    /**
1235     * The pattern used to construct the test implementation name.
1236     */
1237    private static final String TEST_IMPLEMENTATION_NAME_PATTERN = "testImplementationNamePattern";
1238
1239    /**
1240     * @return this.getName() formatted as this.getConfiguredProperty(TEST_IMPLEMENTATION_NAME_PATTERN)
1241     * @see org.andromda.cartridges.webservice.metafacades.WebService#getTestImplementationName()
1242     */
1243    protected String handleGetTestImplementationName()
1244    {
1245        return String.valueOf(this.getConfiguredProperty(TEST_IMPLEMENTATION_NAME_PATTERN)).replaceAll(
1246            "\\{0\\}",
1247            this.getName());
1248    }
1249
1250    /**
1251     * @return this.getTestPackageName() + '.' + this.getTestImplementationName()
1252     * @see org.andromda.cartridges.webservice.metafacades.WebService#getFullyQualifiedTestImplementationName()
1253     */
1254    protected String handleGetFullyQualifiedTestImplementationName()
1255    {
1256        return this.getTestPackageName() + '.' + this.getTestImplementationName();
1257    }
1258
1259    /**
1260
1261     * @return TypeMappings from WebServiceGlobals.SCHEMA_TYPE_MAPPINGS_URI "schemaTypeMappingsUri"
1262     * @see org.andromda.cartridges.webservice.metafacades.WebService#getSchemaMappings()
1263     */
1264    protected TypeMappings handleGetSchemaMappings()
1265    {
1266        final String propertyName = WebServiceGlobals.SCHEMA_TYPE_MAPPINGS_URI;
1267        Object property = this.getConfiguredProperty(propertyName);
1268        TypeMappings mappings = null;
1269        String uri = null;
1270        if (property instanceof String)
1271        {
1272            uri = (String)property;
1273            try
1274            {
1275                mappings = TypeMappings.getInstance(uri);
1276                mappings.setArraySuffix(this.getArraySuffix());
1277                this.setProperty(propertyName, mappings);
1278            }
1279            catch (Throwable th)
1280            {
1281                String errMsg = "Error getting '" + propertyName + "' --> '" + uri + '\'';
1282                logger.error(errMsg, th);
1283                // don't throw the exception
1284            }
1285        }
1286        else
1287        {
1288            mappings = (TypeMappings)property;
1289        }
1290        return mappings;
1291    }
1292
1293    /**
1294     * Gets the array suffix from the configured metafacade properties.
1295     *
1296     * @return the array suffix.
1297     */
1298    private String getArraySuffix()
1299    {
1300        return String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.ARRAY_NAME_SUFFIX));
1301    }
1302
1303    /**
1304     * @see org.andromda.cartridges.webservice.metafacades.WebServiceLogic#handleGetAllowedOperationExceptions()
1305     */
1306    protected Collection handleGetAllowedOperationExceptions()
1307    {
1308        final Collection exceptions = new HashSet();
1309
1310        // collect the exceptions of all allowed operations into a single set
1311        for (final OperationFacade operation : this.getAllowedOperations())
1312        {
1313            exceptions.addAll(operation.getExceptions());
1314        }
1315
1316        return exceptions;
1317    }
1318
1319    /**
1320     * @return packages from this.getAllowedOperations()
1321     * @see org.andromda.cartridges.webservice.WebServiceUtils#getPackages(WebServiceLogicImpl, Set, boolean)
1322     */
1323    public Collection<PackageFacade> getPackages() {
1324        return new WebServiceUtils().getPackages(this, (Set) this.getAllowedOperations(), true);
1325    }
1326
1327    /**
1328     * @param pkg
1329     * @return WebServiceUtils().getPkgAbbr(pkg)
1330     * @see org.andromda.cartridges.webservice.metafacades.WebServiceLogicImpl#getPkgAbbr(PackageFacade)
1331     */
1332    public String getPkgAbbr(PackageFacade pkg) {
1333        return new WebServiceUtils().getPkgAbbr(pkg);
1334    }
1335
1336    /**
1337     * The property defining if the web service XML should be validated against the wsdl/xsd schema.
1338     */
1339    private static final String PROPERTY_SCHEMA_VALIDATION = "schemaValidation";
1340    private static final String BOOLEAN_FALSE = "false";
1341    private static final String BOOLEAN_TRUE = "true";
1342
1343    /**
1344     * @see org.andromda.cartridges.webservice.metafacades.WebServiceLogic#handleIsSchemaValidation()
1345     */
1346    @Override
1347    protected boolean handleIsSchemaValidation()
1348    {
1349        String mode = (String)this.findTaggedValue(WebServiceGlobals.XML_SCHEMA_VALIDATION);
1350        if (StringUtils.isBlank(mode) || mode.equals(DEFAULT))
1351        {
1352            mode = String.valueOf(this.getConfiguredProperty(PROPERTY_SCHEMA_VALIDATION));
1353        }
1354        if (StringUtils.isBlank(mode) || mode.equals(DEFAULT))
1355        {
1356            mode = BOOLEAN_FALSE;
1357        }
1358        return Boolean.parseBoolean(mode);
1359    }
1360
1361    /**
1362     * The property defining the default style to give the web services.
1363     */
1364    private static final String PROPERTY_SIMPLE_BINDING_MODE = "simpleBindingMode";
1365
1366    /**
1367     * @see org.andromda.cartridges.webservice.metafacades.WebServiceLogic#handleIsSimpleBindingMode()
1368     */
1369    @Override
1370    protected boolean handleIsSimpleBindingMode()
1371    {
1372        String mode = (String)this.findTaggedValue(WebServiceGlobals.JAXB_SIMPLE_BINDING_MODE);
1373        if (StringUtils.isBlank(mode) || mode.equals(DEFAULT))
1374        {
1375            mode = String.valueOf(this.getConfiguredProperty(PROPERTY_SIMPLE_BINDING_MODE));
1376        }
1377        return Boolean.parseBoolean(mode);
1378    }
1379
1380    /**
1381     * The property defining the Jaxb XJC arguments used with wsdl2java utility.
1382     * @see org.andromda.cartridges.webservice.metafacades.WebServiceLogic#getSchemaMappings()
1383     */
1384    private static final String PROPERTY_XJC_ARGUMENTS = "xjcArguments";
1385
1386    /**
1387     * @see org.andromda.cartridges.webservice.metafacades.WebServiceLogic#getXjcArguments()
1388     */
1389    @Override
1390    protected String handleGetXjcArguments()
1391    {
1392        String mode = (String)this.findTaggedValue(WebServiceGlobals.JAXB_XJC_ARGUMENTS);
1393        if (StringUtils.isBlank(mode) || mode.equals(DEFAULT))
1394        {
1395            mode = String.valueOf(this.getConfiguredProperty(PROPERTY_XJC_ARGUMENTS));
1396        }
1397        return mode;
1398    }
1399
1400    /**
1401     * @see org.andromda.cartridges.webservice.metafacades.WebServiceLogic#getRestCacheType()
1402     */
1403    @Override
1404    protected String handleGetRestCacheType()
1405    {
1406        String cacheType = (String)this.findTaggedValue(WebServiceGlobals.CACHE_TYPE);
1407        if (!(this.getRestCount()>0) || StringUtils.isBlank(cacheType) || cacheType.equals(DEFAULT))
1408        {
1409            cacheType = EMPTY_STRING;
1410        }
1411        return cacheType;
1412    }
1413
1414    /**
1415     * @see org.andromda.cartridges.webservice.metafacades.WebServiceLogic#getRestConsumes()
1416     */
1417    @Override
1418    protected String handleGetRestConsumes()
1419    {
1420        String consumes = (String)this.findTaggedValue(WebServiceGlobals.REST_CONSUMES);
1421        if (!(this.getRestCount()>0) || StringUtils.isBlank(consumes) || consumes.equals(DEFAULT))
1422        {
1423            consumes = EMPTY_STRING;
1424        }
1425        else
1426        {
1427            consumes = WebServiceOperationLogicImpl.translateMediaType(consumes);
1428        }
1429        return consumes;
1430    }
1431
1432    /**
1433     * Contexts should be in the form fullyqualifiedclassname variable
1434     * @see org.andromda.cartridges.webservice.metafacades.WebServiceLogic#getRestContexts()
1435     */
1436    @Override
1437    protected List<String> handleGetRestContexts()
1438    {
1439        List<String> contexts = new ArrayList<String>();
1440        String context = (String)this.findTaggedValue(WebServiceGlobals.REST_CONTEXT);
1441        if (!(this.getRestCount()>0) || StringUtils.isBlank(context) || context.equals(DEFAULT))
1442        {
1443            context = EMPTY_STRING;
1444        }
1445        else
1446        {
1447            // Parse comma/pipe/semicolon delimited elements into ArrayList
1448            String[] parsed = StringUtils.split(context, ",;|");
1449            for (int i=0; i<parsed.length; i++)
1450            {
1451                contexts.add(parsed[i]);
1452            }
1453        }
1454        return contexts;
1455    }
1456
1457    /**
1458     * @see org.andromda.cartridges.webservice.metafacades.WebServiceLogic#getRestMethod()
1459     */
1460    @Override
1461    protected String handleGetRestMethod()
1462    {
1463        String method = (String)this.findTaggedValue(WebServiceGlobals.REST_HTTP_METHOD);
1464        if (!(this.getRestCount()>0) || StringUtils.isBlank(method) || method.equals(DEFAULT))
1465        {
1466            method = EMPTY_STRING;
1467        }
1468        return method;
1469    }
1470
1471    private static final String SLASH = "/";
1472    private static final String QUOTE = "\"";
1473    //private static final String LBRACKET = "{";
1474    //private static final String RBRACKET = "}";
1475    /**
1476     * @see org.andromda.cartridges.webservice.metafacades.WebServiceLogic#getRestPath()
1477     */
1478    @Override
1479    protected String handleGetRestPath()
1480    {
1481        String path = (String)this.findTaggedValue(WebServiceGlobals.REST_PATH);
1482        if (StringUtils.isBlank(path))
1483        {
1484            path = EMPTY_STRING;
1485        }
1486        if (!(this.getRestCount()>0) || StringUtils.isBlank(path) || path.equals(DEFAULT))
1487        {
1488            path = QUOTE + SLASH + this.getName().toLowerCase() + SLASH + QUOTE;
1489        }
1490        else
1491        {
1492            if (!path.startsWith(QUOTE))
1493            {
1494                path = QUOTE + path;
1495            }
1496            if (!path.endsWith(QUOTE) || path.length()<2)
1497            {
1498                path = path + QUOTE;
1499            }
1500        }
1501        return path;
1502    }
1503
1504    //private static final String PRODUCE_DEFAULT = "application/xml";
1505    /**
1506     * @see org.andromda.cartridges.webservice.metafacades.WebServiceLogic#getRestProduces()
1507     */
1508    @Override
1509    protected String handleGetRestProduces()
1510    {
1511        return WebServiceOperationLogicImpl.translateMediaType((String)this.findTaggedValue(WebServiceGlobals.REST_PRODUCES));
1512    }
1513
1514    /**
1515     * @see org.andromda.cartridges.webservice.metafacades.WebServiceLogic#getRestProvider()
1516     */
1517    @Override
1518    protected String handleGetRestProvider()
1519    {
1520        String provider = (String)this.findTaggedValue(WebServiceGlobals.REST_PROVIDER);
1521        if (!(this.getRestCount()>0) || StringUtils.isBlank(provider) || provider.equals(DEFAULT))
1522        {
1523            provider = EMPTY_STRING;
1524        }
1525        return provider;
1526    }
1527
1528    /**
1529     * @see org.andromda.cartridges.webservice.metafacades.WebServiceLogic#getRestRetention()
1530     */
1531    @Override
1532    protected String handleGetRestRetention()
1533    {
1534        String retention = (String)this.findTaggedValue(WebServiceGlobals.REST_RETENTION);
1535        if (!(this.getRestCount()>0) || StringUtils.isBlank(retention) || retention.equals(DEFAULT))
1536        {
1537            retention = EMPTY_STRING;
1538        }
1539        return retention;
1540    }
1541
1542    /**
1543     * @see org.andromda.cartridges.webservice.metafacades.WebServiceLogic#getRestTarget()
1544     */
1545    @Override
1546    protected String handleGetRestTarget()
1547    {
1548        String target = (String)this.findTaggedValue(WebServiceGlobals.REST_TARGET);
1549        if (!(this.getRestCount()>0) || StringUtils.isBlank(target) || target.equals(DEFAULT))
1550        {
1551            target = EMPTY_STRING;
1552        }
1553        return target;
1554    }
1555
1556    /**
1557     * @see org.andromda.cartridges.webservice.metafacades.WebServiceLogic#handleIsRestAtom()
1558     */
1559    @Override
1560    protected boolean handleIsRestAtom()
1561    {
1562        boolean restAtom = false;
1563        if (this.getRestCount()>0)
1564        {
1565            Collection<WebServiceOperation> operations = this.getAllowedOperations();
1566            for (WebServiceOperation operation : operations)
1567            {
1568                String restProduces = operation.getRestProduces();
1569                if (StringUtils.isNotBlank(restProduces) && restProduces.contains("atom"))
1570                {
1571                    restAtom = true;
1572                    break;
1573                }
1574            }
1575            if (!restAtom)
1576            {
1577                restAtom = StringUtils.isNotBlank(this.getRestProduces()) && this.getRestProduces().indexOf("atom") > -1;
1578            }
1579        }
1580        return restAtom;
1581    }
1582
1583    /**
1584     * @see org.andromda.cartridges.webservice.metafacades.WebServiceLogic#handleGetRestCount()
1585     */
1586    @Override
1587    protected int handleGetRestCount()
1588    {
1589        int restCount = 0;
1590        String rest = (String)this.findTaggedValue(WebServiceGlobals.REST);
1591        for (WebServiceOperation operation : this.getAllowedOperations())
1592        {
1593            if (StringUtils.isNotBlank(rest) && (operation.isRest() || rest.equals(BOOLEAN_TRUE)))
1594            {
1595                restCount++;
1596            }
1597        }
1598        return restCount;
1599    }
1600
1601    /**
1602     * @see org.andromda.cartridges.webservice.metafacades.WebServiceLogic#handleGetJaxwsCount()
1603     */
1604    @Override
1605    protected int handleGetJaxwsCount()
1606    {
1607        int jaxwsCount = 0;
1608        String rest = (String)this.findTaggedValue(WebServiceGlobals.REST);
1609        for (WebServiceOperation operation : this.getAllowedOperations())
1610        {
1611            if (StringUtils.isBlank(rest) || rest.equals(BOOLEAN_FALSE) && (!operation.isRest()))
1612            {
1613                jaxwsCount++;
1614            }
1615        }
1616        return jaxwsCount;
1617    }
1618
1619    /**
1620     * Used to map between XML (list of restricted strings) and Java enum (has both a name and a value).
1621     *
1622     * @return useEnumValueInXSD true if EnumerationLiteral.value is used instead of EnumerationLiteral.Name.
1623     */
1624    public Boolean useEnumValueInXSD()
1625    {
1626        final String propertyName = WebServiceGlobals.USE_ENUM_VALUE_IN_XSD;
1627        // getConfiguredProperty is protected so we must wrap it for use in WebServiceUtils
1628        Object property = this.getConfiguredProperty(propertyName);
1629        if (property != null && String.class.isAssignableFrom(property.getClass()))
1630        {
1631            return Boolean.valueOf(String.valueOf(property)).booleanValue();
1632        }
1633        return Boolean.TRUE;
1634    }
1635
1636    /**
1637     * @param validationMessages Collection<ModelValidationMessage>
1638     * @see MetafacadeBase#validateInvariants(Collection validationMessages)
1639     */
1640    @Override
1641    public void validateInvariants(Collection<ModelValidationMessage> validationMessages)
1642    {
1643        super.validateInvariants(validationMessages);
1644        try
1645        {
1646            final Object contextElement = this.THIS();
1647            final String name = (String)OCLIntrospector.invoke(contextElement,"name");
1648            boolean constraintValid = OCLExpressions.equal(
1649                name.substring(0,1).toUpperCase(),
1650                name.substring(0,1));
1651            if (!constraintValid)
1652            {
1653                validationMessages.add(
1654                    new ModelValidationMessage(
1655                        (MetafacadeBase)contextElement ,
1656                        "org::andromda::cartridges::webservice::metafacades::WebService::class name must start with an uppercase letter",
1657                        "WebService Class name must start with an uppercase letter."));
1658            }
1659        }
1660        catch (Throwable th)
1661        {
1662            Throwable cause = th.getCause();
1663            int depth = 0; // Some throwables have infinite recursion
1664            while (cause != null && depth < 7)
1665            {
1666                th = cause;
1667                depth++;
1668            }
1669            logger.error("Error validating constraint 'org::andromda::cartridges::webservice::WebService::class name must start with an uppercase letter' ON "
1670                + this.THIS().toString() + ": " + th.getMessage(), th);
1671        }
1672    }
1673}