1/*
2 * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.xml.internal.ws.wsdl.writer;
27
28
29import static com.sun.xml.internal.bind.v2.schemagen.Util.*;
30
31import com.oracle.webservices.internal.api.databinding.WSDLResolver;
32
33import com.sun.xml.internal.txw2.TXW;
34import com.sun.xml.internal.txw2.TypedXmlWriter;
35import com.sun.xml.internal.txw2.output.ResultFactory;
36import com.sun.xml.internal.txw2.output.XmlSerializer;
37import com.sun.xml.internal.txw2.output.TXWResult;
38import com.sun.xml.internal.ws.api.SOAPVersion;
39import com.sun.xml.internal.ws.api.WSBinding;
40import com.sun.xml.internal.ws.api.model.JavaMethod;
41import com.sun.xml.internal.ws.api.model.MEP;
42import com.sun.xml.internal.ws.api.model.ParameterBinding;
43import com.sun.xml.internal.ws.api.model.SEIModel;
44import com.sun.xml.internal.ws.api.model.soap.SOAPBinding;
45import com.sun.xml.internal.ws.api.server.Container;
46import com.sun.xml.internal.ws.api.wsdl.writer.WSDLGeneratorExtension;
47import com.sun.xml.internal.ws.api.wsdl.writer.WSDLGenExtnContext;
48import com.sun.xml.internal.ws.model.AbstractSEIModelImpl;
49import com.sun.xml.internal.ws.model.CheckedExceptionImpl;
50import com.sun.xml.internal.ws.model.JavaMethodImpl;
51import com.sun.xml.internal.ws.model.ParameterImpl;
52import com.sun.xml.internal.ws.model.WrapperParameter;
53import com.sun.xml.internal.ws.util.xml.XmlUtil;
54import com.sun.xml.internal.ws.wsdl.parser.SOAPConstants;
55import com.sun.xml.internal.ws.wsdl.parser.WSDLConstants;
56import com.sun.xml.internal.ws.wsdl.writer.document.Binding;
57import com.sun.xml.internal.ws.wsdl.writer.document.BindingOperationType;
58import com.sun.xml.internal.ws.wsdl.writer.document.Definitions;
59import com.sun.xml.internal.ws.wsdl.writer.document.Fault;
60import com.sun.xml.internal.ws.wsdl.writer.document.FaultType;
61import com.sun.xml.internal.ws.wsdl.writer.document.Import;
62import com.sun.xml.internal.ws.wsdl.writer.document.Message;
63import com.sun.xml.internal.ws.wsdl.writer.document.Operation;
64import com.sun.xml.internal.ws.wsdl.writer.document.ParamType;
65import com.sun.xml.internal.ws.wsdl.writer.document.Port;
66import com.sun.xml.internal.ws.wsdl.writer.document.PortType;
67import com.sun.xml.internal.ws.wsdl.writer.document.Service;
68import com.sun.xml.internal.ws.wsdl.writer.document.Types;
69import com.sun.xml.internal.ws.wsdl.writer.document.soap.Body;
70import com.sun.xml.internal.ws.wsdl.writer.document.soap.BodyType;
71import com.sun.xml.internal.ws.wsdl.writer.document.soap.Header;
72import com.sun.xml.internal.ws.wsdl.writer.document.soap.SOAPAddress;
73import com.sun.xml.internal.ws.wsdl.writer.document.soap.SOAPFault;
74import com.sun.xml.internal.ws.spi.db.BindingContext;
75import com.sun.xml.internal.ws.spi.db.BindingHelper;
76import com.sun.xml.internal.ws.util.RuntimeVersion;
77import com.sun.xml.internal.ws.policy.jaxws.PolicyWSDLGeneratorExtension;
78import com.sun.xml.internal.ws.encoding.soap.streaming.SOAPNamespaceConstants;
79
80import javax.jws.soap.SOAPBinding.Style;
81import javax.jws.soap.SOAPBinding.Use;
82import javax.xml.bind.SchemaOutputResolver;
83import javax.xml.namespace.QName;
84import javax.xml.transform.Result;
85import javax.xml.transform.Transformer;
86import javax.xml.transform.TransformerConfigurationException;
87import javax.xml.transform.TransformerException;
88import javax.xml.transform.TransformerFactory;
89import javax.xml.transform.dom.DOMResult;
90import javax.xml.transform.dom.DOMSource;
91import javax.xml.transform.sax.SAXResult;
92import javax.xml.ws.Holder;
93import javax.xml.ws.WebServiceException;
94
95import org.w3c.dom.Document;
96import org.w3c.dom.NodeList;
97
98import java.io.IOException;
99import java.net.URI;
100import java.net.URISyntaxException;
101import java.util.ArrayList;
102import java.util.HashSet;
103import java.util.Iterator;
104import java.util.List;
105import java.util.Set;
106
107
108/**
109 * Class used to generate WSDLs from a {@link SEIModel}.
110 *
111 * @author WS Development Team
112 */
113public class WSDLGenerator {
114    private JAXWSOutputSchemaResolver resolver;
115    private WSDLResolver wsdlResolver = null;
116    private AbstractSEIModelImpl model;
117    private Definitions serviceDefinitions;
118    private Definitions portDefinitions;
119    private Types types;
120    /**
121     * Constant String for ".wsdl"
122     */
123    private static final String DOT_WSDL = ".wsdl";
124    /**
125     * The WSDL namespace
126     */
127    private static final String WSDL_NAMESPACE = WSDLConstants.NS_WSDL;
128
129    /**
130     * the XSD namespace
131     */
132    private static final String XSD_NAMESPACE = SOAPNamespaceConstants.XSD;
133    /**
134     * the namespace prefix to use for the XSD namespace
135     */
136    private static final String XSD_PREFIX = "xsd";
137    /**
138     * The SOAP 1.1 namespace
139     */
140    private static final String SOAP11_NAMESPACE = SOAPConstants.NS_WSDL_SOAP;
141    /**
142     * The SOAP 1.2 namespace
143     */
144    private static final String SOAP12_NAMESPACE = SOAPConstants.NS_WSDL_SOAP12;
145    /**
146     * The namespace prefix to use for the SOAP 1.1 namespace
147     */
148    private static final String SOAP_PREFIX = "soap";
149    /**
150     * The namespace prefix to use for the SOAP 1.2 namespace
151     */
152    private static final String SOAP12_PREFIX = "soap12";
153    /**
154     * The namespace prefix to use for the targetNamespace
155     */
156    private static final String TNS_PREFIX = "tns";
157
158    /**
159     * Constant String "document" used to specify <code>document</code> style
160     * soapBindings
161     */
162    private static final String DOCUMENT = "document";
163    /**
164     * Constant String "rpc" used to specify <code>rpc</code> style
165     * soapBindings
166     */
167    private static final String RPC = "rpc";
168    /**
169     * Constant String "literal" used to create <code>literal</code> use binddings
170     */
171    private static final String LITERAL = "literal";
172    /**
173     * Constant String to flag the URL to replace at runtime for the endpoint
174     */
175    private static final String REPLACE_WITH_ACTUAL_URL = "REPLACE_WITH_ACTUAL_URL";
176
177    static public final String XsdNs = "http://www.w3.org/2001/XMLSchema";
178
179    private Set<QName> processedExceptions = new HashSet<QName>();
180    private WSBinding binding;
181    private String wsdlLocation;
182    private String portWSDLID;
183    private String schemaPrefix;
184    private WSDLGeneratorExtension extension;
185    List<WSDLGeneratorExtension> extensionHandlers;
186
187    private String endpointAddress = REPLACE_WITH_ACTUAL_URL;
188    private Container container;
189    private final Class implType;
190
191    private boolean inlineSchemas;      // TODO
192    private final boolean disableXmlSecurity;
193
194    /**
195     * Creates the WSDLGenerator
196     * @param model The {@link AbstractSEIModelImpl} used to generate the WSDL
197     * @param wsdlResolver The {@link WSDLResolver} to use resovle names while generating the WSDL
198     * @param binding specifies which {@link javax.xml.ws.BindingType} to generate
199     * @param extensions an array {@link WSDLGeneratorExtension} that will
200     * be invoked to generate WSDL extensions
201     */
202    public WSDLGenerator(AbstractSEIModelImpl model, WSDLResolver wsdlResolver, WSBinding binding, Container container,
203                         Class implType, boolean inlineSchemas, WSDLGeneratorExtension... extensions) {
204        this(model, wsdlResolver, binding, container, implType, inlineSchemas, false, extensions);
205    }
206
207    /**
208     * Creates the WSDLGenerator
209     * @param model The {@link AbstractSEIModelImpl} used to generate the WSDL
210     * @param wsdlResolver The {@link WSDLResolver} to use resovle names while generating the WSDL
211     * @param binding specifies which {@link javax.xml.ws.BindingType} to generate
212     * @param disableXmlSecurity specifies whether to disable the secure xml processing feature
213     * @param extensions an array {@link WSDLGeneratorExtension} that will
214     * be invoked to generate WSDL extensions
215     */
216    public WSDLGenerator(AbstractSEIModelImpl model, WSDLResolver wsdlResolver, WSBinding binding, Container container,
217                         Class implType, boolean inlineSchemas, boolean disableXmlSecurity,
218                         WSDLGeneratorExtension... extensions) {
219
220        this.model = model;
221        resolver = new JAXWSOutputSchemaResolver();
222        this.wsdlResolver = wsdlResolver;
223        this.binding = binding;
224        this.container = container;
225        this.implType = implType;
226        extensionHandlers = new ArrayList<WSDLGeneratorExtension>();
227        this.inlineSchemas = inlineSchemas;
228        this.disableXmlSecurity = disableXmlSecurity;
229
230        // register handlers for default extensions
231        register(new W3CAddressingWSDLGeneratorExtension());
232        register(new W3CAddressingMetadataWSDLGeneratorExtension());
233        register(new PolicyWSDLGeneratorExtension());
234
235        if (container != null) { // on server
236            WSDLGeneratorExtension[] wsdlGeneratorExtensions = container.getSPI(WSDLGeneratorExtension[].class);
237            if (wsdlGeneratorExtensions != null) {
238                for (WSDLGeneratorExtension wsdlGeneratorExtension : wsdlGeneratorExtensions) {
239                    register(wsdlGeneratorExtension);
240                }
241            }
242        }
243
244        for (WSDLGeneratorExtension w : extensions)
245            register(w);
246
247        this.extension = new WSDLGeneratorExtensionFacade(extensionHandlers.toArray(new WSDLGeneratorExtension[0]));
248    }
249
250    /**
251     * Sets the endpoint address string to be written.
252     * Defaults to {@link #REPLACE_WITH_ACTUAL_URL}.
253     *
254     * @param address wsdl:port/soap:address/[@location] value
255     */
256    public void setEndpointAddress(String address) {
257        this.endpointAddress = address;
258    }
259
260    protected String mangleName(String name) {
261        return BindingHelper.mangleNameToClassName(name);
262    }
263
264    /**
265     * Performes the actual WSDL generation
266     */
267    public void doGeneration() {
268        XmlSerializer serviceWriter;
269        XmlSerializer portWriter = null;
270        String fileName = mangleName(model.getServiceQName().getLocalPart());
271        Result result = wsdlResolver.getWSDL(fileName + DOT_WSDL);
272        wsdlLocation = result.getSystemId();
273        serviceWriter = new CommentFilter(ResultFactory.createSerializer(result));
274        if (model.getServiceQName().getNamespaceURI().equals(model.getTargetNamespace())) {
275            portWriter = serviceWriter;
276            schemaPrefix = fileName + "_";
277        } else {
278            String wsdlName = mangleName(model.getPortTypeName().getLocalPart());
279            if (wsdlName.equals(fileName))
280                wsdlName += "PortType";
281            Holder<String> absWSDLName = new Holder<String>();
282            absWSDLName.value = wsdlName + DOT_WSDL;
283            result = wsdlResolver.getAbstractWSDL(absWSDLName);
284
285            if (result != null) {
286                portWSDLID = result.getSystemId();
287                if (portWSDLID.equals(wsdlLocation)) {
288                    portWriter = serviceWriter;
289                } else {
290                    portWriter = new CommentFilter(ResultFactory.createSerializer(result));
291                }
292            } else {
293                portWSDLID = absWSDLName.value;
294            }
295            schemaPrefix = new java.io.File(portWSDLID).getName();
296            int idx = schemaPrefix.lastIndexOf('.');
297            if (idx > 0)
298                schemaPrefix = schemaPrefix.substring(0, idx);
299            schemaPrefix = mangleName(schemaPrefix) + "_";
300        }
301        generateDocument(serviceWriter, portWriter);
302    }
303
304    /**
305     * Writing directly to XmlSerializer is a problem, since it doesn't suppress
306     * xml declaration. Creating filter so that comment is written before TXW writes
307     * anything in the WSDL.
308     */
309    private static class CommentFilter implements XmlSerializer {
310        final XmlSerializer serializer;
311        private static final String VERSION_COMMENT =
312                " Generated by JAX-WS RI (http://jax-ws.java.net). RI's version is " + RuntimeVersion.VERSION + ". ";
313
314        CommentFilter(XmlSerializer serializer) {
315            this.serializer = serializer;
316        }
317
318        @Override
319        public void startDocument() {
320            serializer.startDocument();
321            comment(new StringBuilder(VERSION_COMMENT));
322            text(new StringBuilder("\n"));
323        }
324
325        @Override
326        public void beginStartTag(String uri, String localName, String prefix) {
327            serializer.beginStartTag(uri, localName, prefix);
328        }
329
330        @Override
331        public void writeAttribute(String uri, String localName, String prefix, StringBuilder value) {
332            serializer.writeAttribute(uri, localName, prefix, value);
333        }
334
335        @Override
336        public void writeXmlns(String prefix, String uri) {
337            serializer.writeXmlns(prefix, uri);
338        }
339
340        @Override
341        public void endStartTag(String uri, String localName, String prefix) {
342            serializer.endStartTag(uri, localName, prefix);
343        }
344
345        @Override
346        public void endTag() {
347            serializer.endTag();
348        }
349
350        @Override
351        public void text(StringBuilder text) {
352            serializer.text(text);
353        }
354
355        @Override
356        public void cdata(StringBuilder text) {
357            serializer.cdata(text);
358        }
359
360        @Override
361        public void comment(StringBuilder comment) {
362            serializer.comment(comment);
363        }
364
365        @Override
366        public void endDocument() {
367            serializer.endDocument();
368        }
369
370        @Override
371        public void flush() {
372            serializer.flush();
373        }
374
375    }
376
377    private void generateDocument(XmlSerializer serviceStream, XmlSerializer portStream) {
378        serviceDefinitions = TXW.create(Definitions.class, serviceStream);
379        serviceDefinitions._namespace(WSDL_NAMESPACE, "");//WSDL_PREFIX);
380        serviceDefinitions._namespace(XSD_NAMESPACE, XSD_PREFIX);
381        serviceDefinitions.targetNamespace(model.getServiceQName().getNamespaceURI());
382        serviceDefinitions._namespace(model.getServiceQName().getNamespaceURI(), TNS_PREFIX);
383        if (binding.getSOAPVersion() == SOAPVersion.SOAP_12)
384            serviceDefinitions._namespace(SOAP12_NAMESPACE, SOAP12_PREFIX);
385        else
386            serviceDefinitions._namespace(SOAP11_NAMESPACE, SOAP_PREFIX);
387        serviceDefinitions.name(model.getServiceQName().getLocalPart());
388        WSDLGenExtnContext serviceCtx = new WSDLGenExtnContext(serviceDefinitions, model, binding, container, implType);
389        extension.start(serviceCtx);
390        if (serviceStream != portStream && portStream != null) {
391            // generate an abstract and concrete wsdl
392            portDefinitions = TXW.create(Definitions.class, portStream);
393            portDefinitions._namespace(WSDL_NAMESPACE, "");//WSDL_PREFIX);
394            portDefinitions._namespace(XSD_NAMESPACE, XSD_PREFIX);
395            if (model.getTargetNamespace() != null) {
396                portDefinitions.targetNamespace(model.getTargetNamespace());
397                portDefinitions._namespace(model.getTargetNamespace(), TNS_PREFIX);
398            }
399
400            String schemaLoc = relativize(portWSDLID, wsdlLocation);
401            Import _import = serviceDefinitions._import().namespace(model.getTargetNamespace());
402            _import.location(schemaLoc);
403        } else if (portStream != null) {
404            // abstract and concrete are the same
405            portDefinitions = serviceDefinitions;
406        } else {
407            // import a provided abstract wsdl
408            String schemaLoc = relativize(portWSDLID, wsdlLocation);
409            Import _import = serviceDefinitions._import().namespace(model.getTargetNamespace());
410            _import.location(schemaLoc);
411        }
412        extension.addDefinitionsExtension(serviceDefinitions);
413
414        if (portDefinitions != null) {
415            generateTypes();
416            generateMessages();
417            generatePortType();
418        }
419        generateBinding();
420        generateService();
421        //Give a chance to WSDLGeneratorExtensions to write stuff before closing </wsdl:defintions>
422        extension.end(serviceCtx);
423        serviceDefinitions.commit();
424        if (portDefinitions != null && portDefinitions != serviceDefinitions)
425            portDefinitions.commit();
426    }
427
428
429    /**
430     * Generates the types section of the WSDL
431     */
432    protected void generateTypes() {
433        types = portDefinitions.types();
434        if (model.getBindingContext() != null) {
435            if (inlineSchemas && model.getBindingContext().getClass().getName().indexOf("glassfish") == -1) {
436                resolver.nonGlassfishSchemas = new ArrayList<DOMResult>();
437            }
438            try {
439                model.getBindingContext().generateSchema(resolver);
440            } catch (IOException e) {
441                // TODO locallize and wrap this
442                throw new WebServiceException(e.getMessage());
443            }
444        }
445        if (resolver.nonGlassfishSchemas != null) {
446            TransformerFactory tf = XmlUtil.newTransformerFactory(!disableXmlSecurity);
447            try {
448                Transformer t = tf.newTransformer();
449                for (DOMResult xsd : resolver.nonGlassfishSchemas) {
450                    Document doc = (Document) xsd.getNode();
451                    if (inlineSchemas) {
452                        NodeList importList = doc.getDocumentElement().getElementsByTagNameNS("http://www.w3.org/2001/XMLSchema", "import");
453                        for(int i = 0; i < importList.getLength(); i++) {
454                            org.w3c.dom.Element impElem = (org.w3c.dom.Element)importList.item(i);
455                            impElem.removeAttribute("schemaLocation");
456                        }
457                    }
458                    SAXResult sax = new SAXResult(new TXWContentHandler(types));
459                    t.transform(new DOMSource(doc.getDocumentElement()), sax);
460                }
461            } catch (TransformerConfigurationException e) {
462                throw new WebServiceException(e.getMessage(), e);
463            } catch (TransformerException e) {
464                throw new WebServiceException(e.getMessage(), e);
465            }
466        }
467    }
468
469    /**
470     * Generates the WSDL messages
471     */
472    protected void generateMessages() {
473        for (JavaMethodImpl method : model.getJavaMethods()) {
474            generateSOAPMessages(method, method.getBinding());
475        }
476    }
477
478    /**
479     * Generates messages for a SOAPBinding
480     * @param method The {@link JavaMethod} to generate messages for
481     * @param binding The {@link com.sun.xml.internal.ws.api.model.soap.SOAPBinding} to add the generated messages to
482     */
483    protected void generateSOAPMessages(JavaMethodImpl method, com.sun.xml.internal.ws.api.model.soap.SOAPBinding binding) {
484        boolean isDoclit = binding.isDocLit();
485//        Message message = portDefinitions.message().name(method.getOperation().getName().getLocalPart());
486        Message message = portDefinitions.message().name(method.getRequestMessageName());
487        extension.addInputMessageExtension(message, method);
488        com.sun.xml.internal.ws.wsdl.writer.document.Part part;
489        BindingContext jaxbContext = model.getBindingContext();
490        boolean unwrappable = true;
491        for (ParameterImpl param : method.getRequestParameters()) {
492            if (isDoclit) {
493                if (isHeaderParameter(param))
494                    unwrappable = false;
495
496                part = message.part().name(param.getPartName());
497                part.element(param.getName());
498            } else {
499                if (param.isWrapperStyle()) {
500                    for (ParameterImpl childParam : ((WrapperParameter) param).getWrapperChildren()) {
501                        part = message.part().name(childParam.getPartName());
502                        part.type(jaxbContext.getTypeName(childParam.getXMLBridge().getTypeInfo()));
503                    }
504                } else {
505                    part = message.part().name(param.getPartName());
506                    part.element(param.getName());
507                }
508            }
509        }
510        if (method.getMEP() != MEP.ONE_WAY) {
511            message = portDefinitions.message().name(method.getResponseMessageName());
512            extension.addOutputMessageExtension(message, method);
513
514            for (ParameterImpl param : method.getResponseParameters()) {
515                if (isDoclit) {
516                    part = message.part().name(param.getPartName());
517                    part.element(param.getName());
518
519                } else {
520                    if (param.isWrapperStyle()) {
521                        for (ParameterImpl childParam : ((WrapperParameter) param).getWrapperChildren()) {
522                            part = message.part().name(childParam.getPartName());
523                            part.type(jaxbContext.getTypeName(childParam.getXMLBridge().getTypeInfo()));
524                        }
525                    } else {
526                        part = message.part().name(param.getPartName());
527                        part.element(param.getName());
528                    }
529                }
530            }
531        }
532        for (CheckedExceptionImpl exception : method.getCheckedExceptions()) {
533            QName tagName = exception.getDetailType().tagName;
534            String messageName = exception.getMessageName();
535            QName messageQName = new QName(model.getTargetNamespace(), messageName);
536            if (processedExceptions.contains(messageQName))
537                continue;
538            message = portDefinitions.message().name(messageName);
539
540            extension.addFaultMessageExtension(message, method, exception);
541            part = message.part().name("fault");//tagName.getLocalPart());
542            part.element(tagName);
543            processedExceptions.add(messageQName);
544        }
545    }
546
547    /**
548     * Generates the WSDL portType
549     */
550    protected void generatePortType() {
551
552        PortType portType = portDefinitions.portType().name(model.getPortTypeName().getLocalPart());
553        extension.addPortTypeExtension(portType);
554        for (JavaMethodImpl method : model.getJavaMethods()) {
555            Operation operation = portType.operation().name(method.getOperationName());
556            generateParameterOrder(operation, method);
557            extension.addOperationExtension(operation, method);
558            switch (method.getMEP()) {
559                case REQUEST_RESPONSE:
560                    // input message
561                    generateInputMessage(operation, method);
562                    // output message
563                    generateOutputMessage(operation, method);
564                    break;
565                case ONE_WAY:
566                    generateInputMessage(operation, method);
567                    break;
568                default:
569                    break;
570            }
571            // faults
572            for (CheckedExceptionImpl exception : method.getCheckedExceptions()) {
573                QName messageName = new QName(model.getTargetNamespace(), exception.getMessageName());
574                FaultType paramType = operation.fault().message(messageName).name(exception.getMessageName());
575                extension.addOperationFaultExtension(paramType, method, exception);
576            }
577        }
578    }
579
580    /**
581     * Determines if the <CODE>method</CODE> is wrapper style
582     * @param method The {@link JavaMethod} to check if it is wrapper style
583     * @return true if the method is wrapper style, otherwise, false.
584     */
585    protected boolean isWrapperStyle(JavaMethodImpl method) {
586        if (method.getRequestParameters().size() > 0) {
587            ParameterImpl param = method.getRequestParameters().iterator().next();
588            return param.isWrapperStyle();
589        }
590        return false;
591    }
592
593    /**
594     * Determines if a {@link JavaMethod} is rpc/literal
595     * @param method The method to check
596     * @return true if method is rpc/literal, otherwise, false
597     */
598    protected boolean isRpcLit(JavaMethodImpl method) {
599        return method.getBinding().getStyle() == Style.RPC;
600    }
601
602    /**
603     * Generates the parameterOrder for a PortType operation
604     * @param operation The operation to generate the parameterOrder for
605     * @param method The {@link JavaMethod} to generate the parameterOrder from
606     */
607    protected void generateParameterOrder(Operation operation, JavaMethodImpl method) {
608        if (method.getMEP() == MEP.ONE_WAY)
609            return;
610        if (isRpcLit(method))
611            generateRpcParameterOrder(operation, method);
612        else
613            generateDocumentParameterOrder(operation, method);
614    }
615
616    /**
617     * Generates the parameterOrder for a PortType operation
618     * @param operation the operation to generate the parameterOrder for
619     * @param method the {@link JavaMethod} to generate the parameterOrder from
620     */
621    protected void generateRpcParameterOrder(Operation operation, JavaMethodImpl method) {
622        String partName;
623        StringBuilder paramOrder = new StringBuilder();
624        Set<String> partNames = new HashSet<String>();
625        List<ParameterImpl> sortedParams = sortMethodParameters(method);
626        int i = 0;
627        for (ParameterImpl parameter : sortedParams) {
628            if (parameter.getIndex() >= 0) {
629                partName = parameter.getPartName();
630                if (!partNames.contains(partName)) {
631                    if (i++ > 0)
632                        paramOrder.append(' ');
633                    paramOrder.append(partName);
634                    partNames.add(partName);
635                }
636            }
637        }
638        if (i > 1) {
639            operation.parameterOrder(paramOrder.toString());
640        }
641    }
642
643
644    /**
645     * Generates the parameterOrder for a PortType operation
646     * @param operation the operation to generate the parameterOrder for
647     * @param method the {@link JavaMethod} to generate the parameterOrder from
648     */
649    protected void generateDocumentParameterOrder(Operation operation, JavaMethodImpl method) {
650        String partName;
651        StringBuilder paramOrder = new StringBuilder();
652        Set<String> partNames = new HashSet<String>();
653        List<ParameterImpl> sortedParams = sortMethodParameters(method);
654//        boolean isWrapperStyle = isWrapperStyle(method);
655        int i = 0;
656        for (ParameterImpl parameter : sortedParams) {
657//            System.out.println("param: "+parameter.getIndex()+" name: "+parameter.getName().getLocalPart());
658            if (parameter.getIndex() < 0)
659                continue;
660
661            // This should be safe change. if it affects compatibility,
662            // remove the following single statement and uncomment the code in block below.
663            partName = parameter.getPartName();
664            /*
665            if (isWrapperStyle && isBodyParameter(parameter)) {
666               System.out.println("isWrapper and is body");
667                if (method.getRequestParameters().contains(parameter))
668                    partName = PARAMETERS;
669                else {
670                    //Rama: don't understand this logic "Response" below,
671
672                    // really make sure this is a wrapper style wsdl we are creating
673                    partName = RESPONSE;
674                }
675            } else {
676               partName = parameter.getPartName();
677            }*/
678
679            if (!partNames.contains(partName)) {
680                if (i++ > 0)
681                    paramOrder.append(' ');
682                paramOrder.append(partName);
683                partNames.add(partName);
684            }
685        }
686        if (i > 1) {
687            operation.parameterOrder(paramOrder.toString());
688        }
689    }
690
691    /**
692     * Sorts the parameters for the method by their position
693     * @param method the {@link JavaMethod} used to sort the parameters
694     * @return the sorted {@link List} of parameters
695     */
696    protected List<ParameterImpl> sortMethodParameters(JavaMethodImpl method) {
697        Set<ParameterImpl> paramSet = new HashSet<ParameterImpl>();
698        List<ParameterImpl> sortedParams = new ArrayList<ParameterImpl>();
699        if (isRpcLit(method)) {
700            for (ParameterImpl param : method.getRequestParameters()) {
701                if (param instanceof WrapperParameter) {
702                    paramSet.addAll(((WrapperParameter) param).getWrapperChildren());
703                } else {
704                    paramSet.add(param);
705                }
706            }
707            for (ParameterImpl param : method.getResponseParameters()) {
708                if (param instanceof WrapperParameter) {
709                    paramSet.addAll(((WrapperParameter) param).getWrapperChildren());
710                } else {
711                    paramSet.add(param);
712                }
713            }
714        } else {
715            paramSet.addAll(method.getRequestParameters());
716            paramSet.addAll(method.getResponseParameters());
717        }
718        Iterator<ParameterImpl> params = paramSet.iterator();
719        if (paramSet.isEmpty())
720            return sortedParams;
721        ParameterImpl param = params.next();
722        sortedParams.add(param);
723        ParameterImpl sortedParam;
724        int pos;
725        for (int i = 1; i < paramSet.size(); i++) {
726            param = params.next();
727            for (pos = 0; pos < i; pos++) {
728                sortedParam = sortedParams.get(pos);
729                if (param.getIndex() == sortedParam.getIndex() &&
730                        param instanceof WrapperParameter)
731                    break;
732                if (param.getIndex() < sortedParam.getIndex()) {
733                    break;
734                }
735            }
736            sortedParams.add(pos, param);
737        }
738        return sortedParams;
739    }
740
741    /**
742     * Determines if a parameter is associated with the message Body
743     * @param parameter the parameter to check
744     * @return true if the parameter is a <code>body</code> parameter
745     */
746    protected boolean isBodyParameter(ParameterImpl parameter) {
747        ParameterBinding paramBinding = parameter.getBinding();
748        return paramBinding.isBody();
749    }
750
751    protected boolean isHeaderParameter(ParameterImpl parameter) {
752        ParameterBinding paramBinding = parameter.getBinding();
753        return paramBinding.isHeader();
754    }
755
756    protected boolean isAttachmentParameter(ParameterImpl parameter) {
757        ParameterBinding paramBinding = parameter.getBinding();
758        return paramBinding.isAttachment();
759    }
760
761
762    /**
763     * Generates the Binding section of the WSDL
764     */
765    protected void generateBinding() {
766        Binding newBinding = serviceDefinitions.binding().name(model.getBoundPortTypeName().getLocalPart());
767        extension.addBindingExtension(newBinding);
768        newBinding.type(model.getPortTypeName());
769        boolean first = true;
770        for (JavaMethodImpl method : model.getJavaMethods()) {
771            if (first) {
772                SOAPBinding sBinding = method.getBinding();
773                SOAPVersion soapVersion = sBinding.getSOAPVersion();
774                if (soapVersion == SOAPVersion.SOAP_12) {
775                    com.sun.xml.internal.ws.wsdl.writer.document.soap12.SOAPBinding soapBinding = newBinding.soap12Binding();
776                    soapBinding.transport(this.binding.getBindingId().getTransport());
777                    if (sBinding.getStyle().equals(Style.DOCUMENT))
778                        soapBinding.style(DOCUMENT);
779                    else
780                        soapBinding.style(RPC);
781                } else {
782                    com.sun.xml.internal.ws.wsdl.writer.document.soap.SOAPBinding soapBinding = newBinding.soapBinding();
783                    soapBinding.transport(this.binding.getBindingId().getTransport());
784                    if (sBinding.getStyle().equals(Style.DOCUMENT))
785                        soapBinding.style(DOCUMENT);
786                    else
787                        soapBinding.style(RPC);
788                }
789                first = false;
790            }
791            if (this.binding.getBindingId().getSOAPVersion() == SOAPVersion.SOAP_12)
792                generateSOAP12BindingOperation(method, newBinding);
793            else
794                generateBindingOperation(method, newBinding);
795        }
796    }
797
798    protected void generateBindingOperation(JavaMethodImpl method, Binding binding) {
799        BindingOperationType operation = binding.operation().name(method.getOperationName());
800        extension.addBindingOperationExtension(operation, method);
801        String targetNamespace = model.getTargetNamespace();
802        QName requestMessage = new QName(targetNamespace, method.getOperationName());
803        List<ParameterImpl> bodyParams = new ArrayList<ParameterImpl>();
804        List<ParameterImpl> headerParams = new ArrayList<ParameterImpl>();
805        splitParameters(bodyParams, headerParams, method.getRequestParameters());
806        SOAPBinding soapBinding = method.getBinding();
807        operation.soapOperation().soapAction(soapBinding.getSOAPAction());
808
809        // input
810        TypedXmlWriter input = operation.input();
811        extension.addBindingOperationInputExtension(input, method);
812        BodyType body = input._element(Body.class);
813        boolean isRpc = soapBinding.getStyle().equals(Style.RPC);
814        if (soapBinding.getUse() == Use.LITERAL) {
815            body.use(LITERAL);
816            if (headerParams.size() > 0) {
817                if (bodyParams.size() > 0) {
818                    ParameterImpl param = bodyParams.iterator().next();
819                    if (isRpc) {
820                        StringBuilder parts = new StringBuilder();
821                        int i = 0;
822                        for (ParameterImpl parameter : ((WrapperParameter) param).getWrapperChildren()) {
823                            if (i++ > 0)
824                                parts.append(' ');
825                            parts.append(parameter.getPartName());
826                        }
827                        body.parts(parts.toString());
828                    } else {
829                        body.parts(param.getPartName());
830                    }
831                } else {
832                    body.parts("");
833                }
834                generateSOAPHeaders(input, headerParams, requestMessage);
835            }
836            if (isRpc) {
837                body.namespace(method.getRequestParameters().iterator().next().getName().getNamespaceURI());
838            }
839        } else {
840            // TODO localize this
841            throw new WebServiceException("encoded use is not supported");
842        }
843
844        if (method.getMEP() != MEP.ONE_WAY) {
845            // output
846            bodyParams.clear();
847            headerParams.clear();
848            splitParameters(bodyParams, headerParams, method.getResponseParameters());
849            TypedXmlWriter output = operation.output();
850            extension.addBindingOperationOutputExtension(output, method);
851            body = output._element(Body.class);
852            body.use(LITERAL);
853            if (headerParams.size() > 0) {
854                StringBuilder parts = new StringBuilder();
855                if (bodyParams.size() > 0) {
856                    ParameterImpl param = bodyParams.iterator().hasNext() ? bodyParams.iterator().next() : null;
857                    if (param != null) {
858                        if (isRpc) {
859                            int i = 0;
860                            for (ParameterImpl parameter : ((WrapperParameter) param).getWrapperChildren()) {
861                                if (i++ > 0) {
862                                    parts.append(" ");
863                                }
864                                parts.append(parameter.getPartName());
865                            }
866                        } else {
867                            parts = new StringBuilder(param.getPartName());
868                        }
869                    }
870                }
871                body.parts(parts.toString());
872                QName responseMessage = new QName(targetNamespace, method.getResponseMessageName());
873                generateSOAPHeaders(output, headerParams, responseMessage);
874            }
875            if (isRpc) {
876                body.namespace(method.getRequestParameters().iterator().next().getName().getNamespaceURI());
877            }
878        }
879        for (CheckedExceptionImpl exception : method.getCheckedExceptions()) {
880            Fault fault = operation.fault().name(exception.getMessageName());
881            extension.addBindingOperationFaultExtension(fault, method, exception);
882            SOAPFault soapFault = fault._element(SOAPFault.class).name(exception.getMessageName());
883            soapFault.use(LITERAL);
884        }
885    }
886
887    protected void generateSOAP12BindingOperation(JavaMethodImpl method, Binding binding) {
888        BindingOperationType operation = binding.operation().name(method.getOperationName());
889        extension.addBindingOperationExtension(operation, method);
890        String targetNamespace = model.getTargetNamespace();
891        QName requestMessage = new QName(targetNamespace, method.getOperationName());
892        ArrayList<ParameterImpl> bodyParams = new ArrayList<ParameterImpl>();
893        ArrayList<ParameterImpl> headerParams = new ArrayList<ParameterImpl>();
894        splitParameters(bodyParams, headerParams, method.getRequestParameters());
895        SOAPBinding soapBinding = method.getBinding();
896
897        String soapAction = soapBinding.getSOAPAction();
898        if (soapAction != null) {
899            operation.soap12Operation().soapAction(soapAction);
900        }
901
902        // input
903        TypedXmlWriter input = operation.input();
904        extension.addBindingOperationInputExtension(input, method);
905        com.sun.xml.internal.ws.wsdl.writer.document.soap12.BodyType body = input._element(com.sun.xml.internal.ws.wsdl.writer.document.soap12.Body.class);
906        boolean isRpc = soapBinding.getStyle().equals(Style.RPC);
907        if (soapBinding.getUse().equals(Use.LITERAL)) {
908            body.use(LITERAL);
909            if (headerParams.size() > 0) {
910                if (bodyParams.size() > 0) {
911                    ParameterImpl param = bodyParams.iterator().next();
912                    if (isRpc) {
913                        StringBuilder parts = new StringBuilder();
914                        int i = 0;
915                        for (ParameterImpl parameter : ((WrapperParameter) param).getWrapperChildren()) {
916                            if (i++ > 0)
917                                parts.append(' ');
918                            parts.append(parameter.getPartName());
919                        }
920                        body.parts(parts.toString());
921                    } else {
922                        body.parts(param.getPartName());
923                    }
924                } else {
925                    body.parts("");
926                }
927                generateSOAP12Headers(input, headerParams, requestMessage);
928            }
929            if (isRpc) {
930                body.namespace(method.getRequestParameters().iterator().next().getName().getNamespaceURI());
931            }
932        } else {
933            // TODO localize this
934            throw new WebServiceException("encoded use is not supported");
935        }
936
937        if (method.getMEP() != MEP.ONE_WAY) {
938            // output
939            bodyParams.clear();
940            headerParams.clear();
941            splitParameters(bodyParams, headerParams, method.getResponseParameters());
942            TypedXmlWriter output = operation.output();
943            extension.addBindingOperationOutputExtension(output, method);
944            body = output._element(com.sun.xml.internal.ws.wsdl.writer.document.soap12.Body.class);
945            body.use(LITERAL);
946            if (headerParams.size() > 0) {
947                if (bodyParams.size() > 0) {
948                    ParameterImpl param = bodyParams.iterator().next();
949                    if (isRpc) {
950                        StringBuilder parts = new StringBuilder();
951                        int i = 0;
952                        for (ParameterImpl parameter : ((WrapperParameter) param).getWrapperChildren()) {
953                            if (i++ > 0) {
954                                parts.append(" ");
955                            }
956                            parts.append(parameter.getPartName());
957                        }
958                        body.parts(parts.toString());
959                    } else {
960                        body.parts(param.getPartName());
961                    }
962                } else {
963                    body.parts("");
964                }
965                QName responseMessage = new QName(targetNamespace, method.getResponseMessageName());
966                generateSOAP12Headers(output, headerParams, responseMessage);
967            }
968            if (isRpc) {
969                body.namespace(method.getRequestParameters().iterator().next().getName().getNamespaceURI());
970            }
971        }
972        for (CheckedExceptionImpl exception : method.getCheckedExceptions()) {
973            Fault fault = operation.fault().name(exception.getMessageName());
974            extension.addBindingOperationFaultExtension(fault, method, exception);
975            com.sun.xml.internal.ws.wsdl.writer.document.soap12.SOAPFault soapFault = fault._element(com.sun.xml.internal.ws.wsdl.writer.document.soap12.SOAPFault.class).name(exception.getMessageName());
976            soapFault.use(LITERAL);
977        }
978    }
979
980    protected void splitParameters(List<ParameterImpl> bodyParams, List<ParameterImpl> headerParams, List<ParameterImpl> params) {
981        for (ParameterImpl parameter : params) {
982            if (isBodyParameter(parameter)) {
983                bodyParams.add(parameter);
984            } else {
985                headerParams.add(parameter);
986            }
987        }
988    }
989
990    protected void generateSOAPHeaders(TypedXmlWriter writer, List<ParameterImpl> parameters, QName message) {
991
992        for (ParameterImpl headerParam : parameters) {
993            Header header = writer._element(Header.class);
994            header.message(message);
995            header.part(headerParam.getPartName());
996            header.use(LITERAL);
997        }
998    }
999
1000    protected void generateSOAP12Headers(TypedXmlWriter writer, List<ParameterImpl> parameters, QName message) {
1001
1002        for (ParameterImpl headerParam : parameters) {
1003            com.sun.xml.internal.ws.wsdl.writer.document.soap12.Header header = writer._element(com.sun.xml.internal.ws.wsdl.writer.document.soap12.Header.class);
1004            header.message(message);
1005
1006
1007            header.part(headerParam.getPartName());
1008            header.use(LITERAL);
1009        }
1010    }
1011
1012    /**
1013     * Generates the Service section of the WSDL
1014     */
1015    protected void generateService() {
1016        QName portQName = model.getPortName();
1017        QName serviceQName = model.getServiceQName();
1018        Service service = serviceDefinitions.service().name(serviceQName.getLocalPart());
1019        extension.addServiceExtension(service);
1020        Port port = service.port().name(portQName.getLocalPart());
1021        port.binding(model.getBoundPortTypeName());
1022        extension.addPortExtension(port);
1023        if (model.getJavaMethods().isEmpty())
1024            return;
1025
1026        if (this.binding.getBindingId().getSOAPVersion() == SOAPVersion.SOAP_12) {
1027            com.sun.xml.internal.ws.wsdl.writer.document.soap12.SOAPAddress address = port._element(com.sun.xml.internal.ws.wsdl.writer.document.soap12.SOAPAddress.class);
1028            address.location(endpointAddress);
1029        } else {
1030            SOAPAddress address = port._element(SOAPAddress.class);
1031            address.location(endpointAddress);
1032        }
1033    }
1034
1035    protected void generateInputMessage(Operation operation, JavaMethodImpl method) {
1036        ParamType paramType = operation.input();
1037        extension.addOperationInputExtension(paramType, method);
1038//        paramType.message(method.getOperation().getName());
1039        paramType.message(new QName(model.getTargetNamespace(), method.getRequestMessageName()));
1040    }
1041
1042    protected void generateOutputMessage(Operation operation, JavaMethodImpl method) {
1043        ParamType paramType = operation.output();
1044        extension.addOperationOutputExtension(paramType, method);
1045//        paramType.message(new QName(model.getTargetNamespace(), method.getOperation().getLocalName()+RESPONSE));
1046        paramType.message(new QName(model.getTargetNamespace(), method.getResponseMessageName()));
1047    }
1048
1049    /**
1050     * Creates the {@link Result} object used by JAXB to generate a schema for the
1051     * namesapceUri namespace.
1052     * @param namespaceUri The namespace for the schema being generated
1053     * @param suggestedFileName the JAXB suggested file name for the schema file
1054     * @return the {@link Result} for JAXB to generate the schema into
1055     * @throws java.io.IOException thrown if on IO error occurs
1056     */
1057    public Result createOutputFile(String namespaceUri, String suggestedFileName) throws IOException {
1058        Result result;
1059        if (namespaceUri == null) {
1060            return null;
1061        }
1062
1063        Holder<String> fileNameHolder = new Holder<String>();
1064        fileNameHolder.value = schemaPrefix + suggestedFileName;
1065        result = wsdlResolver.getSchemaOutput(namespaceUri, fileNameHolder);
1066//        System.out.println("schema file: "+fileNameHolder.value);
1067//        System.out.println("result: "+result);
1068        String schemaLoc;
1069        if (result == null)
1070            schemaLoc = fileNameHolder.value;
1071        else
1072            schemaLoc = relativize(result.getSystemId(), wsdlLocation);
1073        boolean isEmptyNs = namespaceUri.trim().equals("");
1074        if (!isEmptyNs) {
1075            com.sun.xml.internal.ws.wsdl.writer.document.xsd.Import _import = types.schema()._import();
1076            _import.namespace(namespaceUri);
1077            _import.schemaLocation(schemaLoc);
1078        }
1079        return result;
1080    }
1081
1082    private Result createInlineSchema(String namespaceUri, String suggestedFileName) throws IOException {
1083        Result result;
1084        if (namespaceUri.equals("")) {
1085            return null;
1086        }
1087
1088//        Holder<String> fileNameHolder = new Holder<String>();
1089//        fileNameHolder.value = schemaPrefix+suggestedFileName;
1090//        result = wsdlResolver.getSchemaOutput(namespaceUri, fileNameHolder);
1091//        if (result == null) {
1092//            // JAXB doesn't have to generate it, a schema is already available
1093//            com.sun.xml.internal.ws.wsdl.writer.document.xsd.Import _import = types.schema()._import().namespace(namespaceUri);
1094//            _import.schemaLocation(fileNameHolder.value);
1095//        } else {
1096        // Let JAXB write the schema directly into wsdl's TypedXmlWriter
1097        result = new TXWResult(types);
1098        result.setSystemId("");
1099//        }
1100        return result;
1101    }
1102
1103    /**
1104     * Relativizes a URI by using another URI (base URI.)
1105     *
1106     * <p>
1107     * For example, {@code relative("http://www.sun.com/abc/def","http://www.sun.com/pqr/stu") => "../abc/def"}
1108     *
1109     * <p>
1110     * This method only works on hierarchical URI's, not opaque URI's (refer to the
1111     * <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/net/URI.html">java.net.URI</a>
1112     * javadoc for complete definitions of these terms.
1113     *
1114     * <p>
1115     * This method will not normalize the relative URI.
1116     * @param uri the URI to relativize
1117     *
1118     *
1119     * @param baseUri the base URI to use for the relativization
1120     * @return the relative URI or the original URI if a relative one could not be computed
1121     */
1122    protected static String relativize(String uri, String baseUri) {
1123        try {
1124            assert uri != null;
1125
1126            if (baseUri == null) return uri;
1127
1128            URI theUri = new URI(escapeURI(uri));
1129            URI theBaseUri = new URI(escapeURI(baseUri));
1130
1131            if (theUri.isOpaque() || theBaseUri.isOpaque())
1132                return uri;
1133
1134            if (!equalsIgnoreCase(theUri.getScheme(), theBaseUri.getScheme()) ||
1135                    !equal(theUri.getAuthority(), theBaseUri.getAuthority()))
1136                return uri;
1137
1138            String uriPath = theUri.getPath();
1139            String basePath = theBaseUri.getPath();
1140
1141            // normalize base path
1142            if (!basePath.endsWith("/")) {
1143                basePath = normalizeUriPath(basePath);
1144            }
1145
1146            if (uriPath.equals(basePath))
1147                return ".";
1148
1149            String relPath = calculateRelativePath(uriPath, basePath);
1150
1151            if (relPath == null)
1152                return uri; // recursion found no commonality in the two uris at all
1153            StringBuilder relUri = new StringBuilder();
1154            relUri.append(relPath);
1155            if (theUri.getQuery() != null)
1156                relUri.append('?').append(theUri.getQuery());
1157            if (theUri.getFragment() != null)
1158                relUri.append('#').append(theUri.getFragment());
1159
1160            return relUri.toString();
1161        } catch (URISyntaxException e) {
1162            throw new InternalError("Error escaping one of these uris:\n\t" + uri + "\n\t" + baseUri);
1163        }
1164    }
1165
1166    private static String calculateRelativePath(String uri, String base) {
1167        if (base == null) {
1168            return null;
1169        }
1170        if (uri.startsWith(base)) {
1171            return uri.substring(base.length());
1172        } else {
1173            return "../" + calculateRelativePath(uri, getParentUriPath(base));
1174        }
1175    }
1176
1177
1178    /**
1179     * Implements the SchemaOutputResolver used by JAXB to
1180     */
1181    protected class JAXWSOutputSchemaResolver extends SchemaOutputResolver {
1182        ArrayList<DOMResult> nonGlassfishSchemas = null;
1183
1184        /**
1185         * Creates the {@link Result} object used by JAXB to generate a schema for the
1186         * namesapceUri namespace.
1187         * @param namespaceUri The namespace for the schema being generated
1188         * @param suggestedFileName the JAXB suggested file name for the schema file
1189         * @return the {@link Result} for JAXB to generate the schema into
1190         * @throws java.io.IOException thrown if on IO error occurs
1191         */
1192        @Override
1193        public Result createOutput(String namespaceUri, String suggestedFileName) throws IOException {
1194            return inlineSchemas
1195                    ? ((nonGlassfishSchemas != null) ? nonGlassfishSchemaResult(namespaceUri, suggestedFileName) : createInlineSchema(namespaceUri, suggestedFileName))
1196//                    ? createInlineSchema(namespaceUri, suggestedFileName)
1197                    : createOutputFile(namespaceUri, suggestedFileName);
1198        }
1199
1200        private Result nonGlassfishSchemaResult(String namespaceUri, String suggestedFileName) throws IOException {
1201            DOMResult result = new DOMResult();
1202            result.setSystemId("");
1203            nonGlassfishSchemas.add(result);
1204            return result;
1205        }
1206    }
1207
1208    private void register(WSDLGeneratorExtension h) {
1209        extensionHandlers.add(h);
1210    }
1211}
1212