Tutorial - Template Transformation in Candle

Introduction

Template transformation in Candle is closely based on XSLT. The biggest difference between the two is in the syntax. XSLT uses XML markup syntax, which is very verbose. Whereas Candle templates use scripting syntax. Secondly, XSLT is a DSL (domain-specific language), whereas Candle is a general-purpose scripting language. If you already has knowledge of XSLT, then this tutorial should be straightforward for you.

Through this tutorial, you'll see how core features in XSLT and XQuery are weaved into a more consistent scripting language. A rule-of-thumb is that XQuery features maps to expressions in Candle, and XSLT elements maps to functional statements in Candle. As mentioned in previous tutorials, expressions in Candle focus on atomic value processing and node selection, whereas functional statements in Candle focus on node construction.

Additional Functional Statements in Candle

Functional statements in Candle are statements that can be used in a function. We've already covered the functional flow control statements in previous tutorial. They are: if statement, for statement, foreach statement, let statement, with statement, switch statement, put statement.

The other functional statements in Candle are generally related to node construction:
Statement Type Syntax Remarks
literal node statement "text"; 123; true; :qname; ... Any literal value can appear directly at the statement level, which constructs a data node. The difference between text nodes and other literal value data nodes is that consecutive text nodes are always merged into one text node.
enclosed expression statement { expr } This statement is like the value-of element in XSLT. It evaluates the expression and then output the string value of the result as a text node. (It shall be changed to follow the semantics of enclosed expression in XQuery.)
comment construction statement comment(expr); Constructs a comment node. The string value of the body expression gives the content of the comment.
element construction statement element (expr} { statements; } Constructs an element dynamically. The expr should evaluates to a qname, which is the name of element to construct.
attribute construction statement attribute (expr) {value} Constructs an attribute dynamically. The expr should evaluates to a qname, which is the name of element to construct.
copy statement copy(expr); Copies the result of the expr to the output. If the expr evaluates to a node, it is copied exactly; if the expr evaluates to an atomic value, it is copied as a data node; if the expr evaluates to a sequence, the items are copied individually.
apply statement apply();
apply(parameters);
apply(parameters) mode m;
Applies templates to the child nodes of the current item.
apply-to statement apply-to(expr);
apply-to(expr, parameters);
apply-to(expr, parameters) mode m;
Applies templates to the result of the expr.
Here's an example showing the usage of these statements:
<?csp1.0?>
function main() {
  element (value::div) {
    attribute (value::style) { "border: 1px solid black; background-color:yellow" }
    comment("you'll more clearly see this node in Candle output");
    copy of <div>"some text"</div>;
  }
}
Try it yourself »

Templates in Candle

The syntax for defining a template is:

  template <match-pattern> name (parameters) priority number { statements; }

The match pattern of a template is always required. The name of a template is optional. If it is specified, then the template can also be called as a normal statement function. The parameters can be omitted if the template does not need any parameter. The priority clause is also optional.

A template is just a special statement function. Like statement function, it never has return value and it's primary role is to construct node output. The difference is that a template has additional clauses like the match pattern and priority, which allow it to participate in input transformation.

Match Pattern of a Template

The match pattern of a template uses a special subset of the path expression syntax. The restrictions are:
These restrictions only applies to the top-level match pattern. In the filter predicate, you can still use full path expression syntax. However, it is recommended to keep your match pattern simple, otherwise your template transformation will suffer performance issue.

Although the match pattern has the form of a path expression, the way it works is quite different. Given a context item, a path evaluates from left to right. But given a current node, the match pattern evaluates from right to left. The rightmost step matches the current node itself, and the steps on the left matches the parent and ancestors of the current node. The way it works is very similar to CSS selector, although the syntax of the two are very different.

Template Priority

It is possible for a source node to match more than one template rule. The template rule to be used is determined as follows:

  1. First, all matching template rules that have lower import precedence than the matching template rule or rules with the highest import precedence are eliminated from consideration.

  2. Next, all matching template rules that have lower priority than the matching template rule or rules with the highest priority are eliminated from consideration. The priority of a template rule is specified by the priority clause on the template. The value of this must be a real number (positive or negative). The default priority is computed as follows:

Built-in Template Rules

There is a built-in template rule to allow recursive processing to continue in the absence of a successful pattern match by an explicit template rule in the stylesheet. This template rule applies to both element nodes and the root node. The following shows the equivalent of the built-in template rule:

template <* | /> { apply; }

There is also a built-in template rule for text and attribute nodes that copies the node through:

template <text() | @*> { copy of .; } 

The built-in template rule for comments is to do nothing.

template <comment()> { }

You can override these built-in rules. However, as these rules may apply to many nodes, you need to take into consideration the impact on performance. Your templates will always be slower than the built-in templates whose handling are optimized by the query engine.

Below is an example showing the template transformation features of Candle:

<?csp1.0?>
template <cd> {
  <p>
    apply-to(child::title);
    apply-to(child::artist);
  </p>
}
template <title> {
  "Title: " <span style="color:#ff0000">{.}</span> <br/>
}
template <artist> {
  "Artist: " <span style="color:#00ff00">{.}</span> <br/>
}
function main(input) {
  <html>
  <body>
    <h2>"My CD Collection"</h2>
    apply-to(input);
  </body>
  </html>
}
Try it yourself
This example is actually a translation of an XSLT example at w3schools. You can compare the syntax of Candle template against XSLT template.