-wdocbook.xml
wdocbook.xsl
-wdocbook.html
weave.xml
weave.xsl
weave.html
tangle.xml
tangle.xsl
tangle.html
+xtangle.xsl
--- /dev/null
+<?xml version="1.0"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ version="1.0">
+
+<xsl:import href="../../xsl/fo/docbook.xsl"/>
+
+<xsl:param name="page.margin.outer" select="'1in'"/>
+<xsl:param name="title.margin.left" select="'-0pt'"/>
+
+<xsl:template match="revhistory" mode="titlepage.mode"/>
+
+<xsl:template match="affiliation" mode="titlepage.mode">
+ <fo:block font-weight="normal" font-size="12pt">
+ <xsl:apply-templates mode="titlepage.mode"/>
+ </fo:block>
+</xsl:template>
+
+<xsl:template match="phrase[@condition='online']"/>
+
+</xsl:stylesheet>
--- /dev/null
+<?xml version="1.0"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="1.0">
+
+<xsl:import href="../../xsl/html/docbook.xsl"/>
+
+<xsl:output method="html"/>
+
+<xsl:param name="html.stylesheet" select="'paper.css'"/>
+
+<xsl:template match="revhistory" mode="titlepage.mode"/>
+
+</xsl:stylesheet>
include ../cvstools/Makefile.incl
-all: tangle.xsl weave.xsl wdocbook.xsl
+all: tangle.xsl weave.xsl wdocbook.xsl xtangle.xsl
$(MAKE) -C html
+ $(MAKE) -C example
-xml: tangle.xml weave.xml wdocbook.xml
+xml: tangle.xml weave.xml
+ $(MAKE) -C html xml
+ $(MAKE) -C example xml
-html: xml tangle.html weave.html wdocbook.html
+html: xml tangle.html weave.html
+ $(MAKE) -C html html
+ $(MAKE) -C example html
-tangle.html: tangle.xml html/ldocbook.xsl
+%.html: %.xml html/ldocbook.xsl
$(XSLT) $< html/ldocbook.xsl $@
-tangle.xml: tangle.xweb wdocbook.xsl
- $(XSLT) $< wdocbook.xsl $@
+%.fo: %.xml fo/ldocbook.xsl
+ $(XSLT) $< fo/ldocbook.xsl $@
+
+%.pdf: %.fo
+ xep $< $@
+ rm -f $@
# Can this rule be improved? Ideally bootstrap-tangle should only be
# required if tangle.xsl doesn't exist. If it exists but is out-of-date
# it can rebuild itself.
-tangle.xsl: tangle.xweb bootstrap-tangle.xsl
- $(XSLT) $< bootstrap-tangle.xsl .temp.xsl
- $(XSLT) $< .temp.xsl tangle.xsl
- rm -f .temp.xsl
+xtangle.xsl: tangle.xweb bootstrap-tangle.xsl
+ $(XSLT) $< bootstrap-tangle.xsl .boot.tangle.xsl
+ $(XSLT) $< .boot.tangle.xsl $@ top=xtop
+ rm -f .boot.tangle.xsl
+
+tangle.xsl: tangle.xweb xtangle.xsl
+ $(XSLT) $< xtangle.xsl $@
+
+tangle.xml: tangle.xweb wdocbook.xsl
+ $(XSLT) $< wdocbook.xsl $@
-weave.html: weave.xml html/ldocbook.xsl
- $(XSLT) $< html/ldocbook.xsl $@
+weave.xsl: weave.xweb xtangle.xsl
+ $(XSLT) $< xtangle.xsl $@
weave.xml: weave.xweb wdocbook.xsl
$(XSLT) $< wdocbook.xsl $@
xjparse $@
-weave.xsl: weave.xweb tangle.xsl
- $(XSLT) $< tangle.xsl $@
+wdocbook.xsl: weave.xweb xtangle.xsl weave.xsl
+ $(XSLT) $< xtangle.xsl $@ top=wdocbook
-wdocbook.html: wdocbook.xml wdocbook.xsl
- $(XSLT) $< wdocbook.xsl $@
+realclean: clean
+ rm -f xtangle.xsl
+ rm -f weave.xsl wdocbook.xsl
+ rm -f tangle.xsl
+ $(MAKE) -C html realclean
+ $(MAKE) -C fo realclean
+ $(MAKE) -C example realclean
+
+clean:
+ rm -f weave.{xml,html,fo,pdf}
+ rm -f tangle.{xml,html,fo,pdf}
+ $(MAKE) -C html clean
+ $(MAKE) -C fo clean
+ $(MAKE) -C example clean
-wdocbook.xml: wdocbook.xweb wdocbook.xsl
- $(XSLT) $< wdocbook.xsl $@
-wdocbook.xsl: wdocbook.xweb tangle.xsl
- $(XSLT) $< tangle.xsl $@
-clean:
- rm -f weave.{xsl,xml,html}
- rm -f tangle.{xsl,xml,html}
- rm -f wdocbook.{xsl,xml,html}
<xsl:output method="xml"/>
+<xsl:param name="top" select="'xtop'"/>
+
<xsl:template match="/">
- <xsl:apply-templates select="//src:topfragment"/>
+ <xsl:apply-templates select="//src:fragment[@id=$top]"/>
</xsl:template>
<xsl:template match="text()"/>
<xsl:apply-templates/>
</xsl:template>
-<xsl:template match="src:topfragment">
- <xsl:apply-templates mode="copy"/>
-</xsl:template>
-
<xsl:template match="src:fragment">
<xsl:apply-templates mode="copy"/>
</xsl:template>
--- /dev/null
+doc.html
+doc.xml
+doc.xpp
+doc.xsd
+fib.html
+fib.pl
+fib.xml
+fib.xpp
--- /dev/null
+include ../../cvstools/Makefile.incl
+
+all: fib.pl doc.xsd fib.xpp doc.xpp
+
+html: doc.html fib.html
+
+xml: doc.xml fib.xml
+
+doc.html: doc.xml
+ $(XSLT) $< doc.xsl $@
+
+doc.xml: doc.xweb
+ $(XSLT) $< ../wdocbook.xsl $@
+
+doc.xsd: doc.xweb
+ $(XSLT) $< ../xtangle.xsl $@
+
+doc.xpp: doc.xweb prettyprint.pl
+ perl prettyprint.pl $< > $@
+
+fib.html: fib.xml
+ $(XSLT) $< ../html/ldocbook.xsl $@
+
+fib.fo: fib.xml
+ $(XSLT) $< ../fo/ldocbook.xsl $@
+
+fib.pdf: fib.fo
+ xep $< $@
+
+fib.pl: fib.xweb
+ $(XSLT) $< ../tangle.xsl $@
+
+fib.xml: fib.xweb
+ $(XSLT) $< ../wdocbook.xsl $@
+
+fib.xpp: fib.xweb prettyprint.pl
+ perl prettyprint.pl $< > $@
+
+realclean:
+ rm -f doc.{xsd,xpp}
+ rm -f fib.{pl,xpp}
+
+clean:
+ rm -f doc.{html,xml,fo,pdf}
+ rm -f fib.{html,xml,fo,pdf}
--- /dev/null
+<?xml version="1.0"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:src="http://nwalsh.com/xmlns/litprog/fragment"
+ xmlns:h="http://www.w3.org/TR/xhtml-basic"
+ exclude-result-prefixes="h"
+ version="1.0">
+
+<xsl:output method="html"/>
+
+<xsl:template match="*">
+ <xsl:element name="{name(.)}" namespace="{namespace-uri(.)}">
+ <xsl:copy-of select="@*"/>
+ <xsl:apply-templates/>
+ </xsl:element>
+</xsl:template>
+
+<xsl:template match="src:fragment" priority="2">
+ <table border="1" width="100%" summary="Source Fragment">
+ <tr>
+ <td>
+ <a name="{@id}"/>
+ <b>
+ <xsl:apply-templates select="." mode="label.markup"/>
+ </b>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <pre>
+ <xsl:apply-templates/>
+ </pre>
+ </td>
+ </tr>
+ </table>
+</xsl:template>
+
+<xsl:template match="src:fragref" priority="2">
+ <xsl:variable name="linkend" select="@linkend"/>
+ <a href="#{$linkend}">
+ <xsl:apply-templates select="//*[@id = $linkend]" mode="label.markup"/>
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates select="//*[@id = $linkend]" mode="title.markup"/>
+ </a>
+</xsl:template>
+
+<xsl:template match="src:fragment" mode="label.markup">
+ <xsl:text>§</xsl:text>
+ <xsl:number from="/" level="any"/>
+ <xsl:text>.</xsl:text>
+</xsl:template>
+
+<xsl:template match="src:fragment" mode="title.markup">
+ <xsl:variable name="div" select="ancestor::h:div[1]"/>
+ <xsl:if test="$div">
+ <xsl:variable name="title" select="($div/h:h1[1]
+ |$div/h:h2[1]
+ |$div/h:h3[1]
+ |$div/h:h4[1]
+ |$div/h:h5[1]
+ |$div/h:h6[1])[1]"/>
+ <xsl:value-of select="$title"/>
+ </xsl:if>
+</xsl:template>
+
+</xsl:stylesheet>
--- /dev/null
+<html xmlns="http://www.w3.org/TR/xhtml-basic"
+ xmlns:xs='http://www.w3.org/2001/XMLSchema'
+ xmlns:src="http://nwalsh.com/xmlns/litprog/fragment"
+ xmlns:ex='urn:publicid:-:Norman+Walsh:Schema Example:EN'>
+<head>
+<title>Document Schema</title>
+</head>
+
+<div>
+<h1>A Simple Document W3C XML Schema</h1>
+
+<p>This schema defines elements in the
+<tt>urn:publicid:-:Norman+Walsh:Schema Example:EN</tt> namespace.</p>
+
+<p>This schema defines several complex types and several elements that
+are instances of those types.</p>
+
+<src:fragment id="top" mundane-result-prefixes="ex xs">
+<xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema'
+ xmlns:ex='urn:publicid:-:Norman+Walsh:Schema Example:EN'
+ targetNamespace='urn:publicid:-:Norman+Walsh:Schema Example:EN'
+ elementFormDefault='qualified'>
+ <src:fragref linkend="types"/>
+ <src:fragref linkend="elements"/>
+</xs:schema>
+</src:fragment>
+</div>
+
+<div>
+<h1><a name="types"/>The Complex Types</h1>
+
+<p>There are three complex types in this schema: <tt>doc</tt>,
+<tt>title</tt>, and <tt>para</tt>.</p>
+
+<src:fragment id="types">
+ <src:fragref linkend="doc.type"/>
+ <src:fragref linkend="title.type"/>
+ <src:fragref linkend="para.type"/>
+</src:fragment>
+
+<div>
+<h2>The <tt>doc</tt> Type</h2>
+
+<src:fragment id="doc.type">
+ <xs:complexType name='doc'>
+ <xs:sequence>
+ <xs:element ref="ex:title" minOccurs='0' maxOccurs='1'/>
+ <xs:choice minOccurs='1' maxOccurs='unbounded'>
+ <xs:element ref='ex:para'/>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+</src:fragment>
+</div>
+
+<div>
+<h2>The <tt>title</tt> Type</h2>
+
+<src:fragment id="title.type">
+ <xs:complexType name='title' mixed="true">
+ <src:fragref linkend="role.attrib"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+</src:fragment>
+
+<div>
+<h2>The <tt>role</tt> Attribute</h2>
+
+<p>The role attribute is an optional string.</p>
+
+<src:fragment id="role.attrib">
+ <xs:attribute name="role" type="xs:string"/>
+</src:fragment>
+</div>
+</div>
+
+<div>
+
+<h2>The <tt>para</tt> Type</h2>
+
+<src:fragment id="para.type">
+ <xs:complexType name='para' mixed="true">
+ <src:fragref linkend="role.attrib"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+</src:fragment>
+</div>
+</div>
+
+<div>
+<h1>The Elements</h1>
+
+<p>This schema defines one element of each <a href="#types">complex
+type</a>.</p>
+
+<src:fragment id="elements">
+ <xs:element name="doc" type="ex:doc"/>
+ <xs:element name="para" type="ex:para"/>
+ <xs:element name="title" type="ex:title"/>
+</src:fragment>
+</div>
+</html>
\ No newline at end of file
--- /dev/null
+<article xmlns:src="http://nwalsh.com/xmlns/litprog/fragment"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+<articleinfo>
+<title>Calculating Members of the Fibonacci Series</title>
+<author>
+ <firstname>Norman</firstname>
+ <surname>Walsh</surname>
+</author>
+</articleinfo>
+
+<para>This trivial document describes a simple, recursive
+implementation of the Fibonacci series in Perl. The principal
+motivation for this document is to demonstrate the use of Literate XML.
+</para>
+
+<section><title>The <function>fib</function> Function</title>
+
+<para>The heart of this program is the recursive function that
+calculates the members of the Fibonacci series.</para>
+
+<para>The first and second members of the Fibonnacci series are
+<quote>1</quote>; all other values are calculated recursively.</para>
+
+<src:fragment id="sub.fib">
+sub fib {
+ my $n = shift;
+
+ if ($n <= 2) {
+ return 1;
+ } else {
+ return <src:fragref linkend="sub.fib.recursion"/>
+ }
+}
+</src:fragment>
+
+<section><title>Recursive Definition</title>
+
+<para>The Fibonacci series begins: 1 1 2 3 5 8 13... Each member of
+the series, after the first two, is the sum of the preceding two
+members.</para>
+
+<para>This can be implemented recursively by calculating the preceding
+two members of the series and returning their sum:</para>
+
+<src:fragment id="sub.fib.recursion">
+&fib($n-2) + &fib($n-1);
+</src:fragment>
+
+</section>
+</section>
+
+<section><title>Preamble</title>
+
+<para>The program preamble simply establishes a default location for
+the Perl executable and informs the interpreter that we want to use
+the strict pragma.</para>
+
+<src:fragment id="preamble">
+#!/usr/bin/perl -w
+
+use strict;
+</src:fragment>
+</section>
+
+<section><title>Argument Checking</title>
+
+<para>This program expects its argument on the command line and it expects
+that argument to be an unsigned decimal integer.</para>
+
+<src:fragment id="argcheck">
+my $num = shift @ARGV || die;
+
+die "Not a number: $num\n" if $num !~ /^\d+$/;
+</src:fragment>
+</section>
+
+<section><title>The Program</title>
+
+<para>The program prints out the Fibonacci number requested:</para>
+
+<src:fragment id="top">
+<src:fragref linkend="preamble"/>
+<src:fragref linkend="argcheck"/>
+
+print "Fib($num) = ", &fib($num), "\n";
+
+<src:fragref linkend="sub.fib"/>
+</src:fragment>
+</section>
+
+</article>
--- /dev/null
+#!/usr/bin/perl -w
+
+use strict;
+
+my $file = shift @ARGV || die "Usage: $0 file.xml\n";
+
+open (F, $file);
+read (F, $_, -s $file);
+close (F);
+
+print "<programlisting>";
+
+if (/<!DOCTYPE/) {
+ &prettyprint($`);
+ $_ = $& . $';
+
+ if (/<!DOCTYPE\s*\S+\s*\[.*?\]>/s) {
+ print &charescape($&);
+ &prettyprint($');
+ } elsif (/<!DOCTYPE.*>/) {
+ print &charescape($&);
+ &prettyprint($');
+ } else {
+ die "Unparseable !DOCTYPE declaration.\n";
+ }
+} else {
+ &prettyprint($_);
+}
+
+print "</programlisting>\n";
+
+sub prettyprint {
+ local $_ = shift;
+
+ while (/<(.*?)>/s) {
+ my $post = $';
+ $_ = $1;
+
+ print &escape($`);
+
+ if (/^\?(.*)\?$/s) {
+ print &pi($1);
+ } elsif (/^\!--(.*)--$/s) {
+ print &comment($1);
+ } elsif (/^\/(.*)$/s) {
+ print &endtag($1);
+ } else {
+ print &starttag($_);
+ }
+
+ $_ = $post;
+ }
+
+ print &escape($_);
+}
+
+sub escape {
+ local $_ = shift;
+ my $ret = "";
+
+ while (/&(\S+?);/) {
+ my $pre = $`;
+ my $ent = $1;
+ my $post = $';
+
+ $ret .= &charescape($pre);
+ if ($ent =~ /^\#/) {
+ $ret .= &sgmltag('numcharref', $ent);
+ } else {
+ $ret .= &sgmltag('genentity', $ent);
+ }
+
+ $_ = $post;
+ }
+
+ return $ret . &charescape($_);
+}
+
+sub charescape {
+ local $_ = shift;
+
+ s/&/&/sg;
+ s/</</sg;
+ s/>/>/sg;
+
+ return $_;
+}
+
+sub sgmltag {
+ my $class = shift;
+ my $content = shift;
+
+# my $tagname = "???";
+# $tagname = $1 if $content =~ s/^(\S+)/$1/s;
+# $tagname =~ s/:/-/sg;
+
+ return "<sgmltag class=\"$class\">$content</sgmltag>"
+}
+
+sub pi {
+ return &sgmltag('xmlpi', $_[0]);
+}
+
+sub comment {
+ return &sgmltag('sgmlcomment', $_[0]);
+}
+
+sub starttag {
+ my $content = shift;
+
+ if ($content =~ /\/$/) {
+ return &sgmltag('emptytag', $`);
+ } else {
+ return &sgmltag('starttag', $content);
+ }
+}
+
+sub endtag {
+ return &sgmltag('endtag', $_[0]);
+}
--- /dev/null
+ldocbook.xsl
--- /dev/null
+include ../../cvstools/Makefile.incl
+
+all: ldocbook.xsl
+
+xml:
+ echo Nothing to do here, build xml in ../html instead
+
+html:
+ echo Nothing to do here, build html in ../html instead
+
+ldocbook.xsl: ../html/ldocbook.xweb ../xtangle.xsl
+ $(XSLT) $< ../xtangle.xsl $@ top=fo-top
+
+realclean: clean
+ rm -f ldocbook.xsl
+
+clean:
+ rm -f ldocbook.{html,xml}
ldocbook.xml: ldocbook.xweb ../wdocbook.xsl
$(XSLT) $< ../wdocbook.xsl $@
-ldocbook.xsl: ldocbook.xweb ../tangle.xsl
- $(XSLT) $< ../tangle.xsl $@
+ldocbook.xsl: ldocbook.xweb ../xtangle.xsl
+ $(XSLT) $< ../xtangle.xsl $@
+
+realclean: clean
+ rm -f ldocbook.xsl
clean:
- rm -f ldocbook.html ldocbook.xml ldocbook.xsl
+ rm -f ldocbook.html ldocbook.xml
+
+<!DOCTYPE article [
+<!ENTITY xweb "<acronym>XWEB</acronym>">
+<!ENTITY lt "&#60;"> <!-- LESS-THAN SIGN -->
+<!ENTITY gt ">"> <!-- GREATER-THAN SIGN -->
+<!ENTITY amp "&#38;"> <!-- AMPERSAND -->
+]>
<article xmlns:src="http://nwalsh.com/xmlns/litprog/fragment"
- xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
-<title>Litprog.xsl</title>
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:fo="http://www.w3.org/1999/XSL/Format">
+<articleinfo>
+<title>Literate DocBook XML Stylesheets</title>
+<subtitle>Part of
+<citetitle>Literate Programming in XML</citetitle></subtitle>
+<pubdate>05 Oct 2001</pubdate>
+<releaseinfo role="meta">
+$Id$
+</releaseinfo>
-<src:topfragment id="top" default-exclude-result-prefixes="xsl">
+<revhistory>
+<revision>
+<revnumber>0.1</revnumber>
+<date>05 Oct 2001</date>
+<authorinitials>ndw</authorinitials>
+<revremark>Initial draft.</revremark>
+</revision>
+</revhistory>
+
+<author><firstname>Norman</firstname><surname>Walsh</surname>
+</author>
+</articleinfo>
+
+<!-- ============================================================ -->
+
+<para>The <filename>ldocbook.xsl</filename> stylesheet transforms an
+&xweb; documentation file (a <quote>woven</quote> &xweb; document)
+into HTML documentation.</para>
+
+<para>This stylesheet assumes that fragments appear inside DocBook
+<sgmltag>section</sgmltag> elements. Some additional customization of
+the <src:fragref linkend="fragment-label"/> and
+<src:fragref linkend="fragment-title"/> will be required
+to support a different organization.</para>
+
+<section><title>The Stylesheets</title>
+
+<para>This document describes both the HTML and XSL Formatting Objects
+versions of the stylesheet.</para>
+
+<section><title>The HTML Stylesheet</title>
+
+<para>The stylesheet performs some initialization and establishes
+templates for the Literate Programming elements that can appear in
+the documentation. Most of the documentation elements will be
+DocBook elements handled by the imported stylesheet.</para>
+
+<src:fragment id="top" mundane-result-prefixes="xsl fo verb">
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:src="http://nwalsh.com/xmlns/litprog/fragment"
xmlns:verb="com.nwalsh.saxon.Verbatim"
exclude-result-prefixes="verb src"
version="1.0">
- <src:fragref linkend="rest"/>
+ <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl"/>
+
+ <src:fragref linkend="init"/>
+ <src:fragref linkend="proc-html-fragment"/>
+ <src:fragref linkend="proc-html-fragref"/>
</xsl:stylesheet>
-</src:topfragment>
+</src:fragment>
-<section><title>The Rest</title>
+</section>
+<section><title>The FO Stylesheet</title>
-<src:fragment id="rest">
-<xsl:import href="../xsl/html/docbook.xsl"/>
+<para>The stylesheet performs some initialization and establishes
+templates for the Literate Programming elements that can appear in
+the documentation. Most of the documentation elements will be
+DocBook elements handled by the imported stylesheet.</para>
+
+<src:fragment id="fo-top" mundane-result-prefixes="xsl fo verb">
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ xmlns:src="http://nwalsh.com/xmlns/litprog/fragment"
+ xmlns:verb="com.nwalsh.saxon.Verbatim"
+ exclude-result-prefixes="verb src fo"
+ version="1.0">
+
+ <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/fo/docbook.xsl"/>
+
+ <xsl:param name="page.margin.outer" select="'1in'"/>
+ <xsl:param name="title.margin.left" select="'-0pt'"/>
+
+ <src:fragref linkend="init"/>
+ <src:fragref linkend="proc-fo-fragment"/>
+ <src:fragref linkend="proc-fo-fragref"/>
+</xsl:stylesheet>
+</src:fragment>
+</section>
+
+<section><title>Initialization</title>
+
+<para>The stylesheet initializes the processor by loading its version
+information (stored in a separate file because it is shared by several
+stylesheets), importing the base DocBook stylesheet, setting some
+parameters, and setting up
+some internationalized text.</para>
+
+<src:fragment id="init">
+ <xsl:include href="../VERSION"/>
+
+ <xsl:template match="revhistory" mode="titlepage.mode"/>
+
+ <src:fragref linkend="param"/>
+ <src:fragref linkend="i18n"/>
+</src:fragment>
+<section><title>Parameters</title>
+
+<para>In DocBook Literate Programming documentation, we always number
+sections and verbatim environments.</para>
+
+<src:fragment id="param">
+<xsl:param name="section.autolabel" select="1"/>
+<xsl:param name="linenumbering.everyNth" select="5"/>
+<xsl:param name="linenumbering.separator" select="'| '"/>
+</src:fragment>
+</section>
+
+<section><title>Localization</title>
+
+<para>Localized text in the DocBook stylesheets makes use of an
+external document to determine the nature of, among other things,
+element titles and cross references.</para>
+
+<para>Cross references to <sgmltag>src:fragments</sgmltag> are not
+supported in the stock stylesheets (since there is no such element
+in DocBook).</para>
+
+<para>In order to make cross references work properly, we must provide
+an appropriate localization for <sgmltag>src:fragment</sgmltag> titles.
+We do this by pointing the <parameter>local.l10n.xml</parameter>
+<emphasis>at ourselves</emphasis> (at the stylesheet document).</para>
+
+<para>In our local localization document, we add an
+<sgmltag>l:i18n</sgmltag> element to provide the necessary context.</para>
+
+<para>If you want to support DocBook Literate Programming in other
+languages, this section will have to be updated.</para>
+
+<src:fragment id="i18n">
<xsl:param name="local.l10n.xml" select="document('')"/>
<l:i18n xmlns:l="http://docbook.sourceforge.net/xmlns/l10n/1.0">
</l:context>
</l:l10n>
</l:i18n>
+</src:fragment>
+</section>
+</section>
+</section>
-<xsl:param name="section.autolabel" select="1"/>
-<xsl:param name="linenumbering.everyNth" select="'3'"/>
-<xsl:param name="linenumbering.separator" select="'|'"/>
+<section><title>Process HTML Fragments</title>
-<xsl:output method="html"/>
+<para>Fragments are output as verbatim environments with numbered lines,
+if possible. A table is used to make the listings stand out.</para>
+<src:fragment id="proc-html-fragment">
<xsl:template match="src:fragment">
<xsl:param name="suppress-numbers" select="'0'"/>
<xsl:param name="linenumbering" select="'numbered'"/>
+
<xsl:variable name="section" select="ancestor::section[1]"/>
+ <xsl:variable name="id" select="@id"/>
+ <xsl:variable name="referents"
+ select="//src:fragment[.//src:fragref[@linkend=$id]]"/>
+ <div class="src-fragment">
<a name="{@id}"/>
<table border="1" width="100%">
<tr>
<td>
<p>
<b>
- <xsl:text>§</xsl:text>
- <xsl:apply-templates select="$section" mode="label.markup"/>
- <xsl:number from="section"/>
+ <xsl:apply-templates select="." mode="label.markup"/>
</b>
+ <xsl:if test="$referents">
+ <xsl:text>: </xsl:text>
+ <xsl:for-each select="$referents">
+ <xsl:if test="position() > 1">, </xsl:if>
+ <a href="#{@id}">
+ <xsl:apply-templates select="." mode="label.markup"/>
+ </a>
+ </xsl:for-each>
+ </xsl:if>
</p>
</td>
</tr>
</td>
</tr>
</table>
+ </div>
</xsl:template>
-<xsl:template match="src:fragref">
- <xsl:variable name="targets" select="id(@linkend)"/>
- <xsl:variable name="target" select="$targets[1]"/>
- <xsl:variable name="refelem" select="local-name($target)"/>
+<src:fragref linkend="fragment-label"/>
+<src:fragref linkend="fragment-title"/>
+</src:fragment>
- <xsl:call-template name="check.id.unique">
- <xsl:with-param name="linkend" select="@linkend"/>
- </xsl:call-template>
+<para>Fragment labels and titles must also be supported.</para>
- <i><xsl:apply-templates/></i>
- <xsl:text> (</xsl:text>
+<section><title>Fragment Labels</title>
- <xsl:choose>
- <xsl:when test="$refelem=''">
- <xsl:message>
- <xsl:text>XRef to nonexistent id: </xsl:text>
- <xsl:value-of select="@linkend"/>
- </xsl:message>
- <xsl:text>???</xsl:text>
- </xsl:when>
+<para>A fragment label consists of a section mark followed by the
+label of the current section and the number of the fragment within
+that section.</para>
- <xsl:when test="$target/@xreflabel">
- <a>
- <xsl:attribute name="href">
- <xsl:call-template name="href.target">
- <xsl:with-param name="object" select="$target"/>
- </xsl:call-template>
- </xsl:attribute>
- <xsl:call-template name="xref.xreflabel">
- <xsl:with-param name="target" select="$target"/>
- </xsl:call-template>
- </a>
- </xsl:when>
+<src:fragment id="fragment-label">
+<xsl:template match="src:fragment" mode="label.markup">
+ <xsl:variable name="section" select="ancestor::section[1]"/>
- <xsl:otherwise>
- <a>
- <xsl:attribute name="href">
- <xsl:call-template name="href.target">
- <xsl:with-param name="object" select="$target"/>
- </xsl:call-template>
- </xsl:attribute>
+ <xsl:text>§</xsl:text>
- <xsl:apply-templates select="id(@linkend)" mode="xref-to-section"/>
- </a>
+ <xsl:choose>
+ <xsl:when test="$section">
+ <xsl:variable name="section.label">
+ <xsl:apply-templates select="$section" mode="label.markup"/>
+ </xsl:variable>
+ <xsl:choose>
+ <xsl:when test="string($section.label) = ''">
+ <xsl:number from="section"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:copy-of select="$section.label"/>
+ <xsl:text>.</xsl:text>
+ <xsl:number from="section"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:number from="/"/>
</xsl:otherwise>
</xsl:choose>
- <xsl:text>)</xsl:text>
</xsl:template>
+</src:fragment>
+</section>
-<xsl:template match="src:fragment" mode="xref-to">
+<section><title>Fragment Titles</title>
+
+<para>A fragment title is the title of the section that contains it.
+</para>
+
+<src:fragment id="fragment-title">
+<xsl:template match="src:fragment" mode="title.markup">
<xsl:variable name="section" select="ancestor::section[1]"/>
- <i>
- <xsl:text>§</xsl:text>
- <xsl:apply-templates select="$section" mode="label.markup"/>
- <xsl:number from="section"/>
- <xsl:text>. </xsl:text>
+ <xsl:if test="$section">
<xsl:apply-templates select="$section" mode="title.markup"/>
- </i>
+ </xsl:if>
+</xsl:template>
+</src:fragment>
+</section>
+</section>
+
+<section><title>Process FO Fragments</title>
+
+<para>Fragments are output as verbatim environments with numbered lines,
+if possible.</para>
+
+<src:fragment id="proc-fo-fragment">
+<xsl:template match="src:fragment">
+ <xsl:param name="suppress-numbers" select="'0'"/>
+ <xsl:param name="linenumbering" select="'numbered'"/>
+
+ <xsl:variable name="section" select="ancestor::section[1]"/>
+ <xsl:variable name="id" select="@id"/>
+ <xsl:variable name="referents"
+ select="//src:fragment[.//src:fragref[@linkend=$id]]"/>
+
+ <fo:block xsl:use-attribute-sets="formal.object.properties"
+ keep-together="always"
+ border-style="solid"
+ border-width="0pt"
+ border-before-width="0.25pt"
+ border-after-width="0.25pt">
+ <fo:block id="{@id}"
+ border-width="0pt"
+ border-style="solid"
+ border-after-width="0.25pt">
+ <fo:inline font-weight="bold">
+ <xsl:apply-templates select="." mode="label.markup"/>
+ </fo:inline>
+ <fo:inline>
+ <xsl:if test="$referents">
+ <xsl:text>: </xsl:text>
+ <xsl:for-each select="$referents">
+ <xsl:if test="position() > 1">, </xsl:if>
+ <xsl:apply-templates select="." mode="label.markup"/>
+ </xsl:for-each>
+ </xsl:if>
+ </fo:inline>
+ </fo:block>
+
+ <fo:block wrap-option='no-wrap'
+ text-align='start'
+ white-space-collapse='false'
+ linefeed-treatment="preserve"
+ xsl:use-attribute-sets="monospace.verbatim.properties">
+ <xsl:variable name="rtf">
+ <xsl:apply-templates/>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="$suppress-numbers = '0'
+ and $linenumbering = 'numbered'
+ and $use.extensions != '0'
+ and $linenumbering.extension != '0'">
+ <xsl:call-template name="number.rtf.lines">
+ <xsl:with-param name="rtf" select="$rtf"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:copy-of select="$rtf"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </fo:block>
+ </fo:block>
+</xsl:template>
+
+<src:fragref linkend="fragment-label"/>
+<src:fragref linkend="fragment-title"/>
+</src:fragment>
+</section>
+
+<section><title>Process HTML Fragment References</title>
+
+<para>A fragment reference has exactly the same semantics as a DocBook
+<sgmltag>xref</sgmltag>.</para>
+
+<src:fragment id="proc-html-fragref">
+<xsl:template match="src:fragref">
+ <xsl:call-template name="xref"/>
</xsl:template>
+<src:fragref linkend="fragref-html-xref-to"/>
+</src:fragment>
+
+<section><title>Fragment Cross-Reference Text</title>
-<xsl:template match="src:fragment" mode="xref-to-section">
+<para>Cross references to fragments produce the fragment label
+and title.</para>
+
+<src:fragment id="fragref-html-xref-to">
+<xsl:template match="src:fragment" mode="xref-to">
<xsl:variable name="section" select="ancestor::section[1]"/>
<i>
- <xsl:text>§</xsl:text>
- <xsl:apply-templates select="$section" mode="label.markup"/>
- <xsl:number from="section"/>
+ <xsl:apply-templates select="." mode="label.markup"/>
+ <xsl:text>. </xsl:text>
+ <xsl:apply-templates select="." mode="title.markup"/>
</i>
</xsl:template>
+</src:fragment>
+</section>
+</section>
-<xsl:template match="src:fragment" mode="title.markup">
- <xsl:text>SRC:FRAGMENT TITLE</xsl:text>
+<section><title>Process FO Fragment References</title>
+
+<para>A fragment reference has exactly the same semantics as a DocBook
+<sgmltag>xref</sgmltag>.</para>
+
+<src:fragment id="proc-fo-fragref">
+<xsl:template match="src:fragref">
+ <xsl:call-template name="xref"/>
</xsl:template>
+<src:fragref linkend="fragref-fo-xref-to"/>
+</src:fragment>
+
+<section><title>Fragment Cross-Reference Text</title>
+<para>Cross references to fragments produce the fragment label
+and title.</para>
+
+<src:fragment id="fragref-fo-xref-to">
+<xsl:template match="src:fragment" mode="xref-to">
+ <xsl:variable name="section" select="ancestor::section[1]"/>
+
+ <fo:inline font-style="italic">
+ <xsl:apply-templates select="." mode="label.markup"/>
+ <xsl:text>. </xsl:text>
+ <xsl:apply-templates select="." mode="title.markup"/>
+ </fo:inline>
+</xsl:template>
</src:fragment>
</section>
+</section>
</article>
+<!DOCTYPE article [
+<!ENTITY xweb "<acronym>XWEB</acronym>">
+]>
<article xmlns:src="http://nwalsh.com/xmlns/litprog/fragment"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
-<title>Tangle.xsl</title>
+<articleinfo>
+<title>Tangle</title>
+<subtitle>Part of
+<citetitle>Literate Programming in XML</citetitle></subtitle>
+<pubdate>05 Oct 2001</pubdate>
+<releaseinfo role="meta">
+$Id$
+</releaseinfo>
-<src:topfragment id="top" default-exclude-result-prefixes="xsl">
-<src:passthrough>
-<!DOCTYPE xsl:stylesheet [
-<!ENTITY nl "<xsl:text>
</xsl:text>">
-]>
-</src:passthrough>
+<revhistory>
+<revision>
+<revnumber>0.1</revnumber>
+<date>05 Oct 2001</date>
+<authorinitials>ndw</authorinitials>
+<revremark>Initial draft.</revremark>
+</revision>
+</revhistory>
+
+<author><firstname>Norman</firstname><surname>Walsh</surname>
+</author>
+</articleinfo>
+
+<!-- ============================================================ -->
+
+<para>The <filename>tangle.xsl</filename> stylesheet transforms an
+&xweb; document into a <quote>source code</quote> document. This
+is a relatively straightforward process: starting with the top fragment,
+all of the source fragments are simply stitched together, discarding any
+intervening documentation.</para>
+
+<para>The resulting <quote>tangled</quote> document is ready for use
+by the appropriate processor.</para>
+
+<section><title>The Stylesheet</title>
+
+<para>This &xweb; document contains the source for two stylesheets,
+<filename>tangle.xsl</filename> and <filename>xtangle.xsl</filename>.
+Both stylesheets produce tangled sources, the latter is a simple
+customization of the former for producing XML vocabularies.</para>
+
+<para>Each of these stylesheets performs some initialization, sets
+the output method appropriately, begins processing at the root template,
+and processes fragments, copying the content appropriately.</para>
+
+<section><title>The <filename>tangle.xsl</filename> Stylesheet</title>
+
+<para>The tangle stylesheet produces text output.</para>
+
+<src:fragment id="top" mundane-result-prefixes="xsl">
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:src="http://nwalsh.com/xmlns/litprog/fragment"
exclude-result-prefixes="src"
version="1.0">
- <xsl:include href="VERSION"/>
+ <src:fragref linkend="init"/>
+
+ <xsl:output method="text"/>
- <src:fragref linkend="space"/>
- <src:fragref linkend="output"/>
<src:fragref linkend="root.template"/>
- <src:fragref linkend="text"/>
- <src:fragref linkend="default"/>
- <src:fragref linkend="rest"/>
+ <src:fragref linkend="process-fragments"/>
+ <src:fragref linkend="copy-elements"/>
</xsl:stylesheet>
-</src:topfragment>
+</src:fragment>
+</section>
-<section><title>Stuff</title>
+<section><title>The <filename>xtangle.xsl</filename> Stylesheet</title>
-<src:fragment id="space">
-<xsl:preserve-space elements="*"/>
+<para>The xtangle stylesheet produces XML output.</para>
+
+<src:fragment id="xtop" mundane-result-prefixes="xsl">
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:src="http://nwalsh.com/xmlns/litprog/fragment"
+ exclude-result-prefixes="src"
+ version="1.0">
+
+ <src:fragref linkend="init"/>
+
+ <xsl:output method="xml"/>
+
+ <src:fragref linkend="root.template"/>
+ <src:fragref linkend="process-fragments"/>
+ <src:fragref linkend="copy-elements"/>
+ <src:fragref linkend="copy-xml-constructs"/>
+</xsl:stylesheet>
</src:fragment>
+</section>
+</section>
+
+<section><title>Initialization</title>
+
+<para>The stylesheet initializes the processor by loading its version
+information (stored in a separate file because it is shared by several
+stylesheets) and telling the processor to preserve whitespace on all
+input elements.</para>
+
+<para>The stylesheet also constructs a key for the ID values used on
+fragments. Because &xweb; documents do not have to be valid according
+to any particular DTD or Schema, the stylesheet cannot rely on having
+the IDs identified as type ID in the source document.</para>
+
+<src:fragment id="init">
+ <xsl:include href="VERSION"/>
+ <xsl:preserve-space elements="*"/>
+
+ <xsl:key name="fragment" match="src:fragment" use="@id"/>
+
+ <xsl:param name="top" select="'top'"/>
-<src:fragment id="output">
-<xsl:output method="xml"/>
</src:fragment>
+</section>
+
+<section><title>The Root Template</title>
+
+<para>The root template begins processing at the root of the &xweb;
+document. It outputs a couple of informative comments and then
+directs the processor to transform the <sgmltag>src:fragment</sgmltag>
+element with the <varname>$top</varname> ID.</para>
+
+<para>Source code fragments in the &xweb; document are not required
+to be sequential, so it is necessary to distinguish one fragment
+as the primary starting point.</para>
+
<src:fragment id="root.template">
<xsl:template match="/">
- <xsl:text>
</xsl:text>
- <xsl:comment>
- <xsl:text> This file was generated by tangle.xsl version </xsl:text>
- <xsl:value-of select="$VERSION"/>
- <xsl:text>. Do not edit! </xsl:text>
- </xsl:comment>
- <xsl:text>
</xsl:text>
- <xsl:comment> See http://sourceforge.net/projects/docbook/ </xsl:comment>
- <xsl:apply-templates select="//src:topfragment"/>
+ <xsl:apply-templates select="key('fragment', $top)"/>
</xsl:template>
</src:fragment>
+</section>
-<src:fragment id="text">
-<xsl:template match="text()"/>
-</src:fragment>
-
-<src:fragment id="default">
-<xsl:template match="*">
- <xsl:apply-templates/>
-</xsl:template>
-</src:fragment>
+<section><title>Processing Fragments</title>
-<src:fragment id="rest">
-<xsl:template match="src:topfragment">
- <xsl:apply-templates mode="copy"/>
-</xsl:template>
+<para>In order to <quote>tangle</quote> an &xweb; document, we need
+only copy the contents of the fragments to the result tree.
+Processing <sgmltag>src:fragment</sgmltag> elements is easy, simply copy
+their children:</para>
+<src:fragment id="process-fragments">
<xsl:template match="src:fragment">
<xsl:apply-templates mode="copy"/>
</xsl:template>
+</src:fragment>
+</section>
-<xsl:template match="*" mode="copy">
- <xsl:variable name="node" select="."/>
- <xsl:element name="{name(.)}" namespace="{namespace-uri(.)}">
- <xsl:for-each select="namespace::*">
- <xsl:if test="string(.) != namespace-uri($node)">
- <xsl:copy/>
- </xsl:if>
- </xsl:for-each>
- <xsl:copy-of select="@*"/>
- <xsl:apply-templates mode="copy"/>
- </xsl:element>
-</xsl:template>
+<section><title>Copying Elements</title>
+
+<para>Copying elements to the result tree can be divided into four
+cases: <link linkend="s.copy-passthrough">copying passthrough elements</link>,
+<link linkend="s.copy-fragref">copying fragment references</link>, and
+<link linkend="s.copy-default">copying everything else</link>.</para>
+
+<src:fragment id="copy-elements">
+<src:fragref linkend="copy-passthrough"/>
+<src:fragref linkend="copy-fragref"/>
+<src:fragref linkend="copy-default"/>
+</src:fragment>
+
+<section id="s.copy-passthrough">
+<title>Copying <sgmltag>src:passthrough</sgmltag></title>
+<para>Passthrough elements contain text that is intended to appear
+literally in the result tree. We use XSLT
+<quote>disable-output-escaping</quote> to copy it without
+interpretation:</para>
+
+<src:fragment id="copy-passthrough">
<xsl:template match="src:passthrough" mode="copy">
<xsl:value-of disable-output-escaping="yes" select="."/>
</xsl:template>
+</src:fragment>
+</section>
+
+<section id="s.copy-fragref">
+<title>Copying <sgmltag>src:fragref</sgmltag></title>
+
+<para>With a unique exception, copying fragment references is
+straightforward: find the fragment that is identified by the
+cross-reference and process it.
+</para>
+
+<para>The single exception arises only in the processing of
+<sgmltag>src:fragref</sgmltag> elements in the <sgmltag>weave.xweb</sgmltag>
+document. There is a single template in the <quote>weave</quote> program
+that needs to copy a literal <sgmltag>src:fragref</sgmltag> element to the
+result tree. That is the <emphasis>only</emphasis> time the
+<xref linkend="doe-fragref"/> branch is executed.
+</para>
+<src:fragment id="copy-fragref">
<xsl:template match="src:fragref" mode="copy">
<xsl:variable name="node" select="."/>
<xsl:choose>
+ <src:fragref linkend="doe-fragref"/>
+ <src:fragref linkend="normal-fragref"/>
+ </xsl:choose>
+</xsl:template>
+</src:fragment>
+
+<section><title>Copying Normal Fragment References</title>
+
+<para>To copy a normal fragment reference, identify what the
+<sgmltag class="attribute">linkend</sgmltag> attribute points to,
+make sure it is valid, and process it.</para>
+
+<src:fragment id="normal-fragref"> <xsl:otherwise>
+ <xsl:variable name="fragment" select="key('fragment', @linkend)"/>
+ <src:fragref linkend="fragref-unique"/>
+ <src:fragref linkend="fragref-isfragment"/>
+ <xsl:apply-templates select="$fragment"/>
+ </xsl:otherwise></src:fragment>
+
+<section><title>Fragment is Unique</title>
+
+<para>Make sure that the <sgmltag class="attribute">linkend</sgmltag>
+attribute points to exactly one node in the source tree. It is an error
+if no element exists with that ID value or if more than one exists.</para>
+
+<src:fragment id="fragref-unique"> <xsl:if test="count($fragment) != 1">
+ <xsl:message terminate="yes">
+ <xsl:text>Link to fragment "</xsl:text>
+ <xsl:value-of select="@linkend"/>
+ <xsl:text>" does not uniquely identify a single fragment.</xsl:text>
+ </xsl:message>
+ </xsl:if></src:fragment>
+
+</section>
+
+<section><title>Fragment is a <sgmltag>src:fragment</sgmltag></title>
+
+<para>Make sure that the <sgmltag class="attribute">linkend</sgmltag>
+attribute points to a <sgmltag>src:fragment</sgmltag> element.</para>
+
+<para>FIXME: this code should test the namespace name of the $fragment</para>
+
+<src:fragment id="fragref-isfragment">
+<xsl:if test="local-name($fragment) != 'fragment'">
+ <xsl:message terminate="yes">
+ <xsl:text>Link "</xsl:text>
+ <xsl:value-of select="@linkend"/>
+ <xsl:text>" does not point to a src:fragment.</xsl:text>
+ </xsl:message>
+</xsl:if>
+</src:fragment>
+</section>
+</section>
+</section>
+
+<section><title>Copying Disable-Output-Escaping Fragment References</title>
+
+<para>A <sgmltag>src:fragref</sgmltag> that specifies
+<sgmltag class="attribute">disable-output-escaping</sgmltag> is treated
+essentially as if it was
+<link linkend="s.copy-default">any other element</link>. The only
+exception is that the
+<sgmltag class="attribute">disable-output-escaping</sgmltag> attribute
+is not copied.</para>
+
+<para>Because <application>tangle</application> and
+<application>weave</application> are XSLT stylesheets that process
+XSLT stylesheets, processing <sgmltag>src:fragref</sgmltag> poses
+a unique challenge.</para>
+
+<para>In ordinary <application>tangle</application> processing, they
+are expanded and replaced with the content of the fragment that they
+point to. But when <filename>weave.xweb</filename> is tangled, they
+must be copied through literally. The
+<sgmltag class="attribute">disable-output-escaping</sgmltag> attribute
+provides the hook that allows this.
+</para>
+
+<src:fragment id="doe-fragref">
<xsl:when test="@disable-output-escaping='yes'">
- <xsl:element name="{name(.)}" namespace="namespace-uri(.)">
- <xsl:for-each select="namespace::*">
- <xsl:if test="string(.) != namespace-uri($node)">
- <xsl:copy/>
- </xsl:if>
- </xsl:for-each>
+ <xsl:element name="{name(.)}" namespace="{namespace-uri(.)}">
+ <src:fragref linkend="copy-namespaces"/>
<xsl:for-each select="@*">
<xsl:if test="not(name(.) = 'disable-output-escaping')">
<xsl:copy/>
</xsl:for-each>
<xsl:apply-templates mode="copy"/>
</xsl:element>
- </xsl:when>
- <xsl:otherwise>
- <xsl:variable name="linkend" select="@linkend"/>
- <xsl:variable name="fragment" select="//src:fragment[@id=$linkend]"/>
- <xsl:apply-templates select="$fragment"/>
- </xsl:otherwise>
- </xsl:choose>
+ </xsl:when></src:fragment>
+</section>
+
+<section id="s.copy-default">
+<title>Copying Everything Else</title>
+
+<para>Everything else is copied verbatim. This is a five step process:
+</para>
+
+<orderedlist>
+<listitem><para>Save a copy of the context node in
+<literal>$node</literal> so that we can refer to it later from
+inside an <sgmltag>xsl:for-each</sgmltag>.</para>
+</listitem>
+<listitem><para>Construct a new node in the result tree with
+the same qualified name and namespace as the context node.</para>
+</listitem>
+<listitem><para>Copy the namespace nodes on the context node to the
+new node in the result tree. We must do this manually because the
+&xweb; file may have broken the content of this element into several
+separate fragments. Breaking things into separate fragments makes it
+impossible for the XSLT processor to always construct the right namespace
+nodes automatically.</para>
+</listitem>
+<listitem><para>Copy the attributes.
+</para></listitem>
+<listitem><para>Copy the children.
+</para></listitem>
+</orderedlist>
+
+<src:fragment id="copy-default">
+<xsl:template match="*" mode="copy">
+ <xsl:variable name="node" select="."/>
+ <xsl:element name="{name(.)}" namespace="{namespace-uri(.)}">
+ <src:fragref linkend="copy-namespaces"/>
+ <xsl:copy-of select="@*"/>
+ <xsl:apply-templates mode="copy"/>
+ </xsl:element>
+</xsl:template>
+</src:fragment>
+
+<para>For non-XML source docuements, this template will never match
+because there will be no XML elements in the source fragments.</para>
+
+<section><title>Copy Namespaces</title>
+
+<para>Copying the namespaces is a simple loop over the elements on
+the <literal>namespace</literal> axis, with one wrinkle.</para>
+
+<para>It is an error to copy a namespace node onto an element if a
+namespace node is already present for that namespace. The fact that
+we're running this loop in a context where we've constructed the
+result node explicitly in the correct namespace means that attempting
+to copy that namespace node again will produce an error. We work
+around this problem by explicitly testing for that namespace and not
+copying it.
+</para>
+
+<src:fragment id="copy-namespaces">
+ <xsl:for-each select="namespace::*">
+ <xsl:if test="string(.) != namespace-uri($node)">
+ <xsl:copy/>
+ </xsl:if>
+ </xsl:for-each>
+</src:fragment>
+
+</section>
+</section>
+</section>
+
+<section><title>Copy XML Constructs</title>
+
+<para>In the <filename>xtangle.xsl</filename> stylesheet, we also want
+to preserve XML constructs (processing instructions and comments) that
+we encounter in the fragments.</para>
+
+<para>Note that many implementations of XSLT do not provide comments in
+the source document (they are discarded before building the tree), in which
+case the comments cannot be preserved.</para>
+
+<src:fragment id="copy-xml-constructs">
+<xsl:template match="processing-instruction()" mode="copy">
+ <xsl:processing-instruction name='{name(.)}'>
+ <xsl:value-of select="."/>
+ </xsl:processing-instruction>
</xsl:template>
-<xsl:template match="src:comment" mode="copy">
+<xsl:template match="comment()" mode="copy">
<xsl:comment>
<xsl:value-of select="."/>
</xsl:comment>
</xsl:template>
</src:fragment>
+
</section>
+
</article>
+<!DOCTYPE article [
+<!ENTITY xweb "<acronym>XWEB</acronym>">
+<!ENTITY lt "&#60;"> <!-- LESS-THAN SIGN -->
+<!ENTITY gt ">"> <!-- GREATER-THAN SIGN -->
+<!ENTITY amp "&#38;"> <!-- AMPERSAND -->
+]>
<article xmlns:src="http://nwalsh.com/xmlns/litprog/fragment"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
-<title>Weave.xsl</title>
+<articleinfo>
+<title>Weave</title>
+<subtitle>Part of
+<citetitle>Literate Programming in XML</citetitle></subtitle>
+<pubdate>05 Oct 2001</pubdate>
+<releaseinfo role="meta">
+$Id$
+</releaseinfo>
-<src:topfragment id="top" default-exclude-result-prefixes="xsl">
-<src:passthrough>
-<!DOCTYPE xsl:stylesheet [
-<!ENTITY nl "<xsl:text>
</xsl:text>">
-]>
-</src:passthrough>
+<revhistory>
+<revision>
+<revnumber>0.1</revnumber>
+<date>05 Oct 2001</date>
+<authorinitials>ndw</authorinitials>
+<revremark>Initial draft.</revremark>
+</revision>
+</revhistory>
+
+<author><firstname>Norman</firstname><surname>Walsh</surname>
+</author>
+</articleinfo>
+
+<!-- ============================================================ -->
+
+<para>The <filename>weave.xsl</filename> stylesheet transforms an
+&xweb; document into a <quote>documentation</quote> document. This
+is accomplished by <quote>weaving</quote> the documentation from
+the &xweb; file with a pretty-printed version of the source code.</para>
+
+<para>The resulting document is ready to be processed by whatever
+down-stream publishing tools are appropriate.</para>
+
+<section><title>The Stylesheet</title>
+
+<para>The stylesheet performs some initialization, begins processing
+at the root of the &xweb; document, and processes fragments and
+elements. This stylesheet also requires some recursive templates that
+are stored at the end of the stylesheet.</para>
+
+<src:fragment id="top" mundane-result-prefixes="xsl">
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:src="http://nwalsh.com/xmlns/litprog/fragment"
exclude-result-prefixes="xsl src xml"
version="1.0">
+ <src:fragref linkend="init"/>
+ <src:fragref linkend="root.template"/>
+ <src:fragref linkend="doc.content"/>
+ <src:fragref linkend="fragments"/>
+ <src:fragref linkend="copy-elements"/>
+ <src:fragref linkend="named-templates"/>
+</xsl:stylesheet>
+</src:fragment>
- <xsl:include href="VERSION"/>
+<section><title>Initialization</title>
- <src:fragref linkend="space"/>
+<para>The stylesheet initializes the processor by loading its version
+information (stored in a separate file because it is shared by several
+stylesheets), telling the processor to preserve whitespace on all
+input elements, setting the output method, and initializing
+the excluded result prefixes.</para>
+
+<para>The stylesheet also constructs a key for the ID values used on
+fragments. Because &xweb; documents do not have to be valid according
+to any particular DTD or Schema, the stylesheet cannot rely on having
+the IDs identified as type ID in the source document.</para>
+
+<src:fragment id="init">
+ <xsl:include href="VERSION"/>
+ <xsl:preserve-space elements="*"/>
+ <xsl:output method="xml"/>
<src:fragref linkend="param.ex.result.prefixes"/>
- <src:fragref linkend="root.template"/>
- <src:fragref linkend="default.template"/>
- <src:fragref linkend="topfragment"/>
- <src:fragref linkend="fragment"/>
- <src:fragref linkend="rest"/>
-</xsl:stylesheet>
-</src:topfragment>
+ <xsl:key name="fragment" match="src:fragment" use="@id"/>
-<section><title>Whitespace Control</title>
+ <xsl:param name="top" select="'top'"/>
+</src:fragment>
-<src:fragment id="space">
-<xsl:preserve-space elements="*"/>
+<section id="s.param.ex.result.prefixes">
+<title>Default Exclude Result Prefixes</title>
+
+<para>Generally, the namespace declarations for namespaces used by
+the source code portion of the &xweb; file are not needed in the
+<quote>woven</quote> documentation. To reduce the size of the documentation
+file, and to reduce the clutter of unnecessary declarations, you can
+specify prefixes that should be excluded.</para>
+
+<para>This is done as a parameter so that it can be adjusted dynamically,
+though it rarely needs to be. The initial value comes from the
+<sgmltag class="attribute">mundane-result-prefixes</sgmltag>
+attribute on the &xweb; file's top <sgmltag>src:fragment</sgmltag>.</para>
+
+<src:fragment id="param.ex.result.prefixes">
+<xsl:param name="mundane-result-prefixes"
+ select="key('fragment',$top)/@mundane-result-prefixes"/>
</src:fragment>
+</section>
</section>
-<section><title>Default Exclude Result Prefixes</title>
+<section><title>Named Templates</title>
-<src:fragment id="param.ex.result.prefixes">
-<xsl:param name="default-exclude-result-prefixes"
- select="//src:topfragment/@default-exclude-result-prefixes"/>
+<para>Correctly copying elements requires the ability to calculate
+applicable namespaces and output the appropriate namespace psuedo-attributes
+and attributes. These templates accomplish those tasks.</para>
+
+<src:fragment id="named-templates">
+ <src:fragref linkend="count.applicable.namespaces"/>
+ <src:fragref linkend="output.applicable.namespaces"/>
+ <src:fragref linkend="output.applicable.attributes"/>
+ <src:fragref linkend="indent"/>
+ <src:fragref linkend="spaces"/>
+ <src:fragref linkend="trailing-space-chars"/>
</src:fragment>
+</section>
</section>
+
<section><title>Root Template</title>
+<para>The root template begins processing at the root of the &xweb;
+document. It outputs a couple of informative comments and then
+processes the document.</para>
+
+<para>Source code fragments in the &xweb; document are not required
+to be sequential, we assume that they appear in the order in which
+they should be documented.</para>
+
<src:fragment id="root.template">
<xsl:template match="/">
<xsl:text>
</xsl:text>
</src:fragment>
</section>
-<section><title>Default Template</title>
-<src:fragment id="default.template">
-<xsl:template match="*">
- <xsl:variable name="node" select="."/>
- <xsl:element name="{name(.)}" namespace="{namespace-uri(.)}">
- <xsl:for-each select="namespace::*">
- <xsl:if test="string(.) != namespace-uri($node)">
- <xsl:copy/>
- </xsl:if>
- </xsl:for-each>
- <xsl:copy-of select="@*"/>
- <xsl:apply-templates/>
- </xsl:element>
+<section><title>Fragments</title>
+
+<para>The goal when copying the source code fragments
+is to preserve the <sgmltag>src:fragment</sgmltag>
+elements in the documentation file (so that they can be formatted
+appropriately) but to escape all of the fragment content so that
+it appears simply as <quote>text</quote> in the documentation.</para>
+
+<para>For example, if the following fragment appears in the &xweb; file:
+</para>
+
+<screen><src:fragment id="foo">
+ <emphasis>some code</emphasis>
+</src:fragment></screen>
+
+<para>the documentation must contain:</para>
+
+<screen><src:fragment id="foo">
+ &lt;emphasis&gt;some code&lt;/emphasis&gt;
+</src:fragment></screen>
+
+<para>The significance of this escaping is less obvious when the
+fragment contains non-XML code, but it is in fact still relevant.</para>
+
+<para>This task is accomplished by constructing a literal
+<sgmltag>src:fragment</sgmltag> element and then copying the content
+of the source document's <sgmltag>src:fragment</sgmltag> element
+in a mode that escapes all markup characters.</para>
+
+<src:fragment id="fragments">
+<xsl:template match="src:fragment">
+ <src:fragment id="{@id}">
+ <xsl:call-template name="copy-content"/>
+ </src:fragment>
+</xsl:template>
+
+<src:fragref linkend="copy-content"/>
+
+</src:fragment>
+
+<section><title>Copying Content</title>
+
+<para>The <quote><literal>copy-content</literal></quote> template
+could be as simple as:</para>
+
+<screen
+><xsl:apply-templates mode="copy"/></screen>
+
+<para>but we play one more trick for the convenience of &xweb; authors.
+</para>
+
+<para>It's convenient for authors to use newlines at the beginning
+and end of each program fragment, producing fragments that look like
+the one shown above. The problem is that white space is significant
+inside fragments, so the resulting documenation will contain a listing
+like this:</para>
+
+<screen> 1 |
+ 2 | <emphasis>some code</emphasis>
+ 3 |</screen>
+
+<para>The leading and trailing blank lines in this listing are distracting
+and almost certainly insignificant. Authors can avoid this problem by
+removing the offending newlines:</para>
+
+<screen><src:fragment id="foo"><emphasis>some code</emphasis></src:fragment></screen>
+
+<para>but this makes the source document more difficult to read and
+introduces tedious cut-and-paste problems. To avoid this problem, the
+<quote><literal>copy-content</literal></quote> template takes special
+pains to trim off one optional leading newline and one optional
+trailing newline. It does this by dealing with the first, last, and
+middle nodes of the <sgmltag>src:fragment</sgmltag> elements
+separately:</para>
+
+<src:fragment id="copy-content">
+<xsl:template name="copy-content">
+ <src:fragref linkend="cc-storevars"/>
+ <src:fragref linkend="cc-first-node"/>
+ <src:fragref linkend="cc-middle-nodes"/>
+ <src:fragref linkend="cc-last-node"/>
</xsl:template>
</src:fragment>
+<section><title>Convenience Variables</title>
+
+<para>For convenience, we store subexpressions containing the first,
+last, and all the middle nodes in variables.</para>
+
+<src:fragment id="cc-storevars">
+ <xsl:variable name="first-node"
+ select="node()[1]"/>
+ <xsl:variable name="middle-nodes"
+ select="node()[position() > 1 and position() < last()]"/>
+ <xsl:variable name="last-node"
+ select="node()[position() > 1 and position() = last()]"/>
+</src:fragment>
+
</section>
-<section><title>Fragments</title>
-<src:fragment id="topfragment">
-<xsl:template match="src:topfragment">
- <src:fragment id="{@id}">
+<section><title>Handle First Node</title>
+
+<para>Handling the leading newline is conceptually a simple matter of
+looking at the first character on the line and skipping it if it is
+a newline. A slight complexity is introduced by the fact that if the
+fragment contains only a single text node, the first node is also the
+last node and we have to possibly trim off a trialing newline as well.
+We separate that out as a special case.
+</para>
+
+<src:fragment id="cc-first-node">
<xsl:choose>
- <xsl:when test="node()[1] = '
'">
- <xsl:apply-templates select="node()[position()>1 and position()<last()]" mode="copy"/>
+ <src:fragref linkend="cc-only-node"/>
+ <src:fragref linkend="cc-leading-nl"/>
+ <src:fragref linkend="cc-no-leading-nl"/>
+ </xsl:choose>
+</src:fragment>
+
+<section><title>Handle A Fragment that Contains a Single Node</title>
+
+<para>If the <varname>$first-node</varname> is a text node and the
+fragment contains only a single child, then it is also the last node.</para>
+
+<para>In order to deal with a single text node child, we must address
+four cases: the node has both leading and trailing newlines, the node
+has only leading newlines, only trailing newlines, or no newlines at
+all.</para>
+
+<src:fragment id="cc-only-node">
+ <xsl:when test="$first-node = text() and count(node()) = 1">
+ <src:fragref linkend="cc-more-conv"/>
+ <xsl:choose>
+ <src:fragref linkend="cc-both"/>
+ <src:fragref linkend="cc-leading"/>
+ <src:fragref linkend="cc-trailing"/>
+ <src:fragref linkend="cc-none"/>
+ </xsl:choose>
</xsl:when>
+</src:fragment>
+
+<section><title>More Convenience Variables</title>
+
+<para>For convenience, we calculate whether or not the node in question
+has leading and/or trailing newlines and store those results in variables.
+</para>
+
+<src:fragment id="cc-more-conv">
+ <xsl:variable name="leading-nl"
+ select="substring($first-node, 1, 1) = '
'"/>
+ <xsl:variable name="trailing-nl"
+ select="substring($first-node, string-length($first-node), 1) = '
'"/>
+</src:fragment>
+</section>
+
+<section><title>Handle a Single Node With Leading and Trailing Newlines</title>
+
+<para>If the node has both leading and trailing newlines, trim a character
+off each end.</para>
+
+<src:fragment id="cc-both">
+ <xsl:when test="$leading-nl and $trailing-nl">
+ <xsl:value-of select="substring($first-node, 2, string-length($first-node)-2)"/>
+ </xsl:when>
+</src:fragment>
+</section>
+
+<section><title>Handle a Single Node With Only Leading Newlines</title>
+
+<para>If the node has only leading newlines, trim off the first character.
+</para>
+
+<src:fragment id="cc-leading">
+ <xsl:when test="$leading-nl">
+ <xsl:value-of select="substring($first-node, 2)"/>
+ </xsl:when>
+</src:fragment>
+</section>
+
+<section><title>Handle a Single Node with Only Trailing Newlines</title>
+
+<para>If the node has only trailing newlines, trim off the last character.
+</para>
+
+<src:fragment id="cc-trailing">
+ <xsl:when test="$trailing-nl">
+ <xsl:value-of select="substring($first-node, 1, string-length($first-node)-1)"/>
+ </xsl:when>
+</src:fragment>
+</section>
+
+<section><title>Handle a Single Node with No Newlines</title>
+
+<para>Otherwise, the node has no newlines and it is simply printed.
+</para>
+
+<src:fragment id="cc-none">
+ <xsl:otherwise>
+ <xsl:value-of select="$first-node"/>
+ </xsl:otherwise>
+</src:fragment>
+</section>
+</section>
+
+<section><title>Handle a First Node with a Leading Newline</title>
+
+<para>If the first node is a text node and begins with a newline,
+trim off the first character.</para>
+
+<src:fragment id="cc-leading-nl">
+ <xsl:when test="$first-node = text() and substring($first-node, 1, 1) = '
'">
+ <xsl:value-of select="substring($first-node, 2)"/>
+ </xsl:when>
+</src:fragment>
+</section>
+
+<section><title>Handle a First Node without a Leading Newline</title>
+
+<para>Otherwise, the first node is not a text node or does not begin
+with a newline, so use the <quote>copy</quote> mode to copy it to
+the result tree.</para>
+
+<src:fragment id="cc-no-leading-nl">
<xsl:otherwise>
- <xsl:apply-templates select="node()[position()<last()]" mode="copy"/>
+ <xsl:apply-templates select="$first-node" mode="copy"/>
</xsl:otherwise>
- </xsl:choose>
- </src:fragment>
-</xsl:template>
</src:fragment>
+</section>
+</section>
-<src:fragment id="fragment">
-<xsl:template match="src:fragment">
- <src:fragment id="{@id}">
+<section><title>Handle Last Node</title>
+
+<para>Handling the last node is roughly analagous to handling the first
+node, except that we know this code is only evaluated if the last node
+is not also the first node.</para>
+
+<para>If the last node is a text node and ends with a newline, strip
+it off. Otherwise, just copy the content of the last node
+using the <quote>copy</quote> mode.
+</para>
+
+<src:fragment id="cc-last-node">
<xsl:choose>
- <xsl:when test="node()[1] = '
'">
- <xsl:apply-templates select="node()[position()>1]" mode="copy"/>
+ <xsl:when test="$last-node = text() and substring($last-node, string-length($last-node), 1) = '
'">
+ <xsl:value-of select="substring($last-node, 1, string-length($last-node)-1)"/>
</xsl:when>
<xsl:otherwise>
- <xsl:apply-templates select="node()" mode="copy"/>
+ <xsl:apply-templates select="$last-node" mode="copy"/>
</xsl:otherwise>
</xsl:choose>
- </src:fragment>
-</xsl:template>
</src:fragment>
</section>
-<section><title>Rest</title>
-<src:fragment id="rest">
+<section><title>Handle the Middle Nodes</title>
-<!-- ============================================================ -->
+<para>The middle nodes are easy, just copy them
+using the <quote>copy</quote> mode.</para>
+
+<src:fragment id="cc-middle-nodes">
+ <xsl:apply-templates select="$middle-nodes" mode="copy"/>
+</src:fragment>
+
+</section>
+</section>
+</section>
+
+<section><title>Fragment References</title>
-<xsl:template match="src:fragref">
- <xsl:variable name="linkend" select="@linkend"/>
+<para>Fragment references, like fragments, are simply copied to the
+documentation file. The use of
+<sgmltag class="attribute">disable-output-escaping</sgmltag> is
+unique to this template (it instructs the <quote>tangle</quote>
+stylesheet to make a literal copy of the <sgmltag>src:fragref</sgmltag>,
+rather than expanding it, as it usually would).</para>
+
+<src:fragment id="fragref"><xsl:template match="src:fragref">
<src:fragref linkend="{@linkend}" disable-output-escaping="yes">
<xsl:apply-templates/>
</src:fragref>
</xsl:template>
+</src:fragment>
-<!-- ============================================================ -->
+<section><title>Copying Elements</title>
+
+<para>Copying elements to the result tree can be divided into four
+cases: <link linkend="s.copy-passthrough">copying passthrough elements</link>,
+<link linkend="s.copy-fragref">copying fragment references</link> and
+<link linkend="s.copy-default">copying everything else</link>.</para>
+
+<src:fragment id="copy-elements">
+<src:fragref linkend="copy-passthrough"/>
+<src:fragref linkend="copy-fragref"/>
+<src:fragref linkend="copy-default"/>
+</src:fragment>
+
+<section id="s.copy-passthrough">
+<title>Copying <sgmltag>src:passthrough</sgmltag></title>
+
+<para>Passthrough elements contain text that is intended to appear
+literally in the result tree. We simply copy it through.
+</para>
+
+<src:fragment id="copy-passthrough">
+<xsl:template match="src:passthrough" mode="copy" priority="3">
+ <xsl:apply-templates select="node()|@*" mode="copy"/>
+</xsl:template>
+</src:fragment>
+</section>
+
+<section id="s.copy-fragref">
+<title>Copying <sgmltag>src:fragref</sgmltag></title>
+
+<para>Because <application>tangle</application> and
+<application>weave</application> are XSLT stylesheets that process
+XSLT stylesheets, processing <sgmltag>src:fragref</sgmltag> poses
+a unique challenge.</para>
+
+<para>In ordinary <application>tangle</application> processing, they
+are expanded and replaced with the content of the fragment that they
+point to. But when <filename>weave.xweb</filename> is tangled, they
+must be copied through literally. The
+<sgmltag class="attribute">disable-output-escaping</sgmltag> attribute
+provides the hook that allows this.
+</para>
+
+<para>When we're weaving, if the
+<sgmltag class="attribute">disable-output-escaping</sgmltag> attribute
+is <quote>yes</quote>, the <sgmltag>src:fragref</sgmltag> is treated literally.
+When it isn't, the element is copied through literally.</para>
+
+<src:fragment id="copy-fragref">
+<xsl:template match="src:fragref" mode="copy" priority="3">
+ <xsl:choose>
+ <xsl:when test="@disable-output-escaping='yes'">
+ <xsl:text><src:fragref linkend="</xsl:text>
+ <xsl:value-of select="@linkend"/>
+ <xsl:text>"/></xsl:text>
+ <xsl:apply-templates mode="copy"/>
+ <xsl:text></src:fragref></xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <src:fragref linkend="{@linkend}" disable-output-escaping="yes"/>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+</src:fragment>
+</section>
+
+<section id="s.copy-default">
+<title>Copying Everything Else</title>
+
+<para>There are two kinds of everything else: elements and other
+nodes.</para>
+
+<src:fragment id="copy-default">
+<src:fragref linkend="copy-default-elements"/>
+<src:fragref linkend="copy-default-nodes"/>
+</src:fragment>
+
+<para>This element template is quite complex, but it's goal is simple:
+to translate bona-fide elements in the source document into text in
+the result document. In other words, where the element
+<quote><literal><foo></literal></quote> occurs in the source
+document, the result document should contain
+<quote><literal>&lt;foo&gt;</literal></quote>.</para>
+
+<para>Three things make this tricky:</para>
+
+<orderedlist>
+<listitem><para>Elements in the source documents may have namespace
+nodes associated with them that are not explicitly declared on them.
+To the best of our ability, we must avoid copying these namespace
+nodes to the result tree.
+</para></listitem>
+<listitem><para>Attributes must be copied and formatted in some reasonable
+way in order to avoid excessively long lines in the documentation.
+</para></listitem>
+<listitem><para>Empty elements must be printed using the appropriate
+empty-element syntax. (It is simply impossible to determine what syntax
+was used in the source document, the best we can do is always use the
+empty-element syntax in the result as it is likely to be more common
+in the soruce.)
+</para></listitem>
+</orderedlist>
+
+<para>The plan of attack is:</para>
+
+<itemizedlist>
+<listitem><para>Calculate what namespaces should be excluded (by prefix).
+</para></listitem>
+<listitem><para>Calculate the applicable namespaces.
+</para></listitem>
+<listitem><para>Output the leading <quote><</quote> and the element
+name.
+</para></listitem>
+<listitem><para>Output the applicable namespaces.
+</para></listitem>
+<listitem><para>Output the attributes.
+</para></listitem>
+<listitem><para>If the element is not empty, finish the start tag,
+copy the element contents, and output and end tag. If the element is
+empty, finish the start tag with the empty-element syntax.
+</para></listitem>
+</itemizedlist>
+
+<src:fragment id="copy-default-elements">
+<xsl:template match="processing-instruction()" mode="copy" priority="2">
+ <xsl:text><?</xsl:text>
+ <xsl:value-of select="name(.)"/>
+ <xsl:value-of select="."/>
+ <xsl:text>?></xsl:text>
+</xsl:template>
+
+<xsl:template match="comment()" mode="copy" priority="2">
+ <xsl:text><!--</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text>--></xsl:text>
+</xsl:template>
<xsl:template match="*" mode="copy" priority="2">
- <xsl:variable name="name" select="name(.)"/>
- <xsl:variable name="prevtext" select="preceding-sibling::text()"/>
<xsl:variable name="exclude">
- <xsl:choose>
- <xsl:when test="ancestor::src:fragment/@exclude-result-prefixes">
- <xsl:value-of select="concat(' xml ',
- ancestor::src:fragment/@exclude-result-prefixes,
- ' ')"/>
- </xsl:when>
- <xsl:when test="ancestor::src:topfragment">
- <xsl:value-of select="concat(' xml ',
- ancestor::src:top-fragment/@exclude-result-prefixes,
- ' ')"/>
- </xsl:when>
- <xsl:otherwise>
- <xsl:value-of select="concat(' xml ',
- $default-exclude-result-prefixes,
- ' ')"/>
- </xsl:otherwise>
- </xsl:choose>
+ <src:fragref linkend="calculate-excluded-prefixes"/>
</xsl:variable>
- <xsl:text><</xsl:text>
- <xsl:value-of select="name(.)"/>
-
<xsl:variable name="applicable.namespaces">
<xsl:call-template name="count.applicable.namespaces">
<xsl:with-param name="namespaces" select="namespace::*"/>
</xsl:call-template>
</xsl:variable>
+ <xsl:text><</xsl:text>
+ <xsl:value-of select="name(.)"/>
+
<xsl:if test="$applicable.namespaces > 0">
<xsl:call-template name="output.applicable.namespaces">
<xsl:with-param name="namespaces" select="namespace::*"/>
</xsl:call-template>
</xsl:if>
- <xsl:choose>
- <xsl:when test="$applicable.namespaces > 0">
- <xsl:call-template name="output.applicable.attributes">
- <xsl:with-param name="attributes" select="attribute::*"/>
- <xsl:with-param name="first" select="'0'"/>
- </xsl:call-template>
- </xsl:when>
- <xsl:otherwise>
- <xsl:call-template name="output.applicable.attributes">
- <xsl:with-param name="attributes" select="attribute::*"/>
- </xsl:call-template>
- </xsl:otherwise>
- </xsl:choose>
+ <src:fragref linkend="output.attributes"/>
<xsl:choose>
<xsl:when test="node()">
</xsl:otherwise>
</xsl:choose>
</xsl:template>
+</src:fragment>
-<xsl:template match="src:passthrough" mode="copy" priority="3">
- <xsl:apply-templates select="node()|@*" mode="copy"/>
-</xsl:template>
+<para>The preceding template handles elements. Everything else is simply
+copied.</para>
-<xsl:template match="src:comment" mode="copy" priority="3">
- <xsl:text><!--</xsl:text>
- <xsl:apply-templates select="node()|@*" mode="copy"/>
- <xsl:text>--></xsl:text>
+<src:fragment id="copy-default-nodes">
+<xsl:template match="node()|@*" mode="copy">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" mode="copy"/>
+ </xsl:copy>
</xsl:template>
+</src:fragment>
-<xsl:template match="src:fragref" mode="copy" priority="3">
- <xsl:variable name="linkend" select="@linkend"/>
+<section><title>Calculate Excluded Prefixes</title>
+
+<para>Calculating the excluded prefixes requires evaluating the following
+conditions:</para>
+
+<orderedlist>
+<listitem><para>If the element we are copying is inside a
+<sgmltag>src:fragment</sgmltag> element that specifies a set of
+<sgmltag class="attribute">exclude-result-prefixes</sgmltag>, use
+those prefixes.
+</para></listitem>
+<listitem><para>Otherwise, use the
+<varname>$mundane-result-prefixes</varname> we
+<link linkend="s.param.ex.result.prefixes">calculated earlier</link>.
+</para></listitem>
+</orderedlist>
+
+<para>Note that in every case we exclude the namespace associated
+with <quote><literal>xml</literal></quote>.</para>
+
+<src:fragment id="calculate-excluded-prefixes">
+ <xsl:choose>
+ <xsl:when test="ancestor::src:fragment/@exclude-result-prefixes">
+ <xsl:value-of select="concat(' xml ',
+ ancestor::src:fragment/@exclude-result-prefixes,
+ ' ')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat(' xml ',
+ $mundane-result-prefixes,
+ ' ')"/>
+ </xsl:otherwise>
+ </xsl:choose>
+</src:fragment>
+</section>
+
+<section><title>Output Attributes</title>
+
+<para>The mechanics of outputting the applicable attributes is
+described in <xref linkend="s.output.atts"/>. The
+only wrinkle here is that if we have already output
+<quote><literal>xmlns</literal></quote> declarations for namespaces,
+the first real attribute is not really the first thing that looks
+like an attribute in the result.</para>
+
+<src:fragment id="output.attributes">
<xsl:choose>
- <xsl:when test="@disable-output-escaping='yes'">
- <xsl:text><src:fragref linkend="</xsl:text>
- <xsl:value-of select="@linkend"/>
- <xsl:text>"/></xsl:text>
- <xsl:apply-templates mode="copy"/>
- <xsl:text></src:fragref></xsl:text>
+ <xsl:when test="$applicable.namespaces > 0">
+ <xsl:call-template name="output.applicable.attributes">
+ <xsl:with-param name="attributes" select="attribute::*"/>
+ <xsl:with-param name="first" select="'0'"/>
+ </xsl:call-template>
</xsl:when>
<xsl:otherwise>
- <xref linkend="{@linkend}"/>
+ <xsl:call-template name="output.applicable.attributes">
+ <xsl:with-param name="attributes" select="attribute::*"/>
+ </xsl:call-template>
</xsl:otherwise>
</xsl:choose>
-</xsl:template>
+</src:fragment>
+</section>
+</section>
+</section>
-<xsl:template name="indent">
- <xsl:param name="name" select="name(.)"/>
- <xsl:param name="prevtext" select="preceding-sibling::text()"/>
+<section><title>Count Applicable Namespaces</title>
- <xsl:variable name="namelen" select="string-length($name)"/>
- <xsl:call-template name="spaces">
- <xsl:with-param name="count" select="$namelen + 1"/>
- </xsl:call-template>
-</xsl:template>
+<para>The applicable namespaces are determined by walking recursively
+over the list of namespace nodes associated with an element.</para>
-<xsl:template name="spaces">
- <xsl:param name="count" select="'0'"/>
- <xsl:if test="$count > 0">
- <xsl:text> </xsl:text>
- <xsl:call-template name="spaces">
- <xsl:with-param name="count" select="$count - 1"/>
- </xsl:call-template>
- </xsl:if>
-</xsl:template>
+<para>For each namespace node, if it has a prefix that is in the list
+of excluded prefixes or if it is the Literate Programming namespace,
+it is not counted (because it will not be output). Otherwise, it is
+counted.</para>
+<para>The recursion bottoms out when the list of namespace nodes has
+been exhausted. The total number of counted namespaces is then
+returned.</para>
+
+<src:fragment id="count.applicable.namespaces">
<xsl:template name="count.applicable.namespaces">
<xsl:param name="namespaces" select="namespace::*"/>
<xsl:param name="exclude-prefixes" select="''"/>
select="'http://nwalsh.com/xmlns/litprog/fragment'"/>
<xsl:param name="count" select="'0'"/>
-<!--
- <xsl:message>
- <xsl:text>ns: </xsl:text>
- <xsl:value-of select="name(.)"/>
- <xsl:text> </xsl:text>
- <xsl:value-of select="$count"/>
- <xsl:text> </xsl:text>
- <xsl:value-of select="count($namespaces)"/>
- <xsl:text> </xsl:text>
- <xsl:value-of select="name($namespaces[1])"/>
- <xsl:text>=</xsl:text>
- <xsl:value-of select="$namespaces[1]"/>
- <xsl:choose>
- <xsl:when test="not(contains($exclude-prefixes, name($namespaces[1]))
- or ($namespaces[1] = $exclude-uri))"> 1</xsl:when>
- <xsl:otherwise> 0</xsl:otherwise>
- </xsl:choose>
- </xsl:message>
--->
-
<xsl:choose>
<xsl:when test="count($namespaces) = 0">
<xsl:value-of select="$count"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
+</src:fragment>
+</section>
+
+<section id="s.output.atts">
+<title>Output Applicable Attributes and Pseudo-Attributes</title>
+
+<para>Outputing the attributes (or namespace psuedo-attributes) is
+straightforward, the only tricky part is pretty-printing the resulting
+document.</para>
+
+<para>Pretty-printing has three cases:</para>
+
+<orderedlist>
+<listitem><para>Before outputting the very first
+attribute or psuedo-attribute, we want to output only a single space,
+to separate the result from the preceding element name.
+</para></listitem>
+<listitem><para>Before outputting any additional attribute or
+psuedo-attribute, we want to output a line-feed and then indent the
+result appropriately. This prevents the attributes and psuedo-attributes
+from appearing as one huge, long line in the result.
+</para></listitem>
+<listitem><para>If the element has no attributes or psuedo attributes,
+we don't want to output anything; we want the closing tag delimiter
+to appear immediately after the element name.
+</para></listitem>
+</orderedlist>
+<section><title>Output Applicable Namespaces</title>
+
+<para>The applicable namespaces are determined by walking recursively
+over the list of namespace nodes associated with an element.</para>
+
+<para>For each namespace node, if it has a prefix that is in the list
+of excluded prefixes or if it is the Literate Programming namespace,
+it is not output, otherwise, it is.</para>
+
+<para>The recursion bottoms out when the list of namespace nodes has
+been exhausted.</para>
+
+<src:fragment id="output.applicable.namespaces">
<xsl:template name="output.applicable.namespaces">
<xsl:param name="namespaces" select="namespace::*"/>
<xsl:param name="exclude-prefixes" select="''"/>
select="'http://nwalsh.com/xmlns/litprog/fragment'"/>
<xsl:param name="first" select="'1'"/>
-<!--
- <xsl:message>
- <xsl:text>ons: </xsl:text>
- <xsl:value-of select="name(.)"/>
- <xsl:text> </xsl:text>
- <xsl:value-of select="$first"/>
- <xsl:text> </xsl:text>
- <xsl:value-of select="count($namespaces)"/>
- <xsl:text> </xsl:text>
- <xsl:value-of select="name($namespaces[1])"/>
- <xsl:text>=</xsl:text>
- <xsl:value-of select="$namespaces[1]"/>
- <xsl:choose>
- <xsl:when test="not(contains($exclude-prefixes, name($namespaces[1]))
- or ($namespaces[1] = $exclude-uri))"> 1</xsl:when>
- <xsl:otherwise> 0</xsl:otherwise>
- </xsl:choose>
- </xsl:message>
--->
-
<xsl:choose>
- <xsl:when test="count($namespaces) = 0"/>
+ <xsl:when test="count($namespaces) = 0">
+ <!-- do nothing -->
+ </xsl:when>
<xsl:when test="not(contains($exclude-prefixes, name($namespaces[1]))
or ($namespaces[1] = $exclude-uri))">
- <xsl:choose>
- <xsl:when test="$first = 0">
- <xsl:text>
</xsl:text>
- <xsl:call-template name="indent"/>
- </xsl:when>
- <xsl:otherwise>
- <xsl:text> </xsl:text>
- </xsl:otherwise>
- </xsl:choose>
+ <src:fragref linkend="indent-attribute"/>
<xsl:text>xmlns</xsl:text>
<xsl:if test="name($namespaces[1]) != ''">:</xsl:if>
<xsl:value-of select="name($namespaces[1])"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
+</src:fragment>
-<xsl:template name="output.applicable.attributes">
- <xsl:param name="attributes" select="attribute::*"/>
- <xsl:param name="first" select="'1'"/>
+<section><title>Indent Attribute</title>
-<!--
- <xsl:message>
- <xsl:text>att: </xsl:text>
- <xsl:value-of select="$first"/>
- <xsl:value-of select="count($attributes)"/>
- <xsl:value-of select="name($attributes[1])"/>
- </xsl:message>
--->
+<para>If this is not the first attribute or pseudo-attribute, output a
+newline and then indent an appropriate amount. Otherwise, simply output
+a space.</para>
- <xsl:choose>
- <xsl:when test="count($attributes) = 0"/>
- <xsl:otherwise>
+<src:fragment id="indent-attribute">
<xsl:choose>
<xsl:when test="$first = 0">
<xsl:text>
</xsl:text>
<xsl:text> </xsl:text>
</xsl:otherwise>
</xsl:choose>
+</src:fragment>
+
+<para>Indenting is accomplished by outputting a series of spaces. The
+number of spaces is determined by the length of the name of the current
+element plus two (one for the leading <quote><</quote> and one for
+the space that separates the name from the first attribute).</para>
+
+<src:fragment id="indent">
+<xsl:template name="indent">
+ <xsl:param name="name" select="name(.)"/>
+
+ <xsl:variable name="indent-spaces">
+ <xsl:call-template name="trailing-space-chars">
+ <xsl:with-param name="string" select="preceding-sibling::text()"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- +2 for the leading < and the space after the name -->
+ <xsl:variable name="indentlen"
+ select="string-length($name) + $indent-spaces + 2"/>
+
+ <xsl:call-template name="spaces">
+ <xsl:with-param name="count" select="$indentlen"/>
+ </xsl:call-template>
+</xsl:template>
+</src:fragment>
+
+<para>Spaces is a recursive template that outputs a specified
+number of spaces.</para>
+
+<src:fragment id="spaces">
+<xsl:template name="spaces">
+ <xsl:param name="count" select="'0'"/>
+ <xsl:if test="$count > 0">
+ <xsl:text> </xsl:text>
+ <xsl:call-template name="spaces">
+ <xsl:with-param name="count" select="$count - 1"/>
+ </xsl:call-template>
+ </xsl:if>
+</xsl:template>
+</src:fragment>
+
+<para>Given a string, this template walks it recursively counting
+and returning the number of trailing spaces.</para>
+
+<src:fragment id="trailing-space-chars">
+<xsl:template name="trailing-space-chars">
+ <xsl:param name="string" select="''"/>
+ <xsl:param name="count" select="0"/>
+
+ <xsl:choose>
+ <xsl:when test="$string = ''
+ or substring($string,string-length($string),1) != ' '">
+ <xsl:value-of select="$count"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="trailing-space-chars">
+ <xsl:with-param name="string" select="substring($string,1,string-length($string)-1)"/>
+ <xsl:with-param name="count" select="$count + 1"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+</src:fragment>
+
+</section>
+</section>
+
+<section><title>Output Applicable Attributes</title>
+
+<para>This template walks recursively over the attributes associated
+with a node and outputs each one of them in turn. (All attributes
+are applicable.)</para>
+
+<src:fragment id="output.applicable.attributes">
+<xsl:template name="output.applicable.attributes">
+ <xsl:param name="attributes" select="attribute::*"/>
+ <xsl:param name="first" select="'1'"/>
+
+ <xsl:choose>
+ <xsl:when test="count($attributes) = 0">
+ <!-- do nothing -->
+ </xsl:when>
+ <xsl:otherwise>
+ <src:fragref linkend="indent-attribute"/>
<xsl:value-of select="name($attributes[1])"/>
<xsl:text>="</xsl:text>
<xsl:value-of select="$attributes[1]"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
+</src:fragment>
+</section>
+</section>
+</section>
-<xsl:template match="node()|@*" mode="copy">
- <xsl:copy>
- <xsl:apply-templates select="@*|node()" mode="copy"/>
- </xsl:copy>
+<section><title>Other Content</title>
+
+<para>The remaining elements, processing instructions, and comments are
+part of the documentation and must simply be copied to the result:</para>
+
+<src:fragment id="doc.content">
+ <src:fragref linkend="default.template"/>
+ <src:fragref linkend="pis"/>
+ <src:fragref linkend="comments"/>
+</src:fragment>
+
+<section><title>Elements</title>
+
+<para>The default template handles copying elements.
+It is a five step process:
+</para>
+
+<orderedlist>
+<listitem><para>Save a copy of the context node in
+<literal>$node</literal> so that we can refer to it later from
+inside an <sgmltag>xsl:for-each</sgmltag>.</para>
+</listitem>
+<listitem><para>Construct a new node in the result tree with
+the same qualified name and namespace as the context node.</para>
+</listitem>
+<listitem><para>Copy the namespace nodes on the context node to the
+new node in the result tree. We must do this manually because the
+&xweb; file may have broken the content of this element into several
+separate fragments. Breaking things into separate fragments makes it
+impossible for the XSLT processor to always construct the right namespace
+nodes automatically.</para>
+</listitem>
+<listitem><para>Copy the attributes.
+</para></listitem>
+<listitem><para>Copy the children.
+</para></listitem>
+</orderedlist>
+
+<src:fragment id="default.template">
+<xsl:template match="*">
+ <xsl:variable name="node" select="."/>
+ <xsl:element name="{name(.)}" namespace="{namespace-uri(.)}">
+ <src:fragref linkend="copy-namespaces"/>
+ <xsl:copy-of select="@*"/>
+ <xsl:apply-templates/>
+ </xsl:element>
</xsl:template>
+</src:fragment>
-<!-- ============================================================ -->
+<section><title>Copy Namespaces</title>
+
+<para>Copying the namespaces is a simple loop over the elements on
+the <literal>namespace</literal> axis, with one wrinkle.</para>
+
+<para>It is an error to copy a namespace node onto an element if a
+namespace node is already present for that namespace. The fact that
+we're running this loop in a context where we've constructed the
+result node explicitly in the correct namespace means that attempting
+to copy that namespace node again will produce an error. We work
+around this problem by explicitly testing for that namespace and not
+copying it.
+</para>
+
+<src:fragment id="copy-namespaces"> <xsl:for-each select="namespace::*">
+ <xsl:if test="string(.) != namespace-uri($node)">
+ <xsl:copy/>
+ </xsl:if>
+ </xsl:for-each></src:fragment>
+</section>
+</section>
+
+<section><title>Processing Instructions</title>
+
+<para>Processing instructions are simply copied through.</para>
+
+<src:fragment id="pis">
+<xsl:template match="processing-instruction()">
+ <xsl:processing-instruction name="{name(.)}">
+ <xsl:value-of select="."/>
+ </xsl:processing-instruction>
+</xsl:template>
</src:fragment>
+</section>
+<section><title>Comments</title>
+
+<para>Comments are simply copied through. Note, however, that many
+processors do not preserve comments in the source document, so this
+template may never be matched.</para>
+
+<src:fragment id="comments">
+<xsl:template match="comment()">
+ <xsl:comment>
+ <xsl:value-of select="."/>
+ </xsl:comment>
+</xsl:template>
+</src:fragment>
</section>
+</section>
+
+<section><title>Weaving DocBook</title>
+
+<para>It's no secret (and probably no surprise) that I use DocBook for
+most of my document authoring. Web files are no exception, and I have
+DocBook customization layer that validates woven &xweb; documentation
+files.</para>
+
+<para>In order to validate my woven documentation, I need to make sure
+that the appropriate document type declaration is associated with the
+documents. This is a simple change to the <sgmltag>xsl:output</sgmltag>
+instruction.</para>
+<para>This stylesheet imports <filename>weave.xsl</filename> for the
+weaving functionality and simply sets the public and system identifiers.
+</para>
+
+<src:fragment id="wdocbook" mundane-result-prefixes="xsl">
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:src="http://nwalsh.com/xmlns/litprog/fragment"
+ exclude-result-prefixes="xsl src xml"
+ version="1.0">
+
+ <xsl:import href="weave.xsl"/>
+ <xsl:output method="xml"
+ doctype-public="-//DocBook Open Repository//DTD DocBook Literate Programming V0.0//EN"
+ doctype-system="http://docbook.sourceforge.net/release/litprog/current/dtd/ldocbook.dtd"/>
+</xsl:stylesheet>
+</src:fragment>
+</section>
</article>