Package com.nwalsh.xalan

Source Code of com.nwalsh.xalan.Verbatim

// Verbatim.java - Xalan extensions supporting DocBook verbatim environments

package com.nwalsh.xalan;

import java.util.Stack;
import java.util.StringTokenizer;

import org.xml.sax.*;
import org.xml.sax.helpers.AttributesImpl;
import org.w3c.dom.*;
import org.w3c.dom.traversal.NodeIterator;
import org.apache.xerces.dom.*;

import org.apache.xpath.objects.XObject;
import org.apache.xpath.XPath;
import org.apache.xpath.XPathContext;
import org.apache.xpath.NodeSet;
import org.apache.xpath.DOMHelper;
import org.apache.xalan.extensions.XSLProcessorContext;
import org.apache.xalan.extensions.ExpressionContext;
import org.apache.xalan.transformer.TransformerImpl;
import org.apache.xalan.templates.StylesheetRoot;
import org.apache.xalan.templates.ElemExtensionCall;
import org.apache.xalan.templates.OutputProperties;
import org.apache.xalan.res.XSLTErrorResources;
import org.apache.xml.utils.DOMBuilder;
import org.apache.xml.utils.AttList;
import org.apache.xml.utils.QName;

import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.TransformerException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import com.nwalsh.xalan.Callout;
import com.nwalsh.xalan.Params;

/**
* <p>Xalan extensions supporting DocBook verbatim environments</p>
*
* <p>$Id: Verbatim.java,v 1.4 2001/07/15 20:07:49 nwalsh Exp $</p>
*
* <p>Copyright (C) 2001 Norman Walsh.</p>
*
* <p>This class provides a
* <a href="http://xml.apache.org/xalan">Xalan</a>
* implementation of two features that would be impractical to
* implement directly in XSLT: line numbering and callouts.</p>
*
* <p><b>Line Numbering</b></p>
* <p>The <tt>numberLines</tt> family of functions takes a result tree
* fragment (assumed to contain the contents of a formatted verbatim
* element in DocBook: programlisting, screen, address, literallayout,
* or synopsis) and returns a result tree fragment decorated with
* line numbers.</p>
*
* <p><b>Callouts</b></p>
* <p>The <tt>insertCallouts</tt> family of functions takes an
* <tt>areaspec</tt> and a result tree fragment
* (assumed to contain the contents of a formatted verbatim
* element in DocBook: programlisting, screen, address, literallayout,
* or synopsis) and returns a result tree fragment decorated with
* callouts.</p>
*
* <p><b>Change Log:</b></p>
* <dl>
* <dt>1.0</dt>
* <dd><p>Initial release.</p></dd>
* </dl>
*
* @author Norman Walsh
* <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
*
* @version $Id: Verbatim.java,v 1.4 2001/07/15 20:07:49 nwalsh Exp $
*
*/
public class Verbatim {
  /** A stack to hold the open elements while walking through a RTF. */
  private Stack elementStack = null;
  /** A stack to hold the temporarily closed elements. */
  private Stack tempStack = null;
  /** The current line number. */
  private int lineNumber = 0;
  /** The current column number. */
  private int colNumber = 0;
  /** The modulus for line numbering (every 'modulus' line is numbered). */
  private int modulus = 0;
  /** The width (in characters) of line numbers (for padding). */
  private int width = 0;
  /** The separator between the line number and the verbatim text. */
  private String separator = "";
  /** The (sorted) array of callouts obtained from the areaspec. */
  private Callout callout[] = null;
  /** The number of callouts in the callout array. */
  private int calloutCount = 0;
  /** A pointer used to keep track of our position in the callout array. */
  private int calloutPos = 0;
  /** The path to use for graphical callout decorations. */
  private String graphicsPath = null;
  /** The extension to use for graphical callout decorations. */
  private String graphicsExt = null;
  /** The largest callout number that can be represented graphically. */
  private int graphicsMax = 10;
  /** Should graphic callouts use fo:external-graphics or imgs. */
  private boolean graphicsFO = false;

  private static final String foURI = "http://www.w3.org/1999/XSL/Format";
  private static final String xhURI = "http://www.w3.org/1999/xhtml";

  /**
   * <p>Constructor for Verbatim</p>
   *
   * <p>All of the methods are static, so the constructor does nothing.</p>
   */
  public Verbatim() {
  }

  /**
   * <p>Number lines in a verbatim environment.</p>
   *
   * <p>This method adds line numbers to a result tree fragment. Each
   * newline that occurs in a text node is assumed to start a new line.
   * The first line is always numbered, every subsequent xalanMod line
   * is numbered (so if xalanMod=5, lines 1, 5, 10, 15, etc. will be
   * numbered. If there are fewer than xalanMod lines in the environment,
   * every line is numbered.</p>
   *
   * <p>xalanMod is taken from the $linenumbering.everyNth parameter.</p>
   *
   * <p>Every line number will be right justified in a string xalanWidth
   * characters long. If the line number of the last line in the
   * environment is too long to fit in the specified width, the width
   * is automatically increased to the smallest value that can hold the
   * number of the last line. (In other words, if you specify the value 2
   * and attempt to enumerate the lines of an environment that is 100 lines
   * long, the value 3 will automatically be used for every line in the
   * environment.)</p>
   *
   * <p>xalanWidth is taken from the $linenumbering.width parameter.</p>
   *
   * <p>The xalanSep string is inserted between the line
   * number and the original program listing. Lines that aren't numbered
   * are preceded by a xalanWidth blank string and the separator.</p>
   *
   * <p>xalanSep is taken from the $linenumbering.separator parameter.</p>
   *
   * <p>If inline markup extends across line breaks, markup changes are
   * required. All the open elements are closed before the line break and
   * "reopened" afterwards. The reopened elements will have the same
   * attributes as the originals, except that 'name' and 'id' attributes
   * are not duplicated.</p>
   *
   * @param xalanRTF The result tree fragment of the verbatim environment.
   *
   * @return The modified result tree fragment.
   */
  public DocumentFragment numberLines (ExpressionContext context,
               NodeIterator xalanNI) {

    int xalanMod = Params.getInt(context, "linenumbering.everyNth");
    int xalanWidth = Params.getInt(context, "linenumbering.width");
    String xalanSep = Params.getString(context, "linenumbering.separator");

    DocumentFragment xalanRTF = (DocumentFragment) xalanNI.nextNode();
    int numLines = countLineBreaks(xalanRTF) + 1;

    DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
    DocumentBuilder docBuilder = null;

    try {
      docBuilder = docFactory.newDocumentBuilder();
    } catch (ParserConfigurationException e) {
      System.out.println("PCE!");
      return xalanRTF;
    }
    Document doc = docBuilder.newDocument();
    DocumentFragment df = doc.createDocumentFragment();
    DOMBuilder db = new DOMBuilder(doc, df);

    elementStack = new Stack();
    lineNumber = 0;
    modulus = numLines < xalanMod ? 1 : xalanMod;
    width = xalanWidth;
    separator = xalanSep;

    double log10numLines = Math.log(numLines) / Math.log(10);

    if (width < log10numLines + 1) {
      width = (int) Math.floor(log10numLines + 1);
    }

    lineNumberFragment(db, xalanRTF);
    return df;
  }

  /**
   * <p>Count the number of lines in a verbatim environment.</p>
   *
   * <p>This method walks over the nodes of a DocumentFragment and
   * returns the number of lines breaks that it contains.</p>
   *
   * @param node The root of the tree walk over.
   */
  private int countLineBreaks(Node node) {
    int numLines = 0;

    if (node.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE
  || node.getNodeType() == Node.DOCUMENT_NODE
  || node.getNodeType() == Node.ELEMENT_NODE) {
      Node child = node.getFirstChild();
      while (child != null) {
  numLines += countLineBreaks(child);
  child = child.getNextSibling();
      }
    } else if (node.getNodeType() == Node.TEXT_NODE) {
      String text = node.getNodeValue();

      // Walk through the text node looking for newlines
      int pos = 0;
      for (int count = 0; count < text.length(); count++) {
  if (text.charAt(count) == '\n') {
    numLines++;
  }
      }
    } else {
      // nop
    }

    return numLines;
  }

  /**
   * <p>Build a DocumentFragment with numbered lines.</p>
   *
   * <p>This is the method that actually does the work of numbering
   * lines in a verbatim environment. It recursively walks through a
   * tree of nodes, copying the structure into the rtf. Text nodes
   * are examined for new lines and modified as requested by the
   * global line numbering parameters.</p>
   *
   * <p>When called, rtf should be an empty DocumentFragment and node
   * should be the first child of the result tree fragment that contains
   * the existing, formatted verbatim text.</p>
   *
   * @param rtf The resulting verbatim environment with numbered lines.
   * @param node The root of the tree to copy.
   */
  private void lineNumberFragment(DOMBuilder rtf,
          Node node) {
    try {
      if (node.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE
    || node.getNodeType() == Node.DOCUMENT_NODE) {
  Node child = node.getFirstChild();
  while (child != null) {
    lineNumberFragment(rtf, child);
    child = child.getNextSibling();
  }
      } else if (node.getNodeType() == Node.ELEMENT_NODE) {
  String ns = node.getNamespaceURI();
  String localName = node.getLocalName();
  String name = ((Element) node).getTagName();

  rtf.startElement(ns, localName, name,
       copyAttributes((Element) node));

  elementStack.push(node);

  Node child = node.getFirstChild();
  while (child != null) {
    lineNumberFragment(rtf, child);
    child = child.getNextSibling();
  }
      } else if (node.getNodeType() == Node.TEXT_NODE) {
  String text = node.getNodeValue();

  if (lineNumber == 0) {
    // The first line is always numbered
    formatLineNumber(rtf, ++lineNumber);
  }

  // Walk through the text node looking for newlines
  char chars[] = text.toCharArray();
  int pos = 0;
  for (int count = 0; count < text.length(); count++) {
    if (text.charAt(count) == '\n') {
      // This is the tricky bit; if we find a newline, make sure
      // it doesn't occur inside any markup.

      if (pos > 0) {
        rtf.characters(chars, 0, pos);
        pos = 0;
      }

      closeOpenElements(rtf);

      // Copy the newline to the output
      chars[pos++] = text.charAt(count);
      rtf.characters(chars, 0, pos);
      pos = 0;

      // Add the line number
      formatLineNumber(rtf, ++lineNumber);

      openClosedElements(rtf);
    } else {
      chars[pos++] = text.charAt(count);
    }
  }

  if (pos > 0) {
    rtf.characters(chars, 0, pos);
  }
      } else if (node.getNodeType() == Node.COMMENT_NODE) {
  String text = node.getNodeValue();
  char chars[] = text.toCharArray();
  rtf.comment(chars, 0, text.length());
      } else if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) {
  rtf.processingInstruction(node.getNodeName(), node.getNodeValue());
      } else {
  System.out.println("Warning: unexpected node type in lineNumberFragment");
      }

      if (node.getNodeType() == Node.ELEMENT_NODE) {
  String ns = node.getNamespaceURI();
  String localName = node.getLocalName();
  String name = ((Element) node).getTagName();
  rtf.endElement(ns, localName, name);
  elementStack.pop();
      }
    } catch (SAXException e) {
      System.out.println("SAX Exception in lineNumberFragment");
    }
  }

  /**
   * <p>Add a formatted line number to the result tree fragment.</p>
   *
   * <p>This method examines the global parameters that control line
   * number presentation (modulus, width, and separator) and adds
   * the appropriate text to the result tree fragment.</p>
   *
   * @param rtf The resulting verbatim environment with numbered lines.
   * @param lineNumber The number of the current line.
   */
  private void formatLineNumber(DOMBuilder rtf,
        int lineNumber) {
    char ch = 160;
    String lno = "";
    if (lineNumber == 1
  || (modulus >= 1 && (lineNumber % modulus == 0))) {
      lno = "" + lineNumber;
    }

    while (lno.length() < width) {
      lno = ch + lno;
    }

    lno += separator;

    char chars[] = lno.toCharArray();
    try {
      rtf.characters(chars, 0, lno.length());
    } catch (SAXException e) {
      System.out.println("SAX Exception in formatLineNumber");
    }
  }

  /**
   * <p>Insert text callouts into a verbatim environment.</p>
   *
   * <p>This method examines the <tt>areaset</tt> and <tt>area</tt> elements
   * in the supplied <tt>areaspec</tt> and decorates the supplied
   * result tree fragment with appropriate callout markers.</p>
   *
   * <p>If a <tt>label</tt> attribute is supplied on an <tt>area</tt>,
   * its content will be used for the label, otherwise the callout
   * number will be used, surrounded by parenthesis. Callouts are
   * numbered in document order. All of the <tt>area</tt>s in an
   * <tt>areaset</tt> get the same number.</p>
   *
   * <p>Only the <tt>linecolumn</tt> and <tt>linerange</tt> units are
   * supported. If no unit is specifed, <tt>linecolumn</tt> is assumed.
   * If only a line is specified, the callout decoration appears in
   * the defaultColumn. Lines will be padded with blanks to reach the
   * necessary column, but callouts that are located beyond the last
   * line of the verbatim environment will be ignored.</p>
   *
   * <p>Callouts are inserted before the character at the line/column
   * where they are to occur.</p>
   *
   * @param areaspecNodeSet The source node set that contains the areaspec.
   * @param xalanRTF The result tree fragment of the verbatim environment.
   * @param defaultColumn The column for callouts that specify only a line.
   *
   * @return The modified result tree fragment.  */

  /**
   * <p>Insert graphical callouts into a verbatim environment.</p>
   *
   * <p>This method examines the <tt>areaset</tt> and <tt>area</tt> elements
   * in the supplied <tt>areaspec</tt> and decorates the supplied
   * result tree fragment with appropriate callout markers.</p>
   *
   * <p>If a <tt>label</tt> attribute is supplied on an <tt>area</tt>,
   * its content will be used for the label, otherwise the callout
   * number will be used. Callouts are
   * numbered in document order. All of the <tt>area</tt>s in an
   * <tt>areaset</tt> get the same number.</p>
   *
   * <p>If the callout number is not greater than <tt>gMax</tt>, the
   * callout generated will be:</p>
   *
   * <pre>
   * &lt;img src="$gPath/conumber$gExt" alt="conumber">
   * </pre>
   *
   * <p>Otherwise, it will be the callout number surrounded by
   * parenthesis.</p>
   *
   * <p>Only the <tt>linecolumn</tt> and <tt>linerange</tt> units are
   * supported. If no unit is specifed, <tt>linecolumn</tt> is assumed.
   * If only a line is specified, the callout decoration appears in
   * the defaultColumn. Lines will be padded with blanks to reach the
   * necessary column, but callouts that are located beyond the last
   * line of the verbatim environment will be ignored.</p>
   *
   * <p>Callouts are inserted before the character at the line/column
   * where they are to occur.</p>
   *
   * @param areaspecNodeSet The source node set that contains the areaspec.
   * @param xalanRTF The result tree fragment of the verbatim environment.
   * @param defaultColumn The column for callouts that specify only a line.
   * @param gPath The path to use for callout graphics.
   * @param gExt The extension to use for callout graphics.
   * @param gMax The largest number that can be represented as a graphic.
   * @param useFO Should fo:external-graphics be produced, as opposed to
   * HTML imgs. This is bogus, the extension should figure it out, but I
   * haven't figured out how to do that yet.
   *
   * @return The modified result tree fragment.
   */

  public DocumentFragment insertCallouts (ExpressionContext context,
            NodeIterator areaspecNodeSet,
            NodeIterator xalanNI) {
    String type = Params.getString(context, "stylesheet.result.type");
    boolean useFO = type.equals("fo");
    int defaultColumn = Params.getInt(context, "callout.defaultcolumn");

    if (Params.getBoolean(context, "callout.graphics")) {
      String gPath = Params.getString(context, "callout.graphics.path");
      String gExt = Params.getString(context, "callout.graphics.extension");
      int gMax = Params.getInt(context, "callout.graphics.number.limit");
      return insertGraphicCallouts(areaspecNodeSet, xalanNI, defaultColumn,
           gPath, gExt, gMax, useFO);
    } else if (Params.getBoolean(context, "callout.unicode")) {
      int uStart = Params.getInt(context, "callout.unicode.start.character");
      int uMax = Params.getInt(context, "callout.unicode.number.limit");
      return insertUnicodeCallouts(areaspecNodeSet, xalanNI, defaultColumn,
           uStart, uMax, useFO);
    } else if (Params.getBoolean(context, "callout.dingbats")) {
      int dMax = 10;
      return insertDingbatCallouts(areaspecNodeSet, xalanNI, defaultColumn,
           dMax, useFO);
    } else {
      return insertTextCallouts(areaspecNodeSet, xalanNI, defaultColumn, useFO);
    }
  }

  public DocumentFragment insertGraphicCallouts (NodeIterator areaspecNodeSet,
             NodeIterator xalanNI,
             int defaultColumn,
             String gPath,
             String gExt,
             int gMax,
             boolean useFO) {
    FormatGraphicCallout fgc = new FormatGraphicCallout(gPath,gExt,gMax,useFO);
    return insertCallouts(areaspecNodeSet, xalanNI, defaultColumn, fgc);
  }

  public DocumentFragment insertUnicodeCallouts (NodeIterator areaspecNodeSet,
             NodeIterator xalanNI,
             int defaultColumn,
             int uStart,
             int uMax,
             boolean useFO) {
    FormatUnicodeCallout fuc = new FormatUnicodeCallout(uStart, uMax, useFO);
    return insertCallouts(areaspecNodeSet, xalanNI, defaultColumn, fuc);
  }

  public DocumentFragment insertDingbatCallouts (NodeIterator areaspecNodeSet,
             NodeIterator xalanNI,
             int defaultColumn,
             int gMax,
             boolean useFO) {
    FormatDingbatCallout fdc = new FormatDingbatCallout(gMax,useFO);
    return insertCallouts(areaspecNodeSet, xalanNI, defaultColumn, fdc);
  }

  public DocumentFragment insertTextCallouts (NodeIterator areaspecNodeSet,
                NodeIterator xalanNI,
                int defaultColumn,
                boolean useFO) {
    FormatTextCallout ftc = new FormatTextCallout(useFO);
    return insertCallouts(areaspecNodeSet, xalanNI, defaultColumn, ftc);
  }

  public DocumentFragment insertCallouts (NodeIterator areaspecNodeSet,
            NodeIterator xalanNI,
            int defaultColumn,
            FormatCallout fCallout) {

    DocumentFragment xalanRTF = (DocumentFragment) xalanNI.nextNode();

    callout = new Callout[10];
    calloutCount = 0;
    calloutPos = 0;
    lineNumber = 1;
    colNumber = 1;

    // First we walk through the areaspec to calculate the position
    // of the callouts
    //  <areaspec>
    //  <areaset id="ex.plco.const" coords="">
    //    <area id="ex.plco.c1" coords="4"/>
    //    <area id="ex.plco.c2" coords="8"/>
    //  </areaset>
    //  <area id="ex.plco.ret" coords="12"/>
    //  <area id="ex.plco.dest" coords="12"/>
    //  </areaspec>
    int pos = 0;
    int coNum = 0;
    boolean inAreaSet = false;
    Node node = areaspecNodeSet.nextNode();
    node = node.getFirstChild();
    while (node != null) {
      if (node.getNodeType() == Node.ELEMENT_NODE) {
  if (node.getNodeName().equals("areaset")) {
    coNum++;
    Node area = node.getFirstChild();
    while (area != null) {
      if (area.getNodeType() == Node.ELEMENT_NODE) {
        if (area.getNodeName().equals("area")) {
    addCallout(coNum, area, defaultColumn);
        } else {
    System.out.println("Unexpected element in areaset: "
           + area.getNodeName());
        }
      }
      area = area.getNextSibling();
    }
  } else if (node.getNodeName().equalsIgnoreCase("area")) {
    coNum++;
    addCallout(coNum, node, defaultColumn);
  } else {
    System.out.println("Unexpected element in areaspec: "
           + node.getNodeName());
  }
      }

      node = node.getNextSibling();
    }

    // Now sort them
    java.util.Arrays.sort(callout, 0, calloutCount);

    DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
    DocumentBuilder docBuilder = null;

    try {
      docBuilder = docFactory.newDocumentBuilder();
    } catch (ParserConfigurationException e) {
      System.out.println("PCE 2!");
      return xalanRTF;
    }
    Document doc = docBuilder.newDocument();
    DocumentFragment df = doc.createDocumentFragment();
    DOMBuilder db = new DOMBuilder(doc, df);

    elementStack = new Stack();
    calloutFragment(db, xalanRTF, fCallout);
    return df;
  }

  /**
   * <p>Build a FragmentValue with callout decorations.</p>
   *
   * <p>This is the method that actually does the work of adding
   * callouts to a verbatim environment. It recursively walks through a
   * tree of nodes, copying the structure into the rtf. Text nodes
   * are examined for the position of callouts as described by the
   * global callout parameters.</p>
   *
   * <p>When called, rtf should be an empty FragmentValue and node
   * should be the first child of the result tree fragment that contains
   * the existing, formatted verbatim text.</p>
   *
   * @param rtf The resulting verbatim environment with numbered lines.
   * @param node The root of the tree to copy.
   */
  private void calloutFragment(DOMBuilder rtf,
             Node node,
             FormatCallout fCallout) {
    try {
      if (node.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE
  || node.getNodeType() == Node.DOCUMENT_NODE) {
  Node child = node.getFirstChild();
  while (child != null) {
    calloutFragment(rtf, child, fCallout);
    child = child.getNextSibling();
  }
      } else if (node.getNodeType() == Node.ELEMENT_NODE) {
  String ns = node.getNamespaceURI();
  String localName = node.getLocalName();
  String name = ((Element) node).getTagName();

  rtf.startElement(ns, localName, name,
       copyAttributes((Element) node));

  elementStack.push(node);

  Node child = node.getFirstChild();
  while (child != null) {
    calloutFragment(rtf, child, fCallout);
    child = child.getNextSibling();
  }
      } else if (node.getNodeType() == Node.TEXT_NODE) {
  String text = node.getNodeValue();

  char chars[] = text.toCharArray();
  int pos = 0;
  for (int count = 0; count < text.length(); count++) {
    if (calloutPos < calloutCount
        && callout[calloutPos].getLine() == lineNumber
        && callout[calloutPos].getColumn() == colNumber) {
      if (pos > 0) {
        rtf.characters(chars, 0, pos);
        pos = 0;
      }

      closeOpenElements(rtf);

      while (calloutPos < calloutCount
       && callout[calloutPos].getLine() == lineNumber
       && callout[calloutPos].getColumn() == colNumber) {
        fCallout.formatCallout(rtf, callout[calloutPos]);
        calloutPos++;
      }

      openClosedElements(rtf);
    }

    if (text.charAt(count) == '\n') {
      // What if we need to pad this line?
      if (calloutPos < calloutCount
    && callout[calloutPos].getLine() == lineNumber
    && callout[calloutPos].getColumn() > colNumber) {

        if (pos > 0) {
    rtf.characters(chars, 0, pos);
    pos = 0;
        }

        closeOpenElements(rtf);

        while (calloutPos < calloutCount
         && callout[calloutPos].getLine() == lineNumber
         && callout[calloutPos].getColumn() > colNumber) {
    formatPad(rtf, callout[calloutPos].getColumn() - colNumber);
    colNumber = callout[calloutPos].getColumn();
    while (calloutPos < calloutCount
           && callout[calloutPos].getLine() == lineNumber
           && callout[calloutPos].getColumn() == colNumber) {
      fCallout.formatCallout(rtf, callout[calloutPos]);
      calloutPos++;
    }
        }

        openClosedElements(rtf);
      }

      lineNumber++;
      colNumber = 1;
    } else {
      colNumber++;
    }
    chars[pos++] = text.charAt(count);
  }

  if (pos > 0) {
    rtf.characters(chars, 0, pos);
  }
      } else if (node.getNodeType() == Node.COMMENT_NODE) {
  String text = node.getNodeValue();
  char chars[] = text.toCharArray();
  rtf.comment(chars, 0, text.length());
      } else if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) {
  rtf.processingInstruction(node.getNodeName(), node.getNodeValue());
      } else {
  System.out.println("Warning: unexpected node type in calloutFragment: " + node.getNodeType() + ": " + node.getNodeName());
      }

      if (node.getNodeType() == Node.ELEMENT_NODE) {
  String ns = node.getNamespaceURI();
  String localName = node.getLocalName();
  String name = ((Element) node).getTagName();
  rtf.endElement(ns, localName, name);
  elementStack.pop();
      } else {
  // nop
      }
    } catch (SAXException e) {
      System.out.println("SAX Exception in calloutFragment");
    }
  }

  /**
   * <p>Add a callout to the global callout array</p>
   *
   * <p>This method examines a callout <tt>area</tt> and adds it to
   * the global callout array if it can be interpreted.</p>
   *
   * <p>Only the <tt>linecolumn</tt> and <tt>linerange</tt> units are
   * supported. If no unit is specifed, <tt>linecolumn</tt> is assumed.
   * If only a line is specified, the callout decoration appears in
   * the <tt>defaultColumn</tt>.</p>
   *
   * @param coNum The callout number.
   * @param node The <tt>area</tt>.
   * @param defaultColumn The default column for callouts.
   */
  private void addCallout (int coNum,
         Node node,
         int defaultColumn) {
    Element area = (Element) node;

    String units = area.getAttribute("units");
    String otherUnits = area.getAttribute("otherunits");
    String coords = area.getAttribute("coords");
    int type = 0;
    String otherType = null;

    if (units == null || units.equals("linecolumn")) {
      type = Callout.LINE_COLUMN; // the default
    } else if (units.equals("linerange")) {
      type = Callout.LINE_RANGE;
    } else if (units.equals("linecolumnpair")) {
      type = Callout.LINE_COLUMN_PAIR;
    } else if (units.equals("calspair")) {
      type = Callout.CALS_PAIR;
    } else {
      type = Callout.OTHER;
      otherType = otherUnits;
    }

    if (type != Callout.LINE_COLUMN
  && type != Callout.LINE_RANGE) {
      System.out.println("Only linecolumn and linerange units are supported");
      return;
    }

    if (coords == null) {
      System.out.println("Coords must be specified");
      return;
    }

    // Now let's see if we can interpret the coordinates...
    StringTokenizer st = new StringTokenizer(coords);
    int tokenCount = 0;
    int c1 = 0;
    int c2 = 0;
    while (st.hasMoreTokens()) {
      tokenCount++;
      if (tokenCount > 2) {
  System.out.println("Unparseable coordinates");
  return;
      }
      try {
  String token = st.nextToken();
  int coord = Integer.parseInt(token);
  c2 = coord;
  if (tokenCount == 1) {
    c1 = coord;
  }
      } catch (NumberFormatException e) {
  System.out.println("Unparseable coordinate");
  return;
      }
    }

    // Make sure we aren't going to blow past the end of our array
    if (calloutCount == callout.length) {
      Callout bigger[] = new Callout[calloutCount+10];
      for (int count = 0; count < callout.length; count++) {
  bigger[count] = callout[count];
      }
      callout = bigger;
    }

    // Ok, add the callout
    if (tokenCount == 2) {
      if (type == Callout.LINE_RANGE) {
  for (int count = c1; count <= c2; count++) {
    callout[calloutCount++] = new Callout(coNum, area,
            count, defaultColumn,
            type);
  }
      } else {
  // assume linecolumn
  callout[calloutCount++] = new Callout(coNum, area, c1, c2, type);
      }
    } else {
      // if there's only one number, assume it's the line
      callout[calloutCount++] = new Callout(coNum, area, c1, defaultColumn, type);
    }
  }

  /**
   * <p>Add blanks to the result tree fragment.</p>
   *
   * <p>This method adds <tt>numBlanks</tt> to the result tree fragment.
   * It's used to pad lines when callouts occur after the last existing
   * characater in a line.</p>
   *
   * @param rtf The resulting verbatim environment with numbered lines.
   * @param numBlanks The number of blanks to add.
   */
  private void formatPad(DOMBuilder rtf,
       int numBlanks) {
    char chars[] = new char[numBlanks];
    for (int count = 0; count < numBlanks; count++) {
      chars[count] = ' ';
    }

    try {
      rtf.characters(chars, 0, numBlanks);
    } catch (SAXException e) {
      System.out.println("SAX Exception in formatCallout");
    }
  }

  private void closeOpenElements(DOMBuilder rtf)
    throws SAXException {
    // Close all the open elements...
    tempStack = new Stack();
    while (!elementStack.empty()) {
      Node elem = (Node) elementStack.pop();

      String ns = elem.getNamespaceURI();
      String localName = elem.getLocalName();
      String name = ((Element) elem).getTagName();

      // If this is the bottom of the stack and it's an fo:block
      // or an HTML pre or div, don't duplicate it...
      if (elementStack.empty()
    && (((ns != null)
         && ns.equals(foURI)
         && localName.equals("block"))
        || (((ns == null)
       && localName.equalsIgnoreCase("pre"))
      || ((ns != null)
          && ns.equals(xhURI)
          && localName.equals("pre")))
        || (((ns == null)
       && localName.equalsIgnoreCase("div"))
      || ((ns != null)
          && ns.equals(xhURI)
          && localName.equals("div"))))) {
  elementStack.push(elem);
  break;
      } else {
  rtf.endElement(ns, localName, name);
  tempStack.push(elem);
      }
    }
  }

  private void openClosedElements(DOMBuilder rtf)
    throws SAXException {
    // Now "reopen" the elements that we closed...
    while (!tempStack.empty()) {
      Node elem = (Node) tempStack.pop();

      String ns = elem.getNamespaceURI();
      String localName = elem.getLocalName();
      String name = ((Element) elem).getTagName();
      NamedNodeMap domAttr = elem.getAttributes();

      AttributesImpl attr = new AttributesImpl();
      for (int acount = 0; acount < domAttr.getLength(); acount++) {
  Node a = domAttr.item(acount);

  if (((ns == null || ns == "http://www.w3.org/1999/xhtml")
       && localName.equalsIgnoreCase("a"))
      || (a.getLocalName().equalsIgnoreCase("id"))) {
    // skip this attribute
  } else {
    attr.addAttribute(a.getNamespaceURI(),
          a.getLocalName(),
          a.getNodeName(),
          "CDATA",
          a.getNodeValue());
  }
      }

      rtf.startElement(ns, localName, name, attr);
      elementStack.push(elem);
    }

    tempStack = null;
  }

  private Attributes copyAttributes(Element node) {
    AttributesImpl attrs = new AttributesImpl();
    NamedNodeMap nnm = node.getAttributes();
    for (int count = 0; count < nnm.getLength(); count++) {
      Attr attr = (Attr) nnm.item(count);
      String name = attr.getName();
      if (name.startsWith("xmlns:") || name.equals("xmlns")) {
  // Skip it; (don't ya just love it!!)
      } else {
  attrs.addAttribute(attr.getNamespaceURI(), attr.getName(),
         attr.getName(), "CDATA", attr.getValue());
      }
    }
    return attrs;
  }
}
TOP

Related Classes of com.nwalsh.xalan.Verbatim

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.