Variables and Expressions


Variables and Expressions

The system of data types lies at the core of any language, and the way expressions are used to compute values and assign these to variables is closely tied up with the type system. The type system for XSLT is actually defined by the XPath language, and is fully explained in XPath 2.0 Programmer's Reference (in particular, Chapter 3). But here it's appropriate to look at how XSLT makes use of this type system. So this section will look in more detail at these aspects of the language.

The more sophisticated aspects of the XSLT type system interact closely with types as defined in XML Schema. We'll look in more detail in Chapter 4 at how stylesheets and schemas interact.

Variables

XSLT allows global variables to be defined, which are available throughout the whole stylesheet, as well as local variables, which are available only within a particular sequence constructor. The name and value of a variable are defined in an <xsl:variable> element. For example:

  <xsl:variable name="width" select="50" as="xs:integer"/>  

This defines a variable whose name is width and whose value is the number 50. The variable can subsequently be referenced in an XPath expression as $width . If the <xsl:variable> element appears at the top level of the stylesheet (as a child of the <xsl:stylesheet> element) then it declares a global variable; if it appears within the body of an <xsl:template> or <xsl:function> element then it defines a local variable.

The use of variables is superficially very similar to their use in conventional programming and scripting languages. They even have similar scoping rules. However, there is one key difference: once a value has been given to a variable, it cannot be changed. This difference has a profound impact on the way programs are written, so it is discussed in detail in the section Programming without Assignment Statements in Chapter 9, page 625.

The as attribute is optional, and defines the data type of the variable as being an integer. In this example, this doesn't add much: you can tell that it's an integer by looking at the value, and so can the XSLT processor. But there are cases where it's useful to specify the type. The select attribute doesn't have to be a constant, as it is in the previous example-it might, for example, be a call on a function. The as attribute acts both as an assertion about the type of the value (if the value is of the wrong type, you'll see an error message, either at compile time or at runtime) and also as a request to perform certain limited conversions from the supplied value to the specified type.

Broadly speaking, the type conversions that are possible in XSLT can be categorized as strong conversions and weak conversions. In a context like this, only weak conversions are applied. The weak conversions include the following:

  • Treating a value of type T as a value of a supertype of T, for example an xs:integer as an xs:decimal , or an xs:ID as an xs:string .

  • Extracting the typed value of a node, in cases where the supplied value is a reference to a node and the required type is atomic. This process is called atomization. The typed value of a node has the type determined by schema validation, for example if the node has been validated as an integer, you can use its value where an integer is expected, but not where (say) an xs:anyURI is expected. If there is no schema, then the typed value is the same as the string value.

  • Numeric promotion of an xs:integer or xs:decimal to an xs:float or xs:double , and of an xs:float to an xs:double . In XML Schema, xs:float is not defined as a subtype of xs:double , but XPath and XSLT behave largely as if it were.

  • Conversion of untyped atomic values (which usually arise as the values of nodes in documents, or parts of documents, that have not been schema-validated) to the required type. This conversion uses the rules defined in XML Schema: for example, if the required type is xs:date , then the supplied value must have the correct lexical form for an xs:date , as defined in the XML Schema specifications (that is, YYYY-MM-DD with an optional timezone).

These weak conversions are applied to the values of variables and parameters, and they are also used when converting the arguments supplied in a function call to the types declared in the function signature.

Strong conversions can be achieved by use of constructor functions, for example you can convert a string to an integer with the function «xs:integer ($s) » . Generally in XSLT 2.0, strong conversions are not applied automatically; you have to ask for them. Strong conversion is also referred to as casting, and the rules for casting are given in Chapter 9 of XPath 2.0 Programmer's Reference .

In the case of function calls, strong conversions are applied implicitly if you run the stylesheet in backwards-compatible mode. This is described in Chapter 3 on page 126. You can select backwards -compatible mode by specifying «version="1.0" » on the <xsl:stylesheet> element. However, even in backwards-compatible mode strong conversions are not applied to variables and parameters in the stylesheet. This is because in XSLT 1.0 it was not possible to declare the type of a variable or parameter, so there is no requirement here for backwards compatibility.

Parameters

Parameters in XSLT are very similar to variables. They are declared with an <xsl:param> element instead of <xsl:variable> , and they differ in that the value can be supplied externally. There are three places you can use parameters in a stylesheet:

  • Stylesheet parameters (also known as global parameters) are supplied when the transformation is invoked, and they can be referenced from anywhere in the stylesheet. They are set from outside the stylesheet (for example, from the command line or from an API-the actual mechanism is implementation defined).

  • Template parameters are defined within an <xsl:template> element, and are available only during the evaluation of that template. They are set by means of <xsl: with-param > elements within the instruction (for example <xsl:call-template> or <xsl:apply-templates> ) that invokes the template. These parameters can take different values each time the template is invoked.

  • Function parameters are defined within an <xsl:function> element, and are available only during the evaluation of that function. Functions are very similar to templates, except that they are invoked not by means of XSLT instructions, but by evaluating a function call within an XPath expression. The parameters to a function are supplied as part of this function call.

As with variables, the expected type of a parameter can be declared using an «as » attribute. Here it's much more useful to declare the expected type, because you can then fail cleanly if the caller supplies the wrong type of value, rather than crashing or producing wrong answers. It's also very valuable to the XSLT compiler to know in advance what type of value will be supplied, because it means that it can generate tighter code that doesn't have to deal with as many runtime possibilities. You don't have to declare the types of parameters, and if you don't, then any type of value will be accepted-but I would recommend it as good programming practice always to declare the types. I certainly find that it catches many of my sillier programming mistakes.

Data Types

The data type of a variable or parameter is described using the XPath 2.0 construct called SequenceType . The full syntax is explored in XPath 2.0 Programmer's Reference .

Every value in the XPath 2.0 data model is a sequence of items, and the SequenceType syntax is therefore in two parts: an ItemType , which describes the allowed content of each item, and an OccurrenceIndicator , which specifies how many items are allowed in the sequence. The possible occurrence indicators are «* » meaning any number of items (zero or more), «? » meaning zero or one item, and «+ » meaning one or more items. If there is no occurrence indicator, then the sequence must contain exactly one item.

The values for ItemType that every XSLT processor is obliged to recognize (even if it doesn't support schema-aware stylesheets) are listed in the following table.

Item Type

Allowed Values

item()

Any item

node()

Any node

document-node()

Any document node

element()

Any element node. If a name is supplied within the parentheses, only elements with that name are allowed. A schema-aware processor allows further options to control which types of element node are allowed: see Chapter 4 for details.

attribute()

Any attribute node. If a name is supplied within the parentheses, only attributes with that name are allowed. A schema-aware processor allows further options to control which types of attribute node are allowed: see Chapter 4 for details.

text()

Any text node

comment()

Any comment node

processing-instruction()

Any processing instruction node. If a name is supplied within the parentheses, only processing instructions with that name are allowed.

xdt:anyAtomicType

Any atomic value

xs:boolean

A boolean value (true or false)

xs:string

A string of characters

xs:decimal

A decimal number

xs:integer

An integer (integers are considered to be a subtype of decimal numbers )

xs:double

A double-precision floating point number

xs:dateTime

A date and time

xs:date

A date

xs:time

A time

xs:Qname

A namespace-qualified name. Note that the value of an xs:QName contains the namespace URI and the local name, it does not contain a prefix.

xs:anyURI

A URI

xdt:untypedAtomic

An untyped atomic value, usually obtained by extracting the typed value of a node that has not been schema validated

xdt:dayTimeDuration

A duration expressed in days, hours, minutes, and seconds (and possibly fractions of a second)

xdt:yearMonthDuration

A duration expressed in years and months

The prefixes «xs » and «xdt » in this list are conventional. You can use any prefix that has been declared with the correct namespace URI (some people prefer to use «xsd » for the schema namespace). The «xs » prefix represents the namespace URI http://www.w3.org/2001/XMLSchema , while the «xdt » prefix represents a namespace that is given in the current draft specifications as http://www.w3.org/2003/11/xpath-data types, but this is likely to change in the final Recommendation.

Most of the time you can get by using only these data types. But if you are using a schema-aware XSLT processor, then you can also use the additional data types defined in the XML Schema Recommendation (for example, xs:positiveInteger or xs:hexBinary), as well as your own user -defined data types defined in a schema that has been imported into the stylesheet using an <xsl:import-schema> declaration. More on this in Chapter 4.

Expressions

The syntax of expressions is defined in the XPath Recommendation, and is described in detail in XPath 2.0 Programmer's Reference .

XPath expressions are used in a number of contexts in an XSLT stylesheet. They are used as attribute values for many XSLT elements, for example:

  <xsl:value-of select="($x + $y) * 2"/>  

In this example $x and $y are references to variables, and the operators «+ » and «* » have their usual meanings of addition and multiplication.

Many XPath expressions, like this one, follow a syntax that is similar to other programming languages. The one that stands out, however, and the one that gave XPath its name, is the path expression.

A path expression defines a navigation path through the document tree. Starting at a defined origin, usually either the current node or the root, it follows a sequence of steps in defined directions. At each stage the path can branch, so for example you can find all the attributes of all the children of the origin node. The result is always a sequence of nodes in a fixed order (known as document order) with no duplicates. It might be empty or contain only one node, but it is still treated as a sequence.

The directions of navigation through the tree are called axes. The various axes are defined in detail in Chapter 7 of XPath 2.0 Programmer's Reference . They include the following:

  • The child axis, which finds all the children of a node.

  • The attribute axis, which finds all the attributes of a node.

  • The ancestor axis, which finds all the ancestors of a node.

  • The following-sibling axis, which finds the nodes that come after this one and share the same parent.

  • The preceding -sibling axis, which finds the nodes that come before this one and share the same parent.

As well as specifying the direction of navigation through the tree, each step in a path expression can also qualify which nodes are to be selected. This can be done in several different ways:

  • By specifying the name of the nodes (completely or partially).

  • By specifying the kind of nodes (for example, elements or processing instructions).

  • By specifying the schema-defined type of the nodes (for example, elements of type person , or attributes of type xs:date ).

  • By defining a predicate that the nodes must satisfy -an arbitrary boolean expression.

  • By defining the relative position of the node along the axis: for example, it is possible to select only the immediately preceding sibling.

The syntax of a path expression uses « / » as an operator to separate the successive steps. A « / » at the start of a path expression indicates that the origin is the document node; otherwise it is generally the context node (we'll be looking at the notion of context in the next section). Within each step, the axis is written first, separated from the other conditions by the separator «:: » . However, the child axis is the default, so it may be omitted; and the attribute axis may be abbreviated to «@ » .

For example:

  child::item/attribute::category  

is a path expression of two steps, the first selects all the child <item> elements of the current node, and the second step selects their category attributes. This can be abbreviated to

  item/@category  

Predicates that the nodes must satisfy are written in square brackets, for example:

  item[@code='T']/@category  

This selects the category attributes of those child <item> elements that have a code attribute whose value is «T » .

There are many ways of abbreviating path expressions to make them easier to write, but the basic structure remains the same. The full detail appears in Chapter 7 of XPath 2.0 Programmer's Reference .

Context

The way in which expressions are evaluated is to some extent context dependent. For example, the value of the expression $x depends on the current value of the variable x, and the value of the expression «.. » depends on which node is currently being processed in the source document.

There are two aspects to the context: the static context, which depends only on where the expression appears in the stylesheet; and the dynamic context, which depends on the state of processing at the time the expression is evaluated.

The static context for an expression includes the following:

  • The set of namespace declarations in force at the point where the expression is written. This determines the validity and meaning of any namespace prefixes used in the expression. As well as defining the namespace prefixes that are available, the context also defines the default namespace that will be used for unqualified element names appearing in path expressions.

  • The set of variable declarations (that is, <xsl:variable> and <xsl:param> elements) in scope at the point where the expression is written. This determines the validity of any variable references used in the expression. As well as checking at compile time that the variable has been declared, the processor can also make checks on its type: for example it would be an error to use a variable of type xs:date as an argument to the round() function, and the processor can often detect such errors and report them at compile time.

  • The functions that are available to be called. These always include the core library of functions defined in the XSLT and XPath specifications, and the constructor functions for built-in atomic types such as xs:date. They also include user-defined functions written using the <xsl:function> declaration, constructor functions for user-defined types in an imported schema (as described in Chapter 4), vendor-defined extension functions, and user-defined extension functions linked in to the stylesheet using vendor-defined mechanisms.

  • The base URI of the stylesheet element containing the XPath expression. This only affects the result if the expression uses functions such as document() that make explicit use of the base URI.

All of these aspects of the static context for XPath expressions can be controlled from within the stylesheet, and may be different for different XPath expressions. There are other aspects of the XPath context that cannot be controlled using XSLT itself, but where implementors are likely to allow you some control via the API of their individual products. The most important example in this category is the set of URIs that are available for identifying collations (that is, rules for sorting and comparing strings according to the conventions of different languages).

The dynamic context is set only at stylesheet execution time. It consists of the following:

  • The current values of all the variables that are in scope for the expression. These may be different each time the expression is evaluated.

  • The focus, which reflects the current state of processing in the stylesheet. The focus comprises the following:

    • The context item. This is the item (often a node in the source tree) that is currently being processed. An item becomes the context item when it is selected using the <xsl:apply-templates> or <xsl:for-each> instructions. The context item can also be set by the XPath processor when evaluating a subexpression. The context item can be referenced using the expression «. » . In addition, the current() function (defined in Chapter 7) can always be used to reference the item that's the context item at the XSLT level, ignoring any changes made at the XPath level.

    • The context position. This is an integer ( 1) that indicates the position of the context item in the sequence of items currently being processed. The context position can be referenced using the position() function. When <xsl:apply-templates> or <xsl:for-each> are used to process a sequence of items, the context position takes the values 1... n as each of the items in the list is processed. Similarly, when a predicate is used within a path expression, the context position is the position of the node being tested within the set of nodes being tested . For example, «child::a[position() ! = 1] » selects all the child elements named <a> , except the first.

    • The context size . This is an integer ( 1) that indicates the number of items in the sequence of items currently being processed (that is, the highest value that position() will reach). The context size can be referenced using the last() function. For example, «child::a [position() != last() ] » selects all the child elements named <a> , except the last.

  • The set of documents that can be accessed using the doc() and document() functions is also regarded as being part of the dynamic context. This might include the whole of the Web, or it might be restricted by security policies to a local machine, or (if, say, the transformation is running on an embedded processor controlling the engine of your car) it might contain no documents at all. Modeling the set of addressable documents as part of the context is a formal device for describing the language semantics (it's a way of saying that the result of the document() function is defined by the environment in which the stylesheet runs, not by the language specification itself) and it turns out to be quite a neat device for distinguishing those aspects of these functions that are defined by the language spec from those that depend on the implementation.

Some system functions that can be used in XPath 2.0 expressions have other dependencies on the stylesheet context, for example the effect of the key() function depends on the set of <xsl:key> declarations in force; but the list given earlier covers all the context information that is directly accessible to user-written expressions.

Temporary Trees

As we described at the beginning of the chapter, a transformation takes a source tree as input (or perhaps more than one source tree) and produces a result tree (or several result trees) as output.

Very often, however, the easiest way to write a complex transformation is to split it into a number of phases, each of which performs one task. Like pipes in Unix, this creates a powerful way of reusing modules of code-on the basis that each module does only one job. For example, if your stylesheet involves selecting input records, sorting them, grouping them, numbering them, and then formatting the result as HTML, you could potentially carry out each of these five steps in a separate transformation phase. The result would be that if you wanted to change the output from HTML to PDF, the first four steps would be completely reusable.

One way of doing this is to write five separate stylesheets, and couple them together into a processing pipeline. The Java JAXP API, described in Appendix D, is well suited to this task. But often, you want rather closer coupling than this, and you don't necessarily want to write Java code to control the transformations. So the alternative is to write all the phases of the transformation in a single stylesheet, using temporary trees to represent the intermediate results between one phase of processing and the next.

A temporary tree is created by using an <xsl:variable> element with no «as » attribute, containing a sequence constructor to create the content of the tree. For example:

  <xsl:variable name="author">   <person>   <first>Michael</first>   <last>Kay</last>   <nationality>British</nationality>   </person>   </xsl:variable>  

In this example, the value of the variable is a document node, which contains the <person> element as its only child node.

One popular way to use a temporary tree is as a lookup table. The following stylesheet fragment uses data held in a temporary tree to get the name of the month, given its number held in a variable $mm .

  <xsl:variable name="months">   <name>January</name><name>February</name><name>March</name>   <name>April</name><name>May</name><name>June</name>   <name>July</name><name>August</name><name>September</name>   <name>October</name><name>November</name><name>December</name>   </xsl:variable>   ...   <xsl:value-of select="$months/name[position()=$mm])"/>  

Of course, the sequence constructor does not have to contain constant values as in these two examples; it can also contain instructions such as <xsl:value-of> and <xsl:apply-templates> to build the content of the temporary tree dynamically. This is shown in the following example.

  <xsl:variable name="tree">   <xsl:text>AAA</xsl:text>   <xsl:element name="x">   <xsl:attribute name="att">att-value</xsl:attribute>   <xsl:text>BBB</xsl:text>   </xsl:element>   <xsl:element name="y"/>   <xsl:text>CCC</xsl:text>   </xsl:variable>  

This creates the tree illustrated in Figure 2-8. Each box shows a node; the three layers are respectively the node kind, the node name, and the string value of the node. Once again, an asterisk indicates that the string value is the concatenation of the string values of the child nodes.

click to expand
Figure 2-8

In XSLT 1.0, temporary trees went under the name of result tree fragments . I introduced the term temporary tree in an earlier edition of this book, because I felt that the phrase result tree fragment undervalued the range of purposes to which these structures can be applied. The term temporary tree is now the one used in the official language specification. In fact, result tree fragments in XSLT 1.0 were very limited in their capability because of a quite artificial restriction that prevented them being accessed using path expressions. Most vendors ended up circumventing this restriction using an extension function generally named xx:node-set() , where xx refers to the vendor's particular namespace. In XSLT 2.0, the restriction is gone for good, and temporary trees can now be used in exactly the same way as any source document: they can be used as the result tree for one phase of transformation, and the source tree for the next.

The restrictions in XSLT 1.0 were defined by making result tree fragments a separate data type, with a restricted range of operations available. In XSLT 2.0, a temporary tree is a tree just like any other, and is manipulated using variables or expressions that refer to its root node, which is always a document node. (In XSLT 2.0 you can have trees rooted at elements, or even at attributes or text nodes-though in that case there will only be one node in the tree. But the name temporary tree is reserved for trees with a document node at their root.)

A temporary tree does not necessarily correspond to a well- formed XML document, for example the document node can own text nodes directly, and it can have more than one element node among its children. However, it must conform to the same rules as an XML external parsed entity: for example, all the attributes belonging to an element node must have distinct names.

The ability to use temporary trees as intermediate results in a multiphase transformation greatly increases the options available to the stylesheet designer (which is why the xx:node-set() extension function was so popular in XSLT 1.0). The general structure of such a stylesheet follows the pattern:

  <xsl:variable name="phase-1-output">   <xsl:apply-templates mode="phase-1"/>   </xsl:variable>   <xsl:variable name="phase-2-output">   <xsl:apply-templates select="$phase-1-output" mode="phase-2"/>   </xsl:variable>   <xsl:result-document>   <xsl:apply-templates select="phase-2-output" mode="phase-3"/>   </xsl:result-document>  

Some people prefer to use local variables for the intermediate results, some use global variables; it makes little difference.

One way that I often use multiphase transformations is to write a preprocessor for some specialized data source, to convert it into the format expected by an existing stylesheet module that renders it into HTML. For example, to create a glossary as an appendix in a document, you may want to write some code that searches the document for terms and their definitions. Rather than generating HTML directly from this code, you can generate the XML vocabulary used in the rest of the document, and then reuse the existing stylesheet code to render this as phase two of your transformation.

Since multiphase transformations are often used to keep stylesheets modular, some discipline is required to keep the template rules for each phase separate. I generally do this in two ways:

  • Keep the rules for each phase of transformation in a separate stylesheet module. Stylesheet modules are discussed in Chapter 3.

  • Use a different mode for each phase of the transformation. Modes were described earlier in this chapter, on page 70.




XSLT 2.0 Programmer's Reference
NetBeansв„ў IDE Field Guide: Developing Desktop, Web, Enterprise, and Mobile Applications (2nd Edition)
ISBN: 764569090
EAN: 2147483647
Year: 2003
Pages: 324

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net