So far, we have looked at the various individual elements that can be
used to control how AxKit applies style transformations. So many, in
fact, that seeing how these parts all fit together may be a bit
tough. For example, a named style block (selected by a StyleChooser
based on an environmental condition) may contain one or more
AxAddDTDProcessor
or similar
conditional processing directives that are only applied if an
additional condition is met. True AxKit mastery comes from knowing
how to combine all its various configuration options to create
elegant styling rules that meet the need of your specific
application.
To help examine the processing order for various configuration
combinations, we will create a series of very simple XSLT stylesheets
whose sole purpose is to show the order in which AxKit applies a
given style. The stylesheet in Example 4-2, alpha.xsl
, simply
appends the string . . . Alpha
processed
to the text of the top-level
root
element of the document being processed.
Example 4-2. alpha.xsl
<?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/"> <root><xsl:value-of select="/*"/> . . . Alpha processed</root> </xsl:template> </xsl:stylesheet>
The tiny sample XML document used for the transformations is shown in Example 4-3.
Add more stylesheets to these—beta.xsl
,
gamma.xsl
, and so on—that do more or less
the same thing—that is, adding . . . Beta processed
, etc., to the text of the root element. Wherever
a simple description does not suffice, use these stylesheets to
examine the precise
processing order based on the returned result.
Wherever more than one style processing directive exists within a
given context, each will be added (or, in the case of conditional
processors, evaluated) in the order in which they appear in the
configuration files. In cases in which directives are added to the
same context by both the
httpd.conf
file and
an
.htaccess
file,
those from the httpd.conf
are added or evaluated
first:
<Directory /www/mysite.com/> AxAddProcessor text/xsl /styles/alpha.xsl AxAddProcessor text/xsl /styles/beta.xsl </Directory>
As you may expect with this configuration, a document requested from
root
directory mysite.com
will have the alpha.xsl
stylesheet applied to
the source content, then the beta.xsl
stylesheet
applied to the result of the first transformation:
<?xml version="1.0"?> <root>Base content . . . Alpha processed . . . Beta processed</root>
Similarly, the rules governing conditional processing directives are tested in the order in which they appear in the configuration files:
<Directory /www/mysite.com/> AxAddProcessor text/xsl /styles/alpha.xsl AxAddRootProcessor text/xsl /styles/beta.xsl root AxAddProcessor text/xsl /styles/gamma.xsl </Directory>
Here, the alpha.xsl
stylesheet is added
unconditionally. Then, if the source document’s
top-level element is named <root>
, the
beta.xsl
stylesheet is added. Finally, the
gamma.xsl
stylesheet is unconditionally added to
the processing chain. The result looks like this:
<?xml version="1.0"?> <root>Base content . . . Alpha processed . . . Beta processed . . . Gamma processed</root>
If the root element of the source document were something other than
<root>
, or if that element were bound to a
namespace that did not match the one used in the
AxAddRootProcessor
directive, only the
alpha.xsl
and gamma.xsl
processors would be added to the processing chain. Compare this
configuration snippet to the previous one:
<Directory /www/mysite.com/> AxAddProcessor text/xsl /styles/alpha.xsl AxAddRootProcessor text/xsl /styles/beta.xsl {http://myhost.tld/namspaces/root}root AxAddProcessor text/xsl /styles/gamma.xsl </Directory>
Here, the beta.xsl
would
not be added to the processing chain because,
although the name of the top-level element matches the one in your
AxAddRootProcessor
directive, the directive
specifies that the <root>
element must be
bound to the http://myhost.tld/namespaces/root
namespace URI. This is not the case in our sample XML document.
Therefore, only the alpha.xsl
and
gamma.xsl
stylesheets would be applied.
Mixing various conditional processing directives at the same level can be quite powerful. Many simple sites really only use a handful of source XML grammars, and if only one result format is expected (i.e., HTML), setting up a block of carefully chosen conditional processors for the host’s top-level directory can often cover most, if not all, the processing rules for the entire site. Here is a more practical example:
# The root directory for our AxKit-enabled site <Directory /www/mysite.com/> AxAddDocTypeProcessor text/xsl /styles/docbook/html/docbookx.xsl "-//OASIS//DTD DocBook XML V4.2//EN" AxAddRootProcessor text/xsl /styles/rss2html.xsl {http://www.w3.org/1999/02/22-rdf- syntax-ns#}RDF AxAddRootProcessor application/x-xsp NULL {http://apache.org/xsp/core/v1}page AxAddRootProcessor text/xsl /styles/appgrammar2html.xsl {http://apache.org/xsp/core/v1} page </Directory>
Here, you have a small group of conditional processing
directives—one that will be applied to documents containing a
Document Type Definition with the SYSTEM ID indicating the latest
version of DocBook, one that will be applied if the
content’s top-level element is named
RDF
and that element is bound to the
http://www.w3.org/1999/02/22-rdf-syntax-ns#
namespace, and two that will match any document that contains a
top-level page
element bound to the
http://apache.org/xsp/core/v1
namespace URI.
Just by setting up this simple block of conditional processing directives at the top level of the site, you can now publish static RSS 1.0 news feeds and DocBook documents (in their myriad forms from books to FAQs), as well as generate dynamic XML content via eXtensible Server Pages from any directory at or below this level in the site. AxKit will simply work as expected. Admittedly, this setup presumes that you will publish documents only as HTML, but that’s still a lot of power for minimal effort.
By setting two conditional AxAddRootProcessor
directives to match the same root
element, you create a two-step processing chain for XSP documents in
which AxKit’s XSP engine processes the source, and
the /styles/appgrammar2html.xsl
XSLT stylesheet
processes the resulting markup.
The rules for style processors that are applied conditionally based
on a feature contained in the XML source
(AxAddDTDProcessor
,
AxAddRootProcessor
, etc.) are only evaluated
against the original source content, not subsequent transformations
in the processing chain. That is, the rules are evaluated
once, using the source document returned from
the ContentProvider. The rules are not
reevaluated. Therefore, if you have a stylesheet that changes the
root element name during transformation, and that new root element
coincides with the name passed to an
AxAddRootProcessor
directive that is in scope for
that request, the AxAddRootProcessor
rule does
not match, since the transformed result is not
examined. Similarly, only those xml-stylesheet
processing instructions contained in the original source document
will be considered. You cannot add to or otherwise alter the
processing chain by adding a stylesheet PI to the result of a
transformation.
Although we touched on this earlier, it bears repeating: style definitions are prepended to the processing chain as one descends into the site’s directory tree. That is, processing definitions at deeper levels in the site are added to the front of the processing chain, not to the end. This is most easily explained with a simple example using the stylesheets that illustrate AxKit processing order:
# the site's DocumentRoot directory <Directory /www/mysite.com/> AxAddProcessor text/xsl /styles/alpha.xsl </Directory> # a child directory of the DocumentRoot <Directory /www/mysite,com/levelone/> AxAddProcessor text/xsl /styles/beta.xsl </Directory> # a child directory of the 'levelone' directory <Directory /www/mysite.com/levelone/leveltwo/> AxAddProcessor text/xsl /styles/gamma.xsl </Directory>
Given this configuration, a request for
http::/myhost.tld/levelone/leveltwo/minimal.xml
yields the following result:
<?xml version="1.0"?> <root>Base content . . . Gamma processed . . . Beta processed . . . Alpha processed</root>
The styles are added to the processing chain from the bottom up, so
the alpha.xsl
stylesheet is applied last since
it is at the top level of the site’s hierarchy.
Again, the reason is that, in general, most sites deploy more generic
styles (those that will be applied to all or most of the documents in
the site) at higher levels in the directory tree. This behavior
allows that to work as expected, while handling special cases in
lower-level directories by prepending special processors to the
chain.
To see the utility of this behavior, imagine that you are publishing a site that is divided into several sections. In addition to the content being rendered, these sites usually have a certain amount of markup that appears on every page (a common graphical header, a legal/copyright footer, a common navigation bar, etc.). They also have a certain amount of markup that is common, but unique, to each individual section (section headers, contextual navigation, etc.). By building the processing chain from the bottom up, AxKit allows you to put the site-wide boilerplate markup in a top-level stylesheet, while handling the section-specific markup in each section’s unique directory.
# the site's DocumentRoot <Directory /www/mysite.com/> # the global stylesheet that contains the site-wide headers/footers, etc. AxAddProcessor text/xsl /styles/global.xsl </Directory> <Directory /www/mysite.com/products/> # adds the markup common to all pages in the 'products' section (contextual navigation, section headers, etc).. AxAddProcessor text/xsl /styles/products.xsl </Directory> <Directory /www/mysite.com/locations/> # adds the markup common to all pages in the 'locations' section . . . AxAddProcessor text/xsl /styles/locations.xsl </Directory>
Here, a request for a document from the products
directory would be transformed by the
/styles/locations.xsl
stylesheet to add the
section-specific content, then passed to the
/styles/global.xsl
stylesheet to add the
site-wide boilerplate. This strategy presumes that higher-level
stylesheets add only what is needed at that level and pass the rest
of the markup through as-is. For example, with the above
configuration, the stylesheet applied to the source content may
return a result that has a div
root element
containing the page’s main text; that result may
then be wrapped in another div
by the
/styles/location.xsl
stylesheet to add the
section-specific markup (while copying the result of the original
transformation through, untouched). Finally,
that result is wrapped by the
/styles/global.xsl
stylesheet to create the
completed document. (Think: the component-based approach to
constructing pages but built from the bottom up.)
Also remember that Rule 1 still applies: style definitions in the same lexical scope are evaluated in the order found in the configuration files. This means that if you have more than one processing directive that matches at a given level in the directory hierarchy, the processors for that level are still added in the order they appear, and that group of processors will be prepended to the list of processors from a higher level. An example may help clarify:
# the site's DocumentRoot directory <Directory /www/mysite.com/> AxAddProcessor text/xsl /styles/alpha.xsl AxAddProcessor text/xsl /styles/beta.xsl </Directory> # a child directory of the DocumentRoot <Directory /www/mysite.com/levelone/> AxAddProcessor text/xsl /styles/gamma.xsl AxAddProcessor text/xsl /styles/delta.xsl </Directory> # a child directory of the 'levelone' directory <Directory /www/mysite.com/levelone/leveltwo/> AxAddProcessor text/xsl /styles/epsilon.xsl </Directory>
Here, using your processing order stylesheets, a request for
http::/myhost.tld/levelone/leveltwo/minimal.xml
returns:
<?xml version="1.0"?> <root>Base content . . . Epsilon processed . . . Gamma processed . . . Delta processed . . . Alpha processed . . . Beta processed</root>
At each level, the processors are added in the order found in the
configuration file for that scope, but each of those groups of
processors is added to the chain from the bottom up. Now, apply this
idea to the previous sample. You can see how the
page-level content can be generated
for the location
and products
sections of the site:
# the site's DocumentRoot <Directory /www/mysite.com/> # the global stylesheet that contains the site-wide headers/footers, etc. AxAddProcessor text/xsl /styles/global.xsl </Directory> <Directory /www/mysite,com/products/> # generate XML from database select for 'products' AxAddProcessor application/x-xsp NULL # Transform 'product' application grammar to HTML AxAddProcessor text/xsl /styles/product2html.xsl # adds the markup common to all pages in the 'products' # section (contextual navigation, section headers, etc).. AxAddProcessor text/xsl /styles/products.xsl </Directory> <Directory /www/mysite,com/locations/> # generate XML from database select for 'locations' AxAddProcessor application/x-xsp NULL # Transform 'product' application grammar to HTML AxAddProcessor text/xsl /styles/locations2html.xsl # adds the markup common to all pages in the 'locations' section . . . AxAddProcessor text/xsl /styles/locations.xsl </Directory>
Now, each subdirectory has two processing definitions: one that
processes the XSP source content to generate markup for the location
and products data from a database query, respectively, and one that
transforms each domain-specific grammar into HTML. The results of
those transformations are then passed to the
global.xsl
stylesheet, which passes that markup
through, adding things like the common header, site-wide navigation,
copyright information, etc. Again, in each lexical scope (the
location/ and
products
/
subdirectories),
the processor definitions are added to the processing chain in the
same order they are found in the configuration file. However, since
they are at a lower level in the directory structure than the global
stylesheet, each ordered group is processed
before the global one. If you want to keep this
same layout but need to allow more than a single type of document in
each of the subdirectories, you could use groups of conditional processing
directives instead of the unconditional
AxAddProcessor
directives you have here to match the properties of each supported
document type. As long as the result passes up to the global
stylesheet in the grammar expected, you can mix and match as needed.
Everything still works.
Essentially, named style and media blocks create their own lexical scope. When a named style or media is selected by a StyleChooser, MediaChooser, or other plug-in and there are other processing directives that match for the same request, the processors from the named blocks are always added last. Let’s break out the processing order stylesheets again and examine the details:
# the site's DocumentRoot <Directory /www/mysite.com/> # First, add the QueryString StyleChooser so you can easily select named styles AxAddPlugin Apache::AxKit::StyleChooser::QueryString # Add a simple named style <AxStyleName styleone> AxAddProcessor text/xsl /styles/beta.xsl AxAddProcessor text/xsl /styles/gamma.xsl </AxStyleName> # Add a 'global' style AxAddProcessor text/xsl /styles/alpha.xsl </Directory>
A request for the minimal.xml
file in the
directory that does not specify a preferred style yields the expected
result—only the alpha.xsl
stylesheet is
applied:
<?xml version="1.0"?> <root>Base content . . . Alpha processed</root>
But what happens if you select the named style for a request to the
same document? Given Rule 1, you may expect the processors in the
named style block to be applied first, since the named block appears
before the global style. This is not the case. A request for
minimal.xml
with
style=styleone
in the query string gives the
following:
<?xml version="1.0"?> <root>Base content . . . Alpha processed . . . Beta processed . . . Gamma processed</root>
The styles contained in the <AxStyleName>
block are added according to Rule 1. (They are at the same lexical
level and are, therefore, added in the order they appear in the
configuration file.) Because they are contained in that block, they
are added last to the processing chain, after
the unnamed, global style (alpha.xsl
).
The "named processors are always last” rule also applies in cases in which a named block appears at a lower level in the document hierarchy than any matching unnamed directives. This creates a partial exception to Rule 3.
# the site's DocumentRoot <Directory /www/mysite.com/> # Add the QueryString StyleChooser so you can easily select named styles AxAddPlugin Apache::AxKit::StyleChooser::QueryString # Add a 'global' style AxAddProcessor text/xsl /styles/alpha.xsl </Directory> # A first-level subdirectory <Directory /www/mysite.com/levelone/> # Add a simple named style <AxStyleName styleone> AxAddProcessor text/xsl /styles/beta.xsl AxAddProcessor text/xsl /styles/gamma.xsl </AxStyleName> </Directory>
Here, a request for http://mysite.com/levelone/minimal.xml?style=styleone gives the same result as before, despite the fact that the matching named style block is defined at a lower level in the directory tree than the global stylesheet configured for the site’s DocumentRoot:
<?xml version="1.0"?> <root>Base content . . . Alpha processed . . . Beta processed . . . Gamma processed</root>
One final, admittedly pernicious, example reveals precisely what’s going on:
# the site's DocumentRoot <Directory /www/mysite.com/> # Add the QueryString StyleChooser so you can easily select named styles AxAddPlugin Apache::AxKit::StyleChooser::QueryString # Add a 'global' style AxAddProcessor text/xsl /styles/alpha.xsl # Add a simple named style <AxStyleName styleone> AxAddProcessor text/xsl /styles/beta.xsl AxAddProcessor text/xsl /styles/gamma.xsl </AxStyleName> </Directory> # A first-level subdirectory <Directory /www/mysite.com/levelone/> # another 'global' style AxAddProcessor text/xsl /styles/delta.xsl # The style name here is the same as the one in the parent directory! <AxStyleName styleone> AxAddProcessor text/xsl /styles/eplison.xsl AxAddProcessor text/xsl /styles/zeta.xsl </AxStyleName> </Directory>
Here, a request to http://mysite.com/levelone/minimal.xml?style=styleone results in the following:
<?xml version="1.0"?> <root>Base content . . . Delta processed . . . Alpha processed . . . Epsilon processed . . . Zeta processed . . . Beta processed . . . Gamma processed</root>
Surprised? Let’s take things step by step:
The global
alpha.xsl
stylesheet is added to the processing chain.The global processor
delta.xsl
is configured for a directory at a lower level and is, therefore, prepended to the list of processors, according to Rule 3.The style named
styleone
is selected, so the two processors contained in the named block at the root level of the site (beta.xsl
andgamma.xsl
) are added in configuration order, according to Rule 1.The two processors (
epsilon.xsl
andzeta.xsl
) in the named block in thelevelone
directory are also added in configuration order, according to Rule 1. Since they appear at a lower level in the directory tree, they are added, as a group, to the list of named styles before the two in the parent directory, according to Rule 3.All processors associated with a named style or media block are added to the processing chain after all those that are not, according to Rule 4.
The simplest way to conceptualize the processing order for rare cases
such as this is that all processing directives not contained in an
<AxStyleName>
or
<AxMediaType>
blocks are combined into one
list, according to Rules 1-3; a second list of those contained in a
named style or media block is created, also using Rules 1-3; then the
two lists are combined, with the “named styles
list” always appearing last.
If all this seems a bit much, rest assured. Cases such as this last example are extremely rare in the real world. The example here is particularly nasty only because it illustrates every detail of how AxKit creates its processing chain.
Now that you have had a close look at AxKit’s many options for applying different transformative styles to XML documents. It is time to look at how those transformations happen by examining two of the more popular languages that it supports for transforming content: XSLT and XPathScript.
Get XML Publishing with AxKit now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.