/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://oss.oracle.com/licenses/CDDL+GPL-1.1
 * or LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package com.sun.xml.ws.wsdl.parser;

import com.sun.istack.NotNull;
import com.sun.istack.Nullable;
import com.sun.xml.stream.buffer.MutableXMLStreamBuffer;
import com.sun.xml.stream.buffer.XMLStreamBuffer;
import com.sun.xml.stream.buffer.XMLStreamBufferMark;
import com.sun.xml.stream.buffer.stax.StreamReaderBufferCreator;
import com.sun.xml.ws.api.BindingID;
import com.sun.xml.ws.api.BindingIDFactory;
import com.sun.xml.ws.api.SOAPVersion;
import com.sun.xml.ws.api.EndpointAddress;
import com.sun.xml.ws.api.WSDLLocator;
import com.sun.xml.ws.api.policy.PolicyResolver;
import com.sun.xml.ws.api.policy.PolicyResolverFactory;
import com.sun.xml.ws.api.addressing.AddressingVersion;
import com.sun.xml.ws.api.addressing.WSEndpointReference;
import com.sun.xml.ws.api.model.ParameterBinding;
import com.sun.xml.ws.api.model.wsdl.WSDLDescriptorKind;
import com.sun.xml.ws.api.model.wsdl.WSDLModel;
import com.sun.xml.ws.api.model.wsdl.editable.EditableWSDLBoundFault;
import com.sun.xml.ws.api.model.wsdl.editable.EditableWSDLBoundOperation;
import com.sun.xml.ws.api.model.wsdl.editable.EditableWSDLBoundPortType;
import com.sun.xml.ws.api.model.wsdl.editable.EditableWSDLFault;
import com.sun.xml.ws.api.model.wsdl.editable.EditableWSDLInput;
import com.sun.xml.ws.api.model.wsdl.editable.EditableWSDLMessage;
import com.sun.xml.ws.api.model.wsdl.editable.EditableWSDLModel;
import com.sun.xml.ws.api.model.wsdl.editable.EditableWSDLOperation;
import com.sun.xml.ws.api.model.wsdl.editable.EditableWSDLOutput;
import com.sun.xml.ws.api.model.wsdl.editable.EditableWSDLPart;
import com.sun.xml.ws.api.model.wsdl.editable.EditableWSDLPort;
import com.sun.xml.ws.api.model.wsdl.editable.EditableWSDLPortType;
import com.sun.xml.ws.api.model.wsdl.editable.EditableWSDLService;
import com.sun.xml.ws.api.server.Container;
import com.sun.xml.ws.api.server.ContainerResolver;
import com.sun.xml.ws.api.streaming.XMLStreamReaderFactory;
import com.sun.xml.ws.api.wsdl.parser.MetaDataResolver;
import com.sun.xml.ws.api.wsdl.parser.MetadataResolverFactory;
import com.sun.xml.ws.api.wsdl.parser.ServiceDescriptor;
import com.sun.xml.ws.api.wsdl.parser.WSDLParserExtension;
import com.sun.xml.ws.api.wsdl.parser.XMLEntityResolver;
import com.sun.xml.ws.api.wsdl.parser.XMLEntityResolver.Parser;
import com.sun.xml.ws.model.wsdl.*;
import com.sun.xml.ws.resources.ClientMessages;
import com.sun.xml.ws.resources.WsdlmodelMessages;
import com.sun.xml.ws.streaming.SourceReaderFactory;
import com.sun.xml.ws.streaming.TidyXMLStreamReader;
import com.sun.xml.ws.streaming.XMLStreamReaderUtil;
import com.sun.xml.ws.util.ServiceFinder;
import com.sun.xml.ws.util.xml.XmlUtil;
import com.sun.xml.ws.policy.jaxws.PolicyWSDLParserExtension;

import org.xml.sax.EntityResolver;
import org.xml.sax.SAXException;

import javax.jws.soap.SOAPBinding.Style;
import javax.xml.namespace.QName;
import javax.xml.stream.*;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.ws.Service;
import javax.xml.ws.WebServiceException;

import java.io.IOException;
import java.io.InputStream;
import java.io.FilterInputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;
import java.util.logging.Logger;

/**
 * Parses WSDL and builds {@link com.sun.xml.ws.api.model.wsdl.WSDLModel}.
 *
 * @author Vivek Pandey
 * @author Rama Pulavarthi
 */
public class RuntimeWSDLParser {

    private final EditableWSDLModel wsdlDoc;
    /**
     * Target namespace URI of the WSDL that we are currently parsing.
     */
    private String targetNamespace;
    /**
     * System IDs of WSDLs that are already read.
     */
    private final Set<String> importedWSDLs = new HashSet<String>();
    /**
     * Must not be null.
     */
    private final XMLEntityResolver resolver;

    private final PolicyResolver policyResolver;

    /**
     * The {@link WSDLParserExtension}. Always non-null.
     */
    private final WSDLParserExtension extensionFacade;

    private final WSDLParserExtensionContextImpl context;

    List<WSDLParserExtension> extensions;

    //Capture namespaces declared on the ancestors of wsa:EndpointReference, so that valid XmlStreamBuffer is created
    // from the EndpointReference fragment.
    Map<String, String> wsdldef_nsdecl = new HashMap<String, String>();
    Map<String, String> service_nsdecl = new HashMap<String, String>();
    Map<String, String> port_nsdecl = new HashMap<String, String>();

    /**
     * Parses the WSDL and gives WSDLModel. If wsdl parameter is null, then wsdlLoc is used to get the WSDL. If the WSDL
     * document could not be obtained then {@link MetadataResolverFactory} is tried to get the WSDL document, if not found
     * then as last option, if the wsdlLoc has no '?wsdl' as query parameter then it is tried by appending '?wsdl'.
     *
     * @param wsdlLoc
     *      Either this or {@code wsdl} parameter must be given.
     *      Null location means the system won't be able to resolve relative references in the WSDL,
     */
    public static WSDLModel parse(@Nullable URL wsdlLoc, @NotNull Source wsdlSource, @NotNull EntityResolver resolver,
                                      boolean isClientSide, Container container,
                                      WSDLParserExtension... extensions) throws IOException, XMLStreamException, SAXException {
        return parse(wsdlLoc, wsdlSource, resolver, isClientSide, container, Service.class, PolicyResolverFactory.create(),extensions);
    }

    /**
     * Parses the WSDL and gives WSDLModel. If wsdl parameter is null, then wsdlLoc is used to get the WSDL. If the WSDL
     * document could not be obtained then {@link MetadataResolverFactory} is tried to get the WSDL document, if not found
     * then as last option, if the wsdlLoc has no '?wsdl' as query parameter then it is tried by appending '?wsdl'.
     *
     * @param wsdlLoc
     *      Either this or {@code wsdl} parameter must be given.
     *      Null location means the system won't be able to resolve relative references in the WSDL,
     */
    public static WSDLModel parse(@Nullable URL wsdlLoc, @NotNull Source wsdlSource, @NotNull EntityResolver resolver,
                                      boolean isClientSide, Container container, Class serviceClass,
                                      WSDLParserExtension... extensions) throws IOException, XMLStreamException, SAXException {
        return parse(wsdlLoc, wsdlSource, resolver, isClientSide, container, serviceClass, PolicyResolverFactory.create(),extensions);
    }

    /**
     * Parses the WSDL and gives WSDLModel. If wsdl parameter is null, then wsdlLoc is used to get the WSDL. If the WSDL
     * document could not be obtained then {@link MetadataResolverFactory} is tried to get the WSDL document, if not found
     * then as last option, if the wsdlLoc has no '?wsdl' as query parameter then it is tried by appending '?wsdl'.
     *
     * @param wsdlLoc
     *      Either this or {@code wsdl} parameter must be given.
     *      Null location means the system won't be able to resolve relative references in the WSDL,
     */
    public static WSDLModel parse(@Nullable URL wsdlLoc, @NotNull Source wsdlSource, @NotNull EntityResolver resolver,
                                      boolean isClientSide, Container container, @NotNull PolicyResolver policyResolver,
                                      WSDLParserExtension... extensions) throws IOException, XMLStreamException, SAXException {
    	return parse(wsdlLoc, wsdlSource, resolver, isClientSide, container, Service.class, policyResolver, extensions);
    }
    
    /**
     * Parses the WSDL and gives WSDLModel. If wsdl parameter is null, then wsdlLoc is used to get the WSDL. If the WSDL
     * document could not be obtained then {@link MetadataResolverFactory} is tried to get the WSDL document, if not found
     * then as last option, if the wsdlLoc has no '?wsdl' as query parameter then it is tried by appending '?wsdl'.
     *
     * @param wsdlLoc
     *      Either this or {@code wsdl} parameter must be given.
     *      Null location means the system won't be able to resolve relative references in the WSDL,
     */
    public static WSDLModel parse(@Nullable URL wsdlLoc, @NotNull Source wsdlSource, @NotNull EntityResolver resolver,
                                      boolean isClientSide, Container container, Class serviceClass, 
                                      @NotNull PolicyResolver policyResolver,
                                      WSDLParserExtension... extensions) throws IOException, XMLStreamException, SAXException {
    	return parse(wsdlLoc, wsdlSource, resolver, isClientSide, container, serviceClass, policyResolver, false, extensions);
    }
    
    /**
     * Parses the WSDL and gives WSDLModel. If wsdl parameter is null, then wsdlLoc is used to get the WSDL. If the WSDL
     * document could not be obtained then {@link MetadataResolverFactory} is tried to get the WSDL document, if not found
     * then as last option, if the wsdlLoc has no '?wsdl' as query parameter then it is tried by appending '?wsdl'.
     *
     * @param wsdlLoc
     *      Either this or {@code wsdl} parameter must be given.
     *      Null location means the system won't be able to resolve relative references in the WSDL,
     */
    public static WSDLModel parse(@Nullable URL wsdlLoc, @NotNull Source wsdlSource, @NotNull EntityResolver resolver,
                                      boolean isClientSide, Container container, Class serviceClass, 
                                      @NotNull PolicyResolver policyResolver,
                                      boolean isUseStreamFromEntityResolverWrapper,
                                      WSDLParserExtension... extensions) throws IOException, XMLStreamException, SAXException {
        assert resolver != null;

        RuntimeWSDLParser wsdlParser = new RuntimeWSDLParser(wsdlSource.getSystemId(), new EntityResolverWrapper(resolver, isUseStreamFromEntityResolverWrapper), isClientSide, container, policyResolver, extensions);
        Parser parser;
        try{
            parser = wsdlParser.resolveWSDL(wsdlLoc, wsdlSource, serviceClass);
            if(!hasWSDLDefinitions(parser.parser)){
                throw new XMLStreamException(ClientMessages.RUNTIME_WSDLPARSER_INVALID_WSDL(parser.systemId,
                        WSDLConstants.QNAME_DEFINITIONS, parser.parser.getName(), parser.parser.getLocation()));
            }
        }catch(XMLStreamException e){
            //Try MEX if there is WSDLLoc available
            if(wsdlLoc == null)
                throw e;
            return tryWithMex(wsdlParser, wsdlLoc, resolver, isClientSide, container, e, serviceClass, policyResolver, extensions);

        }catch(IOException e){
            //Try MEX if there is WSDLLoc available
            if(wsdlLoc == null)
                throw e;
            return tryWithMex(wsdlParser, wsdlLoc, resolver, isClientSide, container, e, serviceClass, policyResolver, extensions);
        }
        wsdlParser.extensionFacade.start(wsdlParser.context);
        wsdlParser.parseWSDL(parser, false);
        wsdlParser.wsdlDoc.freeze();
        wsdlParser.extensionFacade.finished(wsdlParser.context);
        wsdlParser.extensionFacade.postFinished(wsdlParser.context);

        if(wsdlParser.wsdlDoc.getServices().isEmpty())
            throw new WebServiceException(ClientMessages.WSDL_CONTAINS_NO_SERVICE(wsdlLoc));

        return wsdlParser.wsdlDoc;
    }
    
    private static WSDLModel tryWithMex(@NotNull RuntimeWSDLParser wsdlParser, @NotNull URL wsdlLoc, @NotNull EntityResolver resolver, boolean isClientSide, Container container, Throwable e, Class serviceClass, PolicyResolver policyResolver, WSDLParserExtension... extensions) throws SAXException, XMLStreamException {
        ArrayList<Throwable> exceptions = new ArrayList<Throwable>();
        try {
            WSDLModel wsdlModel = wsdlParser.parseUsingMex(wsdlLoc, resolver, isClientSide, container, serviceClass, policyResolver,extensions);
            if(wsdlModel == null){
                throw new WebServiceException(ClientMessages.FAILED_TO_PARSE(wsdlLoc.toExternalForm(), e.getMessage()), e);
            }
            return wsdlModel;
        } catch (URISyntaxException e1) {
            exceptions.add(e);
            exceptions.add(e1);
        } catch(IOException e1){
            exceptions.add(e);
            exceptions.add(e1);
        }
        throw new InaccessibleWSDLException(exceptions);                
    }

    private WSDLModel parseUsingMex(@NotNull URL wsdlLoc, @NotNull EntityResolver resolver, boolean isClientSide, Container container, Class serviceClass, PolicyResolver policyResolver, WSDLParserExtension[] extensions) throws IOException, SAXException, XMLStreamException, URISyntaxException {
        //try MEX
        MetaDataResolver mdResolver = null;
        ServiceDescriptor serviceDescriptor = null;
        RuntimeWSDLParser wsdlParser = null;

        //Currently we try the first available MetadataResolverFactory that gives us a WSDL document
        for (MetadataResolverFactory resolverFactory : ServiceFinder.find(MetadataResolverFactory.class)) {
            mdResolver = resolverFactory.metadataResolver(resolver);
            serviceDescriptor = mdResolver.resolve(wsdlLoc.toURI());
            //we got the ServiceDescriptor, now break
            if (serviceDescriptor != null)
                break;
        }
        if (serviceDescriptor != null) {
            List<? extends Source> wsdls = serviceDescriptor.getWSDLs();
            wsdlParser = new RuntimeWSDLParser(wsdlLoc.toExternalForm(), new MexEntityResolver(wsdls), isClientSide, container, policyResolver, extensions);
            wsdlParser.extensionFacade.start(wsdlParser.context);
            
            for(Source src: wsdls ) {
                String systemId = src.getSystemId();
                Parser parser = wsdlParser.resolver.resolveEntity(null, systemId);
                wsdlParser.parseWSDL(parser, false);
            }
        }
        //Incase that mex is not present or it couldn't get the metadata, try by appending ?wsdl and give
        // it a last shot else fail
        if ((mdResolver == null || serviceDescriptor == null) && (wsdlLoc.getProtocol().equals("http") || wsdlLoc.getProtocol().equals("https")) && (wsdlLoc.getQuery() == null)) {
            String urlString = wsdlLoc.toExternalForm();
            urlString += "?wsdl";
            wsdlLoc = new URL(urlString);
            wsdlParser = new RuntimeWSDLParser(wsdlLoc.toExternalForm(),new EntityResolverWrapper(resolver), isClientSide, container, policyResolver, extensions);
            wsdlParser.extensionFacade.start(wsdlParser.context);
            Parser parser = resolveWSDL(wsdlLoc, new StreamSource(wsdlLoc.toExternalForm()), serviceClass);
            wsdlParser.parseWSDL(parser, false);
        }

        if(wsdlParser == null)
            return null;

        wsdlParser.wsdlDoc.freeze();
        wsdlParser.extensionFacade.finished(wsdlParser.context);
        wsdlParser.extensionFacade.postFinished(wsdlParser.context);
        return wsdlParser.wsdlDoc;
    }

    private static boolean hasWSDLDefinitions(XMLStreamReader reader) {
        XMLStreamReaderUtil.nextElementContent(reader);
        return reader.getName().equals(WSDLConstants.QNAME_DEFINITIONS);
    }

    public static WSDLModel parse(XMLEntityResolver.Parser wsdl, XMLEntityResolver resolver, boolean isClientSide, Container container, PolicyResolver policyResolver, WSDLParserExtension... extensions) throws IOException, XMLStreamException, SAXException {
        assert resolver != null;
        RuntimeWSDLParser parser = new RuntimeWSDLParser( wsdl.systemId.toExternalForm(), resolver, isClientSide, container, policyResolver, extensions);
        parser.extensionFacade.start(parser.context);
        parser.parseWSDL(wsdl, false);
        parser.wsdlDoc.freeze();
        parser.extensionFacade.finished(parser.context);
        parser.extensionFacade.postFinished(parser.context);
        return parser.wsdlDoc;
    }

    public static WSDLModel parse(XMLEntityResolver.Parser wsdl, XMLEntityResolver resolver, boolean isClientSide, Container container, WSDLParserExtension... extensions) throws IOException, XMLStreamException, SAXException {
        assert resolver != null;
        RuntimeWSDLParser parser = new RuntimeWSDLParser( wsdl.systemId.toExternalForm(), resolver, isClientSide, container, PolicyResolverFactory.create(), extensions);
        parser.extensionFacade.start(parser.context);
        parser.parseWSDL(wsdl, false);
        parser.wsdlDoc.freeze();
        parser.extensionFacade.finished(parser.context);
        parser.extensionFacade.postFinished(parser.context);
        return parser.wsdlDoc;
    }

    private RuntimeWSDLParser(@NotNull String sourceLocation, XMLEntityResolver resolver, boolean isClientSide, Container container, PolicyResolver policyResolver, WSDLParserExtension... extensions) {
        this.wsdlDoc = sourceLocation!=null ? new WSDLModelImpl(sourceLocation) : new WSDLModelImpl();
        this.resolver = resolver;
        this.policyResolver = policyResolver;
        this.extensions = new ArrayList<WSDLParserExtension>();
        this.context = new WSDLParserExtensionContextImpl(wsdlDoc, isClientSide, container, policyResolver);

        boolean isPolicyExtensionFound = false;
        for (WSDLParserExtension e : extensions) {
        	if (e instanceof com.sun.xml.ws.api.wsdl.parser.PolicyWSDLParserExtension)
        		isPolicyExtensionFound = true;
            register(e);
        }

        // register handlers for default extensions
        if (!isPolicyExtensionFound)
        	register(new PolicyWSDLParserExtension());
        register(new MemberSubmissionAddressingWSDLParserExtension());
        register(new W3CAddressingWSDLParserExtension());
        register(new W3CAddressingMetadataWSDLParserExtension());

        this.extensionFacade =  new WSDLParserExtensionFacade(this.extensions.toArray(new WSDLParserExtension[0]));
    }

    private Parser resolveWSDL(@Nullable URL wsdlLoc, @NotNull Source wsdlSource, Class serviceClass) throws IOException, SAXException, XMLStreamException {
        String systemId = wsdlSource.getSystemId();

        XMLEntityResolver.Parser parser = resolver.resolveEntity(null, systemId);
        if (parser == null && wsdlLoc != null) {
        	String exForm = wsdlLoc.toExternalForm();
            parser = resolver.resolveEntity(null, exForm);

            if (parser == null && serviceClass != null) {
            	URL ru = serviceClass.getResource(".");
            	if (ru != null) {
            		String ruExForm = ru.toExternalForm();
	            	if (exForm.startsWith(ruExForm)) {
	            		parser = resolver.resolveEntity(null, exForm.substring(ruExForm.length()));
	            	}
            	}
            }
        }
        if (parser == null) {
            //If a WSDL source is provided that is known to be readable, then
            //prioritize that over the URL - this avoids going over the network
            //an additional time if a valid WSDL Source is provided - Deva Sagar 09/20/2011
            if (isKnownReadableSource(wsdlSource)) {
                parser = new Parser(wsdlLoc, createReader(wsdlSource));
            } else if (wsdlLoc != null) {
                parser = new Parser(wsdlLoc, createReader(wsdlLoc, serviceClass));
            }

            //parser could still be null if isKnownReadableSource returns
            //false and wsdlLoc is also null. Fall back to using Source based 
            //parser since Source is not null
            if (parser == null) {
                parser = new Parser(wsdlLoc, createReader(wsdlSource));
            }
        }
        return parser;
    }

    private boolean isKnownReadableSource(Source wsdlSource) {
		if (wsdlSource instanceof StreamSource) {
			return (((StreamSource) wsdlSource).getInputStream() != null ||
					((StreamSource) wsdlSource).getReader() != null);
		} else {
			return false;
		}
	}

    private XMLStreamReader createReader(@NotNull Source src) throws XMLStreamException {
        return new TidyXMLStreamReader(SourceReaderFactory.createSourceReader(src, true), null);
    }

    private void parseImport(@NotNull URL wsdlLoc) throws XMLStreamException, IOException, SAXException {
        String systemId = wsdlLoc.toExternalForm();
        XMLEntityResolver.Parser parser = resolver.resolveEntity(null, systemId);
        if (parser == null) {
            parser = new Parser(wsdlLoc, createReader(wsdlLoc));
        }
        parseWSDL(parser, true);
    }

    private void parseWSDL(Parser parser, boolean imported) throws XMLStreamException, IOException, SAXException {
        XMLStreamReader reader = parser.parser;
        try {
            // avoid processing the same WSDL twice.
            // if no system ID is given, the check won't work
            if (parser.systemId != null && !importedWSDLs.add(parser.systemId.toExternalForm()))
                return;

            if(reader.getEventType() == XMLStreamConstants.START_DOCUMENT)
                XMLStreamReaderUtil.nextElementContent(reader);
            if (WSDLConstants.QNAME_DEFINITIONS.equals(reader.getName())) {
                readNSDecl(wsdldef_nsdecl, reader);
            }
            if (reader.getEventType()!= XMLStreamConstants.END_DOCUMENT && reader.getName().equals(WSDLConstants.QNAME_SCHEMA)) {
                if (imported) {
                    // wsdl:import could be a schema. Relaxing BP R2001 requirement.
                    LOGGER.warning(WsdlmodelMessages.WSDL_IMPORT_SHOULD_BE_WSDL(parser.systemId));
                    return;
                }
            }

            //get the targetNamespace of the service
            String tns = ParserUtil.getMandatoryNonEmptyAttribute(reader, WSDLConstants.ATTR_TNS);

            final String oldTargetNamespace = targetNamespace;
            targetNamespace = tns;

            while (XMLStreamReaderUtil.nextElementContent(reader) !=
                    XMLStreamConstants.END_ELEMENT) {
                if (reader.getEventType() == XMLStreamConstants.END_DOCUMENT)
                    break;

                QName name = reader.getName();
                if (WSDLConstants.QNAME_IMPORT.equals(name)) {
                    parseImport(parser.systemId, reader);
                } else if (WSDLConstants.QNAME_MESSAGE.equals(name)) {
                    parseMessage(reader);
                } else if (WSDLConstants.QNAME_PORT_TYPE.equals(name)) {
                    parsePortType(reader);
                } else if (WSDLConstants.QNAME_BINDING.equals(name)) {
                    parseBinding(reader);
                } else if (WSDLConstants.QNAME_SERVICE.equals(name)) {
                    parseService(reader);
                } else {
                    extensionFacade.definitionsElements(reader);
                }
            }
            targetNamespace = oldTargetNamespace;
        } finally {
            this.wsdldef_nsdecl = new HashMap<String,String>();
            reader.close();
        }
    }

    private void parseService(XMLStreamReader reader) {
        service_nsdecl.putAll(wsdldef_nsdecl);
        readNSDecl(service_nsdecl,reader);

        String serviceName = ParserUtil.getMandatoryNonEmptyAttribute(reader, WSDLConstants.ATTR_NAME);
        EditableWSDLService service = new WSDLServiceImpl(reader,wsdlDoc,new QName(targetNamespace, serviceName));
        extensionFacade.serviceAttributes(service, reader);
        while (XMLStreamReaderUtil.nextElementContent(reader) != XMLStreamConstants.END_ELEMENT) {
            QName name = reader.getName();
            if (WSDLConstants.QNAME_PORT.equals(name)) {
                parsePort(reader, service);
                if (reader.getEventType() != XMLStreamConstants.END_ELEMENT) {
                    XMLStreamReaderUtil.next(reader);
                }
            } else {
                extensionFacade.serviceElements(service, reader);
            }
        }
        wsdlDoc.addService(service);
        service_nsdecl =  new HashMap<String, String>();
    }

    private void parsePort(XMLStreamReader reader, EditableWSDLService service) {
        port_nsdecl.putAll(service_nsdecl);
        readNSDecl(port_nsdecl,reader);

        String portName = ParserUtil.getMandatoryNonEmptyAttribute(reader, WSDLConstants.ATTR_NAME);
        String binding = ParserUtil.getMandatoryNonEmptyAttribute(reader, "binding");

        QName bindingName = ParserUtil.getQName(reader, binding);
        QName portQName = new QName(service.getName().getNamespaceURI(), portName);
        EditableWSDLPort port = new WSDLPortImpl(reader,service, portQName, bindingName);

        extensionFacade.portAttributes(port, reader);

        String location;
        while (XMLStreamReaderUtil.nextElementContent(reader) != XMLStreamConstants.END_ELEMENT) {
            QName name = reader.getName();
            if (SOAPConstants.QNAME_ADDRESS.equals(name) || SOAPConstants.QNAME_SOAP12ADDRESS.equals(name)) {
                location = ParserUtil.getMandatoryNonEmptyAttribute(reader, WSDLConstants.ATTR_LOCATION);
                if (location != null) {
                    try {
                        port.setAddress(new EndpointAddress(location));
                    } catch (URISyntaxException e) {
                        //Lets not throw any exception, latter on it should be thrown when invocation happens. At this
                        // time user has option to set the endopint address using request contexxt property.
                    }
                }
                XMLStreamReaderUtil.next(reader);
            } else if (AddressingVersion.W3C.nsUri.equals(name.getNamespaceURI()) &&
                    "EndpointReference".equals(name.getLocalPart())) {
                try {
                    StreamReaderBufferCreator creator = new StreamReaderBufferCreator(new MutableXMLStreamBuffer());
                    XMLStreamBuffer eprbuffer = new XMLStreamBufferMark(port_nsdecl, creator);
                    creator.createElementFragment(reader, false);

                    WSEndpointReference wsepr = new WSEndpointReference(eprbuffer, AddressingVersion.W3C);
                    //wsepr.toSpec().writeTo(new StreamResult(System.out));
                    port.setEPR(wsepr);
                    /** XMLStreamBuffer.createNewBufferFromXMLStreamReader(reader) called from inside WSEndpointReference()
                     *  consumes the complete EPR infoset and moves to the next element. This breaks the normal wsdl parser
                     *  processing where it expects anyone reading the infoset to move to the end of the element that its reading
                     *  and not to the next element.
                     */
                    if(reader.getEventType() == XMLStreamConstants.END_ELEMENT && reader.getName().equals(WSDLConstants.QNAME_PORT))
                        break;
                } catch (XMLStreamException e) {
                    throw new WebServiceException(e);
                }
            } else {

                extensionFacade.portElements(port, reader);
            }
        }
        if (port.getAddress() == null) {
            try {
                port.setAddress(new EndpointAddress(""));
            } catch (URISyntaxException e) {
                //Lets not throw any exception, latter on it should be thrown when invocation happens. At this
                //time user has option to set the endopint address using request contexxt property.
            }
        }
        service.put(portQName, port);
        port_nsdecl =new HashMap<String, String>();                        
    }

    private void parseBinding(XMLStreamReader reader) {
        String bindingName = ParserUtil.getMandatoryNonEmptyAttribute(reader, "name");
        String portTypeName = ParserUtil.getMandatoryNonEmptyAttribute(reader, "type");
        if ((bindingName == null) || (portTypeName == null)) {
            //TODO: throw exception?
            //
            //  wsdl:binding element for now
            XMLStreamReaderUtil.skipElement(reader);
            return;
        }
        EditableWSDLBoundPortType binding = new WSDLBoundPortTypeImpl(reader,wsdlDoc, new QName(targetNamespace, bindingName),
                ParserUtil.getQName(reader, portTypeName));
        extensionFacade.bindingAttributes(binding, reader);

        while (XMLStreamReaderUtil.nextElementContent(reader) != XMLStreamConstants.END_ELEMENT) {
            QName name = reader.getName();
            if (WSDLConstants.NS_SOAP_BINDING.equals(name)) {
                String transport = reader.getAttributeValue(null, WSDLConstants.ATTR_TRANSPORT);
                binding.setBindingId(createBindingId(transport, SOAPVersion.SOAP_11));

                String style = reader.getAttributeValue(null, "style");

                if ((style != null) && (style.equals("rpc"))) {
                    binding.setStyle(Style.RPC);
                } else {
                    binding.setStyle(Style.DOCUMENT);
                }
                goToEnd(reader);
            } else if (WSDLConstants.NS_SOAP12_BINDING.equals(name)) {
                String transport = reader.getAttributeValue(null, WSDLConstants.ATTR_TRANSPORT);
                binding.setBindingId(createBindingId(transport, SOAPVersion.SOAP_12));

                String style = reader.getAttributeValue(null, "style");
                if ((style != null) && (style.equals("rpc"))) {
                    binding.setStyle(Style.RPC);
                } else {
                    binding.setStyle(Style.DOCUMENT);
                }
                goToEnd(reader);
            } else if (WSDLConstants.QNAME_OPERATION.equals(name)) {
                parseBindingOperation(reader, binding);
            } else {
                extensionFacade.bindingElements(binding, reader);
            }
        }
    }
    
    private static BindingID createBindingId(String transport, SOAPVersion soapVersion) {
        if (!transport.equals(SOAPConstants.URI_SOAP_TRANSPORT_HTTP)) {
            for( BindingIDFactory f : ServiceFinder.find(BindingIDFactory.class) ) {
                BindingID bindingId = f.create(transport, soapVersion);
                if(bindingId!=null) {
                    return bindingId;
                }
            }
        }
        return soapVersion.equals(SOAPVersion.SOAP_11)?BindingID.SOAP11_HTTP:BindingID.SOAP12_HTTP;
    }


    private void parseBindingOperation(XMLStreamReader reader, EditableWSDLBoundPortType binding) {
        String bindingOpName = ParserUtil.getMandatoryNonEmptyAttribute(reader, "name");
        if (bindingOpName == null) {
            //TODO: throw exception?
            //skip wsdl:binding element for now
            XMLStreamReaderUtil.skipElement(reader);
            return;
        }

        QName opName = new QName(binding.getPortTypeName().getNamespaceURI(), bindingOpName);
        EditableWSDLBoundOperation bindingOp = new WSDLBoundOperationImpl(reader,binding, opName);
        binding.put(opName, bindingOp);
        extensionFacade.bindingOperationAttributes(bindingOp, reader);

        while (XMLStreamReaderUtil.nextElementContent(reader) != XMLStreamConstants.END_ELEMENT) {
            QName name = reader.getName();
            String style = null;
            if (WSDLConstants.QNAME_INPUT.equals(name)) {
                parseInputBinding(reader, bindingOp);
            } else if (WSDLConstants.QNAME_OUTPUT.equals(name)) {
                parseOutputBinding(reader, bindingOp);
            } else if (WSDLConstants.QNAME_FAULT.equals(name)) {
                parseFaultBinding(reader, bindingOp);
            } else if (SOAPConstants.QNAME_OPERATION.equals(name) ||
                    SOAPConstants.QNAME_SOAP12OPERATION.equals(name)) {
                style = reader.getAttributeValue(null, "style");
                String soapAction = reader.getAttributeValue(null, "soapAction");

                if (soapAction != null)
                    bindingOp.setSoapAction(soapAction);

                goToEnd(reader);
            } else {
                extensionFacade.bindingOperationElements(bindingOp, reader);
            }
            /**
             *  If style attribute is present set it otherwise set the style as defined
             *  on the <soap:binding> element
             */
            if (style != null) {
                if (style.equals("rpc"))
                    bindingOp.setStyle(Style.RPC);
                else
                    bindingOp.setStyle(Style.DOCUMENT);
            } else {
                bindingOp.setStyle(binding.getStyle());
            }            
        }
    }

    private void parseInputBinding(XMLStreamReader reader, EditableWSDLBoundOperation bindingOp) {
        boolean bodyFound = false;
        extensionFacade.bindingOperationInputAttributes(bindingOp, reader);
        while (XMLStreamReaderUtil.nextElementContent(reader) != XMLStreamConstants.END_ELEMENT) {
            QName name = reader.getName();
            if ((SOAPConstants.QNAME_BODY.equals(name) || SOAPConstants.QNAME_SOAP12BODY.equals(name)) && !bodyFound) {
                bodyFound = true;
                bindingOp.setInputExplicitBodyParts(parseSOAPBodyBinding(reader, bindingOp, BindingMode.INPUT));
                goToEnd(reader);
            } else if ((SOAPConstants.QNAME_HEADER.equals(name) || SOAPConstants.QNAME_SOAP12HEADER.equals(name))) {
                parseSOAPHeaderBinding(reader, bindingOp.getInputParts());
            } else if (MIMEConstants.QNAME_MULTIPART_RELATED.equals(name)) {
                parseMimeMultipartBinding(reader, bindingOp, BindingMode.INPUT);
            } else {
                extensionFacade.bindingOperationInputElements(bindingOp, reader);
            }
        }
    }

    private void parseOutputBinding(XMLStreamReader reader, EditableWSDLBoundOperation bindingOp) {
        boolean bodyFound = false;
        extensionFacade.bindingOperationOutputAttributes(bindingOp, reader);
        while (XMLStreamReaderUtil.nextElementContent(reader) != XMLStreamConstants.END_ELEMENT) {
            QName name = reader.getName();
            if ((SOAPConstants.QNAME_BODY.equals(name) || SOAPConstants.QNAME_SOAP12BODY.equals(name)) && !bodyFound) {
                bodyFound = true;
                bindingOp.setOutputExplicitBodyParts(parseSOAPBodyBinding(reader, bindingOp, BindingMode.OUTPUT));
                goToEnd(reader);
            } else if ((SOAPConstants.QNAME_HEADER.equals(name) || SOAPConstants.QNAME_SOAP12HEADER.equals(name))) {
                parseSOAPHeaderBinding(reader, bindingOp.getOutputParts());
            } else if (MIMEConstants.QNAME_MULTIPART_RELATED.equals(name)) {
                parseMimeMultipartBinding(reader, bindingOp, BindingMode.OUTPUT);
            } else {
                extensionFacade.bindingOperationOutputElements(bindingOp, reader);
            }
        }
    }

    private void parseFaultBinding(XMLStreamReader reader, EditableWSDLBoundOperation bindingOp) {
        String faultName = ParserUtil.getMandatoryNonEmptyAttribute(reader, "name");
        EditableWSDLBoundFault wsdlBoundFault = new WSDLBoundFaultImpl(reader, faultName, bindingOp);
        bindingOp.addFault(wsdlBoundFault);

        extensionFacade.bindingOperationFaultAttributes(wsdlBoundFault, reader);

        while (XMLStreamReaderUtil.nextElementContent(reader) != XMLStreamConstants.END_ELEMENT) {
            extensionFacade.bindingOperationFaultElements(wsdlBoundFault, reader);
        }
    }

    private enum BindingMode {
        INPUT, OUTPUT, FAULT}

    private static boolean parseSOAPBodyBinding(XMLStreamReader reader, EditableWSDLBoundOperation op, BindingMode mode) {
        String namespace = reader.getAttributeValue(null, "namespace");
        if (mode == BindingMode.INPUT) {
            op.setRequestNamespace(namespace);
            return parseSOAPBodyBinding(reader, op.getInputParts());
        }
        //resp
        op.setResponseNamespace(namespace);
        return parseSOAPBodyBinding(reader, op.getOutputParts());
    }

    /**
     * Returns true if body has explicit parts declaration
     */
    private static boolean parseSOAPBodyBinding(XMLStreamReader reader, Map<String, ParameterBinding> parts) {
        String partsString = reader.getAttributeValue(null, "parts");
        if (partsString != null) {
            List<String> partsList = XmlUtil.parseTokenList(partsString);
            if (partsList.isEmpty()) {
                parts.put(" ", ParameterBinding.BODY);
            } else {
                for (String part : partsList) {
                    parts.put(part, ParameterBinding.BODY);
                }
            }
            return true;
        }
        return false;
    }

    private static void parseSOAPHeaderBinding(XMLStreamReader reader, Map<String, ParameterBinding> parts) {
        String part = reader.getAttributeValue(null, "part");
        //if(part == null| part.equals("")||message == null || message.equals("")){
        if (part == null || part.equals("")) {
            return;
        }

        //lets not worry about message attribute for now, probably additional headers wont be there
        //String message = reader.getAttributeValue(null, "message");
        //QName msgName = ParserUtil.getQName(reader, message);
        parts.put(part, ParameterBinding.HEADER);
        goToEnd(reader);
    }


    private static void parseMimeMultipartBinding(XMLStreamReader reader, EditableWSDLBoundOperation op, BindingMode mode) {
        while (XMLStreamReaderUtil.nextElementContent(reader) != XMLStreamConstants.END_ELEMENT) {
            QName name = reader.getName();
            if (MIMEConstants.QNAME_PART.equals(name)) {
                parseMIMEPart(reader, op, mode);
            } else {
                XMLStreamReaderUtil.skipElement(reader);
            }
        }
    }

    private static void parseMIMEPart(XMLStreamReader reader, EditableWSDLBoundOperation op, BindingMode mode) {
        boolean bodyFound = false;
        Map<String, ParameterBinding> parts = null;
        if (mode == BindingMode.INPUT) {
            parts = op.getInputParts();
        } else if (mode == BindingMode.OUTPUT) {
            parts = op.getOutputParts();
        } else if (mode == BindingMode.FAULT) {
            parts = op.getFaultParts();
        }
        while (XMLStreamReaderUtil.nextElementContent(reader) != XMLStreamConstants.END_ELEMENT) {
            QName name = reader.getName();
            if (SOAPConstants.QNAME_BODY.equals(name) && !bodyFound) {
                bodyFound = true;
                parseSOAPBodyBinding(reader, op, mode);
                XMLStreamReaderUtil.next(reader);
            } else if (SOAPConstants.QNAME_HEADER.equals(name)) {
                bodyFound = true;
                parseSOAPHeaderBinding(reader, parts);
                XMLStreamReaderUtil.next(reader);
            } else if (MIMEConstants.QNAME_CONTENT.equals(name)) {
                String part = reader.getAttributeValue(null, "part");
                String type = reader.getAttributeValue(null, "type");
                if ((part == null) || (type == null)) {
                    XMLStreamReaderUtil.skipElement(reader);
                    continue;
                }
                ParameterBinding sb = ParameterBinding.createAttachment(type);
                if (parts != null && sb != null && part != null)
                    parts.put(part, sb);
                XMLStreamReaderUtil.next(reader);
            } else {
                XMLStreamReaderUtil.skipElement(reader);
            }
        }
    }

    protected void parseImport(@Nullable URL baseURL, XMLStreamReader reader) throws IOException, SAXException, XMLStreamException {
        // expand to the absolute URL of the imported WSDL.
        String importLocation =
                ParserUtil.getMandatoryNonEmptyAttribute(reader, WSDLConstants.ATTR_LOCATION);
        URL importURL;
        if(baseURL!=null)
            importURL = new URL(baseURL, importLocation);
        else // no base URL. this better be absolute
            importURL = new URL(importLocation);
        parseImport(importURL);
        while (XMLStreamReaderUtil.nextElementContent(reader) != XMLStreamConstants.END_ELEMENT) {
            XMLStreamReaderUtil.skipElement(reader);
        }
    }

    private void parsePortType(XMLStreamReader reader) {
        String portTypeName = ParserUtil.getMandatoryNonEmptyAttribute(reader, WSDLConstants.ATTR_NAME);
        if (portTypeName == null) {
            //TODO: throw exception?
            //skip wsdl:portType element for now
            XMLStreamReaderUtil.skipElement(reader);
            return;
        }
        EditableWSDLPortType portType = new WSDLPortTypeImpl(reader,wsdlDoc, new QName(targetNamespace, portTypeName));
        extensionFacade.portTypeAttributes(portType, reader);
        wsdlDoc.addPortType(portType);
        while (XMLStreamReaderUtil.nextElementContent(reader) != XMLStreamConstants.END_ELEMENT) {
            QName name = reader.getName();
            if (WSDLConstants.QNAME_OPERATION.equals(name)) {
                parsePortTypeOperation(reader, portType);
            } else {
                extensionFacade.portTypeElements(portType, reader);
            }
        }
    }


    private void parsePortTypeOperation(XMLStreamReader reader, EditableWSDLPortType portType) {
        String operationName = ParserUtil.getMandatoryNonEmptyAttribute(reader, WSDLConstants.ATTR_NAME);
        if (operationName == null) {
            //TODO: throw exception?
            //skip wsdl:portType element for now
            XMLStreamReaderUtil.skipElement(reader);
            return;
        }

        QName operationQName = new QName(portType.getName().getNamespaceURI(), operationName);
        EditableWSDLOperation operation = new WSDLOperationImpl(reader,portType, operationQName);
        extensionFacade.portTypeOperationAttributes(operation, reader);
        String parameterOrder = ParserUtil.getAttribute(reader, "parameterOrder");
        operation.setParameterOrder(parameterOrder);
        portType.put(operationName, operation);
        while (XMLStreamReaderUtil.nextElementContent(reader) != XMLStreamConstants.END_ELEMENT) {
            QName name = reader.getName();
            if (name.equals(WSDLConstants.QNAME_INPUT)) {
                parsePortTypeOperationInput(reader, operation);
            } else if (name.equals(WSDLConstants.QNAME_OUTPUT)) {
                parsePortTypeOperationOutput(reader, operation);
            } else if (name.equals(WSDLConstants.QNAME_FAULT)) {
                parsePortTypeOperationFault(reader, operation);
            } else {
                extensionFacade.portTypeOperationElements(operation, reader);
            }
        }
    }


    private void parsePortTypeOperationFault(XMLStreamReader reader, EditableWSDLOperation operation) {
        String msg = ParserUtil.getMandatoryNonEmptyAttribute(reader, "message");
        QName msgName = ParserUtil.getQName(reader, msg);
        String name = ParserUtil.getMandatoryNonEmptyAttribute(reader, "name");
        EditableWSDLFault fault = new WSDLFaultImpl(reader,name, msgName, operation);
        operation.addFault(fault);
        extensionFacade.portTypeOperationFaultAttributes(fault, reader);
        extensionFacade.portTypeOperationFault(operation, reader);
        while (XMLStreamReaderUtil.nextElementContent(reader) != XMLStreamConstants.END_ELEMENT) {
            extensionFacade.portTypeOperationFaultElements(fault, reader);
        }
    }

    private void parsePortTypeOperationInput(XMLStreamReader reader, EditableWSDLOperation operation) {
        String msg = ParserUtil.getMandatoryNonEmptyAttribute(reader, "message");
        QName msgName = ParserUtil.getQName(reader, msg);
        String name = ParserUtil.getAttribute(reader, "name");
        EditableWSDLInput input = new WSDLInputImpl(reader, name, msgName, operation);
        operation.setInput(input);
        extensionFacade.portTypeOperationInputAttributes(input, reader);
        extensionFacade.portTypeOperationInput(operation, reader);
        while (XMLStreamReaderUtil.nextElementContent(reader) != XMLStreamConstants.END_ELEMENT) {
            extensionFacade.portTypeOperationInputElements(input, reader);
        }
    }

    private void parsePortTypeOperationOutput(XMLStreamReader reader, EditableWSDLOperation operation) {
        String msg = ParserUtil.getAttribute(reader, "message");
        QName msgName = ParserUtil.getQName(reader, msg);
        String name = ParserUtil.getAttribute(reader, "name");
        EditableWSDLOutput output = new WSDLOutputImpl(reader,name, msgName, operation);
        operation.setOutput(output);
        extensionFacade.portTypeOperationOutputAttributes(output, reader);
        extensionFacade.portTypeOperationOutput(operation, reader);
        while (XMLStreamReaderUtil.nextElementContent(reader) != XMLStreamConstants.END_ELEMENT) {
            extensionFacade.portTypeOperationOutputElements(output, reader);
        }
    }

    private void parseMessage(XMLStreamReader reader) {
        String msgName = ParserUtil.getMandatoryNonEmptyAttribute(reader, WSDLConstants.ATTR_NAME);
        EditableWSDLMessage msg = new WSDLMessageImpl(reader,new QName(targetNamespace, msgName));
        extensionFacade.messageAttributes(msg, reader);
        int partIndex = 0;
        while (XMLStreamReaderUtil.nextElementContent(reader) != XMLStreamConstants.END_ELEMENT) {
            QName name = reader.getName();
            if (WSDLConstants.QNAME_PART.equals(name)) {
                String part = ParserUtil.getMandatoryNonEmptyAttribute(reader, WSDLConstants.ATTR_NAME);
                String desc = null;
                int index = reader.getAttributeCount();
                WSDLDescriptorKind kind = WSDLDescriptorKind.ELEMENT;
                for (int i = 0; i < index; i++) {
                    QName descName = reader.getAttributeName(i);
                    if (descName.getLocalPart().equals("element"))
                        kind = WSDLDescriptorKind.ELEMENT;
                    else if (descName.getLocalPart().equals("type"))
                        kind = WSDLDescriptorKind.TYPE;

                    if (descName.getLocalPart().equals("element") || descName.getLocalPart().equals("type")) {
                        desc = reader.getAttributeValue(i);
                        break;
                    }
                }
                if (desc != null) {
                    EditableWSDLPart wsdlPart = new WSDLPartImpl(reader, part, partIndex, new WSDLPartDescriptorImpl(reader,ParserUtil.getQName(reader, desc), kind));
                    msg.add(wsdlPart);
                }
                if (reader.getEventType() != XMLStreamConstants.END_ELEMENT)
                    goToEnd(reader);
            } else {
                extensionFacade.messageElements(msg, reader);
            }
        }
        wsdlDoc.addMessage(msg);
        if (reader.getEventType() != XMLStreamConstants.END_ELEMENT)
            goToEnd(reader);
    }

    private static void goToEnd(XMLStreamReader reader) {
        while (XMLStreamReaderUtil.nextElementContent(reader) != XMLStreamConstants.END_ELEMENT) {
            XMLStreamReaderUtil.skipElement(reader);
        }
    }

    /**
     * Make sure to return a "fresh" reader each time it is called because
     * more than one active reader may be needed within a single thread
     * to parse a WSDL file.
     */
    private static XMLStreamReader createReader(URL wsdlLoc) throws IOException, XMLStreamException {
        return createReader(wsdlLoc, null);
    }
    
    /**
     * Make sure to return a "fresh" reader each time it is called because
     * more than one active reader may be needed within a single thread
     * to parse a WSDL file.
     */
    private static XMLStreamReader createReader(URL wsdlLoc, Class<Service> serviceClass) throws IOException, XMLStreamException {
        InputStream stream;
        try {
        	stream = wsdlLoc.openStream();
        } catch (IOException io) {
        	out:
        	do {
	        	if (serviceClass != null) {
	        		WSDLLocator locator = ContainerResolver.getInstance().getContainer().getSPI(WSDLLocator.class);
	        		if (locator != null) {
		        	  String exForm = wsdlLoc.toExternalForm();
		                  URL ru = serviceClass.getResource(".");
	        		  String loc = wsdlLoc.getPath();
                                  if (ru != null) {
                                    String ruExForm = ru.toExternalForm();
                                    if (exForm.startsWith(ruExForm)) {
                                      loc = exForm.substring(ruExForm.length());
                                    }
                                  }
	        		  wsdlLoc = locator.locateWSDL(serviceClass, loc);
	        		  if (wsdlLoc != null) {
						stream = new FilterInputStream(wsdlLoc.openStream()) {
						    boolean closed;
						
						    @Override
						    public void close() throws IOException {
						        if (!closed) {
						            closed = true;
						            byte[] buf = new byte[8192];
						            while(read(buf) != -1);
						            super.close();
						        }
						    }
						};
	        			  break out;
	        		  }
	        		}
	        	}
	        	throw io;
        	} while(true);
        }
        
        return new TidyXMLStreamReader(XMLStreamReaderFactory.create(wsdlLoc.toExternalForm(), stream, false), stream);
    }

    private void register(WSDLParserExtension e) {
        // protect JAX-WS RI from broken parser extension
        extensions.add(new FoolProofParserExtension(e));
    }

    /**
     * Reads the namespace declarations from the reader's current position in to the map. The reader is expected to be
     * on the start element.
     *
     * @param ns_map
     * @param reader
     */
    private static void readNSDecl(Map<String, String> ns_map, XMLStreamReader reader) {
        if (reader.getNamespaceCount() > 0) {
            for (int i = 0; i < reader.getNamespaceCount(); i++) {
                ns_map.put(reader.getNamespacePrefix(i), reader.getNamespaceURI(i));
            }
        }
    }

    private static final Logger LOGGER = Logger.getLogger(RuntimeWSDLParser.class.getName());
}
