The Functional XML Querying Tool
|
What is fxgrep? |
|
The Processing Model of fxgrep |
|
The Pattern Language of fxgrep |
|
The Grammar Formalism of fxgrep |
|
Text Patterns |
|
fxgrep Options |
|
fxgrep's Graphical User Interface |
![]() |
Unary Queries | ||||||||
|
|||||||||
![]() |
Binary Queries |
fxgrep's pattern language is similar to XPath in that it identifies a node in the document tree by means of the path through its ancestors. However, instead of XPath's arbitrary, ad-hoc-style expressions in qualifiers, fxgrep introduces two additional kinds of qualifiers, allowing the formulation of conditions on the children or the siblings of a node on the path.
Furthermore, via binary queries, fxgrep can identify a node in the input tree together with nodes which are in some specified relation with the first node(see below).
A unary fxgrep pattern identifies a set of nodes in the document forest as matches of the pattern; we also say that these nodes match the pattern. A matching node is selected by means of the path through its ancestors. The simplest form of a pattern is a sequence of element types, separated by /. It identifies all nodes in the document forest whose ancestors, starting at the document element, form that sequence of element types. For instance, a simple pattern is the following:
/doc/section/title
It identifies all title elements, that are a child of a section element, which is in turn a child of a doc element, which must be the document element.
The / operator denotes the child relationship. Analogously, a // denotes the descendant relation. Therefore, the pattern
/doc//title
identifies all title elements that are descendants of the doc element. At the beginning of the pattern, the / may be omitted, hence we might also write doc//title instead. The // operator may also start a pattern as in
//title
which selects all title elements in the document. For unambiguity, a leading // may never be dropped.
The || operator specifies an alternative. For instance, in order to select all title children of either section or subsection elements, one might specify the pattern
//(section||subsection)/title
Note that this pattern could also be written as //<section|subsection>/title, specifying an alternative of element types for the parent of the title element (see also node patterns). This is not possible in general: Alternatives can relate to whole paths rather than only to a single element. For instance, the following pattern identifies all text elements that are either a child of a caption or a child of a title within a figure element:
//(caption||figure//title)/text
In order to select a text node, a text pattern must be specified as the last component. For instance, the pattern
//title/"airplane"
selects all nodes that are children of a title and contain the word airplane. An arbitrary text pattern can be given between the quotes. If the text pattern contains the quote character, the text pattern can be quoted with the alternative quote character ' (apostrophe), or the quote symbol can be escaped with a \ in the text pattern. Thus, the two patterns
//"O'Hara" and //'O\'Hara'
are equivalent.
With the help of attribute qualifiers, one can also constrain the attributes of an element on the path. For instance, in order to find all title elements that have an attribute toc with value yes, one would use the pattern:
//title[@toc='yes']
An arbitrary text pattern can be specified for the attribute value. If a ~ is used instead of the =, the attribute value need not be exactly yes: It suffices if it contains that word. Also, the attribute name might be specified via a text pattern. An attribute is matched if it contains a substring conforming to the text pattern. If an exact match of the text pattern is desired, the special text pattern operators ^ and $ must be used (see below). For example:
//*[@'title'~'automata']
locate all elements having an attribute containing the substring title with a value containing the substring automata.
An attribute qualifier can be negated by preceding the @ with the negation operator !, requiring that the attribute value must either not be specified, or it must not match the specified text pattern (in case of ~ it must not contain a matching substring). For instance, the attribute qualifier [!@id~"xml"] states that that id attribute, if present, must not contain the word xml.
It is allowed to specify multiple attribute qualifiers for an element which must then all be fulfilled. For instance, the pattern
.[@x][@y='yes'][!@z~"[0-9]"]
selects all elements (. is a node pattern that matches any node) that have an x attribute a y attribute with value yes, and either have no attribute z or the value of that attribute does not contain a digit.
Another method of constraining an element on the path is using a structure qualifier, which imposes a structural constraint on the forest formed by the children of the element. For instance, the pattern
//section[subsection]/title
selects only the titles of those section elements that have a subsection child. Between the brackets, a forest pattern can be specified which is a regular expression over tree patterns. The forest pattern subsection, for instance, requires a sequence of nodes that consists of a node fulfilling the pattern subsection, preceded and followed by an arbitrary sequence of nodes (the preceding and the following arbitrary sequence of nodes can be explicitely prohibited by a leading ^ or trailing $ respectively; the arbitrary sequence of nodes can be explicitely specified as _). In this context, we say that a node fulfills a pattern if it contains at least one match of the pattern, relative to this node. If a pattern occurring in a forest pattern is not just a simple node pattern, it must be enclosed in parentheses, as in
//section[(//'automata theory')]/title
which selects the titles of all section elements that contain the text automata theory. A structure qualifier may be negated, and multiple structure qualifiers may be specified for a node:
//section[!subsection][(//footnote/'automata')]/title
selects the titles of all sections that do not have a child of type subsection and that do have a descendant of type footnote which contains the text automata.
A node pattern describes properties of trees such as element type, attributes and structure. a[@b] is for example a node pattern describing an element a having an attribute b. Properties of paths in document trees are specified by vertical regular expressions. These are built from node patterns which are followed by the operators / or //. (a[@b]/)+ for example, represents a path composed only of nodes a having an attribute b. An fxgrep pattern is composed of a path, possibly empty, followed by a node pattern as in:
(a[@b]/+)c.
which selects the elements c such that all theirs ancestors are a's having an attribute b.
The pattern:
(a[b]/+)c.
selects the elements c such that all theirs ancestors are a's having a child b.
Finally, a context qualifier may be specified for a node on a path. A context qualifier is a regular expression over node patterns and the special symbol #. The # is a place-holder for the child of the node, where the path continues.
For instance, the pattern
a[b#c]/d
requires that the left sibling of the d element must be a b, and its right sibling must be a c.
Consider the pattern:
a[x#y#z]/b
In a document of type a, containing a sequence of x, b, y, b, z elements, this matches the two b's.
A context qualifier can also be used for selecting the first child of an element, for instance in
//section[^#]/subsection
which selects all subsection elements that are the first child of a section.
The last subsection is selected by:
//section[#$]/subsection
Note that it is only sensible to have at most one context qualifier for a node. A context qualifier also makes sense only if there is at least a # symbol in any sequence permitted by the specified regular expression.
Another example for the use of context qualifiers is
a[^(b#)+]/b
which identifies every second b element in a an a (provided that all children of a are of type b).
In the examples so far, matches were specified by indicating a path pattern from the root to the match node. In general it is possible to specify that the path from the root to the match node should simultaneously satisfy more path patterns, or that it must not satisfy a path pattern. For example, the following pattern locates the c nodes that have an a father and some b ancestor:
((//a/)&(//b//))c
Here, the c match node is preceded by a conjunction of two path patterns, separated by &. The first path pattern, //a/, specifies that the match node must have a father a located arbitrarily deep in the input tree and the second, //b//, that it must have a b ancestor located aribtrarily deep in the input.
Similarly, the pattern:
((//a//a//)&!(//a/a/a//))b
locates the b node which have two a ancestors, but do not have three successive a ancestors.
|
Node Tests and Node Patterns |
|
Paths and Patterns |
|
Structure Qualifiers |
|
Context Qualifiers |
|
Context Patterns and Patterns |
A node test describes the allowable element types for a subtree:
|
The node test * is fulfilled by all element types, whereas only elements of type a satisfy the node test a. The node test <a1...ak> is true for an element with type a if a=ai for some i; for <!a1...ak> a must be different from all ai. The node test <"tp"> or <'tp'> is fulfilled by element whose type name contains a substring which fulfills the text pattern tp. If an exact match of the text pattern is desired, the special text pattern operators ^ and $ must be used (see below).
A node pattern describes a structural property of a subtree by means of its element type and its attributes:
|
A text node fulfills a node pattern of the form "tp" or 'tp' if its text contains a substring matching the text pattern tp. Similarly, a node pattern of the form <?tp?> is fulfilled by a processing instruction whose target contains a match of tp. If an exact match of the text pattern is desired, the special text pattern operators ^ and $ must be used (see below).
For elements satisfying a node test, a list of attribute qualifiers can be specified, constraining the element's attributes. Each attribute test given in such a qualifier must be fulfilled unless it is negated by !; in that case it must not be fulfilled. A more general version of the node test * is the dot (.): In addition to arbitrary elements, it also matches text nodes and processing instructions. Note, however, that these kinds of nodes have no attributes; hence it makes no sense to specify attribute qualifiers if text nodes or processing instructions shall be matched.
An attribute test consisting of an attribute name x is fulfilled for an element if it has an attribute with that name. Alternatively the attribute name might be specified as a text pattern enclosed in single or double quotes, case in which the test is fulfilled if it contains a substring fulfilling the pattern. If the attribute test has the form x="tp" or x='tp', then the attribute's value must additionally match the text pattern tp. Slightly less restrictively, the ~ indicates that the attribute value must contain a substring that matches the text pattern.
For instance, the node pattern <a|b>[@x~"1"][!@y] describes all elements of type a or b that have an attribute x whose value contains the digit 1 and which do not have an attribute y. The node pattern <?^fxp-?> describes all processing instructions whose target begins with fxp-. Note that though the @ appears superfluous at this point, it is necessary for distinguishing attribute qualifiers from other forms of qualifiers to be introduced later on.
Path and patterns are composed from node patterns using the operators /, // and ||:
|
A pattern identifies nodes within a tree by describing the path through the ancestors of a node, starting at the root of the tree. Intuitively, operators / and // express the child and descendant relations, respectively, whereas || specifies an alternative. If a pattern p identifies a node n in tree T, then we also say that n matches p in T, or p locates n in T.
The simplest form of a pattern is a node pattern, which locates the root node of T if it satisfies the node pattern. A pattern p1||p2 locates all nodes in T matching either p1 or p2.
If path1 np1 identifies a node n in tree T then path1 np1/path2np2 identifies all subtrees that match path2np2 in a subtree rooted at a child of T.
path1 np1//path2np2 additionally locates all nodes matching path2np2 in an arbitrary subtree of T. Let us illustrate this with some examples:
|
The tree pattern /. always locates the root node in any tree T, whereas //a[@x~"1"] locates all element nodes in T with element type a and an attribute x whose value contains "1". |
|
The tree pattern /a/b//c identifies all element nodes with type c that are descendants of a b element which is itself a child of the root node of T, provided that this node has element type a. |
|
/(a/||b/)c matches all children of T's root that have element type c, if the root node has type a or b; otherwise no nodes match in T. |
|
In a processing instruction whose target starts with fxp-, the tree pattern /<?^fxp-?>/'forest' locates its text, provided the text contains the word forest. |
A pattern which is not starting with / or // is implicitly considered to start with /.
In general, a pattern consists of a conjunction of possibly negated path patterns followed by a node pattern. The match node must than have the structure specified by the node pattern and the path from the root to the match node must match each positive path pattern and must not match any negative path pattern in the conjunction.
The pattern language presented so far can only refer to a single child of a node, but it can not argue about the entirety of a node's children. We therefore add a new kind of qualifiers now: structure qualifiers for constraining the content of an element. These qualifiers can be given as part of a node pattern in addition to the attribute qualifiers presented above. We thus extend the syntax of node patterns:
|
A structure qualifier is a possibly negated forest pattern, which is an extended regular expression over tree patterns. In order to explain the meaning of forest patterns, let us first define a structural match of a tree pattern: A tree pattern tp structurally matches a tree T if some node matches tp in T. A forest pattern (structurally) matches a forest in either of the following cases:
|
The forest pattern |
|
The forest pattern _ matches an arbitrary forest, whereas ~ matches a white-space sequence. A white-space sequence is a possibly empty sequence of processing instructions and text pieces containing only white-space characters. That is, _ is an abbreviation for .**, and ~ is a shorthand for (<??>|"^.*$")**. |
|
A forest pattern consisting of a single tree pattern tp matches a forest that contains a tree structurally matching tp. |
|
A forest pattern fp1|fp2 matches a forest that matches either of the two forest patterns fp1 or fp2 |
|
A forest pattern fp1 fp2 matches a forest, if it is the concatenation of a forest F1, a white-space sequence and a forest F2, such that F1 and F2 match fp1 and fp2, respectively. The form fp1,fp2 is similar, except that no white space is allowed between F1 and F2 |
|
A forest pattern fp? matches
a forest that either matches fp or is the empty forest.
It is an abbreviation for (fp| |
|
A forest pattern fp* matches a concatenation of zero or more forests matching fp, interspersed with white space. The form fp** is analogous, except that no white space is allowed. Similarly, fp+ and fp++ require that at least one forest matching fp is present. |
|
Parentheses are used for disambiguating nested forest patterns. The precedence of operators is such that ?, *, **, + and ++ bind strongest, followed by the concatenation operators, and || has the lowest precedence. A tree pattern occurring in a forest pattern must be parenthesized unless it consists of a single node pattern. |
|
A whole forest pattern implicitly allows a leading and trailing sequence of arbitrary nodes. Preceding the forest pattern with a ^ or succeeding it with a $ prohibits the leading and trailing arbitrary sequence, respectively. These two symbols are only allowed at the start and end of a forest pattern. Prohibiting leading or trailing white spaces too, can be achieved by preceding, respectively following the forest pattern with ^, or ,$. |
Specifying a structure qualifier [fp] for a node pattern np means that the sequence of children of a node must contain a subsequence fulfilling the forest pattern fp. If the qualifier is negated then they must not contain a subsequence that match fp. Specifying two or more structure qualifiers means that all of them must be fulfilled: This is a way of expressing conjunction. For instance:
|
The node pattern a[b[c]] is fulfilled by an a element that has a child b which itself has child with element type c. |
|
The node pattern a[^b[^c$]$] is fulfilled only by an a element that has a single child b which itself has a c element as its only child. |
|
The node pattern a[(b|(//c))][!c] is fulfilled by an element with type a which has either a child labeled b or a descendant labeled c, but it must not have a child of type c, as claimed by the second, negated qualifier. |
|
The node pattern <?^fxp-?>["forest"] is fulfilled by a processing instruction whose target begins with fxp- and whose text contains the word forest. In a structure qualifier for a processing instruction it makes no sense to specify something other than a text pattern because processing instructions contain only text. |
Note the difference between the two tree patterns a/b[c] and a/b/c. Within a structure qualifier, these two patterns are equivalent, because they structurally match the same trees, namely a elements with a b child that has a child c. However, they locate different nodes: a/b[c] identifies the b whereas a/b/c selects the c node.
Structure qualifiers impose regular conditions on the children of a node. But they can not be used for constraining the position of the next node on the path among its siblings. Consider, e.g., the path pattern a/b, and suppose we want to specify that the b must be the first child of the a. This is not possible with a structure qualifier. Even if we ensure that the first child is a b element by specifying a[^b]/b, the pattern still locates all b children of a.
In order to overcome this deficiency, we add another kind of qualifiers: context qualifiers. For a node pattern that can match an element node, one optional context qualifier may be given. Note that it would not make sense to specify a context qualifier for text nodes or processing instructions: A text node has no children, and a processing instruction can only have a single child.
A context qualifier is a qualifier containing at least one occurrence of the # symbol. The # is a place-holder for the child of the node, where the path continues. This can be seen most easily from examples:
|
In an element of type a, the tree pattern a[^#]/b identifies the first child of a if that child has element type b. This is the solution to the introductory example above. |
|
Consider the tree pattern .[^a*#c*$]/b. In an arbitrary tree T, it locates a b child of T's root all of whose left and right siblings are element nodes of type a and c, respectively. |
|
a[^#$]//b locates in an element of type a all descendants with element type b, provided that the a has only a single child. Note that this child may have arbitrarily many children: The context qualifier refers only to the next node on the path and does not extend to its descendants, regardless of operator //. |
Since a context qualifier can occur at any node pattern, it can also be part of a tree pattern within a structure qualifier, e.g., in a[(b[c#c]/b)]. Note however, that such a context qualifier can be replaced by a structure qualifier, in this case yielding a[(b[c b c])].
A pattern identifies nodes in an XML document tree. But the input to the pattern matcher is not a single tree: It is the forest consisting of the document element and all preceding or following processing instructions. We therefore allow the specification of structure and context qualifiers also for this top-level forest. These qualifiers together with a tree pattern form a context pattern. A query pattern is then a disjunction of context patterns.
|
Let us describe the meaning of query patterns by examples:
|
The pattern [a]//b || [c]//d locates all b elements in an XML document whose root element has type a, whereas it locates all d elements if the root element type is c. |
|
[#_<*>]/<??> selects all processing instructions that come before the document element, whereas [<*>_#]/<??> identifies those which come after the document element. |
Let us illustrate the use of patterns by some more sophisticated examples from real-world applications.
The first example queries the XML recommendation. This document defines the syntax of XML by means of an EBNF grammar whose rules are scattered over the whole document. An XML application developer who wants to use this specification as a reference has to search through the document in order to find a rule. In the XML version of the recommendation, each rule is given by a prod element, which has an attribute named id for referral and contains an lhs element and one or more rhs elements. The lhs element specifies the nonterminal defined by this rule; the rhs elements give the right-hand sides for this nonterminal. Each nonterminal occurring on such a right-hand side is represented by an nt element which carries an attribute named def identifying the prod element which defines this nonterminal. For instance, the production for nonterminal document, which is formatted as
[1] document ::= prolog element Misc*
is given by the following element:
<prod id='NT-document'>
<lhs>document</lhs>
<rhs>
<nt def='NT-prolog'>prolog</nt>
<nt def='NT-element'>element</nt>
<nt def='NT-Misc'>Misc</nt>*
</rhs>
</prod>
|
Note that the numbering of productions is done by the formatting process; the XML element therefore does not specify the production's number [1]. Let us now formulate some queries on this document:
|
The pattern //prod[@id='NT-Char'] locates the production whose id attribute is exactly NT-Char, whereas //prod[@id~'Char'] matches all prod elements for which this attribute contains the word Char, thus also allowing CharData or NameChar. |
|
If we don't want to use the id attribute for selecting the productions - perhaps we are not sure whether each prod element really has an id attribute - we can also specify a structure qualifier for the lhs child: //prod[(lhs/'Char')] selects all productions whose lhs child contains a text node with the word Char. |
|
//prod[(lhs['^Char$'])]/rhs selects all rhs children of productions whose left-hand side is exactly Char. |
|
The pattern //prod[^#_(rhs/nt[@def~'Char'])]/lhs/'' locates the text of the left-hand sides of productions whose right-hand sides use a nonterminal whose name contains Char. This can be expressed more shortly - but less precisely - by //prod[(//nt/'Char')]/lhs/''. |
|
In order to find out the number of the production for Char, we can count the matches of the pattern //.[#_(//prod[@id='NT-element'])]//prod and add one. The pattern identifies all prod nodes that come before the production with identifier NT-element in document order. Note that due to the use of operator // for both prod subpatterns, the prod elements need not be siblings. They need only have ancestors which are siblings. |
The second example deals with Shakespeare's play Macbeth. Encoded in XML, a scene basically has a title and contains a sequence of speeches, each represented by a SPEECH element, interspersed with stage directions. Each speech contains a SPEAKER element and a sequence of lines containing plain text, for instance:
<SCENE>
<TITLE>SCENE I. A desert place.</TITLE>
<STAGEDIR>Thunder and lightning. Enter three Witches</STAGEDIR>
<SPEECH>
<SPEAKER>First Witch</SPEAKER>
<LINE>When shall we three meet again</LINE>
<LINE>In thunder, lightning, or in rain?</LINE>
</SPEECH>
<SPEECH>
<SPEAKER>Second Witch</SPEAKER>
<LINE>When the hurlyburly's done,</LINE>
<LINE>When the battle's lost and won.</LINE>
</SPEECH>
<SPEECH>
<SPEAKER>Third Witch</SPEAKER>
<LINE>That will be ere the set of sun.</LINE>
</SPEECH>
...
<STAGEDIR>Exeunt</STAGEDIR>
</SCENE>
|
Let us formulate some queries for this document:
|
The pattern //SPEECH[(LINE/'thunder')] selects all speeches containing a line with the word thunder. |
|
The pattern //SPEECH[(//LINE/'hurlyburly')]/SPEAKER/. selects the speaker of a line containing the word hurlyburly, that is, the Second Witch. |
|
The same result is produced by the following pattern, using a context qualifier: //SPEECH[#_(LINE/'hurlyburly')]/SPEAKER/. This pattern is much more precise than the previous one: It requires that the SPEAKER precedes the LINE elements within a SPEECH, and that the LINE elements are direct descendants, i.e., children of the SPEECH node. |
|
The pattern //SPEECH[(SPEAKER/'Second Witch')_#]/LINE/'' locates all text nodes in lines spoken by the second witch. |
|
The pattern //SPEECH[(LINE/'hurlyburly')#]/LINE identifies the line immediately following after the line containing hurlyburly, whereas //*[(SPEECH//'hurlyburly')#]/SPEECH/SPEAKER selects the speaker who responds to that speech, that is, the Third Witch. |
|
//*[^<!ACT>*#]/ACT[^<!SCENE>*#]/SCENE/TITLE/'' selects the title text of the first scene in the first act, namely: SCENE I. A desert place. |
|
//SCENE[(//SPEAKER/'Witch')][(//SPEAKER/'MACBETH')]/TITLE locates the titles of scenes in which both Macbeth and a witch speak. |
|
//SCENE[(TITLE/'desert')]/*[!(SPEAKER/'Witch')]/LINE locates the lines in a scene whose title contains the word desert, which are not spoken by a witch. |
The following grammar summarizes the syntax of unary patterns:
|
Observe that in order to avoid syntactical ambiguities, a pattern occurring in a forest pattern must be enclosed in parentheses unless it is just a node pattern. Otherwise the forest pattern /a /b could be interpreted as a sequence of patterns /a and /b, or as the single pattern /a/b.
Rather than finding individual nodes in the input, it is sometimes useful to identify pairs of nodes which are related in some specified way. This is done via the so called binary queries.
Consider for example the following unary pattern which identifies all the book titles whose author's names end in "escu":
//book[(author/"escu$")]/title
Suppose we want to identify the titles as above, but together with the authors of the books with these titles. We specify that we want to locate a title node as with the pattern above. In order to locate the author node corresponding to a title, we need to specify the relation between the two nodes. We specify this in a simple, intuitive way, by preceding the node pattern of the second node in the relation by the symbol %. Thus, the binary query which simultaneously reports the authors having names ending in "escu" and the titles of their books is:
//book[(%author/"escu$")]/title
Strictly speaking, a binary match is a pair. The first node in the pair is called primary match. The second node in the pair is a node related to the first node as specified by the % symbol and is called secondary match. In practice, however, rather than reporting each primary and secondary match separately, if there are more secondary matches related to a same primary match, they are reported at once together with the primary match. The output is a sequence of match XML elements containing a primary element for each primary match, followed by a sequence of secondary elements for each corresponding secondary match. Each of these elements contains a position and a node element reporting the position in the input file and the node itself respectively.
The binary query above considered over the input file input.xml:
<library>
<book>
<author>Mihai Eminescu</author>
<price>10</price>
<title>Blaue Blume</title>
</book>
<book>
<author>Paulo Coelho</author>
<title>Veronika changes her mind</title>
</book>
</library>
|
produces:
<match>
<primary>
<position>[input.xml:5.5]</position>
<node><title>Blaue Blume</title></node>
</primary>
<secondary>
<position>[input.xml:3.5]</position>
<node><author>Mihai Eminescu</author></node>
</secondary>
</match>
|
It is possible to locate a primary match together with more than one set of related nodes. Each set of related nodes is specified by a % symbol preceding the corresponding node pattern. The sets of secondary matches are then reported in the order of the occurrences of the % symbols in the pattern.
The query
//book[%price][(%author/"escu$")]/titleidentifies the title of the books which have a price indication and whose author name end in "escu", together with this price and the author:
<match>
<primary>
<position>[input.xml:5.5]</position>
<node><title>Blaue Blume</title></node>
</primary>
<secondary>
<position>[input.xml:4.5]</position>
<node><price>10</price></node>
</secondary>
<secondary>
<position>[input.xml:3.5]</position>
<node><author>Mihai Eminescu</author></node>
</secondary>
</match>
|
The binary query (%a/)+b locates the nodes b whose ancestors are only a nodes, together with these ancestors. For the input file test1.xml:
<a>
<a>
<b/>
</a>
</a>
|
fxgrep reports:
<match>
<primary>
<position>[test1.xml:3.5]</position>
<node><b></b></node>
</primary>
<secondary>
<position>[test1.xml:1.1]</position>
<node><a>
<a>
<b></b>
</a>
</a></node>
</secondary>
<secondary>
<position>[test1.xml:2.3]</position>
<node><a>
<b></b>
</a></node>
</secondary>
</match>
|
Any node pattern can be specified as a secondary match by simply preceding it by the percent symbol. Otherwise, the syntax of binary patterns is exactly as in the case of unary patterns, except the production for NodePattern which is replaced by:
|