2004
Colin Adams and others
Colin
Adams
colin@colina.demon.co.uk Tuesday, October 19th, 2004

# Output extensions and additional serialization parameters

This chapter relates to the xsl:output and xsl:result-document elements.

## Additional serialization parameters

See Extensions

You can also add your own serialization parameters in a namespace, for use with a QName output method that you have written. This must be a non-null namespace, and it must not be the XSLT namespace, nor can it be `http://www.gobosoft.com/eiffel/gobo/gexslt/extension`. Apart from that, you may use any namespace you wish, but it is strongly recommanded that this be a URI which you control, so as to prevent clashes with other possible extensions. I would suggest using the same namespace as you do for your QName output method.

## Output extensions

The syntax of the xsl:output and xsl:result-document elements allows the "method" parameter to take a QName-but-not-NCName value. This allows an implementation to provide additional output methods other than the four standard ones of "xml", "html", "xhtml" and "text".

The library provides an example of how to write your own output methods. This example only implements one method, which is the same as the standard "xml" method, except that it allows the XSLT programmer to supply an internal subset for the DTD, via an additional serialization parameter. If the serialization parameter is not supplied, then no internal subset is written.

In this section I will talk you through the steps you need to take to implement your own methods, with reference to the example.

1. Create a descendant of `XM_XSLT_EXTENSION_EMITTER_FACTORY`. The example is `XM_XSLT_GEXSLT_EXAMPLES_EMITTER_FACTORY`. The example only implements one method, but you can implement as many methods as you like in your class, provided they will all share the same namespace.

   There are four features which you must implement:

   namespace_uri: STRING

     This is the namespace-uri which the XSLT programmer will use to call your output methods. Note that it must be a non-null namespace and cannot be the XSLT namespace nor the XSLT library's extension namespace.

   is_valid_output_method (a_method_local_name: STRING)

     This is called (indirectly) from gather_output_properties in `XM_XSLT_OUTPUT`, to determine if the QName supplied as an argument to xsl:output's method attribute is supported by the implementation.

   set_defaults (a_method_local_name: STRING; some_properties: XM_XSLT_OUTPUT_PROPERTIES; an_import_precedence: INTEGER)

     This is called (indirectly) from gather_output_properties in `XM_XSLT_OUTPUT`, to set the default serialization parameters for your method. The example does not check the local name, as it only supports one method, and is_valid_output_method has already returned True when this routine is called. But if you want to have different defaults for your different routines, then you can test the value of a_method_local_name.

     You do not need to set most of the defaults, as they will be set when some_properties is created (using feature set_xml_defaults from `XM_XSLT_OUTPUT_PROPERTIES`, but the example sets them all, purely for illustration. Note that you must set the method in exactly the same way that the example does.

     You can also set defaults for any additional serialization parameters that your method(s) require(s). The example shows how to do this, but only as a comment, since the serialization parameter is not required in this case.

     It is not possible to require the presence of an additional serialization parameter, so if your method requires a value, you should set a default.

   new_receiver

     This routine provides the serializer for the transformer. You have to return a chain of `XM_XPATH_RECEIVER`s, of which the last (and possibly only) one must be a descendant of `XM_XSLT_EMITTER`. You will write this descendant in the next step.

     The example first checks the method's local name, and ignores it if it doesn't match. In the latter case, this will result in returning Void, which violates the post-condition, but for this to happen the pre-condition would have been violated, so this is OK.

     Next, the example creates the emitter (you will write your emitter in the next step). Typically, it passes through the values for the transformer, the result stream, and the output properties. You will almost certainly need to do the same. It also passes an `XM_XSLT_CHARACTER_MAP_EXPANDER`, which it gets from the helper routine character_map_expander in `XM_XSLT_EMITTER_FACTORY_ROUTINES`. If you wish to support xsl:output's use-character-maps parameter, then you should do the same. Note that the third parameter to that routine says whether or not the `XM_XSLT_CHARACTER_MAP_EXPANDER` should surround any mapped characters with ASCII NULs. This is used by the standard methods (other than the "text" method) to avoid further escaping of mapped characters (and also Unicode normalization, but this isn't implemented yet), as required by the standard. It is entirely up to you whether you implement escaping of characters in your emitter, and if so, whether you want mapped characters to avoid further escaping and normalization, but if you do, then you should specify True here, and implement the appropriate routines in your emitter accordingly. Otherwise you should specify False.

     Next the example checks the value of the indent serialization parameter, and adds an indenter to the front of the chain. If you choose to support indenting for your method(s), then you should do the same. You can either use one of the existing indenters (`XM_XSLT_XML_INDENTER` or `XM_XSLT_HTML_INDENTER`), or write your own (which must be a descendant of `XM_XPATH_PROXY_RECEIVER`).

     Next the example checks whether any cdata-section-elements have been requested, and if so, adds a `XM_XSLT_CDATA_FILTER`. Again, it is up to you whether or not you want to implement this parameter in your method(s). If you chose to do so, then I suggest using the standard filter.

2. Next, you must create your emitter. This must be a descendant of `XM_XSLT_EMITTER`.

   The example (`XM_XSLT_GEXSLT_EXAMPLES_XML_EMITTER`) simply inherits from `XM_XSLT_XML_EMITTER`, and redefines write_doctype. If your method is a variation on one of the standard methods, then this might be appropriate for you too. Note that the other standard methods are all implemented as descendants of `XM_XSLT_XML_EMITTER`, even `XM_XSLT_TEXT_EMITTER`, so certainly consider this approach.

   On the other hand, if your emitter is doing something highly specialized, such as writing to a relational database, then you probably have to write your own emitter from scratch. So we will take a brief look at `XM_XSLT_EMITTER`.

   There is actually very little in here. The outputter: XM_XSLT_OUTPUT_ENCODER is necessary for implementing the encoding parameter, although even this isn't absolutely required (it is possible that it is meaningless for your method). The output_properties: XM_XSLT_OUTPUT_PROPERTIES is surely necessary (unless your method is completely parameterless), so it is required to be non-void. This is where you can find the values of the serialization parameters.

   The character_map_expander: XM_XSLT_CHARACTER_MAP_EXPANDER is optionally available for implementing use-character-maps, if your method supports that. When Unicode normalization is implemented, it will be in normalized_string . The transformer must also be non-Void, as it is needed for error reporting.

3. Finally, you need to register your `XM_XSLT_EXTENSION_EMITTER_FACTORY` with the library. You do this by inheriting from `XM_XSLT_SHARED_EMITTER_FACTORY`, and calling emitter_factory.register_extension_emitter_factory paasing an instance of your extension emitter factory as the sole argument. This uses the namespace_uri feature of the factory to direct requests for that namespace URI to your factory.