for revision start and <REVEND/>
for revision end) to mark the boundaries of edits. The following pointer part would select, for each revision, a range starting at the beginning of the REVST
element and ending at the end of the next REVEND
element:
xpointer(descendant::REVST/range-to(following::REVEND[1]))
string-range
Function location-setstring-range(location-set, string, number?, number?)
For each location in the location-set argument, string-range
returns a set of ranges determined by searching the string-value of the location for substrings that match the string argument. An empty string is defined to match before each character of the string-value and after the final character. White space in a string is matched literally, with no normalization except that provided by XML for line ends and attribute values. Each non-overlapping match can contribute a range to the resulting location set.
The third argument gives the position of the first character to be in the resulting range, relative to the start of the match. The default value is 1, which makes the range start immediately before the first character of the matched sub-string. The fourth argument gives the number of characters in the range; the default is that the range extends to the end of the matched string. Thus, both the start point and end point of each range returned by the string-range
function will be within text nodes.
Element boundaries, as well as entire embedded nodes such as processing instructions and comments, are ignored as specified by the definition of string-value in [XPath].
For any particular location, if the string argument is not found in the string-value of the location, or if the third and fourth argument indicates a range that is wholly beyond the beginning or end of the document or entity, then no range is added to the result for that match.
The start and end points of the range-locations in the returned location-set will all be character-points.
For example, the following expression returns a range that selects the 17th of those "Thomas Pynchon" strings appearing in a title
element:
string-range(//title,"Thomas Pynchon")[17]
As another example, the following expression returns a collapsed range whose points immediately precede the letter "P" (8 from the start of the string) in the third of those "Thomas Pynchon" strings appearing in a P
element:
string-range(//P,"Thomas Pynchon",8,0)[3]
Alternatively this could be specified as follows:
string-range(string-range(//P,"Thomas Pynchon")[3],"P",1,0)
String-values are "views" into only the string content of a document or entity; they do not retain the structural context of any non-text nodes interspersed with the text. Because the string-range
function operates on a string-value, markup that intervenes in the middle of a string does not prevent a match. (Note that for this reason, a string-range
match is a range describing the relevant substring of the string-value, not necessarily a contiguous string in a single text node in the document.) For example, if the 17th occurrence of "Thomas Pynchon" had some inline markup in it as follows, it would not change the string identified by the XPointer processor:
Thomas <em>Pyn</em>chon
The following expression selects the fifth of those exclamation marks appearing in any text node in the document and the character immediately following that exclamation mark:
string-range(/,"!",1,2)[5]
Although these examples locate ranges via text in the string-values of elements, string-range
is useful for locating ranges that are wholly enclosed in other node types as well, such as attributes, processing instructions, and comments.
The following functions are related to ranges.
covering-range
Function location-setrange(location-set)
The covering-range
function returns the covering ranges for the locations in the argument location-set. For each location x in the argument location-set, a range location representing the covering range of x is added to the result location-set.
range-inside
Function location-setrange-inside(location-set)
The range-inside
function returns the distinct covering ranges of the locations in the argument location-set. For each location x in the argument location-set, a location is added to the result location-set. If x is a range location or a point, then x is added to the result location-set. Otherwise x is used as the container node of the start and end points of the range location to be added, which is defined in this way: The index of the start point of the range is zero. If the end point is a character-point then its index is the length of the string-value of x; otherwise its index is the number of children of x.
start-point
Function location-setstart-point(location-set)
For each location x in the argument location-set, start-point
adds a location of type point to the resulting location-set. That point represents the start point of location x and is determined by the following rules:
If x is of type point, the resulting point is x.
If x is of type range, the resulting point is the start point of x.
If x is of type root, element, text, comment, or processing instruction, the container node of the resulting point is x and the index is 0.
If x is of type attribute or namespace, the pointer part in which the function appears fails.
end-point
Function location-setend-point(location-set)
For each location x in the argument location-set, end-point
adds a location of type point to the result location-set. That point represents the end point of location x and is determined by the following rules:
If x is of type point, the resulting point is x.
If x is of type range, the resulting point is the end point of x.
If x is of type root or element, the container node of the resulting point is x and the index is the number of children of x.
If x is of type text, comment, or processing instruction, the container node of the resulting point is x and the index is the length of the string-value of x.
If x is of type attribute or namespace, the pointer part in which the function appears fails.
here
Function location-sethere()
The here
function is meaningful only when the expression being interpreted occurs in an XML document or external parsed entity; otherwise the pointer part in which the here
function appears fails. When in an XML context, the here
function returns a location-set with a single member. There are two possibilities for the location returned:
If the expression being evaluated appears in a text node inside an element node, the location returned is the element node.
Otherwise, the location returned is the node that directly contains the expression being evaluated.
In the following example, the here
function appears inside an expression that is in an attribute node. The expression as a whole, then, returns the slide
element just preceding the slide
element that most directly contains the attribute node in question.
<button xlink:type="simple" xlink:href="#xpointer(here()/ancestor::slide[1]/preceding::slide[1])"> Previous </button>
Note:
The type of the node in which the here
function appears is likely to be text
, attribute
, or processing-instruction
. The returned location for an expression appearing in element content does not have a node type of element
because the expression is in a text node that is itself inside an element.
origin
Function location-setorigin()
The origin()
function is meaningful only when the expression is being processed in response to traversal of a link expressed in an XML document. The origin
function enables addressing relative to third-party and inbound links such as defined in the XLink Recommendation. This allows expressions to express relative locations when links do not reside directly at one of their endpoints. The function returns a location-set with a single member, which locates the element from which a user or program initiated traversal of the link. (See [XLink] for information about traversal.)
It is an error to use origin
in the fragment identifier portion of a URI reference where a URI is also provided and identifies a resource different from the resource from which traversal was initiated, or in a situation where traversal is not occurring.
The XML Recommendation requires well-formed documents to contain a single element at the top level. Thus, the XPath data model of a well-formed document will have a root node with a single child node of type element. In order to address locations in arbitrary external parsed entities, along with well-formed documents, the xpointer()
scheme extends the XPath data model to allow the root node to have any sequence of nodes as children that would be possible of an element node. This extension is identical to the one made by XSLT. Thus, the root node may contain child nodes of type text, and any number of child nodes of type element.
The notation defined here builds on the XPointer element()
scheme. However, where element()
can only identify a whole element node in an XML Infoset, the notation defined here can also identify any point or range. This notation is intended to be easy to understand and to provide a clear way to explicate the sometimes-complex ordering relationships of document locations. It also has advantageous properties such as that many useful properties of document locations can be determined merely by examining their representation in this notation, and without having to resort to the actual document or Infoset.
The initial portion of the notation is identical to element()
: a list of child numbers separated by the slash character ("/"). Such a list is called a [Definition: child sequence]. The following XML source corresponds to the diagram below it and shows the element()
scheme's child sequence number above each node:
<p>hello, <emph>big </emph>world.</p>
ROOT | 1 | p ---------------------------------- | | | 1/1 1/2 1/3 | | | txt emph txt ------------- ------- ----------- h e l l o , _ | w o r l d . 1/2/1 | txt ------- b i g _
Figure 2: Structure diagram with child sequences for all nodes.
"ROOT" here indicates the root node, "txt" indicates text nodes, "_" indicates the space character, and "p" and "emph" indicate element nodes.
For example:
element(1) identifies the p
element (the document element; note that the root node has no number).
element(1/2/1) identifies the (only) text node within the emph
element, which contains the text "big ".
Points, ranges, and non-element nodes cannot be identified by the numbering mechanism. However, the DOM 2: Range[DOM2] specification (http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html) defines points in terms of a containing node and an offset within it. This numbering covers all the points adjacent to nodes of all ordered node types, and all the points adjacent to characters within the text-containing node types (not only text nodes, but also comments, processing instructions, namespace and attribute nodes).
For the exact same structure shown above, those points are numbered as shown below.
ROOT | | ^ p ^ 0 | 1 | ---------------------------------- | | | | | | | | | ^ txt ^ emph ^ txt ^ 0 ^ 1 | 2 ^ 3 / \ | / \ / \ | / \ / \ | / \ ------------- txt ----------- h e l l o , _ ^ w o r l d . ^ ^ ^ ^ ^ ^ ^ ^ / \ ^ ^ ^ ^ ^ ^ ^ 0 1 2 3 4 5 6 7 / \ 0 1 2 3 4 5 6 / \ ------- b i g _ ^ ^ ^ ^ ^ 0 1 2 3 4
Figure 3: Structure diagram with points numbered.
For example:
the point just before the first text node is offset 0 within the p
element
the point after the "r" of "world" is offset 3 within the last text node.
the point between the emph
element and the following text node is offset 2 within the p
element
Note:
Implementors must take care regarding a difference in how XPath [XPath] and DOM 2: Range [DOM2] count characters. XPath and (and hence this specification) count UCS characters; DOM instead counts UTF-16 code points.
A point is always within some node, but a point cannot have children as a node can. Thus, a point can always be identified by the child sequence of the node it is in, plus the offset of the point within that node.
This specification therefore defines the [Definition: point sequence] notation to be like the element()
scheme syntax except that:
the slash-separated numbers are determined not by counting only element nodes, but by counting nodes of all types
an integer is appended when representing a point, to represent the point's offset within the node specified by the preceding child sequence
the final integer is separated from the preceding ones by "." instead of "/", to distinguish its different meaning
the child sequence may be empty, meaning that the point is directly within the root node
the notation is not named "element", but "point"
For example:
the point sequence to identify the point immediately following the period within the last text node above is point(1/3.6)
. "1/3" identifies the last text node, while ".6" indicates the offset with that node.
the point immediately preceding the p
element has point sequence point(.0)
.
The last component must be delimited by "." instead of "/" in order to distinguish specifying a point from specifying a node at the same level. For example, point(1/2)
is a child sequence that identifies the emph element above, but point(1.2)
identifies the point immediately following it. Thus the XPointer startpoint() and endpoint() functions can be easily implemented in terms of this representation.
The point preceding the root node itself is identified as ".0"; the point after the root node itself is identified as ".1". The sequence "/" refers to the root node itself and may optionally be written before the first numeric component.
For an expression range(S, E) in which S and E are both points: If S precedes E in document order the expression represents the range beginning at S and ending at E; if S is equal to E in document order, the expression represents the collapsed range (equivalent to the point) at S (and equally at E); if S follows E in documents order, the expression is in error and results are undefined.
An expression point(P) in which P is not a point, is treated as point(start-point(P)). An expression range(S) is treated as range(S,S). An expression range(S, E) in which either S or E is not a point, is equivalent to range(start-point(S),end-point(E)).
The name of the point notation is "point", and its formal grammar is:
[1] PointSchemeData ::= (NCName PointSequence?) | PointSequence [2] PointSequence ::= (ChildSequence Offset?) | (Root Offset?) | Offset [3] ChildSequence ::= ('/' [1-9] [0-9]*)+ [4] Root ::= '/' [5] Offset ::= ('.' [0-9]+)
The name of the range notation is "range", and its formal grammar is:
[6] RangeSchemeData ::= PointSchemeData s* ("," s* PointSchemeData)?
The diagram in the section "Document Ordering" above, shows the point sequences to all points in its example, omitting the "point(" and ")" to save space.
Note:
As noted earlier, the point immediately before (or after) a text node occurs at the same place in an XML source document as the point immediately before (or after) that text node's first (or last) character. This specification defines those locations as distinct; they do not compare as equal. For example, the point at offset 2 within the p element above is not equal to the point at offset 0 within the text node containing "world." It is beyond the scope of this specification to define editing operations such as insertion, but we note that the permissible operations at such points may differ. For example, an interface might reject inserting a node within a text node even at offset 0, or inserting characters at the corresponding offset within the containing element.
Since a range is defined in terms of a starting point and an ending point, a range is uniquely identified by two point sequences. For example, the range extending from before the 2nd "l" of "hello", to immediately after the emph element, is from point(1/1.3)
to point(1.2)
and is here written range(1/1.3, 1.2)
.
The covering range of any location, as defined above, is equivalent to the location and compares identically. For example,
the point just described following "l" is equivalent to range(1/1.3, 1/1.3)
.
the "i" itself is equivalent to range(1/2/1.1, 1/2/1.2)
.
the emph element above is equivalent to range(1.1, 1.2)
.
the entire content of the root node (but not the root node itself) is equivalent to range(.0,.1) or range(/.0, /.1).
.
the entire root node is equivalent to range(/,/)
.
Note:
The node representation just shown is analogous to the "outerHTML" property in some scripting languages, as it contains the entire node as a whole, not merely its children or contents. The "inner" aspect of a node is also representable. For example, the complete content of the p element above, but not the p element itself, is range(1.0, 1.3)
. The 3 represents offset 3 within the p element, the position following its 3rd child (which is the text node containing 'world.').
Note:
Several useful operations can be done using solely the point sequence representation of ranges. For example, order comparisons, tests of containment and equivalence, tests of whether a range could form a well-formed XML entity, and measures of nesting depth can all be trivially constructed based on this representation. However, this specification does not mandate implementation of this or any other internal representation, so long as the results specified here are obtained.
The first working drafts of this specification were developed in the XML Working Group, whose members are listed in [XML]. The work was completed in the XML Linking Working Group, with the following members active at the completion of this specification:
The editors wish to acknowledge substantial contributions from Tim Bray, who previously served as co-editor and co-chair; from James Clark on integration with XPath; from Gavin Nicol and Martin Dürst on passages related to internationalization; and from Jeni Tennison on the formal definition of document ordering. Finally, we would like to thank the XML Linking Interest Group and Working Group for their support and input.