<?xml version="1.0"?>
<?xml-stylesheet href="http://xslt.fourthought.com/api.xslt" type="text/xsl"?>
<tech:ApiSpec title="4XPath API"
  xmlns:tech="http://namespaces.fourthought.com/technical"
  xmlns:dc= "http://purl.org/dc/elements/1.1"
  xmlns="http://docbook.org/docbook/xml/4.0/namespace"
>

<dc:Title>4XPath API</dc:Title>
<dc:Subject>4XPath</dc:Subject>
<dc:Subject>XPAth</dc:Subject>
<dc:Subject>API</dc:Subject>
<dc:Description>Application Programming Interface for 4XPath.</dc:Description>


<sect1><title>About XPath</title>
<para>
For the formal definition of XPath, see <ulink url="http://www.w3.org/TR/xpath">The XPath 1.0 Recommendation</ulink>.  More gentle introductions the <ulink url="http://www.zvon.org/xxl/XPathTutorial/Output/index.html">ZVON XPath Tutorial</ulink>, and <ulink url="http://www.pro-solutions.com/tutorials/xpath/">ProSolutions XPath tutorial</ulink>.
</para>
</sect1>

<sect1>
<title>Usage</title>
<para>
4XPath can be used to either evaluate an XPath expression on the fly or to
create a reusable pre-parsed object.  The second approach is useful if you
evaluate the same expression over and over again.  You always need a
context in order to evaluate an expression.  This is at the minimum a DOM
node.  You can use any DOM implementation that conforms to the Python
binding for DOM Level 2.  Examples are 4DOM, minidom, cDomlette or pDomlette.
The latter two have some specializations that 4XPath can take advantage of
for optimization.
</para>

<para>
In the following discussions, we'll use the following two XML documents (set up as Python strings).  The second is simply the first with all the elements in a namespace.
</para>

<screen>
<![CDATA[
xmlstring = """<employees>
<employee id="100">Memnon</employee>
<employee id="101">Emathion</employee>
<employee id="102">Castor</employee>
<employee id="103">Polydeuces</employee>
</employees>
"""

nsxmlstring = """<employees xmlns='http://spam.com/foo'>
<employee id="100">Memnon</employee>
<employee id="101">Emathion</employee>
<employee id="102">Castor</employee>
<employee id="103">Polydeuces</employee>
</employees>
"""
]]>
</screen>

<sect2><title>On-the-fly XPaths</title>

<para>
To use a one-off XPath expression just start by importing the 4XPath module 
</para>

<screen>
from xml import xpath
</screen>

<para>
Then set up the DOM document to be used as the context.
</para>

<screen>
from Ft.Lib.pDomlette import PyExpatReader
reader = PyExpatReader()
doc = reader.fromString(xmlstring)
</screen>

<para>
Now just evaluate the XPaths you want.
</para>

<screen>
result = xpath.Evaluate('/*', contextNode=doc)
</screen>

<para>
Which basically selects the document element.  Note that the return value of
this XPath is a node set, which is implemented as a Python list of DOM nodes.
For instance "print result" would display
</para>

<screen>
<![CDATA[
[<Domlette Element Node at 820fc74: name='employees' with 0 attributes and 9 children>]
]]>
</screen>

<para>
Or similar (the "820fc74" represents the memory location of the Python object and will almost certainly be different for you).  The following are the mappings from XPath object types to the Python objects returned by evaluation 
</para>

<itemizedlist>
  <listitem><emphasis>boolean</emphasis>: integer (either 0 or 1)</listitem>
  <listitem><emphasis>number</emphasis>: floating-point number</listitem>
  <listitem><emphasis>string</emphasis>: string or unicode object</listitem>
  <listitem><emphasis>node set</emphasis>: list of DOM nodes</listitem>
</itemizedlist>

<para>
So, if you follow the following examples
</para>

<screen>
<![CDATA[
>>> print repr(xpath.Evaluate('/employees/employee', contextNode=doc))
[<Domlette Element Node at 8217784: name='employee' with 1 attributes and 1 children>,
 <Domlette Element Node at 81ddbf4: name='employee' with 1 attributes and 1 children>,
 <Domlette Element Node at 810c234: name='employee' with 1 attributes and 1 children>,
 <Domlette Element Node at 82131c4: name='employee' with 1 attributes and 1 children>]
>>> print repr(xpath.Evaluate('/employees/employee[1]/@id', contextNode=doc))
[<Domlette Attribute Node at 82190fc: name='id', value='100'>]
>>> print repr(xpath.Evaluate('string(/employees/employee[1]/@id)', contextNode=doc))
u'100'
>>> print repr(xpath.Evaluate('number(/employees/employee[1]/@id)', contextNode=doc))
100.0
>>> print repr(xpath.Evaluate('/employees/employee[1]/@id = "100"', contextNode=doc))
1
]]>
</screen>

<para>
Note the u"100" returned by one of the expressions.  This is the new unicode
type introduced in Python 2.0.  In Python 1.5.2, the return value would be a simple string objec: "100".
</para>

<para>
In our examples so far, we've set the context node directly.  This actually creates a context with the given node, a context list size of 1 and position of 1; no namespace mappings will be defined.  But this isn't always what we want to do.  Sometimes we want to specify the other context elements.  The most common reason for this is to set up a namespace mapping.  For instance, if we try to do the above processing on the nsxmlstring document:
</para>

<screen>
nsdoc = reader.fromString(nsxmlstring)
print xpath.Evaluate('/employees/employee', contextNode=nsdoc)
</screen>

<para>
We get an empty node-set.  If you remember your XPath and think carefully, you'll see why.  The node test "employees" strictly matches an element node with no namespace.  Since the employee element in the nsxmlstring document is actually in the "http://spam.com/foo" namespace, the node test fails.  The solution is to use a namespace prefix in the <emphasis>XPath expression</emphasis> which is mapped to the right namespace.  Remember that this is so even though we don't use a namespace prefix in the nsxmlstring document.
</para>

<para>
So how do we set up the namespace mappings we need?  This is where the ability to set the full context comes in.  We can create a 4XPath Context object with the node we want and the namespace mappings we want
</para>

<screen>
from xml.xpath.Context import Context
con = Context(nsdoc, processorNss={'x': 'http://spam.com/foo'})
</screen>

<para>
The first argument is the context node.  Then we specify a keyword argument "processorNss" which is a dictionary with the prefixes to map as keys and the namespace URIs as values.  Note: don't try to set an empty string as a prefix: this is illegal.  Now we can use the context object we created.
</para>

<screen>
print xpath.Evaluate('/x:employees/x:employee', context=con)
</screen>

<para>
And we get what we expect: a node set with four entries.
</para>

</sect2>

<sect2>
<title>Reusable XPath expression objects</title>

<para>
If your usage pattern is more along the lines of repeated evaluation of a particular expression against different contexts or documents, you probably want to parse the expression for the sake of performance.
</para>

<screen>
from xml import xpath
from xml.xpath.Context import Context

from Ft.Lib.pDomlette import PyExpatReader
reader = PyExpatReader()
doc = reader.fromString(xmlstring)
expr = xpath.Compile('/employees/employee')
</screen>

<para>
To evaluate it, we are required to have a full context object, not just a bare node.  Then we can use the "evaluate" method of the parsed expression object, passing in the context:
</para>

<screen>
con = Context(nsdoc)
expr.evaluate(con)
</screen>

<!--

<para>
</para>

<screen>

</screen>

<para>
</para>
-->

</sect2>

</sect1>

<sect1>
<title>Optimization: Document Indexing</title>
<para>
<emphasis role="bold">Note: you needn't bother with this if you are using pDomlette or cDomlette.</emphasis>
</para>
<para>
Many XPath constructs require a sorting of nodes according to XML document order.  This can be an expensive operation if the DOM implementation is not already primed for this, so XPath allows users of such implementations to pre-index documents for faster sorting.  To do so:
</para>

<screen>
from xml.xpath import Util
...
Util.IndexDocument(document_node)
...XPath operations...
Util.FreeDocumentIndex(document_node)
</screen>

<para>
Do be sure to free the index to avoid memory leaks.  Also note that it's a bad idea to mutate any node in the document while it is indexed.
</para>

</sect1>

<tech:Module>
  <tech:Name>xml.xpath</tech:Name>
  <tech:Desc>4XPath core module: provides the basic API for 4XPath</tech:Desc>

  <tech:Function>
    <tech:Name>Evaluate</tech:Name>
    <tech:Desc>Evaluates an XPath expression</tech:Desc>
    <tech:Arg><tech:Name>expr</tech:Name><tech:Type>string</tech:Type><tech:Desc>XPath expression to be evaluated</tech:Desc></tech:Arg>
    <tech:Arg><tech:Name>contextNode</tech:Name><tech:Type>Python DOM binding node object</tech:Type><tech:Desc>The context node, which will be used as the sole entry in the context node list.  If None, the context argument must be an xml.xpath.Context object.  Defaults to None</tech:Desc></tech:Arg>
    <tech:Arg><tech:Name>context</tech:Name><tech:Type>xml.xpath.Context</tech:Type><tech:Desc>The user-specified context.  If None, the contextNode argument must be a valid DOM node.  Defaults to None</tech:Desc></tech:Arg>
    <tech:Return><tech:Type>Ft.Lib.boolean, float, string or list of DOM nodes</tech:Type><tech:Desc>The result of the XPath expresssion evaluation.</tech:Desc></tech:Return>
    <tech:Exception>xml.xpath.XPathException</tech:Exception>
  </tech:Function>

  <tech:Function>
    <tech:Name>Compile</tech:Name>
    <tech:Desc>Compile an XPath expression for quicker evaluation.</tech:Desc>
    <tech:Arg><tech:Name>expr</tech:Name><tech:Type>string</tech:Type><tech:Desc>XPath expression to be compiled</tech:Desc></tech:Arg>
    <tech:Return><tech:Type>xml.xpath.ParsedExpr</tech:Type><tech:Desc>A pre-compiled XPath expression object</tech:Desc></tech:Return>
    <tech:Exception>xml.xpath.XPathException</tech:Exception>
  </tech:Function>

  <tech:Function>
    <tech:Name>RegisterExtensionModules</tech:Name>
    <tech:Desc>Register XPath extension functions contained in Python modules.</tech:Desc>
    <tech:Arg><tech:Name>moduleList</tech:Name><tech:Type>list of strings each representing a fully-qualified module name.  Each module must follow the 4XPath <ulink url="4XPath-ExtFunctions.html">extension protocol</ulink>.</tech:Type><tech:Desc>Each module is imported, and any XPath extension functions conteined therein are available to the 4XPath run-time.</tech:Desc></tech:Arg>

  </tech:Function>


  <tech:Function>
    <tech:Name>NormalizeNode</tech:Name>
    <tech:Desc>Normalize a DOM tree for processing with XPath.  This will convert all CDATA Sections to Text Nodes (as per XPath spec 5.7) and then normalize all text nodes.  This function mutates the original DOM tree.</tech:Desc>
    <tech:Arg><tech:Name>node</tech:Name><tech:Type>DOM Node</tech:Type><tech:Desc>The root of the node to normalize.</tech:Desc></tech:Arg>
  </tech:Function>


</tech:Module>

<tech:Module>
  <tech:Name>xml.xpath.Context</tech:Name>
  <tech:Desc>XPath context</tech:Desc>
  <tech:Class>
    <tech:Name>Context</tech:Name>
    <tech:Desc>Represents the context used for XPath processing at any given point</tech:Desc>
    <tech:Attribute><tech:Name>node</tech:Name><tech:Type>Python DOM binding node object</tech:Type><tech:Desc>The context node, as used for computing XPath expressions</tech:Desc></tech:Attribute>
    <tech:Attribute><tech:Name>position</tech:Name><tech:Type>positive integer</tech:Type><tech:Desc>The context node's position in the context node list, as returned by the XPath position() function</tech:Desc></tech:Attribute>
    <tech:Attribute><tech:Name>size</tech:Name><tech:Type>positive integer</tech:Type><tech:Desc>The size of the context node list</tech:Desc></tech:Attribute>
    <tech:Attribute><tech:Name>varBindings</tech:Name><tech:Type>dictionary with keys a tuple of two strings and value a string, integer, BooleanType or node set (list of nodes)</tech:Type><tech:Desc>Maps variable and parameters by expanded name to the value of the variable</tech:Desc></tech:Attribute>
    <tech:Attribute><tech:Name>processorNss</tech:Name><tech:Type>dictionary with string key and value</tech:Type><tech:Desc>provides expansion from namespace prefixes to uris for expanded names in name tests, variable names, etc.</tech:Desc></tech:Attribute>
    <tech:Method init="yes">
    <tech:Arg><tech:Name>node</tech:Name><tech:Type>Python DOM binding node object</tech:Type><tech:Desc>The context node, as used for computing XPath expressions</tech:Desc></tech:Arg>
    <tech:Arg><tech:Name>position</tech:Name><tech:Type>positive integer</tech:Type><tech:Desc>The context node's position in the context node list, as returned by the XPath position() function</tech:Desc></tech:Arg>
    <tech:Arg><tech:Name>size</tech:Name><tech:Type>positive integer</tech:Type><tech:Desc>The size of the context node list</tech:Desc></tech:Arg>
    <tech:Arg><tech:Name>varBindings</tech:Name><tech:Type>dictionary with keys a tuple of two strings and value a string, integer, BooleanType or node set (list of nodes)</tech:Type><tech:Desc>Maps variable and parameters by expanded name to the value of the variable.  Defaults to an empty dictionary.</tech:Desc></tech:Arg>
    <tech:Arg><tech:Name>processorNss</tech:Name><tech:Type>dictionary with string key and value</tech:Type><tech:Desc>provides expansion from namespace prefixes to uris for expanded names in name tests, variable names, etc.  Defaults to an empty dictionary.</tech:Desc></tech:Arg>
    </tech:Method>
    <tech:Method>
      <tech:Name>nss</tech:Name>
      <tech:Desc>Get a dictionary representing namespace nodes defined at the context node</tech:Desc>
      <tech:Return><tech:Type>dictionary with string key and string value</tech:Type><tech:Desc>Maps prefixes to namespace URIs</tech:Desc></tech:Return>
      <tech:exception></tech:exception>
    </tech:Method>
  </tech:Class>
</tech:Module>

</tech:ApiSpec>
