Output extensions and additional serialization parameters |

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

## 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`, `XM_XSLT_HTML_INDENTER` or `XM_XSLT_XHTML_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.

## Additional serialization parameters

See Extensions

You can also add your own serialization parameters in a namespace, for use with a QName-but-not-NCName 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.