/*
* soapUI, copyright (C) 2004-2011 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.LineNumberReader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import com.eviware.soapui.support.types.StringToStringMap;
/**
*
* @author lars
*/
public class XPathData
{
private StringToStringMap nsMap = new StringToStringMap();
private List<String> pathComponents = new ArrayList<String>();
private String function;
private boolean absolute = false;
public XPathData( StringToStringMap nsMap, List<String> pathComponents, boolean absolute )
{
this.nsMap = nsMap;
this.pathComponents = pathComponents;
this.absolute = absolute;
}
public XPathData( String xpath, boolean skipFirst )
{
try
{
LineNumberReader reader = new LineNumberReader( new StringReader( xpath.trim() ) );
String s;
while( ( s = reader.readLine() ) != null )
{
if( s.startsWith( "declare namespace " ) )
{
String[] words = s.substring( "declare namespace ".length() ).split( "=" );
String prefix = words[0];
int ix1 = words[1].indexOf( '\'' );
int ix2 = words[1].lastIndexOf( '\'' );
String ns = words[1].substring( ix1 + 1, ix2 - ix1 );
nsMap.put( ns, prefix );
}
else
{
if( s.startsWith( "count(" ) && s.endsWith( ")" ) )
{
function = "count";
s = s.substring( "count(".length(), s.length() - ")".length() );
}
else if( s.startsWith( "exists(" ) && s.endsWith( ")" ) )
{
function = "exists";
s = s.substring( "exists(".length(), s.length() - ")".length() );
}
String[] words = s.split( "/" );
int firstWord = 1 + ( skipFirst ? 1 : 0 );
for( int i = firstWord; i < words.length; i++ )
{
pathComponents.add( 0, words[i] );
}
}
}
}
catch( IOException e )
{
throw new RuntimeException( e );
}
}
public void strip()
{
if( pathComponents.size() > 0 && "text()".equals( pathComponents.get( 0 ) ) )
pathComponents.remove( 0 );
for( int i1 = 0; i1 < pathComponents.size(); i1++ )
{
String s = pathComponents.get( i1 );
if( s.indexOf( '[' ) >= 0 )
{
StringBuffer buf = new StringBuffer();
boolean skip = false;
for( int i2 = 0; i2 < s.length(); i2++ )
{
char ch = s.charAt( i2 );
if( ch == '[' )
skip = true;
if( !skip )
buf.append( ch );
if( ch == ']' )
skip = false;
}
s = buf.toString();
pathComponents.set( i1, s );
}
}
function = null;
}
public XPathData createParent()
{
if( pathComponents.isEmpty() )
return null;
StringToStringMap nsMap2 = new StringToStringMap( nsMap );
ArrayList<String> pathComponents2 = new ArrayList<String>( pathComponents );
pathComponents2.remove( 0 );
return new XPathData( nsMap2, pathComponents2, absolute );
}
@Override
public String toString()
{
return getShortPath();
}
@Override
public boolean equals( Object obj )
{
if( obj == null || obj.getClass() != XPathData.class )
return false;
XPathData other = ( XPathData )obj;
return this.getHashKey().equals( other.getHashKey() );
}
@Override
public int hashCode()
{
return getHashKey().hashCode();
}
public String getHashKey()
{
return getCanonicalPath();
}
public StringToStringMap getNamespaceMap()
{
return nsMap;
}
public List<String> getPathComponents()
{
return pathComponents;
}
public boolean isAttribute()
{
if( pathComponents.isEmpty() )
return false;
String c = pathComponents.get( 0 );
return c.startsWith( "@" );
}
public String getAttributeName()
{
String c = pathComponents.get( 0 );
return c.substring( 1 );
}
public String getFunction()
{
return function;
}
public String getXPath()
{
return buildXPath( null );
}
public String getFullPath()
{
return buildXPath( null );
}
public String buildXPath( XPathModifier modifier )
{
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( function != null )
xpath.append( function ).append( "(" );
if( modifier != null )
modifier.beforeSelector( xpath );
String firstComponent = "";
if( pathComponents.size() > 0 )
firstComponent = pathComponents.get( pathComponents.size() - 1 );
if( !absolute && !"".equals( firstComponent ) )
xpath.append( "/" );
for( int c = pathComponents.size() - 1; c >= 0; c-- )
{
xpath.append( "/" ).append( pathComponents.get( c ) );
}
if( modifier != null )
modifier.afterSelector( xpath );
if( function != null )
xpath.append( ")" );
return xpath.toString();
}
public String getPath()
{
StringBuffer buf = new StringBuffer();
buf.append( "/" );
for( int c = pathComponents.size() - 1; c >= 0; c-- )
{
buf.append( "/" ).append( pathComponents.get( c ) );
}
return buf.toString();
}
/** Get a path with all namespaces replaced. */
public String getCanonicalPath()
{
HashMap<String, String> inverseNsMap = new HashMap<String, String>();
for( String key : nsMap.keySet() )
{
String value = nsMap.get( key );
inverseNsMap.put( value, key );
}
StringBuffer buf = new StringBuffer();
buf.append( "/" );
for( int c = pathComponents.size() - 1; c >= 0; c-- )
{
buf.append( "/" );
String s = pathComponents.get( c );
String[] words = s.split( ":" );
if( words.length == 2 )
{
String ns = inverseNsMap.get( words[0] );
if( ns != null )
{
buf.append( ns ).append( ":" ).append( words[1] );
}
else
{
buf.append( s );
}
}
else
{
buf.append( s );
}
}
return buf.toString();
}
/** Get a path with no namespaces or namespace prefixes. */
public String getShortPath()
{
StringBuffer buf = new StringBuffer();
buf.append( "/" );
for( int c = pathComponents.size() - 1; c >= 0; c-- )
{
buf.append( "/" );
String s = pathComponents.get( c );
String[] words = s.split( ":" );
if( words.length == 2 )
{
buf.append( words[1] );
}
else
{
buf.append( s );
}
}
return buf.toString();
}
public Collection<String> getNamespaces()
{
return nsMap.keySet();
}
public boolean hasNamespaces()
{
return nsMap != null && !nsMap.isEmpty();
}
}