O'Reilly logo

XML Publishing with AxKit by Kip Hampton

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Defining Style Processors

The most basic component used to define which transformations will be applied to a given resource within AxKit is perhaps best termed a style processor definition. These definitions indicate a single transformational step (applying an XSLT stylesheet or passing the content through a SAX Filter, for example) in what may be a chain of transformations. In their most basic and typical form, these style definitions declare two bits of crucial information: a MIME type that will be used by AxKit to determine which Language module will be used to transform the content, and a file path to a stylesheet (or other Language-specific argument) that will be used by that Language module to determine how to transform the content. Individual processor definitions may optionally be combined into named style and media groups that can then be selected conditionally, based on a number of factors.

By default, style processors are defined within AxKit in one of two ways: by using AxKit’s processor configuration directives or by special stylesheet processing instructions contained in the source documents themselves. (In Chapter 8, you’ll learn to create your own way to configure AxKit’s styling rules by rolling a custom ConfigReader module, but that’s another story.) The following illustrates how to create a simple named style containing a single style definition using AxKit’s server configuration directives. The lone processor definition contains the required MIME type and path to the stylesheet that will be applied.

<AxStyleName "#default">
    AxAddProcessor text/xsl /path/to/style1.xsl
</AxStyleName>

The MIME type used in the processor definition corresponds to the one you associated earlier with the Language::LibXSLT module. The effect of this is that, starting with the source, XML will be transformed by the LibXSLT processor by applying the stylesheet at the location defined by the second argument.

AxKit’s Runtime Styling Directives

AxKit offers a small host of runtime configuration directives that can be used to define the styles for a given site. These directives are actually extensions of Apache’s configuration syntax and, as such, are added to the usual server configuration files, such as httpd.conf or .htaccess files. They can be mixed and matched with other Apache directives. A basic familiarity with Apache’s runtime configuration syntax is presumed in this section.

Using AxKit’s configuration directives, style definitions can be added and applied in all cases or applied conditionally, based on an aspect of the XML document being served (the document’s root element name, URI on the server, etc.). A style definition’s first argument is the MIME type associated with the language module that will apply the transformation, and the second is the path to the stylesheet that will be applied. Conditional processor definitions add a third, directive-specific argument that defines the rules that govern when and if that processor will be adding to the current processing chain.

Relative stylesheet paths are resolved in the context of the directory that contains the resource being requested, while qualified paths are resolved relative to the host’s DocumentRoot. However, not all language modules employ external stylesheets. For example, the SAXMachines module expects a quoted, whitespace-separated list of Perl package names that implement SAX Filters instead of a stylesheet path. Others, such as XSP, are self-contained (the source document itself defines the transformations), and the literal string NULL is used in place of the stylesheet path.

We will now take a look at each of AxKit’s processor definition configuration directives. Keep in mind as you read the details of each directive that these are just the lowest-level building blocks from which you can create sophisticated application- and media-specific processing chains.

AxAddProcessor

AxAddProcessor, the most basic of the styling configuration directives, unconditionally adds that processor definition to AxKit’s processing chain. The processor definition requires two arguments: the MIME type associated with the language processor that will be used to do the transformation, and the path to the stylesheet that will be used by that processor to transform the XML source.

AxAddProcessor text/xsl /styles/global.xsl

This unconditionally adds the stylesheet /styles/global.xsl to the processing chain and tells AxKit to use the language module associated with the MIME type text/xsl to perform the transformation.

The AxAddProcessor directive is commonly used in conjunction with Apache’s standard <Files>, <Directory>, <Location>, and similar directives. For example, adding the following to a .htaccess at the root level of a site would configure AxKit to apply the stylesheet /styles/docbook_simple.xsl to all documents in that site that have the file extension .dkb, while transforming all documents with .xml extension with the global stylesheet from the above example:

<Files *.dkb>
  AxAddProcessor text/xsl /styles/docbook_simple.xsl
</Files>

<Files *.xml>
  AxAddProcessor text/xsl /styles/global.xsl
</Files>

Transformation chains can be created by adding more than one style processor definition to a given context. The following would configure AxKit to apply the stylesheet pre_process.xsl to all documents in the /docs directory and then to apply the stylesheet main.xsl to the result of the first transformation:

<Directory /docs>
  AxAddProcessor text/xsl /styles/pre_process.xsl
  AxAddProcessor text/xsl /styles/main.xsl
</Directory>

AxKit processing definitions, like many Apache directives, are inherited recursively down directory branches. So, a style definition configured for the root of a site will be applied to all documents in that site, unless explicitly overridden.

AxAddRootProcessor

The AxAddRootProcessor directive sets up document-to-stylesheet mappings based on the name of the root element in the source XML document. It requires three arguments: the MIME type associated with the language processor that will perform the transformation, the path to the stylesheet that will be applied, and the name of the root element to match against.

The following configures AxKit to apply the stylesheet /styles/docbook_simple.xsl to all documents in the current scope that have a root element named article:

AxAddRootProcessor text/xsl /styles/docbook_simple.xsl article

Matching top-level element names in documents employing XML namespaces is supported using the Clarkian Notation, in which the element’s namespace URI is wrapped in curly braces { } and prepended to the element’s local name.

AxAddRootProcessor text/xsl /styles/mydoc.xsl {http://localhost/NS/mydoc}document

This would apply the stylesheet /styles/mydoc.xsl to both of the following documents:

<?xml version="1.0"?>
<document xmlns="http://localhost/NS/mydoc">
  <element/>
</document>

<?xml version="1.0"?>
<doc:document xmlns:doc="http://localhost/NS/mydoc">
  <doc:element/>
</doc:document>

The element’s namespace prefix is not considered, since it is really only a syntactic shortcut used to bind the given element to the namespace URI to which the prefix is bound. Hence, the following would match neither of the above documents:

AxAddRootProcessor text/xsl /styles/mydoc.xsl {http://localhost/NS/mydoc}
doc:document

AxAddDocTypeProcessor and AxAddDTDProcessor

These directives allow for stylesheet selection based on aspects of the source content’s Document Type Definition (DTD).

The AxAddDocTypeProcessor directive conditionally adds processors based on the value assigned to the PUBLIC identifier contained in the source document’s DTD. So, to add a style processor to match the following Simplified DocBook document, do the following:

<?xml version="1.0"?>
<!DOCTYPE article
          PUBLIC "-//OASIS//DTD Simplified DocBook XML V4.1.2.5//EN"
          "http://www.oasis-open.org/docbook/xml/simple/4.1.2.5/sdocbook.dtd" [  ]>
<article>
  . . . 
</article>

You can use the following AxAddDocTypeProcessor directive:

AxAddDocTypeProcessor text/xsl /styles/sdocbook.xsl "-//OASIS//DTD Simplified 
DocBook XML V4.1.2.5//EN"

Similarly, the AxAddDTDProcessor directive conditionally adds processors based on the path contained in the SYSTEM identifier in the source document’s DTD:

<?xml version="1.0"?>
<!DOCTYPE mydoc
          SYSTEM "/path/to/my.dtd" [  ]>
<article>
  . . . 
</article>

and the matching AxAddDTDProcessor directive:

AxAddDTDProcessor text/xsl /styles/sdocbook.xsl /path/to/my.dtd

It is important to remember that only the original source document is examined for matches against the conditional AxAddRootProcessor, AxAddDTDProcessor, and AxAddDocTypeProcessor directives. So, for example, if you alter the document’s root element name during a transformation by one stylesheet, that new root element will not be evaluated against any AxAddRootProcessor directives that may exist in the current context.

AxAddURIProcessor

The AxAddURIProcessor provides a way to apply styles by matching a Perl regular expression against the current request URI. Mostly, this directive is a useful way to emulate LocationMatch blocks in contexts in which those blocks are not allowed.

# In httpd.conf, fine here
<LocationMatch "/my/virtual/uri">
  AxAddProcessor text/xsl /styles/application.xsl
</LocationMatch>

# But I like .htaccess files and can't use LocationMatch!!
AxAddURIProcessor text/xsl /styles/application.xsl "/my/virtual/uri"

AxAddDynamicProcessor

AxAddDynamicProcessor, the syntactic oddball among the processor directives, accepts the name of a Perl package as its sole argument. When this directive is found in a given context, AxKit calls the handler( ) subroutine in the package specified. That function is expected to return a list of processor definitions that will be added to the current processing chain.

The handler( ) subroutine is passed, in order: an instance of the current ContentProvider class, the preferred media name, the preferred style name, the source content’s DTD PUBLIC identifier, its SYSTEM identifier, and root element name. If the source content does not contain a DTD, or a preferred media and style that have not been set by an upstream plug-in for the current request, those corresponding arguments will not be defined. The styles returned take the form of a list of HASH references, each containing two required key/value pairs: href, whose value should contain the path to the stylesheet being applied; and type, whose value should contain the MIME associated with the Language module that will do the transformation.

package My::Processors;
sub handler {
     my ($provider, $preferred_media_name, $preferred_style_name, $doctype, 
$dtd, $root) = @_;
     
     # normally, @styles will be generated dynamically rather than hardcoded, as
     # it is here.
     
     my @styles = (
         { type => 'application/x-xsp', href => 'NULL' },
         { type => 'text/xsl', href => '/styles/mystyle.xsl' },
     );

     return @styles;
}

AxResetProcessors

The AxResetProcessors directive clears the list of processor mapping within the scope of its surrounding block. This is especially useful for handling special cases in otherwise homogeneous configurations.

# Set the default style for the entire site
<Directory "/www/sites/mysite">
  AxAddProcessor text/xsl /styles/global.xsl
</Directory>

# But you need to transform the content in the 'products'
# directory with a different style and you don't want to
# inherit the global style.
<Directory "/www/sites/mysite/products">
  AxResetProcessors
  AxAddProcessor text/xsl /styles/products.xsl
</Directory>

Style definition directive inheritance

Like most Apache configuration directives, AxKit style processor definitions are inherited recursively down directories. A style defined at the root level of the site, for example, will be applied to all documents in or below that directory in the hierarchy, and, hence, the entire site. Styles added to locations deeper in the hierarchy do not override the styles defined by their parents but rather are added to the processing chain. So, for instance, if you have a global style defined for the root level of the site, and one defined for a child directory named contact, all documents in the contact directory will have both styles applied during processing. What surprises many new AxKit users, however, is the order in which the styles are applied in these cases—styles defined in child directories are prepended to the list of processors defined by their parents, not appended, as you may initially expect. Consider the following style configuration:

<Directory "/www/sites/mysite">
  AxAddProcessor text/xsl /styles/global.xsl
</Directory>

<Directory "/www/sites/mysite/contact">
  AxAddProcessor application/x-xsp NULL
</Directory>

The mysite directory has a style definition that applies the global.xsl XSLT stylesheet to all of its contents, and that the .contact directory (a child of the mysite directory) adds a style definition that configures AxKit to process contents of that directory with the XSP processor. With this configuration, a document requested from the contact directory will have both styles applied, but the local XSP process will be applied first and the result of that will be processed using the global.xsl stylesheet. While this behavior seems a bit strange at first glance, in practice it helps simplify setting up the Style processing for many sites where a common look and feel is applied to all content and various source-specific transformations are configured for lower levels in the document hierarchy. (The results are often copied through verbatim by the higher level transformations.)

Taken together, these configuration directives represent a rich set of options that, when combined with Apache’s standard configuration blocks, can meet the styling requirements of a great many sites. However, in Section 4.3, you will see how these components can be combined with AxKit’s StyleChooser and media chooser modules to make your sites even more dynamic and responsive.

Stylesheet Processing Instructions

In addition to defining style processor mappings through the configuration directives, stylesheets can also be applied based on one or more xml-stylesheet processing instructions in the source document itself. Stylesheet processing instructions must appear in the prolog of the document—that is, between the XML declaration and the top-level document element:

<?xml version="1.0"?>]]><emphasis role="bold"><![CDATA[
<?xml-stylesheet type="text/xsl" href="/styles/docbook_simple.xsl" ?>]]></emphasis><![CDATA[
<article>
   . . . 
</article>

Similar in behavior to the AddAddProcessor configuration directive, the xml-stylesheet processing instruction’s type attribute should contain the MIME type associated with the language module that will perform the transformation. The href attribute should contain the path to the stylesheet that will be applied.

Styles added via xml-stylesheet processing instructions come in three flavors: persistent styles that are applied in all cases, preferred styles that define a default style if none is specifically selected, and alternate styles that are only applied under specific conditions.

Persistent styles are defined as those whose processing instruction has neither a title nor an alternate attribute. The following adds two persistent styles to the document that contains them:

<?xml-stylesheet type="text/xsl" href="/styles/nav_includes.xsl"?>
<?xml-stylesheet type="text/xsl" href="/styles/html.xsl"?>

All relevant styles in a given document are applied in the order in which their xml-stylesheet processing instructions appear. So, the above would tell AxKit to apply the /styles/nav_includes.xsl stylesheet to the source XML and then apply the /styles/html.xsl stylesheet to the result of the first transformation.

A preferred style is one whose processing instruction contains a title but not an alternate attribute. It is used to define the nonpersistent default style among a set of alternate styles:

<?xml-stylesheet type="text/xsl" href="/styles/html.xsl" 
title="html"?>

An alternate style is one whose processing instruction contains both a title and an alternate attribute:

<![CDATA[
<?xml-stylesheet type="text/xsl" href="/styles/html.xsl" title="html"
alternate="yes"
]]>

In AxKit, alternate styles are used together with a StyleChooser module to select the appropriate styles to apply for a given circumstance. (See Section 4.3.1 later in this chapter.)

Finally, styles set via xml-stylesheet processing instruction can associate themselves with a specific media type by adding the media attribute. This allows alternate styles to be selected based on the type of device requesting the resource. (See Section 4.3.1.)

<?xml-stylesheet type="text/xsl" href="/styles/wap.xsl" 
alternate="yes" media="handheld"?>

Stylesheet processing instructions are extracted only by the ContentProvider module that fetches the original source XML document. This means that you cannot add stylesheets to the processing chain by including xml-stylesheet instructions in the output of a stylesheet transformation. Once the document is sent to AxKit’s language modules to process, the stylesheet PIs (if any) have already been extracted, and the results of the transformations are not reexamined.

AxIgnoreStylesheetPI

Any styles defined by xml-stylesheet processing instructions in the source document override all those defined via configuration directive. This is often desirable. For example, it provides applications that return dynamic documents through a custom ContentProvider, an easy way to set the styles for that application state while still having defaults set via configuration directive to cover common cases. In many situations, however, this is not the behavior that you want. To disregard all styles defined by xml-stylesheet processing instructions in a particular context, use the AxIgnoreStylesheetPI directive:

AxIgnoreStylesheetPI On

So, which is better, styles defined by configuration directive or by xml-stylesheet processing instruction? The answer depends largely on the site’s requirements, but, in general, sites that define style processors via configuration directive tend to be more flexible and easier to maintain in the long run. For example, altering the stylesheet path for a given style is a one-line change for a site that uses configuration directive to define its styles, while the path would have to be altered in each document that references that style in the same site that relies on xml-stylesheet processing instructions.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required