/*
* soapUI, copyright (C) 2004-2007 eviware.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.support.xml;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.log4j.Logger;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import com.eviware.soapui.SoapUI;
import com.eviware.soapui.impl.wsdl.WsdlInterface;
import com.eviware.soapui.impl.wsdl.support.soap.SoapVersion;
import com.eviware.soapui.support.types.StringToStringMap;
/**
* General XML-related utilities
*/
public final class XmlUtils
{
private static DocumentBuilder documentBuilder;
private final static Logger log = Logger.getLogger( XmlUtils.class );
static public Document parse( InputStream in )
{
try
{
return ensureDocumentBuilder().parse( in );
}
catch( Exception e )
{
log.error( "Error parsing InputStream; " + e.getMessage(), e );
}
return null;
}
static public Document parse( String fileName ) throws IOException
{
try
{
return ensureDocumentBuilder().parse( fileName );
}
catch( SAXException e )
{
log.error( "Error parsing fileName [" + fileName + "]; " + e.getMessage(), e );
}
return null;
}
public static String entitize( String xml )
{
return xml.replaceAll( "&", "&" ).replaceAll( "<", "<" ).replaceAll( ">", ">" ).
replaceAll( "\"", """ ).replaceAll( "'", "'" );
}
static public Document parse( InputSource inputSource ) throws IOException
{
try
{
return ensureDocumentBuilder().parse( inputSource );
}
catch( SAXException e )
{
throw new IOException( e.toString() );
}
}
private static DocumentBuilder ensureDocumentBuilder()
{
if( documentBuilder == null )
{
try
{
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware( true );
documentBuilder = dbf.newDocumentBuilder();
}
catch( ParserConfigurationException e )
{
log.error( "Error creating DocumentBuilder; " + e.getMessage() );
}
}
return documentBuilder;
}
public static void serializePretty( Document document )
{
try
{
serializePretty( document, new OutputStreamWriter( System.out ) );
}
catch( IOException e )
{
log.error( "Failed to seraialize: " + e );
}
}
public static void serializePretty( Document dom, Writer writer )
throws IOException
{
try
{
XmlObject xmlObject = XmlObject.Factory.parse(dom.getDocumentElement());
serializePretty(xmlObject, writer);
}
catch (Exception e)
{
throw new IOException( e.toString() );
}
}
public static void serializePretty( XmlObject xmlObject, Writer writer ) throws IOException
{
XmlOptions options = new XmlOptions();
options.setSavePrettyPrint();
options.setSavePrettyPrintIndent( 3 );
options.setSaveNoXmlDecl();
options.setSaveAggressiveNamespaces();
xmlObject.save(writer, options);
}
public static void serialize( Document dom, Writer writer )
throws IOException
{
serialize( dom.getDocumentElement(), writer );
}
public static void serialize( Element elm, Writer writer )
throws IOException
{
try
{
XmlObject xmlObject = XmlObject.Factory.parse(elm);
xmlObject.save( writer );
}
catch (XmlException e)
{
throw new IOException( e.toString() );
}
}
static public void setElementText( Element elm, String text )
{
Node node = elm.getFirstChild();
if( node == null )
{
if( text != null)
elm.appendChild( elm.getOwnerDocument().createTextNode( text ) );
}
else if( node.getNodeType() == Node.TEXT_NODE )
{
if( text == null )
node.getParentNode().removeChild( node );
else
node.setNodeValue( text );
}
else if( text != null )
{
Text textNode = node.getOwnerDocument().createTextNode( text );
elm.insertBefore( textNode, elm.getFirstChild() );
}
}
public static String getChildElementText( Element elm, String name )
{
Element child = getFirstChildElement( elm, name );
return child == null ? null : getElementText( child );
}
public static Element getFirstChildElement( Element elm )
{
return getFirstChildElement( elm, null );
}
public static Element getFirstChildElement( Element elm, String name )
{
NodeList nl = elm.getChildNodes();
for( int c = 0; c < nl.getLength(); c++ )
{
Node node = nl.item( c );
if( node.getNodeType() == Node.ELEMENT_NODE && (name == null || node.getNodeName().equals( name )) )
return (Element) node;
}
return null;
}
public static Element getFirstChildElementNS( Element elm, String tns, String localName )
{
if( tns == null && localName == null )
return getFirstChildElement( elm );
if( tns == null )
return getFirstChildElement( elm, localName );
NodeList nl = elm.getChildNodes();
for( int c = 0; c < nl.getLength(); c++ )
{
Node node = nl.item( c );
if( node.getNodeType() != Node.ELEMENT_NODE ) continue;
if( localName == null && tns.equals( node.getNamespaceURI() ))
return ( Element ) node;
if( localName != null && tns.equals( node.getNamespaceURI() ) && localName.equals( node.getLocalName() ))
return ( Element ) node;
}
return null;
}
static public String getElementText( Element elm )
{
Node node = elm.getFirstChild();
if( node != null && node.getNodeType() == Node.TEXT_NODE )
return node.getNodeValue();
return null;
}
static public String getFragmentText( DocumentFragment elm )
{
Node node = elm.getFirstChild();
if( node != null && node.getNodeType() == Node.TEXT_NODE )
return node.getNodeValue();
return null;
}
public static String getChildElementText( Element elm, String name, String defaultValue )
{
String result = getChildElementText( elm, name );
return result == null ? defaultValue : result;
}
static public String getNodeValue( Node node )
{
if( node.getNodeType() == Node.ELEMENT_NODE )
return getElementText( (Element) node );
else if( node.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE )
return getFragmentText( (DocumentFragment) node );
else
return node.getNodeValue();
}
public static Node createNodeFromPath( Element modelElement, String path )
{
Document document = modelElement.getOwnerDocument();
StringTokenizer st = new StringTokenizer( path, "/" );
while( st.hasMoreTokens() )
{
String t = st.nextToken();
if( st.hasMoreTokens() )
{
if( t.equals( ".." ) )
{
modelElement = (Element) modelElement.getParentNode();
}
else
{
Element elm = getFirstChildElement( modelElement, t );
if( elm == null )
modelElement = (Element) modelElement.insertBefore(
document.createElement( t ),
getFirstChildElement( modelElement, t ) );
else
modelElement = elm;
}
}
else
{
modelElement = (Element) modelElement.insertBefore(
document.createElement( t ),
getFirstChildElement( modelElement, t ) );
}
}
return modelElement;
}
public static Element addChildElement( Element element, String name, String text )
{
Document document = element.getOwnerDocument();
Element result = (Element) element.appendChild( document.createElement( name ) );
if( text != null )
result.appendChild( document.createTextNode( text ) );
return result;
}
public static void setChildElementText( Element element, String name, String text )
{
Element elm = getFirstChildElement( element, name );
if( elm == null )
{
elm = element.getOwnerDocument().createElement( name );
element.appendChild( elm );
}
setElementText( elm, text );
}
public static Document parseXml( String xmlString ) throws IOException
{
return parse( new InputSource( new StringReader( xmlString )));
}
public static void dumpParserErrors(XmlObject xmlObject)
{
List errors = new ArrayList();
xmlObject.validate(new XmlOptions().setErrorListener(errors));
for (Iterator i = errors.iterator(); i.hasNext();)
{
System.out.println(i.next());
}
}
public static String transferValues(String source, String dest)
{
XmlCursor cursor = null;
try
{
XmlObject sourceXml = XmlObject.Factory.parse( source );
XmlObject destXml = XmlObject.Factory.parse( dest );
cursor = sourceXml.newCursor();
cursor.toNextToken();
while( !cursor.isEnddoc() )
{
while( !cursor.isContainer() && !cursor.isEnddoc() )
cursor.toNextToken();
if( cursor.isContainer() )
{
Element elm = ( Element ) cursor.getDomNode();
String path = createXPath( elm );
XmlObject[] paths = destXml.selectPath( path );
if( paths != null && paths.length > 0 )
{
Element elm2 = ( Element ) paths[0].getDomNode();
// transfer attributes
NamedNodeMap attributes = elm.getAttributes();
for( int c = 0; c < attributes.getLength(); c++ )
{
Attr attr = (Attr) attributes.item( c );
elm2.setAttribute( attr.getNodeName(), attr.getNodeValue() );
}
// transfer text
setElementText( elm2, getElementText( elm ));
}
cursor.toNextToken();
}
}
return destXml.xmlText();
}
catch (Exception e)
{
SoapUI.logError( e );
}
finally
{
if( cursor != null )
cursor.dispose();
}
return dest;
}
/**
* Returns absolute xpath for specified element, ignores namespaces
*
* @param elm the element to create for
* @return the elements path in its containing document
*/
public static String getElementPath(Element element)
{
Node elm = element;
String result = elm.getNodeName() + "[" + getElementIndex( elm ) + "]";
while( elm.getParentNode() != null && elm.getParentNode().getNodeType() != Node.DOCUMENT_NODE )
{
elm = elm.getParentNode();
result = elm.getNodeName() + "[" + getElementIndex( elm ) + "]/" + result;
}
return "/" + result;
}
/**
* Gets the index of the specified element amongst elements with the same name
*
* @param element the element to get for
* @return the index of the element, will be >= 1
*/
public static int getElementIndex(Node element)
{
int result = 1;
Node elm = element.getPreviousSibling();
while( elm != null )
{
if( elm.getNodeType() == Node.ELEMENT_NODE && elm.getNodeName().equals( element.getNodeName() ))
result++;
elm = elm.getPreviousSibling();
}
return result;
}
public static String declareXPathNamespaces( String xmlString ) throws XmlException
{
return declareXPathNamespaces( XmlObject.Factory.parse(xmlString));
}
public static synchronized String prettyPrintXml( String xml )
{
try
{
if( xml == null )
return null;
StringWriter writer = new StringWriter();
XmlUtils.serializePretty( XmlObject.Factory.parse( xml ), writer );
return writer.toString();
}
catch( Exception e )
{
log.warn( "Failed to prettyPrint xml: " + e );
return xml;
}
}
public static synchronized String prettyPrintXml( XmlObject xml )
{
try
{
if( xml == null )
return null;
StringWriter writer = new StringWriter();
XmlUtils.serializePretty( xml, writer );
return writer.toString();
}
catch( Exception e )
{
log.warn( "Failed to prettyPrint xml: " + e );
return xml.xmlText();
}
}
public static String declareXPathNamespaces(WsdlInterface iface)
{
StringBuffer buf = new StringBuffer();
buf.append( "declare namespace soap='" );
buf.append( iface.getSoapVersion().getEnvelopeNamespace() );
buf.append( "';\n");
try
{
Collection<String> namespaces = iface.getWsdlContext().getDefinedNamespaces();
int c = 1;
for (Iterator<String> i = namespaces.iterator(); i.hasNext();)
{
buf.append("declare namespace ns");
buf.append(c++);
buf.append("='");
buf.append(i.next());
buf.append("';\n");
}
}
catch (Exception e)
{
SoapUI.logError( e );
}
return buf.toString();
}
public static String createXPath(Node node )
{
return createXPath( node, false, false, null );
}
public static String createXPath(Node node, boolean anonymous, boolean selectText, XPathModifier modifier )
{
StringToStringMap nsMap = new StringToStringMap();
int nsCnt = 1;
List<String> pathComponents = new ArrayList<String>();
String namespaceURI = node.getNamespaceURI();
if( node.getNodeType() == Node.ATTRIBUTE_NODE )
{
if( namespaceURI.length() > 0 )
{
String prefix = node.getPrefix();
if( prefix == null || prefix.length() == 0 )
prefix = "ns" + nsCnt++;
nsMap.put( namespaceURI, prefix );
pathComponents.add( "@" + prefix + ":" + node.getLocalName() );
}
else
{
pathComponents.add( "@" + node.getLocalName() );
}
node = ((Attr)node).getOwnerElement();
}
if( node.getNodeType() == Node.ELEMENT_NODE )
{
int index = anonymous ? 0 : findNodeIndex( node );
String pc = null;
namespaceURI = node.getNamespaceURI();
if( namespaceURI.length() > 0 )
{
String prefix = node.getPrefix();
if( prefix == null || prefix.length() == 0 )
prefix = "ns" + nsCnt++;
nsMap.put( namespaceURI, prefix );
pc = prefix + ":" + node.getLocalName();
}
else
{
pc = node.getLocalName();
}
String elementText = XmlUtils.getElementText( (Element) node );
// not an attribute?
if( selectText && pathComponents.isEmpty() && elementText != null && elementText.trim().length() > 0 )
pathComponents.add( "text()" );
pathComponents.add( pc + ((index == 0 ) ? "" : "[" + index + "]" ));
}
else
return null;
node = node.getParentNode();
namespaceURI = node.getNamespaceURI();
while( node != null && node.getNodeType() == Node.ELEMENT_NODE &&
!node.getNodeName().equals( "Body" ) &&
!namespaceURI.equals( SoapVersion.Soap11.getEnvelopeNamespace() ) &&
!namespaceURI.equals( SoapVersion.Soap12.getEnvelopeNamespace() ))
{
int index = anonymous ? 0 : findNodeIndex( node );
String ns = nsMap.get( namespaceURI );
String pc = null;
if( ns == null && namespaceURI.length() > 0 )
{
String prefix = node.getPrefix();
if( prefix == null || prefix.length() == 0 )
prefix = "ns" + nsCnt++;
nsMap.put( namespaceURI, prefix );
ns = nsMap.get( namespaceURI );
pc = prefix + ":" + node.getLocalName();
}
else if( ns != null )
{
pc = ns + ":" + node.getLocalName();
}
else
{
pc = node.getLocalName();
}
pathComponents.add( pc + ((index == 0 ) ? "" : "[" + index + "]" ));
node = node.getParentNode();
namespaceURI = node.getNamespaceURI();
}
StringBuffer xpath = new StringBuffer();
for( Iterator<String> i = nsMap.keySet().iterator(); i.hasNext(); )
{
String ns = i.next();
xpath.append( "declare namespace " + nsMap.get( ns ) + "='" + ns + "';\n");
}
if( modifier != null )
modifier.beforeSelector( xpath );
xpath.append( "/" );
for( int c = pathComponents.size()-1; c >= 0; c-- )
{
xpath.append( "/" ).append( pathComponents.get( c ));
}
if( modifier != null )
modifier.afterSelector( xpath );
return xpath.toString();
}
private static int findNodeIndex(Node node)
{
String nm = node.getLocalName();
String ns = node.getNamespaceURI();
Node parentNode = node.getParentNode();
if( parentNode.getNodeType() != Node.ELEMENT_NODE )
return 1;
NodeList nl = ((Element)parentNode).getElementsByTagNameNS( ns, nm );
if( nl.getLength() == 1 )
return 0;
int mod = 0;
for( int c = 0; c < nl.getLength(); c++ )
{
if( nl.item( c ).getParentNode() != node.getParentNode() )
mod++;
else if( nl.item( c ) == node )
return c+1-mod;
}
throw new RuntimeException( "Child node not found in parent!?" );
}
public static boolean setNodeValue( Node domNode, String string )
{
short nodeType = domNode.getNodeType();
if( nodeType == Node.ELEMENT_NODE )
{
setElementText( ( Element ) domNode, string );
return true;
}
else if( nodeType == Node.ATTRIBUTE_NODE || nodeType == Node.TEXT_NODE )
{
domNode.setNodeValue( string );
return true;
}
return false;
}
public static String declareXPathNamespaces( XmlObject xmlObject )
{
Map<QName,String> map = new HashMap<QName,String>();
XmlCursor cursor = xmlObject.newCursor();
while( cursor.hasNextToken() )
{
if( cursor.toNextToken().isNamespace() )
map.put( cursor.getName(), cursor.getTextValue() );
}
Iterator<QName> i = map.keySet().iterator();
int nsCnt = 0;
StringBuffer buf = new StringBuffer();
Set<String> prefixes = new HashSet<String>();
Set<String> usedPrefixes = new HashSet<String>();
while( i.hasNext() )
{
QName name = i.next();
String prefix = name.getLocalPart();
if( prefix.length() == 0 ) prefix = "ns" + Integer.toString( ++nsCnt );
else if( prefix.equals( "xsd") || prefix.equals( "xsi")) continue;
if( usedPrefixes.contains( prefix ))
{
int c = 1;
while( usedPrefixes.contains( prefix + c )) c++;
prefix = prefix + Integer.toString( c );
}
else prefixes.add( prefix );
buf.append( "declare namespace " );
buf.append( prefix );
buf.append( "='" );
buf.append( map.get( name ));
buf.append( "';\n");
usedPrefixes.add( prefix );
}
return buf.toString();
}
public static String setXPathContent( String emptyResponse, String string, String actor )
{
try
{
XmlObject xmlObject = XmlObject.Factory.parse( emptyResponse );
String namespaces = declareXPathNamespaces( xmlObject );
if( namespaces != null && namespaces.trim().length() > 0 )
string = namespaces + string;
XmlObject[] path = xmlObject.selectPath( string );
for( XmlObject xml : path )
{
setNodeValue( xml.getDomNode(), actor );
}
return xmlObject.toString();
}
catch( Exception e )
{
SoapUI.logError( e );
}
return emptyResponse;
}
public static QName getQName( Node node )
{
if( node.getNamespaceURI() == null )
return new QName( node.getNodeName());
else
return new QName( node.getNamespaceURI(), node.getLocalName() );
}
public static String removeXPathNamespaceDeclarations( String xpath )
{
while( xpath.startsWith( "declare namespace" ))
{
int ix = xpath.indexOf( ';' );
if( ix == -1 )
break;
xpath = xpath.substring( ix+1 ).trim();
}
return xpath;
}
public static String stripWhitespaces( String content )
{
try
{
XmlObject xml = XmlObject.Factory.parse( content, new XmlOptions().setLoadStripWhitespace().setLoadStripComments() );
content = xml.xmlText();
}
catch( Exception e )
{
SoapUI.logError( e );
}
return content;
}
public static NodeList getChildElements( Element elm )
{
List<Element> list = new ArrayList<Element>();
NodeList nl = elm.getChildNodes();
for( int c = 0; c < nl.getLength(); c++ )
{
if( nl.item( c ).getNodeType() == Node.ELEMENT_NODE )
list.add( ( Element ) nl.item( c ) );
}
return new ElementNodeList( list );
}
private final static class ElementNodeList implements NodeList
{
private final List<Element> list;
public ElementNodeList( List<Element> list )
{
this.list = list;
}
public int getLength()
{
return list.size();
}
public Node item( int index )
{
return list.get( index );
}}
public static boolean seemsToBeXml( String str )
{
try
{
return str != null && XmlObject.Factory.parse( str ) != null;
}
catch( Exception e )
{
return false;
}
}
public static String extractNamespaces( String xpath )
{
String result = xpath;
int ix = xpath.lastIndexOf( "declare namespace" );
if( ix != -1 )
{
ix = xpath.indexOf( '\'', ix+1 );
if( ix != -1 )
{
ix = xpath.indexOf( '\'', ix+1 );
if( ix != -1 )
{
ix = xpath.indexOf( ';' );
if( ix != -1 )
{
result = xpath.substring( 0, ix+1 );
}
}
}
}
return result;
}
}
|