Primitive node constructors
parent
08a43bdd78
commit
77ff279139
|
@ -10,7 +10,8 @@
|
||||||
@copying
|
@copying
|
||||||
This manual is for hoxsl, version @value{VERSION}.
|
This manual is for hoxsl, version @value{VERSION}.
|
||||||
|
|
||||||
Copyright @copyright{} 2014 LoVullo Associates, Inc.
|
Copyright @copyright{} 2014, 2015 LoVullo Associates, Inc.
|
||||||
|
Copyright @copyright{} 2015 Mike Gerwitz
|
||||||
|
|
||||||
@quotation
|
@quotation
|
||||||
Permission is granted to copy, distribute and/or modify this document
|
Permission is granted to copy, distribute and/or modify this document
|
||||||
|
@ -45,6 +46,7 @@ Free Documentation License".
|
||||||
|
|
||||||
@menu
|
@menu
|
||||||
* Higher-Order Functions:: XSLT 2.0 compatible higher-order functions
|
* Higher-Order Functions:: XSLT 2.0 compatible higher-order functions
|
||||||
|
* Node Constructors:: Constructing nodes functionally
|
||||||
* License:: Document License
|
* License:: Document License
|
||||||
@end menu
|
@end menu
|
||||||
|
|
||||||
|
@ -57,6 +59,10 @@ Free Documentation License".
|
||||||
@chapter Higher-Order Functions
|
@chapter Higher-Order Functions
|
||||||
@include ../src/apply.texi
|
@include ../src/apply.texi
|
||||||
|
|
||||||
|
@node Node Constructors
|
||||||
|
@chapter Node Constructors
|
||||||
|
@include ../src/node.texi
|
||||||
|
|
||||||
@include license.texi
|
@include license.texi
|
||||||
|
|
||||||
@bye
|
@bye
|
||||||
|
|
|
@ -0,0 +1,209 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--@comment
|
||||||
|
Node constructors
|
||||||
|
|
||||||
|
Copyright (C) 2015 Mike Gerwitz
|
||||||
|
|
||||||
|
This file is part of hoxsl.
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<stylesheet version="2.0"
|
||||||
|
xmlns="http://www.w3.org/1999/XSL/Transform"
|
||||||
|
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||||
|
xmlns:n="http://www.lovullo.com/hoxsl/node"
|
||||||
|
xmlns:_n="http://www.lovullo.com/hoxsl/node/_priv">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Function composition becomes a frustrating endeavor if nodes cannot be
|
||||||
|
constructed within an expression. For example, to create an element
|
||||||
|
to pass to a function @code{foo}, the only option with vanilla XSLT is to
|
||||||
|
use a variable to hold that element:
|
||||||
|
|
||||||
|
@example
|
||||||
|
<variable name="element" as="element( bar )">
|
||||||
|
<bar baz="quux" />
|
||||||
|
</variable>
|
||||||
|
|
||||||
|
<sequence select="foo( $element )" />
|
||||||
|
@end example
|
||||||
|
|
||||||
|
This easily interrupts pipelines or results in functions whose sole
|
||||||
|
purpose is to create specific nodes for the sake of composition.
|
||||||
|
|
||||||
|
Hoxsl provides constructors for nodes that fit cleanly into a
|
||||||
|
functional system.
|
||||||
|
|
||||||
|
@menu
|
||||||
|
* Primitive Constructors:: Functional equivalents of XSLT node primitives
|
||||||
|
* Private Functions:Node Private Functions. Internal details
|
||||||
|
@end menu
|
||||||
|
|
||||||
|
|
||||||
|
@node Primitive Constructors
|
||||||
|
@section Primitive Constructors
|
||||||
|
The constructors in this section can be thought of as primitives
|
||||||
|
corresponding to their XSLT node-based counterparts: @code{xsl:element},
|
||||||
|
@code{xsl:text}, and @code{xsl:comment}. They can be arbitrarily nested
|
||||||
|
to create tree structures.
|
||||||
|
|
||||||
|
@float Figure, fig:node-primitive
|
||||||
|
@verbatim
|
||||||
|
<sequence select="n:element( QName( 'ns', 'foo' ),
|
||||||
|
( QName( 'ns', 'attr1' ), 'value1',
|
||||||
|
QName( 'ns', 'attr2' ), 'value2' ),
|
||||||
|
( n:text( 'Nest to create trees' ),
|
||||||
|
n:comment( 'functional nodes' ),
|
||||||
|
n:element( QName( 'ns', 'bar' ) ) ) )" />
|
||||||
|
@end verbatim
|
||||||
|
@caption{Generating an XML tree using the primitive constructors}
|
||||||
|
@end float
|
||||||
|
|
||||||
|
Consider @ref{fig:node-primitive}, which will output the following tree:
|
||||||
|
|
||||||
|
@example
|
||||||
|
<foo attr1="value1" attr2="value2">@c
|
||||||
|
Nest to create trees@c
|
||||||
|
@xmlcomment{functional nodes}@c
|
||||||
|
<bar />@c
|
||||||
|
</foo>
|
||||||
|
@end example
|
||||||
|
|
||||||
|
Newlines are not automatically added.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Construct an element named @var{qname} with attributes defined by
|
||||||
|
the QName-value pairs @var{attr-pairs} and child nodes @var{children}. An
|
||||||
|
empty sequence may be provided if no attributes or children are desired
|
||||||
|
(see also the @ref{n:element#1,,unary} and @ref{n:element#2,,binary}
|
||||||
|
overloads).
|
||||||
|
|
||||||
|
For QName-value pair @var{attr-pairs}, @math{2n} will always be considered
|
||||||
|
to be a QName and @math{2n+1} its associated value. If the final value in
|
||||||
|
the attribute pair sequence is missing, then it will result in an
|
||||||
|
attribute with an empty string as its value.
|
||||||
|
-->
|
||||||
|
<function name="n:element" as="element()">
|
||||||
|
<param name="qname" as="xs:QName" />
|
||||||
|
<param name="attr-pairs" as="item()*" />
|
||||||
|
<param name="child-nodes" as="node()*" />
|
||||||
|
|
||||||
|
<variable name="element" as="element()">
|
||||||
|
<element name="{$qname}"
|
||||||
|
namespace="{namespace-uri-from-QName( $qname ) }">
|
||||||
|
<sequence select="_n:attr-from-pair( $attr-pairs ),
|
||||||
|
$child-nodes" />
|
||||||
|
</element>
|
||||||
|
</variable>
|
||||||
|
|
||||||
|
<sequence select="$element" />
|
||||||
|
</function>
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Construct an element named @var{qname} with attributes defined by
|
||||||
|
the QName-value pairs @var{attr-pairs} and no child nodes. An empty
|
||||||
|
sequence may be provided if no attributes are desired (see also the
|
||||||
|
@ref{n:element#1,,unary} overload).
|
||||||
|
|
||||||
|
This is equivalent to @code{n:element( $qname, $attr-pairs, () )}; see
|
||||||
|
@ref{n:element#3}.
|
||||||
|
-->
|
||||||
|
<function name="n:element" as="element()">
|
||||||
|
<param name="qname" as="xs:QName" />
|
||||||
|
<param name="attr-pairs" as="item()*" />
|
||||||
|
|
||||||
|
<sequence select="n:element( $qname, $attr-pairs, () )" />
|
||||||
|
</function>
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Construct an element named @var{qname} with no attributes or children.
|
||||||
|
|
||||||
|
This is equivalent to @code{n:element( $qname, (), () )}; see
|
||||||
|
@ref{n:element#3}.
|
||||||
|
-->
|
||||||
|
<function name="n:element" as="element()">
|
||||||
|
<param name="qname" as="xs:QName" />
|
||||||
|
|
||||||
|
<sequence select="n:element( $qname, () )" />
|
||||||
|
</function>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Create a text node with the given @var{text}. The @var{text} will be
|
||||||
|
output verbatim without any whitespace processing.
|
||||||
|
-->
|
||||||
|
<function name="n:text" as="text()">
|
||||||
|
<param name="text" as="xs:string" />
|
||||||
|
|
||||||
|
<variable name="text-node" as="text()">
|
||||||
|
<value-of select="$text" />
|
||||||
|
</variable>
|
||||||
|
|
||||||
|
<sequence select="$text-node" />
|
||||||
|
</function>
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Create a comment node with the given @var{text}. The @var{text} will be
|
||||||
|
output verbatim without any whitespace processing.
|
||||||
|
-->
|
||||||
|
<function name="n:comment" as="comment()">
|
||||||
|
<param name="text" as="xs:string" />
|
||||||
|
|
||||||
|
<variable name="comment" as="comment()">
|
||||||
|
<comment select="$text" />
|
||||||
|
</variable>
|
||||||
|
|
||||||
|
<sequence select="$comment" />
|
||||||
|
</function>
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
@node Node Private Functions
|
||||||
|
@section Private Functions
|
||||||
|
These functions support the various node functions, but should not be used
|
||||||
|
outside of Hoxsl itself.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Construct attributes from a list of QName-value pairs.
|
||||||
|
|
||||||
|
For more information on the pair format, see @ref{n:element#3}.
|
||||||
|
-->
|
||||||
|
<function name="_n:attr-from-pair" as="attribute()*">
|
||||||
|
<param name="attr-pairs" as="item()*" />
|
||||||
|
|
||||||
|
<variable name="attribute" as="attribute()">
|
||||||
|
<variable name="qname" as="xs:QName"
|
||||||
|
select="$attr-pairs[ 1 ]" />
|
||||||
|
<variable name="value" as="xs:anyAtomicType?"
|
||||||
|
select="$attr-pairs[ 2 ]" />
|
||||||
|
|
||||||
|
<attribute name="{$qname}"
|
||||||
|
namespace="{namespace-uri-from-QName( $qname ) }"
|
||||||
|
select="$value" />
|
||||||
|
</variable>
|
||||||
|
|
||||||
|
<sequence select="if ( exists( $attr-pairs ) ) then
|
||||||
|
( $attribute,
|
||||||
|
_n:attr-from-pair(
|
||||||
|
subsequence( $attr-pairs, 3 ) ) )
|
||||||
|
else
|
||||||
|
()" />
|
||||||
|
</function>
|
||||||
|
|
||||||
|
</stylesheet>
|
|
@ -0,0 +1,243 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Tests node constructors
|
||||||
|
|
||||||
|
Copyright (C) 2015 Mike Gerwitz
|
||||||
|
|
||||||
|
This file is part of hoxsl.
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<description xmlns="http://www.jenitennison.com/xslt/xspec"
|
||||||
|
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||||
|
xmlns:x="http://www.jenitennison.com/xslt/xspec"
|
||||||
|
xmlns:n="http://www.lovullo.com/hoxsl/node"
|
||||||
|
xmlns:foo="http://www.lovullo.com/_junk"
|
||||||
|
xmlns:test-prefix-a="test-ns-a"
|
||||||
|
stylesheet="../src/node.xsl">
|
||||||
|
|
||||||
|
<variable name="test-qname-a"
|
||||||
|
select="QName( 'test-ns-a', 'test-prefix-a:a' )" />
|
||||||
|
<variable name="test-qname-b"
|
||||||
|
select="QName( 'test-ns-b', 'test-prefix-b:b' )" />
|
||||||
|
<variable name="test-qname-c"
|
||||||
|
select="QName( 'test-ns-c', 'test-prefix-c:c' )" />
|
||||||
|
|
||||||
|
<variable name="test-node-element" as="element()">
|
||||||
|
<foo:node-a foo="bar">
|
||||||
|
<node-child />
|
||||||
|
</foo:node-a>
|
||||||
|
</variable>
|
||||||
|
|
||||||
|
<variable name="test-node-comment" as="comment()">
|
||||||
|
<!-- comment -->
|
||||||
|
</variable>
|
||||||
|
|
||||||
|
<!-- whitespace intentional -->
|
||||||
|
<variable name="test-text-a"
|
||||||
|
select="' This is some test text '" />
|
||||||
|
<variable name="test-text-b"
|
||||||
|
select="'Some more test text'" />
|
||||||
|
|
||||||
|
<variable name="test-value"
|
||||||
|
select="5" />
|
||||||
|
|
||||||
|
|
||||||
|
<scenario label="A new element">
|
||||||
|
<scenario label="with only a QName">
|
||||||
|
<call function="n:element">
|
||||||
|
<param name="qname" select="$test-qname-a" />
|
||||||
|
</call>
|
||||||
|
|
||||||
|
<expect label="produces an element"
|
||||||
|
test="$x:result instance of element()" />
|
||||||
|
|
||||||
|
<expect label="produces an element with the given QName"
|
||||||
|
test="node-name( $x:result ) = $test-qname-a" />
|
||||||
|
|
||||||
|
<expect label="produces an element with no attributes"
|
||||||
|
test="empty( $x:result/@* )" />
|
||||||
|
|
||||||
|
<expect label="produces an element with no children"
|
||||||
|
test="empty( $x:result/node() )" />
|
||||||
|
</scenario>
|
||||||
|
|
||||||
|
|
||||||
|
<scenario label="with a sequence of N attributes">
|
||||||
|
<call function="n:element">
|
||||||
|
<param name="qname" select="$test-qname-b" />
|
||||||
|
|
||||||
|
<param name="attr-pairs"
|
||||||
|
select="( $test-qname-a, 'a',
|
||||||
|
$test-qname-c, 'c' )" />
|
||||||
|
</call>
|
||||||
|
|
||||||
|
<expect label="produces an element"
|
||||||
|
test="$x:result instance of element()" />
|
||||||
|
|
||||||
|
<expect label="produces an element with the given QName"
|
||||||
|
test="node-name( $x:result ) = $test-qname-b" />
|
||||||
|
|
||||||
|
<expect label="produces an element with no children"
|
||||||
|
test="empty( $x:result/node() )" />
|
||||||
|
|
||||||
|
<expect label="produces an element with N attributes"
|
||||||
|
test="count( $x:result/@* ) = 2" />
|
||||||
|
|
||||||
|
<expect label="produces an element with proper attribute QName/value"
|
||||||
|
test="( some $x in $x:result/@* satisfies
|
||||||
|
( node-name( $x ) = $test-qname-a
|
||||||
|
and $x = 'a' ) )
|
||||||
|
and ( some $x in $x:result/@* satisfies
|
||||||
|
( node-name( $x ) = $test-qname-c
|
||||||
|
and $x = 'c' ) )" />
|
||||||
|
</scenario>
|
||||||
|
|
||||||
|
|
||||||
|
<scenario label="with an attribute missing a value">
|
||||||
|
<call function="n:element">
|
||||||
|
<param name="qname" select="$test-qname-b" />
|
||||||
|
|
||||||
|
<param name="attr-pairs"
|
||||||
|
select="( $test-qname-a )" />
|
||||||
|
</call>
|
||||||
|
|
||||||
|
<expect label="produces an attribute with an empty string value"
|
||||||
|
test="$x:result/@test-prefix-a:a = ''" />
|
||||||
|
</scenario>
|
||||||
|
|
||||||
|
|
||||||
|
<scenario label="with child nodes">
|
||||||
|
<call function="n:element">
|
||||||
|
<param name="qname" select="$test-qname-b" />
|
||||||
|
<param name="attr-pairs" select="()" />
|
||||||
|
|
||||||
|
<param name="child-nodes"
|
||||||
|
select="$test-node-element,
|
||||||
|
$test-node-comment" />
|
||||||
|
</call>
|
||||||
|
|
||||||
|
<expect label="produces an element"
|
||||||
|
test="$x:result instance of element()" />
|
||||||
|
|
||||||
|
<expect label="produces an element with the given QName"
|
||||||
|
test="node-name( $x:result ) = $test-qname-b" />
|
||||||
|
|
||||||
|
<expect label="produces an element with N children"
|
||||||
|
test="count( $x:result/node() ) = 2" />
|
||||||
|
|
||||||
|
<expect label="includes exact child nodes"
|
||||||
|
test="( some $x in $x:result/node() satisfies
|
||||||
|
deep-equal( $x, $test-node-element ) )
|
||||||
|
and ( some $x in $x:result/node() satisfies
|
||||||
|
deep-equal( $x, $test-node-comment ) )" />
|
||||||
|
</scenario>
|
||||||
|
</scenario>
|
||||||
|
|
||||||
|
|
||||||
|
<scenario label="A new comment">
|
||||||
|
<scenario label="standalone">
|
||||||
|
<call function="n:comment">
|
||||||
|
<param name="text" select="$test-text-a" />
|
||||||
|
</call>
|
||||||
|
|
||||||
|
<expect label="produces a comment node"
|
||||||
|
test="$x:result instance of comment()" />
|
||||||
|
|
||||||
|
<expect label="contains given text verbatim"
|
||||||
|
test="$x:result = $test-text-a" />
|
||||||
|
</scenario>
|
||||||
|
|
||||||
|
|
||||||
|
<scenario label="within an element">
|
||||||
|
<call function="n:element">
|
||||||
|
<param name="name" select="QName( '', 'foo' )" />
|
||||||
|
<param name="attr-pairs" select="()" />
|
||||||
|
|
||||||
|
<param name="child-nodes"
|
||||||
|
select="n:comment( $test-text-a ),
|
||||||
|
n:comment( $test-text-b )" />
|
||||||
|
</call>
|
||||||
|
|
||||||
|
<expect label="can be produced N times"
|
||||||
|
test="count( $x:result/comment() ) = 2" />
|
||||||
|
|
||||||
|
<expect label="results in no other child nodes"
|
||||||
|
test="count( $x:result/node() ) = 2" />
|
||||||
|
|
||||||
|
<expect label="contains given text verbatim, ordered"
|
||||||
|
test="$x:result/comment()[1] = $test-text-a
|
||||||
|
and $x:result/comment()[2] = $test-text-b" />
|
||||||
|
</scenario>
|
||||||
|
</scenario>
|
||||||
|
|
||||||
|
|
||||||
|
<scenario label="A new text node">
|
||||||
|
<scenario label="standalone">
|
||||||
|
<call function="n:text">
|
||||||
|
<param name="text" select="$test-text-a" />
|
||||||
|
</call>
|
||||||
|
|
||||||
|
<expect label="produces a text node"
|
||||||
|
test="$x:result instance of text()" />
|
||||||
|
|
||||||
|
<expect label="contains given text verbatim"
|
||||||
|
test="$x:result = $test-text-a" />
|
||||||
|
</scenario>
|
||||||
|
|
||||||
|
|
||||||
|
<scenario label="adjacent to text within an element">
|
||||||
|
<call function="n:element">
|
||||||
|
<param name="name" select="QName( '', 'foo' )" />
|
||||||
|
<param name="attr-pairs" select="()" />
|
||||||
|
|
||||||
|
<param name="child-nodes"
|
||||||
|
select="n:text( $test-text-a ),
|
||||||
|
n:text( $test-text-b )" />
|
||||||
|
</call>
|
||||||
|
|
||||||
|
<expect label="contains given text verbatim, ordered"
|
||||||
|
test="$x:result/text()[1] =
|
||||||
|
concat( $test-text-a, $test-text-b )" />
|
||||||
|
|
||||||
|
<!-- they're adjacent, and so combined into a single node -->
|
||||||
|
<expect label="results in no other child nodes"
|
||||||
|
test="count( $x:result/node() ) = 1" />
|
||||||
|
</scenario>
|
||||||
|
|
||||||
|
|
||||||
|
<scenario label="separated by other nodes within an element">
|
||||||
|
<call function="n:element">
|
||||||
|
<param name="name" select="QName( '', 'foo' )" />
|
||||||
|
<param name="attr-pairs" select="()" />
|
||||||
|
|
||||||
|
<param name="child-nodes"
|
||||||
|
select="n:text( $test-text-a ),
|
||||||
|
$test-node-element,
|
||||||
|
n:text( $test-text-b )" />
|
||||||
|
</call>
|
||||||
|
|
||||||
|
<expect label="can produce N separate text nodes"
|
||||||
|
test="count( $x:result/text() ) = 2" />
|
||||||
|
|
||||||
|
<expect label="contains given text verbatim, ordered"
|
||||||
|
test="$x:result/text()[1] = $test-text-a
|
||||||
|
and $x:result/text()[2] = $test-text-b" />
|
||||||
|
|
||||||
|
<expect label="results in no other child nodes"
|
||||||
|
test="count( $x:result/node() ) = 3" />
|
||||||
|
</scenario>
|
||||||
|
</scenario>
|
||||||
|
</description>
|
Loading…
Reference in New Issue