SolidApp - a fundamental system
SolidApp builds open source software for standard-based declarative applications in Java(TM) and .NET
Our mission is to build and service software that creates, deploys and runs standard-based declarative applications for JavaTM and .NET

Using the XPath Engine

This article will explain howto use the XPath engine. It will also be demonstrated how to add new functions.

All the examples here is included in the examples directory in Subversion:

svn checkout

Table of Contents

  1. The XPath Engine Design
  2. Using the XPath Engine
  3. Extending the XPath Engine
  4. Advanced features

The XPath Engine Design

The XPath Engine uses a three phase compilation cycle:

  1. Builds a semantic tree from the XPath expression.
  2. Optimizes the semantic tree.
  3. Generates the Java object for doing the execution.

The created expression is not thread safe and only one execution may be performed at a time. It is possible to copy() a created expression if it has to be used in a multi-threaded environment.

For performance the compilation context will be asked by the engine for a symbol mapper instance. The symbol mapper makes it possible to use the same QNames in both the DOM implementation and in the XPath engine. Additional it is possible to change the way the engine do comparison of QNames which can increase performance (since it is cheaper to do == than .equals(<object>). A default implementation of the symbol mapper can just return the same string instance.

import com.solidapp.xpath.IXPathStringMapper; /** Default implementation of the String mapper */ public class XPathStringMapper implements IXPathStringMapper { /** Identity function */ public String map(String str) { return str; } }
Example implementation of a symbol mapper

Using the XPath Engine

Before the XPath Engine can be used for compilation and execution is is required to provide a compiler context and an evaluation context. Under compilation the compiler context is used to resolved QNames and providing a sorter class. Under execution the evaluation context is used to set the initial node, position and context size (there is also a callback for variables but that isn't used at the moment since the XPath engine does not support variables).

The following is an example for how to provide a compiler context when using a DOM Core Level 3 implementation.

import org.w3c.dom.Document; import org.w3c.dom.Node; import com.solidapp.xpath.IXPathCompilerContext; import com.solidapp.xpath.IXPathNodeComparator; import com.solidapp.xpath.IXPathStringMapper; /** A default implementation of the compiler context */ public class XPathCompilerContext implements IXPathCompilerContext { /** The node comparator, can be static since */ static IXPathNodeComparator s_comparator = new XPathNodeComparator(); /** The string mapper (identity funciton), can be static */ static IXPathStringMapper s_string_mapper = new XPathStringMapper(); /** The node used for resolving prefixes */ Node m_namespace_node; /** The constructor * * @param namespaceNode */ public XPathCompilerContext(Node namespaceNode) { m_namespace_node = namespaceNode.getNodeType()==Node.DOCUMENT_NODE ? ((Document)namespaceNode).getDocumentElement() : namespaceNode; } /** The comparator */ public IXPathNodeComparator getComparator() { return s_comparator; } /** The string mapper */ public IXPathStringMapper getStringMapper() { return s_string_mapper; } /** Looks up the prefix */ public String lookupNamespaceForPrefix(String prefix) { return m_namespace_node.lookupNamespaceURI(prefix); } }
Implementation example of a compiler context

The comparator used for maintaining document order can be implemented as follows.

import org.w3c.dom.Node; import com.solidapp.xpath.IXPathNodeComparator; public class XPathNodeComparator implements IXPathNodeComparator{ public int compareNodePosition(Node n1, Node n2) { if (n1==n2) return 0; short pos = n1.compareDocumentPosition(n2); if ((pos&Node.DOCUMENT_POSITION_PRECEDING)==Node.DOCUMENT_POSITION_PRECEDING) { return 1; } return -1; } }
Implementation of a node sorter

The evaluation context could be implemented as follows.

import org.w3c.dom.Node; import com.solidapp.xpath.IXPathContext; /** Default implementation of an XPath context * */ public class XPathContext implements IXPathContext { /** The context node */ Node m_context; /** The context position */ int m_position; /** The context size */ int m_last; /** Constructor for an context with a node and size and positon 1 * * @param context the context node */ public XPathContext(Node context) { this(context, 1, 1); } /** Constructor for a context with a node and a specific size and position * * @param context the context node * @param size the context size * @param position the position in the context */ public XPathContext(Node context, int size, int position) { m_context = context; m_last = size; m_position = position; } /** Gets the last position */ public int getLast() { return m_last; } /** Gets the context node */ public Node getNode() { return m_context; } /** Gets the position */ public int getPosition() { return m_position; } /** Asks for a variable (* Not Supported *) */ public Object resolveVariable(String namespaceURI, String localName) { return null; } }
Implementation of an evaluation context

The following example illustrates howto use the engine.

import org.w3c.dom.Document; import org.w3c.dom.Element; import com.solidapp.xpath.IXPathBoolean; import com.solidapp.xpath.IXPathCompilerContext; import com.solidapp.xpath.IXPathContext; import com.solidapp.xpath.IXPathCreator; import com.solidapp.xpath.IXPathNodeIterator; import com.solidapp.xpath.IXPathNumber; import com.solidapp.xpath.IXPathSingleNode; import com.solidapp.xpath.IXPathString; import com.solidapp.xpath.IXPathStringIterator; import com.solidapp.xpath.XPathFactory; import com.solidapp.xpath.examples.common.DocumentUtil; import com.solidapp.xpath.examples.common.XPathCompilerContext; import com.solidapp.xpath.examples.common.XPathContext; /** This examples illustrates howto use the XPath Engine */ public class CreatorUsage { public static void main(String[] args) { // Our example DOM Document dom = DocumentUtil.XML( "<cv:cv xmlns:cv='urn:cv'>" + " <name>John Smith</name>" + " <age>24</age>" + " <jobs>4</jobs>" + " <grades>" + " <subject name='math'>7</subject>" + " <subject name='english'>6</subject>" + " <subject name='history'>8</subject>" + " </grades>" + "</cv:cv>" ); // Make a XPathCreator from the system XPathCreatorFactory IXPathCreator creator = XPathFactory.create(); // The compiler context we will use IXPathCompilerContext compiler_context = new XPathCompilerContext(dom); // The evaluation context we will use IXPathContext context = new XPathContext(dom); // Now we will make some queries // The name of the person on the VC IXPathString personNameExpr = creator.createString(compiler_context, "cv:cv/name"); System.out.println("Name: " + personNameExpr.string(context)); // His age in number of days roughly IXPathNumber daysOfAgeExpr = creator.createNumber(compiler_context, "365.25*cv:cv/age"); System.out.println("Days old: " + daysOfAgeExpr.number(context)); // The avarage of his grades IXPathNumber subjectAverageExpr = creator.createNumber(compiler_context, "sum(//subject) div count(//subject)"); System.out.println("Average: " + subjectAverageExpr.number(context)); // Test if he has had more than 3 jobs IXPathBoolean moreThan3JobsExpr = creator.createBoolean(compiler_context, "/cv:cv/jobs >= 3"); System.out.println("More than 3 jobs: " + moreThan3JobsExpr.bool(context)); // We can select all the subject nodes and iterate IXPathNodeIterator subjectExpr = creator.createNodeIterator(compiler_context, "*/grades/subject"); subjectExpr.init(context); Element subjectNode; while ((subjectNode=(Element)!=null) { System.out.println(" Subject : " + subjectNode.getAttribute("name")); } // Or we can make a string iterator and iterate over the subjects again IXPathStringIterator substrExpr = creator.createStringIterator(compiler_context, "*/grades/subject/@name"); substrExpr.init(context); while (substrExpr.hasNext()) { System.out.println(" Subject : " +; } // We can also just get a single node like the last subject IXPathSingleNode lastSubjectExpr = creator.createNode(compiler_context, "*/grades/subject[last()]"); System.out.println("Name of the last subject: " + ((Element)lastSubjectExpr.node(context)).getAttribute("name")); } }
How to use the XPath Engine

Extending the XPath Engine

... to be written

Advanced features

... to be written

Copyright © 2006 SolidApp. All rights reserved.