File: extensions.md

package info (click to toggle)
xalan 1.12-8
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 10,336 kB
  • sloc: cpp: 145,645; xml: 1,523; ansic: 434; sh: 27; makefile: 17
file content (286 lines) | stat: -rw-r--r-- 10,444 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# Xalan-C++ extension functions

## Introduction

At times, you may want to call your own custom C functions from a
stylesheet.  For these situations, Xalan-C++ supports the creation and
use of extension functions.  Xalan-C++ also provides a
[library of extension functions](extensionslib.md)
for your use.

You can think of extension functions as extending the core library of
functions that XPath provides. Like the XPath functions, an extension
function returns an XObject, which may contain a value of any of the
five XSLT data types: `node-set`, `result-tree-fragment`, `string`,
`boolean` or `number`.

You can send arguments to an extension function in the form of XPath
expressions, literals (for `string`, `boolean`, and `number`), the
values returned by other functions, and XSL variables or parameters set
to any of the preceding.

For an example that implements, installs, and uses three extension
functions, see the [External Functions](samples.md#externalfunctions)
sample.

Note: Xalan-C++ does not support extension elements.

## Implementing an extension function

Like the standard XPath functions, the functions you create derive from
the Function base class. Set up your extension function class as
follows:

1. The body of a function is the `execute()` method. Use the
   appropriate `XObjectFactory` method -- `createNumber()`
   in the example below -- to create an `XObject` corresponding to the
   XSLT data type the function returns.
2. Implement a `clone()` method to enable Xalan to create and maintain
    a copy of the extension function.
3. (Optional) As Xalan does for the XPath functions, you may want to
   prevent the compiler from generating an assignment or equality
   operator for this function.

These features all appear in the following example.

```c++
// Base header file.  Must be first.
#include <xalanc/Include/PlatformDefinitions.hpp>

#include <cmath>
#include <ctime>

#include <xercesc/util/PlatformUtils.hpp>
#include <xalanc/XalanTransformer/XalanTransformer.hpp>
#include <xalanc/XPath/XObjectFactory.hpp>

using namespace xalanc;

// This class defines a function that will return the square root
// of its argument.
class FunctionSquareRoot : public Function
{
public:

  /**
   * Execute an XPath function object.  The function must return a valid
   * XObject.
   *
   * @param executionContext executing context
   * @param context          current context node
   * @param opPos            current op position
   * @param args             vector of pointers to XObject arguments
   * @return                 pointer to the result XObject
   */
  virtual XObjectPtr
  execute(
      XPathExecutionContext& executionContext,
      XalanNode* /* context */,
      const XObjectPtr arg,
     	const Locator*	/* locator */) const
  {
    if (args.size() != 1)
    {
      executionContext.error("The square-root() function takes one argument!",
                              context);
    }
    assert(args[0] != 0);
    // Use the XObjectFactory createNumber() method to create an XObject
    // corresponding to the XSLT number data type.
    return executionContext.getXObjectFactory().createNumber(
                                                        sqrt(args[0]->num()));
  }

  /**
   * Implement clone() so Xalan can copy the square-root function into
   * its own function table.
   *
   * @return pointer to the new object
   */
  virtual FunctionSquareRoot*
  clone() const
  {
    return new FunctionSquareRoot(*this);
  }

private:
  // The assignment and equality operators are not implemented…
  FunctionSquareRoot&
  operator=(const FunctionSquareRoot&);
  bool
  operator==(const FunctionSquareRoot&) const;
}
```

## Installing an extension function

[`XalanTransformer`](https://apache.github.io/xalan-c/api/classxalanc_1_1XalanTransformer.html)
provides methods for installing and uninstalling external functions:

* [`installExternalFunction()`](https://apache.github.io/xalan-c/api/classxalanc_1_1XalanTransformer.html#a7d932d74897e12629afd62bba2735456)
  makes the function available in the current instance of `XalanTransformer`. Use
  [`uninstallExternalFunction()`](https://apache.github.io/xalan-c/api/classxalanc_1_1XalanTransformer.html#a8dbb4538357ab2909925dd16a449dbac)
  to remove the function.
* [`installExternalFunctionGlobal()`](https://apache.github.io/xalan-c/api/classxalanc_1_1XalanTransformer.html#a7d4f49b03739feaa01cdbffd5668e736)
  makes the function available globally.
  Use
  [`uninstallExternalFunctionGlobal()`](https://apache.github.io/xalan-c/api/classxalanc_1_1XalanTransformer.html#ab5c8f39bcf0bf84b6bbe0607bbe4afde)
  to remove the function. The global install and uninstall operations
  are not thread-safe.  However, all global functions should be
  thread-safe, because multiple threads could call a particular
  function instance at the same time.

These methods include arguments for the namespace, the function name,
and the function implementation.

When you install an extension function, the function inhabits the
namespace you designate. For information about XML namespaces, see
[Namespaces in XML](http://www.w3.org/TR/REC-xml-names/).

The following code fragment installs locally the square root function
defined above, and binds it to the extension-function name
`square-root` in the namespace
`http://MyExternalFunction.mycompany.org` so it can be accessed from
stylesheets. Keep in mind that the function name does not need to be
the same as the name of the function class, and that a function name
may be used more than once provided that each function with that name
is installed in a different namespace.

```c++
#include <xercesc/util/PlatformUtils.hpp>
#include <xalanc/XalanTransformer/XalanTransformer.hpp>
// You have created a header file for FunctionSquareRoot.
#include <MyFunctions/FunctionSquareRoot.hpp>
// The namespace…
const XalanDOMString	
    theNamespace("http://MyExternalFunction.mycompany.org");

theXalanTransformer.installExternalFunction(theNamespace,
                                            XalanDOMString("square-root"),
                                            FunctionSquareRoot());
```

For an example that installs three functions, see the
[External Functions](samples.md#externalfunctions) sample.

## Using an extension function

To use the extension function in a stylesheet, you must do the following:

1. Declare the extension function namespace.
   <br>
   `xmlns:prefix=URI`
   <br>
   The `prefix` identifies the namespace, and `URI` matches the
   namespace specified when the function is installed.
   <br>
   By default, namespace declarations are included in the
   transformation output. To exclude namespaces from the output,
   use
   <br>
   `exclude-result-prefixes="prefix-1 prefix-2 …"`
   <br>
   in the stylesheet element or
   <br>
   `xsl:exclude-result-prefixes="prefix-1 prefix-2 …"`
   <br>
   in a literal result element or extension element.
2. Call the extension function in the same manner you would call an
   XPath function. The function name you use in the stylesheet is a
   Qualified Name (QName) made up of the prefix you declared in step 1
   and the function name you specified when you installed the function.
   <br>
   You can use XPath expressions, literals (for `string`, `boolean`,
   and `number`), and values returned by other functions to specify
   function arguments.

Suppose, for example, you are working with XML documents containing
area elements like `<area value="397"/>`, where the value attribute
identifies the area of a square.

The following stylesheet declares the square-root function namespace
(the prefix is up to you), instructs the processor not to copy the
namespace declaration to the result tree, and uses the square-root
function to return the square root of `//area/@value:`

```xml
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0"
	xmlns:external="http://ExternalFunction.xalan-c.xml.apache.org"
  exclude-result-prefixes="external">

    <xsl:template match="//area">
    <out>
      The area of the square is
      <xsl:value-of select="@value"/> square units.
      The length of each side is
      <xsl:value-of select="external:square-root(@value)"/> units
    </out>
    </xsl:template>
</xsl:stylesheet>
```

This stylesheet converts `<area value="397"/>` into the following
output:

```xml
<out>
  The area of the square is
  397 square units.
  The length of each side is
  19.9249 units.
</out>
```

For a slightly more complex variation on this example, see the
[External Functions](samples.md#externalfunctions) sample

### Passing Nodes to a function

Please keep in mind that *all* `LocationPath` expressions return a
`node-set`, even if the expression only returns a single attribute or a
`text` node (`node-set`s with one member). You can use the XSLT
`string()` function to convert a `node-set` value to `string`, and the
`number()` function to convert a `node-set` value to `number` (a
double).

If you pass a `node-set` to an extension function, be sure to set up
the function to process a node-set.

Suppose, for example, you have a `ProcessNodes` function class that
uses

```c++
const NodeRefListBase& theNodeList = args[0]->nodeset();
```

in the `execute()` method to get a reference to the `node-set`.

Assuming you install the function as `ProcessNodes` and use the
`node-ext` prefix in a stylesheet to refer to the `ProcessNodes`
function namespace, any of the following function calls are
syntactically possible:

```xml
<!--Process the current node-->
<xsl:variable name="success" select="node-ext:ProcessNodes(.)"/>

<!--Process all nodes in current context-->
<xsl:variable name="success" select="node-ext:ProcessNodes(*)"/>

<!-- Process all nodes -->
<xsl:variable name="success" select="node-ext:ProcessNodes(/*)"/>

<!--Process the foo/baz nodes in current context -->
<xsl:variable name="success" select="node-ext:ProcessNodes(foo/baz)"/>

<!--Process the/foo/baz and /bar/saz nodes -->
<xsl:variable name="success" select="node-ext:ProcessNodes(/foo/baz | /bar/saz)"/>
```

The `NodeRefListBase` is in fact a list of references into the XML
document, so keep in mind that
[`getNextSibling()`](https://apache.github.io/xalan-c/api/classxalanc_1_1XalanNode.html#a9b4a7333371ffee9a6fd851203f4c98a),
for example, gets you the next sibling in the document, which may not
be the next `Node` in the `node-set`.