From 9878de2658649833ff3616088b07bceed29f9db9 Mon Sep 17 00:00:00 2001 From: Norman Walsh Date: Sun, 24 Oct 2004 19:01:01 +0000 Subject: [PATCH] Initial checkin for (some) Saxon8 classes; they don't actually work yet because the current stylesheets don't work very well with Saxon 8 --- xsl/extensions/saxon8/.classes/Makefile | 8 + .../saxon8/.classes/Makefile.common | 38 + xsl/extensions/saxon8/.classes/Makefile.incl | 1 + xsl/extensions/saxon8/.classes/com/Makefile | 8 + .../saxon8/.classes/com/Makefile.incl | 1 + .../saxon8/.classes/com/nwalsh/Makefile | 8 + .../saxon8/.classes/com/nwalsh/Makefile.incl | 1 + .../.classes/com/nwalsh/saxon8/.cvsignore | 1 + .../.classes/com/nwalsh/saxon8/Makefile | 8 + .../.classes/com/nwalsh/saxon8/Makefile.incl | 4 + .../saxon8/com/nwalsh/saxon8/Callout.java | 90 ++ .../com/nwalsh/saxon8/ImageIntrinsics.java | 160 ++++ .../saxon8/com/nwalsh/saxon8/Verbatim.java | 853 ++++++++++++++++++ .../saxon8/com/nwalsh/saxon8/package.html | 48 + 14 files changed, 1229 insertions(+) create mode 100644 xsl/extensions/saxon8/.classes/Makefile create mode 100644 xsl/extensions/saxon8/.classes/Makefile.common create mode 100644 xsl/extensions/saxon8/.classes/Makefile.incl create mode 100644 xsl/extensions/saxon8/.classes/com/Makefile create mode 100644 xsl/extensions/saxon8/.classes/com/Makefile.incl create mode 100644 xsl/extensions/saxon8/.classes/com/nwalsh/Makefile create mode 100644 xsl/extensions/saxon8/.classes/com/nwalsh/Makefile.incl create mode 100644 xsl/extensions/saxon8/.classes/com/nwalsh/saxon8/.cvsignore create mode 100644 xsl/extensions/saxon8/.classes/com/nwalsh/saxon8/Makefile create mode 100644 xsl/extensions/saxon8/.classes/com/nwalsh/saxon8/Makefile.incl create mode 100644 xsl/extensions/saxon8/com/nwalsh/saxon8/Callout.java create mode 100644 xsl/extensions/saxon8/com/nwalsh/saxon8/ImageIntrinsics.java create mode 100644 xsl/extensions/saxon8/com/nwalsh/saxon8/Verbatim.java create mode 100644 xsl/extensions/saxon8/com/nwalsh/saxon8/package.html diff --git a/xsl/extensions/saxon8/.classes/Makefile b/xsl/extensions/saxon8/.classes/Makefile new file mode 100644 index 000000000..e19e7275c --- /dev/null +++ b/xsl/extensions/saxon8/.classes/Makefile @@ -0,0 +1,8 @@ +SOURCEPATH=.. +VPATH=$(SOURCEPATH)/ +CLASSBASE=$(SOURCEPATH)/.classes +SUBDIRS=__PHONY__ com + +include Makefile.incl +include ../.classes/Makefile.common + diff --git a/xsl/extensions/saxon8/.classes/Makefile.common b/xsl/extensions/saxon8/.classes/Makefile.common new file mode 100644 index 000000000..f36de6397 --- /dev/null +++ b/xsl/extensions/saxon8/.classes/Makefile.common @@ -0,0 +1,38 @@ +CLASSPATH=$(CLASSBASE):/usr/local/java/saxonb-8/saxon8.jar + +all: $(CLASSFILES) + -@for f in $(SUBDIRS); do \ + if [ "$$f" != "__PHONY__" ]; then \ + cd $$f; make all; cd ..; \ + fi \ + done + +clean: + rm -f *.class + -@for f in $(SUBDIRS); do \ + if [ "$$f" != "__PHONY__" ]; then \ + cd $$f; make clean; cd ..; \ + fi \ + done + +jar: all + jar cf ../../saxon8.jar `find com -name "*.class" -print` + +%.class : %.java + javac -classpath $(CLASSPATH) \ + -sourcepath $(SOURCEPATH) \ + -g -d $(CLASSBASE) \ + $< + +includes: + classlist $(VPATH) > Makefile.incl + -@for f in $(SUBDIRS); do \ + if [ "$$f" != "__PHONY__" ]; then \ + if [ -d "$$f" -a -f "$$f/Makefile" ]; then \ + cd $$f; make includes; cd ..; \ + else if [ ! -d "$$f" ]; then \ + mkdir $$f; \ + fi; \ + fi; \ + fi; \ + done diff --git a/xsl/extensions/saxon8/.classes/Makefile.incl b/xsl/extensions/saxon8/.classes/Makefile.incl new file mode 100644 index 000000000..db567cb78 --- /dev/null +++ b/xsl/extensions/saxon8/.classes/Makefile.incl @@ -0,0 +1 @@ +CLASSFILES= diff --git a/xsl/extensions/saxon8/.classes/com/Makefile b/xsl/extensions/saxon8/.classes/com/Makefile new file mode 100644 index 000000000..ce49ec1f0 --- /dev/null +++ b/xsl/extensions/saxon8/.classes/com/Makefile @@ -0,0 +1,8 @@ +SOURCEPATH=../.. +VPATH=$(SOURCEPATH)/com +CLASSBASE=$(SOURCEPATH)/.classes +SUBDIRS=__PHONY__ nwalsh + +include Makefile.incl +include ../../.classes/Makefile.common + diff --git a/xsl/extensions/saxon8/.classes/com/Makefile.incl b/xsl/extensions/saxon8/.classes/com/Makefile.incl new file mode 100644 index 000000000..db567cb78 --- /dev/null +++ b/xsl/extensions/saxon8/.classes/com/Makefile.incl @@ -0,0 +1 @@ +CLASSFILES= diff --git a/xsl/extensions/saxon8/.classes/com/nwalsh/Makefile b/xsl/extensions/saxon8/.classes/com/nwalsh/Makefile new file mode 100644 index 000000000..617fc1d3b --- /dev/null +++ b/xsl/extensions/saxon8/.classes/com/nwalsh/Makefile @@ -0,0 +1,8 @@ +SOURCEPATH=../../.. +VPATH=$(SOURCEPATH)/com/nwalsh +CLASSBASE=$(SOURCEPATH)/.classes +SUBDIRS=__PHONY__ saxon8 + +include Makefile.incl +include ../../../.classes/Makefile.common + diff --git a/xsl/extensions/saxon8/.classes/com/nwalsh/Makefile.incl b/xsl/extensions/saxon8/.classes/com/nwalsh/Makefile.incl new file mode 100644 index 000000000..db567cb78 --- /dev/null +++ b/xsl/extensions/saxon8/.classes/com/nwalsh/Makefile.incl @@ -0,0 +1 @@ +CLASSFILES= diff --git a/xsl/extensions/saxon8/.classes/com/nwalsh/saxon8/.cvsignore b/xsl/extensions/saxon8/.classes/com/nwalsh/saxon8/.cvsignore new file mode 100644 index 000000000..6b468b62a --- /dev/null +++ b/xsl/extensions/saxon8/.classes/com/nwalsh/saxon8/.cvsignore @@ -0,0 +1 @@ +*.class diff --git a/xsl/extensions/saxon8/.classes/com/nwalsh/saxon8/Makefile b/xsl/extensions/saxon8/.classes/com/nwalsh/saxon8/Makefile new file mode 100644 index 000000000..559e6672c --- /dev/null +++ b/xsl/extensions/saxon8/.classes/com/nwalsh/saxon8/Makefile @@ -0,0 +1,8 @@ +SOURCEPATH=../../../.. +VPATH=$(SOURCEPATH)/com/nwalsh/saxon8 +CLASSBASE=$(SOURCEPATH)/.classes +SUBDIRS=__PHONY__ + +include Makefile.incl +include ../../../../.classes/Makefile.common + diff --git a/xsl/extensions/saxon8/.classes/com/nwalsh/saxon8/Makefile.incl b/xsl/extensions/saxon8/.classes/com/nwalsh/saxon8/Makefile.incl new file mode 100644 index 000000000..a75f45be0 --- /dev/null +++ b/xsl/extensions/saxon8/.classes/com/nwalsh/saxon8/Makefile.incl @@ -0,0 +1,4 @@ +CLASSFILES=Callout.class \ +ImageIntrinsics.class \ +Verbatim.class + diff --git a/xsl/extensions/saxon8/com/nwalsh/saxon8/Callout.java b/xsl/extensions/saxon8/com/nwalsh/saxon8/Callout.java new file mode 100644 index 000000000..893cad522 --- /dev/null +++ b/xsl/extensions/saxon8/com/nwalsh/saxon8/Callout.java @@ -0,0 +1,90 @@ +package com.nwalsh.saxon8; + +import net.sf.saxon.om.NodeInfo; + +/** + *

A class for maintaining information about callouts.

+ * + *

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.)

+ * + *

This class is just the little record + * that we store in the array for each callout.

+ */ +public class Callout implements Comparable { + /** The callout number. */ + private int callout = 0; + /** The area Element item that generated this callout. */ + private NodeInfo 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, NodeInfo area, int line, int col) { + this.callout = callout; + this.area = area; + this.line = line; + this.col = col; + } + + /** + *

The compareTo method compares this Callout with another.

+ * + *

Given two Callouts, A and B, A < B if:

+ * + *
    + *
  1. A.line < B.line, or
  2. + *
  3. A.line = B.line && A.col < B.col, or
  4. + *
  5. A.line = B.line && A.col = B.col && A.callout < B.callout
  6. + *
  7. Otherwise, they're equal.
  8. + *
+ */ + 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 NodeInfo 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/extensions/saxon8/com/nwalsh/saxon8/ImageIntrinsics.java b/xsl/extensions/saxon8/com/nwalsh/saxon8/ImageIntrinsics.java new file mode 100644 index 000000000..b2543b19a --- /dev/null +++ b/xsl/extensions/saxon8/com/nwalsh/saxon8/ImageIntrinsics.java @@ -0,0 +1,160 @@ +package com.nwalsh.saxon8; + +import java.io.*; +import java.awt.*; +import java.awt.image.*; +import java.lang.Thread; +import java.util.StringTokenizer; + +/** + *

Saxon extension to examine intrinsic size of images

+ * + *

$Id$

+ * + *

Copyright (C) 2002 Norman Walsh.

+ * + *

This class provides a + * Saxon + * extension to find the intrinsic size of images.

+ * + *

Change Log:

+ *
+ *
1.0
+ *

Initial release.

+ *
+ * + * @author Norman Walsh + * ndw@nwalsh.com + * + * @version $Id$ + * + */ +public class ImageIntrinsics implements ImageObserver { + boolean imageLoaded = false; + boolean imageFailed = false; + Image image = null; + int width = -1; + int depth = -1; + + /** + *

Constructor for ImageIntrinsics

+ */ + public ImageIntrinsics(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(); + } + } else { + System.err.println("Failed to interpret image: " + imageFn); + } + } catch (Exception e) { + // nop; + } + + 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/extensions/saxon8/com/nwalsh/saxon8/Verbatim.java b/xsl/extensions/saxon8/com/nwalsh/saxon8/Verbatim.java new file mode 100644 index 000000000..680d73703 --- /dev/null +++ b/xsl/extensions/saxon8/com/nwalsh/saxon8/Verbatim.java @@ -0,0 +1,853 @@ +// Verbatim.java - Saxon extensions supporting DocBook verbatim environments + +package com.nwalsh.saxon8; + +import java.util.Vector; +import java.util.StringTokenizer; +import java.util.Collections; +import javax.xml.transform.TransformerException; + +import net.sf.saxon.Controller; +import net.sf.saxon.Configuration; +import net.sf.saxon.expr.XPathContext; +import net.sf.saxon.om.DocumentInfo; +import net.sf.saxon.om.Axis; +import net.sf.saxon.om.AxisIterator; +import net.sf.saxon.om.Item; +import net.sf.saxon.om.SequenceIterator; +import net.sf.saxon.om.NamePool; +import net.sf.saxon.om.NodeInfo; +import net.sf.saxon.type.Type; +import net.sf.saxon.tinytree.TinyBuilder; +import net.sf.saxon.event.NamespaceReducer; +import net.sf.saxon.event.Receiver; + +/** + *

Saxon extensions supporting DocBook verbatim environments

+ * + *

$Id$

+ * + *

Copyright (C) 2000 Norman Walsh.

+ * + *

This class provides a + * Saxon 8.x + * implementation of two features that would be impractical to + * implement directly in XSLT: line numbering and callouts.

+ * + *

Line Numbering

+ *

The numberLines method takes a tree + * (assumed to contain the contents of a formatted verbatim + * element in DocBook: programlisting, screen, address, literallayout, + * or synopsis) and returns a tree decorated with + * line numbers.

+ * + *

Callouts

+ *

The insertCallouts method takes an + * areaspec and a tree + * (assumed to contain the contents of a formatted verbatim + * element in DocBook: programlisting, screen, address, literallayout, + * or synopsis) and returns a tree decorated with + * callouts.

+ * + *

Change Log:

+ *
+ *
1.0
+ *

Initial release.

+ *
+ * + * @author Norman Walsh + * ndw@nwalsh.com + * + * @version $Id$ + * + */ +public class Verbatim { + private static final int CALLOUT_TEXT = 1; + private static final int CALLOUT_UNICODE = 2; + private static final int CALLOUT_GRAPHICS = 3; + + /** The modulus for line numbering (every 'modulus' line is numbered). */ + private static int modulus = 5; + /** The width (in characters) of line numbers (for padding). */ + private static int width = 0; + /** The current line number. */ + private static int lineNumber = 1; + /** The separator between the line number and the verbatim text. */ + private static String separator = " | "; + + /** The number of lines in the verbatim environment. */ + private static int lineCount = 0; + + /** The line number wrapper element. */ + private static NodeInfo wrapper = null; + + /** List of open elements. */ + private static Vector openElements = null; + + /** List of callouts. */ + private static Vector calloutList = null; + + /** The current column number. */ + private static int colNumber = 1; + + /** The default column for callouts that have only a line or line range */ + private static int defaultColumn = 60; + + /** The type of callout to use. */ + private static int calloutStyle = CALLOUT_TEXT; + + /** The prefix for text callouts. */ + private static String textPrefix = "("; + + /** The suffix for text callouts. */ + private static String textSuffix = ")"; + + /** The text callout wrapper element. */ + private static NodeInfo textWrapper = null; + + /** The starting code point for Unicode callouts. */ + private static int unicodeStart = 0; + + /** The highest Unicode callout available before falling back to text. */ + private static int unicodeMax = 10; + + /** The Unicode callout wrapper element. */ + private static NodeInfo unicodeWrapper = null; + + /** The highest graphical callout available before falling back to text. */ + private static int graphicsMax = 10; + + /** The Graphics callout wrapper element. */ + private static NodeInfo graphicsWrapper = null; + + /** + *

Constructor for Verbatim

+ * + *

All of the methods are static, so the constructor does nothing.

+ */ + public Verbatim() { + } + + /** + *

Number lines in a verbatim environment

+ * + *

The extension function expects the following variables to be + * available in the calling context: $linenumbering.everyNth, + * $linenumbering.width, $linenumbering.separator, and + * $stylesheet.result.type.

+ * + *

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.

+ * + *

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.)

+ * + *

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.

+ * + *

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".

+ * + * @param rtf The result tree fragment of the verbatim environment. + * + * @return The modified result tree fragment. + */ + public static DocumentInfo numberLines (XPathContext context, + SequenceIterator ns, + SequenceIterator wrapperns, + int lnLineNumber, + int lnEveryNth, + int lnWidth, + String lnSeparator) + throws TransformerException { + + Controller controller = context.getController(); + Configuration config = controller.getConfiguration(); + NamePool pool = config.getNamePool(); + TinyBuilder builder = new TinyBuilder(); + + NamespaceReducer reducer = new NamespaceReducer(); + reducer.setUnderlyingReceiver(builder); + Receiver tree = reducer; + + tree.setConfiguration(config); + builder.setConfiguration(config); + tree.startDocument(); + + wrapper = findWrapper(wrapperns); + + // We're really counting newlines so there's a tendency to be short + // by one. That's why we start at 1 and not zero. + lineCount = 1; + SequenceIterator nscount = ns.getAnother(); + NodeInfo verbatim = (NodeInfo) nscount.next(); + if (verbatim.getNodeKind() != Type.ELEMENT) { + System.err.println("Error!!!: " + verbatim); + } else { + NodeInfo node = (NodeInfo) verbatim; + if (node.hasChildNodes()) { + AxisIterator children = node.iterateAxis(Axis.CHILD); + NodeInfo child = (NodeInfo) children.next(); + while (child != null) { + countLines(child); + child = (NodeInfo) children.next(); + } + } + } + + // Start with initialLineNumber + lineNumber = lnLineNumber; + + // Number every line if we have fewer than 'modulus' lines + modulus = (lineCount < lnEveryNth || lnEveryNth < 1) ? 1 : lnEveryNth; + + // Make the width at least wide enough for the longest number we may need + double log10numLines = Math.log(lineCount) / Math.log(10); + width = lnWidth < log10numLines+1 + ? (int) Math.floor(log10numLines + 1) + : lnWidth; + + // Use the specified separator + separator = lnSeparator; + + // Now walk through the content and number those lines + openElements = new Vector(); + verbatim = (NodeInfo) ns.next(); + if (verbatim.getNodeKind() != Type.ELEMENT) { + System.err.println("Error!!!: " + verbatim); + } else { + NodeInfo node = (NodeInfo) verbatim; + if (node.hasChildNodes()) { + formatLineNumber(pool, tree); + AxisIterator children = node.iterateAxis(Axis.CHILD); + NodeInfo child = (NodeInfo) children.next(); + while (child != null) { + number(child, pool, tree); + child = (NodeInfo) children.next(); + } + } + } + + tree.endDocument(); + return builder.getCurrentDocument(); + } + + private static NodeInfo findWrapper (SequenceIterator wrapperns) + throws TransformerException { + NodeInfo wrapper = (NodeInfo) wrapperns.next(); + if (wrapper.getNodeKind() == Type.DOCUMENT) { + if (wrapper.hasChildNodes()) { + AxisIterator children = wrapper.iterateAxis(Axis.CHILD); + NodeInfo child = (NodeInfo) children.next(); + while (child != null && child.getNodeKind() != Type.ELEMENT) { + child = (NodeInfo) children.next(); + } + if (child != null) { + wrapper = child; + } + } else { + wrapper = null; + } + } else if (wrapper.getNodeKind() != Type.ELEMENT) { + wrapper = null; + } + + return wrapper; + } + + public static void countLines (NodeInfo node) + throws TransformerException { + + // we have to recurse through elements + if (node.getNodeKind() == Type.ELEMENT) { + if (node.hasChildNodes()) { + AxisIterator children = node.iterateAxis(Axis.CHILD); + NodeInfo child = (NodeInfo) children.next(); + while (child != null) { + countLines(child); + child = (NodeInfo) children.next(); + } + } + } + + // we have to look for newlines in text nodes + if (node.getNodeKind() == Type.TEXT) { + String text = node.getStringValue(); + int pos = text.indexOf('\n'); + while (pos >= 0) { + lineCount++; + text = text.substring(pos+1); + pos = text.indexOf('\n'); + } + } + } + + public static void number (NodeInfo node, + NamePool pool, + Receiver tree) + throws TransformerException { + + // Maybe node is an element, a text node, a comment, or a PI + switch (node.getNodeKind()) { + case Type.ELEMENT: + tree.startElement(node.getNameCode(), 0, 0); + + { + AxisIterator attrIter = node.iterateAxis(Axis.ATTRIBUTE); + NodeInfo attr = (NodeInfo) attrIter.next(); + while (attr != null) { + tree.attribute(attr.getNameCode(), 0, attr.getStringValue(), 0); + attr = (NodeInfo) attrIter.next(); + } + } + + openElements.add(node); + + tree.startContent(); + if (node.hasChildNodes()) { + AxisIterator children = node.iterateAxis(Axis.CHILD); + NodeInfo child = (NodeInfo) children.next(); + while (child != null) { + number(child, pool, tree); + child = (NodeInfo) children.next(); + } + } + + openElements.remove(openElements.size()-1); + + tree.endElement(); + break; + case Type.TEXT: + String text = node.getStringValue(); + int pos = text.indexOf('\n'); + while (pos >= 0) { + tree.characters(text.substring(0, pos), 0); + + // Close any open elements + for (int openpos = 0; openpos < openElements.size(); openpos++) { + tree.endElement(); + } + + // Output the line number + tree.characters("\n", 0); + lineNumber++; + formatLineNumber(pool, tree); + + // Now re-open the elements + for (int openpos = 0; openpos < openElements.size(); openpos++) { + NodeInfo onode = (NodeInfo) openElements.get(openpos); + + tree.startElement(onode.getNameCode(), 0, 0); + + AxisIterator oattrIter = onode.iterateAxis(Axis.ATTRIBUTE); + NodeInfo attr = (NodeInfo) oattrIter.next(); + while (attr != null) { + // Don't output {xml:}id attributes again + if (!"id".equals(attr.getLocalPart())) { + tree.attribute(attr.getNameCode(), 0, attr.getStringValue(), 0); + } + attr = (NodeInfo) oattrIter.next(); + } + + tree.startContent(); + } + + text = text.substring(pos+1); + pos = text.indexOf('\n'); + } + tree.characters(text, 0); + break; + case Type.COMMENT: + tree.comment(node.getStringValue(), 0); + break; + case Type.PROCESSING_INSTRUCTION: + tree.processingInstruction(node.getDisplayName(), + node.getStringValue(), 0); + break; + default: + System.err.println("Error!"); + break; + } + } + + public static void formatLineNumber(NamePool pool, Receiver tree) + throws TransformerException { + + char ch = 160; //   + + String lno = ""; + if (lineNumber == 1 + || (modulus >= 1 && (lineNumber % modulus == 0))) { + lno = "" + lineNumber; + } + + while (lno.length() < width) { + lno = ch + lno; + } + + lno += separator; + + if (wrapper != null) { + tree.startElement(wrapper.getNameCode(), 0, 0); + AxisIterator attrIter = wrapper.iterateAxis(Axis.ATTRIBUTE); + NodeInfo attr = (NodeInfo) attrIter.next(); + while (attr != null) { + tree.attribute(attr.getNameCode(), 0, attr.getStringValue(), 0); + attr = (NodeInfo) attrIter.next(); + } + tree.startContent(); + } + + tree.characters(lno, 0); + + if (wrapper != null) { + tree.endElement(); + } + } + + public static DocumentInfo insertCallouts (XPathContext context, + SequenceIterator ns, + SequenceIterator areaspecns, + String prefix, String suffix, + boolean useText, + SequenceIterator twrapperns, + int uStart, int uMax, + boolean useUnicode, + SequenceIterator uwrapperns, + int gMax, boolean useGraphics, + SequenceIterator gwrapperns) + throws TransformerException { + + // Take care of parameter passing + textPrefix = prefix; + textSuffix = suffix; + if (useText) { + calloutStyle = CALLOUT_TEXT; + } + + unicodeStart = uStart; + unicodeMax = uMax; + if (useUnicode) { + calloutStyle = CALLOUT_UNICODE; + } + + graphicsMax = gMax; + if (useGraphics) { + calloutStyle = CALLOUT_GRAPHICS; + } + + textWrapper = findWrapper(twrapperns); + unicodeWrapper = findWrapper(uwrapperns); + graphicsWrapper = findWrapper(gwrapperns); + + setupCallouts(areaspecns); + + Controller controller = context.getController(); + Configuration config = controller.getConfiguration(); + NamePool pool = config.getNamePool(); + TinyBuilder builder = new TinyBuilder(); + + NamespaceReducer reducer = new NamespaceReducer(); + reducer.setUnderlyingReceiver(builder); + Receiver tree = reducer; + + tree.setConfiguration(config); + builder.setConfiguration(config); + tree.startDocument(); + + // Start at (1,1) + lineNumber = 1; + colNumber = 1; + + // Now walk through the content and number those lines + openElements = new Vector(); + NodeInfo verbatim = (NodeInfo) ns.next(); + if (verbatim.getNodeKind() != Type.ELEMENT) { + System.err.println("Error!!!: " + verbatim); + } else { + NodeInfo node = (NodeInfo) verbatim; + if (node.hasChildNodes()) { + formatLineNumber(pool, tree); + AxisIterator children = node.iterateAxis(Axis.CHILD); + NodeInfo child = (NodeInfo) children.next(); + while (child != null) { + insert(child, pool, tree); + child = (NodeInfo) children.next(); + } + } + } + + tree.endDocument(); + return builder.getCurrentDocument(); + } + + public static void insert (NodeInfo node, + NamePool pool, + Receiver tree) + throws TransformerException { + + // Maybe node is an element, a text node, a comment, or a PI + switch (node.getNodeKind()) { + case Type.ELEMENT: + tree.startElement(node.getNameCode(), 0, 0); + + { + AxisIterator attrIter = node.iterateAxis(Axis.ATTRIBUTE); + NodeInfo attr = (NodeInfo) attrIter.next(); + while (attr != null) { + tree.attribute(attr.getNameCode(), 0, attr.getStringValue(), 0); + attr = (NodeInfo) attrIter.next(); + } + } + + openElements.add(node); + + tree.startContent(); + if (node.hasChildNodes()) { + AxisIterator children = node.iterateAxis(Axis.CHILD); + NodeInfo child = (NodeInfo) children.next(); + while (child != null) { + insert(child, pool, tree); + child = (NodeInfo) children.next(); + } + } + + openElements.remove(openElements.size()-1); + + tree.endElement(); + break; + case Type.TEXT: + String text = node.getStringValue(); + + // Are we ready to do a callout? + if (calloutList.size() > 0) { + Callout callout = (Callout) calloutList.get(0); + System.err.println(lineNumber + ", " + callout.getLine()); + + for (int count = 0; count < text.length(); count++) { + boolean done = (callout == null); + while (!done) { + done = true; + if (lineNumber == callout.getLine() + && colNumber == callout.getColumn()) { + insertCallout(callout, node, pool, tree); + callout = null; + calloutList.remove(0); + if (calloutList.size() > 0) { + callout = (Callout) calloutList.get(0); + done = false; + } + } + } + + String ch = text.substring(count, count+1); + + while (callout != null && "\n".equals(ch) + && lineNumber == callout.getLine()) { + tree.characters(" ", 0); + colNumber++; + + done = false; + while (!done) { + done = true; + if (lineNumber == callout.getLine() + && colNumber == callout.getColumn()) { + insertCallout(callout, node, pool, tree); + callout = null; + calloutList.remove(0); + if (calloutList.size() > 0) { + callout = (Callout) calloutList.get(0); + done = false; + } + } + } + } + + tree.characters(ch, 0); + + if ("\n".equals(ch)) { + lineNumber++; + colNumber = 1; + } else { + colNumber++; + } + } + } else { + tree.characters(text, 0); + } + break; + case Type.COMMENT: + tree.comment(node.getStringValue(), 0); + break; + case Type.PROCESSING_INSTRUCTION: + tree.processingInstruction(node.getDisplayName(), + node.getStringValue(), 0); + break; + default: + System.err.println("Error!"); + break; + } + } + + public static void insertCallout (Callout callout, + NodeInfo node, + NamePool pool, + Receiver tree) + throws TransformerException { + + int style = calloutStyle; + + if ((style == CALLOUT_UNICODE + && callout.getCallout() > unicodeMax) + || (style == CALLOUT_GRAPHICS + && callout.getCallout() > graphicsMax)) { + style = CALLOUT_TEXT; + } + + // Close any open elements + for (int openpos = 0; openpos < openElements.size(); openpos++) { + tree.endElement(); + } + + switch (style) { + case CALLOUT_TEXT: + startCalloutWrapper(callout, textWrapper, tree); + tree.characters(textPrefix + callout.getCallout() + textSuffix, 0); + endCalloutWrapper(textWrapper, tree); + break; + case CALLOUT_UNICODE: + startCalloutWrapper(callout, unicodeWrapper, tree); + int codepoint = unicodeStart + callout.getCallout() - 1; + char chars[] = new char[1]; + chars[0] = (char) codepoint; + String unicodeCh = new String(chars); + tree.characters(unicodeCh, 0); + endCalloutWrapper(unicodeWrapper, tree); + break; + case CALLOUT_GRAPHICS: + startCalloutWrapper(callout, graphicsWrapper, tree); + endCalloutWrapper(graphicsWrapper, tree); + break; + default: + } + + // Now re-open the elements + for (int openpos = 0; openpos < openElements.size(); openpos++) { + NodeInfo onode = (NodeInfo) openElements.get(openpos); + + tree.startElement(onode.getNameCode(), 0, 0); + + AxisIterator oattrIter = onode.iterateAxis(Axis.ATTRIBUTE); + NodeInfo attr = (NodeInfo) oattrIter.next(); + while (attr != null) { + // Don't output {xml:}id attributes again + if (!"id".equals(attr.getLocalPart())) { + tree.attribute(attr.getNameCode(), 0, attr.getStringValue(), 0); + } + attr = (NodeInfo) oattrIter.next(); + } + + tree.startContent(); + } + } + + private static void startCalloutWrapper (Callout callout, + NodeInfo wrapper, + Receiver tree) + throws TransformerException { + if (wrapper != null) { + tree.startElement(wrapper.getNameCode(), 0, 0); + AxisIterator attrIter = wrapper.iterateAxis(Axis.ATTRIBUTE); + NodeInfo attr = (NodeInfo) attrIter.next(); + while (attr != null) { + String value = attr.getStringValue().replaceAll("\\{CALLOUT\\}", + ""+callout.getCallout()); + tree.attribute(attr.getNameCode(), 0, value, 0); + attr = (NodeInfo) attrIter.next(); + } + tree.startContent(); + } + } + + private static void endCalloutWrapper (NodeInfo wrapper, Receiver tree) + throws TransformerException { + if (wrapper != null) { + tree.endElement(); + } + } + + /** + *

Examine the areaspec and determine the number and position of + * callouts.

+ * + *

The areaspecNodeSet + * is examined and a sorted list of the callouts is constructed.

+ * + *

This data structure is used to augment the tree + * with callout bullets.

+ * + * @param areaspecNodeSet The source document <areaspec> element. + * + */ + public static void setupCallouts (SequenceIterator areaspecns) + throws TransformerException { + + // First we walk through the areaspec to calculate the position + // of the callouts + // + // + // + // + // + // + // + // + + calloutList = new Vector(); + + int pos = 0; + int coNum = 0; + boolean inAreaSet = false; + NodeInfo areaspec = (NodeInfo) areaspecns.next(); + + if (!areaspec.hasChildNodes()) { + return; + } + + AxisIterator children = areaspec.iterateAxis(Axis.CHILD); + NodeInfo child = (NodeInfo) children.next(); + while (child != null) { + if (child.getNodeKind() == Type.ELEMENT) { + if ("areaset".equals(child.getLocalPart())) { + coNum++; + + AxisIterator areas = child.iterateAxis(Axis.CHILD); + NodeInfo area = (NodeInfo) areas.next(); + while (area != null) { + if (area.getNodeKind() == Type.ELEMENT) { + if ("area".equals(area.getLocalPart())) { + addCallout(coNum, area, defaultColumn); + } else { + System.out.println("Unexpected element in areaset: " + + area.getDisplayName()); + } + } + area = (NodeInfo) areas.next(); + } + } else if ("area".equals(child.getLocalPart())) { + coNum++; + addCallout(coNum, child, defaultColumn); + } else { + System.out.println("Unexpected element in areaspec: " + + child.getLocalPart()); + } + } + child = (NodeInfo) children.next(); + } + + // Now sort them + Collections.sort(calloutList); + } + + /** + *

Add a callout to the global callout array

+ * + *

This method examines a callout area and adds it to + * the global callout array if it can be interpreted.

+ * + *

Only the linecolumn and linerange units are + * supported. If no unit is specifed, linecolumn is assumed. + * If only a line is specified, the callout decoration appears in + * the defaultColumn.

+ * + * @param coNum The callout number. + * @param node The area. + * @param defaultColumn The default column for callouts. + */ + protected static void addCallout (int coNum, + NodeInfo area, + int defaultColumn) { + String units = null; + String coords = null; + + AxisIterator attrIter = area.iterateAxis(Axis.ATTRIBUTE); + NodeInfo attr = (NodeInfo) attrIter.next(); + while (attr != null) { + if ("".equals(attr.getURI())) { + if ("units".equals(attr.getLocalPart())) { + units = attr.getStringValue(); + } + if ("coords".equals(attr.getLocalPart())) { + coords = attr.getStringValue(); + } + } + attr = (NodeInfo) attrIter.next(); + } + + if (units != null + && !units.equals("linecolumn") + && !units.equals("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; + } + } + + // Ok, add the callout + if (tokenCount == 2) { + if (units != null && units.equals("linerange")) { + for (int count = c1; count <= c2; count++) { + calloutList.add(new Callout(coNum, area, count, defaultColumn)); + } + } else { + // assume linecolumn + calloutList.add(new Callout(coNum, area, c1, c2)); + } + } else { + // if there's only one number, assume it's the line + calloutList.add(new Callout(coNum, area, c1, defaultColumn)); + } + } +} diff --git a/xsl/extensions/saxon8/com/nwalsh/saxon8/package.html b/xsl/extensions/saxon8/com/nwalsh/saxon8/package.html new file mode 100644 index 000000000..d1cb8a991 --- /dev/null +++ b/xsl/extensions/saxon8/com/nwalsh/saxon8/package.html @@ -0,0 +1,48 @@ + + +Norman Walsh's Saxon Extensions Package + + +

Norman Walsh's Saxon Extensions Package for Saxon 8.*

+ +

This package implements Saxon extensions for XSLT.

+ +

Copyright (C) 2004 Norman Walsh

+

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.

+ +

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.

+ + +
+

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.

+
+ + + -- 2.40.0