package eu.geclipse.jsdl.xpath;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Class simplifying querying of XML {@link Document} using XPath language.
* Handle such things like namespaces, caching compiled expressions etc.
*/
public class XPathDocument {
private static XPath xpathEngine;
private Document document;
private Map<String,XPathExpression> expressionsMap;
/**
* @param document on which XPath queries will be run
*/
public XPathDocument( final Document document ) {
super();
this.document = document;
this.expressionsMap = new HashMap<String,XPathExpression>();
}
/**
* Get nodes selected by giving XPath
* @param parentNode XML node, from which query will be executed.
* @param xpathQuery
* @return nodes selected by query
* @throws XPathExpressionException
*/
public NodeList getNodes( final Node parentNode, final String xpathQuery ) throws XPathExpressionException {
NodeList nodes = ( NodeList )getExpression( xpathQuery ).evaluate( parentNode, XPathConstants.NODESET );
return nodes;
}
/**
* Makes the same as {@link XPathDocument#getNodes(Node, String)} starting from document root
* @param xpathQuery
* @return nodes selected by query
* @throws XPathExpressionException
*/
public NodeList getNodes( final String xpathQuery ) throws XPathExpressionException {
return getNodes( this.document.getDocumentElement(), xpathQuery );
}
/**
* Get String value from node selected by query.
* @param parentNode
* @param xpathQuery
* @return text value from selected node
* @throws XPathExpressionException
*/
public String getValue( final Node parentNode, final String xpathQuery ) throws XPathExpressionException {
return getExpression( xpathQuery ).evaluate( parentNode );
}
private XPath getXPathEngine() {
if( xpathEngine == null ) {
xpathEngine = XPathFactory.newInstance().newXPath();
xpathEngine.setNamespaceContext( getNamespaceContext() );
}
return xpathEngine;
}
private XPathExpression getExpression( final String xpathQuery ) throws XPathExpressionException {
XPathExpression expression = this.expressionsMap.get( xpathQuery );
if( expression == null ) {
expression = getXPathEngine().compile( xpathQuery );
this.expressionsMap.put( xpathQuery, expression );
}
return expression;
}
private NamespaceContext getNamespaceContext() {
return new NamespaceContext() {
// TODO mariusz parse source jsdl document add here all prefixes and namespaces (user may use these prefixes)
public String getNamespaceURI( final String prefix ) {
String namespace = XMLConstants.NULL_NS_URI;
if ("jsdl".equals(prefix)) { //$NON-NLS-1$
namespace = "http://schemas.ggf.org/jsdl/2005/11/jsdl"; //$NON-NLS-1$
}
else if( "jsdl-posix".equals( prefix ) ) { //$NON-NLS-1$
namespace = "http://schemas.ggf.org/jsdl/2005/11/jsdl-posix"; //$NON-NLS-1$
}
else if( "sweep".equals( prefix ) ) { //$NON-NLS-1$
namespace = "http://schemas.ogf.org/jsdl/2007/01/jsdl-sweep" ; //$NON-NLS-1$
}
else if( "sweepfunc".equals( prefix ) ) { //$NON-NLS-1$
namespace = "http://schemas.ogf.org/jsdl/2007/01/jsdl-sweep/functions" ; //$NON-NLS-1$
}
else if ("xml".equals( prefix )) { //$NON-NLS-1$
namespace = XMLConstants.XML_NS_URI;
}
else if( "fn".equals( prefix ) ) { //$NON-NLS-1$
namespace = "http://www.w3.org/2005/xpath-functions"; //$NON-NLS-1$
}
return namespace;
}
public String getPrefix( final String namespaceURI ) {
throw new UnsupportedOperationException();
}
@SuppressWarnings("unchecked")
public Iterator getPrefixes( final String namespaceURI ) {
throw new UnsupportedOperationException();
}};
}
@Override
public String toString() {
String string = "(null)"; //$NON-NLS-1$
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform( new DOMSource( this.document ), new StreamResult( outputStream ) );
string = outputStream.toString();
} catch( TransformerConfigurationException exception ) {
string = exception.getMessage();
} catch( TransformerException exception ) {
string = exception.getMessage();
}
return string;
}
/**
* @return dom document, which is attached to this xpath document
*/
public Document getDomDocument() {
return this.document;
}
}