]> granicus.if.org Git - docbook-dsssl/commitdiff
Copied xsl/extensions up a level to create a top-level
authorMichael Smith <xmldoc@users.sourceforge.net>
Mon, 5 Mar 2007 17:01:58 +0000 (17:01 +0000)
committerMichael Smith <xmldoc@users.sourceforge.net>
Mon, 5 Mar 2007 17:01:58 +0000 (17:01 +0000)
xsl-java module. Rationale is that we need to do a
separate docbook-xsl-java release in order to get the
XSL Java extensions packaged for Debian, and having it
as a top-level module allows the release build and
packaging to be done using the common build files in
releasetools/ and so makes it a lot easier to set up
and maintain the build and do the packaging and upload, etc.
After OK, would be best to next remove the
xsl/extensions directory from version control and
instead update the docbook-xsl build to simply copy
over the built jars from the xsl-java dir.

57 files changed:
xsl-java/AUTHORS [new file with mode: 0644]
xsl-java/BUGS [new file with mode: 0644]
xsl-java/COPYING [new file with mode: 0644]
xsl-java/INSTALL [new file with mode: 0644]
xsl-java/Makefile [new file with mode: 0644]
xsl-java/README [new file with mode: 0644]
xsl-java/TODO [new file with mode: 0644]
xsl-java/VERSION [new file with mode: 0644]
xsl-java/saxon65/build.xml [new file with mode: 0644]
xsl-java/saxon65/nbproject/build-impl.xml [new file with mode: 0644]
xsl-java/saxon65/nbproject/genfiles.properties [new file with mode: 0644]
xsl-java/saxon65/nbproject/project.properties [new file with mode: 0644]
xsl-java/saxon65/nbproject/project.xml [new file with mode: 0644]
xsl-java/saxon65/src/com/nwalsh/saxon/CVS.java [new file with mode: 0644]
xsl-java/saxon65/src/com/nwalsh/saxon/Callout.java [new file with mode: 0644]
xsl-java/saxon65/src/com/nwalsh/saxon/CalloutEmitter.java [new file with mode: 0644]
xsl-java/saxon65/src/com/nwalsh/saxon/ColumnScanEmitter.java [new file with mode: 0644]
xsl-java/saxon65/src/com/nwalsh/saxon/ColumnUpdateEmitter.java [new file with mode: 0644]
xsl-java/saxon65/src/com/nwalsh/saxon/CopyEmitter.java [new file with mode: 0644]
xsl-java/saxon65/src/com/nwalsh/saxon/FormatCallout.java [new file with mode: 0644]
xsl-java/saxon65/src/com/nwalsh/saxon/FormatGraphicCallout.java [new file with mode: 0644]
xsl-java/saxon65/src/com/nwalsh/saxon/FormatTextCallout.java [new file with mode: 0644]
xsl-java/saxon65/src/com/nwalsh/saxon/FormatUnicodeCallout.java [new file with mode: 0644]
xsl-java/saxon65/src/com/nwalsh/saxon/ImageIntrinsics.java [new file with mode: 0644]
xsl-java/saxon65/src/com/nwalsh/saxon/LineCountEmitter.java [new file with mode: 0644]
xsl-java/saxon65/src/com/nwalsh/saxon/NumberLinesEmitter.java [new file with mode: 0644]
xsl-java/saxon65/src/com/nwalsh/saxon/Table.java [new file with mode: 0644]
xsl-java/saxon65/src/com/nwalsh/saxon/Text.java [new file with mode: 0644]
xsl-java/saxon65/src/com/nwalsh/saxon/TextFactory.java [new file with mode: 0644]
xsl-java/saxon65/src/com/nwalsh/saxon/UnwrapLinks.java [new file with mode: 0644]
xsl-java/saxon65/src/com/nwalsh/saxon/UnwrapLinksEmitter.java [new file with mode: 0644]
xsl-java/saxon65/src/com/nwalsh/saxon/Verbatim.java [new file with mode: 0644]
xsl-java/saxon65/src/com/nwalsh/saxon/Windows1252.java [new file with mode: 0644]
xsl-java/saxon65/src/com/nwalsh/saxon/package.html [new file with mode: 0644]
xsl-java/xalan27/build.xml [new file with mode: 0644]
xsl-java/xalan27/nbproject/.cvsignore [new file with mode: 0644]
xsl-java/xalan27/nbproject/build-impl.xml [new file with mode: 0644]
xsl-java/xalan27/nbproject/genfiles.properties [new file with mode: 0644]
xsl-java/xalan27/nbproject/project.properties [new file with mode: 0644]
xsl-java/xalan27/nbproject/project.xml [new file with mode: 0644]
xsl-java/xalan27/src/com/nwalsh/xalan/CVS.java [new file with mode: 0644]
xsl-java/xalan27/src/com/nwalsh/xalan/Callout.java [new file with mode: 0644]
xsl-java/xalan27/src/com/nwalsh/xalan/FormatCallout.java [new file with mode: 0644]
xsl-java/xalan27/src/com/nwalsh/xalan/FormatDingbatCallout.java [new file with mode: 0644]
xsl-java/xalan27/src/com/nwalsh/xalan/FormatGraphicCallout.java [new file with mode: 0644]
xsl-java/xalan27/src/com/nwalsh/xalan/FormatTextCallout.java [new file with mode: 0644]
xsl-java/xalan27/src/com/nwalsh/xalan/FormatUnicodeCallout.java [new file with mode: 0644]
xsl-java/xalan27/src/com/nwalsh/xalan/Func.java [new file with mode: 0644]
xsl-java/xalan27/src/com/nwalsh/xalan/ImageIntrinsics.java [new file with mode: 0644]
xsl-java/xalan27/src/com/nwalsh/xalan/Params.java [new file with mode: 0644]
xsl-java/xalan27/src/com/nwalsh/xalan/Table.java [new file with mode: 0644]
xsl-java/xalan27/src/com/nwalsh/xalan/Text.java [new file with mode: 0644]
xsl-java/xalan27/src/com/nwalsh/xalan/Verbatim.java [new file with mode: 0644]
xsl-java/xsltproc/python/.cvsignore [new file with mode: 0644]
xsl-java/xsltproc/python/README [new file with mode: 0644]
xsl-java/xsltproc/python/docbook.py [new file with mode: 0644]
xsl-java/xsltproc/python/xslt.py [new file with mode: 0644]

diff --git a/xsl-java/AUTHORS b/xsl-java/AUTHORS
new file mode 100644 (file)
index 0000000..5fba4b5
--- /dev/null
@@ -0,0 +1,3 @@
+The DocBook XSL Java extensions are maintained by Norman Walsh,
+<ndw@nwalsh.com>, and members of the DocBook Project,
+<docbook-developers@sf.net>
diff --git a/xsl-java/BUGS b/xsl-java/BUGS
new file mode 100644 (file)
index 0000000..a8152cf
--- /dev/null
@@ -0,0 +1,23 @@
+To view a list of all open bugs for the DocBook Project XSL Java
+extensions:
+
+  http://docbook.sf.net/tracker/xsl-java/bugs
+
+To submit a bug report against the XSL Java extensions::
+
+  http://docbook.sf.net/tracker/submit/bug
+
+To do a full-text search of all DocBook Project issues:
+
+  http://docbook.sf.net/tracker/search
+
+The XSL Java extensions are for use with the DocBook Project XSL
+stylesheets. Discussion about the DocBook XSL stylesheets takes
+place on the docbook-apps mailing list:
+
+  http://wiki.docbook.org/topic/DocBookAppsMailingList
+
+Real-time discussion takes place on IRC:
+
+  http://wiki.docbook.org/topic/DocBookIrcChannel
+  irc://irc.freenode.net/docbook
diff --git a/xsl-java/COPYING b/xsl-java/COPYING
new file mode 100644 (file)
index 0000000..555b97f
--- /dev/null
@@ -0,0 +1,45 @@
+Copyright
+---------
+Copyright (C) 1999-2007 Norman Walsh
+Copyright (C) 2005-2007 The DocBook Project
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the ``Software''), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+Except as contained in this notice, the names of individuals
+credited with contribution to this software shall not be used in
+advertising or otherwise to promote the sale, use or other
+dealings in this Software without prior written authorization
+from the individuals in question.
+
+Any stylesheet derived from this Software that is publically
+distributed will be identified with a different name and the
+version strings in any derived Software will be changed so that
+no possibility of confusion between the derived package and this
+Software will exist.
+
+Warranty
+--------
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT.  IN NO EVENT SHALL NORMAN WALSH OR ANY OTHER
+CONTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Contacting the Author
+---------------------
+The DocBook XSL Java extensions are maintained by Norman Walsh,
+<ndw@nwalsh.com>, and members of the DocBook Project,
+<docbook-developers@sf.net>
diff --git a/xsl-java/INSTALL b/xsl-java/INSTALL
new file mode 100644 (file)
index 0000000..37081ad
--- /dev/null
@@ -0,0 +1,32 @@
+$Id$
+
+INSTALL file for the DocBook XSL Java extensions
+
+----------------------------------------------------------------------
+Case #1: Installation using a package management system
+----------------------------------------------------------------------
+If you've installed the DocBook XSL Java distribution using
+"apt-get", "yum", "urpmi", or some similar package-management
+front-end, then, as part of the package installation, the
+saxon65.jar and xalan27.jar files for the extensions have most
+likely already been installed in the appropriate location for your
+system. To determine where the jar files are installed, consult
+the specific documentation for your system (for example, the
+README.Debian file).
+
+----------------------------------------------------------------------
+Case #2: Installing manually
+----------------------------------------------------------------------
+To manually "install" the saxon65.jar and xalan27.jar files, simply
+place them in a directory where you'd normally store jar files.
+
+----------------------------------------------------------------------
+How to use the DocBook XSL Java extensions
+----------------------------------------------------------------------
+To use the DocBook XSL Java extensions, add the absolute paths to
+the saxon65.jar and xalan27.jar files (wherever you may have them
+installed) to your Java classpath and process your documents with
+either the Saxon or Xalan-Java XSLT engines and with the value of
+the DocBook XSL stylesheets "use.extensions" parameter set to 1.
+
+For a few more details, see the README file.
diff --git a/xsl-java/Makefile b/xsl-java/Makefile
new file mode 100644 (file)
index 0000000..83bd57c
--- /dev/null
@@ -0,0 +1,35 @@
+include ../buildtools/Makefile.incl
+include ../releasetools/Variables.mk
+
+DISTRO=xsl-java
+
+# value of DISTRIB_DEPENDS is a space-separated list of any
+# targets for this distro's "distrib" target to depend on
+DISTRIB_DEPENDS =
+
+# value of ZIP_EXCLUDES is a space-separated list of any file or
+# directory names (shell wildcards OK) that should be excluded
+# from the zip file and tarball for the release
+DISTRIB_EXCLUDES = xsltproc saxon65 xalan27
+
+ANT = ant
+ANT_OPTS =
+
+all: jars
+
+jars: saxon65.jar xalan27.jar
+
+saxon65.jar: $(wildcard saxon65/src/com/nwalsh/saxon/*.java)
+       cd saxon65 && $(ANT) jar
+       cp saxon65/dist/saxon65.jar .
+
+xalan27.jar: $(wildcard xalan27/src/com/nwalsh/xalan/*.java)
+       cd xalan27 && $(ANT) jar
+       cp xalan27/dist/xalan27.jar .
+
+clean:
+       cd saxon65 && $(ANT) clean
+       cd xalan27 && $(ANT) clean
+       $(RM) *.jar
+
+include ../releasetools/Targets.mk
diff --git a/xsl-java/README b/xsl-java/README
new file mode 100644 (file)
index 0000000..06cc995
--- /dev/null
@@ -0,0 +1,52 @@
+----------------------------------------------------------------------
+              README file for the DocBook XSL Java Extensions
+----------------------------------------------------------------------
+$Id$
+
+These are XSL Java extensions for use with the DocBook XML
+stylesheets and the Saxon and Xalan-Java XSLT engines.
+
+This README file provides only very minimal documentation on the
+DocBook Project XSL Java extensions. For more complete information
+on the features the extensions provide and on how to use them
+with the DocBook Project XSL stylesheets, see Bob Stayton's book
+"DocBook XSL: The Complete Guide", available online at:
+
+  http://www.sagehill.net/docbookxsl/
+
+----------------------------------------------------------------------
+Installation
+----------------------------------------------------------------------
+For information about installing these extensions, see the INSTALL file.
+
+----------------------------------------------------------------------
+How to use the DocBook XSL Java extensions
+----------------------------------------------------------------------
+To use the DocBook XSL Java extensions, add the absolute paths to
+the saxon65.jar and xalan27.jar files to your Java classpath and
+process your documents with either the Saxon or Xalan-Java XSLT
+engines and with the value of the DocBook XSL stylesheets
+"use.extensions" parameter set to 1.
+
+----------------------------------------------------------------------
+Manifest
+----------------------------------------------------------------------
+AUTHORS       contact information
+BUGS          about known problems
+COPYING       copyright information
+INSTALL       installation instructions
+README        this file
+TODO          about planned features not yet implemented
+VERSION       release metadata, including the current version
+              number (note that the VERSION file is an XSL stylesheet)
+NEWS          changes since the last public release (for a cumulative
+              list of changes, see the ChangeHistory.xml file)
+
+saxon65.jar   Java extensions for use with the Saxon XSLT engine
+xalan27.jar   Java extensions for use with the Xalan-Java XSLT engine
+
+----------------------------------------------------------------------
+Changes
+----------------------------------------------------------------------
+See the ChangeHistory.xml.zip file for a cumulative list of all
+changes. See the NEWS file for changes made since the previous release.
diff --git a/xsl-java/TODO b/xsl-java/TODO
new file mode 100644 (file)
index 0000000..1f421cd
--- /dev/null
@@ -0,0 +1,23 @@
+The "to do" list for the DocBook Project XSL stylesheets is
+maintained at Sourceforge. To view a list of all open feature
+requests for the stylesheets:
+
+  http://docbook.sf.net/tracker/xsl/requests
+
+To submit a feature request against the stylesheets:
+
+  http://docbook.sf.net/tracker/submit/request
+
+To do a full-text search of all DocBook Project issues:
+
+  http://docbook.sf.net/tracker/search
+
+Discussion about the DocBook Project XSL stylesheets takes place
+on the docbook-apps mailing list:
+
+  http://wiki.docbook.org/topic/DocBookAppsMailingList
+
+Real-time discussion takes place on IRC:
+
+  http://wiki.docbook.org/topic/DocBookIrcChannel
+  irc://irc.freenode.net/docbook
diff --git a/xsl-java/VERSION b/xsl-java/VERSION
new file mode 100644 (file)
index 0000000..2a61786
--- /dev/null
@@ -0,0 +1,90 @@
+<?xml version='1.0'?> <!-- -*- nxml -*- -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:fm="http://freshmeat.net/projects/freshmeat-submit/"
+               xmlns:sf="http://sourceforge.net/"
+               exclude-result-prefixes="fm sf"
+                version='1.0'>
+
+<xsl:param name="VERSION" select="string(document('')//fm:Version[1])"/>
+<xsl:param name="DistroTitle" select="string(document('')//fm:Branch[1])"/>
+<xsl:param name="DistroName">docbook-xsl-java</xsl:param>
+<xsl:param name="PreviousRelease">1.72.0</xsl:param>
+<xsl:param name="PreviousReleaseRevision">6553</xsl:param>
+<xsl:param name="Revision">$Revision$</xsl:param>
+<xsl:param name="sf-relid" select="0"/>
+<xsl:strip-space elements="fm:*"/>
+
+<fm:project>
+  <fm:Project>DocBook</fm:Project>
+  <fm:Branch>XSL Stylesheets</fm:Branch>
+  <fm:Version>1.72.1-pre</fm:Version>
+<!--
+  <fm:License>MIT/X Consortium License</fm:License>
+-->
+  <fm:Release-Focus>
+<!-- * Initial freshmeat announcement -->
+<!-- * Documentation -->
+<!-- * Code cleanup -->
+<!-- * Minor feature enhancements -->
+Major feature enhancements
+<!-- * Minor bugfixes -->
+<!-- * Major bugfixes -->
+<!-- * Minor security fixes -->
+<!-- * Major security fixes -->
+  </fm:Release-Focus>
+  <fm:Home-Page-URL>http://sourceforge.net/projects/docbook/</fm:Home-Page-URL>
+  <fm:Gzipped-Tar-URL>http://prdownloads.sourceforge.net/docbook/{DISTRONAME-VERSION}.tar.gz?download</fm:Gzipped-Tar-URL>
+  <fm:Zipped-Tar-URL>http://prdownloads.sourceforge.net/docbook/{DISTRONAME-VERSION}.zip?download</fm:Zipped-Tar-URL>
+  <fm:Bzipped-Tar-URL>http://prdownloads.sourceforge.net/docbook/{DISTRONAME-VERSION}.bz2?download</fm:Bzipped-Tar-URL>
+  <fm:Changelog-URL>http://sourceforge.net/project/shownotes.php?release_id={SFRELID}</fm:Changelog-URL>
+  <fm:CVS-URL>http://docbook.cvs.sourceforge.net/docbook/</fm:CVS-URL>
+  <fm:Mailing-List-URL>http://lists.oasis-open.org/archives/docbook-apps/</fm:Mailing-List-URL>
+  <fm:Changes>This is a bug-fix release with a few feature enhancements.
+</fm:Changes>
+</fm:project>
+
+<xsl:template match="/" priority="-100">
+  <xsl:if test="$sf-relid = 0">
+    <xsl:message terminate="yes">
+      <xsl:text>You must specify the sf-relid as a parameter.</xsl:text>
+    </xsl:message>
+  </xsl:if>
+
+  <xsl:apply-templates select="//fm:project"/>
+</xsl:template>
+
+<xsl:template match="fm:project">
+  <xsl:text>&#10;</xsl:text>
+  <xsl:apply-templates/>
+  <xsl:text>&#10;</xsl:text>
+  <xsl:apply-templates select="fm:Changes" mode="text"/>
+</xsl:template>
+
+<xsl:template match="fm:Changes"/>
+
+<xsl:template match="fm:Gzipped-Tar-URL|fm:Zipped-Tar-URL|fm:Bzipped-Tar-URL">
+  <xsl:value-of select="local-name(.)"/>
+  <xsl:text>: </xsl:text>
+  <xsl:value-of select="substring-before(., '{DISTRONAME-VERSION}')"/>
+  <xsl:value-of select="concat($DistroName, '-', $VERSION)"/>
+  <xsl:value-of select="substring-after(., '{DISTRONAME-VERSION}')"/>
+  <xsl:text>&#10;</xsl:text>
+</xsl:template>
+
+<xsl:template match="fm:Changelog-URL">
+  <xsl:value-of select="local-name(.)"/>
+  <xsl:text>: </xsl:text>
+  <xsl:value-of select="substring-before(., '{SFRELID}')"/>
+  <xsl:value-of select="$sf-relid"/>
+  <xsl:value-of select="substring-after(., '{SFRELID}')"/>
+  <xsl:text>&#10;</xsl:text>
+</xsl:template>
+
+<xsl:template match="fm:*">
+  <xsl:value-of select="local-name(.)"/>
+  <xsl:text>: </xsl:text>
+  <xsl:value-of select="normalize-space(.)"/>
+  <xsl:text>&#10;</xsl:text>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/xsl-java/saxon65/build.xml b/xsl-java/saxon65/build.xml
new file mode 100644 (file)
index 0000000..8c065fa
--- /dev/null
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- You may freely edit this file. See commented blocks below for -->
+<!-- some examples of how to customize the build. -->
+<!-- (If you delete it and reopen the project it will be recreated.) -->
+<project name="saxon65" default="default" basedir=".">
+    <description>Builds, tests, and runs the project saxon65.</description>
+    <import file="nbproject/build-impl.xml"/>
+    <!--
+
+    There exist several targets which are by default empty and which can be 
+    used for execution of your tasks. These targets are usually executed 
+    before and after some main targets. They are: 
+
+      -pre-init:                 called before initialization of project properties
+      -post-init:                called after initialization of project properties
+      -pre-compile:              called before javac compilation
+      -post-compile:             called after javac compilation
+      -pre-compile-single:       called before javac compilation of single file
+      -post-compile-single:      called after javac compilation of single file
+      -pre-compile-test:         called before javac compilation of JUnit tests
+      -post-compile-test:        called after javac compilation of JUnit tests
+      -pre-compile-test-single:  called before javac compilation of single JUnit test
+      -post-compile-test-single: called after javac compilation of single JUunit test
+      -pre-jar:                  called before JAR building
+      -post-jar:                 called after JAR building
+      -post-clean:               called after cleaning build products
+
+    (Targets beginning with '-' are not intended to be called on their own.)
+
+    Example of inserting an obfuscator after compilation could look like this:
+
+        <target name="-post-compile">
+            <obfuscate>
+                <fileset dir="${build.classes.dir}"/>
+            </obfuscate>
+        </target>
+
+    For list of available properties check the imported 
+    nbproject/build-impl.xml file. 
+
+
+    Another way to customize the build is by overriding existing main targets.
+    The targets of interest are: 
+
+      -init-macrodef-javac:     defines macro for javac compilation
+      -init-macrodef-junit:     defines macro for junit execution
+      -init-macrodef-debug:     defines macro for class debugging
+      -init-macrodef-java:      defines macro for class execution
+      -do-jar-with-manifest:    JAR building (if you are using a manifest)
+      -do-jar-without-manifest: JAR building (if you are not using a manifest)
+      run:                      execution of project 
+      -javadoc-build:           Javadoc generation
+      test-report:              JUnit report generation
+
+    An example of overriding the target for project execution could look like this:
+
+        <target name="run" depends="saxon65-impl.jar">
+            <exec dir="bin" executable="launcher.exe">
+                <arg file="${dist.jar}"/>
+            </exec>
+        </target>
+
+    Notice that the overridden target depends on the jar target and not only on 
+    the compile target as the regular run target does. Again, for a list of available 
+    properties which you can use, check the target you are overriding in the
+    nbproject/build-impl.xml file. 
+
+    -->
+</project>
diff --git a/xsl-java/saxon65/nbproject/build-impl.xml b/xsl-java/saxon65/nbproject/build-impl.xml
new file mode 100644 (file)
index 0000000..9624598
--- /dev/null
@@ -0,0 +1,574 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+*** GENERATED FROM project.xml - DO NOT EDIT  ***
+***         EDIT ../build.xml INSTEAD         ***
+
+For the purpose of easier reading the script
+is divided into following sections:
+
+  - initialization
+  - compilation
+  - jar
+  - execution
+  - debugging
+  - javadoc
+  - junit compilation
+  - junit execution
+  - junit debugging
+  - applet
+  - cleanup
+
+-->
+<project name="saxon65-impl" default="default" basedir=".." xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:j2seproject2="http://www.netbeans.org/ns/j2se-project/2" xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1">
+    <target name="default" depends="test,jar,javadoc" description="Build and test whole project."/>
+    <!-- 
+    ======================
+    INITIALIZATION SECTION 
+    ======================
+    -->
+    <target name="-pre-init">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="-init-private" depends="-pre-init">
+        <property file="nbproject/private/private.properties"/>
+    </target>
+    <target name="-init-user" depends="-pre-init,-init-private">
+        <property file="${user.properties.file}"/>
+        <!-- The two properties below are usually overridden -->
+        <!-- by the active platform. Just a fallback. -->
+        <property name="default.javac.source" value="1.4"/>
+        <property name="default.javac.target" value="1.4"/>
+    </target>
+    <target name="-init-project" depends="-pre-init,-init-private,-init-user">
+        <property file="nbproject/project.properties"/>
+    </target>
+    <target name="-do-init" depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property">
+        <j2seproject1:property name="platform.home" value="platforms.${platform.active}.home"/>
+        <j2seproject1:property name="platform.bootcp" value="platforms.${platform.active}.bootclasspath"/>
+        <j2seproject1:property name="platform.compiler" value="platforms.${platform.active}.compile"/>
+        <j2seproject1:property name="platform.javac.tmp" value="platforms.${platform.active}.javac"/>
+        <condition property="platform.javac" value="${platform.home}/bin/javac">
+            <equals arg1="${platform.javac.tmp}" arg2="$${platforms.${platform.active}.javac}"/>
+        </condition>
+        <property name="platform.javac" value="${platform.javac.tmp}"/>
+        <j2seproject1:property name="platform.java.tmp" value="platforms.${platform.active}.java"/>
+        <condition property="platform.java" value="${platform.home}/bin/java">
+            <equals arg1="${platform.java.tmp}" arg2="$${platforms.${platform.active}.java}"/>
+        </condition>
+        <property name="platform.java" value="${platform.java.tmp}"/>
+        <j2seproject1:property name="platform.javadoc.tmp" value="platforms.${platform.active}.javadoc"/>
+        <condition property="platform.javadoc" value="${platform.home}/bin/javadoc">
+            <equals arg1="${platform.javadoc.tmp}" arg2="$${platforms.${platform.active}.javadoc}"/>
+        </condition>
+        <property name="platform.javadoc" value="${platform.javadoc.tmp}"/>
+        <condition property="platform.invalid" value="true">
+            <or>
+                <contains string="${platform.javac}" substring="$${platforms."/>
+                <contains string="${platform.java}" substring="$${platforms."/>
+                <contains string="${platform.javadoc}" substring="$${platforms."/>
+            </or>
+        </condition>
+        <fail unless="platform.home">Must set platform.home</fail>
+        <fail unless="platform.bootcp">Must set platform.bootcp</fail>
+        <fail unless="platform.java">Must set platform.java</fail>
+        <fail unless="platform.javac">Must set platform.javac</fail>
+        <fail if="platform.invalid">Platform is not correctly set up</fail>
+        <available file="${manifest.file}" property="manifest.available"/>
+        <condition property="manifest.available+main.class">
+            <and>
+                <isset property="manifest.available"/>
+                <isset property="main.class"/>
+                <not>
+                    <equals arg1="${main.class}" arg2="" trim="true"/>
+                </not>
+            </and>
+        </condition>
+        <condition property="manifest.available+main.class+mkdist.available">
+            <and>
+                <istrue value="${manifest.available+main.class}"/>
+                <isset property="libs.CopyLibs.classpath"/>
+            </and>
+        </condition>
+        <condition property="have.tests">
+            <or>
+                <available file="${test.src.dir}"/>
+            </or>
+        </condition>
+        <condition property="have.sources">
+            <or>
+                <available file="${src.dir}"/>
+            </or>
+        </condition>
+        <condition property="netbeans.home+have.tests">
+            <and>
+                <isset property="netbeans.home"/>
+                <isset property="have.tests"/>
+            </and>
+        </condition>
+        <condition property="no.javadoc.preview">
+            <isfalse value="${javadoc.preview}"/>
+        </condition>
+        <property name="run.jvmargs" value=""/>
+        <property name="javac.compilerargs" value=""/>
+        <property name="work.dir" value="${basedir}"/>
+        <condition property="no.deps">
+            <and>
+                <istrue value="${no.dependencies}"/>
+            </and>
+        </condition>
+        <property name="javac.debug" value="true"/>
+        <property name="javadoc.preview" value="true"/>
+    </target>
+    <target name="-post-init">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="-init-check" depends="-pre-init,-init-private,-init-user,-init-project,-do-init">
+        <fail unless="src.dir">Must set src.dir</fail>
+        <fail unless="test.src.dir">Must set test.src.dir</fail>
+        <fail unless="build.dir">Must set build.dir</fail>
+        <fail unless="dist.dir">Must set dist.dir</fail>
+        <fail unless="build.classes.dir">Must set build.classes.dir</fail>
+        <fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
+        <fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail>
+        <fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
+        <fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
+        <fail unless="dist.jar">Must set dist.jar</fail>
+    </target>
+    <target name="-init-macrodef-property">
+        <macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1">
+            <attribute name="name"/>
+            <attribute name="value"/>
+            <sequential>
+                <property name="@{name}" value="${@{value}}"/>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-macrodef-javac">
+        <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute name="srcdir" default="${src.dir}"/>
+            <attribute name="destdir" default="${build.classes.dir}"/>
+            <attribute name="classpath" default="${javac.classpath}"/>
+            <attribute name="debug" default="${javac.debug}"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <javac srcdir="@{srcdir}" destdir="@{destdir}" debug="@{debug}" deprecation="${javac.deprecation}" source="${javac.source}" target="${javac.target}" fork="yes" executable="${platform.javac}" tempdir="${java.io.tmpdir}" includeantruntime="false">
+                    <classpath>
+                        <path path="@{classpath}"/>
+                    </classpath>
+                    <compilerarg line="${javac.compilerargs}"/>
+                    <customize/>
+                </javac>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-macrodef-junit">
+        <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute name="includes" default="**/*Test.java"/>
+            <sequential>
+                <junit showoutput="true" fork="true" dir="${basedir}" failureproperty="tests.failed" errorproperty="tests.failed" jvm="${platform.java}">
+                    <batchtest todir="${build.test.results.dir}">
+                        <fileset dir="${test.src.dir}" includes="@{includes}"/>
+                    </batchtest>
+                    <classpath>
+                        <path path="${run.test.classpath}"/>
+                    </classpath>
+                    <syspropertyset>
+                        <propertyref prefix="test-sys-prop."/>
+                        <mapper type="glob" from="test-sys-prop.*" to="*"/>
+                    </syspropertyset>
+                    <formatter type="brief" usefile="false"/>
+                    <formatter type="xml"/>
+                    <jvmarg line="${run.jvmargs}"/>
+                </junit>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-macrodef-nbjpda">
+        <macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
+            <attribute name="name" default="${main.class}"/>
+            <attribute name="classpath" default="${debug.classpath}"/>
+            <attribute name="stopclassname" default=""/>
+            <sequential>
+                <nbjpdastart transport="dt_socket" addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}">
+                    <classpath>
+                        <path path="@{classpath}"/>
+                    </classpath>
+                    <bootclasspath>
+                        <path path="${platform.bootcp}"/>
+                    </bootclasspath>
+                </nbjpdastart>
+            </sequential>
+        </macrodef>
+        <macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1">
+            <attribute name="dir" default="${build.classes.dir}"/>
+            <sequential>
+                <nbjpdareload>
+                    <fileset includes="${fix.includes}*.class" dir="@{dir}"/>
+                </nbjpdareload>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-macrodef-debug">
+        <macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute name="classname" default="${main.class}"/>
+            <attribute name="classpath" default="${debug.classpath}"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <java fork="true" classname="@{classname}" dir="${work.dir}" jvm="${platform.java}">
+                    <jvmarg value="-Xdebug"/>
+                    <jvmarg value="-Xnoagent"/>
+                    <jvmarg value="-Djava.compiler=none"/>
+                    <jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
+                    <jvmarg line="${run.jvmargs}"/>
+                    <classpath>
+                        <path path="@{classpath}"/>
+                    </classpath>
+                    <syspropertyset>
+                        <propertyref prefix="run-sys-prop."/>
+                        <mapper type="glob" from="run-sys-prop.*" to="*"/>
+                    </syspropertyset>
+                    <customize/>
+                </java>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-macrodef-java">
+        <macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
+            <attribute name="classname" default="${main.class}"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <java fork="true" classname="@{classname}" dir="${work.dir}" jvm="${platform.java}">
+                    <jvmarg line="${run.jvmargs}"/>
+                    <classpath>
+                        <path path="${run.classpath}"/>
+                    </classpath>
+                    <syspropertyset>
+                        <propertyref prefix="run-sys-prop."/>
+                        <mapper type="glob" from="run-sys-prop.*" to="*"/>
+                    </syspropertyset>
+                    <customize/>
+                </java>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-presetdef-jar">
+        <presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
+            <jar jarfile="${dist.jar}" compress="${jar.compress}">
+                <j2seproject1:fileset dir="${build.classes.dir}"/>
+            </jar>
+        </presetdef>
+    </target>
+    <target name="init" depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-junit,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar"/>
+    <!--
+    ===================
+    COMPILATION SECTION
+    ===================
+    -->
+    <target name="deps-jar" depends="init" unless="no.deps"/>
+    <target name="-pre-pre-compile" depends="init,deps-jar">
+        <mkdir dir="${build.classes.dir}"/>
+    </target>
+    <target name="-pre-compile">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="-do-compile" depends="init,deps-jar,-pre-pre-compile,-pre-compile" if="have.sources">
+        <j2seproject3:javac/>
+        <copy todir="${build.classes.dir}">
+            <fileset dir="${src.dir}" excludes="${build.classes.excludes}"/>
+        </copy>
+    </target>
+    <target name="-post-compile">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="compile" depends="init,deps-jar,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project."/>
+    <target name="-pre-compile-single">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="-do-compile-single" depends="init,deps-jar,-pre-pre-compile">
+        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
+        <j2seproject3:javac>
+            <customize>
+                <patternset includes="${javac.includes}"/>
+            </customize>
+        </j2seproject3:javac>
+    </target>
+    <target name="-post-compile-single">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="compile-single" depends="init,deps-jar,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single"/>
+    <!--
+    ====================
+    JAR BUILDING SECTION
+    ====================
+    -->
+    <target name="-pre-pre-jar" depends="init">
+        <dirname property="dist.jar.dir" file="${dist.jar}"/>
+        <mkdir dir="${dist.jar.dir}"/>
+    </target>
+    <target name="-pre-jar">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="-do-jar-without-manifest" depends="init,compile,-pre-pre-jar,-pre-jar" unless="manifest.available">
+        <j2seproject1:jar/>
+    </target>
+    <target name="-do-jar-with-manifest" depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available" unless="manifest.available+main.class">
+        <j2seproject1:jar manifest="${manifest.file}"/>
+    </target>
+    <target name="-do-jar-with-mainclass" depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class" unless="manifest.available+main.class+mkdist.available">
+        <j2seproject1:jar manifest="${manifest.file}">
+            <j2seproject1:manifest>
+                <j2seproject1:attribute name="Main-Class" value="${main.class}"/>
+            </j2seproject1:manifest>
+        </j2seproject1:jar>
+        <echo>To run this application from the command line without Ant, try:</echo>
+        <property name="build.classes.dir.resolved" location="${build.classes.dir}"/>
+        <property name="dist.jar.resolved" location="${dist.jar}"/>
+        <pathconvert property="run.classpath.with.dist.jar">
+            <path path="${run.classpath}"/>
+            <map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
+        </pathconvert>
+        <echo>${platform.java} -cp "${run.classpath.with.dist.jar}" ${main.class}</echo>
+    </target>
+    <target name="-do-jar-with-libraries" depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class+mkdist.available">
+        <property name="build.classes.dir.resolved" location="${build.classes.dir}"/>
+        <pathconvert property="run.classpath.without.build.classes.dir">
+            <path path="${run.classpath}"/>
+            <map from="${build.classes.dir.resolved}" to=""/>
+        </pathconvert>
+        <pathconvert property="jar.classpath" pathsep=" ">
+            <path path="${run.classpath.without.build.classes.dir}"/>
+            <chainedmapper>
+                <flattenmapper/>
+                <globmapper from="*" to="lib/*"/>
+            </chainedmapper>
+        </pathconvert>
+        <taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" name="copylibs" classpath="${libs.CopyLibs.classpath}"/>
+        <copylibs manifest="${manifest.file}" runtimeclasspath="${run.classpath.without.build.classes.dir}" jarfile="${dist.jar}" compress="${jar.compress}">
+            <fileset dir="${build.classes.dir}"/>
+            <manifest>
+                <attribute name="Main-Class" value="${main.class}"/>
+                <attribute name="Class-Path" value="${jar.classpath}"/>
+            </manifest>
+        </copylibs>
+        <echo>To run this application from the command line without Ant, try:</echo>
+        <property name="dist.jar.resolved" location="${dist.jar}"/>
+        <echo>${platform.java} -jar "${dist.jar.resolved}"</echo>
+    </target>
+    <target name="-post-jar">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="jar" depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-post-jar" description="Build JAR."/>
+    <!--
+    =================
+    EXECUTION SECTION
+    =================
+    -->
+    <target name="run" depends="init,compile" description="Run a main class.">
+        <j2seproject1:java>
+            <customize>
+                <arg line="${application.args}"/>
+            </customize>
+        </j2seproject1:java>
+    </target>
+    <target name="run-single" depends="init,compile-single">
+        <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
+        <j2seproject1:java classname="${run.class}"/>
+    </target>
+    <!--
+    =================
+    DEBUGGING SECTION
+    =================
+    -->
+    <target name="-debug-start-debugger" if="netbeans.home" depends="init">
+        <j2seproject1:nbjpdastart name="${debug.class}"/>
+    </target>
+    <target name="-debug-start-debuggee" depends="init,compile">
+        <j2seproject3:debug>
+            <customize>
+                <arg line="${application.args}"/>
+            </customize>
+        </j2seproject3:debug>
+    </target>
+    <target name="debug" if="netbeans.home" depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE."/>
+    <target name="-debug-start-debugger-stepinto" if="netbeans.home" depends="init">
+        <j2seproject1:nbjpdastart stopclassname="${main.class}"/>
+    </target>
+    <target name="debug-stepinto" if="netbeans.home" depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee"/>
+    <target name="-debug-start-debuggee-single" if="netbeans.home" depends="init,compile-single">
+        <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
+        <j2seproject3:debug classname="${debug.class}"/>
+    </target>
+    <target name="debug-single" if="netbeans.home" depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-single"/>
+    <target name="-pre-debug-fix" depends="init">
+        <fail unless="fix.includes">Must set fix.includes</fail>
+        <property name="javac.includes" value="${fix.includes}.java"/>
+    </target>
+    <target name="-do-debug-fix" if="netbeans.home" depends="init,-pre-debug-fix,compile-single">
+        <j2seproject1:nbjpdareload/>
+    </target>
+    <target name="debug-fix" if="netbeans.home" depends="init,-pre-debug-fix,-do-debug-fix"/>
+    <!--
+    ===============
+    JAVADOC SECTION
+    ===============
+    -->
+    <target name="-javadoc-build" depends="init">
+        <mkdir dir="${dist.javadoc.dir}"/>
+        <javadoc destdir="${dist.javadoc.dir}" source="${javac.source}" notree="${javadoc.notree}" use="${javadoc.use}" nonavbar="${javadoc.nonavbar}" noindex="${javadoc.noindex}" splitindex="${javadoc.splitindex}" author="${javadoc.author}" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}" private="${javadoc.private}" additionalparam="${javadoc.additionalparam}" failonerror="true" useexternalfile="true" executable="${platform.javadoc}">
+            <classpath>
+                <path path="${javac.classpath}"/>
+            </classpath>
+            <sourcepath>
+                <pathelement location="${src.dir}"/>
+            </sourcepath>
+            <packageset dir="${src.dir}" includes="*/**"/>
+            <fileset dir="${src.dir}" includes="*.java"/>
+        </javadoc>
+    </target>
+    <target name="-javadoc-browse" if="netbeans.home" unless="no.javadoc.preview" depends="init,-javadoc-build">
+        <nbbrowse file="${dist.javadoc.dir}/index.html"/>
+    </target>
+    <target name="javadoc" depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc."/>
+    <!--
+    =========================
+    JUNIT COMPILATION SECTION
+    =========================
+    -->
+    <target name="-pre-pre-compile-test" if="have.tests" depends="init,compile">
+        <mkdir dir="${build.test.classes.dir}"/>
+    </target>
+    <target name="-pre-compile-test">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="-do-compile-test" if="have.tests" depends="init,compile,-pre-pre-compile-test,-pre-compile-test">
+        <j2seproject3:javac srcdir="${test.src.dir}" destdir="${build.test.classes.dir}" debug="true" classpath="${javac.test.classpath}"/>
+        <copy todir="${build.test.classes.dir}">
+            <fileset dir="${test.src.dir}" excludes="**/*.java"/>
+        </copy>
+    </target>
+    <target name="-post-compile-test">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="compile-test" depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test"/>
+    <target name="-pre-compile-test-single">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="-do-compile-test-single" if="have.tests" depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single">
+        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
+        <j2seproject3:javac srcdir="${test.src.dir}" destdir="${build.test.classes.dir}" debug="true" classpath="${javac.test.classpath}">
+            <customize>
+                <patternset includes="${javac.includes}"/>
+            </customize>
+        </j2seproject3:javac>
+        <copy todir="${build.test.classes.dir}">
+            <fileset dir="${test.src.dir}" excludes="**/*.java"/>
+        </copy>
+    </target>
+    <target name="-post-compile-test-single">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="compile-test-single" depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single"/>
+    <!--
+    =======================
+    JUNIT EXECUTION SECTION
+    =======================
+    -->
+    <target name="-pre-test-run" if="have.tests" depends="init">
+        <mkdir dir="${build.test.results.dir}"/>
+    </target>
+    <target name="-do-test-run" if="have.tests" depends="init,compile-test,-pre-test-run">
+        <j2seproject3:junit/>
+    </target>
+    <target name="-post-test-run" if="have.tests" depends="init,compile-test,-pre-test-run,-do-test-run">
+        <fail if="tests.failed">Some tests failed; see details above.</fail>
+    </target>
+    <target name="test-report" if="have.tests" depends="init"/>
+    <target name="-test-browse" if="netbeans.home+have.tests" depends="init"/>
+    <target name="test" depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests."/>
+    <target name="-pre-test-run-single" if="have.tests" depends="init">
+        <mkdir dir="${build.test.results.dir}"/>
+    </target>
+    <target name="-do-test-run-single" if="have.tests" depends="init,compile-test-single,-pre-test-run-single">
+        <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
+        <j2seproject3:junit includes="${test.includes}"/>
+    </target>
+    <target name="-post-test-run-single" if="have.tests" depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single">
+        <fail if="tests.failed">Some tests failed; see details above.</fail>
+    </target>
+    <target name="test-single" depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test."/>
+    <!--
+    =======================
+    JUNIT DEBUGGING SECTION
+    =======================
+    -->
+    <target name="-debug-start-debuggee-test" if="have.tests" depends="init,compile-test">
+        <fail unless="test.class">Must select one file in the IDE or set test.class</fail>
+        <j2seproject3:debug classname="junit.textui.TestRunner" classpath="${debug.test.classpath}">
+            <customize>
+                <arg line="${test.class}"/>
+            </customize>
+        </j2seproject3:debug>
+    </target>
+    <target name="-debug-start-debugger-test" if="netbeans.home+have.tests" depends="init,compile-test">
+        <j2seproject1:nbjpdastart name="${test.class}" classpath="${debug.test.classpath}"/>
+    </target>
+    <target name="debug-test" depends="init,compile-test,-debug-start-debugger-test,-debug-start-debuggee-test"/>
+    <target name="-do-debug-fix-test" if="netbeans.home" depends="init,-pre-debug-fix,compile-test-single">
+        <j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
+    </target>
+    <target name="debug-fix-test" if="netbeans.home" depends="init,-pre-debug-fix,-do-debug-fix-test"/>
+    <!--
+    =========================
+    APPLET EXECUTION SECTION
+    =========================
+    -->
+    <target name="run-applet" depends="init,compile-single">
+        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
+        <j2seproject1:java classname="sun.applet.AppletViewer">
+            <customize>
+                <arg value="${applet.url}"/>
+            </customize>
+        </j2seproject1:java>
+    </target>
+    <!--
+    =========================
+    APPLET DEBUGGING  SECTION
+    =========================
+    -->
+    <target name="-debug-start-debuggee-applet" if="netbeans.home" depends="init,compile-single">
+        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
+        <j2seproject3:debug classname="sun.applet.AppletViewer">
+            <customize>
+                <arg value="${applet.url}"/>
+            </customize>
+        </j2seproject3:debug>
+    </target>
+    <target name="debug-applet" if="netbeans.home" depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet"/>
+    <!--
+    ===============
+    CLEANUP SECTION
+    ===============
+    -->
+    <target name="deps-clean" depends="init" unless="no.deps"/>
+    <target name="-do-clean" depends="init">
+        <delete dir="${build.dir}"/>
+        <delete dir="${dist.dir}"/>
+    </target>
+    <target name="-post-clean">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="clean" depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products."/>
+</project>
diff --git a/xsl-java/saxon65/nbproject/genfiles.properties b/xsl-java/saxon65/nbproject/genfiles.properties
new file mode 100644 (file)
index 0000000..f20b702
--- /dev/null
@@ -0,0 +1,8 @@
+build.xml.data.CRC32=8e458dcb
+build.xml.script.CRC32=c0233ac0
+build.xml.stylesheet.CRC32=d5b6853a
+# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
+# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
+nbproject/build-impl.xml.data.CRC32=8e458dcb
+nbproject/build-impl.xml.script.CRC32=39e8d752
+nbproject/build-impl.xml.stylesheet.CRC32=99b91518
diff --git a/xsl-java/saxon65/nbproject/project.properties b/xsl-java/saxon65/nbproject/project.properties
new file mode 100644 (file)
index 0000000..a0924a4
--- /dev/null
@@ -0,0 +1,56 @@
+application.args=
+build.classes.dir=${build.dir}/classes
+build.classes.excludes=**/*.java,**/*.form
+# This directory is removed when the project is cleaned:
+build.dir=build
+build.generated.dir=${build.dir}/generated
+# Only compile against the classpath explicitly listed here:
+build.sysclasspath=ignore
+build.test.classes.dir=${build.dir}/test/classes
+build.test.results.dir=${build.dir}/test/results
+debug.classpath=\
+    ${run.classpath}
+debug.test.classpath=\
+    ${run.test.classpath}
+# This directory is removed when the project is cleaned:
+dist.dir=dist
+dist.jar=${dist.dir}/saxon65.jar
+dist.javadoc.dir=${dist.dir}/javadoc
+file.reference.saxon.jar=../../../../../usr/local/java/saxon6/saxon.jar
+jar.compress=false
+javac.classpath=\
+    ${file.reference.saxon.jar}
+# Space-separated list of extra javac options
+javac.compilerargs=
+javac.deprecation=false
+javac.source=1.4
+javac.target=1.4
+javac.test.classpath=\
+    ${javac.classpath}:\
+    ${build.classes.dir}:\
+    ${libs.junit.classpath}
+javadoc.additionalparam=
+javadoc.author=false
+javadoc.encoding=
+javadoc.noindex=false
+javadoc.nonavbar=false
+javadoc.notree=false
+javadoc.private=false
+javadoc.splitindex=true
+javadoc.use=true
+javadoc.version=false
+javadoc.windowtitle=
+meta.inf.dir=${src.dir}/META-INF
+platform.active=Java_HotSpot_TM__Client_VM_1.4.2_11-b06
+run.classpath=\
+    ${javac.classpath}:\
+    ${build.classes.dir}
+# Space-separated list of JVM arguments used when running the project
+# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
+# or test-sys-prop.name=value to set system properties for unit tests):
+run.jvmargs=
+run.test.classpath=\
+    ${javac.test.classpath}:\
+    ${build.test.classes.dir}
+src.dir=src
+test.src.dir=test
diff --git a/xsl-java/saxon65/nbproject/project.xml b/xsl-java/saxon65/nbproject/project.xml
new file mode 100644 (file)
index 0000000..3f4c7e7
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+    <type>org.netbeans.modules.java.j2seproject</type>
+    <configuration>
+        <data xmlns="http://www.netbeans.org/ns/j2se-project/3">
+            <name>saxon65</name>
+            <minimum-ant-version>1.6.5</minimum-ant-version>
+            <explicit-platform explicit-source-supported="true"/>
+            <source-roots>
+                <root id="src.dir"/>
+            </source-roots>
+            <test-roots>
+                <root id="test.src.dir"/>
+            </test-roots>
+        </data>
+    </configuration>
+</project>
diff --git a/xsl-java/saxon65/src/com/nwalsh/saxon/CVS.java b/xsl-java/saxon65/src/com/nwalsh/saxon/CVS.java
new file mode 100644 (file)
index 0000000..ca89f39
--- /dev/null
@@ -0,0 +1,90 @@
+package com.nwalsh.saxon;
+
+import java.io.*;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.text.DateFormat;
+import java.text.ParseException;
+
+/**
+ * <p>Saxon extension to convert CVS date strings into local time</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000 Norman Walsh.</p>
+ *
+ * <p>This class provides a
+ * <a href="http://saxon.sourceforge.net/">Saxon</a>
+ * extension to turn the CVS date strings, which are UTC:</p>
+ *
+ * <pre>&#36;Date: 2000/11/09 02:34:20 &#36;</pre>
+ *
+ * <p>into legibly formatted local time:</p>
+ *
+ * <pre>Wed Nov 08 18:34:20 PST 2000</pre>
+ *
+ * <p>(I happened to be in California when I wrote this documentation.)</p>
+
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @version $Id$
+ *
+ */
+public class CVS {
+  /**
+   * <p>Constructor for CVS</p>
+   *
+   * <p>All of the methods are static, so the constructor does nothing.</p>
+   */
+  public CVS() {
+  }
+
+  /**
+   * <p>Convert a CVS date string into local time.</p>
+   *
+   * @param cvsDate The CVS date string.
+   *
+   * @return The date, converted to local time and reformatted.
+   */
+  public static String localTime (String cvsDate) {
+    // A cvsDate has the following form "$Date$"
+    if (!cvsDate.startsWith("$Date: ")) {
+      return cvsDate;
+    }
+
+    String yrS = cvsDate.substring(7,11);
+    String moS = cvsDate.substring(12,14);
+    String daS = cvsDate.substring(15,17);
+    String hrS = cvsDate.substring(18,20);
+    String miS = cvsDate.substring(21,23);
+    String seS = cvsDate.substring(24,26);
+
+    TimeZone tz = TimeZone.getTimeZone("GMT+0");
+    GregorianCalendar gmtCal = new GregorianCalendar(tz);
+
+    try {
+      gmtCal.set(Integer.parseInt(yrS),
+                Integer.parseInt(moS)-1,
+                Integer.parseInt(daS),
+                Integer.parseInt(hrS),
+                Integer.parseInt(miS),
+                Integer.parseInt(seS));
+    } catch (NumberFormatException e) {
+      // nop
+    }
+
+    Date d = gmtCal.getTime();
+
+    return d.toString();
+  }
+}
diff --git a/xsl-java/saxon65/src/com/nwalsh/saxon/Callout.java b/xsl-java/saxon65/src/com/nwalsh/saxon/Callout.java
new file mode 100644 (file)
index 0000000..cffa054
--- /dev/null
@@ -0,0 +1,90 @@
+package com.nwalsh.saxon;
+
+import org.w3c.dom.*;
+
+/**
+ * <p>A class for maintaining information about callouts.</p>
+ *
+ * <p>To make processing callouts easier, they are parsed out of the
+ * input structure and stored in a sorted array. (The array is sorted
+ * according to the order in which the callouts occur.)</p>
+ *
+ * <p>This class is just the little record
+ * that we store in the array for each callout.</p>
+ */
+public class Callout implements Comparable {
+  /** The callout number. */
+  private int callout = 0;
+  /** The area Element item that generated this callout. */
+  private Element area = null;
+  /** The line on which this callout occurs. */
+  private int line = 0;
+  /** The column in which this callout appears. */
+  private int col = 0;
+
+  /** The constructor; initialize the private data structures. */
+  public Callout(int callout, Element area, int line, int col) {
+    this.callout = callout;
+    this.area = area;
+    this.line = line;
+    this.col = col;
+  }
+
+  /**
+   * <p>The compareTo method compares this Callout with another.</p>
+   *
+   * <p>Given two Callouts, A and B, A < B if:</p>
+   *
+   * <ol>
+   * <li>A.line < B.line, or</li>
+   * <li>A.line = B.line && A.col < B.col, or</li>
+   * <li>A.line = B.line && A.col = B.col && A.callout < B.callout</li>
+   * <li>Otherwise, they're equal.</li>
+   * </ol>
+   */
+  public int compareTo (Object o) {
+    Callout c = (Callout) o;
+
+    if (line == c.getLine()) {
+       if (col > c.getColumn()) {
+         return 1;
+       } else if (col < c.getColumn()) {
+         return -1;
+       } else {
+         if (callout < c.getCallout()) {
+           return -1;
+         } else if (callout > c.getCallout()) {
+           return 1;
+         } else {
+           return 0;
+         }
+       }
+    } else {
+       if (line > c.getLine()) {
+         return 1;
+       } else {
+         return -1;
+       }
+    }
+  }
+
+  /** Access the Callout's area. */
+  public Element getArea() {
+    return area;
+  }
+
+  /** Access the Callout's line. */
+  public int getLine() {
+    return line;
+  }
+
+  /** Access the Callout's column. */
+  public int getColumn() {
+    return col;
+  }
+
+  /** Access the Callout's callout number. */
+  public int getCallout() {
+    return callout;
+  }
+}
diff --git a/xsl-java/saxon65/src/com/nwalsh/saxon/CalloutEmitter.java b/xsl-java/saxon65/src/com/nwalsh/saxon/CalloutEmitter.java
new file mode 100644 (file)
index 0000000..a61b38c
--- /dev/null
@@ -0,0 +1,532 @@
+package com.nwalsh.saxon;
+
+import java.util.Stack;
+import java.util.StringTokenizer;
+import org.xml.sax.*;
+import org.w3c.dom.*;
+import javax.xml.transform.TransformerException;
+import com.icl.saxon.Controller;
+import com.icl.saxon.om.NamePool;
+import com.icl.saxon.output.Emitter;
+import com.icl.saxon.tree.AttributeCollection;
+
+/**
+ * <p>Saxon extension to decorate a result tree fragment with callouts.</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000 Norman Walsh.</p>
+ *
+ * <p>This class provides the guts of a
+ * <a href="http://saxon.sourceforge.net/">Saxon 6.*</a>
+ * implementation of callouts for verbatim environments. (It is used
+ * by the Verbatim class.)</p>
+ *
+ * <p>The general design is this: the stylesheets construct a result tree
+ * fragment for some verbatim environment. The Verbatim class initializes
+ * a CalloutEmitter with information about the callouts that should be applied
+ * to the verbatim environment in question. Then the result tree fragment
+ * is "replayed" through the CalloutEmitter; the CalloutEmitter builds a
+ * new result tree fragment from this event stream, decorated with callouts,
+ * and that is returned.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @see Verbatim
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @version $Id$
+ *
+ */
+public class CalloutEmitter extends CopyEmitter {
+  /** A stack for the preserving information about open elements. */
+  protected Stack elementStack = null;
+
+  /** A stack for holding information about temporarily closed elements. */
+  protected Stack tempStack = null;
+
+  /** Is the next element absolutely the first element in the fragment? */
+  protected boolean firstElement = false;
+
+  /** The FO namespace name. */
+  protected static String foURI = "http://www.w3.org/1999/XSL/Format";
+
+  /** The XHTML namespace name. */
+  protected static String xhURI = "http://www.w3.org/1999/xhtml";
+
+  /** The default column for callouts that specify only a line. */
+  protected int defaultColumn = 60;
+
+  /** Is the stylesheet currently running an FO stylesheet? */
+  protected boolean foStylesheet = false;
+
+  /** The current line number. */
+  private static int lineNumber = 0;
+
+  /** The current column number. */
+  private static int colNumber = 0;
+
+  /** The (sorted) array of callouts obtained from the areaspec. */
+  private static Callout callout[] = null;
+
+  /** The number of callouts in the callout array. */
+  private static int calloutCount = 0;
+
+  /** A pointer used to keep track of our position in the callout array. */
+  private static int calloutPos = 0;
+
+  /** The FormatCallout object to use for formatting callouts. */
+  private static FormatCallout fCallout = null;
+
+  /** <p>Constructor for the CalloutEmitter.</p>
+   *
+   * @param controller
+   * @param namePool The name pool to use for constructing elements and attributes.
+   * @param defaultColumn The default column for callouts.
+   * @param foStylesheet Is this an FO stylesheet?
+   * @param fCallout
+   */
+  public CalloutEmitter(Controller controller,
+                       NamePool namePool,
+                       int defaultColumn,
+                       boolean foStylesheet,
+                       FormatCallout fCallout) {
+    super(controller, namePool);
+    elementStack = new Stack();
+    firstElement = true;
+
+    this.defaultColumn = defaultColumn;
+    this.foStylesheet = foStylesheet;
+    this.fCallout = fCallout;
+  }
+
+  /**
+   * <p>Examine the areaspec and determine the number and position of 
+   * callouts.</p>
+   *
+   * <p>The <code><a href="http://docbook.org/tdg/html/areaspec.html">areaspecNodeSet</a></code>
+   * is examined and a sorted list of the callouts is constructed.</p>
+   *
+   * <p>This data structure is used to augment the result tree fragment
+   * with callout bullets.</p>
+   *
+   * @param areaspecNodeList The source document &lt;areaspec&gt; element.
+   */
+  public void setupCallouts (NodeList areaspecNodeList) {
+    callout = new Callout[10];
+    calloutCount = 0;
+    calloutPos = 0;
+    lineNumber = 1;
+    colNumber = 1;
+
+    // First we walk through the areaspec to calculate the position
+    // of the callouts
+    //  <areaspec>
+    //  <areaset id="ex.plco.const" coords="">
+    //    <area id="ex.plco.c1" coords="4"/>
+    //    <area id="ex.plco.c2" coords="8"/>
+    //  </areaset>
+    //  <area id="ex.plco.ret" coords="12"/>
+    //  <area id="ex.plco.dest" coords="12"/>
+    //  </areaspec>
+    int pos = 0;
+    int coNum = 0;
+    boolean inAreaSet = false;
+    Node areaspec = areaspecNodeList.item(0);
+    NodeList children = areaspec.getChildNodes();
+
+    for (int count = 0; count < children.getLength(); count++) {
+      Node node = children.item(count);
+      if (node.getNodeType() == Node.ELEMENT_NODE) {
+       if (node.getNodeName().equalsIgnoreCase("areaset")) {
+         coNum++;
+         NodeList areas = node.getChildNodes();
+         for (int acount = 0; acount < areas.getLength(); acount++) {
+           Node area = areas.item(acount);
+           if (area.getNodeType() == Node.ELEMENT_NODE) {
+             if (area.getNodeName().equalsIgnoreCase("area")) {
+               addCallout(coNum, area, defaultColumn);
+             } else {
+               System.out.println("Unexpected element in areaset: "
+                                  + area.getNodeName());
+             }
+           }
+         }
+       } else if (node.getNodeName().equalsIgnoreCase("area")) {
+         coNum++;
+         addCallout(coNum, node, defaultColumn);
+       } else {
+         System.out.println("Unexpected element in areaspec: "
+                            + node.getNodeName());
+       }
+      }
+    }
+
+    // Now sort them
+    java.util.Arrays.sort(callout, 0, calloutCount);
+  }
+
+  /** Process characters. */
+  public void characters(char[] chars, int start, int len)
+    throws TransformerException {
+
+    // If we hit characters, then there's no first element...
+    firstElement = false;
+
+    if (lineNumber == 0) {
+      // if there are any text nodes, there's at least one line
+      lineNumber++;
+      colNumber = 1;
+    }
+
+    // Walk through the text node looking for callout positions
+    char[] newChars = new char[len];
+    int pos = 0;
+    for (int count = start; count < start+len; count++) {
+      if (calloutPos < calloutCount
+         && callout[calloutPos].getLine() == lineNumber
+         && callout[calloutPos].getColumn() == colNumber) {
+       if (pos > 0) {
+         rtfEmitter.characters(newChars, 0, pos);
+         pos = 0;
+       }
+
+       closeOpenElements(rtfEmitter);
+
+       while (calloutPos < calloutCount
+              && callout[calloutPos].getLine() == lineNumber
+              && callout[calloutPos].getColumn() == colNumber) {
+         fCallout.formatCallout(rtfEmitter, callout[calloutPos]);
+         calloutPos++;
+       }
+
+       openClosedElements(rtfEmitter);
+      }
+
+      if (chars[count] == '\n') {
+       // What if we need to pad this line?
+       if (calloutPos < calloutCount
+           && callout[calloutPos].getLine() == lineNumber
+           && callout[calloutPos].getColumn() > colNumber) {
+
+         if (pos > 0) {
+           rtfEmitter.characters(newChars, 0, pos);
+           pos = 0;
+         }
+
+         closeOpenElements(rtfEmitter);
+
+         while (calloutPos < calloutCount
+                && callout[calloutPos].getLine() == lineNumber
+                && callout[calloutPos].getColumn() > colNumber) {
+           formatPad(callout[calloutPos].getColumn() - colNumber);
+           colNumber = callout[calloutPos].getColumn();
+           while (calloutPos < calloutCount
+                  && callout[calloutPos].getLine() == lineNumber
+                  && callout[calloutPos].getColumn() == colNumber) {
+             fCallout.formatCallout(rtfEmitter, callout[calloutPos]);
+             calloutPos++;
+           }
+         }
+
+         openClosedElements(rtfEmitter);
+       }
+
+       lineNumber++;
+       colNumber = 1;
+      } else {
+       colNumber++;
+      }
+      newChars[pos++] = chars[count];
+    }
+
+    if (pos > 0) {
+      rtfEmitter.characters(newChars, 0, pos);
+    }
+  }
+
+  /**
+   * <p>Add blanks to the result tree fragment.</p>
+   *
+   * <p>This method adds <tt>numBlanks</tt> to the result tree fragment.
+   * It's used to pad lines when callouts occur after the last existing
+   * characater in a line.</p>
+   *
+   * @param numBlanks The number of blanks to add.
+   */
+  protected void formatPad(int numBlanks) {
+    char chars[] = new char[numBlanks];
+    for (int count = 0; count < numBlanks; count++) {
+      chars[count] = ' ';
+    }
+
+    try {
+      rtfEmitter.characters(chars, 0, numBlanks);
+    } catch (TransformerException e) {
+      System.out.println("Transformer Exception in formatPad");
+    }
+  }
+
+  /**
+   * <p>Add a callout to the global callout array</p>
+   *
+   * <p>This method examines a callout <tt>area</tt> and adds it to
+   * the global callout array if it can be interpreted.</p>
+   *
+   * <p>Only the <tt>linecolumn</tt> and <tt>linerange</tt> units are
+   * supported. If no unit is specifed, <tt>linecolumn</tt> is assumed.
+   * If only a line is specified, the callout decoration appears in
+   * the <tt>defaultColumn</tt>.</p>
+   *
+   * @param coNum The callout number.
+   * @param node The <tt>area</tt>.
+   * @param defaultColumn The default column for callouts.
+   */
+  protected void addCallout (int coNum,
+                            Node node,
+                            int defaultColumn) {
+
+    Element area  = (Element) node;
+    String units  = null;
+    String coords = null;
+
+    if (area.hasAttribute("units")) {
+      units = area.getAttribute("units");
+    }
+
+    if (area.hasAttribute("coords")) {
+      coords = area.getAttribute("coords");
+    }
+
+    if (units != null
+       && !units.equalsIgnoreCase("linecolumn")
+       && !units.equalsIgnoreCase("linerange")) {
+      System.out.println("Only linecolumn and linerange units are supported");
+      return;
+    }
+
+    if (coords == null) {
+      System.out.println("Coords must be specified");
+      return;
+    }
+
+    // Now let's see if we can interpret the coordinates...
+    StringTokenizer st = new StringTokenizer(coords);
+    int tokenCount = 0;
+    int c1 = 0;
+    int c2 = 0;
+    while (st.hasMoreTokens()) {
+      tokenCount++;
+      if (tokenCount > 2) {
+       System.out.println("Unparseable coordinates");
+       return;
+      }
+      try {
+       String token = st.nextToken();
+       int coord = Integer.parseInt(token);
+       c2 = coord;
+       if (tokenCount == 1) {
+         c1 = coord;
+       }
+      } catch (NumberFormatException e) {
+       System.out.println("Unparseable coordinate");
+       return;
+      }
+    }
+
+    // Make sure we aren't going to blow past the end of our array
+    if (calloutCount == callout.length) {
+      Callout bigger[] = new Callout[calloutCount+10];
+      for (int count = 0; count < callout.length; count++) {
+       bigger[count] = callout[count];
+      }
+      callout = bigger;
+    }
+
+    // Ok, add the callout
+    if (tokenCount == 2) {
+      if (units != null && units.equalsIgnoreCase("linerange")) {
+       for (int count = c1; count <= c2; count++) {
+         callout[calloutCount++] = new Callout(coNum, area,
+                                               count, defaultColumn);
+       }
+      } else {
+       // assume linecolumn
+       callout[calloutCount++] = new Callout(coNum, area, c1, c2);
+      }
+    } else {
+      // if there's only one number, assume it's the line
+      callout[calloutCount++] = new Callout(coNum, area, c1, defaultColumn);
+    }
+  }
+
+  /** Process end element events. */
+  public void endElement(int nameCode)
+    throws TransformerException {
+
+    if (!elementStack.empty()) {
+      // if we didn't push the very first element (an fo:block or
+      // pre or div surrounding the whole block), then the stack will
+      // be empty when we get to the end of the first element...
+      elementStack.pop();
+    }
+    rtfEmitter.endElement(nameCode);
+  }
+
+  /** Process start element events. */
+  public void startElement(int nameCode,
+                          org.xml.sax.Attributes attributes,
+                          int[] namespaces,
+                          int nscount)
+    throws TransformerException {
+
+    if (!skipThisElement(nameCode)) {
+      StartElementInfo sei = new StartElementInfo(nameCode, attributes,
+                                                 namespaces, nscount);
+      elementStack.push(sei);
+    }
+
+    firstElement = false;
+
+    rtfEmitter.startElement(nameCode, attributes, namespaces, nscount);
+  }
+
+  /**
+   * <p>Protect the outer-most block wrapper.</p>
+   *
+   * <p>Open elements in the result tree fragment are closed and reopened
+   * around callouts (so that callouts don't appear inside links or other
+   * environments). But if the result tree fragment is a single block
+   * (a div or pre in HTML, an fo:block in FO), that outer-most block is
+   * treated specially.</p>
+   *
+   * <p>This method returns true if the element in question is that
+   * outermost block.</p>
+   *
+   * @param nameCode The name code for the element
+   *
+   * @return True if the element is the outer-most block, false otherwise.
+   */
+  protected boolean skipThisElement(int nameCode) {
+    // FIXME: This is such a gross hack...
+    if (firstElement) {
+      int thisFingerprint    = namePool.getFingerprint(nameCode);
+      int foBlockFingerprint = namePool.getFingerprint(foURI, "block");
+      int htmlPreFingerprint = namePool.getFingerprint("", "pre");
+      int htmlDivFingerprint = namePool.getFingerprint("", "div");
+      int xhtmlPreFingerprint = namePool.getFingerprint(xhURI, "pre");
+      int xhtmlDivFingerprint = namePool.getFingerprint(xhURI, "div");
+
+      if ((foStylesheet && thisFingerprint == foBlockFingerprint)
+         || (!foStylesheet && (thisFingerprint == htmlPreFingerprint
+                               || thisFingerprint == htmlDivFingerprint
+                               || thisFingerprint == xhtmlPreFingerprint
+                               || thisFingerprint == xhtmlDivFingerprint))) {
+       // Don't push the outer-most wrapping div, pre, or fo:block
+       return true;
+      }
+    }
+
+    return false;
+  }
+
+  private void closeOpenElements(Emitter rtfEmitter)
+    throws TransformerException {
+    // Close all the open elements...
+    tempStack = new Stack();
+    while (!elementStack.empty()) {
+      StartElementInfo elem = (StartElementInfo) elementStack.pop();
+      rtfEmitter.endElement(elem.getNameCode());
+      tempStack.push(elem);
+    }
+  }
+
+  private void openClosedElements(Emitter rtfEmitter)
+    throws TransformerException {
+    // Now "reopen" the elements that we closed...
+    while (!tempStack.empty()) {
+      StartElementInfo elem = (StartElementInfo) tempStack.pop();
+      AttributeCollection attr = (AttributeCollection) elem.getAttributes();
+      AttributeCollection newAttr = new AttributeCollection(namePool);
+
+      for (int acount = 0; acount < attr.getLength(); acount++) {
+       String localName = attr.getLocalName(acount);
+       int nameCode = attr.getNameCode(acount);
+       String type = attr.getType(acount);
+       String value = attr.getValue(acount);
+       String uri = attr.getURI(acount);
+       String prefix = "";
+
+       if (localName.indexOf(':') > 0) {
+         prefix = localName.substring(0, localName.indexOf(':'));
+         localName = localName.substring(localName.indexOf(':')+1);
+       }
+
+       if (uri.equals("")
+           && ((foStylesheet
+                && localName.equals("id"))
+               || (!foStylesheet
+                   && (localName.equals("id")
+                       || localName.equals("name"))))) {
+         // skip this attribute
+       } else {
+         newAttr.addAttribute(prefix, uri, localName, type, value);
+       }
+      }
+
+      rtfEmitter.startElement(elem.getNameCode(),
+                             newAttr,
+                             elem.getNamespaces(),
+                             elem.getNSCount());
+
+      elementStack.push(elem);
+    }
+  }
+
+  /**
+   * <p>A private class for maintaining the information required to call
+   * the startElement method.</p>
+   *
+   * <p>In order to close and reopen elements, information about those
+   * elements has to be maintained. This class is just the little record
+   * that we push on the stack to keep track of that info.</p>
+   */
+  private class StartElementInfo {
+    private int _nameCode;
+    org.xml.sax.Attributes _attributes;
+    int[] _namespaces;
+    int _nscount;
+
+    public StartElementInfo(int nameCode,
+                           org.xml.sax.Attributes attributes,
+                           int[] namespaces,
+                           int nscount) {
+      _nameCode = nameCode;
+      _attributes = attributes;
+      _namespaces = namespaces;
+      _nscount = nscount;
+    }
+
+    public int getNameCode() {
+      return _nameCode;
+    }
+
+    public org.xml.sax.Attributes getAttributes() {
+      return _attributes;
+    }
+
+    public int[] getNamespaces() {
+      return _namespaces;
+    }
+
+    public int getNSCount() {
+      return _nscount;
+    }
+  }
+}
diff --git a/xsl-java/saxon65/src/com/nwalsh/saxon/ColumnScanEmitter.java b/xsl-java/saxon65/src/com/nwalsh/saxon/ColumnScanEmitter.java
new file mode 100644 (file)
index 0000000..6efe35a
--- /dev/null
@@ -0,0 +1,170 @@
+package com.nwalsh.saxon;
+
+import org.xml.sax.*;
+import javax.xml.transform.TransformerException;
+import com.icl.saxon.output.*;
+import com.icl.saxon.om.*;
+import com.icl.saxon.expr.FragmentValue;
+
+/**
+ * <p>Saxon extension to scan the column widths in a result tree fragment.</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000 Norman Walsh.</p>
+ *
+ * <p>This class provides a
+ * <a href="http://saxon.sourceforge.net/">Saxon 6.*</a>
+ * implementation to scan the column widths in a result tree
+ * fragment.</p>
+ *
+ * <p>The general design is this: the stylesheets construct a result tree
+ * fragment for some colgroup environment. That result tree fragment
+ * is "replayed" through the ColumnScanEmitter; the ColumnScanEmitter watches
+ * the cols go by and extracts the column widths that it sees. These
+ * widths are then made available.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @version $Id$
+ *
+ */
+public class ColumnScanEmitter extends com.icl.saxon.output.Emitter {
+  /** The number of columns seen. */
+  protected int numColumns = 0;
+  protected String width[] = new String[5];
+  protected NamePool namePool = null;
+
+  /** The FO namespace name. */
+  protected static String foURI = "http://www.w3.org/1999/XSL/Format";
+
+  /** Construct a new ColumnScanEmitter. */
+  public ColumnScanEmitter(NamePool namePool) {
+    numColumns = 0;
+    this.namePool = namePool;
+  }
+
+  /** Return the number of columns. */
+  public int columnCount() {
+    return numColumns;
+  }
+
+  /** Return the number of columns. */
+  public String[] columnWidths() {
+    // Return a width array with exactly the right number of columns
+    String rWidth[] = new String[numColumns];
+    for (int count = 0; count < numColumns; count++) {
+      rWidth[count] = width[count];
+    }
+    return rWidth;
+  }
+
+  /** Discarded. */
+  public void characters(char[] chars, int start, int len)
+    throws TransformerException {
+    // nop
+  }
+
+  /** Discarded. */
+  public void comment(char[] chars, int start, int length)
+    throws TransformerException {
+    // nop
+  }
+
+  /** Discarded. */
+  public void endDocument()
+    throws TransformerException {
+    // nop
+  }
+
+  /** Discarded. */
+  public void endElement(int nameCode)
+    throws TransformerException {
+    // nop
+  }
+
+  /** Discarded. */
+  public void processingInstruction(java.lang.String name,
+                                   java.lang.String data)
+    throws TransformerException {
+    // nop
+  }
+
+  /** Discarded. */
+  public void setDocumentLocator(org.xml.sax.Locator locator) {
+    // nop
+  }
+
+  /** Discarded. */
+  public void setEscaping(boolean escaping)
+    throws TransformerException {
+    // nop
+  }
+
+  /** Discarded. */
+  public void setNamePool(NamePool namePool) {
+    // nop
+  }
+
+  /** Discarded. */
+  public void setUnparsedEntity(java.lang.String name, java.lang.String uri)
+    throws TransformerException {
+    // nop
+  }
+
+  /** Discarded. */
+  public void setWriter(java.io.Writer writer) {
+    // nop
+  }
+
+  /** Discarded. */
+  public void startDocument()
+    throws TransformerException {
+    // nop
+  }
+
+  /** Examine for column info. */
+  public void startElement(int nameCode,
+                   org.xml.sax.Attributes attributes,
+                   int[] namespaces, int nscount)
+    throws TransformerException {
+
+    int thisFingerprint = namePool.getFingerprint(nameCode);
+    int colFingerprint = namePool.getFingerprint("", "col");
+    int foColFingerprint = namePool.getFingerprint(foURI, "table-column");
+
+    if (thisFingerprint == colFingerprint
+       || thisFingerprint == foColFingerprint) {
+      if (numColumns >= width.length) {
+       String newWidth[] = new String[width.length+10];
+       for (int count = 0; count < width.length; count++) {
+         newWidth[count] = width[count];
+       }
+       width = newWidth;
+      }
+
+      if (thisFingerprint == colFingerprint) {
+       if (attributes.getValue("width") == null) {
+         width[numColumns++] = "1*";
+       } else {
+         width[numColumns++] = attributes.getValue("width");
+       }
+      } else {
+       if (attributes.getValue("column-width") == null) {
+         width[numColumns++] = "1*";
+       } else {
+         width[numColumns++] = attributes.getValue("column-width");
+       }
+      }
+    }
+  }
+}
+
+
diff --git a/xsl-java/saxon65/src/com/nwalsh/saxon/ColumnUpdateEmitter.java b/xsl-java/saxon65/src/com/nwalsh/saxon/ColumnUpdateEmitter.java
new file mode 100644 (file)
index 0000000..36434ca
--- /dev/null
@@ -0,0 +1,96 @@
+package com.nwalsh.saxon;
+
+import org.xml.sax.*;
+import com.icl.saxon.output.*;
+import com.icl.saxon.Controller;
+import com.icl.saxon.om.*;
+import javax.xml.transform.TransformerException;
+import com.icl.saxon.expr.FragmentValue;
+import com.icl.saxon.tree.AttributeCollection;
+
+/**
+ * <p>Saxon extension to scan the column widths in a result tree fragment.</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000 Norman Walsh.</p>
+ *
+ * <p>This class provides a
+ * <a href="http://saxon.sourceforge.net/">Saxon 6.*</a>
+ * implementation to scan the column widths in a result tree
+ * fragment.</p>
+ *
+ * <p>The general design is this: the stylesheets construct a result tree
+ * fragment for some colgroup environment. That result tree fragment
+ * is "replayed" through the ColumnUpdateEmitter; the ColumnUpdateEmitter watches
+ * the cols go by and extracts the column widths that it sees. These
+ * widths are then made available.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @version $Id$
+ *
+ */
+public class ColumnUpdateEmitter extends CopyEmitter {
+  /** The number of columns seen. */
+  protected int numColumns = 0;
+  protected String width[] = null;
+  protected NamePool namePool = null;
+
+  /** The FO namespace name. */
+  protected static String foURI = "http://www.w3.org/1999/XSL/Format";
+
+  /** Construct a new ColumnUpdateEmitter. */
+  public ColumnUpdateEmitter(Controller controller,
+                            NamePool namePool,
+                            String width[]) {
+    super(controller, namePool);
+    numColumns = 0;
+    this.width = width;
+    this.namePool = namePool;
+  }
+
+  /** Examine for column info. */
+  public void startElement(int nameCode,
+                   org.xml.sax.Attributes attributes,
+                   int[] namespaces, int nscount)
+    throws TransformerException {
+
+    int thisFingerprint = namePool.getFingerprint(nameCode);
+    int colFingerprint = namePool.getFingerprint("", "col");
+    int foColFingerprint = namePool.getFingerprint(foURI, "table-column");
+
+    if (thisFingerprint == colFingerprint) {
+      AttributeCollection attr = new AttributeCollection(namePool, attributes);
+      int widthFingerprint = namePool.getFingerprint("", "width");
+
+      if (attr.getValueByFingerprint(widthFingerprint) == null) {
+       attr.addAttribute(widthFingerprint, "CDATA", width[numColumns++]);
+      } else {
+       attr.setAttribute(widthFingerprint, "CDATA", width[numColumns++]);
+      }
+      attributes = attr;
+    } else if (thisFingerprint == foColFingerprint) {
+      AttributeCollection attr = new AttributeCollection(namePool, attributes);
+      int widthFingerprint = namePool.getFingerprint("", "column-width");
+
+      if (attr.getValueByFingerprint(widthFingerprint) == null) {
+       attr.addAttribute(widthFingerprint, "CDATA", width[numColumns++]);
+      } else {
+       attr.setAttribute(widthFingerprint, "CDATA", width[numColumns++]);
+      }
+      attributes = attr;
+    }
+
+    rtfEmitter.startElement(nameCode, attributes, namespaces, nscount);
+  }
+}
+
+
diff --git a/xsl-java/saxon65/src/com/nwalsh/saxon/CopyEmitter.java b/xsl-java/saxon65/src/com/nwalsh/saxon/CopyEmitter.java
new file mode 100644 (file)
index 0000000..3718c50
--- /dev/null
@@ -0,0 +1,150 @@
+package com.nwalsh.saxon;
+
+import java.util.Stack;
+import java.util.StringTokenizer;
+import org.xml.sax.*;
+import org.w3c.dom.*;
+import javax.xml.transform.TransformerException;
+import com.icl.saxon.Context;
+import com.icl.saxon.expr.*;
+import com.icl.saxon.expr.FragmentValue;
+import com.icl.saxon.Controller;
+import com.icl.saxon.functions.Extensions;
+import com.icl.saxon.om.*;
+import com.icl.saxon.output.*;
+import com.icl.saxon.pattern.*;
+import com.icl.saxon.tree.*;
+
+/**
+ * <p>A Saxon 6.0 Emitter that clones its input.</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000 Norman Walsh.</p>
+ *
+ * <p>This class provides a
+ * <a href="http://saxon.sourceforge.net/">Saxon 6.*</a>
+ * implementation of an emitter that manufactures a cloned result
+ * tree fragment.</p>
+ *
+ * <p>The purpose of this emitter is to provide something for
+ * CalloutEmitter and NumberLinesEmitter to extend.
+ * This emitter simply copies all input to a new result tree fragment.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @see CalloutEmitter
+ * @see NumberLinesEmitter
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @version $Id$
+ *
+ */
+public class CopyEmitter extends com.icl.saxon.output.Emitter {
+  /** The result tree fragment containing the copied fragment. */
+  protected FragmentValue rtf = null;
+  protected Emitter rtfEmitter = null;
+
+  /** <p>The namePool.</p>
+   *
+   * <p>Copied from the caller, it should be the runtime name pool.</p>
+   */
+  protected NamePool namePool = null;
+
+  /** <p>Constructor for the CopyEmitter.</p>
+   *
+   * @param namePool The name pool to use for constructing elements and attributes.
+   */
+  public CopyEmitter(Controller controller, NamePool namePool) {
+    rtf = new FragmentValue(controller);
+    rtfEmitter = rtf.getEmitter();
+    this.namePool = namePool;
+  }
+
+  /**
+   * <p>Return the result tree fragment constructed by replaying events
+   * through this emitter.</p>
+   */
+  public FragmentValue getResultTreeFragment() {
+    return rtf;
+  }
+
+  /** Copy characters. */
+  public void characters(char[] chars, int start, int len)
+    throws TransformerException {
+    rtfEmitter.characters(chars, start, len);
+  }
+
+  /** Copy comments. */
+  public void comment(char[] chars, int start, int length)
+    throws TransformerException {
+    rtfEmitter.comment(chars, start, length);
+  }
+
+  /** Copy end document events. */
+  public void endDocument()
+    throws TransformerException {
+    rtfEmitter.endDocument();
+  }
+
+  /** Copy end element events. */
+  public void endElement(int nameCode)
+    throws TransformerException {
+    rtfEmitter.endElement(nameCode);
+  }
+
+  /** Copy processing instructions. */
+  public void processingInstruction(java.lang.String name,
+                                   java.lang.String data)
+    throws TransformerException {
+    rtfEmitter.processingInstruction(name, data);
+  }
+
+  /** Copy set document locator events. */
+  public void setDocumentLocator(org.xml.sax.Locator locator) {
+    rtfEmitter.setDocumentLocator(locator);
+  }
+
+  /** Copy set escaping events. */
+  public void setEscaping(boolean escaping)
+    throws TransformerException {
+    rtfEmitter.setEscaping(escaping);
+  }
+
+  /** Copy set name pool events. */
+  public void setNamePool(NamePool namePool) {
+    rtfEmitter.setNamePool(namePool);
+  }
+
+  /** Copy set unparsed entity events. */
+  public void setUnparsedEntity(java.lang.String name, java.lang.String uri)
+    throws TransformerException {
+    rtfEmitter.setUnparsedEntity(name, uri);
+  }
+
+  /** Copy set writer events. */
+  public void setWriter(java.io.Writer writer) {
+    rtfEmitter.setWriter(writer);
+  }
+
+  /** Copy start document events. */
+  public void startDocument()
+    throws TransformerException {
+    rtfEmitter.startDocument();
+  }
+
+  /** Copy start element events. */
+  public void startElement(int nameCode,
+                          org.xml.sax.Attributes attributes,
+                          int[] namespaces,
+                          int nscount)
+    throws TransformerException {
+    rtfEmitter.startElement(nameCode, attributes, namespaces, nscount);
+  }
+}
diff --git a/xsl-java/saxon65/src/com/nwalsh/saxon/FormatCallout.java b/xsl-java/saxon65/src/com/nwalsh/saxon/FormatCallout.java
new file mode 100644 (file)
index 0000000..256a3b5
--- /dev/null
@@ -0,0 +1,111 @@
+package com.nwalsh.saxon;
+
+import org.xml.sax.SAXException;
+import org.w3c.dom.*;
+
+import javax.xml.transform.TransformerException;
+
+import com.icl.saxon.om.NamePool;
+import com.icl.saxon.output.Emitter;
+import com.icl.saxon.tree.AttributeCollection;
+
+import com.nwalsh.saxon.Callout;
+
+/**
+ * <p>Utility class for the Verbatim extension (ignore this).</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000, 2001 Norman Walsh.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @see Verbatim
+ *
+ * @version $Id$
+ **/
+
+public abstract class FormatCallout {
+  protected static final String foURI = "http://www.w3.org/1999/XSL/Format";
+  protected static final String xhURI = "http://www.w3.org/1999/xhtml";
+  protected boolean foStylesheet = false;
+  protected NamePool namePool = null;
+
+  public FormatCallout(NamePool nPool, boolean fo) {
+    namePool = nPool;
+    foStylesheet = fo;
+  }
+
+  public String areaLabel(Element area) {
+    String label = null;
+
+    if (area.hasAttribute("label")) {
+      // If this area has a label, use it
+      label = area.getAttribute("label");
+    } else {
+      // Otherwise, if its parent is an areaset and it has a label, use that
+      Element parent = (Element) area.getParentNode();
+      if (parent != null
+         && parent.getLocalName().equalsIgnoreCase("areaset")
+         && parent.hasAttribute("label")) {
+       label = parent.getAttribute("label");
+      }
+    }
+
+    return label;
+  }
+
+  public void startSpan(Emitter rtf)
+    throws TransformerException {
+    // no point in doing this for FO, right?
+    if (!foStylesheet && namePool != null) {
+      int spanName = namePool.allocate("", "", "span");
+      AttributeCollection spanAttr = new AttributeCollection(namePool);
+      int namespaces[] = new int[1];
+      spanAttr.addAttribute("", "", "class", "CDATA", "co");
+      rtf.startElement(spanName, spanAttr, namespaces, 0);
+    }
+  }
+
+  public void endSpan(Emitter rtf)
+    throws TransformerException {
+    // no point in doing this for FO, right?
+    if (!foStylesheet && namePool != null) {
+      int spanName = namePool.allocate("", "", "span");
+      rtf.endElement(spanName);
+    }
+  }
+
+  public void formatTextCallout(Emitter rtfEmitter,
+                               Callout callout) {
+    Element area = callout.getArea();
+    int num = callout.getCallout();
+    String userLabel = areaLabel(area);
+    String label = "(" + num + ")";
+
+    if (userLabel != null) {
+      label = userLabel;
+    }
+
+    char chars[] = label.toCharArray();
+
+    try {
+      startSpan(rtfEmitter);
+      rtfEmitter.characters(chars, 0, label.length());
+      endSpan(rtfEmitter);
+    } catch (TransformerException e) {
+      System.out.println("Transformer Exception in formatTextCallout");
+    }
+  }
+
+  public abstract void formatCallout(Emitter rtfEmitter,
+                                    Callout callout);
+}
+
diff --git a/xsl-java/saxon65/src/com/nwalsh/saxon/FormatGraphicCallout.java b/xsl-java/saxon65/src/com/nwalsh/saxon/FormatGraphicCallout.java
new file mode 100644 (file)
index 0000000..c2f16d9
--- /dev/null
@@ -0,0 +1,89 @@
+package com.nwalsh.saxon;
+
+import org.xml.sax.SAXException;
+import org.w3c.dom.*;
+
+import javax.xml.transform.TransformerException;
+
+import com.icl.saxon.om.NamePool;
+import com.icl.saxon.output.Emitter;
+import com.icl.saxon.tree.AttributeCollection;
+
+import com.nwalsh.saxon.Callout;
+
+/**
+ * <p>Utility class for the Verbatim extension (ignore this).</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000, 2001 Norman Walsh.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @see Verbatim
+ *
+ * @version $Id$
+ **/
+
+public class FormatGraphicCallout extends FormatCallout {
+  String graphicsPath = "";
+  String graphicsExt = "";
+  int graphicsMax = 0;
+
+  public FormatGraphicCallout(NamePool nPool, String path, String ext, int max, boolean fo) {
+    super(nPool, fo);
+    graphicsPath = path;
+    graphicsExt = ext;
+    graphicsMax = max;
+  }
+
+  public void formatCallout(Emitter rtfEmitter,
+                           Callout callout) {
+    Element area = callout.getArea();
+    int num = callout.getCallout();
+    String userLabel = areaLabel(area);
+    String label = "(" + num + ")";
+
+    if (userLabel != null) {
+      label = userLabel;
+    }
+
+    try {
+      if (userLabel == null && num <= graphicsMax) {
+       int imgName = 0;
+       AttributeCollection imgAttr = null;
+       int namespaces[] = new int[1];
+
+       if (foStylesheet) {
+         imgName = namePool.allocate("fo", foURI, "external-graphic");
+         imgAttr = new AttributeCollection(namePool);
+         imgAttr.addAttribute("", "", "src", "CDATA", "url(" +          
+                              graphicsPath + num + graphicsExt + ")"); 
+         
+       } else {
+         imgName = namePool.allocate("", "", "img");
+         imgAttr = new AttributeCollection(namePool);
+         imgAttr.addAttribute("", "", "src", "CDATA",
+                              graphicsPath + num + graphicsExt);
+         imgAttr.addAttribute("", "", "alt", "CDATA", label);
+       }
+
+       startSpan(rtfEmitter);
+       rtfEmitter.startElement(imgName, imgAttr, namespaces, 0);
+       rtfEmitter.endElement(imgName);
+       endSpan(rtfEmitter);
+      } else {
+       formatTextCallout(rtfEmitter, callout);
+      }
+    } catch (TransformerException e) {
+      System.out.println("Transformer Exception in graphic formatCallout");
+    }
+  }
+}
diff --git a/xsl-java/saxon65/src/com/nwalsh/saxon/FormatTextCallout.java b/xsl-java/saxon65/src/com/nwalsh/saxon/FormatTextCallout.java
new file mode 100644 (file)
index 0000000..6e2d75c
--- /dev/null
@@ -0,0 +1,43 @@
+package com.nwalsh.saxon;
+
+import org.xml.sax.SAXException;
+import org.w3c.dom.*;
+
+import javax.xml.transform.TransformerException;
+
+import com.icl.saxon.om.NamePool;
+import com.icl.saxon.output.Emitter;
+
+import com.nwalsh.saxon.Callout;
+
+/**
+ * <p>Utility class for the Verbatim extension (ignore this).</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000, 2001 Norman Walsh.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @see Verbatim
+ *
+ * @version $Id$
+ **/
+
+public class FormatTextCallout extends FormatCallout {
+  public FormatTextCallout(NamePool nPool, boolean fo) {
+    super(nPool, fo);
+  }
+
+  public void formatCallout(Emitter rtfEmitter,
+                           Callout callout) {
+    formatTextCallout(rtfEmitter, callout);
+  }
+}
diff --git a/xsl-java/saxon65/src/com/nwalsh/saxon/FormatUnicodeCallout.java b/xsl-java/saxon65/src/com/nwalsh/saxon/FormatUnicodeCallout.java
new file mode 100644 (file)
index 0000000..e51e1c0
--- /dev/null
@@ -0,0 +1,98 @@
+package com.nwalsh.saxon;
+
+import org.xml.sax.SAXException;
+import org.w3c.dom.*;
+
+import javax.xml.transform.TransformerException;
+
+import com.icl.saxon.om.NamePool;
+import com.icl.saxon.output.Emitter;
+import com.icl.saxon.tree.AttributeCollection;
+
+import com.nwalsh.saxon.Callout;
+
+/**
+ * <p>Utility class for the Verbatim extension (ignore this).</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000, 2001 Norman Walsh.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @see Verbatim
+ *
+ * @version $Id$
+ **/
+
+public class FormatUnicodeCallout extends FormatCallout {
+  int unicodeMax = 0;
+  int unicodeStart = 0;
+  String unicodeFont = "";
+
+  public FormatUnicodeCallout(NamePool nPool,
+                             String font,
+                             int start,
+                             int max,
+                             boolean fo) {
+    super(nPool, fo);
+    unicodeFont = font;
+    unicodeMax = max;
+    unicodeStart = start;
+  }
+
+  public void formatCallout(Emitter rtfEmitter,
+                           Callout callout) {
+    Element area = callout.getArea();
+    int num = callout.getCallout();
+    String userLabel = areaLabel(area);
+    String label = "";
+
+    if (userLabel != null) {
+      label = userLabel;
+    }
+
+    try {
+      if (userLabel == null && num <= unicodeMax) {
+       int inName = 0;
+       AttributeCollection inAttr = null;
+       int namespaces[] = new int[1];
+
+       if (!unicodeFont.equals("")) {
+         if (foStylesheet) {
+           inName = namePool.allocate("fo", foURI, "inline");
+           inAttr = new AttributeCollection(namePool);
+           inAttr.addAttribute("", "", "font-family", "CDATA", unicodeFont);
+         } else {
+           inName = namePool.allocate("", "", "font");
+           inAttr = new AttributeCollection(namePool);
+           inAttr.addAttribute("", "", "face", "CDATA", unicodeFont);
+         }
+
+         startSpan(rtfEmitter);
+         rtfEmitter.startElement(inName, inAttr, namespaces, 0);
+       }
+
+       char chars[] = new char[1];
+       chars[0] = (char) (unicodeStart + num - 1);
+       rtfEmitter.characters(chars, 0, 1);
+
+       if (!unicodeFont.equals("")) {
+         rtfEmitter.endElement(inName);
+         endSpan(rtfEmitter);
+       }
+      } else {
+       formatTextCallout(rtfEmitter, callout);
+      }
+    } catch (TransformerException e) {
+      System.out.println("Transformer Exception in graphic formatCallout");
+    }
+  }
+}
diff --git a/xsl-java/saxon65/src/com/nwalsh/saxon/ImageIntrinsics.java b/xsl-java/saxon65/src/com/nwalsh/saxon/ImageIntrinsics.java
new file mode 100644 (file)
index 0000000..9f6b9e4
--- /dev/null
@@ -0,0 +1,189 @@
+package com.nwalsh.saxon;
+
+import java.io.File;
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.net.URL;
+import java.net.MalformedURLException;
+import java.awt.Toolkit;
+import java.awt.Image;
+import java.awt.image.ImageObserver;
+import java.lang.Thread;
+import java.util.StringTokenizer;
+
+/**
+ * <p>Saxon extension to examine intrinsic size of images</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2002 Norman Walsh.</p>
+ *
+ * <p>This class provides a
+ * <a href="http://saxon.sourceforge.net/">Saxon</a>
+ * extension to find the intrinsic size of images.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @version $Id$
+ *
+ */
+public class ImageIntrinsics implements ImageObserver {
+  boolean imageLoaded = false;
+  boolean imageFailed = false;
+  Image image = null;
+  int width = -1;
+  int depth = -1;
+
+  /**
+   * <p>Constructor for ImageIntrinsics</p>
+   */
+  public ImageIntrinsics(String imageFn) {
+    System.setProperty("java.awt.headless","true");
+
+    // Hack. I expect the right way to do this is to always use a URL.
+    // However, that means getting the base URI correct and dealing
+    // with the various permutations of the file: URI scheme on different
+    // platforms. So instead, what we're going to do is cheat. If it
+    // starts with http: or ftp:, call it a URI. Otherwise, call it
+    // a file. Also call it a file if the URI is malformed.
+
+    URL imageUrl = null;
+
+    if (imageFn.startsWith("http:") || imageFn.startsWith("ftp:") || imageFn.startsWith("file:")) {
+       try {
+           imageUrl = new URL(imageFn);
+       } catch (MalformedURLException mue) {
+           imageUrl = null;
+       }
+    }
+
+    if (imageUrl != null) {
+       image = Toolkit.getDefaultToolkit().getImage (imageUrl);
+    } else {
+       image = Toolkit.getDefaultToolkit().getImage (imageFn);
+    }
+
+    width = image.getWidth(this);
+
+    while (!imageFailed && (width == -1 || depth == -1)) {
+      try {
+       java.lang.Thread.currentThread().sleep(50);
+      } catch (Exception e) {
+       // nop;
+      }
+      width = image.getWidth(this);
+      depth = image.getHeight(this);
+    }
+
+    if (imageFailed) {
+      // Maybe it's an EPS or PDF?
+      // FIXME: this code is crude (and doesn't handle the URL case!!!)
+      BufferedReader ir = null;
+      String line = null;
+      int lineLimit = 100;
+
+      try {
+       ir = new BufferedReader(new FileReader(new File(imageFn)));
+       line = ir.readLine();
+
+       if (line != null && line.startsWith("%PDF-")) {
+         // We've got a PDF!
+         while (lineLimit > 0 && line != null) {
+           lineLimit--;
+           if (line.startsWith("/CropBox [")) {
+             line = line.substring(10);
+             if (line.indexOf("]") >= 0) {
+               line = line.substring(0, line.indexOf("]"));
+             }
+             parseBox(line);
+             lineLimit = 0;
+           }
+           line = ir.readLine();
+         }
+       } else if (line != null && line.startsWith("%!") && line.indexOf(" EPSF-") > 0) {
+         // We've got an EPS!
+         while (lineLimit > 0 && line != null) {
+           lineLimit--;
+           if (line.startsWith("%%BoundingBox: ")) {
+             line = line.substring(15);
+             parseBox(line);
+             lineLimit = 0;
+           }
+           line = ir.readLine();
+         }
+       } else {
+         System.err.println("Failed to interpret image: " + imageFn);
+       }
+      } catch (Exception e) {
+       System.err.println("Failed to load image: " + imageFn);
+      }
+
+      if (ir != null) {
+       try {
+         ir.close();
+       } catch (Exception e) {
+         // nop;
+       }
+      }
+    }
+  }
+
+  public int getWidth(int defaultWidth) {
+    if (width >= 0) {
+      return width;
+    } else {
+      return defaultWidth;
+    }
+  }
+
+  public int getDepth(int defaultDepth) {
+    if (depth >= 0) {
+      return depth;
+    } else {
+      return defaultDepth;
+    }
+  }
+
+  private void parseBox(String line) {
+    int [] corners = new int [4];
+    int count = 0;
+
+    StringTokenizer st = new StringTokenizer(line);
+    while (count < 4 && st.hasMoreTokens()) {
+      try {
+       corners[count++] = Integer.parseInt(st.nextToken());
+      } catch (Exception e) {
+       // nop;
+      }
+    }
+
+    width = corners[2] - corners[0];
+    depth = corners[3] - corners[1];
+  }
+
+  public boolean imageUpdate(Image img, int infoflags,
+                            int x, int y, int width, int height) {
+    if ((infoflags & ImageObserver.ERROR) == ImageObserver.ERROR) {
+      imageFailed = true;
+      return false;
+    }
+
+    // I really only care about the width and height, but if I return false as
+    // soon as those are available, the BufferedInputStream behind the loader
+    // gets closed too early.
+    int flags = ImageObserver.ALLBITS;
+    if ((infoflags & flags) == flags) {
+      return false;
+    } else {
+      return true;
+    }
+  }
+}
diff --git a/xsl-java/saxon65/src/com/nwalsh/saxon/LineCountEmitter.java b/xsl-java/saxon65/src/com/nwalsh/saxon/LineCountEmitter.java
new file mode 100644 (file)
index 0000000..d8110de
--- /dev/null
@@ -0,0 +1,141 @@
+package com.nwalsh.saxon;
+
+import org.xml.sax.*;
+import javax.xml.transform.TransformerException;
+import com.icl.saxon.output.*;
+import com.icl.saxon.om.*;
+import com.icl.saxon.expr.FragmentValue;
+
+/**
+ * <p>Saxon extension to count the lines in a result tree fragment.</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000 Norman Walsh.</p>
+ *
+ * <p>This class provides a
+ * <a href="http://saxon.sourceforge.net/">Saxon 6.*</a>
+ * implementation to count the number of lines in a result tree
+ * fragment.</p>
+ *
+ * <p>The general design is this: the stylesheets construct a result tree
+ * fragment for some verbatim environment. That result tree fragment
+ * is "replayed" through the LineCountEmitter; the LineCountEmitter watches
+ * characters go by and counts the number of line feeds that it sees.
+ * That number is then returned.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @see Verbatim
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @version $Id$
+ *
+ */
+public class LineCountEmitter extends com.icl.saxon.output.Emitter {
+  /** The number of lines seen. */
+  protected int numLines = 0;
+
+  /** Construct a new LineCountEmitter. */
+  public LineCountEmitter() {
+    numLines = 0;
+  }
+
+  /** Reset the number of lines. */
+  public void reset() {
+    numLines = 0;
+  }
+
+  /** Return the number of lines. */
+  public int lineCount() {
+    return numLines;
+  }
+
+  /** Process characters. */
+  public void characters(char[] chars, int start, int len)
+    throws javax.xml.transform.TransformerException {
+
+    if (numLines == 0) {
+      // If there are any characters at all, there's at least one line
+      numLines++;
+    }
+
+    for (int count = start; count < start+len; count++) {
+      if (chars[count] == '\n') {
+       numLines++;
+      }
+    }
+  }
+
+  /** Discarded. */
+  public void comment(char[] chars, int start, int length)
+    throws javax.xml.transform.TransformerException {
+    // nop
+  }
+
+  /** Discarded. */
+  public void endDocument()
+    throws javax.xml.transform.TransformerException {
+    // nop
+  }
+
+  /** Discarded. */
+  public void endElement(int nameCode)
+    throws javax.xml.transform.TransformerException {
+    // nop
+  }
+
+  /** Discarded. */
+  public void processingInstruction(java.lang.String name,
+                                   java.lang.String data)
+    throws javax.xml.transform.TransformerException {
+    // nop
+  }
+
+  /** Discarded. */
+  public void setDocumentLocator(org.xml.sax.Locator locator) {
+    // nop
+  }
+
+  /** Discarded. */
+  public void setEscaping(boolean escaping)
+    throws javax.xml.transform.TransformerException {
+    // nop
+  }
+
+  /** Discarded. */
+  public void setNamePool(NamePool namePool) {
+    // nop
+  }
+
+  /** Discarded. */
+  public void setUnparsedEntity(java.lang.String name, java.lang.String uri)
+    throws javax.xml.transform.TransformerException {
+    // nop
+  }
+
+  /** Discarded. */
+  public void setWriter(java.io.Writer writer) {
+    // nop
+  }
+
+  /** Discarded. */
+  public void startDocument()
+    throws javax.xml.transform.TransformerException {
+    // nop
+  }
+
+  /** Discarded. */
+  public void startElement(int nameCode,
+                   org.xml.sax.Attributes attributes,
+                   int[] namespaces, int nscount)
+    throws javax.xml.transform.TransformerException {
+    // nop
+  }
+}
diff --git a/xsl-java/saxon65/src/com/nwalsh/saxon/NumberLinesEmitter.java b/xsl-java/saxon65/src/com/nwalsh/saxon/NumberLinesEmitter.java
new file mode 100644 (file)
index 0000000..c32efa9
--- /dev/null
@@ -0,0 +1,338 @@
+package com.nwalsh.saxon;
+
+import java.util.Stack;
+import java.util.StringTokenizer;
+import org.xml.sax.*;
+import org.w3c.dom.*;
+import javax.xml.transform.TransformerException;
+import com.icl.saxon.output.*;
+import com.icl.saxon.om.*;
+import com.icl.saxon.Controller;
+import com.icl.saxon.tree.AttributeCollection;
+import com.icl.saxon.expr.FragmentValue;
+
+/**
+ * <p>Saxon extension to decorate a result tree fragment with line numbers.</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000 Norman Walsh.</p>
+ *
+ * <p>This class provides the guts of a
+ * <a href="http://saxon.sourceforge.net/">Saxon 6.*</a>
+ * implementation of line numbering for verbatim environments. (It is used
+ * by the Verbatim class.)</p>
+ *
+ * <p>The general design is this: the stylesheets construct a result tree
+ * fragment for some verbatim environment. The Verbatim class initializes
+ * a NumberLinesEmitter with information about what lines should be
+ * numbered and how. Then the result tree fragment
+ * is "replayed" through the NumberLinesEmitter; the NumberLinesEmitter
+ * builds a
+ * new result tree fragment from this event stream, decorated with line
+ * numbers,
+ * and that is returned.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @see Verbatim
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @version $Id$
+ *
+ */
+public class NumberLinesEmitter extends CopyEmitter {
+  /** A stack for the preserving information about open elements. */
+  protected Stack elementStack = null;
+
+  /** The current line number. */
+  protected int lineNumber = 0;
+
+  /** Is the next element absolutely the first element in the fragment? */
+  protected boolean firstElement = false;
+
+  /** The FO namespace name. */
+  protected static String foURI = "http://www.w3.org/1999/XSL/Format";
+
+  /** The XHTML namespace name. */
+  protected static String xhURI = "http://www.w3.org/1999/xhtml";
+
+  /** The first line number will be <code>startinglinenumber</code>. */
+  protected int startinglinenumber = 1;
+
+  /** Every <code>modulus</code> line will be numbered. */
+  protected int modulus = 5;
+
+  /** Line numbers are <code>width</code> characters wide. */
+  protected int width = 3;
+
+  /** Line numbers are separated from the listing by <code>separator</code>. */
+  protected String separator = " ";
+
+  /** Is the stylesheet currently running an FO stylesheet? */
+  protected boolean foStylesheet = false;
+
+  /** <p>Constructor for the NumberLinesEmitter.</p>
+   *
+   * @param namePool The name pool to use for constructing elements and attributes.
+   * @param modulus The modulus to use for this listing.
+   * @param width The width to use for line numbers in this listing.
+   * @param separator The separator to use for this listing.
+   * @param foStylesheet Is this an FO stylesheet?
+   */
+  public NumberLinesEmitter(Controller controller,
+                           NamePool namePool,
+                           int startingLineNumber,
+                           int modulus,
+                           int width,
+                           String separator,
+                           boolean foStylesheet) {
+    super(controller,namePool);
+    elementStack = new Stack();
+    firstElement = true;
+
+    this.modulus = modulus;
+    this.startinglinenumber = startingLineNumber;
+    this.width = width;
+    this.separator = separator;
+    this.foStylesheet = foStylesheet;
+  }
+
+  /** Process characters. */
+  public void characters(char[] chars, int start, int len)
+    throws TransformerException {
+
+    // If we hit characters, then there's no first element...
+    firstElement = false;
+
+    if (lineNumber == 0) {
+      // The first line is always numbered
+      lineNumber = startinglinenumber;
+      formatLineNumber(lineNumber);
+    }
+
+    // Walk through the text node looking for newlines
+    char[] newChars = new char[len];
+    int pos = 0;
+    for (int count = start; count < start+len; count++) {
+      if (chars[count] == '\n') {
+       // This is the tricky bit; if we find a newline, make sure
+       // it doesn't occur inside any markup.
+
+       if (pos > 0) {
+         // Output any characters that preceded this newline
+         rtfEmitter.characters(newChars, 0, pos);
+         pos = 0;
+       }
+
+       // Close all the open elements...
+       Stack tempStack = new Stack();
+       while (!elementStack.empty()) {
+         StartElementInfo elem = (StartElementInfo) elementStack.pop();
+         rtfEmitter.endElement(elem.getNameCode());
+         tempStack.push(elem);
+       }
+
+       // Copy the newline to the output
+       newChars[pos++] = chars[count];
+       rtfEmitter.characters(newChars, 0, pos);
+       pos = 0;
+
+       // Add the line number
+       formatLineNumber(++lineNumber);
+
+       // Now "reopen" the elements that we closed...
+       while (!tempStack.empty()) {
+         StartElementInfo elem = (StartElementInfo) tempStack.pop();
+         AttributeCollection attr = (AttributeCollection)elem.getAttributes();
+         AttributeCollection newAttr = new AttributeCollection(namePool);
+
+         for (int acount = 0; acount < attr.getLength(); acount++) {
+           String localName = attr.getLocalName(acount);
+           int nameCode = attr.getNameCode(acount);
+           String type = attr.getType(acount);
+           String value = attr.getValue(acount);
+           String uri = attr.getURI(acount);
+           String prefix = "";
+
+           if (localName.indexOf(':') > 0) {
+             prefix = localName.substring(0, localName.indexOf(':'));
+             localName = localName.substring(localName.indexOf(':')+1);
+           }
+
+           if (uri.equals("")
+               && ((foStylesheet
+                    && localName.equals("id"))
+                   || (!foStylesheet
+                       && (localName.equals("id")
+                           || localName.equals("name"))))) {
+             // skip this attribute
+           } else {
+             newAttr.addAttribute(prefix, uri, localName, type, value);
+           }
+         }
+
+         rtfEmitter.startElement(elem.getNameCode(),
+                          newAttr,
+                          elem.getNamespaces(),
+                          elem.getNSCount());
+
+         elementStack.push(elem);
+       }
+      } else {
+       newChars[pos++] = chars[count];
+      }
+    }
+
+    if (pos > 0) {
+      rtfEmitter.characters(newChars, 0, pos);
+      pos = 0;
+    }
+  }
+
+  /**
+   * <p>Add a formatted line number to the result tree fragment.</p>
+   *
+   * @param lineNumber The number of the current line.
+   */
+  protected void formatLineNumber(int lineNumber) 
+    throws TransformerException {
+
+    char ch = 160; // &nbsp;
+
+    String lno = "";
+    if (lineNumber == 1
+       || (modulus >= 1 && (lineNumber % modulus == 0))) {
+      lno = "" + lineNumber;
+    }
+
+    while (lno.length() < width) {
+      lno = ch + lno;
+    }
+
+    lno += separator;
+
+    char chars[] = new char[lno.length()];
+    for (int count = 0; count < lno.length(); count++) {
+      chars[count] = lno.charAt(count);
+    }
+
+    characters(chars, 0, lno.length());
+  }
+
+  /** Process end element events. */
+  public void endElement(int nameCode)
+    throws TransformerException {
+    if (!elementStack.empty()) {
+      // if we didn't push the very first element (an fo:block or
+      // pre or div surrounding the whole block), then the stack will
+      // be empty when we get to the end of the first element...
+      elementStack.pop();
+    }
+    rtfEmitter.endElement(nameCode);
+  }
+
+  /** Process start element events. */
+  public void startElement(int nameCode,
+                          org.xml.sax.Attributes attributes,
+                          int[] namespaces,
+                          int nscount)
+    throws TransformerException {
+
+    if (!skipThisElement(nameCode)) {
+      StartElementInfo sei = new StartElementInfo(nameCode, attributes,
+                                                 namespaces, nscount);
+      elementStack.push(sei);
+    }
+
+    firstElement = false;
+
+    rtfEmitter.startElement(nameCode, attributes, namespaces, nscount);
+  }
+
+  /**
+   * <p>Protect the outer-most block wrapper.</p>
+   *
+   * <p>Open elements in the result tree fragment are closed and reopened
+   * around callouts (so that callouts don't appear inside links or other
+   * environments). But if the result tree fragment is a single block
+   * (a div or pre in HTML, an fo:block in FO), that outer-most block is
+   * treated specially.</p>
+   *
+   * <p>This method returns true if the element in question is that
+   * outermost block.</p>
+   *
+   * @param nameCode The name code for the element
+   *
+   * @return True if the element is the outer-most block, false otherwise.
+   */
+  protected boolean skipThisElement(int nameCode) {
+    // FIXME: This is such a gross hack...
+    if (firstElement) {
+      int thisFingerprint    = namePool.getFingerprint(nameCode);
+      int foBlockFingerprint = namePool.getFingerprint(foURI, "block");
+      int htmlPreFingerprint = namePool.getFingerprint("", "pre");
+      int htmlDivFingerprint = namePool.getFingerprint("", "div");
+      int xhtmlPreFingerprint = namePool.getFingerprint(xhURI, "pre");
+      int xhtmlDivFingerprint = namePool.getFingerprint(xhURI, "div");
+
+      if ((foStylesheet && thisFingerprint == foBlockFingerprint)
+         || (!foStylesheet && (thisFingerprint == htmlPreFingerprint
+                               || thisFingerprint == htmlDivFingerprint
+                               || thisFingerprint == xhtmlPreFingerprint
+                               || thisFingerprint == xhtmlDivFingerprint))) {
+       // Don't push the outer-most wrapping div, pre, or fo:block
+       return true;
+      }
+    }
+
+    return false;
+  }
+
+  /**
+   * <p>A private class for maintaining the information required to call
+   * the startElement method.</p>
+   *
+   * <p>In order to close and reopen elements, information about those
+   * elements has to be maintained. This class is just the little record
+   * that we push on the stack to keep track of that info.</p>
+   */
+  private class StartElementInfo {
+    private int _nameCode;
+    org.xml.sax.Attributes _attributes;
+    int[] _namespaces;
+    int _nscount;
+
+    public StartElementInfo(int nameCode,
+                           org.xml.sax.Attributes attributes,
+                           int[] namespaces,
+                           int nscount) {
+      _nameCode = nameCode;
+      _attributes = attributes;
+      _namespaces = namespaces;
+      _nscount = nscount;
+    }
+
+    public int getNameCode() {
+      return _nameCode;
+    }
+
+    public org.xml.sax.Attributes getAttributes() {
+      return _attributes;
+    }
+
+    public int[] getNamespaces() {
+      return _namespaces;
+    }
+
+    public int getNSCount() {
+      return _nscount;
+    }
+  }
+}
diff --git a/xsl-java/saxon65/src/com/nwalsh/saxon/Table.java b/xsl-java/saxon65/src/com/nwalsh/saxon/Table.java
new file mode 100644 (file)
index 0000000..ef23b04
--- /dev/null
@@ -0,0 +1,477 @@
+// Verbatim.java - Saxon extensions supporting DocBook verbatim environments
+
+package com.nwalsh.saxon;
+
+import java.util.Hashtable;
+import org.xml.sax.*;
+import org.w3c.dom.*;
+import javax.xml.transform.TransformerException;
+import com.icl.saxon.Controller;
+import com.icl.saxon.expr.*;
+import com.icl.saxon.om.*;
+import com.icl.saxon.pattern.*;
+import com.icl.saxon.Context;
+import com.icl.saxon.tree.*;
+import com.icl.saxon.functions.Extensions;
+
+/**
+ * <p>Saxon extensions supporting Tables</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000 Norman Walsh.</p>
+ *
+ * <p>This class provides a
+ * <a href="http://saxon.sourceforge.net/">Saxon</a>
+ * implementation of some code to adjust CALS Tables to HTML
+ * Tables.</p>
+ *
+ * <p><b>Column Widths</b></p>
+ * <p>The <tt>adjustColumnWidths</tt> method takes a result tree
+ * fragment (assumed to contain the colgroup of an HTML Table)
+ * and returns the result tree fragment with the column widths
+ * adjusted to HTML terms.</p>
+ *
+ * <p><b>Convert Lengths</b></p>
+ * <p>The <tt>convertLength</tt> method takes a length specification
+ * of the form 9999.99xx (where "xx" is a unit) and returns that length
+ * as an integral number of pixels. For convenience, percentage lengths
+ * are returned unchanged.</p>
+ * <p>The recognized units are: inches (in), centimeters (cm),
+ * millimeters (mm), picas (pc, 1pc=12pt), points (pt), and pixels (px).
+ * A number with no units is assumed to be pixels.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @version $Id$
+ *
+ */
+public class Table {
+  /** The number of pixels per inch */
+  private static int pixelsPerInch = 96;
+
+  /** The nominal table width (6in by default). */
+  private static int nominalWidth = 6 * pixelsPerInch;
+
+  /** The default table width (100% by default). */
+  private static String tableWidth = "100%";
+
+  /** Is this an FO stylesheet? */
+  private static boolean foStylesheet = false;
+
+  /** The hash used to associate units with a length in pixels. */
+  protected static Hashtable unitHash = null;
+
+  /**
+   * <p>Constructor for Verbatim</p>
+   *
+   * <p>All of the methods are static, so the constructor does nothing.</p>
+   */
+  public Table() {
+  }
+
+  /** Initialize the internal hash table with proper values. */
+  protected static void initializeHash() {
+    unitHash = new Hashtable();
+    unitHash.put("in", new Float(pixelsPerInch));
+    unitHash.put("cm", new Float(pixelsPerInch / 2.54));
+    unitHash.put("mm", new Float(pixelsPerInch / 25.4));
+    unitHash.put("pc", new Float((pixelsPerInch / 72) * 12));
+    unitHash.put("pt", new Float(pixelsPerInch / 72));
+    unitHash.put("px", new Float(1));
+  }
+
+  /** Set the pixels-per-inch value. Only positive values are legal. */
+  public static void setPixelsPerInch(int value) {
+    if (value > 0) {
+      pixelsPerInch = value;
+      initializeHash();
+    }
+  }
+
+  /** Return the current pixels-per-inch value. */
+  public int getPixelsPerInch() {
+    return pixelsPerInch;
+  }
+
+  /**
+   * <p>Convert a length specification to a number of pixels.</p>
+   *
+   * <p>The specified length should be of the form [+/-]999.99xx,
+   * where xx is a valid unit.</p>
+   */
+  public static int convertLength(String length) {
+    // The format of length should be 999.999xx
+    int sign = 1;
+    String digits = "";
+    String units = "";
+    char lench[] = length.toCharArray();
+    float flength = 0;
+    boolean done = false;
+    int pos = 0;
+    float factor = 1;
+    int pixels = 0;
+
+    if (unitHash == null) {
+      initializeHash();
+    }
+
+    if (lench[pos] == '+' || lench[pos] == '-') {
+      if (lench[pos] == '-') {
+       sign = -1;
+      }
+      pos++;
+    }
+
+    while (!done) {
+      if (pos >= lench.length) {
+       done = true;
+      } else {
+       if ((lench[pos] > '9' || lench[pos] < '0') && lench[pos] != '.') {
+         done = true;
+         units = length.substring(pos);
+       } else {
+         digits += lench[pos++];
+       }
+      }
+    }
+
+    try {
+      flength = Float.parseFloat(digits);
+    } catch (NumberFormatException e) {
+      System.out.println(digits + " is not a number; 1 used instead.");
+      flength = 1;
+    }
+
+    Float f = null;
+
+    if (!units.equals("")) {
+      f = (Float) unitHash.get(units);
+      if (f == null) {
+       System.out.println(units + " is not a known unit; 1 used instead.");
+       factor = 1;
+      } else {
+       factor = f.floatValue();
+      }
+    } else {
+      factor = 1;
+    }
+
+    f = new Float(flength * factor);
+
+    pixels = f.intValue() * sign;
+
+    return pixels;
+  }
+
+  /**
+   * <p>Find the string value of a stylesheet variable or parameter</p>
+   *
+   * <p>Returns the string value of <code>varName</code> in the current
+   * <code>context</code>. Returns the empty string if the variable is
+   * not defined.</p>
+   *
+   * @param context The current stylesheet context
+   * @param varName The name of the variable (without the dollar sign)
+   *
+   * @return The string value of the variable
+   */
+  protected static String getVariable(Context context, String varName) 
+    throws TransformerException {
+    Value variable = null;
+    String varString = null;
+
+    try {
+      variable = Extensions.evaluate(context, "$" + varName);
+      varString = variable.asString();
+      return varString;
+    } catch (IllegalArgumentException e) {
+      System.out.println("Undefined variable: " + varName);
+      return "";
+    }
+  }
+
+  /**
+   * <p>Setup the parameters associated with column width calculations</p>
+   *
+   * <p>This method queries the stylesheet for the variables
+   * associated with table column widths. It is called automatically before
+   * column widths are adjusted. The context is used to retrieve the values,
+   * this allows templates to redefine these variables.</p>
+   *
+   * <p>The following variables are queried. If the variables do not
+   * exist, builtin defaults will be used (but you may also get a bunch
+   * of messages from the Java interpreter).</p>
+   *
+   * <dl>
+   * <dt><code>nominal.table.width</code></dt>
+   * <dd>The "normal" width for tables. This must be an absolute length.</dd>
+   * <dt><code>table.width</code></dt>
+   * <dd>The width for tables. This may be either an absolute
+   * length or a percentage.</dd>
+   * </dl>
+   *
+   * @param context The current stylesheet context
+   *
+   */
+  private static void setupColumnWidths(Context context) {
+    // Hardcoded defaults
+    nominalWidth = 6 * pixelsPerInch;
+    tableWidth = "100%";
+
+    String varString = null;
+
+    try {
+      // Get the stylesheet type
+      varString = getVariable(context, "stylesheet.result.type");
+      foStylesheet = varString.equals("fo");
+
+      // Get the nominal table width
+      varString = getVariable(context, "nominal.table.width");
+      nominalWidth = convertLength(varString);
+
+      // Get the table width
+      varString = getVariable(context, "table.width");
+      tableWidth = varString;
+    } catch (TransformerException e) {
+      //nop, can't happen
+    }
+  }
+
+  /**
+   * <p>Adjust column widths in an HTML table.</p>
+   *
+   * <p>The specification of column widths in CALS (a relative width
+   * plus an optional absolute width) are incompatible with HTML column
+   * widths. This method adjusts CALS column width specifiers in an
+   * attempt to produce equivalent HTML specifiers.</p>
+   *
+   * <p>In order for this method to work, the CALS width specifications
+   * should be placed in the "width" attribute of the &lt;col>s within
+   * a &lt;colgroup>. Then the colgroup result tree fragment is passed
+   * to this method.</p>
+   *
+   * <p>This method makes use of two parameters from the XSL stylesheet
+   * that calls it: <code>nominal.table.width</code> and
+   * <code>table.width</code>. The value of <code>nominal.table.width</code>
+   * must be an absolute distance. The value of <code>table.width</code>
+   * can be either absolute or relative.</p>
+   *
+   * <p>Presented with a mixture of relative and
+   * absolute lengths, the table width is used to calculate
+   * appropriate values. If the <code>table.width</code> is relative,
+   * the nominal width is used for this calculation.</p>
+   *
+   * <p>There are three possible combinations of values:</p>
+   *
+   * <ol>
+   * <li>There are no relative widths; in this case the absolute widths
+   * are used in the HTML table.</li>
+   * <li>There are no absolute widths; in this case the relative widths
+   * are used in the HTML table.</li>
+   * <li>There are a mixture of absolute and relative widths:
+   *   <ol>
+   *     <li>If the table width is absolute, all widths become absolute.</li>
+   *     <li>If the table width is relative, make all the widths absolute
+   *         relative to the nominal table width then turn them all
+   *         back into relative widths.</li>
+   *   </ol>
+   * </li>
+   * </ol>
+   *
+   * @param context The stylesheet context; supplied automatically by Saxon
+   * @param rtf_ns The result tree fragment containing the colgroup.
+   *
+   * @return The result tree fragment containing the adjusted colgroup.
+   *
+   */
+  public static NodeSetValue adjustColumnWidths (Context context,
+                                                NodeSetValue rtf_ns) {
+
+    FragmentValue rtf = (FragmentValue) rtf_ns;
+
+    setupColumnWidths(context);
+
+    try {
+      Controller controller = context.getController();
+      NamePool namePool = controller.getNamePool();
+
+      ColumnScanEmitter csEmitter = new ColumnScanEmitter(namePool);
+      rtf.replay(csEmitter);
+
+      int numColumns = csEmitter.columnCount();
+      String widths[] = csEmitter.columnWidths();
+
+      float relTotal = 0;
+      float relParts[] = new float[numColumns];
+
+      float absTotal = 0;
+      float absParts[] = new float[numColumns];
+
+      for (int count = 0; count < numColumns; count++) {
+       String width = widths[count];
+
+       int pos = width.indexOf("*");
+       if (pos >= 0) {
+         String relPart = width.substring(0, pos);
+         String absPart = width.substring(pos+1);
+
+         try {
+           float rel = Float.parseFloat(relPart);
+           relTotal += rel;
+           relParts[count] = rel;
+         } catch (NumberFormatException e) {
+           System.out.println(relPart + " is not a valid relative unit.");
+         }
+
+         int pixels = 0;
+         if (absPart != null && !absPart.equals("")) {
+           pixels = convertLength(absPart);
+         }
+
+         absTotal += pixels;
+         absParts[count] = pixels;
+       } else {
+         relParts[count] = 0;
+
+         int pixels = 0;
+         if (width != null && !width.equals("")) {
+           pixels = convertLength(width);
+         }
+
+         absTotal += pixels;
+         absParts[count] = pixels;
+       }
+      }
+
+      // Ok, now we have the relative widths and absolute widths in
+      // two parallel arrays.
+      //
+      // - If there are no relative widths, output the absolute widths
+      // - If there are no absolute widths, output the relative widths
+      // - If there are a mixture of relative and absolute widths,
+      //   - If the table width is absolute, turn these all into absolute
+      //     widths.
+      //   - If the table width is relative, turn these all into absolute
+      //     widths in the nominalWidth and then turn them back into
+      //     percentages.
+
+      if (relTotal == 0) {
+       for (int count = 0; count < numColumns; count++) {
+         Float f = new Float(absParts[count]);
+         if (foStylesheet) {
+           int pixels = f.intValue();
+           float inches = (float) pixels / pixelsPerInch;
+           widths[count] = inches + "in";
+         } else {
+           widths[count] = Integer.toString(f.intValue());
+         }
+       }
+      } else if (absTotal == 0) {
+       for (int count = 0; count < numColumns; count++) {
+         float rel = relParts[count] / relTotal * 100;
+         Float f = new Float(rel);
+         widths[count] = Integer.toString(f.intValue());
+       }
+       widths = correctRoundingError(widths);
+      } else {
+       int pixelWidth = nominalWidth;
+
+       if (tableWidth.indexOf("%") <= 0) {
+         pixelWidth = convertLength(tableWidth);
+       }
+
+       if (pixelWidth <= absTotal) {
+         System.out.println("Table is wider than table width.");
+       } else {
+         pixelWidth -= absTotal;
+       }
+
+       absTotal = 0;
+       for (int count = 0; count < numColumns; count++) {
+         float rel = relParts[count] / relTotal * pixelWidth;
+         relParts[count] = rel + absParts[count];
+         absTotal += rel + absParts[count];
+       }
+
+       if (tableWidth.indexOf("%") <= 0) {
+         for (int count = 0; count < numColumns; count++) {
+           Float f = new Float(relParts[count]);
+           if (foStylesheet) {
+             int pixels = f.intValue();
+             float inches = (float) pixels / pixelsPerInch;
+             widths[count] = inches + "in";
+           } else {
+             widths[count] = Integer.toString(f.intValue());
+           }
+         }
+       } else {
+         for (int count = 0; count < numColumns; count++) {
+           float rel = relParts[count] / absTotal * 100;
+           Float f = new Float(rel);
+           widths[count] = Integer.toString(f.intValue());
+         }
+         widths = correctRoundingError(widths);
+       }
+      }
+
+      ColumnUpdateEmitter cuEmitter = new ColumnUpdateEmitter(controller,
+                                                             namePool,
+                                                             widths);
+
+      rtf.replay(cuEmitter);
+      return cuEmitter.getResultTreeFragment();
+    } catch (TransformerException e) {
+      // This "can't" happen.
+      System.out.println("Transformer Exception in adjustColumnWidths");
+      return rtf;
+    }
+  }
+
+  /**
+   * Correct rounding errors introduced in calculating the width of each
+   * column. Make sure they sum to 100% in the end.
+   */
+  protected static String[] correctRoundingError(String widths[]) {
+    int totalWidth = 0;
+
+    for (int count = 0; count < widths.length; count++) {
+      try {
+       int width = Integer.parseInt(widths[count]);
+       totalWidth += width;
+      } catch (NumberFormatException nfe) {
+       // nop; "can't happen"
+      }
+    }
+
+    float totalError = 100 - totalWidth;
+    float columnError = totalError / widths.length;
+    float error = 0;
+
+    for (int count = 0; count < widths.length; count++) {
+      try {
+       int width = Integer.parseInt(widths[count]);
+       error = error + columnError;
+       if (error >= 1.0) {
+         int adj = (int) Math.round(Math.floor(error));
+         error = error - (float) Math.floor(error);
+         width = width + adj;
+         widths[count] = Integer.toString(width) + "%";
+       } else {
+         widths[count] = Integer.toString(width) + "%";
+       }
+      } catch (NumberFormatException nfe) {
+       // nop; "can't happen"
+      }
+    }
+
+    return widths;
+  }
+}
diff --git a/xsl-java/saxon65/src/com/nwalsh/saxon/Text.java b/xsl-java/saxon65/src/com/nwalsh/saxon/Text.java
new file mode 100644 (file)
index 0000000..71ef568
--- /dev/null
@@ -0,0 +1,203 @@
+// Text - Saxon extension element for inserting text
+
+package com.nwalsh.saxon;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.FileNotFoundException;
+import java.net.URL;
+import java.net.MalformedURLException;
+
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.Source;
+
+import com.icl.saxon.Context;
+import com.icl.saxon.style.StyleElement;
+import com.icl.saxon.output.Outputter;
+import com.icl.saxon.expr.Expression;
+
+import org.xml.sax.AttributeList;
+
+/**
+ * <p>Saxon extension element for inserting text
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000 Norman Walsh.</p>
+ *
+ * <p>This class provides a
+ * <a href="http://saxon.sourceforge.net/">Saxon</a>
+ * extension element for inserting text into a result tree.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @version $Id$
+ *
+ */
+public class Text extends StyleElement {
+  /**
+   * <p>Constructor for Text</p>
+   *
+   * <p>Does nothing.</p>
+   */
+  public Text() {
+  }
+
+  /**
+   * <p>Is this element an instruction?</p>
+   *
+   * <p>Yes, it is.</p>
+   *
+   * @return true
+   */
+  public boolean isInstruction() {
+    return true;
+  }
+
+    /**
+    * <p>Can this element contain a template-body?</p>
+    *
+    * <p>Yes, it can, but only so that it can contain xsl:fallback.</p>
+    *
+    * @return true
+    */
+  public boolean mayContainTemplateBody() {
+    return true;
+  }
+
+  /**
+   * <p>Validate the arguments</p>
+   *
+   * <p>The element must have an href attribute.</p>
+   */
+  public void prepareAttributes() throws TransformerConfigurationException {
+    // Get mandatory href attribute
+    String fnAtt = getAttribute("href");
+    if (fnAtt == null) {
+      reportAbsence("href");
+    }
+  }
+
+  /** Validate that the element occurs in a reasonable place. */
+  public void validate() throws TransformerConfigurationException {
+    checkWithinTemplate();
+  }
+
+  /**
+   * <p>Insert the text of the file into the result tree</p>
+   *
+   * <p>Processing this element inserts the contents of the URL named
+   * by the href attribute into the result tree as plain text.</p>
+   * 
+   * <p>Optional encoding attribute can specify encoding of resource.
+   * If not specified default system encoding is used.</p>
+   *
+   */
+  public void process( Context context ) throws TransformerException {
+    Outputter out = context.getOutputter();
+
+    String hrefAtt = getAttribute("href");
+    Expression hrefExpr = makeAttributeValueTemplate(hrefAtt);
+    String href = hrefExpr.evaluateAsString(context);
+
+    String encodingAtt = getAttribute("encoding");
+    Expression encodingExpr = makeAttributeValueTemplate(encodingAtt);
+    String encoding = encodingExpr.evaluateAsString(context);
+
+    String baseURI = context.getContextNodeInfo().getBaseURI();
+
+    URIResolver resolver = context.getController().getURIResolver();
+
+    if (resolver != null) {
+      Source source = resolver.resolve(href, baseURI);
+      href = source.getSystemId();
+    }
+
+    URL baseURL = null;
+    URL fileURL = null;
+
+    try {
+      baseURL = new URL(baseURI);
+    } catch (MalformedURLException e0) {
+      // what the!?
+      baseURL = null;
+    }
+
+    try {
+      try {
+        fileURL = new URL(baseURL, href);
+      } catch (MalformedURLException e1) {
+        try {
+          fileURL = new URL(baseURL, "file:" + href);
+        } catch (MalformedURLException e2) {
+          System.out.println("Cannot open " + href);
+          return;
+        }
+      }
+
+      InputStreamReader isr = null;
+      if (encoding.equals("") == true)
+        isr = new InputStreamReader(fileURL.openStream());
+      else
+        isr = new InputStreamReader(fileURL.openStream(), encoding);
+
+      BufferedReader is = new BufferedReader(isr);
+
+      final int BUFFER_SIZE = 4096;
+      char chars[] = new char[BUFFER_SIZE];
+      char nchars[] = new char[BUFFER_SIZE];
+      int len = 0;
+      int i = 0;
+      int carry = -1;
+
+      while ((len = is.read(chars)) > 0) {
+        // various new lines are normalized to LF to prevent blank lines
+       // between lines
+
+        int nlen = 0;
+        for (i=0; i<len; i++) {
+          // is current char CR?
+          if (chars[i] == '\r') {
+            if (i < (len - 1)) {
+              // skip it if next char is LF
+              if (chars[i+1] == '\n') continue;
+              // single CR -> LF to normalize MAC line endings
+              nchars[nlen] = '\n';
+              nlen++;
+              continue;
+            } else {
+              // if CR is last char of buffer we must look ahead
+              carry = is.read();
+              nchars[nlen] = '\n';
+              nlen++;
+              if (carry == '\n') {
+                carry = -1;
+              }
+              break;
+            }
+          }
+          nchars[nlen] = chars[i];
+          nlen++;
+        }
+        out.writeContent(nchars, 0, nlen);
+        // handle look aheaded character
+        if (carry != -1) out.writeContent(String.valueOf((char)carry));
+        carry = -1;
+      }
+      is.close();
+    } catch (Exception e) {
+      System.out.println("Cannot read " + href);
+    }
+  }
+}
diff --git a/xsl-java/saxon65/src/com/nwalsh/saxon/TextFactory.java b/xsl-java/saxon65/src/com/nwalsh/saxon/TextFactory.java
new file mode 100644 (file)
index 0000000..6dc0967
--- /dev/null
@@ -0,0 +1,67 @@
+// TextFactory - Saxon extension element factory
+
+package com.nwalsh.saxon;
+
+import com.icl.saxon.style.ExtensionElementFactory;
+import org.xml.sax.SAXException;
+
+/**
+ * <p>Saxon extension element factory
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000 Norman Walsh.</p>
+ *
+ * <p>This class provides a
+ * <a href="http://saxon.sourceforge.net/">Saxon</a>
+ * extension element factory for the Text extension element
+ * family.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @version $Id$
+ *
+ * @see Text
+ *
+ */
+public class TextFactory implements ExtensionElementFactory {
+  /**
+   * <p>Constructor for TextFactory</p>
+   *
+   * <p>Does nothing.</p>
+   */
+  public TextFactory() {
+  }
+
+  /**
+   * <p>Return the class that implements a particular extension element.</p>
+   *
+   * @param localname The local name of the extension element.
+   *
+   * @return The class that handles that extension element.
+   *
+   * @exception SAXException("Unknown Text extension element")
+   */
+  public Class getExtensionClass(String localname) {
+    if (localname.equals("insertfile")) {
+      try {
+       return Class.forName("com.nwalsh.saxon.Text");
+      } catch (ClassNotFoundException e) {
+       return null;
+      }
+    }
+    return null;
+  }
+}
+
+
+
+
+
diff --git a/xsl-java/saxon65/src/com/nwalsh/saxon/UnwrapLinks.java b/xsl-java/saxon65/src/com/nwalsh/saxon/UnwrapLinks.java
new file mode 100644 (file)
index 0000000..18b6c5e
--- /dev/null
@@ -0,0 +1,132 @@
+// UnwrapLinks.java - Saxon extension for unwrapping nested links
+
+package com.nwalsh.saxon;
+
+import java.util.Stack;
+import java.util.StringTokenizer;
+import org.xml.sax.*;
+import org.w3c.dom.*;
+import javax.xml.transform.TransformerException;
+import com.icl.saxon.Controller;
+import com.icl.saxon.expr.*;
+import com.icl.saxon.om.*;
+import com.icl.saxon.pattern.*;
+import com.icl.saxon.Context;
+import com.icl.saxon.tree.*;
+import com.icl.saxon.functions.Extensions;
+import com.nwalsh.saxon.UnwrapLinksEmitter;
+
+/**
+ * <p>Saxon extension for unwrapping nested links</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000, 2002 Norman Walsh.</p>
+ *
+ * <p>This class provides a
+ * <a href="http://saxon.sf.net/">Saxon 6.*</a>
+ * implementation of a link unwrapper.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @version $Id$
+ *
+ */
+public class UnwrapLinks {
+  /** True if the stylesheet is producing formatting objects */
+  private static boolean foStylesheet = false;
+
+  /**
+   * <p>Constructor for UnwrapLinks</p>
+   *
+   * <p>All of the methods are static, so the constructor does nothing.</p>
+   */
+  public UnwrapLinks() {
+  }
+
+  /**
+   * <p>Find the string value of a stylesheet variable or parameter</p>
+   *
+   * <p>Returns the string value of <code>varName</code> in the current
+   * <code>context</code>. Returns the empty string if the variable is
+   * not defined.</p>
+   *
+   * @param context The current stylesheet context
+   * @param varName The name of the variable (without the dollar sign)
+   *
+   * @return The string value of the variable
+   */
+  protected static String getVariable(Context context, String varName) {
+    Value variable = null;
+    String varString = null;
+
+    try {
+      variable = Extensions.evaluate(context, "$" + varName);
+      varString = variable.asString();
+      return varString;
+    } catch (TransformerException te) {
+      System.out.println("Undefined variable: " + varName);
+      return "";
+    } catch (IllegalArgumentException iae) {
+      System.out.println("Undefined variable: " + varName);
+      return "";
+    }
+  }
+
+  /**
+   * <p>Setup the parameters associated with unwrapping links</p>
+   *
+   * @param context The current stylesheet context
+   *
+   */
+  private static void setupUnwrapLinks(Context context) {
+    // Get the stylesheet type
+    String varString = getVariable(context, "stylesheet.result.type");
+    foStylesheet = (varString.equals("fo"));
+  }
+
+  /**
+   * <p>Unwrap links</p>
+   *
+   * @param context The current stylesheet context.
+   * @param rtf_ns The result tree fragment of the verbatim environment.
+   *
+   * @return The modified result tree fragment.
+   */
+  public static NodeSetValue unwrapLinks (Context context,
+                                         NodeSetValue rtf_ns) {
+
+    FragmentValue rtf = (FragmentValue) rtf_ns;
+    boolean tryAgain = true;
+
+    setupUnwrapLinks(context);
+
+    try {
+      Controller controller = context.getController();
+      NamePool namePool = controller.getNamePool();
+
+      while (tryAgain) {
+       UnwrapLinksEmitter ulEmitter = new UnwrapLinksEmitter(controller,
+                                                             namePool,
+                                                             foStylesheet);
+       rtf.replay(ulEmitter);
+       tryAgain = ulEmitter.tryAgain();
+       rtf = (FragmentValue) ulEmitter.getResultTreeFragment();
+      }
+
+      return rtf;
+
+    } catch (TransformerException e) {
+      // This "can't" happen.
+      System.out.println("Transformer Exception in unwrapLinks");
+      return rtf;
+    }
+  }
+}
diff --git a/xsl-java/saxon65/src/com/nwalsh/saxon/UnwrapLinksEmitter.java b/xsl-java/saxon65/src/com/nwalsh/saxon/UnwrapLinksEmitter.java
new file mode 100644 (file)
index 0000000..5828072
--- /dev/null
@@ -0,0 +1,233 @@
+package com.nwalsh.saxon;
+
+import java.util.Stack;
+import org.xml.sax.*;
+import org.w3c.dom.*;
+import javax.xml.transform.TransformerException;
+import com.icl.saxon.output.*;
+import com.icl.saxon.om.*;
+import com.icl.saxon.Controller;
+import com.icl.saxon.tree.AttributeCollection;
+
+/**
+ * <p>Saxon extension to unwrap links in a result tree fragment.</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000, 2002 Norman Walsh.</p>
+ *
+ * <p>This class provides the guts of a
+ * <a href="http://saxon.sf.net/">Saxon 6.*</a>
+ * implementation of a link unwrapper.</p>
+ *
+ * <p>The general design is this: the stylesheets construct a result tree
+ * fragment for some environment. Then the result tree fragment
+ * is "replayed" through the UnwrapLinksEmitter; the UnwrapLinksEmitter
+ * builds a
+ * new result tree fragment from this event stream with top-level links unwrapped.
+ * That RTF is returned. Note that only a <i>single</i> level of unwrapping
+ * is performed. This is clearly a crude implementation.
+ * </p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @version $Id$
+ *
+ */
+public class UnwrapLinksEmitter extends CopyEmitter {
+  /** A stack for the preserving information about open elements. */
+  protected Stack elementStack = null;
+  protected Stack saveStack = null;
+
+  /** The FO namespace name. */
+  protected static String foURI = "http://www.w3.org/1999/XSL/Format";
+
+  /** The XHTML namespace name. */
+  protected static String xhURI = "http://www.w3.org/1999/xhtml";
+
+  /** Is the stylesheet currently running an FO stylesheet? */
+  protected boolean foStylesheet = false;
+
+  /** Are we currently in a link? How deep? */
+  protected int linkDepth = 0;
+  protected int skipDepth = 0;
+
+  protected int htmlAFingerprint  = 0;
+  protected int xhtmlAFingerprint = 0;
+  protected boolean inSkip = false;
+  protected boolean tryAgain = false;
+
+
+  /** <p>Constructor for the UnwrapLinksEmitter.</p>
+   *
+   * @param namePool The name pool to use for constructing elements and attributes.
+   * @param foStylesheet Is this an FO stylesheet?
+   */
+  public UnwrapLinksEmitter(Controller controller,
+                           NamePool namePool,
+                           boolean foStylesheet) {
+    super(controller,namePool);
+    elementStack = new Stack();
+    this.foStylesheet = foStylesheet;
+
+    htmlAFingerprint  = namePool.getFingerprint("", "a");
+    xhtmlAFingerprint = namePool.getFingerprint(xhURI, "a");
+  }
+
+  /** Process start element events. */
+  public void startElement(int nameCode,
+                          org.xml.sax.Attributes attributes,
+                          int[] namespaces,
+                          int nscount)
+    throws TransformerException {
+
+    int thisFingerprint = namePool.getFingerprint(nameCode);
+    boolean isLink = (thisFingerprint == htmlAFingerprint
+                     || thisFingerprint == xhtmlAFingerprint);
+
+    if (isLink) {
+      linkDepth++;
+      tryAgain = tryAgain || inSkip;
+    }
+
+    if (isLink && linkDepth > 1 && !inSkip) {
+      inSkip = true;
+
+      // Close all the open elements
+      saveStack = new Stack();
+      Stack tempStack = new Stack();
+      while (!elementStack.empty()) {
+       StartElementInfo elem = (StartElementInfo) elementStack.pop();
+       rtfEmitter.endElement(elem.getNameCode());
+       saveStack.push(elem);
+       tempStack.push(elem);
+      }
+
+      while (!tempStack.empty()) {
+       StartElementInfo elem = (StartElementInfo) tempStack.pop();
+       elementStack.push(elem);
+      }
+    }
+
+    if (inSkip) {
+      skipDepth++;
+    } else {
+    }
+
+    rtfEmitter.startElement(nameCode,attributes,namespaces,nscount);
+
+    StartElementInfo sei = new StartElementInfo(nameCode, attributes,
+                                               namespaces, nscount);
+    elementStack.push(sei);
+  }
+
+  /** Process end element events. */
+  public void endElement(int nameCode) throws TransformerException {
+    int thisFingerprint   = namePool.getFingerprint(nameCode);
+    boolean isLink = (thisFingerprint == htmlAFingerprint
+                     || thisFingerprint == xhtmlAFingerprint);
+
+    rtfEmitter.endElement(nameCode);
+    elementStack.pop();
+
+    if (isLink) {
+      linkDepth--;
+    }
+
+    if (inSkip) {
+      skipDepth--;
+      inSkip = (skipDepth > 0);
+      if (!inSkip) {
+       // Reopen all the ones we closed before...
+       while (!saveStack.empty()) {
+         StartElementInfo elem = (StartElementInfo) saveStack.pop();
+
+         AttributeCollection attr = (AttributeCollection)elem.getAttributes();
+         AttributeCollection newAttr = new AttributeCollection(namePool);
+
+         for (int acount = 0; acount < attr.getLength(); acount++) {
+           String localName = attr.getLocalName(acount);
+           String type = attr.getType(acount);
+           String value = attr.getValue(acount);
+           String uri = attr.getURI(acount);
+           String prefix = "";
+
+           if (localName.indexOf(':') > 0) {
+             prefix = localName.substring(0, localName.indexOf(':'));
+             localName = localName.substring(localName.indexOf(':')+1);
+           }
+
+           if (uri.equals("")
+               && ((foStylesheet
+                    && localName.equals("id"))
+                   || (!foStylesheet
+                       && (localName.equals("id")
+                           || localName.equals("name"))))) {
+             // skip this attribute
+           } else {
+             newAttr.addAttribute(prefix, uri, localName, type, value);
+           }
+         }
+
+         rtfEmitter.startElement(elem.getNameCode(),
+                                 newAttr,
+                                 elem.getNamespaces(),
+                                 elem.getNSCount());
+       }
+      }
+    }
+  }
+
+  public boolean tryAgain()
+    throws TransformerException {
+    return tryAgain;
+  }
+
+  /**
+   * <p>A private class for maintaining the information required to call
+   * the startElement method.</p>
+   *
+   * <p>In order to close and reopen elements, information about those
+   * elements has to be maintained. This class is just the little record
+   * that we push on the stack to keep track of that info.</p>
+   */
+  private class StartElementInfo {
+    private int _nameCode;
+    org.xml.sax.Attributes _attributes;
+    int[] _namespaces;
+    int _nscount;
+
+    public StartElementInfo(int nameCode,
+                           org.xml.sax.Attributes attributes,
+                           int[] namespaces,
+                           int nscount) {
+      _nameCode = nameCode;
+      _attributes = attributes;
+      _namespaces = namespaces;
+      _nscount = nscount;
+    }
+
+    public int getNameCode() {
+      return _nameCode;
+    }
+
+    public org.xml.sax.Attributes getAttributes() {
+      return _attributes;
+    }
+
+    public int[] getNamespaces() {
+      return _namespaces;
+    }
+
+    public int getNSCount() {
+      return _nscount;
+    }
+  }
+}
diff --git a/xsl-java/saxon65/src/com/nwalsh/saxon/Verbatim.java b/xsl-java/saxon65/src/com/nwalsh/saxon/Verbatim.java
new file mode 100644 (file)
index 0000000..2bff9a0
--- /dev/null
@@ -0,0 +1,491 @@
+// Verbatim.java - Saxon extensions supporting DocBook verbatim environments
+
+package com.nwalsh.saxon;
+
+import java.util.Stack;
+import java.util.StringTokenizer;
+import org.xml.sax.*;
+import org.w3c.dom.*;
+import javax.xml.transform.TransformerException;
+import com.icl.saxon.Controller;
+import com.icl.saxon.expr.*;
+import com.icl.saxon.om.*;
+import com.icl.saxon.pattern.*;
+import com.icl.saxon.Context;
+import com.icl.saxon.tree.*;
+import com.icl.saxon.functions.Extensions;
+import com.nwalsh.saxon.NumberLinesEmitter;
+import com.nwalsh.saxon.CalloutEmitter;
+
+/**
+ * <p>Saxon extensions supporting DocBook verbatim environments</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000 Norman Walsh.</p>
+ *
+ * <p>This class provides a
+ * <a href="http://saxon.sourceforge.net/">Saxon</a>
+ * implementation of two features that would be impractical to
+ * implement directly in XSLT: line numbering and callouts.</p>
+ *
+ * <p><b>Line Numbering</b></p>
+ * <p>The <tt>numberLines</tt> method takes a result tree
+ * fragment (assumed to contain the contents of a formatted verbatim
+ * element in DocBook: programlisting, screen, address, literallayout,
+ * or synopsis) and returns a result tree fragment decorated with
+ * line numbers.</p>
+ *
+ * <p><b>Callouts</b></p>
+ * <p>The <tt>insertCallouts</tt> method takes an
+ * <tt>areaspec</tt> and a result tree fragment
+ * (assumed to contain the contents of a formatted verbatim
+ * element in DocBook: programlisting, screen, address, literallayout,
+ * or synopsis) and returns a result tree fragment decorated with
+ * callouts.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @version $Id$
+ *
+ */
+public class Verbatim {
+  /** True if the stylesheet is producing formatting objects */
+  private static boolean foStylesheet = false;
+  /** The modulus for line numbering (every 'modulus' line is numbered). */
+  private static int modulus = 0;
+  /** The width (in characters) of line numbers (for padding). */
+  private static int width = 0;
+  /** The starting line number. */
+  private static int startinglinenumber = 1;
+  /** The separator between the line number and the verbatim text. */
+  private static String separator = "";
+
+  /** True if callouts have been setup */
+  private static boolean calloutsSetup = false;
+  /** The default column for callouts that have only a line or line range */
+  private static int defaultColumn = 60;
+  /** The path to use for graphical callout decorations. */
+  private static String graphicsPath = null;
+  /** The extension to use for graphical callout decorations. */
+  private static String graphicsExt = null;
+  /** The largest callout number that can be represented graphically. */
+  private static int graphicsMax = 10;
+
+  /** The FormatCallout object to use for formatting callouts. */
+  private static FormatCallout fCallout = null;
+
+  /**
+   * <p>Constructor for Verbatim</p>
+   *
+   * <p>All of the methods are static, so the constructor does nothing.</p>
+   */
+  public Verbatim() {
+  }
+
+  /**
+   * <p>Find the string value of a stylesheet variable or parameter</p>
+   *
+   * <p>Returns the string value of <code>varName</code> in the current
+   * <code>context</code>. Returns the empty string if the variable is
+   * not defined.</p>
+   *
+   * @param context The current stylesheet context
+   * @param varName The name of the variable (without the dollar sign)
+   *
+   * @return The string value of the variable
+   */
+  protected static String getVariable(Context context, String varName) {
+    Value variable = null;
+    String varString = null;
+
+    try {
+      variable = Extensions.evaluate(context, "$" + varName);
+      varString = variable.asString();
+      return varString;
+    } catch (TransformerException te) {
+      System.out.println("Undefined variable: " + varName);
+      return "";
+    } catch (IllegalArgumentException iae) {
+      System.out.println("Undefined variable: " + varName);
+      return "";
+    }
+  }
+
+  /**
+   * <p>Setup the parameters associated with line numbering</p>
+   *
+   * <p>This method queries the stylesheet for the variables
+   * associated with line numbering. It is called automatically before
+   * lines are numbered. The context is used to retrieve the values,
+   * this allows templates to redefine these variables.</p>
+   *
+   * <p>The following variables are queried. If the variables do not
+   * exist, builtin defaults will be used (but you may also get a bunch
+   * of messages from the Java interpreter).</p>
+   *
+   * <dl>
+   * <dt><code>linenumbering.everyNth</code></dt>
+   * <dd>Specifies the lines that will be numbered. The first line is
+   * always numbered. (builtin default: 5).</dd>
+   * <dt><code>linenumbering.width</code></dt>
+   * <dd>Specifies the width of the numbers. If the specified width is too
+   * narrow for the largest number needed, it will automatically be made
+   * wider. (builtin default: 3).</dd>
+   * <dt><code>linenumbering.separator</code></dt>
+   * <dd>Specifies the string that separates line numbers from lines
+   * in the program listing. (builtin default: " ").</dd>
+   * <dt><code>linenumbering.startinglinenumber</code></dt>
+   * <dd>Specifies the initial line number
+   * in the program listing. (builtin default: "1").</dd>
+   * <dt><code>stylesheet.result.type</code></dt>
+   * <dd>Specifies the stylesheet result type. The value is either 'fo'
+   * (for XSL Formatting Objects) or it isn't. (builtin default: html).</dd>
+   * </dl>
+   *
+   * @param context The current stylesheet context
+   *
+   */
+  private static void setupLineNumbering(Context context) {
+    // Hardcoded defaults
+    modulus = 5;
+    width = 3;
+    startinglinenumber = 1;
+    separator = " ";
+    foStylesheet = false;
+
+    String varString = null;
+
+    // Get the modulus
+    varString = getVariable(context, "linenumbering.everyNth");
+    try {
+      modulus = Integer.parseInt(varString);
+    } catch (NumberFormatException nfe) {
+      System.out.println("$linenumbering.everyNth is not a number: " + varString);
+    }
+
+    // Get the width
+    varString = getVariable(context, "linenumbering.width");
+    try {
+      width = Integer.parseInt(varString);
+    } catch (NumberFormatException nfe) {
+      System.out.println("$linenumbering.width is not a number: " + varString);
+    }
+
+    // Get the startinglinenumber
+    varString = getVariable(context, "linenumbering.startinglinenumber");
+    try {
+      startinglinenumber = Integer.parseInt(varString);
+    } catch (NumberFormatException nfe) {
+      System.out.println("$linenumbering.startinglinenumber is not a number: " + varString);
+    }
+
+    // Get the separator
+    varString = getVariable(context, "linenumbering.separator");
+    separator = varString;
+
+    // Get the stylesheet type
+    varString = getVariable(context, "stylesheet.result.type");
+    foStylesheet = (varString.equals("fo"));
+  }
+
+  /**
+   * <p>Number lines in a verbatim environment</p>
+   *
+   * <p>The extension function expects the following variables to be
+   * available in the calling context: $linenumbering.everyNth,
+   * $linenumbering.width, $linenumbering.separator, and
+   * $stylesheet.result.type.</p>
+   *
+   * <p>This method adds line numbers to a result tree fragment. Each
+   * newline that occurs in a text node is assumed to start a new line.
+   * The first line is always numbered, every subsequent 'everyNth' line
+   * is numbered (so if everyNth=5, lines 1, 5, 10, 15, etc. will be
+   * numbered. If there are fewer than everyNth lines in the environment,
+   * every line is numbered.</p>
+   *
+   * <p>Every line number will be right justified in a string 'width'
+   * characters long. If the line number of the last line in the
+   * environment is too long to fit in the specified width, the width
+   * is automatically increased to the smallest value that can hold the
+   * number of the last line. (In other words, if you specify the value 2
+   * and attempt to enumerate the lines of an environment that is 100 lines
+   * long, the value 3 will automatically be used for every line in the
+   * environment.)</p>
+   *
+   * <p>The 'separator' string is inserted between the line
+   * number and the original program listing. Lines that aren't numbered
+   * are preceded by a 'width' blank string and the separator.</p>
+   *
+   * <p>If inline markup extends across line breaks, markup changes are
+   * required. All the open elements are closed before the line break and
+   * "reopened" afterwards. The reopened elements will have the same
+   * attributes as the originals, except that 'name' and 'id' attributes
+   * are not duplicated if the stylesheet.result.type is "html" and
+   * 'id' attributes will not be duplicated if the result type is "fo".</p>
+   *
+   * @param context The current stylesheet context.
+   * @param rtf_ns The result tree fragment of the verbatim environment.
+   *
+   * @return The modified result tree fragment.
+   */
+  public static NodeSetValue numberLines (Context context,
+                                         NodeSetValue rtf_ns) {
+
+    FragmentValue rtf = (FragmentValue) rtf_ns;
+
+    setupLineNumbering(context);
+
+    try {
+      LineCountEmitter lcEmitter = new LineCountEmitter();
+      rtf.replay(lcEmitter);
+      int numLines = lcEmitter.lineCount();
+
+      int listingModulus = numLines < modulus ? 1 : modulus;
+
+      double log10numLines = Math.log(numLines) / Math.log(10);
+
+      int listingWidth = width < log10numLines+1
+       ? (int) Math.floor(log10numLines + 1)
+       : width;
+
+      Controller controller = context.getController();
+      NamePool namePool = controller.getNamePool();
+      NumberLinesEmitter nlEmitter = new NumberLinesEmitter(controller,
+                                                           namePool,
+                                                           startinglinenumber,
+                                                           listingModulus,
+                                                           listingWidth,
+                                                           separator,
+                                                           foStylesheet);
+      rtf.replay(nlEmitter);
+      return nlEmitter.getResultTreeFragment();
+    } catch (TransformerException e) {
+      // This "can't" happen.
+      System.out.println("Transformer Exception in numberLines");
+      return rtf;
+    }
+  }
+
+  /**
+   * <p>Setup the parameters associated with callouts</p>
+   *
+   * <p>This method queries the stylesheet for the variables
+   * associated with line numbering. It is called automatically before
+   * callouts are processed. The context is used to retrieve the values,
+   * this allows templates to redefine these variables.</p>
+   *
+   * <p>The following variables are queried. If the variables do not
+   * exist, builtin defaults will be used (but you may also get a bunch
+   * of messages from the Java interpreter).</p>
+   *
+   * <dl>
+   * <dt><code>callout.graphics</code></dt>
+   * <dd>Are we using callout graphics? A value of 0 or "" is false,
+   * any other value is true. If callout graphics are not used, the
+   * parameters related to graphis are not queried.</dd>
+   * <dt><code>callout.graphics.path</code></dt>
+   * <dd>Specifies the path to callout graphics.</dd>
+   * <dt><code>callout.graphics.extension</code></dt>
+   * <dd>Specifies the extension ot use for callout graphics.</dd>
+   * <dt><code>callout.graphics.number.limit</code></dt>
+   * <dd>Identifies the largest number that can be represented as a
+   * graphic. Larger callout numbers will be represented using text.</dd>
+   * <dt><code>callout.defaultcolumn</code></dt>
+   * <dd>Specifies the default column for callout bullets that do not
+   * specify a column.</dd>
+   * <dt><code>stylesheet.result.type</code></dt>
+   * <dd>Specifies the stylesheet result type. The value is either 'fo'
+   * (for XSL Formatting Objects) or it isn't. (builtin default: html).</dd>
+   * </dl>
+   *
+   * @param context The current stylesheet context
+   *
+   */
+  private static void setupCallouts(Context context) {
+    NamePool namePool = context.getController().getNamePool();
+
+    boolean useGraphics = false;
+    boolean useUnicode = false;
+
+    int unicodeStart = 49;
+    int unicodeMax = 0;
+
+    String unicodeFont = "";
+
+    // Hardcoded defaults
+    defaultColumn = 60;
+    graphicsPath = null;
+    graphicsExt = null;
+    graphicsMax = 0;
+    foStylesheet = false;
+    calloutsSetup = true;
+
+    Value variable = null;
+    String varString = null;
+
+    // Get the stylesheet type
+    varString = getVariable(context, "stylesheet.result.type");
+    foStylesheet = (varString.equals("fo"));
+
+    // Get the default column
+    varString = getVariable(context, "callout.defaultcolumn");
+    try {
+      defaultColumn = Integer.parseInt(varString);
+    } catch (NumberFormatException nfe) {
+      System.out.println("$callout.defaultcolumn is not a number: "
+                        + varString);
+    }
+
+    // Use graphics at all?
+    varString = getVariable(context, "callout.graphics");
+    useGraphics = !(varString.equals("0") || varString.equals(""));
+
+    // Use unicode at all?
+    varString = getVariable(context, "callout.unicode");
+    useUnicode = !(varString.equals("0") || varString.equals(""));
+
+    if (useGraphics) {
+      // Get the graphics path
+      varString = getVariable(context, "callout.graphics.path");
+      graphicsPath = varString;
+
+      // Get the graphics extension
+      varString = getVariable(context, "callout.graphics.extension");
+      graphicsExt = varString;
+
+      // Get the number limit
+      varString = getVariable(context, "callout.graphics.number.limit");
+      try {
+       graphicsMax = Integer.parseInt(varString);
+      } catch (NumberFormatException nfe) {
+       System.out.println("$callout.graphics.number.limit is not a number: "
+                          + varString);
+       graphicsMax = 0;
+      }
+
+      fCallout = new FormatGraphicCallout(namePool,
+                                         graphicsPath,
+                                         graphicsExt,
+                                         graphicsMax,
+                                         foStylesheet);
+    } else if (useUnicode) {
+      // Get the starting character
+      varString = getVariable(context, "callout.unicode.start.character");
+      try {
+       unicodeStart = Integer.parseInt(varString);
+      } catch (NumberFormatException nfe) {
+       System.out.println("$callout.unicode.start.character is not a number: "
+                          + varString);
+       unicodeStart = 48;
+      }
+
+      // Get the number limit
+      varString = getVariable(context, "callout.unicode.number.limit");
+      try {
+       unicodeMax = Integer.parseInt(varString);
+      } catch (NumberFormatException nfe) {
+       System.out.println("$callout.unicode.number.limit is not a number: "
+                          + varString);
+       unicodeStart = 0;
+      }
+
+      // Get the font
+      unicodeFont = getVariable(context, "callout.unicode.font");
+      if (unicodeFont == null) {
+       unicodeFont = "";
+      }
+
+      fCallout = new FormatUnicodeCallout(namePool,
+                                         unicodeFont,
+                                         unicodeStart,
+                                         unicodeMax,
+                                         foStylesheet);
+    } else {
+      fCallout = new FormatTextCallout(namePool, foStylesheet);
+    }
+  }
+
+  /**
+   * <p>Insert text callouts into a verbatim environment.</p>
+   *
+   * <p>This method examines the <tt>areaset</tt> and <tt>area</tt> elements
+   * in the supplied <tt>areaspec</tt> and decorates the supplied
+   * result tree fragment with appropriate callout markers.</p>
+   *
+   * <p>If a <tt>label</tt> attribute is supplied on an <tt>area</tt>,
+   * its content will be used for the label, otherwise the callout
+   * number will be used, surrounded by parenthesis. Callout numbers may
+   * also be represented as graphics. Callouts are
+   * numbered in document order. All of the <tt>area</tt>s in an
+   * <tt>areaset</tt> get the same number.</p>
+   *
+   * <p>Only the <tt>linecolumn</tt> and <tt>linerange</tt> units are
+   * supported. If no unit is specifed, <tt>linecolumn</tt> is assumed.
+   * If only a line is specified, the callout decoration appears in
+   * the defaultColumn. Lines will be padded with blanks to reach the
+   * necessary column, but callouts that are located beyond the last
+   * line of the verbatim environment will be ignored.</p>
+   *
+   * <p>Callouts are inserted before the character at the line/column
+   * where they are to occur.</p>
+   *
+   * <p>If graphical callouts are used, and the callout number is less
+   * than or equal to the $callout.graphics.number.limit, the following image
+   * will be generated for HTML:
+   *
+   * <pre>
+   * &lt;img src="$callout.graphics.path/999$callout.graphics.ext"
+   *         alt="conumber">
+   * </pre>
+   *
+   * If the $stylesheet.result.type is 'fo', the following image will
+   * be generated:
+   *
+   * <pre>
+   * &lt;fo:external-graphic src="$callout.graphics.path/999$callout.graphics.ext"/>
+   * </pre>
+   *
+   * <p>If the callout number exceeds $callout.graphics.number.limit,
+   * the callout will be the callout number surrounded by
+   * parenthesis.</p>
+   *
+   * @param context The stylesheet context.
+   * @param areaspecNodeList The source node set that contains the areaspec.
+   * @param rtf_ns The result tree fragment of the verbatim environment.
+   *
+   * @return The modified result tree fragment.
+   */
+  public static NodeSetValue insertCallouts (Context context,
+                                            NodeList areaspecNodeList,
+                                            NodeSetValue rtf_ns) {
+
+    FragmentValue rtf = (FragmentValue) rtf_ns;
+
+    setupCallouts(context);
+
+    try {
+      Controller controller = context.getController();
+      NamePool namePool = controller.getNamePool();
+      CalloutEmitter cEmitter = new CalloutEmitter(controller,
+                                                  namePool,
+                                                  defaultColumn,
+                                                  foStylesheet,
+                                                  fCallout);
+      cEmitter.setupCallouts(areaspecNodeList);
+      rtf.replay(cEmitter);
+      return cEmitter.getResultTreeFragment();
+    } catch (TransformerException e) {
+      // This "can't" happen.
+      System.out.println("Transformer Exception in insertCallouts");
+      return rtf;
+    }
+  }
+}
diff --git a/xsl-java/saxon65/src/com/nwalsh/saxon/Windows1252.java b/xsl-java/saxon65/src/com/nwalsh/saxon/Windows1252.java
new file mode 100644 (file)
index 0000000..23c4373
--- /dev/null
@@ -0,0 +1,110 @@
+/* This file was kindly provided by Sectra AB, Sweden to DocBook community */
+package com.nwalsh.saxon;
+
+import com.icl.saxon.charcode.PluggableCharacterSet;
+
+/**
+ *
+ * $Id$
+ *
+ * File:      Windows1252CharacterSet.java
+ * Created:   May 26 2004
+ * Author:    Pontus Haglund
+ * Project:   Venus
+ *
+ * This class extends Saxon 6.5.x with the windows-1252 character set.
+ *
+ * It is particularly useful when  generating HTML Help for
+ * Western European Languages.
+ *
+ * To use this class for generating HTML Help output with the
+ * DocBook XSL stylesheets, complete the following steps;
+ *              
+ * 1. Make sure that the Saxon 6.5.x jar file and the jar file for
+ *    the DocBook XSL Java extensions are in your CLASSPATH
+ *
+ * 2. Create a DocBook XSL customization layer -- a file named
+ *    "mystylesheet.xsl" or whatever -- that, at a minimum,
+ *    contains the following:
+ * 
+ *      <xsl:stylesheet
+ *        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ *        version='1.0'>
+ *        <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/htmlhelp/htmlhelp.xsl"/>
+ *        <xsl:output method="html" encoding="WINDOWS-1252" indent="no"/>
+ *        <xsl:param name="htmlhelp.encoding" select="'WINDOWS-1252'"></xsl:param>
+ *        <xsl:param name="chunker.output.encoding" select="'WINDOWS-1252'"></xsl:param>
+ *        <xsl:param name="saxon.character.representation" select="'native'"></xsl:param>
+ *      </xsl:stylesheet>
+ *
+ * 3. Invoke Saxon with the "encoding.windows-1252" Java system
+ *    property set to "com.nwalsh.saxon.Windows1252"; for example:
+ *
+ *      java \
+ *        -Dencoding.windows-1252=com.nwalsh.saxon.Windows1252 \
+ *      com.icl.saxon.StyleSheet \
+ *      mydoc.xml mystylesheet.xsl
+ *
+ *    Or, for a more complete "real world" case showing other
+ *    options you'll typically want to use:
+ *
+ *      java \
+ *        -Dencoding.windows-1252=com.nwalsh.saxon.Windows1252 \
+ *        -Djavax.xml.parsers.DocumentBuilderFactory=org.apache.xerces.jaxp.DocumentBuilderFactoryImpl \
+ *        -Djavax.xml.parsers.SAXParserFactory=org.apache.xerces.jaxp.SAXParserFactoryImpl \
+ *        -Djavax.xml.transform.TransformerFactory=com.icl.saxon.TransformerFactoryImpl \
+ *      com.icl.saxon.StyleSheet \
+ *        -x org.apache.xml.resolver.tools.ResolvingXMLReader \
+ *        -y org.apache.xml.resolver.tools.ResolvingXMLReader \
+ *        -r org.apache.xml.resolver.tools.CatalogResolver \
+ *      mydoc.xml mystylesheet.xsl
+ *
+ *   In both cases, the "mystylesheet.xsl" file should be a DocBook
+ *   customization layer containing the parameters show in step 2.
+ *
+ */
+
+
+
+public class Windows1252 implements PluggableCharacterSet {
+
+    public final boolean inCharset(int c) {
+
+    return  (c >= 0x00 && c <= 0x7F) ||
+            (c >= 0xA0 && c <= 0xFF) ||
+            (c == 0x20AC) ||
+            (c == 0x201A) ||
+            (c == 0x0192) ||
+            (c == 0x201E) ||
+            (c == 0x2026) ||
+            (c == 0x2020) ||
+            (c == 0x2021) ||
+            (c == 0x02C6) ||
+            (c == 0x2030) ||
+            (c == 0x0160) ||
+            (c == 0x2039) ||
+            (c == 0x0152) ||
+            (c == 0x017D) ||
+            (c == 0x2018) ||
+            (c == 0x2019) ||
+            (c == 0x201C) ||
+            (c == 0x201D) ||
+            (c == 0x2022) ||
+            (c == 0x2013) ||
+            (c == 0x2014) ||
+            (c == 0x02DC) ||
+            (c == 0x2122) ||
+            (c == 0x0161) ||
+            (c == 0x203A) ||
+            (c == 0x0153) ||
+            (c == 0x017E) ||
+            (c == 0x0178);
+
+
+    }
+
+    public String getEncodingName() {
+        return "WINDOWS-1252";
+    }
+
+}
diff --git a/xsl-java/saxon65/src/com/nwalsh/saxon/package.html b/xsl-java/saxon65/src/com/nwalsh/saxon/package.html
new file mode 100644 (file)
index 0000000..b05a467
--- /dev/null
@@ -0,0 +1,48 @@
+<html>
+<head>
+<title>Norman Walsh's Saxon Extensions Package</title>
+</head>
+<body>
+<p>Norman Walsh's Saxon Extensions Package for Saxon 6.*</p>
+
+<p>This package implements Saxon extensions for XSLT.</p>
+
+<p><b>Copyright (C) 2000 Norman Walsh</b></p>
+<p>Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use, copy,
+modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:</p>
+
+<p>The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.</p>
+
+<p>Except as contained in this notice, the names of individuals
+credited with contribution to this software shall not be used in
+advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the
+individuals in question.</p>
+
+<p>Anything derived from this Software that is publically
+distributed will be identified with a different name and the
+version strings in any derived Software will be changed so that no
+possibility of confusion between the derived package and this
+Software will exist.</p>
+</blockquote>
+
+<blockquote>
+<p><b>Warranty</b></p>
+<p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT.  IN NO EVENT SHALL NORMAN WALSH OR ANY OTHER
+CONTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.</p>
+</blockquote>
+
+</body>
+</html>
diff --git a/xsl-java/xalan27/build.xml b/xsl-java/xalan27/build.xml
new file mode 100644 (file)
index 0000000..c39733b
--- /dev/null
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- You may freely edit this file. See commented blocks below for -->
+<!-- some examples of how to customize the build. -->
+<!-- (If you delete it and reopen the project it will be recreated.) -->
+<project name="xalan27" default="default" basedir=".">
+    <description>Builds, tests, and runs the project xalan27.</description>
+    <import file="nbproject/build-impl.xml"/>
+    <!--
+
+    There exist several targets which are by default empty and which can be 
+    used for execution of your tasks. These targets are usually executed 
+    before and after some main targets. They are: 
+
+      -pre-init:                 called before initialization of project properties
+      -post-init:                called after initialization of project properties
+      -pre-compile:              called before javac compilation
+      -post-compile:             called after javac compilation
+      -pre-compile-single:       called before javac compilation of single file
+      -post-compile-single:      called after javac compilation of single file
+      -pre-compile-test:         called before javac compilation of JUnit tests
+      -post-compile-test:        called after javac compilation of JUnit tests
+      -pre-compile-test-single:  called before javac compilation of single JUnit test
+      -post-compile-test-single: called after javac compilation of single JUunit test
+      -pre-jar:                  called before JAR building
+      -post-jar:                 called after JAR building
+      -post-clean:               called after cleaning build products
+
+    (Targets beginning with '-' are not intended to be called on their own.)
+
+    Example of inserting an obfuscator after compilation could look like this:
+
+        <target name="-post-compile">
+            <obfuscate>
+                <fileset dir="${build.classes.dir}"/>
+            </obfuscate>
+        </target>
+
+    For list of available properties check the imported 
+    nbproject/build-impl.xml file. 
+
+
+    Another way to customize the build is by overriding existing main targets.
+    The targets of interest are: 
+
+      -init-macrodef-javac:     defines macro for javac compilation
+      -init-macrodef-junit:     defines macro for junit execution
+      -init-macrodef-debug:     defines macro for class debugging
+      -init-macrodef-java:      defines macro for class execution
+      -do-jar-with-manifest:    JAR building (if you are using a manifest)
+      -do-jar-without-manifest: JAR building (if you are not using a manifest)
+      run:                      execution of project 
+      -javadoc-build:           Javadoc generation
+      test-report:              JUnit report generation
+
+    An example of overriding the target for project execution could look like this:
+
+        <target name="run" depends="xalan27-impl.jar">
+            <exec dir="bin" executable="launcher.exe">
+                <arg file="${dist.jar}"/>
+            </exec>
+        </target>
+
+    Notice that the overridden target depends on the jar target and not only on 
+    the compile target as the regular run target does. Again, for a list of available 
+    properties which you can use, check the target you are overriding in the
+    nbproject/build-impl.xml file. 
+
+    -->
+</project>
diff --git a/xsl-java/xalan27/nbproject/.cvsignore b/xsl-java/xalan27/nbproject/.cvsignore
new file mode 100644 (file)
index 0000000..3e18ebf
--- /dev/null
@@ -0,0 +1 @@
+private
diff --git a/xsl-java/xalan27/nbproject/build-impl.xml b/xsl-java/xalan27/nbproject/build-impl.xml
new file mode 100644 (file)
index 0000000..fa2628e
--- /dev/null
@@ -0,0 +1,541 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+*** GENERATED FROM project.xml - DO NOT EDIT  ***
+***         EDIT ../build.xml INSTEAD         ***
+
+For the purpose of easier reading the script
+is divided into following sections:
+
+  - initialization
+  - compilation
+  - jar
+  - execution
+  - debugging
+  - javadoc
+  - junit compilation
+  - junit execution
+  - junit debugging
+  - applet
+  - cleanup
+
+-->
+<project name="xalan27-impl" default="default" basedir=".." xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:j2seproject2="http://www.netbeans.org/ns/j2se-project/2" xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1">
+    <target name="default" depends="test,jar,javadoc" description="Build and test whole project."/>
+    <!-- 
+    ======================
+    INITIALIZATION SECTION 
+    ======================
+    -->
+    <target name="-pre-init">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="-init-private" depends="-pre-init">
+        <property file="nbproject/private/private.properties"/>
+    </target>
+    <target name="-init-user" depends="-pre-init,-init-private">
+        <property file="${user.properties.file}"/>
+        <!-- The two properties below are usually overridden -->
+        <!-- by the active platform. Just a fallback. -->
+        <property name="default.javac.source" value="1.4"/>
+        <property name="default.javac.target" value="1.4"/>
+    </target>
+    <target name="-init-project" depends="-pre-init,-init-private,-init-user">
+        <property file="nbproject/project.properties"/>
+    </target>
+    <target name="-do-init" depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property">
+        <available file="${manifest.file}" property="manifest.available"/>
+        <condition property="manifest.available+main.class">
+            <and>
+                <isset property="manifest.available"/>
+                <isset property="main.class"/>
+                <not>
+                    <equals arg1="${main.class}" arg2="" trim="true"/>
+                </not>
+            </and>
+        </condition>
+        <condition property="manifest.available+main.class+mkdist.available">
+            <and>
+                <istrue value="${manifest.available+main.class}"/>
+                <isset property="libs.CopyLibs.classpath"/>
+            </and>
+        </condition>
+        <condition property="have.tests">
+            <or>
+                <available file="${test.src.dir}"/>
+            </or>
+        </condition>
+        <condition property="have.sources">
+            <or>
+                <available file="${src.dir}"/>
+            </or>
+        </condition>
+        <condition property="netbeans.home+have.tests">
+            <and>
+                <isset property="netbeans.home"/>
+                <isset property="have.tests"/>
+            </and>
+        </condition>
+        <condition property="no.javadoc.preview">
+            <isfalse value="${javadoc.preview}"/>
+        </condition>
+        <property name="run.jvmargs" value=""/>
+        <property name="javac.compilerargs" value=""/>
+        <property name="work.dir" value="${basedir}"/>
+        <condition property="no.deps">
+            <and>
+                <istrue value="${no.dependencies}"/>
+            </and>
+        </condition>
+        <property name="javac.debug" value="true"/>
+        <property name="javadoc.preview" value="true"/>
+    </target>
+    <target name="-post-init">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="-init-check" depends="-pre-init,-init-private,-init-user,-init-project,-do-init">
+        <fail unless="src.dir">Must set src.dir</fail>
+        <fail unless="test.src.dir">Must set test.src.dir</fail>
+        <fail unless="build.dir">Must set build.dir</fail>
+        <fail unless="dist.dir">Must set dist.dir</fail>
+        <fail unless="build.classes.dir">Must set build.classes.dir</fail>
+        <fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
+        <fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail>
+        <fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
+        <fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
+        <fail unless="dist.jar">Must set dist.jar</fail>
+    </target>
+    <target name="-init-macrodef-property">
+        <macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1">
+            <attribute name="name"/>
+            <attribute name="value"/>
+            <sequential>
+                <property name="@{name}" value="${@{value}}"/>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-macrodef-javac">
+        <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute name="srcdir" default="${src.dir}"/>
+            <attribute name="destdir" default="${build.classes.dir}"/>
+            <attribute name="classpath" default="${javac.classpath}"/>
+            <attribute name="debug" default="${javac.debug}"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <javac srcdir="@{srcdir}" destdir="@{destdir}" debug="@{debug}" deprecation="${javac.deprecation}" source="${javac.source}" target="${javac.target}" includeantruntime="false">
+                    <classpath>
+                        <path path="@{classpath}"/>
+                    </classpath>
+                    <compilerarg line="${javac.compilerargs}"/>
+                    <customize/>
+                </javac>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-macrodef-junit">
+        <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute name="includes" default="**/*Test.java"/>
+            <sequential>
+                <junit showoutput="true" fork="true" dir="${basedir}" failureproperty="tests.failed" errorproperty="tests.failed">
+                    <batchtest todir="${build.test.results.dir}">
+                        <fileset dir="${test.src.dir}" includes="@{includes}"/>
+                    </batchtest>
+                    <classpath>
+                        <path path="${run.test.classpath}"/>
+                    </classpath>
+                    <syspropertyset>
+                        <propertyref prefix="test-sys-prop."/>
+                        <mapper type="glob" from="test-sys-prop.*" to="*"/>
+                    </syspropertyset>
+                    <formatter type="brief" usefile="false"/>
+                    <formatter type="xml"/>
+                    <jvmarg line="${run.jvmargs}"/>
+                </junit>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-macrodef-nbjpda">
+        <macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
+            <attribute name="name" default="${main.class}"/>
+            <attribute name="classpath" default="${debug.classpath}"/>
+            <attribute name="stopclassname" default=""/>
+            <sequential>
+                <nbjpdastart transport="dt_socket" addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}">
+                    <classpath>
+                        <path path="@{classpath}"/>
+                    </classpath>
+                </nbjpdastart>
+            </sequential>
+        </macrodef>
+        <macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1">
+            <attribute name="dir" default="${build.classes.dir}"/>
+            <sequential>
+                <nbjpdareload>
+                    <fileset includes="${fix.includes}*.class" dir="@{dir}"/>
+                </nbjpdareload>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-macrodef-debug">
+        <macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute name="classname" default="${main.class}"/>
+            <attribute name="classpath" default="${debug.classpath}"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <java fork="true" classname="@{classname}" dir="${work.dir}">
+                    <jvmarg value="-Xdebug"/>
+                    <jvmarg value="-Xnoagent"/>
+                    <jvmarg value="-Djava.compiler=none"/>
+                    <jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
+                    <jvmarg line="${run.jvmargs}"/>
+                    <classpath>
+                        <path path="@{classpath}"/>
+                    </classpath>
+                    <syspropertyset>
+                        <propertyref prefix="run-sys-prop."/>
+                        <mapper type="glob" from="run-sys-prop.*" to="*"/>
+                    </syspropertyset>
+                    <customize/>
+                </java>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-macrodef-java">
+        <macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
+            <attribute name="classname" default="${main.class}"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <java fork="true" classname="@{classname}" dir="${work.dir}">
+                    <jvmarg line="${run.jvmargs}"/>
+                    <classpath>
+                        <path path="${run.classpath}"/>
+                    </classpath>
+                    <syspropertyset>
+                        <propertyref prefix="run-sys-prop."/>
+                        <mapper type="glob" from="run-sys-prop.*" to="*"/>
+                    </syspropertyset>
+                    <customize/>
+                </java>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-presetdef-jar">
+        <presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
+            <jar jarfile="${dist.jar}" compress="${jar.compress}">
+                <j2seproject1:fileset dir="${build.classes.dir}"/>
+            </jar>
+        </presetdef>
+    </target>
+    <target name="init" depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-junit,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar"/>
+    <!--
+    ===================
+    COMPILATION SECTION
+    ===================
+    -->
+    <target name="deps-jar" depends="init" unless="no.deps"/>
+    <target name="-pre-pre-compile" depends="init,deps-jar">
+        <mkdir dir="${build.classes.dir}"/>
+    </target>
+    <target name="-pre-compile">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="-do-compile" depends="init,deps-jar,-pre-pre-compile,-pre-compile" if="have.sources">
+        <j2seproject3:javac/>
+        <copy todir="${build.classes.dir}">
+            <fileset dir="${src.dir}" excludes="${build.classes.excludes}"/>
+        </copy>
+    </target>
+    <target name="-post-compile">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="compile" depends="init,deps-jar,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project."/>
+    <target name="-pre-compile-single">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="-do-compile-single" depends="init,deps-jar,-pre-pre-compile">
+        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
+        <j2seproject3:javac>
+            <customize>
+                <patternset includes="${javac.includes}"/>
+            </customize>
+        </j2seproject3:javac>
+    </target>
+    <target name="-post-compile-single">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="compile-single" depends="init,deps-jar,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single"/>
+    <!--
+    ====================
+    JAR BUILDING SECTION
+    ====================
+    -->
+    <target name="-pre-pre-jar" depends="init">
+        <dirname property="dist.jar.dir" file="${dist.jar}"/>
+        <mkdir dir="${dist.jar.dir}"/>
+    </target>
+    <target name="-pre-jar">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="-do-jar-without-manifest" depends="init,compile,-pre-pre-jar,-pre-jar" unless="manifest.available">
+        <j2seproject1:jar/>
+    </target>
+    <target name="-do-jar-with-manifest" depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available" unless="manifest.available+main.class">
+        <j2seproject1:jar manifest="${manifest.file}"/>
+    </target>
+    <target name="-do-jar-with-mainclass" depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class" unless="manifest.available+main.class+mkdist.available">
+        <j2seproject1:jar manifest="${manifest.file}">
+            <j2seproject1:manifest>
+                <j2seproject1:attribute name="Main-Class" value="${main.class}"/>
+            </j2seproject1:manifest>
+        </j2seproject1:jar>
+        <echo>To run this application from the command line without Ant, try:</echo>
+        <property name="build.classes.dir.resolved" location="${build.classes.dir}"/>
+        <property name="dist.jar.resolved" location="${dist.jar}"/>
+        <pathconvert property="run.classpath.with.dist.jar">
+            <path path="${run.classpath}"/>
+            <map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
+        </pathconvert>
+        <echo>java -cp "${run.classpath.with.dist.jar}" ${main.class}</echo>
+    </target>
+    <target name="-do-jar-with-libraries" depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class+mkdist.available">
+        <property name="build.classes.dir.resolved" location="${build.classes.dir}"/>
+        <pathconvert property="run.classpath.without.build.classes.dir">
+            <path path="${run.classpath}"/>
+            <map from="${build.classes.dir.resolved}" to=""/>
+        </pathconvert>
+        <pathconvert property="jar.classpath" pathsep=" ">
+            <path path="${run.classpath.without.build.classes.dir}"/>
+            <chainedmapper>
+                <flattenmapper/>
+                <globmapper from="*" to="lib/*"/>
+            </chainedmapper>
+        </pathconvert>
+        <taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" name="copylibs" classpath="${libs.CopyLibs.classpath}"/>
+        <copylibs manifest="${manifest.file}" runtimeclasspath="${run.classpath.without.build.classes.dir}" jarfile="${dist.jar}" compress="${jar.compress}">
+            <fileset dir="${build.classes.dir}"/>
+            <manifest>
+                <attribute name="Main-Class" value="${main.class}"/>
+                <attribute name="Class-Path" value="${jar.classpath}"/>
+            </manifest>
+        </copylibs>
+        <echo>To run this application from the command line without Ant, try:</echo>
+        <property name="dist.jar.resolved" location="${dist.jar}"/>
+        <echo>java -jar "${dist.jar.resolved}"</echo>
+    </target>
+    <target name="-post-jar">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="jar" depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-post-jar" description="Build JAR."/>
+    <!--
+    =================
+    EXECUTION SECTION
+    =================
+    -->
+    <target name="run" depends="init,compile" description="Run a main class.">
+        <j2seproject1:java>
+            <customize>
+                <arg line="${application.args}"/>
+            </customize>
+        </j2seproject1:java>
+    </target>
+    <target name="run-single" depends="init,compile-single">
+        <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
+        <j2seproject1:java classname="${run.class}"/>
+    </target>
+    <!--
+    =================
+    DEBUGGING SECTION
+    =================
+    -->
+    <target name="-debug-start-debugger" if="netbeans.home" depends="init">
+        <j2seproject1:nbjpdastart name="${debug.class}"/>
+    </target>
+    <target name="-debug-start-debuggee" depends="init,compile">
+        <j2seproject3:debug>
+            <customize>
+                <arg line="${application.args}"/>
+            </customize>
+        </j2seproject3:debug>
+    </target>
+    <target name="debug" if="netbeans.home" depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE."/>
+    <target name="-debug-start-debugger-stepinto" if="netbeans.home" depends="init">
+        <j2seproject1:nbjpdastart stopclassname="${main.class}"/>
+    </target>
+    <target name="debug-stepinto" if="netbeans.home" depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee"/>
+    <target name="-debug-start-debuggee-single" if="netbeans.home" depends="init,compile-single">
+        <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
+        <j2seproject3:debug classname="${debug.class}"/>
+    </target>
+    <target name="debug-single" if="netbeans.home" depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-single"/>
+    <target name="-pre-debug-fix" depends="init">
+        <fail unless="fix.includes">Must set fix.includes</fail>
+        <property name="javac.includes" value="${fix.includes}.java"/>
+    </target>
+    <target name="-do-debug-fix" if="netbeans.home" depends="init,-pre-debug-fix,compile-single">
+        <j2seproject1:nbjpdareload/>
+    </target>
+    <target name="debug-fix" if="netbeans.home" depends="init,-pre-debug-fix,-do-debug-fix"/>
+    <!--
+    ===============
+    JAVADOC SECTION
+    ===============
+    -->
+    <target name="-javadoc-build" depends="init">
+        <mkdir dir="${dist.javadoc.dir}"/>
+        <javadoc destdir="${dist.javadoc.dir}" source="${javac.source}" notree="${javadoc.notree}" use="${javadoc.use}" nonavbar="${javadoc.nonavbar}" noindex="${javadoc.noindex}" splitindex="${javadoc.splitindex}" author="${javadoc.author}" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}" private="${javadoc.private}" additionalparam="${javadoc.additionalparam}" failonerror="true" useexternalfile="true">
+            <classpath>
+                <path path="${javac.classpath}"/>
+            </classpath>
+            <sourcepath>
+                <pathelement location="${src.dir}"/>
+            </sourcepath>
+            <packageset dir="${src.dir}" includes="*/**"/>
+            <fileset dir="${src.dir}" includes="*.java"/>
+        </javadoc>
+    </target>
+    <target name="-javadoc-browse" if="netbeans.home" unless="no.javadoc.preview" depends="init,-javadoc-build">
+        <nbbrowse file="${dist.javadoc.dir}/index.html"/>
+    </target>
+    <target name="javadoc" depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc."/>
+    <!--
+    =========================
+    JUNIT COMPILATION SECTION
+    =========================
+    -->
+    <target name="-pre-pre-compile-test" if="have.tests" depends="init,compile">
+        <mkdir dir="${build.test.classes.dir}"/>
+    </target>
+    <target name="-pre-compile-test">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="-do-compile-test" if="have.tests" depends="init,compile,-pre-pre-compile-test,-pre-compile-test">
+        <j2seproject3:javac srcdir="${test.src.dir}" destdir="${build.test.classes.dir}" debug="true" classpath="${javac.test.classpath}"/>
+        <copy todir="${build.test.classes.dir}">
+            <fileset dir="${test.src.dir}" excludes="**/*.java"/>
+        </copy>
+    </target>
+    <target name="-post-compile-test">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="compile-test" depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test"/>
+    <target name="-pre-compile-test-single">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="-do-compile-test-single" if="have.tests" depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single">
+        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
+        <j2seproject3:javac srcdir="${test.src.dir}" destdir="${build.test.classes.dir}" debug="true" classpath="${javac.test.classpath}">
+            <customize>
+                <patternset includes="${javac.includes}"/>
+            </customize>
+        </j2seproject3:javac>
+        <copy todir="${build.test.classes.dir}">
+            <fileset dir="${test.src.dir}" excludes="**/*.java"/>
+        </copy>
+    </target>
+    <target name="-post-compile-test-single">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="compile-test-single" depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single"/>
+    <!--
+    =======================
+    JUNIT EXECUTION SECTION
+    =======================
+    -->
+    <target name="-pre-test-run" if="have.tests" depends="init">
+        <mkdir dir="${build.test.results.dir}"/>
+    </target>
+    <target name="-do-test-run" if="have.tests" depends="init,compile-test,-pre-test-run">
+        <j2seproject3:junit/>
+    </target>
+    <target name="-post-test-run" if="have.tests" depends="init,compile-test,-pre-test-run,-do-test-run">
+        <fail if="tests.failed">Some tests failed; see details above.</fail>
+    </target>
+    <target name="test-report" if="have.tests" depends="init"/>
+    <target name="-test-browse" if="netbeans.home+have.tests" depends="init"/>
+    <target name="test" depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests."/>
+    <target name="-pre-test-run-single" if="have.tests" depends="init">
+        <mkdir dir="${build.test.results.dir}"/>
+    </target>
+    <target name="-do-test-run-single" if="have.tests" depends="init,compile-test-single,-pre-test-run-single">
+        <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
+        <j2seproject3:junit includes="${test.includes}"/>
+    </target>
+    <target name="-post-test-run-single" if="have.tests" depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single">
+        <fail if="tests.failed">Some tests failed; see details above.</fail>
+    </target>
+    <target name="test-single" depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test."/>
+    <!--
+    =======================
+    JUNIT DEBUGGING SECTION
+    =======================
+    -->
+    <target name="-debug-start-debuggee-test" if="have.tests" depends="init,compile-test">
+        <fail unless="test.class">Must select one file in the IDE or set test.class</fail>
+        <j2seproject3:debug classname="junit.textui.TestRunner" classpath="${debug.test.classpath}">
+            <customize>
+                <arg line="${test.class}"/>
+            </customize>
+        </j2seproject3:debug>
+    </target>
+    <target name="-debug-start-debugger-test" if="netbeans.home+have.tests" depends="init,compile-test">
+        <j2seproject1:nbjpdastart name="${test.class}" classpath="${debug.test.classpath}"/>
+    </target>
+    <target name="debug-test" depends="init,compile-test,-debug-start-debugger-test,-debug-start-debuggee-test"/>
+    <target name="-do-debug-fix-test" if="netbeans.home" depends="init,-pre-debug-fix,compile-test-single">
+        <j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
+    </target>
+    <target name="debug-fix-test" if="netbeans.home" depends="init,-pre-debug-fix,-do-debug-fix-test"/>
+    <!--
+    =========================
+    APPLET EXECUTION SECTION
+    =========================
+    -->
+    <target name="run-applet" depends="init,compile-single">
+        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
+        <j2seproject1:java classname="sun.applet.AppletViewer">
+            <customize>
+                <arg value="${applet.url}"/>
+            </customize>
+        </j2seproject1:java>
+    </target>
+    <!--
+    =========================
+    APPLET DEBUGGING  SECTION
+    =========================
+    -->
+    <target name="-debug-start-debuggee-applet" if="netbeans.home" depends="init,compile-single">
+        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
+        <j2seproject3:debug classname="sun.applet.AppletViewer">
+            <customize>
+                <arg value="${applet.url}"/>
+            </customize>
+        </j2seproject3:debug>
+    </target>
+    <target name="debug-applet" if="netbeans.home" depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet"/>
+    <!--
+    ===============
+    CLEANUP SECTION
+    ===============
+    -->
+    <target name="deps-clean" depends="init" unless="no.deps"/>
+    <target name="-do-clean" depends="init">
+        <delete dir="${build.dir}"/>
+        <delete dir="${dist.dir}"/>
+    </target>
+    <target name="-post-clean">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="clean" depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products."/>
+</project>
diff --git a/xsl-java/xalan27/nbproject/genfiles.properties b/xsl-java/xalan27/nbproject/genfiles.properties
new file mode 100644 (file)
index 0000000..fd61ced
--- /dev/null
@@ -0,0 +1,8 @@
+build.xml.data.CRC32=f1bed978
+build.xml.script.CRC32=3c6d9e2a
+build.xml.stylesheet.CRC32=d5b6853a
+# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
+# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
+nbproject/build-impl.xml.data.CRC32=f1bed978
+nbproject/build-impl.xml.script.CRC32=df1e1cd8
+nbproject/build-impl.xml.stylesheet.CRC32=99b91518
diff --git a/xsl-java/xalan27/nbproject/project.properties b/xsl-java/xalan27/nbproject/project.properties
new file mode 100644 (file)
index 0000000..f6e2113
--- /dev/null
@@ -0,0 +1,56 @@
+application.args=
+build.classes.dir=${build.dir}/classes
+build.classes.excludes=**/*.java,**/*.form
+# This directory is removed when the project is cleaned:
+build.dir=build
+build.generated.dir=${build.dir}/generated
+# Only compile against the classpath explicitly listed here:
+build.sysclasspath=ignore
+build.test.classes.dir=${build.dir}/test/classes
+build.test.results.dir=${build.dir}/test/results
+debug.classpath=\
+    ${run.classpath}
+debug.test.classpath=\
+    ${run.test.classpath}
+# This directory is removed when the project is cleaned:
+dist.dir=dist
+dist.jar=${dist.dir}/xalan27.jar
+dist.javadoc.dir=${dist.dir}/javadoc
+file.reference.xalan.jar=../../../../../usr/local/java/xalan2/xalan.jar
+jar.compress=false
+javac.classpath=\
+    ${file.reference.xalan.jar}
+# Space-separated list of extra javac options
+javac.compilerargs=
+javac.deprecation=false
+javac.source=1.5
+javac.target=1.5
+javac.test.classpath=\
+    ${javac.classpath}:\
+    ${build.classes.dir}:\
+    ${libs.junit.classpath}
+javadoc.additionalparam=
+javadoc.author=false
+javadoc.encoding=
+javadoc.noindex=false
+javadoc.nonavbar=false
+javadoc.notree=false
+javadoc.private=false
+javadoc.splitindex=true
+javadoc.use=true
+javadoc.version=false
+javadoc.windowtitle=
+meta.inf.dir=${src.dir}/META-INF
+platform.active=default_platform
+run.classpath=\
+    ${javac.classpath}:\
+    ${build.classes.dir}
+# Space-separated list of JVM arguments used when running the project
+# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
+# or test-sys-prop.name=value to set system properties for unit tests):
+run.jvmargs=
+run.test.classpath=\
+    ${javac.test.classpath}:\
+    ${build.test.classes.dir}
+src.dir=src
+test.src.dir=test
diff --git a/xsl-java/xalan27/nbproject/project.xml b/xsl-java/xalan27/nbproject/project.xml
new file mode 100644 (file)
index 0000000..687ff02
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+    <type>org.netbeans.modules.java.j2seproject</type>
+    <configuration>
+        <data xmlns="http://www.netbeans.org/ns/j2se-project/3">
+            <name>xalan27</name>
+            <minimum-ant-version>1.6.5</minimum-ant-version>
+            <source-roots>
+                <root id="src.dir"/>
+            </source-roots>
+            <test-roots>
+                <root id="test.src.dir"/>
+            </test-roots>
+        </data>
+    </configuration>
+</project>
diff --git a/xsl-java/xalan27/src/com/nwalsh/xalan/CVS.java b/xsl-java/xalan27/src/com/nwalsh/xalan/CVS.java
new file mode 100644 (file)
index 0000000..fbd190e
--- /dev/null
@@ -0,0 +1,90 @@
+package com.nwalsh.xalan;
+
+import java.io.*;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.text.DateFormat;
+import java.text.ParseException;
+
+/**
+ * <p>Xalan extension to convert CVS date strings into local time</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000 Norman Walsh.</p>
+ *
+ * <p>This class provides a
+ * <a href="http://xml.apache.org/xalan-j/">Xalan</a>
+ * extension to turn the CVS date strings, which are UTC:</p>
+ *
+ * <pre>&#36;Date: 2000/11/09 02:34:20 &#36;</pre>
+ *
+ * <p>into legibly formatted local time:</p>
+ *
+ * <pre>Wed Nov 08 18:34:20 PST 2000</pre>
+ *
+ * <p>(I happened to be in California when I wrote this documentation.)</p>
+
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @version $Id$
+ *
+ */
+public class CVS {
+  /**
+   * <p>Constructor for CVS</p>
+   *
+   * <p>All of the methods are static, so the constructor does nothing.</p>
+   */
+  public CVS() {
+  }
+
+  /**
+   * <p>Convert a CVS date string into local time.</p>
+   *
+   * @param cvsDate The CVS date string.
+   *
+   * @return The date, converted to local time and reformatted.
+   */
+  public String localTime (String cvsDate) {
+    // A cvsDate has the following form "$Date$"
+    if (!cvsDate.startsWith("$Date: ")) {
+      return cvsDate;
+    }
+
+    String yrS = cvsDate.substring(7,11);
+    String moS = cvsDate.substring(12,14);
+    String daS = cvsDate.substring(15,17);
+    String hrS = cvsDate.substring(18,20);
+    String miS = cvsDate.substring(21,23);
+    String seS = cvsDate.substring(24,26);
+
+    TimeZone tz = TimeZone.getTimeZone("GMT+0");
+    GregorianCalendar gmtCal = new GregorianCalendar(tz);
+
+    try {
+      gmtCal.set(Integer.parseInt(yrS),
+                Integer.parseInt(moS)-1,
+                Integer.parseInt(daS),
+                Integer.parseInt(hrS),
+                Integer.parseInt(miS),
+                Integer.parseInt(seS));
+    } catch (NumberFormatException e) {
+      // nop
+    }
+
+    Date d = gmtCal.getTime();
+
+    return d.toString();
+  }
+}
diff --git a/xsl-java/xalan27/src/com/nwalsh/xalan/Callout.java b/xsl-java/xalan27/src/com/nwalsh/xalan/Callout.java
new file mode 100644 (file)
index 0000000..920cfc3
--- /dev/null
@@ -0,0 +1,143 @@
+package com.nwalsh.xalan;
+
+import org.w3c.dom.*;
+
+/**
+ * <p>Utility class for the Verbatim extension (ignore this).</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000 Norman Walsh.</p>
+ *
+ * <p>This class is just for book keeping in the Verbatim class.
+ * It stores information about the location of callouts.</p>
+ *
+ * <p>Only line/column based callouts are supported. This class
+ * implements the Comparable interface so that callouts can be sorted.
+ * Callouts are sorted so that they occur in left-to-right,
+ * top-to-bottom order based on line/column.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @see Verbatim
+ *
+ * @version $Id$
+ * */
+public class Callout implements Comparable {
+  /** The callout number. */
+  private int callout = 0;
+  /** The area Element item that generated this callout. */
+  private Element area = null;
+  /** The line on which this callout occurs. */
+  private int line = 0;
+  /** The column in which this callout appears. */
+  private int col = 0;
+  /** The type of callout. */
+  private int type = 0;
+  /** The other type of callout. */
+  private String otherType = null;
+
+  public static final int CALS_PAIR = 1;
+  public static final int LINE_COLUMN = 2;
+  public static final int LINE_COLUMN_PAIR = 3;
+  public static final int LINE_RANGE = 4;
+  public static final int OTHER = 5;
+
+  /** The constructor; initialize the private data structures. */
+  public Callout(int callout, Element area, int line, int col, int type) {
+    this.callout = callout;
+    this.area = area;
+    this.line = line;
+    this.col = col;
+    this.type = type;
+    this.otherType = null;
+  }
+
+  /** The constructor; initialize the private data structures. */
+  public Callout(int callout, Element area, int line, int col, String otherType) {
+    this.callout = callout;
+    this.area = area;
+    this.line = line;
+    this.col = col;
+    this.type = Callout.OTHER;
+    this.otherType = otherType;
+  }
+
+  /**
+   * <p>The compareTo method compares this Callout with another.</p>
+   *
+   * <p>Given two Callouts, A and B, A < B if:</p>
+   *
+   * <ol>
+   * <li>A.line < B.line, or</li>
+   * <li>A.line = B.line && A.col < B.col, or</li>
+   * <li>A.line = B.line && A.col = B.col && A.callout < B.callout</li>
+   * <li>Otherwise, they're equal.</li>
+   * </ol>
+   */
+  public int compareTo (Object o) {
+    Callout c = (Callout) o;
+
+    if (line == c.getLine()) {
+      if (col > c.getColumn()) {
+       return 1;
+      } else if (col < c.getColumn()) {
+       return -1;
+      } else {
+       if (callout < c.getCallout()) {
+         return -1;
+       } else if (callout > c.getCallout()) {
+         return 1;
+       } else {
+         return 0;
+       }
+      }
+    } else {
+      if (line > c.getLine()) {
+       return 1;
+      } else {
+       return -1;
+      }
+    }
+  }
+
+  /** Access the Callout's area. */
+  public Element getArea() {
+    return area;
+  }
+
+  /** Access the Callout's line. */
+  public int getLine() {
+    return line;
+  }
+
+  /** Access the Callout's column. */
+  public int getColumn() {
+    return col;
+  }
+
+  /** Access the Callout's callout number. */
+  public int getCallout() {
+    return callout;
+  }
+
+  /** Access the Callout's type. */
+  public int getType() {
+    return type;
+  }
+
+  /** Access the Callout's otherType. */
+  public String getOtherType() {
+    return otherType;
+  }
+
+
+}
+
diff --git a/xsl-java/xalan27/src/com/nwalsh/xalan/FormatCallout.java b/xsl-java/xalan27/src/com/nwalsh/xalan/FormatCallout.java
new file mode 100644 (file)
index 0000000..ff3018b
--- /dev/null
@@ -0,0 +1,101 @@
+package com.nwalsh.xalan;
+
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+import org.w3c.dom.*;
+import org.apache.xml.utils.DOMBuilder;
+import com.nwalsh.xalan.Callout;
+
+/**
+ * <p>Utility class for the Verbatim extension (ignore this).</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000, 2001 Norman Walsh.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @see Verbatim
+ *
+ * @version $Id$
+ **/
+
+public abstract class FormatCallout {
+  protected static final String foURI = "http://www.w3.org/1999/XSL/Format";
+  protected static final String xhURI = "http://www.w3.org/1999/xhtml";
+  protected boolean stylesheetFO = false;
+
+  public FormatCallout() {
+    //nop;
+  }
+
+  public String areaLabel(Element area) {
+    String label = null;
+
+    if (!"".equals(area.getAttribute("label"))) {
+      // If this area has a label, use it
+      label = area.getAttribute("label");
+    } else {
+      // Otherwise, if its parent is an areaset and it has a label, use that
+      Element parent = (Element) area.getParentNode();
+      if (parent != null
+         && parent.getNodeName().equals("areaset")
+         && !"".equals(parent.getAttribute("label"))) {
+       label = parent.getAttribute("label");
+      }
+    }
+
+    return label;
+  }
+
+  public void startSpan(DOMBuilder rtf)
+    throws SAXException {
+    // no point in doing this for FO, right?
+    if (!stylesheetFO) {
+      AttributesImpl spanAttr = new AttributesImpl();
+      spanAttr.addAttribute("", "class", "class", "CDATA", "co");
+      rtf.startElement("", "span", "span", spanAttr);
+    }
+  }
+
+  public void endSpan(DOMBuilder rtf) 
+    throws SAXException {
+    // no point in doing this for FO, right?
+    if (!stylesheetFO) {
+      rtf.endElement("", "span", "span");
+    }
+  }
+
+  public void formatTextCallout(DOMBuilder rtf,
+                               Callout callout) {
+    Element area = callout.getArea();
+    int num = callout.getCallout();
+    String userLabel = areaLabel(area);
+    String label = "(" + num + ")";
+
+    if (userLabel != null) {
+      label = userLabel;
+    }
+
+    char chars[] = label.toCharArray();
+
+    try {
+      startSpan(rtf);
+      rtf.characters(chars, 0, label.length());
+      endSpan(rtf);
+    } catch (SAXException e) {
+      System.out.println("SAX Exception in text formatCallout");
+    }
+  }
+
+  public abstract void formatCallout(DOMBuilder rtf,
+                                    Callout callout);
+}
+
diff --git a/xsl-java/xalan27/src/com/nwalsh/xalan/FormatDingbatCallout.java b/xsl-java/xalan27/src/com/nwalsh/xalan/FormatDingbatCallout.java
new file mode 100644 (file)
index 0000000..e78d3cc
--- /dev/null
@@ -0,0 +1,82 @@
+package com.nwalsh.xalan;
+
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.SAXException;
+import org.w3c.dom.*;
+import org.apache.xml.utils.DOMBuilder;
+import com.nwalsh.xalan.Callout;
+import org.apache.xml.utils.AttList;
+
+/**
+ * <p>Utility class for the Verbatim extension (ignore this).</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000, 2001 Norman Walsh.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @see Verbatim
+ *
+ * @version $Id$
+ **/
+
+public class FormatDingbatCallout extends FormatCallout {
+  int graphicsMax = 0;
+
+  public FormatDingbatCallout(int max, boolean fo) {
+    graphicsMax = max;
+    stylesheetFO = fo;
+  }
+
+  public void formatCallout(DOMBuilder rtf,
+                           Callout callout) {
+    Element area = callout.getArea();
+    int num = callout.getCallout();
+    String label = areaLabel(area);
+
+    try {
+      if (label == null && num <= graphicsMax) {
+       AttributesImpl imgAttr = new AttributesImpl();
+       String ns = "";
+       String prefix = "";
+       String imgName = "";
+
+       if (stylesheetFO) {
+         ns = foURI;
+         prefix = "fo:"; // FIXME: this could be a problem...
+         imgName = "inline";
+         imgAttr.addAttribute("", "font-family", "font-family", "CDATA",
+                              "ZapfDingbats");
+       } else {
+         ns = "";
+         prefix = "";
+         imgName = "font";
+         imgAttr.addAttribute("", "face", "face", "CDATA",
+                              "ZapfDingbats");
+       }
+
+       startSpan(rtf);
+       rtf.startElement(ns, imgName, prefix+imgName, imgAttr);
+
+       char chars[] = new char[1];
+       chars[0] = (char) (0x2775 + num);
+       rtf.characters(chars, 0, 1);
+
+       rtf.endElement(ns, imgName, prefix+imgName);
+       endSpan(rtf);
+      } else {
+       formatTextCallout(rtf, callout);
+      }
+    } catch (SAXException e) {
+      System.out.println("SAX Exception in graphics formatCallout");
+    }
+  }
+}
diff --git a/xsl-java/xalan27/src/com/nwalsh/xalan/FormatGraphicCallout.java b/xsl-java/xalan27/src/com/nwalsh/xalan/FormatGraphicCallout.java
new file mode 100644 (file)
index 0000000..6686b36
--- /dev/null
@@ -0,0 +1,83 @@
+package com.nwalsh.xalan;
+
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.SAXException;
+import org.w3c.dom.*;
+import org.apache.xml.utils.DOMBuilder;
+import com.nwalsh.xalan.Callout;
+import org.apache.xml.utils.AttList;
+
+/**
+ * <p>Utility class for the Verbatim extension (ignore this).</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000, 2001 Norman Walsh.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @see Verbatim
+ *
+ * @version $Id$
+ **/
+
+public class FormatGraphicCallout extends FormatCallout {
+  String graphicsPath = "";
+  String graphicsExt = "";
+  int graphicsMax = 0;
+
+  public FormatGraphicCallout(String path, String ext, int max, boolean fo) {
+    graphicsPath = path;
+    graphicsExt = ext;
+    graphicsMax = max;
+    stylesheetFO = fo;
+  }
+
+  public void formatCallout(DOMBuilder rtf,
+                           Callout callout) {
+    Element area = callout.getArea();
+    int num = callout.getCallout();
+    String label = areaLabel(area);
+
+    try {
+      if (label == null && num <= graphicsMax) {
+       AttributesImpl imgAttr = new AttributesImpl();
+       String ns = "";
+       String prefix = "";
+       String imgName = "";
+
+       if (stylesheetFO) {
+         ns = foURI;
+         prefix = "fo:"; // FIXME: this could be a problem...
+         imgName = "external-graphic";
+         imgAttr.addAttribute("", "src", "src", "CDATA", "url(" +
+                              graphicsPath + num + graphicsExt + ")");
+
+       } else {
+         ns = "";
+         prefix = "";
+         imgName = "img";
+         imgAttr.addAttribute("", "src", "src", "CDATA",
+                              graphicsPath + num + graphicsExt);
+         imgAttr.addAttribute("", "alt", "alt", "CDATA", label);
+       }
+
+       startSpan(rtf);
+       rtf.startElement(ns, imgName, prefix+imgName, imgAttr);
+       rtf.endElement(ns, imgName, prefix+imgName);
+       endSpan(rtf);
+      } else {
+       formatTextCallout(rtf, callout);
+      }
+    } catch (SAXException e) {
+      System.out.println("SAX Exception in graphics formatCallout");
+    }
+  }
+}
diff --git a/xsl-java/xalan27/src/com/nwalsh/xalan/FormatTextCallout.java b/xsl-java/xalan27/src/com/nwalsh/xalan/FormatTextCallout.java
new file mode 100644 (file)
index 0000000..8cdbef5
--- /dev/null
@@ -0,0 +1,38 @@
+package com.nwalsh.xalan;
+
+import org.w3c.dom.*;
+import org.apache.xml.utils.DOMBuilder;
+import com.nwalsh.xalan.Callout;
+import org.apache.xml.utils.AttList;
+
+/**
+ * <p>Utility class for the Verbatim extension (ignore this).</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000, 2001 Norman Walsh.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @see Verbatim
+ *
+ * @version $Id$
+ **/
+
+public class FormatTextCallout extends FormatCallout {
+  public FormatTextCallout(boolean fo) {
+    stylesheetFO = fo;
+  }
+
+  public void formatCallout(DOMBuilder rtf,
+                           Callout callout) {
+    formatTextCallout(rtf, callout);
+  }
+}
diff --git a/xsl-java/xalan27/src/com/nwalsh/xalan/FormatUnicodeCallout.java b/xsl-java/xalan27/src/com/nwalsh/xalan/FormatUnicodeCallout.java
new file mode 100644 (file)
index 0000000..2fdaf54
--- /dev/null
@@ -0,0 +1,87 @@
+package com.nwalsh.xalan;
+
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.SAXException;
+import org.w3c.dom.*;
+import org.apache.xml.utils.DOMBuilder;
+import com.nwalsh.xalan.Callout;
+import org.apache.xml.utils.AttList;
+
+/**
+ * <p>Utility class for the Verbatim extension (ignore this).</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000, 2001 Norman Walsh.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @see Verbatim
+ *
+ * @version $Id$
+ **/
+
+public class FormatUnicodeCallout extends FormatCallout {
+  int unicodeMax = 0;
+  int unicodeStart = 0;
+  String unicodeFont = "";
+
+  public FormatUnicodeCallout(String font, int start, int max, boolean fo) {
+    unicodeFont = font;
+    unicodeMax = max;
+    unicodeStart = start;
+    stylesheetFO = fo;
+  }
+
+  public void formatCallout(DOMBuilder rtf,
+                           Callout callout) {
+    Element area = callout.getArea();
+    int num = callout.getCallout();
+    String label = areaLabel(area);
+
+    try {
+      if (label == null && num <= unicodeMax) {
+       AttributesImpl inAttr = new AttributesImpl();
+       String ns = "";
+       String prefix = "";
+       String inName = "";
+
+       if (!unicodeFont.equals("")) {
+         if (stylesheetFO) {
+           ns = foURI;
+           prefix = "fo:";
+           inName = "inline";
+           inAttr.addAttribute("", "", "font-family", "CDATA", unicodeFont);
+         } else {
+           inName = "font";
+           inAttr.addAttribute("", "", "face", "CDATA", unicodeFont);
+         }
+       }
+
+       char chars[] = new char[1];
+       chars[0] = (char) (unicodeStart + num - 1);
+
+       startSpan(rtf);
+       if (!unicodeFont.equals("")) {
+         rtf.startElement(ns, inName, prefix+inName, inAttr);
+       }
+       rtf.characters(chars, 0, 1);
+       if (!unicodeFont.equals("")) {
+         rtf.endElement(ns, inName, prefix+inName);
+       }
+       endSpan(rtf);
+      } else {
+       formatTextCallout(rtf, callout);
+      }
+    } catch (SAXException e) {
+      System.out.println("SAX Exception in unicode formatCallout");
+    }
+  }
+}
diff --git a/xsl-java/xalan27/src/com/nwalsh/xalan/Func.java b/xsl-java/xalan27/src/com/nwalsh/xalan/Func.java
new file mode 100644 (file)
index 0000000..771bd13
--- /dev/null
@@ -0,0 +1,58 @@
+// Func - Xalann extension function test
+
+package com.nwalsh.xalan;
+
+import org.xml.sax.SAXException;
+import org.xml.sax.ContentHandler;
+
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Element;
+import org.w3c.dom.traversal.NodeIterator;
+
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XRTreeFrag;
+import org.apache.xpath.XPath;
+import org.apache.xpath.NodeSet;
+import org.apache.xalan.extensions.XSLProcessorContext;
+import org.apache.xalan.extensions.ExpressionContext;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xalan.templates.StylesheetRoot;
+import org.apache.xalan.templates.ElemExtensionCall;
+import org.apache.xalan.templates.OutputProperties;
+import org.apache.xalan.res.XSLTErrorResources;
+
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.TransformerException;
+
+public class Func {
+  public Func() {
+  }
+
+  public DocumentFragment doSomething(NodeIterator rtf) {
+    System.out.println("Got here 2: " + rtf);
+
+    DocumentFragment df = (DocumentFragment) rtf.nextNode();
+    Element node = (Element) df.getFirstChild();
+
+    System.out.println("node=" + node);
+    System.out.println("namesp uri: " + node.getNamespaceURI());
+    System.out.println("local name: " + node.getLocalName());
+
+    return df;
+  }
+
+  public DocumentFragment doSomething(DocumentFragment rtf) {
+    System.out.println("Got here: " + rtf);
+
+    return rtf;
+    /*
+    Element node = (Element) rtf.getFirstChild();
+
+    System.out.println("node=" + node);
+    System.out.println("namesp uri: " + node.getNamespaceURI());
+    System.out.println("local name: " + node.getLocalName());
+
+    return rtf;
+    */
+  }
+}
diff --git a/xsl-java/xalan27/src/com/nwalsh/xalan/ImageIntrinsics.java b/xsl-java/xalan27/src/com/nwalsh/xalan/ImageIntrinsics.java
new file mode 100644 (file)
index 0000000..94f02f4
--- /dev/null
@@ -0,0 +1,160 @@
+package com.nwalsh.xalan;
+
+import java.io.*;
+import java.awt.*;
+import java.awt.image.*;
+import java.lang.Thread;
+import java.util.StringTokenizer;
+
+import org.apache.xalan.extensions.ExpressionContext;
+
+/**
+ * <p>Xalan extension to examine intrinsic size of images</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2002 Norman Walsh.</p>
+ *
+ * <p>This class provides a
+ * <a href="http://xml.apache.org/xalan-j/">Xalan</a>
+ * extension to find the intrinsic size of images.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @version $Id$
+ *
+ */
+public class ImageIntrinsics implements ImageObserver {
+  boolean imageLoaded = false;
+  boolean imageFailed = false;
+  Image image = null;
+  int width = -1;
+  int depth = -1;
+
+  /**
+   * <p>Constructor for ImageIntrinsics</p>
+   */
+  public ImageIntrinsics(ExpressionContext context, String imageFn) {
+    System.setProperty("java.awt.headless","true");
+    image = Toolkit.getDefaultToolkit().getImage (imageFn);
+    width = image.getWidth(this);
+
+    while (!imageFailed && (width == -1 || depth == -1)) {
+      try {
+       java.lang.Thread.currentThread().sleep(50);
+      } catch (Exception e) {
+       // nop;
+      }
+      width = image.getWidth(this);
+      depth = image.getHeight(this);
+    }
+
+    if (imageFailed) {
+      // Maybe it's an EPS or PDF?
+      // FIXME: this code is crude
+      BufferedReader ir = null;
+      String line = null;
+      int lineLimit = 100;
+
+      try {
+       ir = new BufferedReader(new FileReader(new File(imageFn)));
+       line = ir.readLine();
+
+       if (line != null && line.startsWith("%PDF-")) {
+         // We've got a PDF!
+         while (lineLimit > 0 && line != null) {
+           lineLimit--;
+           if (line.startsWith("/CropBox [")) {
+             line = line.substring(10);
+             if (line.indexOf("]") >= 0) {
+               line = line.substring(0, line.indexOf("]"));
+             }
+             parseBox(line);
+             lineLimit = 0;
+           }
+           line = ir.readLine();
+         }
+       } else if (line != null && line.startsWith("%!") && line.indexOf(" EPSF-") > 0) {
+         // We've got an EPS!
+         while (lineLimit > 0 && line != null) {
+           lineLimit--;
+           if (line.startsWith("%%BoundingBox: ")) {
+             line = line.substring(15);
+             parseBox(line);
+             lineLimit = 0;
+           }
+           line = ir.readLine();
+         }
+       }
+      } catch (Exception e) {
+       // nop;
+      }
+
+      if (ir != null) {
+       try {
+         ir.close();
+       } catch (Exception e) {
+         // nop;
+       }
+      }
+    }
+  }
+
+  public int getWidth(ExpressionContext context, int defaultWidth) {
+    if (width >= 0) {
+      return width;
+    } else {
+      return defaultWidth;
+    }
+  }
+
+  public int getDepth(ExpressionContext context, int defaultDepth) {
+    if (depth >= 0) {
+      return depth;
+    } else {
+      return defaultDepth;
+    }
+  }
+
+  private void parseBox(String line) {
+    int [] corners = new int [4];
+    int count = 0;
+
+    StringTokenizer st = new StringTokenizer(line);
+    while (count < 4 && st.hasMoreTokens()) {
+      try {
+       corners[count++] = Integer.parseInt(st.nextToken());
+      } catch (Exception e) {
+       // nop;
+      }
+    }
+
+    width = corners[2] - corners[0];
+    depth = corners[3] - corners[1];
+  }
+
+  public boolean imageUpdate(Image img, int infoflags,
+                            int x, int y, int width, int height) {
+    if ((infoflags & ImageObserver.ERROR) == ImageObserver.ERROR) {
+      imageFailed = true;
+      return false;
+    }
+
+    // I really only care about the width and height, but if I return false as
+    // soon as those are available, the BufferedInputStream behind the loader
+    // gets closed too early.
+    int flags = ImageObserver.ALLBITS;
+    if ((infoflags & flags) == flags) {
+      return false;
+    } else {
+      return true;
+    }
+  }
+}
diff --git a/xsl-java/xalan27/src/com/nwalsh/xalan/Params.java b/xsl-java/xalan27/src/com/nwalsh/xalan/Params.java
new file mode 100644 (file)
index 0000000..2026126
--- /dev/null
@@ -0,0 +1,58 @@
+// Params.java - Read stylesheet parameters in Xalan
+
+package com.nwalsh.xalan;
+
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.XPathContext;
+import org.apache.xalan.extensions.ExpressionContext;
+import org.apache.xml.utils.QName;
+
+import javax.xml.transform.TransformerException;
+
+public class Params {
+
+  public static String getString(ExpressionContext context,
+                                String varName) {
+    try {
+      XObject var = context.getVariableOrParam(new QName(varName));
+      if (var != null) {
+       return var.toString();
+      } else {
+       System.out.println("$" + varName + " is not a defined parameter.");
+       return "";
+      }
+    } catch (TransformerException te) {
+      // Nevermind the warning
+      // System.out.println("Transformer exception getting value of $" + varName);
+      return "";
+    }
+  }
+
+  public static int getInt(ExpressionContext context,
+                          String varName) {
+    String stringValue = getString(context, varName);
+    if (stringValue != null) {
+      try {
+       int value = Integer.parseInt(stringValue);
+       return value;
+      } catch (NumberFormatException e) {
+       System.out.println("$" + varName + " is not an integer.");
+      }
+    }
+    return 0;
+  }
+
+  public static boolean getBoolean(ExpressionContext context,
+                                  String varName) {
+    String stringValue = getString(context, varName);
+    if (stringValue != null) {
+      if (stringValue.equals("0") || stringValue.equals("")) {
+       return false;
+      } else {
+       return true;
+      }
+    } else {
+      return false;
+    }
+  }
+}
diff --git a/xsl-java/xalan27/src/com/nwalsh/xalan/Table.java b/xsl-java/xalan27/src/com/nwalsh/xalan/Table.java
new file mode 100644 (file)
index 0000000..f74fc93
--- /dev/null
@@ -0,0 +1,538 @@
+// Verbatim.java - Xalan extensions supporting DocBook verbatim environments
+
+package com.nwalsh.xalan;
+
+import java.util.Hashtable;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.traversal.NodeIterator;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.XPathContext;
+import org.apache.xalan.extensions.ExpressionContext;
+import org.apache.xml.utils.DOMBuilder;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import org.apache.xml.utils.QName;
+import org.apache.xml.utils.AttList;
+
+/**
+ * <p>Xalan extensions supporting Tables</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000 Norman Walsh.</p>
+ *
+ * <p>This class provides a
+ * <a href="http://xml.apache.org/xalan-j/">Xalan</a>
+ * implementation of some code to adjust CALS Tables to HTML
+ * Tables.</p>
+ *
+ * <p><b>Column Widths</b></p>
+ * <p>The <tt>adjustColumnWidths</tt> method takes a result tree
+ * fragment (assumed to contain the colgroup of an HTML Table)
+ * and returns the result tree fragment with the column widths
+ * adjusted to HTML terms.</p>
+ *
+ * <p><b>Convert Lengths</b></p>
+ * <p>The <tt>convertLength</tt> method takes a length specification
+ * of the form 9999.99xx (where "xx" is a unit) and returns that length
+ * as an integral number of pixels. For convenience, percentage lengths
+ * are returned unchanged.</p>
+ * <p>The recognized units are: inches (in), centimeters (cm),
+ * millimeters (mm), picas (pc, 1pc=12pt), points (pt), and pixels (px).
+ * A number with no units is assumed to be pixels.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @version $Id$
+ *
+ */
+public class Table {
+  /** The number of pixels per inch */
+  private static int pixelsPerInch = 96;
+
+  /** The hash used to associate units with a length in pixels. */
+  protected static Hashtable unitHash = null;
+
+  /** The FO namespace name. */
+  protected static String foURI = "http://www.w3.org/1999/XSL/Format";
+
+  /**
+   * <p>Constructor for Verbatim</p>
+   *
+   * <p>All of the methods are static, so the constructor does nothing.</p>
+   */
+  public Table() {
+  }
+
+  /** Initialize the internal hash table with proper values. */
+  protected static void initializeHash() {
+    unitHash = new Hashtable();
+    unitHash.put("in", new Float(pixelsPerInch));
+    unitHash.put("cm", new Float(pixelsPerInch / 2.54));
+    unitHash.put("mm", new Float(pixelsPerInch / 25.4));
+    unitHash.put("pc", new Float((pixelsPerInch / 72) * 12));
+    unitHash.put("pt", new Float(pixelsPerInch / 72));
+    unitHash.put("px", new Float(1));
+  }
+
+  /** Set the pixels-per-inch value. Only positive values are legal. */
+  public static void setPixelsPerInch(int value) {
+    if (value > 0) {
+      pixelsPerInch = value;
+      initializeHash();
+    }
+  }
+
+  /** Return the current pixels-per-inch value. */
+  public int getPixelsPerInch() {
+    return pixelsPerInch;
+  }
+
+  /**
+   * <p>Convert a length specification to a number of pixels.</p>
+   *
+   * <p>The specified length should be of the form [+/-]999.99xx,
+   * where xx is a valid unit.</p>
+   */
+  public static int convertLength(String length) {
+    // The format of length should be 999.999xx
+    int sign = 1;
+    String digits = "";
+    String units = "";
+    char lench[] = length.toCharArray();
+    float flength = 0;
+    boolean done = false;
+    int pos = 0;
+    float factor = 1;
+    int pixels = 0;
+
+    if (unitHash == null) {
+      initializeHash();
+    }
+
+    if (lench[pos] == '+' || lench[pos] == '-') {
+      if (lench[pos] == '-') {
+       sign = -1;
+      }
+      pos++;
+    }
+
+    while (!done) {
+      if (pos >= lench.length) {
+       done = true;
+      } else {
+       if ((lench[pos] > '9' || lench[pos] < '0') && lench[pos] != '.') {
+         done = true;
+         units = length.substring(pos);
+       } else {
+         digits += lench[pos++];
+       }
+      }
+    }
+
+    try {
+      flength = Float.parseFloat(digits);
+    } catch (NumberFormatException e) {
+      System.out.println(digits + " is not a number; 1 used instead.");
+      flength = 1;
+    }
+
+    Float f = null;
+
+    if (!units.equals("")) {
+      f = (Float) unitHash.get(units);
+      if (f == null) {
+       System.out.println(units + " is not a known unit; 1 used instead.");
+       factor = 1;
+      } else {
+       factor = f.floatValue();
+      }
+    } else {
+      factor = 1;
+    }
+
+    f = new Float(flength * factor);
+
+    pixels = f.intValue() * sign;
+
+    return pixels;
+  }
+
+  /**
+   * <p>Adjust column widths in an HTML table.</p>
+   *
+   * <p>The specification of column widths in CALS (a relative width
+   * plus an optional absolute width) are incompatible with HTML column
+   * widths. This method adjusts CALS column width specifiers in an
+   * attempt to produce equivalent HTML specifiers.</p>
+   *
+   * <p>In order for this method to work, the CALS width specifications
+   * should be placed in the "width" attribute of the &lt;col>s within
+   * a &lt;colgroup>. Then the colgroup result tree fragment is passed
+   * to this method.</p>
+   *
+   * <p>This method makes use of two parameters from the XSL stylesheet
+   * that calls it: <code>nominal.table.width</code> and
+   * <code>table.width</code>. The value of <code>nominal.table.width</code>
+   * must be an absolute distance. The value of <code>table.width</code>
+   * can be either absolute or relative.</p>
+   *
+   * <p>Presented with a mixture of relative and
+   * absolute lengths, the table width is used to calculate
+   * appropriate values. If the <code>table.width</code> is relative,
+   * the nominal width is used for this calculation.</p>
+   *
+   * <p>There are three possible combinations of values:</p>
+   *
+   * <ol>
+   * <li>There are no relative widths; in this case the absolute widths
+   * are used in the HTML table.</li>
+   * <li>There are no absolute widths; in this case the relative widths
+   * are used in the HTML table.</li>
+   * <li>There are a mixture of absolute and relative widths:
+   *   <ol>
+   *     <li>If the table width is absolute, all widths become absolute.</li>
+   *     <li>If the table width is relative, make all the widths absolute
+   *         relative to the nominal table width then turn them all
+   *         back into relative widths.</li>
+   *   </ol>
+   * </li>
+   * </ol>
+   *
+   * @param context The stylesheet context; supplied automatically by Xalan
+   * @param xalanNI
+   *
+   * @return The result tree fragment containing the adjusted colgroup.
+   *
+   */
+
+  public DocumentFragment adjustColumnWidths (ExpressionContext context,
+                                             NodeIterator xalanNI) {
+
+    int nominalWidth = convertLength(Params.getString(context,
+                                                     "nominal.table.width"));
+    String tableWidth = Params.getString(context, "table.width");
+    String styleType = Params.getString(context, "stylesheet.result.type");
+    boolean foStylesheet = styleType.equals("fo");
+
+    DocumentFragment xalanRTF = (DocumentFragment) xalanNI.nextNode();
+    Element colgroup = (Element) xalanRTF.getFirstChild();
+
+    // N.B. ...stree.ElementImpl doesn't implement getElementsByTagName()
+
+    Node firstCol = null;
+    // If this is an FO tree, there might be no colgroup...
+    if (colgroup.getLocalName().equals("colgroup")) {
+      firstCol = colgroup.getFirstChild();
+    } else {
+      firstCol = colgroup;
+    }
+
+    // Count the number of columns...
+    Node child = firstCol;
+    int numColumns = 0;
+    while (child != null) {
+      if (child.getNodeType() == Node.ELEMENT_NODE
+         && (child.getNodeName().equals("col")
+             || (child.getNamespaceURI().equals(foURI)
+                 && child.getLocalName().equals("table-column")))) {
+       numColumns++;
+      }
+
+      child = child.getNextSibling();
+    }
+
+    String widths[] = new String[numColumns];
+    Element columns[] = new Element[numColumns];
+    int colnum = 0;
+
+    child = firstCol;
+    while (child != null) {
+      if (child.getNodeType() == Node.ELEMENT_NODE
+         && (child.getNodeName().equals("col")
+             || (child.getNamespaceURI().equals(foURI)
+                 && child.getLocalName().equals("table-column")))) {
+       Element col = (Element) child;
+
+       columns[colnum] = col;
+
+       if (foStylesheet) {
+         if ("".equals(col.getAttribute("column-width"))) {
+           widths[colnum] = "1*";
+         } else {
+           widths[colnum] = col.getAttribute("column-width");
+         }
+       } else {
+         if ("".equals(col.getAttribute("width"))) {
+           widths[colnum] = "1*";
+         } else {
+           widths[colnum] = col.getAttribute("width");
+         }
+       }
+
+       colnum++;
+      }
+      child = child.getNextSibling();
+    }
+
+    float relTotal = 0;
+    float relParts[] = new float[numColumns];
+
+    float absTotal = 0;
+    float absParts[] = new float[numColumns];
+
+    for (int count = 0; count < numColumns; count++) {
+      String width = widths[count];
+      int pos = width.indexOf("*");
+      if (pos >= 0) {
+       String relPart = width.substring(0, pos);
+       String absPart = width.substring(pos+1);
+
+       try {
+         float rel = Float.parseFloat(relPart);
+         relTotal += rel;
+         relParts[count] = rel;
+       } catch (NumberFormatException e) {
+         System.out.println(relPart + " is not a valid relative unit.");
+       }
+
+       int pixels = 0;
+       if (absPart != null && !absPart.equals("")) {
+         pixels = convertLength(absPart);
+       }
+
+       absTotal += pixels;
+       absParts[count] = pixels;
+      } else {
+       relParts[count] = 0;
+
+       int pixels = 0;
+       if (width != null && !width.equals("")) {
+         pixels = convertLength(width);
+       }
+
+       absTotal += pixels;
+       absParts[count] = pixels;
+      }
+    }
+
+    // Ok, now we have the relative widths and absolute widths in
+    // two parallel arrays.
+    //
+    // - If there are no relative widths, output the absolute widths
+    // - If there are no absolute widths, output the relative widths
+    // - If there are a mixture of relative and absolute widths,
+    //   - If the table width is absolute, turn these all into absolute
+    //     widths.
+    //   - If the table width is relative, turn these all into absolute
+    //     widths in the nominalWidth and then turn them back into
+    //     percentages.
+
+    if (relTotal == 0) {
+      for (int count = 0; count < numColumns; count++) {
+       Float f = new Float(absParts[count]);
+       if (foStylesheet) {
+         int pixels = f.intValue();
+         float inches = (float) pixels / pixelsPerInch;
+         widths[count] = inches + "in";
+       } else {
+         widths[count] = Integer.toString(f.intValue());
+       }
+      }
+    } else if (absTotal == 0) {
+      for (int count = 0; count < numColumns; count++) {
+       float rel = relParts[count] / relTotal * 100;
+       Float f = new Float(rel);
+       widths[count] = Integer.toString(f.intValue());
+      }
+      widths = correctRoundingError(widths);
+    } else {
+      int pixelWidth = nominalWidth;
+
+      if (tableWidth.indexOf("%") <= 0) {
+       pixelWidth = convertLength(tableWidth);
+      }
+
+      if (pixelWidth <= absTotal) {
+       System.out.println("Table is wider than table width.");
+      } else {
+       pixelWidth -= absTotal;
+      }
+
+      absTotal = 0;
+      for (int count = 0; count < numColumns; count++) {
+       float rel = relParts[count] / relTotal * pixelWidth;
+       relParts[count] = rel + absParts[count];
+       absTotal += rel + absParts[count];
+      }
+
+      if (tableWidth.indexOf("%") <= 0) {
+       for (int count = 0; count < numColumns; count++) {
+         Float f = new Float(relParts[count]);
+         if (foStylesheet) {
+           int pixels = f.intValue();
+           float inches = (float) pixels / pixelsPerInch;
+           widths[count] = inches + "in";
+         } else {
+           widths[count] = Integer.toString(f.intValue());
+         }
+       }
+      } else {
+       for (int count = 0; count < numColumns; count++) {
+         float rel = relParts[count] / absTotal * 100;
+         Float f = new Float(rel);
+         widths[count] = Integer.toString(f.intValue());
+       }
+       widths = correctRoundingError(widths);
+      }
+    }
+
+    // Now rebuild the colgroup with the right widths
+
+    DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
+    DocumentBuilder docBuilder = null;
+
+    try {
+      docBuilder = docFactory.newDocumentBuilder();
+    } catch (ParserConfigurationException e) {
+      System.out.println("PCE!");
+      return xalanRTF;
+    }
+    Document doc = docBuilder.newDocument();
+    DocumentFragment df = doc.createDocumentFragment();
+    DOMBuilder rtf = new DOMBuilder(doc, df);
+
+    try {
+      String ns = colgroup.getNamespaceURI();
+      String localName = colgroup.getLocalName();
+      String name = colgroup.getTagName();
+
+      if (colgroup.getLocalName().equals("colgroup")) {
+       rtf.startElement(ns, localName, name,
+                        copyAttributes(colgroup));
+      }
+
+      for (colnum = 0; colnum < numColumns; colnum++) {
+       Element col = columns[colnum];
+
+       NamedNodeMap domAttr = col.getAttributes();
+
+       AttributesImpl attr = new AttributesImpl();
+       for (int acount = 0; acount < domAttr.getLength(); acount++) {
+         Node a = domAttr.item(acount);
+         String a_ns = a.getNamespaceURI();
+         String a_localName = a.getLocalName();
+
+         if ((foStylesheet && !a_localName.equals("column-width"))
+             || !a_localName.equalsIgnoreCase("width")) {
+           attr.addAttribute(a.getNamespaceURI(),
+                             a.getLocalName(),
+                             a.getNodeName(),
+                             "CDATA",
+                             a.getNodeValue());
+         }
+       }
+
+       if (foStylesheet) {
+         attr.addAttribute("", "column-width", "column-width", "CDATA", widths[colnum]);
+       } else {
+         attr.addAttribute("", "width", "width", "CDATA", widths[colnum]);
+       }
+
+       rtf.startElement(col.getNamespaceURI(),
+                        col.getLocalName(),
+                        col.getTagName(),
+                        attr);
+       rtf.endElement(col.getNamespaceURI(),
+                      col.getLocalName(),
+                      col.getTagName());
+      }
+
+      if (colgroup.getLocalName().equals("colgroup")) {
+       rtf.endElement(ns, localName, name);
+      }
+    } catch (SAXException se) {
+      System.out.println("SE!");
+      return xalanRTF;
+    }
+
+    return df;
+  }
+
+  private Attributes copyAttributes(Element node) {
+    AttributesImpl attrs = new AttributesImpl();
+    NamedNodeMap nnm = node.getAttributes();
+    for (int count = 0; count < nnm.getLength(); count++) {
+      Attr attr = (Attr) nnm.item(count);
+      String name = attr.getName();
+      if (name.startsWith("xmlns:") || name.equals("xmlns")) {
+       // Skip it; (don't ya just love it!!)
+      } else {
+       attrs.addAttribute(attr.getNamespaceURI(), attr.getName(),
+                          attr.getName(), "CDATA", attr.getValue());
+      }
+    }
+    return attrs;
+  }
+
+  /**
+   * Correct rounding errors introduced in calculating the width of each
+   * column. Make sure they sum to 100% in the end.
+   */
+  protected String[] correctRoundingError(String widths[]) {
+    int totalWidth = 0;
+
+    for (int count = 0; count < widths.length; count++) {
+      try {
+       int width = Integer.parseInt(widths[count]);
+       totalWidth += width;
+      } catch (NumberFormatException nfe) {
+       // nop; "can't happen"
+      }
+    }
+
+    float totalError = 100 - totalWidth;
+    float columnError = totalError / widths.length;
+    float error = 0;
+
+    for (int count = 0; count < widths.length; count++) {
+      try {
+       int width = Integer.parseInt(widths[count]);
+       error = error + columnError;
+       if (error >= 1.0) {
+         int adj = (int) Math.round(Math.floor(error));
+         error = error - (float) Math.floor(error);
+         width = width + adj;
+         widths[count] = Integer.toString(width) + "%";
+       } else {
+         widths[count] = Integer.toString(width) + "%";
+       }
+      } catch (NumberFormatException nfe) {
+       // nop; "can't happen"
+      }
+    }
+
+    return widths;
+  }
+}
diff --git a/xsl-java/xalan27/src/com/nwalsh/xalan/Text.java b/xsl-java/xalan27/src/com/nwalsh/xalan/Text.java
new file mode 100644 (file)
index 0000000..345d5b1
--- /dev/null
@@ -0,0 +1,201 @@
+// Text - Xalan extension element for inserting text
+
+package com.nwalsh.xalan;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.FileNotFoundException;
+import java.net.URL;
+import java.net.MalformedURLException;
+
+import org.xml.sax.SAXException;
+import org.xml.sax.ContentHandler;
+
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.XPath;
+import org.apache.xpath.NodeSet;
+import org.apache.xalan.extensions.XSLProcessorContext;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xalan.templates.StylesheetRoot;
+import org.apache.xalan.templates.ElemExtensionCall;
+import org.apache.xalan.templates.OutputProperties;
+import org.apache.xalan.res.XSLTErrorResources;
+
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.Source;
+
+/**
+ * <p>Xalan extension element for inserting text
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2001 Norman Walsh.</p>
+ *
+ * <p>This class provides a
+ * <a href="http://xml.apache.org/xalan-j/">Xalan</a>
+ * extension element for inserting text into a result tree.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @version $Id$
+ *
+ */
+public class Text {
+  /**
+   * <p>Constructor for Text</p>
+   *
+   * <p>Does nothing.</p>
+   */
+  public Text() {
+  }
+
+  public String insertfile(XSLProcessorContext context,
+                            ElemExtensionCall elem)
+    throws MalformedURLException,
+           FileNotFoundException,
+           IOException,
+          TransformerException {
+    String href = getFilename(context, elem);
+    String encoding = getEncoding(context, elem);
+
+    String baseURI = context.getTransformer().getBaseURLOfSource();
+    URIResolver resolver = context.getTransformer().getURIResolver();
+
+    if (resolver != null) {
+      Source source = resolver.resolve(href, baseURI);
+      href = source.getSystemId();
+    }
+
+    URL baseURL = null;
+    URL fileURL = null;
+
+    try {
+      baseURL = new URL(baseURI);
+    } catch (MalformedURLException e1) {
+      try {
+       baseURL = new URL("file:" + baseURI);
+      } catch (MalformedURLException e2) {
+       System.out.println("Cannot find base URI for " + baseURI);
+       baseURL = null;
+      }
+    }
+
+    String text = "";
+
+    try {
+      try {
+        fileURL = new URL(baseURL, href);
+      } catch (MalformedURLException e1) {
+        try {
+          fileURL = new URL(baseURL, "file:" + href);
+        } catch (MalformedURLException e2) {
+          System.out.println("Cannot open " + href);
+          return "";
+        }
+      }
+
+      InputStreamReader isr = null;
+      if (encoding.equals("") == true)
+        isr = new InputStreamReader(fileURL.openStream());
+      else
+        isr = new InputStreamReader(fileURL.openStream(), encoding);
+
+      BufferedReader is = new BufferedReader(isr);
+
+      final int BUFFER_SIZE = 4096;
+      char chars[] = new char[BUFFER_SIZE];
+      char nchars[] = new char[BUFFER_SIZE];
+      int len = 0;
+      int i = 0;
+      int carry = -1;
+
+      while ((len = is.read(chars)) > 0) {
+        // various new lines are normalized to LF to prevent blank lines
+       // between lines
+
+        int nlen = 0;
+        for (i=0; i<len; i++) {
+          // is current char CR?
+          if (chars[i] == '\r') {
+            if (i < (len - 1)) {
+              // skip it if next char is LF
+              if (chars[i+1] == '\n') continue;
+              // single CR -> LF to normalize MAC line endings
+              nchars[nlen] = '\n';
+              nlen++;
+              continue;
+            } else {
+              // if CR is last char of buffer we must look ahead
+              carry = is.read();
+              nchars[nlen] = '\n';
+              nlen++;
+              if (carry == '\n') {
+                carry = -1;
+              }
+              break;
+            }
+          }
+          nchars[nlen] = chars[i];
+          nlen++;
+        }
+
+       text += String.valueOf(nchars, 0, nlen);
+
+        // handle look aheaded character
+        if (carry != -1) text += String.valueOf((char)carry);
+        carry = -1;
+      }
+      is.close();
+    } catch (Exception e) {
+      System.out.println("Cannot read " + href);
+    }
+
+    return text;
+  }
+
+  private String getFilename(XSLProcessorContext context, ElemExtensionCall elem)
+    throws java.net.MalformedURLException,
+          java.io.FileNotFoundException,
+          java.io.IOException,
+          javax.xml.transform.TransformerException {
+
+    String fileName;
+
+    fileName = ((ElemExtensionCall)elem).getAttribute ("href",
+                                                      context.getContextNode(),
+                                                      context.getTransformer());
+
+    if ("".equals(fileName)) {
+      context.getTransformer().getMsgMgr().error(elem,
+                                                "No 'href' on text, or not a filename");
+    }
+
+    return fileName;
+  }
+
+  private String getEncoding(XSLProcessorContext context, ElemExtensionCall elem)
+    throws java.net.MalformedURLException,
+          java.io.FileNotFoundException,
+          java.io.IOException,
+          javax.xml.transform.TransformerException {
+
+    String encoding;
+
+    encoding = ((ElemExtensionCall)elem).getAttribute ("encoding",
+                                                      context.getContextNode(),
+                                                      context.getTransformer());
+
+    return encoding;
+  }
+}
diff --git a/xsl-java/xalan27/src/com/nwalsh/xalan/Verbatim.java b/xsl-java/xalan27/src/com/nwalsh/xalan/Verbatim.java
new file mode 100644 (file)
index 0000000..4ab456a
--- /dev/null
@@ -0,0 +1,931 @@
+// Verbatim.java - Xalan extensions supporting DocBook verbatim environments
+
+package com.nwalsh.xalan;
+
+import java.util.Stack;
+import java.util.StringTokenizer;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.Element;
+import org.w3c.dom.traversal.NodeIterator;
+
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.XPath;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.NodeSet;
+import org.apache.xalan.extensions.XSLProcessorContext;
+import org.apache.xalan.extensions.ExpressionContext;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xalan.templates.StylesheetRoot;
+import org.apache.xalan.templates.ElemExtensionCall;
+import org.apache.xalan.templates.OutputProperties;
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xml.utils.DOMBuilder;
+import org.apache.xml.utils.AttList;
+import org.apache.xml.utils.QName;
+
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.TransformerException;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import com.nwalsh.xalan.Callout;
+import com.nwalsh.xalan.Params;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * <p>Xalan extensions supporting DocBook verbatim environments</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2001 Norman Walsh.</p>
+ *
+ * <p>This class provides a
+ * <a href="http://xml.apache.org/xalan-j/">Xalan</a>
+ * implementation of two features that would be impractical to
+ * implement directly in XSLT: line numbering and callouts.</p>
+ *
+ * <p><b>Line Numbering</b></p>
+ * <p>The <tt>numberLines</tt> family of functions takes a result tree
+ * fragment (assumed to contain the contents of a formatted verbatim
+ * element in DocBook: programlisting, screen, address, literallayout,
+ * or synopsis) and returns a result tree fragment decorated with
+ * line numbers.</p>
+ *
+ * <p><b>Callouts</b></p>
+ * <p>The <tt>insertCallouts</tt> family of functions takes an
+ * <tt>areaspec</tt> and a result tree fragment
+ * (assumed to contain the contents of a formatted verbatim
+ * element in DocBook: programlisting, screen, address, literallayout,
+ * or synopsis) and returns a result tree fragment decorated with
+ * callouts.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @version $Id$
+ *
+ */
+public class Verbatim {
+  /** A stack to hold the open elements while walking through a RTF. */
+  private Stack elementStack = null;
+  /** A stack to hold the temporarily closed elements. */
+  private Stack tempStack = null;
+  /** The current line number. */
+  private int lineNumber = 0;
+  /** The current column number. */
+  private int colNumber = 0;
+  /** The modulus for line numbering (every 'modulus' line is numbered). */
+  private int modulus = 0;
+  /** The width (in characters) of line numbers (for padding). */
+  private int width = 0;
+  /** The separator between the line number and the verbatim text. */
+  private String separator = "";
+  /** The (sorted) array of callouts obtained from the areaspec. */
+  private Callout callout[] = null;
+  /** The number of callouts in the callout array. */
+  private int calloutCount = 0;
+  /** A pointer used to keep track of our position in the callout array. */
+  private int calloutPos = 0;
+  /** The path to use for graphical callout decorations. */
+  private String graphicsPath = null;
+  /** The extension to use for graphical callout decorations. */
+  private String graphicsExt = null;
+  /** The largest callout number that can be represented graphically. */
+  private int graphicsMax = 10;
+  /** Should graphic callouts use fo:external-graphics or imgs. */
+  private boolean graphicsFO = false;
+
+  private static final String foURI = "http://www.w3.org/1999/XSL/Format";
+  private static final String xhURI = "http://www.w3.org/1999/xhtml";
+
+  /**
+   * <p>Constructor for Verbatim</p>
+   *
+   * <p>All of the methods are static, so the constructor does nothing.</p>
+   */
+  public Verbatim() {
+  }
+
+  /**
+   * <p>Number lines in a verbatim environment.</p>
+   *
+   * <p>This method adds line numbers to a result tree fragment. Each
+   * newline that occurs in a text node is assumed to start a new line.
+   * The first line is always numbered, every subsequent xalanMod line
+   * is numbered (so if xalanMod=5, lines 1, 5, 10, 15, etc. will be
+   * numbered. If there are fewer than xalanMod lines in the environment,
+   * every line is numbered.</p>
+   *
+   * <p>xalanMod is taken from the $linenumbering.everyNth parameter.</p>
+   *
+   * <p>Every line number will be right justified in a string xalanWidth
+   * characters long. If the line number of the last line in the
+   * environment is too long to fit in the specified width, the width
+   * is automatically increased to the smallest value that can hold the
+   * number of the last line. (In other words, if you specify the value 2
+   * and attempt to enumerate the lines of an environment that is 100 lines
+   * long, the value 3 will automatically be used for every line in the
+   * environment.)</p>
+   *
+   * <p>xalanWidth is taken from the $linenumbering.width parameter.</p>
+   *
+   * <p>The xalanSep string is inserted between the line
+   * number and the original program listing. Lines that aren't numbered
+   * are preceded by a xalanWidth blank string and the separator.</p>
+   *
+   * <p>xalanSep is taken from the $linenumbering.separator parameter.</p>
+   *
+   * <p>If inline markup extends across line breaks, markup changes are
+   * required. All the open elements are closed before the line break and
+   * "reopened" afterwards. The reopened elements will have the same
+   * attributes as the originals, except that 'name' and 'id' attributes
+   * are not duplicated.</p>
+   *
+   * @param context
+   * @param xalanNI
+   *
+   * @return The modified result tree fragment.
+   */
+  public DocumentFragment numberLines (ExpressionContext context,
+                                      NodeIterator xalanNI) {
+
+    int xalanMod = Params.getInt(context, "linenumbering.everyNth");
+    int xalanWidth = Params.getInt(context, "linenumbering.width");
+    String xalanSep = Params.getString(context, "linenumbering.separator");
+
+    DocumentFragment xalanRTF = (DocumentFragment) xalanNI.nextNode();
+    int numLines = countLineBreaks(xalanRTF) + 1;
+
+    DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
+    DocumentBuilder docBuilder = null;
+
+    try {
+      docBuilder = docFactory.newDocumentBuilder();
+    } catch (ParserConfigurationException e) {
+      System.out.println("PCE!");
+      return xalanRTF;
+    }
+    Document doc = docBuilder.newDocument();
+    DocumentFragment df = doc.createDocumentFragment();
+    DOMBuilder db = new DOMBuilder(doc, df);
+
+    elementStack = new Stack();
+    lineNumber = 0;
+    modulus = numLines < xalanMod ? 1 : xalanMod;
+    width = xalanWidth;
+    separator = xalanSep;
+
+    double log10numLines = Math.log(numLines) / Math.log(10);
+
+    if (width < log10numLines + 1) {
+      width = (int) Math.floor(log10numLines + 1);
+    }
+
+    lineNumberFragment(db, xalanRTF);
+    return df;
+  }
+
+  /**
+   * <p>Count the number of lines in a verbatim environment.</p>
+   *
+   * <p>This method walks over the nodes of a DocumentFragment and
+   * returns the number of lines breaks that it contains.</p>
+   *
+   * @param node The root of the tree walk over.
+   */
+  private int countLineBreaks(Node node) {
+    int numLines = 0;
+
+    if (node.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE
+       || node.getNodeType() == Node.DOCUMENT_NODE
+       || node.getNodeType() == Node.ELEMENT_NODE) {
+      Node child = node.getFirstChild();
+      while (child != null) {
+       numLines += countLineBreaks(child);
+       child = child.getNextSibling();
+      }
+    } else if (node.getNodeType() == Node.TEXT_NODE) {
+      String text = node.getNodeValue();
+
+      // Walk through the text node looking for newlines
+      int pos = 0;
+      for (int count = 0; count < text.length(); count++) {
+       if (text.charAt(count) == '\n') {
+         numLines++;
+       }
+      }
+    } else {
+      // nop
+    }
+
+    return numLines;
+  }
+
+  /**
+   * <p>Build a DocumentFragment with numbered lines.</p>
+   *
+   * <p>This is the method that actually does the work of numbering
+   * lines in a verbatim environment. It recursively walks through a
+   * tree of nodes, copying the structure into the rtf. Text nodes
+   * are examined for new lines and modified as requested by the
+   * global line numbering parameters.</p>
+   *
+   * <p>When called, rtf should be an empty DocumentFragment and node
+   * should be the first child of the result tree fragment that contains
+   * the existing, formatted verbatim text.</p>
+   *
+   * @param rtf The resulting verbatim environment with numbered lines.
+   * @param node The root of the tree to copy.
+   */
+  private void lineNumberFragment(DOMBuilder rtf,
+                                 Node node) {
+    try {
+      if (node.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE
+         || node.getNodeType() == Node.DOCUMENT_NODE) {
+       Node child = node.getFirstChild();
+       while (child != null) {
+         lineNumberFragment(rtf, child);
+         child = child.getNextSibling();
+       }
+      } else if (node.getNodeType() == Node.ELEMENT_NODE) {
+       String ns = node.getNamespaceURI();
+       String localName = node.getLocalName();
+       String name = ((Element) node).getTagName();
+
+       rtf.startElement(ns, localName, name,
+                        copyAttributes((Element) node));
+
+       elementStack.push(node);
+
+       Node child = node.getFirstChild();
+       while (child != null) {
+         lineNumberFragment(rtf, child);
+         child = child.getNextSibling();
+       }
+      } else if (node.getNodeType() == Node.TEXT_NODE) {
+       String text = node.getNodeValue();
+
+       if (lineNumber == 0) {
+         // The first line is always numbered
+         formatLineNumber(rtf, ++lineNumber);
+       }
+
+       // Walk through the text node looking for newlines
+       char chars[] = text.toCharArray();
+       int pos = 0;
+       for (int count = 0; count < text.length(); count++) {
+         if (text.charAt(count) == '\n') {
+           // This is the tricky bit; if we find a newline, make sure
+           // it doesn't occur inside any markup.
+
+           if (pos > 0) {
+             rtf.characters(chars, 0, pos);
+             pos = 0;
+           }
+
+           closeOpenElements(rtf);
+
+           // Copy the newline to the output
+           chars[pos++] = text.charAt(count);
+           rtf.characters(chars, 0, pos);
+           pos = 0;
+
+           // Add the line number
+           formatLineNumber(rtf, ++lineNumber);
+
+           openClosedElements(rtf);
+         } else {
+           chars[pos++] = text.charAt(count);
+         }
+       }
+
+       if (pos > 0) {
+         rtf.characters(chars, 0, pos);
+       }
+      } else if (node.getNodeType() == Node.COMMENT_NODE) {
+       String text = node.getNodeValue();
+       char chars[] = text.toCharArray();
+       rtf.comment(chars, 0, text.length());
+      } else if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) {
+       rtf.processingInstruction(node.getNodeName(), node.getNodeValue());
+      } else {
+       System.out.println("Warning: unexpected node type in lineNumberFragment");
+      }
+
+      if (node.getNodeType() == Node.ELEMENT_NODE) {
+       String ns = node.getNamespaceURI();
+       String localName = node.getLocalName();
+       String name = ((Element) node).getTagName();
+       rtf.endElement(ns, localName, name);
+       elementStack.pop();
+      }
+    } catch (SAXException e) {
+      System.out.println("SAX Exception in lineNumberFragment");
+    }
+  }
+
+  /**
+   * <p>Add a formatted line number to the result tree fragment.</p>
+   *
+   * <p>This method examines the global parameters that control line
+   * number presentation (modulus, width, and separator) and adds
+   * the appropriate text to the result tree fragment.</p>
+   *
+   * @param rtf The resulting verbatim environment with numbered lines.
+   * @param lineNumber The number of the current line.
+   */
+  private void formatLineNumber(DOMBuilder rtf,
+                               int lineNumber) {
+    char ch = 160;
+    String lno = "";
+    if (lineNumber == 1
+       || (modulus >= 1 && (lineNumber % modulus == 0))) {
+      lno = "" + lineNumber;
+    }
+
+    while (lno.length() < width) {
+      lno = ch + lno;
+    }
+
+    lno += separator;
+
+    char chars[] = lno.toCharArray();
+    try {
+      rtf.characters(chars, 0, lno.length());
+    } catch (SAXException e) {
+      System.out.println("SAX Exception in formatLineNumber");
+    }
+  }
+
+  /**
+   * <p>Insert text callouts into a verbatim environment.</p>
+   *
+   * <p>This method examines the <tt>areaset</tt> and <tt>area</tt> elements
+   * in the supplied <tt>areaspec</tt> and decorates the supplied
+   * result tree fragment with appropriate callout markers.</p>
+   *
+   * <p>If a <tt>label</tt> attribute is supplied on an <tt>area</tt>,
+   * its content will be used for the label, otherwise the callout
+   * number will be used, surrounded by parenthesis. Callouts are
+   * numbered in document order. All of the <tt>area</tt>s in an
+   * <tt>areaset</tt> get the same number.</p>
+   *
+   * <p>Only the <tt>linecolumn</tt> and <tt>linerange</tt> units are
+   * supported. If no unit is specifed, <tt>linecolumn</tt> is assumed.
+   * If only a line is specified, the callout decoration appears in
+   * the defaultColumn. Lines will be padded with blanks to reach the
+   * necessary column, but callouts that are located beyond the last
+   * line of the verbatim environment will be ignored.</p>
+   *
+   * <p>Callouts are inserted before the character at the line/column
+   * where they are to occur.</p>
+   *
+   * @param areaspecNodeSet The source node set that contains the areaspec.
+   * @param xalanRTF The result tree fragment of the verbatim environment.
+   * @param defaultColumn The column for callouts that specify only a line.
+   *
+   * @return The modified result tree fragment.  */
+
+  /**
+   * <p>Insert graphical callouts into a verbatim environment.</p>
+   *
+   * <p>This method examines the <tt>areaset</tt> and <tt>area</tt> elements
+   * in the supplied <tt>areaspec</tt> and decorates the supplied
+   * result tree fragment with appropriate callout markers.</p>
+   *
+   * <p>If a <tt>label</tt> attribute is supplied on an <tt>area</tt>,
+   * its content will be used for the label, otherwise the callout
+   * number will be used. Callouts are
+   * numbered in document order. All of the <tt>area</tt>s in an
+   * <tt>areaset</tt> get the same number.</p>
+   *
+   * <p>If the callout number is not greater than <tt>gMax</tt>, the
+   * callout generated will be:</p>
+   *
+   * <pre>
+   * &lt;img src="$gPath/conumber$gExt" alt="conumber">
+   * </pre>
+   *
+   * <p>Otherwise, it will be the callout number surrounded by
+   * parenthesis.</p>
+   *
+   * <p>Only the <tt>linecolumn</tt> and <tt>linerange</tt> units are
+   * supported. If no unit is specifed, <tt>linecolumn</tt> is assumed.
+   * If only a line is specified, the callout decoration appears in
+   * the defaultColumn. Lines will be padded with blanks to reach the
+   * necessary column, but callouts that are located beyond the last
+   * line of the verbatim environment will be ignored.</p>
+   *
+   * <p>Callouts are inserted before the character at the line/column
+   * where they are to occur.</p>
+   *
+   * @param context
+   * @param areaspecNodeSet The source node set that contains the areaspec.
+   * @param xalanNI
+   *
+   * @return The modified result tree fragment.
+   */
+  public DocumentFragment insertCallouts (ExpressionContext context,
+                                         NodeIterator areaspecNodeSet,
+                                         NodeIterator xalanNI) {
+    String type = Params.getString(context, "stylesheet.result.type");
+    boolean useFO = type.equals("fo");
+    int defaultColumn = Params.getInt(context, "callout.defaultcolumn");
+
+    if (Params.getBoolean(context, "callout.graphics")) {
+      String gPath = Params.getString(context, "callout.graphics.path");
+      String gExt = Params.getString(context, "callout.graphics.extension");
+      int gMax = Params.getInt(context, "callout.graphics.number.limit");
+      return insertGraphicCallouts(areaspecNodeSet, xalanNI, defaultColumn,
+                                  gPath, gExt, gMax, useFO);
+    } else if (Params.getBoolean(context, "callout.unicode")) {
+      int uStart = Params.getInt(context, "callout.unicode.start.character");
+      int uMax = Params.getInt(context, "callout.unicode.number.limit");
+      String uFont = Params.getString(context, "callout.unicode.font");
+      return insertUnicodeCallouts(areaspecNodeSet, xalanNI, defaultColumn,
+                                  uFont, uStart, uMax, useFO);
+    } else if (Params.getBoolean(context, "callout.dingbats")) {
+      int dMax = 10;
+      return insertDingbatCallouts(areaspecNodeSet, xalanNI, defaultColumn,
+                                  dMax, useFO);
+    } else {
+      return insertTextCallouts(areaspecNodeSet, xalanNI, defaultColumn, useFO);
+    }
+  }
+
+  public DocumentFragment insertGraphicCallouts (NodeIterator areaspecNodeSet,
+                                                NodeIterator xalanNI,
+                                                int defaultColumn,
+                                                String gPath,
+                                                String gExt,
+                                                int gMax,
+                                                boolean useFO) {
+    FormatGraphicCallout fgc = new FormatGraphicCallout(gPath,gExt,gMax,useFO);
+    return insertCallouts(areaspecNodeSet, xalanNI, defaultColumn, fgc);
+  }
+
+  public DocumentFragment insertUnicodeCallouts (NodeIterator areaspecNodeSet,
+                                                NodeIterator xalanNI,
+                                                int defaultColumn,
+                                                String uFont,
+                                                int uStart,
+                                                int uMax,
+                                                boolean useFO) {
+    FormatUnicodeCallout fuc = new FormatUnicodeCallout(uFont, uStart, uMax, useFO);
+    return insertCallouts(areaspecNodeSet, xalanNI, defaultColumn, fuc);
+  }
+
+  public DocumentFragment insertDingbatCallouts (NodeIterator areaspecNodeSet,
+                                                NodeIterator xalanNI,
+                                                int defaultColumn,
+                                                int gMax,
+                                                boolean useFO) {
+    FormatDingbatCallout fdc = new FormatDingbatCallout(gMax,useFO);
+    return insertCallouts(areaspecNodeSet, xalanNI, defaultColumn, fdc);
+  }
+
+  public DocumentFragment insertTextCallouts (NodeIterator areaspecNodeSet,
+                                             NodeIterator xalanNI,
+                                             int defaultColumn,
+                                             boolean useFO) {
+    FormatTextCallout ftc = new FormatTextCallout(useFO);
+    return insertCallouts(areaspecNodeSet, xalanNI, defaultColumn, ftc);
+  }
+
+  public DocumentFragment insertCallouts (NodeIterator areaspecNodeSet,
+                                         NodeIterator xalanNI,
+                                         int defaultColumn,
+                                         FormatCallout fCallout) {
+
+    DocumentFragment xalanRTF = (DocumentFragment) xalanNI.nextNode();
+
+    callout = new Callout[10];
+    calloutCount = 0;
+    calloutPos = 0;
+    lineNumber = 1;
+    colNumber = 1;
+
+    // First we walk through the areaspec to calculate the position
+    // of the callouts
+    //  <areaspec>
+    //  <areaset id="ex.plco.const" coords="">
+    //    <area id="ex.plco.c1" coords="4"/>
+    //    <area id="ex.plco.c2" coords="8"/>
+    //  </areaset>
+    //  <area id="ex.plco.ret" coords="12"/>
+    //  <area id="ex.plco.dest" coords="12"/>
+    //  </areaspec>
+    int pos = 0;
+    int coNum = 0;
+    boolean inAreaSet = false;
+    Node node = areaspecNodeSet.nextNode();
+    node = node.getFirstChild();
+    while (node != null) {
+      if (node.getNodeType() == Node.ELEMENT_NODE) {
+       if (node.getNodeName().equals("areaset")) {
+         coNum++;
+         Node area = node.getFirstChild();
+         while (area != null) {
+           if (area.getNodeType() == Node.ELEMENT_NODE) {
+             if (area.getNodeName().equals("area")) {
+               addCallout(coNum, area, defaultColumn);
+             } else {
+               System.out.println("Unexpected element in areaset: "
+                                  + area.getNodeName());
+             }
+           }
+           area = area.getNextSibling();
+         }
+       } else if (node.getNodeName().equalsIgnoreCase("area")) {
+         coNum++;
+         addCallout(coNum, node, defaultColumn);
+       } else {
+         System.out.println("Unexpected element in areaspec: "
+                            + node.getNodeName());
+       }
+      }
+
+      node = node.getNextSibling();
+    }
+
+    // Now sort them
+    java.util.Arrays.sort(callout, 0, calloutCount);
+
+    DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
+    DocumentBuilder docBuilder = null;
+
+    try {
+      docBuilder = docFactory.newDocumentBuilder();
+    } catch (ParserConfigurationException e) {
+      System.out.println("PCE 2!");
+      return xalanRTF;
+    }
+    Document doc = docBuilder.newDocument();
+    DocumentFragment df = doc.createDocumentFragment();
+    DOMBuilder db = new DOMBuilder(doc, df);
+
+    elementStack = new Stack();
+    calloutFragment(db, xalanRTF, fCallout);
+    return df;
+  }
+
+  /**
+   * <p>Build a FragmentValue with callout decorations.</p>
+   *
+   * <p>This is the method that actually does the work of adding
+   * callouts to a verbatim environment. It recursively walks through a
+   * tree of nodes, copying the structure into the rtf. Text nodes
+   * are examined for the position of callouts as described by the
+   * global callout parameters.</p>
+   *
+   * <p>When called, rtf should be an empty FragmentValue and node
+   * should be the first child of the result tree fragment that contains
+   * the existing, formatted verbatim text.</p>
+   *
+   * @param rtf The resulting verbatim environment with numbered lines.
+   * @param node The root of the tree to copy.
+   */
+  private void calloutFragment(DOMBuilder rtf,
+                              Node node,
+                              FormatCallout fCallout) {
+    try {
+      if (node.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE
+       || node.getNodeType() == Node.DOCUMENT_NODE) {
+       Node child = node.getFirstChild();
+       while (child != null) {
+         calloutFragment(rtf, child, fCallout);
+         child = child.getNextSibling();
+       }
+      } else if (node.getNodeType() == Node.ELEMENT_NODE) {
+       String ns = node.getNamespaceURI();
+       String localName = node.getLocalName();
+       String name = ((Element) node).getTagName();
+
+       rtf.startElement(ns, localName, name,
+                        copyAttributes((Element) node));
+
+       elementStack.push(node);
+
+       Node child = node.getFirstChild();
+       while (child != null) {
+         calloutFragment(rtf, child, fCallout);
+         child = child.getNextSibling();
+       }
+      } else if (node.getNodeType() == Node.TEXT_NODE) {
+       String text = node.getNodeValue();
+
+       char chars[] = text.toCharArray();
+       int pos = 0;
+       for (int count = 0; count < text.length(); count++) {
+         if (calloutPos < calloutCount
+             && callout[calloutPos].getLine() == lineNumber
+             && callout[calloutPos].getColumn() == colNumber) {
+            
+           if (pos > 0) {
+             rtf.characters(chars, 0, pos);
+             pos = 0;
+           }
+
+           closeOpenElements(rtf);
+
+           while (calloutPos < calloutCount
+                  && callout[calloutPos].getLine() == lineNumber
+                  && callout[calloutPos].getColumn() == colNumber) {
+             fCallout.formatCallout(rtf, callout[calloutPos]);
+             calloutPos++;
+           }
+
+           openClosedElements(rtf);
+         }
+
+         if (text.charAt(count) == '\n') {
+           // What if we need to pad this line?
+           if (calloutPos < calloutCount
+               && callout[calloutPos].getLine() == lineNumber
+               && callout[calloutPos].getColumn() > colNumber) {
+                
+             if (pos > 0) {
+               rtf.characters(chars, 0, pos);
+               pos = 0;
+             }
+
+             closeOpenElements(rtf);
+
+             while (calloutPos < calloutCount
+                    && callout[calloutPos].getLine() == lineNumber
+                    && callout[calloutPos].getColumn() > colNumber) {
+               formatPad(rtf, callout[calloutPos].getColumn() - colNumber);
+               colNumber = callout[calloutPos].getColumn();
+               while (calloutPos < calloutCount
+                      && callout[calloutPos].getLine() == lineNumber
+                      && callout[calloutPos].getColumn() == colNumber) {
+                 fCallout.formatCallout(rtf, callout[calloutPos]);
+                 calloutPos++;
+               }
+             }
+
+             openClosedElements(rtf);
+           }
+
+           lineNumber++;
+           colNumber = 1;
+         } else {
+           colNumber++;
+         }
+         chars[pos++] = text.charAt(count);
+       }
+
+       if (pos > 0) {
+         rtf.characters(chars, 0, pos);
+       }
+      } else if (node.getNodeType() == Node.COMMENT_NODE) {
+       String text = node.getNodeValue();
+       char chars[] = text.toCharArray();
+       rtf.comment(chars, 0, text.length());
+      } else if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) {
+       rtf.processingInstruction(node.getNodeName(), node.getNodeValue());
+      } else {
+       System.out.println("Warning: unexpected node type in calloutFragment: " + node.getNodeType() + ": " + node.getNodeName());
+      }
+
+      if (node.getNodeType() == Node.ELEMENT_NODE) {
+       String ns = node.getNamespaceURI();
+       String localName = node.getLocalName();
+       String name = ((Element) node).getTagName();
+       rtf.endElement(ns, localName, name);
+       elementStack.pop();
+      } else {
+       // nop
+      }
+    } catch (SAXException e) {
+      System.out.println("SAX Exception in calloutFragment");
+    }
+  }
+
+  /**
+   * <p>Add a callout to the global callout array</p>
+   *
+   * <p>This method examines a callout <tt>area</tt> and adds it to
+   * the global callout array if it can be interpreted.</p>
+   *
+   * <p>Only the <tt>linecolumn</tt> and <tt>linerange</tt> units are
+   * supported. If no unit is specifed, <tt>linecolumn</tt> is assumed.
+   * If only a line is specified, the callout decoration appears in
+   * the <tt>defaultColumn</tt>.</p>
+   *
+   * @param coNum The callout number.
+   * @param node The <tt>area</tt>.
+   * @param defaultColumn The default column for callouts.
+   */
+  private void addCallout (int coNum,
+                          Node node,
+                          int defaultColumn) {
+    Element area = (Element) node;
+
+    String units = area.getAttribute("units");
+    String otherUnits = area.getAttribute("otherunits");
+    String coords = area.getAttribute("coords");
+    int type = 0;
+    String otherType = null;
+    
+    if ("".equals(units) || units.equals("linecolumn")) {
+      type = Callout.LINE_COLUMN; // the default
+    } else if (units.equals("linerange")) {
+      type = Callout.LINE_RANGE;
+    } else if (units.equals("linecolumnpair")) {
+      type = Callout.LINE_COLUMN_PAIR;
+    } else if (units.equals("calspair")) {
+      type = Callout.CALS_PAIR;
+    } else {
+      type = Callout.OTHER;
+      otherType = otherUnits;
+    }
+
+    if (type != Callout.LINE_COLUMN
+       && type != Callout.LINE_RANGE) {
+      System.out.println("Only linecolumn and linerange units are supported");
+      return;
+    }
+
+    if (coords == null) {
+      System.out.println("Coords must be specified");
+      return;
+    }
+
+    // Now let's see if we can interpret the coordinates...
+    StringTokenizer st = new StringTokenizer(coords);
+    int tokenCount = 0;
+    int c1 = 0;
+    int c2 = 0;
+    while (st.hasMoreTokens()) {
+      tokenCount++;
+      if (tokenCount > 2) {
+       System.out.println("Unparseable coordinates");
+       return;
+      }
+      try {
+       String token = st.nextToken();
+       int coord = Integer.parseInt(token);
+       c2 = coord;
+       if (tokenCount == 1) {
+         c1 = coord;
+       }
+      } catch (NumberFormatException e) {
+       System.out.println("Unparseable coordinate");
+       return;
+      }
+    }
+
+    // Make sure we aren't going to blow past the end of our array
+    if (calloutCount == callout.length) {
+      Callout bigger[] = new Callout[calloutCount+10];
+      for (int count = 0; count < callout.length; count++) {
+       bigger[count] = callout[count];
+      }
+      callout = bigger;
+    }
+
+    // Ok, add the callout
+    if (tokenCount == 2) {
+      if (type == Callout.LINE_RANGE) {
+       for (int count = c1; count <= c2; count++) {
+         callout[calloutCount++] = new Callout(coNum, area,
+                                               count, defaultColumn,
+                                               type);
+       }
+      } else {
+       // assume linecolumn
+       callout[calloutCount++] = new Callout(coNum, area, c1, c2, type);
+      }
+    } else {
+      // if there's only one number, assume it's the line
+      callout[calloutCount++] = new Callout(coNum, area, c1, defaultColumn, type);
+    }
+  }
+
+  /**
+   * <p>Add blanks to the result tree fragment.</p>
+   *
+   * <p>This method adds <tt>numBlanks</tt> to the result tree fragment.
+   * It's used to pad lines when callouts occur after the last existing
+   * characater in a line.</p>
+   *
+   * @param rtf The resulting verbatim environment with numbered lines.
+   * @param numBlanks The number of blanks to add.
+   */
+  private void formatPad(DOMBuilder rtf,
+                        int numBlanks) {
+    char chars[] = new char[numBlanks];
+    for (int count = 0; count < numBlanks; count++) {
+      chars[count] = ' ';
+    }
+
+    try {
+      rtf.characters(chars, 0, numBlanks);
+    } catch (SAXException e) {
+      System.out.println("SAX Exception in formatCallout");
+    }
+  }
+
+  private void closeOpenElements(DOMBuilder rtf)
+    throws SAXException {
+    // Close all the open elements...
+    tempStack = new Stack();
+    while (!elementStack.empty()) {
+      Node elem = (Node) elementStack.pop();
+
+      String ns = elem.getNamespaceURI();
+      String localName = elem.getLocalName();
+      String name = ((Element) elem).getTagName();
+
+      // If this is the bottom of the stack and it's an fo:block
+      // or an HTML pre or div, don't duplicate it...
+      if (elementStack.empty()
+         && (((ns != null)
+              && ns.equals(foURI)
+              && localName.equals("block"))
+             || (((ns == null)
+                  && localName.equalsIgnoreCase("pre"))
+                 || ((ns != null)
+                     && ns.equals(xhURI)
+                     && localName.equals("pre")))
+             || (((ns == null)
+                  && localName.equalsIgnoreCase("div"))
+                 || ((ns != null)
+                     && ns.equals(xhURI)
+                     && localName.equals("div"))))) {
+       elementStack.push(elem);
+       break;
+      } else {
+       rtf.endElement(ns, localName, name);
+       tempStack.push(elem);
+      }
+    }
+  }
+
+  private void openClosedElements(DOMBuilder rtf)
+    throws SAXException {
+    // Now "reopen" the elements that we closed...
+    while (!tempStack.empty()) {
+      Node elem = (Node) tempStack.pop();
+
+      String ns = elem.getNamespaceURI();
+      String localName = elem.getLocalName();
+      String name = ((Element) elem).getTagName();
+      NamedNodeMap domAttr = elem.getAttributes();
+
+      AttributesImpl attr = new AttributesImpl();
+      for (int acount = 0; acount < domAttr.getLength(); acount++) {
+       Node a = domAttr.item(acount);
+
+       if (((ns == null || ns == "http://www.w3.org/1999/xhtml")
+            && localName.equalsIgnoreCase("a"))
+           || (a.getLocalName().equalsIgnoreCase("id"))) {
+         // skip this attribute
+       } else {
+         attr.addAttribute(a.getNamespaceURI(),
+                           a.getLocalName(),
+                           a.getNodeName(),
+                           "CDATA",
+                           a.getNodeValue());
+       }
+      }
+
+      rtf.startElement(ns, localName, name, attr);
+      elementStack.push(elem);
+    }
+
+    tempStack = null;
+  }
+
+  private Attributes copyAttributes(Element node) {
+    AttributesImpl attrs = new AttributesImpl();
+    NamedNodeMap nnm = node.getAttributes();
+    for (int count = 0; count < nnm.getLength(); count++) {
+      Attr attr = (Attr) nnm.item(count);
+      String name = attr.getName();
+      if (name.startsWith("xmlns:") || name.equals("xmlns")) {
+       // Skip it; (don't ya just love it!!)
+      } else {
+       attrs.addAttribute(attr.getNamespaceURI(), attr.getName(),
+                          attr.getName(), "CDATA", attr.getValue());
+      }
+    }
+    return attrs;
+  }
+}
diff --git a/xsl-java/xsltproc/python/.cvsignore b/xsl-java/xsltproc/python/.cvsignore
new file mode 100644 (file)
index 0000000..68e5bd5
--- /dev/null
@@ -0,0 +1,2 @@
+cwtest.xml cwtest.xsl
+*.pyc
diff --git a/xsl-java/xsltproc/python/README b/xsl-java/xsltproc/python/README
new file mode 100644 (file)
index 0000000..a7cd356
--- /dev/null
@@ -0,0 +1,3 @@
+THIS IS A WORK IN PROGRESS
+
+IN PARTICULAR: IT DOES NOT WORK NOW!
diff --git a/xsl-java/xsltproc/python/docbook.py b/xsl-java/xsltproc/python/docbook.py
new file mode 100644 (file)
index 0000000..f82e07a
--- /dev/null
@@ -0,0 +1,240 @@
+#!/usr/bin/python -u
+
+# THIS IS ALPHA CODE AND IS NOT COMPLETE!
+
+import sys
+import string
+import libxml2
+import libxslt
+import re
+import math
+
+# Some globals
+pixelsPerInch = 96.0
+unitHash = { 'in': pixelsPerInch,
+             'cm': pixelsPerInch / 2.54,
+             'mm': pixelsPerInch / 25.4,
+             'pc': (pixelsPerInch / 72.0) * 12,
+             'pt': pixelsPerInch / 72.0,
+             'px': 1 }
+
+# ======================================================================
+
+def adjustColumnWidths(ctx, nodeset):
+    #
+    # Small check to verify the context is correcly accessed
+    #
+    try:
+        pctxt = libxslt.xpathParserContext(_obj=ctx)
+        ctxt = pctxt.context()
+        tctxt = ctxt.transformContext()
+    except:
+        pass
+
+    # Get the nominal table width
+    varString = lookupVariable(tctxt, "nominal.table.width", None);
+    if varString == None:
+        nominalWidth = 6 * pixelsPerInch;
+    else:
+        nominalWidth = convertLength(varString);
+
+    # Get the requested table width
+    tableWidth = lookupVariable(tctxt, "table.width", "100%")
+
+    foStylesheet = (tctxt.variableLookup("stylesheet.result.type", None) == "fo");
+
+    relTotal = 0
+    relParts = []
+
+    absTotal = 0
+    absParts = []
+
+    colgroup = libxml2.xmlNode(_obj = nodeset[0])
+    # If this is an foStylesheet, we've been passed a list of fo:table-columns.
+    # Otherwise we've been passed a colgroup that contains a list of cols.
+    if foStylesheet:
+        colChildren = colgroup
+    else:
+        colChildren = colgroup.children
+
+    col = colChildren
+    while col != None:
+        if foStylesheet:
+            width = col.prop("column-width")
+        else:
+            width = col.prop("width")
+
+        if width == None:
+            width = "1*"
+
+        relPart = 0.0;
+        absPart = 0.0;
+        starPos = string.find(width, "*");
+        if starPos >= 0:
+            relPart, absPart = string.split(width, "*", 2)
+            relPart = float(relPart);
+            relTotal = relTotal + float(relPart)
+        else:
+            absPart = width
+
+        pixels = convertLength(absPart);
+        absTotal = absTotal + pixels;
+
+        relParts.append(relPart);
+        absParts.append(pixels);
+
+        col = col.next
+
+    # Ok, now we have the relative widths and absolute widths in
+    # two parallel arrays.
+    #
+    # - If there are no relative widths, output the absolute widths
+    # - If there are no absolute widths, output the relative widths
+    # - If there are a mixture of relative and absolute widths,
+    #   - If the table width is absolute, turn these all into absolute
+    #     widths.
+    #   - If the table width is relative, turn these all into absolute
+    #     widths in the nominalWidth and then turn them back into
+    #     percentages.
+
+    widths = [];
+
+    if relTotal == 0:
+        for absPart in absParts:
+            if foStylesheet:
+                inches = absPart / pixelsPerInch
+                widths.append("%4.2fin" % inches)
+            else:
+                widths.append("%d" % absPart)
+    elif absTotal == 0:
+        for relPart in relParts:
+            rel = relPart / relTotal * 100;
+            widths.append(rel);
+        widths = correctRoundingError(widths)
+    else:
+        pixelWidth = nominalWidth;
+        if string.find(tableWidth, "%") < 0:
+            pixelWidth = convertLength(tableWidth);
+
+        if pixelWidth <= absTotal:
+            print "Table is wider than table width"
+        else:
+            pixelWidth = pixelWidth - absTotal;
+
+        absTotal = 0
+        for count in range(len(relParts)):
+            rel = relParts[count] / relTotal * pixelWidth
+            relParts[count] = rel + absParts[count]
+            absTotal = absTotal + rel + absParts[count]
+
+        if string.find(tableWidth, "%") < 0:
+            for count in range(len(relParts)):
+                if foStylesheet:
+                    pixels = relParts[count]
+                    inches = pixels / pixelsPerInch
+                    widths.append("%4.2fin" % inches)
+                else:
+                    widths.append(relParts[count])
+        else:
+            for count in range(len(relParts)):
+                rel = relParts[count] / absTotal * 100
+                widths.append(rel)
+            widths = correctRoundingError(widths)
+
+    # Danger, Will Robinson! In-place modification of the result tree!
+    # Side-effect free? We don' need no steenkin' side-effect free!
+    count = 0
+    col = colChildren
+    while col != None:
+        if foStylesheet:
+            col.setProp("column-width", widths[count])
+        else:
+            col.setProp("width", widths[count])
+
+        count = count+1
+        col = col.next
+
+    return nodeset
+
+def convertLength(length):
+    # Given "3.4in" return the width in pixels
+    global pixelsPerInch
+    global unitHash
+
+    m = re.search('([+-]?[\d\.]+)(\S+)', length)
+    if m != None and m.lastindex > 1:
+        unit = pixelsPerInch;
+        if unitHash.has_key(m.group(2)):
+            unit = unitHash[m.group(2)];
+        else:
+            print "Unrecognized length: " + m.group(2)
+
+        pixels = unit * float(m.group(1))
+    else:
+        pixels = 0
+
+    return pixels
+
+def correctRoundingError(floatWidths):
+    # The widths are currently floating point numbers, we have to truncate
+    # them back to integers and then distribute the error so that they sum
+    # to exactly 100%.
+
+    totalWidth = 0
+    widths = [];
+    for width in floatWidths:
+        width = math.floor(width)
+        widths.append(width)
+        totalWidth = totalWidth + math.floor(width)
+
+    totalError = 100 - totalWidth
+    columnError = totalError / len(widths)
+    error = 0
+    for count in range(len(widths)):
+        width = widths[count]
+        error = error + columnError
+        if error >= 1.0:
+            adj = math.floor(error);
+            error = error - adj;
+            widths[count] = "%d%%" % (width + adj)
+        else:
+            widths[count] = "%d%%" % width
+
+    return widths
+
+def lookupVariable(tctxt, varName, default):
+    varString = tctxt.variableLookup(varName, None);
+    if varString == None:
+        return default
+
+    # If it's a list, get the first element
+    if type(varString) == type([]):
+        varString = varString[0]
+
+    # If it's not a string, it must be a node, get its content
+    if type(varString) != type(""):
+        varString = varString.content
+
+    return varString
+
+# ======================================================================
+# Random notes...
+
+#once you have a node which is a libxml2 python xmlNode wrapper all common
+#operations are possible:
+#   .children .last .parent .next .prev .doc for navigation
+#   .content .type for introspection
+#   .prop("attribute_name") to lookup attribute values
+
+#    # Now make a nodeset to return
+#    # Danger, Will Robinson! This creates a memory leak!
+#    newDoc = libxml2.newDoc("1.0");
+#    newColGroup = newDoc.newDocNode(None, "colgroup", None);
+#    newDoc.addChild(newColGroup);
+#    col = colgroup.children
+#    while col != None:
+#        newCol = newDoc.newDocNode(None, "col", None);
+#        newCol.copyPropList(col);
+#        newCol.setProp("width", "4")
+#        newColGroup.addChild(newCol)
+#        col = col.next
diff --git a/xsl-java/xsltproc/python/xslt.py b/xsl-java/xsltproc/python/xslt.py
new file mode 100644 (file)
index 0000000..b593fa8
--- /dev/null
@@ -0,0 +1,77 @@
+#!/usr/bin/python -u
+
+# THIS IS ALPHA CODE AND MAY NOT WORK CORRECTLY!
+
+import sys
+import string
+import libxml2
+import libxslt
+from docbook import adjustColumnWidths
+
+# Check the arguments
+
+usage = "Usage: %s xmlfile.xml xslfile.xsl [outputfile] [param1=val [param2=val]...]" % sys.argv[0]
+
+xmlfile = None
+xslfile = None
+outfile = "-"
+params  = {}
+
+try:
+    xmlfile = sys.argv[1]
+    xslfile = sys.argv[2]
+except IndexError:
+    print usage;
+    sys.exit(1)
+
+try:
+    outfile = sys.argv[3]
+    if string.find(outfile, "=") > 0:
+        name, value = string.split(outfile, "=", 2);
+        params[name] = value
+
+    count = 4;
+    while (sys.argv[count]):
+        try:
+            name, value = string.split(sys.argv[count], "=", 2);
+            if params.has_key(name):
+                print "Warning: '%s' re-specified; replacing value" % name
+            params[name] = value
+        except ValueError:
+            print "Invalid parameter specification: '" + sys.argv[count] + "'"
+            print usage
+            sys.exit(1);
+        count = count+1;
+except IndexError:
+    pass
+
+# ======================================================================
+# Memory debug specific
+# libxml2.debugMemory(1)
+
+# Setup environment
+libxml2.lineNumbersDefault(1)
+libxml2.substituteEntitiesDefault(1)
+libxslt.registerExtModuleFunction("adjustColumnWidths",
+                                  "http://nwalsh.com/xslt/ext/xsltproc/python/Table",
+                                  adjustColumnWidths)
+
+# Initialize and run
+styledoc = libxml2.parseFile(xslfile)
+style = libxslt.parseStylesheetDoc(styledoc)
+doc = libxml2.parseFile(xmlfile)
+result = style.applyStylesheet(doc, params)
+
+# Save the result
+style.saveResultToFilename(outfile, result, 0)
+
+# Free things up
+style.freeStylesheet()
+doc.freeDoc()
+result.freeDoc()
+
+# Memory debug specific
+#libxslt.cleanup()
+#if libxml2.debugMemory(1) != 0:
+#    print "Memory leak %d bytes" % (libxml2.debugMemory(1))
+#    libxml2.dumpMemory()