/*=============================================================================*
* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*=============================================================================*/
package org.apache.ws.util.helper;
import org.apache.ws.util.XmlConstants;
import org.apache.ws.util.i18n.Keys;
import org.apache.ws.util.i18n.Messages;
import org.apache.ws.util.i18n.MessagesImpl;
import javax.xml.soap.Name;
import javax.xml.soap.Node;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.Text;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/** LOG-DONE
* @author Ian P. Springer (Hewlett-Packard Company)
*/
public class Saaj2StringConverter
{
public static final Messages MSG = MessagesImpl.getInstance();
/**
* Whitespace string to use for indentation in {@link #toString( javax.xml.soap.Node )}.
*/
private static final String DEFAULT_NODE_INDENT_STRING = " ";
/**
* The string to use for each indent (may only contain spaces or tabs).
*/
private String m_indent_str = DEFAULT_NODE_INDENT_STRING;
/**
* Given a {@link SOAPElement}, returns a list containing the children of that element. If at least one of the
* children is a SOAPElement, any {@link Text} nodes are excluded from the list. This is because it is assumed that
* if the specified element has mixed content, it is only because of insignificant whitespace text nodes.
*
* @param soap_elem a SAAJ {@link SOAPElement}
*
* @return a list containing the children of the specified {@link SOAPElement}
*/
public static List getChildNodes( SOAPElement soap_elem )
{
List elem_children = new ArrayList( );
List text_children = new ArrayList( );
Iterator node_iter = soap_elem.getChildElements( );
while ( node_iter.hasNext( ) )
{
Node child_node = (Node) node_iter.next( );
if ( isSOAPElement( child_node ) )
{
elem_children.add( child_node );
}
else
{
text_children.add( child_node );
}
}
return ( elem_children.isEmpty( ) ? text_children : elem_children );
}
/**
* @param soap_elem
* @param ns_uri
*
* @return
*/
public static String getDeclaredPrefix( SOAPElement soap_elem,
String ns_uri )
{
Iterator prefixes = soap_elem.getNamespacePrefixes( );
while ( prefixes.hasNext( ) )
{
String prefix = (String) prefixes.next( );
if ( soap_elem.getNamespaceURI( prefix ).equals( ns_uri ) )
{
return ( prefix );
}
}
return ( null );
}
/**
* Sets the indent string.
*
* @param indent_str the indent string to use; if null, indenting will be disabled
*/
public void setIndentString( String indent_str )
{
m_indent_str = indent_str;
}
/**
* Gets the indent string.
*
* @return the indent string being used; if null, indenting is disabled
*/
public String getIndentString( )
{
return m_indent_str;
}
/**
* Returns true if the specified SAAJ node is an element, or false otherwise.
*
* @param soap_node a SAAJ node
*
* @return true if the specified SAAJ node is an element, or false otherwise
*/
public static boolean isSOAPElement( Node soap_node )
{
// NOTE: the second condition below is nessary for Axis, because its Text impl also implements SOAPElement
return ( soap_node instanceof SOAPElement && !( soap_node instanceof Text ) );
}
/**
* DOCUMENT_ME
*
* @param soap_text DOCUMENT_ME
*
* @return DOCUMENT_ME
*
* @throws SOAPException DOCUMENT_ME
*/
public static String getValue( Text soap_text )
throws SOAPException
{
if ( soap_text == null )
{
throw new IllegalArgumentException( MSG.getMessage( Keys.NULL_PARAM_NOT_ALLOWED) );
}
String value = soap_text.getValue( );
if ( value == null )
{
value = soap_text.toString( );
}
if ( value == null )
{
throw new SOAPException( MSG.getMessage( Keys.NULL_VALUE) );
}
return ( value );
}
/**
* Returns true if the specified string is non-null and does not equal ""; otherwise returns false.
*
* @param str a string
*
* @return true if the specified string is non-null and does not equal ""; otherwise false
*/
public static boolean hasNonEmptyValue( String str )
{
return ( ( str != null ) && !str.equals( "" ) );
}
/**
* Returns a string representation of the specified {@link javax.xml.soap.Node}.
*
* @param soap_node a SAAJ SOAP node
*
* @return a string represntation of the SOAP node
*
* @throws SOAPException
*/
public String toString( Node soap_node )
throws SOAPException
{
return ( buildSOAPNodeStringBuffer( new StringBuffer( ),
soap_node,
soap_node,
0 ).toString( ) );
}
/**
* @param soap_elem a SOAP element
*
* @return an Iterator of the element's children
*/
private static Iterator getChildren( SOAPElement soap_elem )
{
List children = null;
// NOTE (ips, 08/12/03): the below block is a workaround for a bug in AXIS 1.1...
if ( soap_elem instanceof SOAPEnvelope && false )
{
children = getEnvelopeChildren( soap_elem );
}
if ( ( children == null ) || children.isEmpty( ) )
{
children = getChildNodes( soap_elem );
}
return ( children.iterator( ) );
}
/**
* DOCUMENT_ME
*
* @param current_soap_node DOCUMENT_ME
*
* @return DOCUMENT_ME
*/
private static boolean isElement( Node current_soap_node )
{
return !( current_soap_node instanceof Text );
}
/**
* DOCUMENT_ME
*
* @param soap_elem DOCUMENT_ME
*
* @return DOCUMENT_ME
*/
private static List getEnvelopeChildren( SOAPElement soap_elem )
{
List children;
children = new ArrayList( );
SOAPEnvelope soap_env = (SOAPEnvelope) soap_elem;
SOAPHeader header = null;
SOAPBody body = null;
try
{
header = soap_env.getHeader( );
body = soap_env.getBody( );
}
catch ( SOAPException soape )
{
//LOG.warn( ResourceKeys.MSG.getMsg( ResourceKeys.EX_FAILED_TO_GET_SOAP_BODY, soape ) );
}
if ( ( header != null ) && ( body != null ) )
{
children.add( header );
children.add( body );
}
return children;
}
/**
* DOCUMENT_ME
*
* @param pqname DOCUMENT_ME
* @param root_soap_elem DOCUMENT_ME
*
* @return DOCUMENT_ME
*/
private static String getPrefix( Name pqname,
SOAPElement root_soap_elem )
{
String prefix = pqname.getPrefix( );
if ( ( prefix != null ) && !prefix.equals( "" ) )
{
return ( prefix );
}
return ( recursiveGetPrefix( pqname, root_soap_elem ) );
}
/**
* @param child
* @param ns_uri
*
* @return
*/
private static String getPrefix( SOAPElement child,
String ns_uri )
{
if ( child == null )
{
//throw new IllegalArgumentException( ResourceKeys.MSG.getMsg( ResourceKeys.EX_SOAP_ELEM_NULL ) );
}
String prefix;
if ( Node.TEXT_NODE == child.getNodeType( ) )
{
return null;
}
Name child_name = child.getElementName( );
if ( child_name == null )
{
return null;
}
String child_uri = child_name.getURI( );
if ( child_uri == null )
{
return null;
}
if ( ns_uri == null )
{
return null;
}
if ( child_uri.equals( ns_uri ) )
{
prefix = child.getElementName( ).getPrefix( );
if ( ( prefix != null ) && !prefix.equals( "" ) )
{
return ( prefix );
}
}
prefix = getDeclaredPrefix( child, ns_uri );
if ( ( prefix != null ) && !prefix.equals( "" ) )
{
return ( prefix );
}
return ( null );
}
/**
* Constructs a string representation of the specified SAAJ {@link Name}.
*
* @param pqname a prefixed qname
*
* @return a string representation of the prefixed qname
*/
private static String nameToString( Name pqname )
{
if ( pqname == null )
{
//LOG.debug( ResourceKeys.MSG.getMsg( ResourceKeys.EX_PASSED_IN_NAME_OBJ_NULL ) );
return ( "" );
}
String prefix = pqname.getPrefix( );
String local_name = pqname.getLocalName( );
if ( ( local_name == null ) || local_name.equals( "" ) )
{
//LOG.debug( ResourceKeys.MSG.getMsg( ResourceKeys.EX_PASSED_IN_NAME_OBJ_NULL_LOCAL ) );
if ( ( ( prefix != null ) && !prefix.equals( "" ) ) )
{
// NOTE (ips): This is a workaround for a bug in AXIS 1.1's MessageElement.getNamespacePrefixes()
return ( prefix );
}
return ( "" );
}
StringBuffer buf = new StringBuffer( );
if ( ( prefix != null ) && !prefix.equals( "" ) )
{
buf.append( prefix );
buf.append( ":" );
}
buf.append( local_name );
return ( buf.toString( ) );
}
/**
* DOCUMENT_ME
*
* @param buf DOCUMENT_ME
* @param soap_elem DOCUMENT_ME
* @param depth DOCUMENT_ME
*/
private void appendAttributes( StringBuffer buf,
SOAPElement soap_elem,
int depth )
{
appendXmlnsAttributeIfNoPrefix( buf, soap_elem, depth );
Iterator iter = soap_elem.getAllAttributes( );
while ( iter.hasNext( ) )
{
Name attrib_pqname = (Name) iter.next( );
// skip all xmlns:foo attributes (e.g. xmlns:xs="urn:schema")
if ( ( attrib_pqname.getPrefix( ) != null )
&& attrib_pqname.getPrefix( ).equals( XmlConstants.NSPREFIX_XMLNS ) )
{
continue;
}
// skip the xmlns attribute (e.g. xmlns="urn:default")
if ( ( attrib_pqname.getLocalName( ) != null )
&& attrib_pqname.getLocalName( ).equals( XmlConstants.NSPREFIX_XMLNS ) )
{
continue;
}
appendAttribute( buf,
nameToString( attrib_pqname ),
soap_elem.getAttributeValue( attrib_pqname ),
depth );
if ( ( ( attrib_pqname.getURI( ) != null ) && !( "".equals( attrib_pqname.getURI( ) ) ) )
&& ( ( attrib_pqname.getPrefix( ) != null ) && !( "".equals( attrib_pqname.getPrefix( ) ) ) )
&& ( soap_elem.getNamespaceURI( attrib_pqname.getPrefix( ) ) == null ) )
{
appendNamespaceDeclarationAttribute( buf, attrib_pqname, depth );
}
}
}
/**
* DOCUMENT_ME
*
* @param buf DOCUMENT_ME
* @param elem_name DOCUMENT_ME
* @param depth DOCUMENT_ME
*/
private void appendElementOpener( StringBuffer buf,
String elem_name,
int depth )
{
appendIndentSpaces( buf, depth );
buf.append( "<" );
buf.append( elem_name );
}
/**
* DOCUMENT_ME
*
* @param buf DOCUMENT_ME
*/
private static void appendEmptyElementCloser( StringBuffer buf )
{
buf.append( " />\n" );
}
/**
* DOCUMENT_ME
*
* @param buf DOCUMENT_ME
* @param elem_name DOCUMENT_ME
* @param depth DOCUMENT_ME
*/
private void appendClosingElement( StringBuffer buf,
String elem_name,
int depth )
{
appendIndentSpaces( buf, depth );
buf.append( "</" );
buf.append( elem_name );
buf.append( ">\n" );
}
/**
* DOCUMENT_ME
*
* @param buf DOCUMENT_ME
*/
private static void appendElementCloser( StringBuffer buf )
{
buf.append( ">\n" );
}
/**
* @param buf
* @param attrib_name
* @param attrib_value
*/
private void appendAttribute( StringBuffer buf,
String attrib_name,
String attrib_value,
int depth )
{
buf.append( "\n" );
appendIndentSpaces( buf, depth );
buf.append( attrib_name );
buf.append( "=\"" );
buf.append( attrib_value );
buf.append( "\"" );
}
/**
* DOCUMENT_ME
*
* @param buf DOCUMENT_ME
* @param children DOCUMENT_ME
* @param depth DOCUMENT_ME
*/
private void appendChildren( StringBuffer buf,
Iterator children,
SOAPElement root_soap_elem,
int depth )
throws SOAPException
{
while ( children.hasNext( ) )
{
Node child = (Node) children.next( );
buildSOAPNodeStringBuffer( buf, root_soap_elem, child, depth );
}
}
/**
* @param buf
* @param depth
*/
private void appendIndentSpaces( StringBuffer buf,
int depth )
{
if ( m_indent_str != null )
{
buf.append( org.apache.commons.lang.StringUtils.repeat( m_indent_str, depth ) );
}
}
/**
* Append an namespace declaration attribute of the form xmlns:prefix=nsuri.
*
* @param buf the {@link StringBuffer} to be appended to
* @param ns_pqname a {@link Name} containing the namespace URI and prefix to be appended; note that the localname
* portion of ns_pqname is ignored
* @param depth the current indentation depth
*/
private void appendNamespaceDeclarationAttribute( StringBuffer buf,
Name ns_pqname,
int depth )
{
if ( ns_pqname.getPrefix( ).equals( XmlConstants.NSPREFIX_XMLNS ) )
{
//LOG.debug( "The prefix \"xmlns\" cannot be bound to any namespace explicitly. Skipping..." );
return;
}
if ( ns_pqname.getURI( ).equals( XmlConstants.NSURI_XMLNS ) )
{
//LOG.debug( "The namespace for \"xmlns\" cannot be bound to any prefix explicitly. Skipping..." );
return;
}
String xmlns_prefix = XmlConstants.NSPREFIX_XMLNS;
// if prefix was not the "default" prefix (xmlns:BLAH="xxx" vs. xmlns="xxx")
if ( hasNonEmptyValue( ns_pqname.getPrefix( ) ) )
{
xmlns_prefix += ( ":" + ns_pqname.getPrefix( ) );
}
appendAttribute( buf,
xmlns_prefix,
ns_pqname.getURI( ),
depth );
}
/**
* DOCUMENT_ME
*
* @param buf DOCUMENT_ME
* @param elem DOCUMENT_ME
* @param depth DOCUMENT_ME
*/
private void appendTextNode( StringBuffer buf,
Node elem,
int depth )
throws SOAPException
{
Text text = (Text) elem;
appendIndentSpaces( buf, depth );
buf.append( getValue( text ) );
buf.append( "\n" );
}
/**
* @param buf
* @param soap_elem
* @param depth
*/
private void appendXmlnsAttributeIfNoPrefix( StringBuffer buf,
SOAPElement soap_elem,
int depth )
{
Name elem_pqname = soap_elem.getElementName( );
if ( ( elem_pqname.getPrefix( ) == null ) && ( elem_pqname.getURI( ) != null ) )
{
appendAttribute( buf,
XmlConstants.NSPREFIX_XMLNS,
elem_pqname.getURI( ),
depth );
}
}
/**
* @param buf string buffer to append to
* @param soap_elem a SOAP element; may not be null
* @param depth current recursion depth
*/
private void appendXmlnsAttributes( StringBuffer buf,
SOAPElement soap_elem,
int depth )
{
Iterator prefixes = soap_elem.getNamespacePrefixes( );
Set encountered_prefixes = new HashSet( );
Name name = soap_elem.getElementName( );
if ( hasNonEmptyValue( name.getPrefix( ) ) && hasNonEmptyValue( name.getURI( ) ) )
{
encountered_prefixes.add( name.getPrefix( ) );
appendNamespaceDeclarationAttribute( buf, name, depth );
}
while ( prefixes.hasNext( ) )
{
String prefix = (String) prefixes.next( );
if ( encountered_prefixes.contains( prefix ) )
{
// duplicate prefix - skip it...
continue;
}
encountered_prefixes.add( prefix );
try
{
Name ns_pqname =
SOAPFactory.newInstance( ).createName( "",
prefix,
soap_elem.getNamespaceURI( prefix ) );
appendNamespaceDeclarationAttribute( buf, ns_pqname, depth );
}
catch ( SOAPException soape )
{
//LOG.error( ResourceKeys.MSG.getMsg( ResourceKeys.METHOD_FAILED, "SOAPFactory.createName()" ),soape );
}
}
}
/**
* Workhorse method for {@link #toString(javax.xml.soap.Node)} - calls itself recursively.
*
* @param buf StringBuffer used to build up the string representation
* @param current_soap_node a SAAJ SOAP node
* @param depth the current tree depth (used for indenting)
*/
private StringBuffer buildSOAPNodeStringBuffer( StringBuffer buf,
Node root_soap_node,
Node current_soap_node,
int depth )
throws SOAPException
{
if ( isElement( current_soap_node ) )
{
SOAPElement soap_elem = (SOAPElement) current_soap_node;
Name elem_pqname = null;
try
{
elem_pqname = soap_elem.getElementName( );
}
catch ( RuntimeException re )
{
//LOG.error( ResourceKeys.MSG.getMsg( ResourceKeys.METHOD_FAILED, "SOAPElement.getElementName()" ) );
}
String elem_name = nameToString( elem_pqname );
if ( ( elem_pqname.getPrefix( ) == null ) || elem_pqname.getPrefix( ).equals( "" ) )
{
String prefix = getPrefix( elem_pqname, (SOAPElement) root_soap_node );
if ( prefix != null )
{
elem_name = prefix + ":" + elem_name;
}
}
appendElementOpener( buf, elem_name, depth );
appendXmlnsAttributes( buf, soap_elem, depth + 1 );
appendAttributes( buf, soap_elem, depth + 1 );
Iterator children = getChildren( soap_elem );
if ( children != null )
{
appendElementCloser( buf );
appendChildren( buf, children, (SOAPElement) root_soap_node, depth + 1 );
appendClosingElement( buf, elem_name, depth );
}
else
{
if ( hasNonEmptyValue( soap_elem ) )
{
appendElementCloser( buf );
appendTextNode( buf, soap_elem, depth + 1 );
appendClosingElement( buf, elem_name, depth );
}
else
{
appendEmptyElementCloser( buf );
}
}
}
else
{
appendTextNode( buf, current_soap_node, depth );
}
return ( buf );
}
/**
* DOCUMENT_ME
*
* @param soap_elem DOCUMENT_ME
*
* @return DOCUMENT_ME
*/
private static boolean hasNonEmptyValue( SOAPElement soap_elem )
{
return ( soap_elem.getValue( ) != null ) && !soap_elem.getValue( ).trim( ).equals( "" );
}
/**
* @param pqname
* @param current_soap_elem
*
* @return
*/
private static String recursiveGetPrefix( Name pqname,
SOAPElement current_soap_elem )
{
String prefix = getPrefix( current_soap_elem,
pqname.getURI( ) );
if ( ( prefix != null ) && !prefix.equals( "" ) )
{
return ( prefix );
}
Iterator children = current_soap_elem.getChildElements( );
while ( children.hasNext( ) )
{
Object obj_child = children.next( );
if ( !( obj_child instanceof SOAPElement ) )
{
continue;
}
SOAPElement child = (SOAPElement) obj_child;
prefix = getPrefix( child,
pqname.getURI( ) );
if ( ( prefix != null ) && !prefix.equals( "" ) )
{
break;
}
prefix = recursiveGetPrefix( pqname, child );
if ( ( prefix != null ) && !prefix.equals( "" ) )
{
break;
}
}
return ( prefix );
}
}