XPath expressions are many and varied, but they can be classified into two general groups:
The result expressions are interconvertible, but not symmetrically so. The interconversions are summarised:
| From | getNodeSet(...) | getString(...) | getNumber(...) | getBoolean(...) |
|---|---|---|---|---|
| NodeSet | self | use getItem(0) | via XPathString | use set length |
| XPathString | empty NodeSet | self | convert | via Number |
| Number | empty NodeSet | convert double | self | via 0= |
| Boolean | empty NodeSet | "true"/"false" | 0/1 | self |
This table is intended to indicate that NodeSet is the most information-rich of the result expressions, and that interconversions are not generally reversible. As an extreme example, if a NodeSet is converted to a Boolean, regardless of whether the result is true or false, it cannot be back-converted meaningfully to a NodeSet.
It is imperative, therefore, that all internal interconversions by generator expressions take account of this. In Limpid parlance, the main groups are:
Before getting involved in detail, two points should be emphasised:
Context values are used intensively in XSLT to encapsulate the context of a node when it is used to evaluate an XPath expression and obtain a result expression (perhaps most commonly this is a NodeSet).
An XPath expression is always defined in terms of a string. Examples are:
Limpid keeps the details of expression construction at arms length: it is always performed using a static function of the class XPathFactory: XPathFactory::getExpr(const DOMString& expressionString).
An expression is always evaluated in a , using a Context value. An example is:
Document dataDocument = getDocument(); // function is not specified here
Context context;
context.setNode(document);
Expr expr = XPathFactory::getExpr("/meal/dessert[@topping = 'cream']");
NodeSet resultSet = expr.getNodeSet(context);
size_t length = resultSet.getLength();
for (size_t index = 0; index <; length; ++index) {
Node& node = resultSet.getItem(index);
// ... display or process the node as required
}
The dominant characteristic of an XPath package is polymorphism; so we have to contend with:
The parent class of all XPath classes is XPathExpression (the unwieldly name is deliberate, to discourage its use). It specifies several interfaces, notably:
The return values are the standard return expressions referred to above, so there is no difficulty in returning one or other of them as a specific value. After all, we would not expect a Number to be returned by getNodeSet(...), would we?
But difficulties arise when we look more broadly at returning XPathExpressions, as in the previous example. Note that XPathFactory::getExpr() returns an Expr value, a class that we have not yet discussed.
Expr is a Handle or Bridge class, described in the standard texts. I can take credit (or blame) for reinventing it, but others may well have known the technique. In essence, it looks like an XPathExpression, in that it has the same interfaces, but it does not inherit from XPathExpression. It is simply a wrapper that delegates the standard function calls to its sole content: a Pointer<;XPathExpression>.
The point of this is that Expr is and always has the same size and can be returned as a value or added to an array or std container. The essentials of its definition are:
class Expr {
public:
Expr();
Expr(const XPathExpression& expression);
NodeSet getNodeSet(Context& ctxt, bool exact = true) const;
XPathString getString(Context& ctxt) const;
Boolean getBoolean(Context& ctxt) const;
Number getNumber(Context& ctxt) const;
private:
Pointer<XPathExpression> xpathExpression_;
};