/*
 *  soapUI, copyright (C) 2004-2012 smartbear.com 
 *
 *  soapUI is free software; you can redistribute it and/or modify it under the 
 *  terms of version 2.1 of the GNU Lesser General Public License as published by 
 *  the Free Software Foundation.
 *
 *  soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without 
 *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 *  See the GNU Lesser General Public License for more details at gnu.org.
 */

package com.eviware.soapui.impl.wsdl.support.wsdl;

import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.wsdl.Binding;
import javax.wsdl.BindingFault;
import javax.wsdl.BindingInput;
import javax.wsdl.BindingOperation;
import javax.wsdl.BindingOutput;
import javax.wsdl.Definition;
import javax.wsdl.Fault;
import javax.wsdl.Input;
import javax.wsdl.Message;
import javax.wsdl.Operation;
import javax.wsdl.Output;
import javax.wsdl.Part;
import javax.wsdl.Port;
import javax.wsdl.Service;
import javax.wsdl.WSDLException;
import javax.wsdl.extensions.AttributeExtensible;
import javax.wsdl.extensions.ElementExtensible;
import javax.wsdl.extensions.ExtensibilityElement;
import javax.wsdl.extensions.ExtensionDeserializer;
import javax.wsdl.extensions.ExtensionRegistry;
import javax.wsdl.extensions.UnknownExtensibilityElement;
import javax.wsdl.extensions.mime.MIMEContent;
import javax.wsdl.extensions.mime.MIMEMultipartRelated;
import javax.wsdl.extensions.mime.MIMEPart;
import javax.wsdl.extensions.soap.SOAPAddress;
import javax.wsdl.extensions.soap.SOAPBinding;
import javax.wsdl.extensions.soap.SOAPBody;
import javax.wsdl.extensions.soap.SOAPFault;
import javax.wsdl.extensions.soap.SOAPHeader;
import javax.wsdl.extensions.soap.SOAPOperation;
import javax.wsdl.extensions.soap12.SOAP12Address;
import javax.wsdl.extensions.soap12.SOAP12Binding;
import javax.wsdl.extensions.soap12.SOAP12Body;
import javax.wsdl.extensions.soap12.SOAP12Fault;
import javax.wsdl.extensions.soap12.SOAP12Header;
import javax.wsdl.extensions.soap12.SOAP12Operation;
import javax.wsdl.factory.WSDLFactory;
import javax.wsdl.xml.WSDLReader;
import javax.xml.XMLConstants;
import javax.xml.namespace.QName;

import org.apache.log4j.Logger;
import org.apache.xmlbeans.SchemaGlobalElement;
import org.apache.xmlbeans.SchemaType;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlString;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import com.eviware.soapui.SoapUI;
import com.eviware.soapui.config.AnonymousTypeConfig;
import com.eviware.soapui.config.DefinitionCacheConfig;
import com.eviware.soapui.config.DefinitionCacheTypeConfig;
import com.eviware.soapui.config.DefintionPartConfig;
import com.eviware.soapui.config.WsaVersionTypeConfig;
import com.eviware.soapui.impl.support.definition.DefinitionLoader;
import com.eviware.soapui.impl.wsdl.WsdlInterface;
import com.eviware.soapui.impl.wsdl.WsdlOperation;
import com.eviware.soapui.impl.wsdl.WsdlRequest;
import com.eviware.soapui.impl.wsdl.submit.WsdlMessageExchange;
import com.eviware.soapui.impl.wsdl.support.Constants;
import com.eviware.soapui.impl.wsdl.support.policy.PolicyUtils;
import com.eviware.soapui.impl.wsdl.support.soap.SoapUtils;
import com.eviware.soapui.impl.wsdl.support.soap.SoapVersion;
import com.eviware.soapui.impl.wsdl.support.wsa.WsaConfig;
import com.eviware.soapui.impl.wsdl.support.wsa.WsaUtils;
import com.eviware.soapui.impl.wsdl.support.xsd.SchemaUtils;
import com.eviware.soapui.settings.WsaSettings;
import com.eviware.soapui.support.StringUtils;
import com.eviware.soapui.support.types.StringList;
import com.eviware.soapui.support.xml.XmlUtils;
import com.ibm.wsdl.util.xml.QNameUtils;
import com.ibm.wsdl.xml.WSDLReaderImpl;
import com.ibm.wsdl.xml.WSDLWriterImpl;

/**
 * Wsdl-related tools
 * 
 * @author Ole.Matzura
 */

public class WsdlUtils
{
	private final static Logger log = Logger.getLogger( WsdlUtils.class );
	private static WSDLReader wsdlReader;
	private final static String WSDL_NAMESPACE = "http://schemas.xmlsoap.org/wsdl/";

	public static <T extends ExtensibilityElement> T getExtensiblityElement( List<?> list, Class<T> clazz )
	{
		List<T> elements = getExtensiblityElements( list, clazz );
		return elements.isEmpty() ? null : elements.get( 0 );
	}

	public static <T extends ExtensibilityElement> List<T> getExtensiblityElements( List list, Class<T> clazz )
	{
		List<T> result = new ArrayList<T>();

		for( Iterator<T> i = list.iterator(); i.hasNext(); )
		{
			T elm = i.next();
			if( clazz.isAssignableFrom( elm.getClass() ) )
			{
				result.add( elm );
			}
		}

		return result;
	}

	public static Element[] getExentsibilityElements( ElementExtensible item, QName qname )
	{
		if( item == null )
			return new Element[0];

		List<Element> result = new ArrayList<Element>();

		List<?> list = item.getExtensibilityElements();
		for( Iterator<?> i = list.iterator(); i.hasNext(); )
		{
			ExtensibilityElement elm = ( ExtensibilityElement )i.next();
			if( elm.getElementType().equals( qname ) && elm instanceof UnknownExtensibilityElement )
			{
				result.add( ( ( UnknownExtensibilityElement )elm ).getElement() );
			}
		}

		return result.toArray( new Element[result.size()] );
	}

	public static String[] getExentsibilityAttributes( AttributeExtensible item, QName qname )
	{
		if( item == null )
			return new String[0];

		StringList result = new StringList();

		Map map = item.getExtensionAttributes();

		for( Iterator<?> i = map.keySet().iterator(); i.hasNext(); )
		{
			QName name = ( QName )i.next();
			if( name.equals( qname ) )
			{
				result.add( map.get( name ).toString() );
			}
		}

		return result.toStringArray();
	}

	public static String getSoapAction( BindingOperation operation )
	{
		List list = operation.getExtensibilityElements();
		SOAPOperation soapOperation = WsdlUtils.getExtensiblityElement( list, SOAPOperation.class );
		if( soapOperation != null )
			return soapOperation.getSoapActionURI();

		SOAP12Operation soap12Operation = WsdlUtils.getExtensiblityElement( list, SOAP12Operation.class );
		if( soap12Operation != null )
			return soap12Operation.getSoapActionURI();

		return null;
	}

	public static String[] getEndpointsForBinding( Definition definition, Binding binding )
	{
		List<String> result = new ArrayList<String>();
		Map map = definition.getAllServices();
		for( Iterator i = map.values().iterator(); i.hasNext(); )
		{
			Service service = ( Service )i.next();
			Map portMap = service.getPorts();
			for( Iterator i2 = portMap.values().iterator(); i2.hasNext(); )
			{
				Port port = ( Port )i2.next();
				if( port.getBinding() == binding )
				{
					String endpoint = WsdlUtils.getSoapEndpoint( port );
					if( endpoint != null )
						result.add( endpoint );
				}
			}
		}

		return result.toArray( new String[result.size()] );
	}

	public static Binding findBindingForOperation( Definition definition, BindingOperation bindingOperation )
	{
		Map services = definition.getAllServices();
		Iterator<Service> s = services.values().iterator();

		while( s.hasNext() )
		{
			Map ports = s.next().getPorts();
			Iterator<Port> p = ports.values().iterator();
			while( p.hasNext() )
			{
				Binding binding = p.next().getBinding();
				List bindingOperations = binding.getBindingOperations();
				for( Iterator iter = bindingOperations.iterator(); iter.hasNext(); )
				{
					BindingOperation op = ( BindingOperation )iter.next();
					if( op.getName().equals( bindingOperation.getName() ) )
						return binding;
				}
			}
		}

		Map bindings = definition.getAllBindings();
		Iterator<QName> names = bindings.keySet().iterator();
		while( names.hasNext() )
		{
			Binding binding = definition.getBinding( names.next() );
			List bindingOperations = binding.getBindingOperations();
			for( Iterator iter = bindingOperations.iterator(); iter.hasNext(); )
			{
				BindingOperation op = ( BindingOperation )iter.next();
				if( op.getName().equals( bindingOperation.getName() ) )
					return binding;
			}
		}

		return null;
	}

	public static BindingOperation findBindingOperation( Definition definition, String operationName )
	{
		Map services = definition.getAllServices();
		for( Iterator i = services.keySet().iterator(); i.hasNext(); )
		{
			QName qName = ( QName )i.next();
			Service service = definition.getService( qName );
			Map ports = service.getPorts();

			for( Iterator iterator = ports.keySet().iterator(); iterator.hasNext(); )
			{
				String key = ( String )iterator.next();
				Port port = service.getPort( key );
				BindingOperation bindingOperation = port.getBinding().getBindingOperation( operationName, null, null );
				if( bindingOperation != null )
					return bindingOperation;
			}
		}

		Map bindings = definition.getAllBindings();
		for( Iterator i = bindings.keySet().iterator(); i.hasNext(); )
		{
			Object key = i.next();
			Binding binding = ( Binding )bindings.get( key );
			BindingOperation bindingOperation = binding.getBindingOperation( operationName, null, null );
			if( bindingOperation != null )
				return bindingOperation;
		}

		return null;
	}

	public static boolean isInputSoapEncoded( BindingOperation bindingOperation )
	{
		if( bindingOperation == null )
			return false;

		BindingInput bindingInput = bindingOperation.getBindingInput();
		if( bindingInput == null )
			return false;

		SOAPBody soapBody = WsdlUtils.getExtensiblityElement( bindingInput.getExtensibilityElements(), SOAPBody.class );

		if( soapBody != null )
		{
			return soapBody.getUse() != null
					&& soapBody.getUse().equalsIgnoreCase( "encoded" )
					&& ( soapBody.getEncodingStyles() == null || soapBody.getEncodingStyles().contains(
							"http://schemas.xmlsoap.org/soap/encoding/" ) );
		}

		SOAP12Body soap12Body = WsdlUtils.getExtensiblityElement( bindingInput.getExtensibilityElements(),
				SOAP12Body.class );

		if( soap12Body != null )
		{
			return soap12Body.getUse() != null
					&& soap12Body.getUse().equalsIgnoreCase( "encoded" )
					&& ( soap12Body.getEncodingStyle() == null || soap12Body.getEncodingStyle().equals(
							"http://schemas.xmlsoap.org/soap/encoding/" ) );
		}

		return false;
	}

	public static boolean isOutputSoapEncoded( BindingOperation bindingOperation )
	{
		if( bindingOperation == null )
			return false;

		BindingOutput bindingOutput = bindingOperation.getBindingOutput();
		if( bindingOutput == null )
			return false;

		SOAPBody soapBody = WsdlUtils.getExtensiblityElement( bindingOutput.getExtensibilityElements(), SOAPBody.class );

		if( soapBody != null )
		{
			return soapBody.getUse() != null
					&& soapBody.getUse().equalsIgnoreCase( "encoded" )
					&& ( soapBody.getEncodingStyles() == null || soapBody.getEncodingStyles().contains(
							"http://schemas.xmlsoap.org/soap/encoding/" ) );
		}

		SOAP12Body soap12Body = WsdlUtils.getExtensiblityElement( bindingOutput.getExtensibilityElements(),
				SOAP12Body.class );

		if( soap12Body != null )
		{
			return soap12Body.getUse() != null
					&& soap12Body.getUse().equalsIgnoreCase( "encoded" )
					&& ( soap12Body.getEncodingStyle() == null || soap12Body.getEncodingStyle().equals(
							"http://schemas.xmlsoap.org/soap/encoding/" ) );
		}

		return false;
	}

	public static boolean isRpc( Definition definition, BindingOperation bindingOperation )
	{
		SOAPOperation soapOperation = WsdlUtils.getExtensiblityElement( bindingOperation.getExtensibilityElements(),
				SOAPOperation.class );

		if( soapOperation != null && soapOperation.getStyle() != null )
			return soapOperation.getStyle().equalsIgnoreCase( "rpc" );

		SOAP12Operation soap12Operation = WsdlUtils.getExtensiblityElement( bindingOperation.getExtensibilityElements(),
				SOAP12Operation.class );

		if( soap12Operation != null && soap12Operation.getStyle() != null )
			return soap12Operation.getStyle().equalsIgnoreCase( "rpc" );

		Binding binding = findBindingForOperation( definition, bindingOperation );
		if( binding == null )
		{
			log.error( "Failed to find binding for operation [" + bindingOperation.getName() + "] in definition ["
					+ definition.getDocumentBaseURI() + "]" );
			return false;
		}

		return isRpc( binding );
	}

	public static boolean isRpc( Binding binding )
	{
		SOAPBinding soapBinding = WsdlUtils
				.getExtensiblityElement( binding.getExtensibilityElements(), SOAPBinding.class );

		if( soapBinding != null )
			return "rpc".equalsIgnoreCase( soapBinding.getStyle() );

		SOAP12Binding soap12Binding = WsdlUtils.getExtensiblityElement( binding.getExtensibilityElements(),
				SOAP12Binding.class );

		if( soap12Binding != null )
			return "rpc".equalsIgnoreCase( soap12Binding.getStyle() );

		return false;
	}

	/**
	 * Returns a list of parts for the specifed operation, either as specified in
	 * body or all
	 */

	public static Part[] getInputParts( BindingOperation operation )
	{
		List<Part> result = new ArrayList<Part>();
		Input input = operation.getOperation().getInput();
		if( input == null || operation.getBindingInput() == null )
			return new Part[0];

		Message msg = input.getMessage();

		if( msg != null )
		{
			SOAPBody soapBody = WsdlUtils.getExtensiblityElement( operation.getBindingInput().getExtensibilityElements(),
					SOAPBody.class );

			if( soapBody == null || soapBody.getParts() == null )
			{
				SOAP12Body soap12Body = WsdlUtils.getExtensiblityElement( operation.getBindingInput()
						.getExtensibilityElements(), SOAP12Body.class );

				if( soap12Body == null || soap12Body.getParts() == null )
				{
					if( msg != null )
						result.addAll( msg.getOrderedParts( null ) );
				}
				else
				{
					Iterator i = soap12Body.getParts().iterator();
					while( i.hasNext() )
					{
						String partName = ( String )i.next();
						Part part = msg.getPart( partName );

						result.add( part );
					}
				}
			}
			else
			{
				Iterator i = soapBody.getParts().iterator();
				while( i.hasNext() )
				{
					String partName = ( String )i.next();
					Part part = msg.getPart( partName );

					result.add( part );
				}
			}
		}
		else
		{
		}

		return result.toArray( new Part[result.size()] );
	}

	public static boolean isAttachmentInputPart( Part part, BindingOperation operation )
	{
		return getInputMultipartContent( part, operation ).length > 0;
	}

	public static boolean isAttachmentOutputPart( Part part, BindingOperation operation )
	{
		return getOutputMultipartContent( part, operation ).length > 0;
	}

	public static MIMEContent[] getOutputMultipartContent( Part part, BindingOperation operation )
	{
		BindingOutput output = operation.getBindingOutput();
		if( output == null )
			return new MIMEContent[0];

		MIMEMultipartRelated multipartOutput = WsdlUtils.getExtensiblityElement( output.getExtensibilityElements(),
				MIMEMultipartRelated.class );

		return getContentParts( part, multipartOutput );
	}

	public static MIMEContent[] getInputMultipartContent( Part part, BindingOperation operation )
	{
		BindingInput bindingInput = operation.getBindingInput();
		if( bindingInput == null )
			return new MIMEContent[0];

		MIMEMultipartRelated multipartInput = WsdlUtils.getExtensiblityElement( bindingInput.getExtensibilityElements(),
				MIMEMultipartRelated.class );

		return getContentParts( part, multipartInput );
	}

	public static MIMEContent[] getContentParts( Part part, MIMEMultipartRelated multipart )
	{
		List<MIMEContent> result = new ArrayList<MIMEContent>();

		if( multipart != null )
		{
			List<MIMEPart> parts = multipart.getMIMEParts();

			for( int c = 0; c < parts.size(); c++ )
			{
				List<MIMEContent> contentParts = WsdlUtils.getExtensiblityElements( parts.get( c )
						.getExtensibilityElements(), MIMEContent.class );

				for( MIMEContent content : contentParts )
				{
					if( content.getPart().equals( part.getName() ) )
						result.add( content );
				}
			}
		}

		return result.toArray( new MIMEContent[result.size()] );
	}

	public static Part[] getFaultParts( BindingOperation bindingOperation, String faultName ) throws Exception
	{
		List<Part> result = new ArrayList<Part>();

		BindingFault bindingFault = bindingOperation.getBindingFault( faultName );
		SOAPFault soapFault = WsdlUtils.getExtensiblityElement( bindingFault.getExtensibilityElements(), SOAPFault.class );

		Operation operation = bindingOperation.getOperation();
		if( soapFault != null && soapFault.getName() != null )
		{
			Fault fault = operation.getFault( soapFault.getName() );
			if( fault == null )
				throw new Exception( "Missing Fault [" + soapFault.getName() + "] in operation [" + operation.getName()
						+ "]" );
			result.addAll( fault.getMessage().getOrderedParts( null ) );
		}
		else
		{
			SOAP12Fault soap12Fault = WsdlUtils.getExtensiblityElement( bindingFault.getExtensibilityElements(),
					SOAP12Fault.class );

			if( soap12Fault != null && soap12Fault.getName() != null )
			{
				Fault fault = operation.getFault( soap12Fault.getName() );
				if( fault != null && fault.getMessage() != null )
					result.addAll( fault.getMessage().getOrderedParts( null ) );
			}
			else
			{
				Fault fault = operation.getFault( faultName );
				if( fault != null && fault.getMessage() != null )
					result.addAll( fault.getMessage().getOrderedParts( null ) );
			}
		}

		return result.toArray( new Part[result.size()] );
	}

	public static String findSoapFaultPartName( SoapVersion soapVersion, BindingOperation bindingOperation,
			String message ) throws Exception
	{
		if( WsdlUtils.isOutputSoapEncoded( bindingOperation ) )
			throw new Exception( "SOAP-Encoded messages not supported" );

		// XmlObject xml = XmlObject.Factory.parse( message );
		XmlObject xml = XmlUtils.createXmlObject( message );
		XmlObject[] msgPaths = xml.selectPath( "declare namespace env='" + soapVersion.getEnvelopeNamespace() + "';"
				+ "$this/env:Envelope/env:Body/env:Fault" );
		if( msgPaths.length == 0 )
			return null;

		XmlObject msgXml = msgPaths[0];

		Map faults = bindingOperation.getBindingFaults();
		for( Iterator<BindingFault> i = faults.values().iterator(); i.hasNext(); )
		{
			BindingFault bindingFault = i.next();
			String faultName = bindingFault.getName();
			Part[] faultParts = WsdlUtils.getFaultParts( bindingOperation, faultName );
			Part part = faultParts[0];

			QName elementName = part.getElementName();
			if( elementName != null )
			{
				XmlObject[] faultPaths = msgXml.selectPath( "declare namespace env='" + soapVersion.getEnvelopeNamespace()
						+ "';" + "declare namespace ns='" + elementName.getNamespaceURI() + "';" + "//env:Fault/detail/ns:"
						+ elementName.getLocalPart() );

				if( faultPaths.length == 1 )
					return faultName;
			}
			// this is not allowed by Basic Profile.. remove?
			else if( part.getTypeName() != null )
			{
				QName typeName = part.getTypeName();
				XmlObject[] faultPaths = msgXml.selectPath( "declare namespace env='" + soapVersion.getEnvelopeNamespace()
						+ "';" + "declare namespace ns='" + typeName.getNamespaceURI() + "';" + "//env:Fault/detail/ns:"
						+ part.getName() );

				if( faultPaths.length == 1 )
					return faultName;
			}
		}

		return null;
	}

	public static Part[] getOutputParts( BindingOperation operation )
	{
		BindingOutput bindingOutput = operation.getBindingOutput();
		if( bindingOutput == null )
			return new Part[0];

		List<Part> result = new ArrayList<Part>();
		Output output = operation.getOperation().getOutput();
		if( output == null )
			return new Part[0];

		Message msg = output.getMessage();
		if( msg != null )
		{
			SOAPBody soapBody = WsdlUtils
					.getExtensiblityElement( bindingOutput.getExtensibilityElements(), SOAPBody.class );

			if( soapBody == null || soapBody.getParts() == null )
			{
				SOAP12Body soap12Body = WsdlUtils.getExtensiblityElement( bindingOutput.getExtensibilityElements(),
						SOAP12Body.class );

				if( soap12Body == null || soap12Body.getParts() == null )
				{
					result.addAll( msg.getOrderedParts( null ) );
				}
				else
				{
					Iterator i = soap12Body.getParts().iterator();
					while( i.hasNext() )
					{
						String partName = ( String )i.next();
						Part part = msg.getPart( partName );

						result.add( part );
					}
				}
			}
			else
			{
				Iterator i = soapBody.getParts().iterator();
				while( i.hasNext() )
				{
					String partName = ( String )i.next();
					Part part = msg.getPart( partName );

					result.add( part );
				}
			}
		}
		else
		{
			log.warn( "Missing output message for binding operation [" + operation.getName() + "]" );
		}

		return result.toArray( new Part[result.size()] );
	}

	public static boolean isMultipartRequest( Definition definition, BindingOperation bindingOperation )
	{
		return WsdlUtils.getExtensiblityElement( bindingOperation.getBindingInput().getExtensibilityElements(),
				MIMEMultipartRelated.class ) != null;
	}

	public static String getUsingAddressing( ElementExtensible item )
	{
		String version = WsaVersionTypeConfig.NONE.toString();

		Element[] usingAddressingElements = WsdlUtils.getExentsibilityElements( item, new QName(
				"http://www.w3.org/2006/05/addressing/wsdl", "UsingAddressing" ) );
		if( usingAddressingElements.length == 0 )
		{
			usingAddressingElements = WsdlUtils.getExentsibilityElements( item, new QName(
					"http://www.w3.org/2006/02/addressing/wsdl", "UsingAddressing" ) );
		}

		if( usingAddressingElements.length != 0 )
		{
			// this should resolve wsdl version, not addressing version??? what is
			// the connection?
			String addressingVersion = usingAddressingElements[0].getAttributeNS( WSDL_NAMESPACE, "required" );
			if( addressingVersion != null && addressingVersion.equals( "true" ) )
			{
				version = WsaVersionTypeConfig.X_200508.toString();
			}

		}
		return version;
	}

	private static String checkIfWsaPolicy( String version, Element policy )
	{
		// Policy builtPolicy = new
		// WsaPolicy().buildPolicy(policy.getTextContent());
		// policy = WsaPolicy.normalize(policy);

		// check if found reference is addressing policy
		// Element wsAddressing = XmlUtils.getFirstChildElementNS(policy,
		// WsaUtils.WSAM_NAMESPACE, "Addressing");
		// Element addressingPolicy = null;
		// if (wsAddressing != null)
		// {
		// String optional =
		// wsAddressing.getAttributeNS(WsaPolicy.WS_POLICY_NAMESPACE, "Optional");
		// addressingPolicy = XmlUtils.getFirstChildElementNS(wsAddressing,
		// WsaPolicy.WS_POLICY_NAMESPACE, "Policy");
		// if (addressingPolicy != null)
		// {
		// if (StringUtils.isNullOrEmpty(optional) || optional.equals("false") ||
		// (optional.equals("true") &&
		// SoapUI.getSettings().getBoolean(WsaSettings.ENABLE_FOR_OPTIONAL)) )
		// {
		// version = WsaVersionTypeConfig.X_200508.toString();
		// }
		// //check if policy has Anonymous
		// Element anonymousElm =
		// XmlUtils.getFirstChildElementNS(addressingPolicy, new
		// QName(WsaPolicy.WSAM_NAMESPACE,"AnonymousResponses"));
		// if (anonymousElm != null)
		// {
		// //TODO anonymous = required
		// } else {
		// Element nonAnonymousElement =
		// XmlUtils.getFirstChildElementNS(addressingPolicy, new
		// QName(WsaPolicy.WSAM_NAMESPACE,"NonAnonymousResponses"));
		// if (nonAnonymousElement != null)
		// {
		// //TODO anonymous = prohibited
		// }
		// }
		// }
		// }
		return version;
	}

	public static String getWsaPolicyAnonymous( Element policy )
	{
		String version = WsaVersionTypeConfig.NONE.toString();
		String anonymous = AnonymousTypeConfig.OPTIONAL.toString();
		// check if found reference is addressing policy
		Element wsAddressing = XmlUtils.getFirstChildElementNS( policy, WsaUtils.WS_A_NAMESPACE_200705, "Addressing" );
		Element addressingPolicy = null;
		if( wsAddressing != null )
		{
			String optional = wsAddressing.getAttributeNS( PolicyUtils.WS_W3_POLICY_NAMESPACE, "Optional" );
			addressingPolicy = XmlUtils
					.getFirstChildElementNS( wsAddressing, PolicyUtils.WS_W3_POLICY_NAMESPACE, "Policy" );
			if( addressingPolicy != null )
			{
				if( StringUtils.isNullOrEmpty( optional ) || optional.equals( "false" )
						|| ( optional.equals( "true" ) && SoapUI.getSettings().getBoolean( WsaSettings.ENABLE_FOR_OPTIONAL ) ) )
				{
					version = WsaVersionTypeConfig.X_200508.toString();
				}
				// check if policy has Anonymous
				Element anonymousElm = XmlUtils.getFirstChildElementNS( addressingPolicy, new QName(
						WsaUtils.WS_A_NAMESPACE_200705, "AnonymousResponses" ) );
				if( anonymousElm != null )
				{
					anonymous = AnonymousTypeConfig.REQUIRED.toString();
				}
				else
				{
					Element nonAnonymousElement = XmlUtils.getFirstChildElementNS( addressingPolicy, new QName(
							WsaUtils.WS_A_NAMESPACE_200705, "NonAnonymousResponses" ) );
					if( nonAnonymousElement != null )
					{
						anonymous = AnonymousTypeConfig.PROHIBITED.toString();
					}
				}
			}
		}
		return anonymous;
	}

	public static String getSoapEndpoint( Port port )
	{
		SOAPAddress soapAddress = WsdlUtils.getExtensiblityElement( port.getExtensibilityElements(), SOAPAddress.class );
		if( soapAddress != null && StringUtils.hasContent( soapAddress.getLocationURI() ) )
		{
			try
			{
				return URLDecoder.decode( soapAddress.getLocationURI(), "UTF-8" );
			}
			catch( UnsupportedEncodingException e )
			{
				e.printStackTrace();
				return soapAddress.getLocationURI();
			}
		}

		SOAP12Address soap12Address = WsdlUtils.getExtensiblityElement( port.getExtensibilityElements(),
				SOAP12Address.class );
		if( soap12Address != null && StringUtils.hasContent( soap12Address.getLocationURI() ) )
		{
			try
			{
				return URLDecoder.decode( soap12Address.getLocationURI(), "UTF-8" );
			}
			catch( UnsupportedEncodingException e )
			{
				e.printStackTrace();
				return soap12Address.getLocationURI();
			}
		}

		return null;
	}

	public static boolean replaceSoapEndpoint( Port port, String endpoint )
	{
		SOAPAddress soapAddress = WsdlUtils.getExtensiblityElement( port.getExtensibilityElements(), SOAPAddress.class );
		if( soapAddress != null )
		{
			soapAddress.setLocationURI( endpoint );
			return true;
		}

		SOAP12Address soap12Address = WsdlUtils.getExtensiblityElement( port.getExtensibilityElements(),
				SOAP12Address.class );
		if( soap12Address != null )
		{
			soap12Address.setLocationURI( endpoint );
			return true;
		}

		return false;
	}

	public static String getSoapBodyNamespace( List<?> list )
	{
		SOAPBody soapBody = WsdlUtils.getExtensiblityElement( list, SOAPBody.class );
		if( soapBody != null )
			return soapBody.getNamespaceURI();

		SOAP12Body soap12Body = WsdlUtils.getExtensiblityElement( list, SOAP12Body.class );
		if( soap12Body != null )
			return soap12Body.getNamespaceURI();

		return null;
	}

	public static final class NonSchemaImportingWsdlReaderImpl extends WSDLReaderImpl
	{
		@SuppressWarnings( "unchecked" )
		@Override
		protected ExtensibilityElement parseSchema( Class parentType, Element el, Definition def, ExtensionRegistry extReg )
				throws WSDLException
		{
			QName elementType = QNameUtils.newQName( el );

			ExtensionDeserializer exDS = extReg.queryDeserializer( parentType, elementType );

			// Now unmarshall the DOM element.
			ExtensibilityElement ee = exDS.unmarshall( parentType, elementType, el, def, extReg );

			return ee;
		}

	}

	/**
	 * A SOAP-Header wrapper
	 * 
	 * @author ole.matzura
	 */

	public interface SoapHeader
	{
		public QName getMessage();

		public String getPart();
	}

	/**
	 * SOAP 1.1 Header implementation
	 * 
	 * @author ole.matzura
	 */

	public static class Soap11Header implements SoapHeader
	{
		private final SOAPHeader soapHeader;

		public Soap11Header( SOAPHeader soapHeader )
		{
			this.soapHeader = soapHeader;
		}

		public QName getMessage()
		{
			return soapHeader.getMessage();
		}

		public String getPart()
		{
			return soapHeader.getPart();
		}
	}

	/**
	 * SOAP 1.2 Header implementation
	 * 
	 * @author ole.matzura
	 */

	public static class Soap12Header implements SoapHeader
	{
		private final SOAP12Header soapHeader;

		public Soap12Header( SOAP12Header soapHeader )
		{
			this.soapHeader = soapHeader;
		}

		public QName getMessage()
		{
			return soapHeader.getMessage();
		}

		public String getPart()
		{
			return soapHeader.getPart();
		}
	}

	public static List<SoapHeader> getSoapHeaders( List list )
	{
		List<SoapHeader> result = new ArrayList<SoapHeader>();

		List<SOAPHeader> soapHeaders = WsdlUtils.getExtensiblityElements( list, SOAPHeader.class );
		if( soapHeaders != null && !soapHeaders.isEmpty() )
		{
			for( SOAPHeader header : soapHeaders )
				result.add( new Soap11Header( header ) );
		}
		else
		{
			List<SOAP12Header> soap12Headers = WsdlUtils.getExtensiblityElements( list, SOAP12Header.class );
			if( soap12Headers != null && !soap12Headers.isEmpty() )
			{
				for( SOAP12Header header : soap12Headers )
					result.add( new Soap12Header( header ) );
			}
		}

		return result;
	}

	public static synchronized Definition readDefinition( String wsdlUrl ) throws Exception
	{
		if( wsdlReader == null )
		{
			WSDLFactory factory = WSDLFactory.newInstance();
			wsdlReader = factory.newWSDLReader();
			wsdlReader.setFeature( "javax.wsdl.verbose", true );
			wsdlReader.setFeature( "javax.wsdl.importDocuments", true );
		}

		return wsdlReader.readWSDL( new UrlWsdlLoader( wsdlUrl ) );
	}

	public static SchemaType getSchemaTypeForPart( WsdlContext wsdlContext, javax.wsdl.Part part ) throws Exception
	{
		SchemaType schemaType = null;
		QName elementName = part.getElementName();

		if( elementName != null )
		{
			SchemaGlobalElement elm = wsdlContext.getSchemaTypeLoader().findElement( elementName );
			if( elm != null )
			{
				schemaType = elm.getType();
			}
			else
				WsdlRequest.log.error( "Could not find element [" + elementName + "] specified in part [" + part.getName()
						+ "]" );
		}
		else
		{
			QName typeName = part.getTypeName();

			if( typeName != null )
			{
				schemaType = wsdlContext.getSchemaTypeLoader().findType( typeName );

				if( schemaType == null )
				{
					WsdlRequest.log.error( "Could not find type [" + typeName + "] specified in part [" + part.getName()
							+ "]" );
				}
			}
		}
		return schemaType;
	}

	public static SchemaGlobalElement getSchemaElementForPart( WsdlContext wsdlContext, javax.wsdl.Part part )
			throws Exception
	{
		QName elementName = part.getElementName();

		if( elementName != null )
		{
			return wsdlContext.getSchemaTypeLoader().findElement( elementName );
		}

		return null;
	}

	public static String replacePortEndpoint( WsdlInterface iface, InputSource inputSource, String endpoint )
			throws WSDLException
	{
		WSDLReader wsdlReader = new NonSchemaImportingWsdlReaderImpl();

		wsdlReader.setFeature( "javax.wsdl.verbose", true );
		wsdlReader.setFeature( "javax.wsdl.importDocuments", false );

		Definition definition = wsdlReader.readWSDL( null, inputSource );

		boolean updated = false;
		Map map = definition.getServices();
		for( Iterator i = map.values().iterator(); i.hasNext(); )
		{
			Service service = ( Service )i.next();
			Map portMap = service.getPorts();
			for( Iterator i2 = portMap.values().iterator(); i2.hasNext(); )
			{
				Port port = ( Port )i2.next();
				if( port.getBinding().getQName().equals( iface.getBindingName() ) )
				{
					if( replaceSoapEndpoint( port, endpoint ) )
						updated = true;
				}
			}
		}

		if( updated )
		{
			StringWriter writer = new StringWriter();

			Map nsMap = definition.getNamespaces();
			if( !nsMap.values().contains( Constants.SOAP_HTTP_BINDING_NS ) )
				definition.addNamespace( "soaphttp", Constants.SOAP_HTTP_BINDING_NS );

			new WSDLWriterImpl().writeWSDL( definition, writer );
			return writer.toString();
		}

		return null;
	}

	public static BindingOperation findBindingOperation( Binding binding, String bindingOperationName, String inputName,
			String outputName )
	{
		if( binding == null )
			return null;

		if( inputName == null )
			inputName = ":none";

		if( outputName == null )
			outputName = ":none";

		BindingOperation result = binding.getBindingOperation( bindingOperationName, inputName, outputName );

		if( result == null && ( inputName.equals( ":none" ) || outputName.equals( ":none" ) ) )
		{
			// fall back to this behaviour for WSDL4j 1.5.0 compatibility
			result = binding.getBindingOperation( bindingOperationName, inputName.equals( ":none" ) ? null : inputName,
					outputName.equals( ":none" ) ? null : outputName );
		}
		return result;
	}

	public static boolean isHeaderInputPart( Part part, Message message, BindingOperation bindingOperation )
	{
		List<SOAPHeader> headers = WsdlUtils.getExtensiblityElements( bindingOperation.getBindingInput()
				.getExtensibilityElements(), SOAPHeader.class );

		if( headers == null || headers.isEmpty() )
			return false;

		for( SOAPHeader header : headers )
		{
			if( message.getQName().equals( header.getMessage() ) && part.getName().equals( header.getPart() ) )
				return true;
		}

		return false;
	}

	public static boolean isHeaderOutputPart( Part part, Message message, BindingOperation bindingOperation )
	{
		BindingOutput bindingOutput = bindingOperation.getBindingOutput();
		List<SOAPHeader> headers = bindingOutput == null ? null : WsdlUtils.getExtensiblityElements(
				bindingOutput.getExtensibilityElements(), SOAPHeader.class );

		if( headers == null || headers.isEmpty() )
			return false;

		for( SOAPHeader header : headers )
		{
			if( message.getQName().equals( header.getMessage() ) && part.getName().equals( header.getPart() ) )
				return true;
		}

		return false;
	}

	public static DefinitionCacheConfig cacheWsdl( DefinitionLoader loader ) throws Exception
	{
		DefinitionCacheConfig definitionCache = DefinitionCacheConfig.Factory.newInstance();
		definitionCache.setRootPart( loader.getBaseURI() );
		definitionCache.setType( DefinitionCacheTypeConfig.TEXT );

		Map<String, XmlObject> urls = SchemaUtils.getDefinitionParts( loader );

		for( Iterator<String> i = urls.keySet().iterator(); i.hasNext(); )
		{
			DefintionPartConfig definitionPart = definitionCache.addNewPart();
			String url = i.next();
			definitionPart.setUrl( url );
			XmlObject xmlObject = urls.get( url );
			Node domNode = xmlObject.getDomNode();

			if( domNode.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE )
			{
				Node node = ( ( DocumentFragment )domNode ).getFirstChild();
				if( node.getNodeType() == Node.TEXT_NODE )
				{
					domNode = XmlUtils.parseXml( node.getNodeValue() );
					// xmlObject = XmlObject.Factory.parse( domNode );
					xmlObject = XmlUtils.createXmlObject( domNode );
				}
			}

			Element contentElement = ( ( Document )domNode ).getDocumentElement();

			Node newDomNode = definitionPart.addNewContent().getDomNode();
			newDomNode.appendChild( newDomNode.getOwnerDocument().createTextNode( xmlObject.toString() ) );
			definitionPart.setType( contentElement.getNamespaceURI() );
		}

		return definitionCache;
	}

	public static void getAnonymous( WsdlOperation wsdlOperation )
	{
		String anonymous = "";

		Element[] anonymousElements = WsdlUtils.getExentsibilityElements( wsdlOperation.getBindingOperation(), new QName(
				"http://www.w3.org/2006/05/addressing/wsdl", "Anonymous" ) );
		if( anonymousElements.length == 0 )
		{
			anonymousElements = WsdlUtils.getExentsibilityElements( wsdlOperation.getBindingOperation(), new QName(
					"http://www.w3.org/2006/02/addressing/wsdl", "Anonymous" ) );
		}

		if( anonymousElements != null && anonymousElements.length > 0 )
		{
			anonymous = XmlUtils.getElementText( anonymousElements[0] );
		}
		wsdlOperation.setAnonymous( anonymous );
	}

	public static String getDefaultWsaAction( WsdlOperation operation, boolean output )
	{
		// SOAP 1.1 specific handling
		if( operation.getInterface().getSoapVersion() == SoapVersion.Soap11
				&& StringUtils.hasContent( operation.getAction() )
				&& SoapUI.getSettings().getBoolean( WsaSettings.SOAP_ACTION_OVERRIDES_WSA_ACTION ) )
			return operation.getAction();

		try
		{
			if( operation.getBindingOperation() == null || operation.getBindingOperation().getOperation() == null )
				return null;

			AttributeExtensible attributeExtensible = output ? operation.getBindingOperation().getOperation().getOutput()
					: operation.getBindingOperation().getOperation().getInput();

			if( attributeExtensible == null )
				return null;

			// String[] attrs =
			// WsdlUtils.getExentsibilityAttributes(attributeExtensible, new QName(
			// WsaUtils.WS_A_NAMESPACE_200408, "Action"));
			// if (attrs == null || attrs.length == 0)
			// attrs = WsdlUtils.getExentsibilityAttributes(attributeExtensible,
			// new QName(WsaUtils.WS_A_NAMESPACE_200508,
			// "Action"));
			// if (attrs != null && attrs.length > 0)
			// {
			// return attrs[0];
			// }
			String[] attrs;
			for( String namespace : WsaUtils.wsaNamespaces )
			{
				attrs = WsdlUtils.getExentsibilityAttributes( attributeExtensible, new QName( namespace, "Action" ) );
				if( attrs != null && attrs.length > 0 )
				{
					return attrs[0];
				}
			}

			WsdlInterface iface = operation.getInterface();

			Definition definition = iface.getWsdlContext().getDefinition();
			String targetNamespace = WsdlUtils.getTargetNamespace( definition );
			String portTypeName = iface.getBinding().getPortType().getQName().getLocalPart();
			String operationName = operation.getName();
			if( !StringUtils.isNullOrEmpty( operationName ) )
			{
				Operation op = iface.getBinding().getPortType().getOperation( operationName, null, null );
				if( op != null )
				{
					attributeExtensible = output ? op.getOutput() : op.getInput();
					attrs = WsdlUtils.getExentsibilityAttributes( attributeExtensible, new QName(
							WsaUtils.WS_A_NAMESPACE_200705, "Action" ) );
					if( attrs != null && attrs.length > 0 )
					{
						return attrs[0];
					}
				}
			}
			String operationInOutName = output ? operation.getOutputName() : operation.getInputName();
			if( operationInOutName == null )
				operationInOutName = operation.getName() + ( output ? "Response" : "Request" );

			StringBuffer result = new StringBuffer( targetNamespace );
			if( targetNamespace.length() > 0 && targetNamespace.charAt( targetNamespace.length() - 1 ) != '/'
					&& portTypeName.charAt( 0 ) != '/' )
				result.append( '/' );
			result.append( portTypeName );
			if( portTypeName.charAt( portTypeName.length() - 1 ) != '/' && operationInOutName.charAt( 0 ) != '/' )
				result.append( '/' );
			result.append( operationInOutName );

			return result.toString();
		}
		catch( Exception e )
		{
			e.printStackTrace();
			log.warn( e.toString() );
			return null;
		}
	}

	public static void setDefaultWsaAction( WsaConfig wsaConfig, boolean b )
	{
		String defaultAction = getDefaultWsaAction( wsaConfig.getWsaContainer().getOperation(), b );
		if( StringUtils.hasContent( defaultAction ) )
			wsaConfig.setAction( defaultAction );
	}

	public static String getRequestWsaMessageId( WsdlMessageExchange messageExchange, String wsaVersionNameSpace )
	{
		String requestMessageId = null;
		try
		{
			// XmlObject xmlObject = XmlObject.Factory.parse(
			// messageExchange.getRequestContent() );
			XmlObject xmlObject = XmlUtils.createXmlObject( messageExchange.getRequestContent() );
			SoapVersion soapVersion = messageExchange.getOperation().getInterface().getSoapVersion();

			Element header = ( Element )SoapUtils.getHeaderElement( xmlObject, soapVersion, true ).getDomNode();
			Element msgNode = XmlUtils.getFirstChildElementNS( header, wsaVersionNameSpace, "MessageID" );
			if( msgNode != null )
			{
				requestMessageId = XmlUtils.getElementText( msgNode );
			}
		}
		catch( XmlException e )
		{
			e.printStackTrace();
			log.warn( e.toString() );
			return null;
		}
		return requestMessageId;
	}

	public static NodeList getRequestReplyToRefProps( WsdlMessageExchange messageExchange, String wsaVersionNameSpace )
	{
		try
		{
			// XmlObject xmlObject = XmlObject.Factory.parse(
			// messageExchange.getRequestContent() );
			XmlObject xmlObject = XmlUtils.createXmlObject( messageExchange.getRequestContent() );
			SoapVersion soapVersion = messageExchange.getOperation().getInterface().getSoapVersion();

			Element header = ( Element )SoapUtils.getHeaderElement( xmlObject, soapVersion, true ).getDomNode();
			Element replyToNode = XmlUtils.getFirstChildElementNS( header, wsaVersionNameSpace, "ReplyTo" );
			Element replyRefParamsNode = XmlUtils.getFirstChildElementNS( replyToNode, wsaVersionNameSpace,
					"ReferenceParameters" );
			if( replyRefParamsNode != null )
			{
				return XmlUtils.getChildElements( replyRefParamsNode );
			}
		}
		catch( XmlException e )
		{
			e.printStackTrace();
			log.warn( e.toString() );
		}
		return null;
	}

	public static NodeList getRequestFaultToRefProps( WsdlMessageExchange messageExchange, String wsaVersionNameSpace )
	{
		try
		{
			// XmlObject xmlObject = XmlObject.Factory.parse(
			// messageExchange.getRequestContent() );
			XmlObject xmlObject = XmlUtils.createXmlObject( messageExchange.getRequestContent() );
			SoapVersion soapVersion = messageExchange.getOperation().getInterface().getSoapVersion();

			Element header = ( Element )SoapUtils.getHeaderElement( xmlObject, soapVersion, true ).getDomNode();
			Element faultToNode = XmlUtils.getFirstChildElementNS( header, wsaVersionNameSpace, "FaultTo" );
			Element faultRefParamsNode = XmlUtils.getFirstChildElementNS( faultToNode, wsaVersionNameSpace,
					"ReferenceParameters" );
			if( faultRefParamsNode != null )
			{
				return XmlUtils.getChildElements( faultRefParamsNode );
			}
		}
		catch( XmlException e )
		{
			e.printStackTrace();
			log.warn( e.toString() );
		}
		return null;
	}

	public static String getFaultCode( WsdlMessageExchange messageExchange )
	{
		try
		{
			// XmlObject xmlObject = XmlObject.Factory.parse(
			// messageExchange.getResponseContent() );
			XmlObject xmlObject = XmlUtils.createXmlObject( messageExchange.getResponseContent() );
			SoapVersion soapVersion = messageExchange.getOperation().getInterface().getSoapVersion();

			Element body = ( Element )SoapUtils.getBodyElement( xmlObject, soapVersion ).getDomNode();
			Element soapenvFault = XmlUtils.getFirstChildElementNS( body, "http://schemas.xmlsoap.org/soap/envelope/",
					"Fault" );
			Element faultCode = XmlUtils.getFirstChildElement( soapenvFault, "faultcode" );
			if( faultCode != null )
			{
				return XmlUtils.getElementText( faultCode );
			}
		}
		catch( XmlException e )
		{
			e.printStackTrace();
			log.warn( e.toString() );
		}
		return null;
	}

	public static String getTargetNamespace( Definition definition )
	{
		return definition.getTargetNamespace() == null ? XMLConstants.NULL_NS_URI : definition.getTargetNamespace();
	}

	public static SchemaType generateRpcBodyType( WsdlOperation operation )
	{
		return XmlString.type;
	}
}
