From 97150d4ac8b2537cb1d27c45a1b7a5bbdf6c2277 Mon Sep 17 00:00:00 2001 From: Norman Walsh <ndw@nwalsh.com> Date: Sun, 13 May 2001 21:46:06 +0000 Subject: [PATCH] Copy of the saxon61 sources; not converted to the 6.3 API --- xsl/extensions/saxon63/.cvsignore | 1 + .../saxon63/com/nwalsh/saxon/CVS.java | 90 +++ .../saxon63/com/nwalsh/saxon/Callout.java | 91 +++ .../com/nwalsh/saxon/CalloutEmitter.java | 527 ++++++++++++++++++ .../com/nwalsh/saxon/ColumnScanEmitter.java | 167 ++++++ .../com/nwalsh/saxon/ColumnUpdateEmitter.java | 96 ++++ .../saxon63/com/nwalsh/saxon/CopyEmitter.java | 150 +++++ .../com/nwalsh/saxon/FormatCallout.java | 112 ++++ .../nwalsh/saxon/FormatGraphicCallout.java | 89 +++ .../com/nwalsh/saxon/FormatTextCallout.java | 44 ++ .../nwalsh/saxon/FormatUnicodeCallout.java | 68 +++ .../com/nwalsh/saxon/LineCountEmitter.java | 143 +++++ .../com/nwalsh/saxon/NumberLinesEmitter.java | 323 +++++++++++ .../saxon63/com/nwalsh/saxon/Table.java | 428 ++++++++++++++ .../saxon63/com/nwalsh/saxon/Text.java | 132 +++++ .../saxon63/com/nwalsh/saxon/TextFactory.java | 67 +++ .../saxon63/com/nwalsh/saxon/Verbatim.java | 458 +++++++++++++++ .../saxon63/com/nwalsh/saxon/package.html | 48 ++ 18 files changed, 3034 insertions(+) create mode 100644 xsl/extensions/saxon63/.cvsignore create mode 100644 xsl/extensions/saxon63/com/nwalsh/saxon/CVS.java create mode 100644 xsl/extensions/saxon63/com/nwalsh/saxon/Callout.java create mode 100644 xsl/extensions/saxon63/com/nwalsh/saxon/CalloutEmitter.java create mode 100644 xsl/extensions/saxon63/com/nwalsh/saxon/ColumnScanEmitter.java create mode 100644 xsl/extensions/saxon63/com/nwalsh/saxon/ColumnUpdateEmitter.java create mode 100644 xsl/extensions/saxon63/com/nwalsh/saxon/CopyEmitter.java create mode 100644 xsl/extensions/saxon63/com/nwalsh/saxon/FormatCallout.java create mode 100644 xsl/extensions/saxon63/com/nwalsh/saxon/FormatGraphicCallout.java create mode 100644 xsl/extensions/saxon63/com/nwalsh/saxon/FormatTextCallout.java create mode 100644 xsl/extensions/saxon63/com/nwalsh/saxon/FormatUnicodeCallout.java create mode 100644 xsl/extensions/saxon63/com/nwalsh/saxon/LineCountEmitter.java create mode 100644 xsl/extensions/saxon63/com/nwalsh/saxon/NumberLinesEmitter.java create mode 100644 xsl/extensions/saxon63/com/nwalsh/saxon/Table.java create mode 100644 xsl/extensions/saxon63/com/nwalsh/saxon/Text.java create mode 100644 xsl/extensions/saxon63/com/nwalsh/saxon/TextFactory.java create mode 100644 xsl/extensions/saxon63/com/nwalsh/saxon/Verbatim.java create mode 100644 xsl/extensions/saxon63/com/nwalsh/saxon/package.html diff --git a/xsl/extensions/saxon63/.cvsignore b/xsl/extensions/saxon63/.cvsignore new file mode 100644 index 000000000..4d3c216f5 --- /dev/null +++ b/xsl/extensions/saxon63/.cvsignore @@ -0,0 +1 @@ +.classes diff --git a/xsl/extensions/saxon63/com/nwalsh/saxon/CVS.java b/xsl/extensions/saxon63/com/nwalsh/saxon/CVS.java new file mode 100644 index 000000000..529546be7 --- /dev/null +++ b/xsl/extensions/saxon63/com/nwalsh/saxon/CVS.java @@ -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://users.iclway.co.uk/mhkay/saxon/">Saxon</a> + * extension to turn the CVS date strings, which are UTC:</p> + * + * <pre>$Date: 2000/11/09 02:34:20 $</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/extensions/saxon63/com/nwalsh/saxon/Callout.java b/xsl/extensions/saxon63/com/nwalsh/saxon/Callout.java new file mode 100644 index 000000000..f0be0cd4f --- /dev/null +++ b/xsl/extensions/saxon63/com/nwalsh/saxon/Callout.java @@ -0,0 +1,91 @@ +package com.nwalsh.saxon; + +import com.icl.saxon.om.*; +import com.icl.saxon.tree.*; + +/** + * <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 ElementInfo item that generated this callout. */ + private ElementInfo 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, ElementInfo 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 ElementInfo 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/saxon63/com/nwalsh/saxon/CalloutEmitter.java b/xsl/extensions/saxon63/com/nwalsh/saxon/CalloutEmitter.java new file mode 100644 index 000000000..693c6e863 --- /dev/null +++ b/xsl/extensions/saxon63/com/nwalsh/saxon/CalloutEmitter.java @@ -0,0 +1,527 @@ +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.Builder; +import com.icl.saxon.Context; +import com.icl.saxon.expr.*; +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.tinytree.TinyBuilder; +import com.icl.saxon.tree.*; + +/** + * <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://users.iclway.co.uk/mhkay/saxon/">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 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 namePool The name pool to use for constructing elements and attributes. + * @param graphicsPath The path to callout number graphics. + * @param graphicsExt The extension for callout number graphics. + * @param graphicsMax The largest callout number that can be represented as a graphic. + * @param defaultColumn The default column for callouts. + * @param foStylesheet Is this an FO stylesheet? + */ + public CalloutEmitter(NamePool namePool, + int defaultColumn, + boolean foStylesheet, + FormatCallout fCallout) { + super(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 areaspecNodeSet The source document <areaspec> element. + * + */ + public void setupCallouts (NodeSetIntent areaspecNodeSet) { + 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> + try { + int pos = 0; + int coNum = 0; + boolean inAreaSet = false; + NodeInfo areaspec = areaspecNodeSet.getFirst(); + NodeInfo children[] = areaspec.getAllChildNodes(); + + for (int count = 0; count < children.length; count++) { + NodeInfo node = children[count]; + if (node.getNodeType() == NodeInfo.ELEMENT) { + if (node.getNodeName().equalsIgnoreCase("areaset")) { + coNum++; + NodeInfo areas[] = node.getAllChildNodes(); + for (int acount = 0; acount < areas.length; acount++) { + NodeInfo area = areas[acount]; + if (area.getNodeType() == NodeInfo.ELEMENT) { + 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); + } catch (TransformerException e) { + //nop; + } + } + + /** 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, + NodeInfo node, + int defaultColumn) { + + ElementInfo area = (ElementInfo) node; + ExtendedAttributes attr = area.getAttributeList(); + String units = attr.getValue("units"); + String coords = attr.getValue("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) { + if (firstElement) { + int thisFingerprint = namePool.getFingerprint(nameCode); + int foBlockFingerprint = namePool.getFingerprint(foURI, "block"); + int htmlPreFingerprint = namePool.getFingerprint("", "pre"); + int htmlDivFingerprint = namePool.getFingerprint("", "div"); + + if ((foStylesheet && thisFingerprint == foBlockFingerprint) + || (!foStylesheet && (thisFingerprint == htmlPreFingerprint + || thisFingerprint == htmlDivFingerprint))) { + // 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/extensions/saxon63/com/nwalsh/saxon/ColumnScanEmitter.java b/xsl/extensions/saxon63/com/nwalsh/saxon/ColumnScanEmitter.java new file mode 100644 index 000000000..0b7c3c99a --- /dev/null +++ b/xsl/extensions/saxon63/com/nwalsh/saxon/ColumnScanEmitter.java @@ -0,0 +1,167 @@ +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.tinytree.TinyBuilder; +import com.icl.saxon.expr.FragmentValue; +import com.icl.saxon.Builder; + +/** + * <p>Saxon extension to scan the column widthsin a result tree fragment.</p> + * + * <p>$Id$</p> + * + * <p>Copyright (C) 2000 Norman Walsh.</p> + * + * <p>This class provides a + * <a href="http://users.iclway.co.uk/mhkay/saxon/">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 width; + } + + /** 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/extensions/saxon63/com/nwalsh/saxon/ColumnUpdateEmitter.java b/xsl/extensions/saxon63/com/nwalsh/saxon/ColumnUpdateEmitter.java new file mode 100644 index 000000000..99b43c8c1 --- /dev/null +++ b/xsl/extensions/saxon63/com/nwalsh/saxon/ColumnUpdateEmitter.java @@ -0,0 +1,96 @@ +package com.nwalsh.saxon; + +import org.xml.sax.*; +import com.icl.saxon.output.*; +import com.icl.saxon.om.*; +import javax.xml.transform.TransformerException; +import com.icl.saxon.tinytree.TinyBuilder; +import com.icl.saxon.expr.FragmentValue; +import com.icl.saxon.Builder; +import com.icl.saxon.tree.AttributeCollection; + +/** + * <p>Saxon extension to scan the column widthsin a result tree fragment.</p> + * + * <p>$Id$</p> + * + * <p>Copyright (C) 2000 Norman Walsh.</p> + * + * <p>This class provides a + * <a href="http://users.iclway.co.uk/mhkay/saxon/">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(NamePool namePool, + String width[]) { + super(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/extensions/saxon63/com/nwalsh/saxon/CopyEmitter.java b/xsl/extensions/saxon63/com/nwalsh/saxon/CopyEmitter.java new file mode 100644 index 000000000..ee15c9b65 --- /dev/null +++ b/xsl/extensions/saxon63/com/nwalsh/saxon/CopyEmitter.java @@ -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.Builder; +import com.icl.saxon.Context; +import com.icl.saxon.expr.*; +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.tinytree.TinyBuilder; +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://users.iclway.co.uk/mhkay/saxon/">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(NamePool namePool) { + rtf = new FragmentValue(); + 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/extensions/saxon63/com/nwalsh/saxon/FormatCallout.java b/xsl/extensions/saxon63/com/nwalsh/saxon/FormatCallout.java new file mode 100644 index 000000000..c8be006ca --- /dev/null +++ b/xsl/extensions/saxon63/com/nwalsh/saxon/FormatCallout.java @@ -0,0 +1,112 @@ +package com.nwalsh.saxon; + +import org.xml.sax.SAXException; +import org.w3c.dom.*; + +import javax.xml.transform.TransformerException; + +import com.icl.saxon.om.ElementInfo; +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(ElementInfo area) { + String label = null; + + if (area.getAttributeList().getValue("label") != null) { + // If this area has a label, use it + label = area.getAttributeList().getValue("label"); + } else { + // Otherwise, if its parent is an areaset and it has a label, use that + ElementInfo parent = (ElementInfo) area.getParentNode(); + if (parent != null + && parent.getLocalName().equalsIgnoreCase("areaset") + && parent.getAttributeList().getValue("label") != null) { + label = parent.getAttributeList().getValue("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) { + ElementInfo 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/extensions/saxon63/com/nwalsh/saxon/FormatGraphicCallout.java b/xsl/extensions/saxon63/com/nwalsh/saxon/FormatGraphicCallout.java new file mode 100644 index 000000000..a61590222 --- /dev/null +++ b/xsl/extensions/saxon63/com/nwalsh/saxon/FormatGraphicCallout.java @@ -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.ElementInfo; +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) { + ElementInfo 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", + 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/extensions/saxon63/com/nwalsh/saxon/FormatTextCallout.java b/xsl/extensions/saxon63/com/nwalsh/saxon/FormatTextCallout.java new file mode 100644 index 000000000..0c0c0f7a1 --- /dev/null +++ b/xsl/extensions/saxon63/com/nwalsh/saxon/FormatTextCallout.java @@ -0,0 +1,44 @@ +package com.nwalsh.saxon; + +import org.xml.sax.SAXException; +import org.w3c.dom.*; + +import javax.xml.transform.TransformerException; + +import com.icl.saxon.om.ElementInfo; +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/extensions/saxon63/com/nwalsh/saxon/FormatUnicodeCallout.java b/xsl/extensions/saxon63/com/nwalsh/saxon/FormatUnicodeCallout.java new file mode 100644 index 000000000..e4fdc0e59 --- /dev/null +++ b/xsl/extensions/saxon63/com/nwalsh/saxon/FormatUnicodeCallout.java @@ -0,0 +1,68 @@ +package com.nwalsh.saxon; + +import org.xml.sax.SAXException; +import org.w3c.dom.*; + +import javax.xml.transform.TransformerException; + +import com.icl.saxon.om.ElementInfo; +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; + + public FormatUnicodeCallout(NamePool nPool, int start, int max, boolean fo) { + super(nPool, fo); + unicodeMax = max; + unicodeStart = start; + } + + public void formatCallout(Emitter rtfEmitter, + Callout callout) { + + ElementInfo area = callout.getArea(); + int num = callout.getCallout(); + String label = areaLabel(area); + + try { + if (label == null && num <= unicodeMax) { + char chars[] = new char[1]; + chars[0] = (char) (unicodeStart + num - 1); + + startSpan(rtfEmitter); + rtfEmitter.characters(chars, 0, 1); + endSpan(rtfEmitter); + } else { + formatTextCallout(rtfEmitter, callout); + } + } catch (TransformerException e) { + System.out.println("Transformer Exception in graphic formatCallout"); + } + } +} diff --git a/xsl/extensions/saxon63/com/nwalsh/saxon/LineCountEmitter.java b/xsl/extensions/saxon63/com/nwalsh/saxon/LineCountEmitter.java new file mode 100644 index 000000000..99d7b2d54 --- /dev/null +++ b/xsl/extensions/saxon63/com/nwalsh/saxon/LineCountEmitter.java @@ -0,0 +1,143 @@ +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.tinytree.TinyBuilder; +import com.icl.saxon.expr.FragmentValue; +import com.icl.saxon.Builder; + +/** + * <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://users.iclway.co.uk/mhkay/saxon/">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/extensions/saxon63/com/nwalsh/saxon/NumberLinesEmitter.java b/xsl/extensions/saxon63/com/nwalsh/saxon/NumberLinesEmitter.java new file mode 100644 index 000000000..f8065e370 --- /dev/null +++ b/xsl/extensions/saxon63/com/nwalsh/saxon/NumberLinesEmitter.java @@ -0,0 +1,323 @@ +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.tree.AttributeCollection; +import com.icl.saxon.tinytree.TinyBuilder; +import com.icl.saxon.expr.FragmentValue; +import com.icl.saxon.Builder; + +/** + * <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://users.iclway.co.uk/mhkay/saxon/">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"; + + /** 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(NamePool namePool, + int modulus, + int width, + String separator, + boolean foStylesheet) { + super(namePool); + elementStack = new Stack(); + firstElement = true; + + this.modulus = modulus; + 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 + 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; // + + 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) { + if (firstElement) { + int foBlockFingerprint = namePool.getFingerprint(foURI, "block"); + int htmlPreFingerprint = namePool.getFingerprint("", "pre"); + int htmlDivFingerprint = namePool.getFingerprint("", "div"); + + if ((foStylesheet && nameCode == foBlockFingerprint) + || (!foStylesheet && (nameCode == htmlPreFingerprint + || nameCode == htmlDivFingerprint))) { + // 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/extensions/saxon63/com/nwalsh/saxon/Table.java b/xsl/extensions/saxon63/com/nwalsh/saxon/Table.java new file mode 100644 index 000000000..8afe55d59 --- /dev/null +++ b/xsl/extensions/saxon63/com/nwalsh/saxon/Table.java @@ -0,0 +1,428 @@ +// 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.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://users.iclway.co.uk/mhkay/saxon/">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 <col>s within + * a <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 The result tree fragment containing the colgroup. + * + * @return The result tree fragment containing the adjusted colgroup. + * + */ + public static FragmentValue adjustColumnWidths (Context context, + FragmentValue rtf) { + setupColumnWidths(context); + + try { + NamePool namePool = context.getController().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()) + "%"; + } + } 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()) + "%"; + } + } + } + + ColumnUpdateEmitter cuEmitter = new ColumnUpdateEmitter(namePool, + widths); + rtf.replay(cuEmitter); + return cuEmitter.getResultTreeFragment(); + } catch (TransformerException e) { + // This "can't" happen. + System.out.println("Transformer Exception in adjustColumnWidths"); + return rtf; + } + } +} diff --git a/xsl/extensions/saxon63/com/nwalsh/saxon/Text.java b/xsl/extensions/saxon63/com/nwalsh/saxon/Text.java new file mode 100644 index 000000000..f5a95d7ff --- /dev/null +++ b/xsl/extensions/saxon63/com/nwalsh/saxon/Text.java @@ -0,0 +1,132 @@ +// 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 com.icl.saxon.*; +import com.icl.saxon.style.*; +import com.icl.saxon.expr.*; +import com.icl.saxon.output.*; +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://users.iclway.co.uk/mhkay/saxon/">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> + * + */ + public void process( Context context ) throws TransformerException { + Outputter out = context.getOutputter(); + + String hrefAtt = getAttribute("href"); + Expression hrefExpr = makeAttributeValueTemplate(hrefAtt); + String href = hrefExpr.evaluateAsString(context); + URL fileURL = null; + + try { + try { + fileURL = new URL(href); + } catch (MalformedURLException e1) { + try { + fileURL = new URL("file:" + href); + } catch (MalformedURLException e2) { + System.out.println("Cannot open " + href); + return; + } + } + + InputStreamReader isr = new InputStreamReader(fileURL.openStream()); + BufferedReader is = new BufferedReader(isr); + + char chars[] = new char[4096]; + int len = 0; + while ((len = is.read(chars)) > 0) { + out.writeContent(chars, 0, len); + } + is.close(); + } catch (Exception e) { + System.out.println("Cannot read " + href); + } + } +} diff --git a/xsl/extensions/saxon63/com/nwalsh/saxon/TextFactory.java b/xsl/extensions/saxon63/com/nwalsh/saxon/TextFactory.java new file mode 100644 index 000000000..ccf5001c0 --- /dev/null +++ b/xsl/extensions/saxon63/com/nwalsh/saxon/TextFactory.java @@ -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://users.iclway.co.uk/mhkay/saxon/">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/extensions/saxon63/com/nwalsh/saxon/Verbatim.java b/xsl/extensions/saxon63/com/nwalsh/saxon/Verbatim.java new file mode 100644 index 000000000..5823d88b7 --- /dev/null +++ b/xsl/extensions/saxon63/com/nwalsh/saxon/Verbatim.java @@ -0,0 +1,458 @@ +// 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.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://users.iclway.co.uk/mhkay/saxon/">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 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>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; + 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 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 rtf The result tree fragment of the verbatim environment. + * + * @return The modified result tree fragment. + */ + public static FragmentValue numberLines (Context context, + FragmentValue rtf) { + + 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; + + NamePool namePool = context.getController().getNamePool(); + NumberLinesEmitter nlEmitter = new NumberLinesEmitter(namePool, + 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; + + // 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; + } + + fCallout = new FormatUnicodeCallout(namePool, + 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> + * <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> + * <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 areaspecNodeSet The source node set that contains the areaspec. + * @param rtf The result tree fragment of the verbatim environment. + * + * @return The modified result tree fragment. + */ + + public static FragmentValue insertCallouts (Context context, + NodeSetIntent areaspecNodeSet, + FragmentValue rtf) { + + setupCallouts(context); + + try { + NamePool namePool = context.getController().getNamePool(); + CalloutEmitter cEmitter = new CalloutEmitter(namePool, + defaultColumn, + foStylesheet, + fCallout); + cEmitter.setupCallouts(areaspecNodeSet); + 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/extensions/saxon63/com/nwalsh/saxon/package.html b/xsl/extensions/saxon63/com/nwalsh/saxon/package.html new file mode 100644 index 000000000..b05a46709 --- /dev/null +++ b/xsl/extensions/saxon63/com/nwalsh/saxon/package.html @@ -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> -- 2.40.0