Package org.apache.xalan.xsltc.runtime

Source Code of org.apache.xalan.xsltc.runtime.TextOutput

/*
* @(#)$Id: TextOutput.java,v 1.61 2003/01/30 18:46:12 mkwan Exp $
*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2001-2003 The Apache Software Foundation.  All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in
*    the documentation and/or other materials provided with the
*    distribution.
*
* 3. The end-user documentation included with the redistribution,
*    if any, must include the following acknowledgment:
*       "This product includes software developed by the
*        Apache Software Foundation (http://www.apache.org/)."
*    Alternately, this acknowledgment may appear in the software itself
*    if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xalan" and "Apache Software Foundation" must
*    not be used to endorse or promote products derived from this
*    software without prior written permission. For written
*    permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
*    nor may "Apache" appear in their name, without prior written
*    permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 2001, Sun
* Microsystems., http://www.sun.com.  For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* @author Jacek Ambroziak
* @author Santiago Pericas-Geertsen
* @author Morten Jorgensen
* @author Seth Ford <seth.ford@fmr.com>
*
*/

package org.apache.xalan.xsltc.runtime;

import java.io.IOException;
import java.util.Stack;

import org.apache.xalan.xsltc.TransletException;
import org.apache.xalan.xsltc.TransletOutputHandler;

import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.AttributesImpl;

/**
* Serializer for SAX events.
*
* @deprecated This class is no longer supported in XSLTC. It has been
* replaced by <code>xsltc.runtime.output.TransletOutputHandlerFactory</code>.
*/
public final class TextOutput implements TransletOutputHandler, Constants {

    // These are the various output types we handle
    public static final int UNKNOWN = 0; // determine type from output contents
    public static final int XML     = 1;
    public static final int HTML    = 2;
    public static final int TEXT    = 3;
    public static final int QNAME   = 4// no special handling

    // These parameters are set by the <xsl:output> element, or by the
    // get/setOutputProperty() methods in TrAX
    private int     _outputType = UNKNOWN;
    private String _encoding;
    private String _mediaType = "text/html";
    private String _doctypeSystem = null;
    private String _doctypePublic = null;

    private boolean   _escapeChars = false;
    private boolean   _startTagOpen = false;
    private boolean   _headTagOpen = false;
    private boolean   _cdataTagOpen = false;
    private boolean   _is8859Encoded = false;

    // Contains all elements that should be output as CDATA sections
    private Hashtable _cdata = null;

    private static final char[] AMP      = "&amp;".toCharArray();
    private static final char[] LT       = "&lt;".toCharArray();
    private static final char[] GT       = "&gt;".toCharArray();
    private static final char[] CRLF     = "&#xA;".toCharArray();
    private static final char[] QUOTE    = "&quot;".toCharArray();
    private static final char[] NBSP     = "&nbsp;".toCharArray();

    private static final char[] BEGCDATA = "<![CDATA[".toCharArray();
    private static final char[] ENDCDATA = "]]>".toCharArray();
    private static final char[] CNTCDATA = "]]]]><![CDATA[>".toCharArray();
    private static final char[] BEGCOMM  = "<!--".toCharArray();
    private static final char[] ENDCOMM  = "-->".toCharArray();

    private static final int AMP_length   = AMP.length;
    private static final int LT_length    = LT.length;
    private static final int GT_length    = GT.length;
    private static final int CRLF_length  = CRLF.length;
    private static final int QUOTE_length = QUOTE.length;
    private static final int NBSP_length  = NBSP.length;
    private static final int BEGCOMM_length = BEGCOMM.length;
    private static final int ENDCOMM_length = ENDCOMM.length;

    private static final String HREF_STR        = "href";
    private static final String CITE_STR        = "cite";
    private static final String SRC_STR         = "src";
    private static final String CHAR_ESC_START  = "&#";
    private static final String CDATA_ESC_START = "]]>&#";
    private static final String CDATA_ESC_END   = ";<![CDATA[";

    private AttributesImpl _attributes = new AttributesImpl();
    private String         _elementName = null;

    // Each entry (prefix) in this hashtable points to a Stack of URIs
    private Hashtable _namespaces;
    // The top of this stack contains an id of the element that last declared
    // a namespace. Used to ensure prefix/uri map scopes are closed correctly
    private Stack     _nodeStack;
    // The top of this stack is the prefix that was last mapped to an URI
    private Stack     _prefixStack;

    // The top of this stack contains the QName of the currently open element
    private Stack     _qnameStack;

    // The top of this stack contains the element id of the last element whose
    // contents should be output as CDATA sections.
    private Stack     _cdataStack;

    // Holds the current tree depth (see startElement() and endElement()).
    private int _depth = 0;

    // Reference to the SAX2 handlers that consume this handler's output
    private ContentHandler _saxHandler;
    private LexicalHandler _lexHandler;

    /**
     * Creates a new translet output post-processor
     *
     * @param handler A SAX2 handler to consume the generated SAX events
     * @throws IOException
     */
    public TextOutput(ContentHandler handler) throws IOException {
        _saxHandler = handler;
        init();
    }

    /**
     * Creates a new translet output post-processor
     *
     * @param handler A SAX2 handler to consume the generated SAX events
     * @param encoding The default encoding to use (set in <xsl:output>)
     * @throws IOException
     */
    public TextOutput(ContentHandler handler, String encoding)
  throws IOException {
        _saxHandler = handler;
        init();
  _encoding = encoding;
  _is8859Encoded = _encoding.equalsIgnoreCase("iso-8859-1")
    }

    /**
     * Creates a new translet output post-processor
     *
     * @param handler A SAX2 handler to consume the generated SAX events
     * @param encoding The default encoding to use (set in <xsl:output>)
     * @throws IOException
     */
    public TextOutput(ContentHandler sax, LexicalHandler lex, String encoding)
  throws IOException {
        _saxHandler = sax;
  _lexHandler = lex;
        init();
  _encoding = encoding;
  _is8859Encoded = _encoding.equalsIgnoreCase("iso-8859-1")
    }

    /**
     * Initialise global variables
     */
    private void init() throws IOException {
  // Reset all output configuration from <xsl:output>
  _outputType = UNKNOWN;
  _encoding = "UTF-8";
  _mediaType = "text/html";

  // Reset all internal variables and tables
  _escapeChars  = false;
  _startTagOpen = false;
  _cdataTagOpen = false;
  _qnameStack = new Stack();
  _cdataStack = new Stack();
  _cdataStack.push(new Integer(-1)); // push dummy value

  // Reset our internal namespace handling
  initNamespaces();
    }

    /**
     * Closes the output stream
     */
    public void close() {
  if ((_saxHandler != null) &&
      (_saxHandler instanceof DefaultSAXOutputHandler)) {
      ((DefaultSAXOutputHandler)_saxHandler).close();
  }
    }

    /**
     * This method is used internally when the output type was initially
     * undefined and the type is set (by this handler) based on the contents
     * of the output. Set the default values for some output paramters.
     */
    private void setTypeInternal(int type) {
  if (type == XML) {
      _escapeChars = true;
  }
  else if (type == HTML) {
      setIndent(true);
      _escapeChars = true;
  }
  setType(type);
    }

    /**
     * Emit header through the SAX handler
     */
    private void emitHeader() throws SAXException {
  // Output HTML header as META element
  if (_outputType == HTML) {
      AttributeList attrs = new AttributeList();
      attrs.add("http-equiv", "Content-Type");
      attrs.add("content", _mediaType+"; charset="+_encoding);
      _saxHandler.startElement(EMPTYSTRING, EMPTYSTRING, "meta", attrs);
      _saxHandler.endElement(EMPTYSTRING, EMPTYSTRING, "meta");
  }
    }

    /**
     * This method is called when all the data needed for a call to the
     * SAX handler's startElement() method has been gathered.
     */
    public void closeStartTag() throws TransletException {
  try {
      _startTagOpen = false;

      // Now is time to send the startElement event
      _saxHandler.startElement(getNamespaceURI(_elementName, true),
    getLocalName(_elementName), _elementName, _attributes);

      // Insert <META> tag directly after <HEAD> element in HTML output
      if (_headTagOpen) {
    emitHeader();
    _headTagOpen = false;
      }
  }
  catch (SAXException e) {
      throw new TransletException(e);
  }
    }

    /**
     * Turns special character escaping on/off. Note that characters will
     * never, even if this option is set to 'true', be escaped within
     * CDATA sections in output XML documents.
     */
    public boolean setEscaping(boolean escape) throws TransletException {

  // Set output type to XML (the default) if still unknown.
  if (_outputType == UNKNOWN) setTypeInternal(XML);

  // Save old escaping setting (for the return value) and set new value
  boolean oldSetting = _escapeChars;
  _escapeChars = escape;

  // Characters are never escaped in output mode 'text'
  if (_outputType == TEXT) _escapeChars = false;

  return(oldSetting);
    }

    /**
     * Starts the output document. Outputs the document header if the
     * output type is set to XML.
     */
    public void startDocument() throws TransletException {
        try {
            _saxHandler.startDocument();
      // Output escaping is _ALWAYS_ enabled for XML output
            if (_outputType == XML) _escapeChars = true;
        } catch (SAXException e) {
            throw new TransletException(e);
        }
    }

    /**
     * Ends the document output.
     */
    public void endDocument() throws TransletException {
        try {
            // Set output type to XML (the default) if still unknown.
            if (_outputType == UNKNOWN) setTypeInternal(XML);

      // Close any open start tag
      if (_startTagOpen) closeStartTag();
      if (_cdataTagOpen) closeCDATA();

      // Close output document
            _saxHandler.endDocument();
        } catch (SAXException e) {
            throw new TransletException(e);
        }
    }

    public void characters(String str) throws TransletException {
  try {
      characters(str.toCharArray(), 0, str.length());
  }
  catch (SAXException e) {
            throw new TransletException(e);
  }
    }

    /**
     * Utility method - pass a whole character array to the SAX handler
     */
    private void characters(char[] ch) throws SAXException {
  characters(ch, 0, ch.length);
    }

    /**
     * Utility method - pass a whole charactes as CDATA to SAX handler
     */
    private void startCDATA(char[] ch, int off, int len) throws SAXException {
 
  final int limit = off + len;
  int offset = off;

  // Output start bracket - "<![CDATA["
  _saxHandler.characters(BEGCDATA, 0, BEGCDATA.length);

  // Detect any occurence of "]]>" in the character array
  for (int i = offset; i < limit-2; i++) {
      if (ch[i] == ']' && ch[i+1] == ']' && ch[i+2] == '>') {
    _saxHandler.characters(ch, offset, i - offset);
    _saxHandler.characters(CNTCDATA, 0, CNTCDATA.length);
    offset = i+3;
    i=i+2; // Skip next chars ']' and '>'.
      }
  }

  // Output the remaining characters
  if (offset < limit) _saxHandler.characters(ch, offset, limit - offset);

  _cdataTagOpen = true;
    }

    private void closeCDATA() throws SAXException {
  // Output closing bracket - "]]>"
  _saxHandler.characters(ENDCDATA, 0, ENDCDATA.length);
  _cdataTagOpen = false;
    }

    public void startCDATA() throws TransletException {
  // This class is deprecated - use runtime.output package
    }

    public void endCDATA() throws TransletException {
  // This class is deprecated - use runtime.output package
    }

    /**
     * Send characters to the output document
     */
    public void characters(char[] ch, int off, int len
  throws TransletException {
        try {
      switch(_outputType) {
      case UNKNOWN: // Set type to XML and fall through
    setTypeInternal(XML);
      case XML:
    // Close any open start tag
    if (_startTagOpen) closeStartTag();

    // Take special precautions if within a CDATA section. If we
    // encounter the sequence ']]>' within the CDATA, we need to
    // break the section in two and leave the ']]' at the end of
    // the first CDATA and '>' at the beginning of the next. Other
    // special characters/sequences are _NOT_ escaped within CDATA.
    Integer I = (Integer)_cdataStack.peek();
    if ((I.intValue() == _depth) && (!_cdataTagOpen)) {
        startCDATA(ch, off, len);
    }
    else if (_escapeChars) {
        if (_cdataTagOpen) {
      escapeCDATA(ch, off, len);
        } else {
      escapeCharacters(ch, off, len);
        }
    }
    else {
        _saxHandler.characters(ch, off, len);
    }
    return;

      case HTML:
    // Close any open start tag
    if (_startTagOpen) closeStartTag();

    // Output escaped characters if required. Non-ASCII characters
    // within HTML attributes should _NOT_ be escaped.
    if (_escapeChars) {
        if (!_qnameStack.isEmpty()) {
      String qname = (String)_qnameStack.peek();
      qname = qname.toLowerCase();
      if ((qname.equals("style"))||(qname.equals("script"))) {
          _saxHandler.characters(ch, off, len);
          return;
      }
        }
        escapeCharacters(ch, off, len);
    }
    // Output the chracters as the are
    else {
        _saxHandler.characters(ch, off, len);
    }
    return;

      case TEXT:
    _saxHandler.characters(ch, off, len);
    return;
      }
        }
  catch (SAXException e) {
            throw new TransletException(e);
        }
    }

    /**
     * Start an element in the output document. This might be an XML
     * element (<elem>data</elem> type) or a CDATA section.
     */
    public void startElement(String elementName) throws TransletException {
  try {
      switch(_outputType) {

      case UNKNOWN:
    // If we don't know the output type yet we need to examine
    // the very first element to see if it is "html".
    if (elementName.toLowerCase().equals("html"))
        setTypeInternal(HTML);
    else
        setTypeInternal(XML);
    startElement(elementName);
    return;

      case XML:
    // Close any open start tag
    if (_startTagOpen) closeStartTag();
    if (_cdataTagOpen) closeCDATA();

    // Handle document type declaration (for first element only)
    if (_lexHandler != null) {
        if (_doctypeSystem != null)
      _lexHandler.startDTD(elementName,
               _doctypePublic,_doctypeSystem);
        _lexHandler = null;
    }

    _depth++;
    _elementName = elementName;
    _attributes.clear();
    _startTagOpen = true;
    _qnameStack.push(elementName);
   
    if ((_cdata != null) && (_cdata.get(elementName) != null))
        _cdataStack.push(new Integer(_depth));
   
    return;

      case HTML:
    // Close any open start tag
    if (_startTagOpen) closeStartTag();

    // Handle document type declaration (for first element only)
    if (_lexHandler != null) {
        if ((_doctypeSystem != null) || (_doctypePublic != null))
      _lexHandler.startDTD(elementName,
               _doctypePublic,_doctypeSystem);
        _lexHandler = null;
    }

    _depth++;
    _elementName = elementName;
    _attributes.clear();
    _startTagOpen = true;
    _qnameStack.push(elementName);

    // Insert <META> tag directly after <HEAD> element in HTML doc
    if (elementName.toLowerCase().equals("head"))
        _headTagOpen = true;
    return;

      case TEXT:
    // Do not output element tags if output mode is 'text'
    return;
   
      }
  }
  catch (SAXException e) {
            throw new TransletException(e);
        }
    }

    /**
     * Utility method - escape special characters and pass to SAX handler
     */
    private void escapeCharacters(char[] ch, int off, int len)
  throws SAXException {

  int limit = off + len;
  int offset = off;

  if (limit > ch.length) limit = ch.length;;


  // Step through characters and escape all special characters
  for (int i = off; i < limit; i++) {
      char current = ch[i];
      switch (current) {
      case '&':
    _saxHandler.characters(ch, offset, i - offset);
    _saxHandler.characters(AMP, 0, AMP_length);
    offset = i + 1;
    break;
      case '<':
    _saxHandler.characters(ch, offset, i - offset);
    _saxHandler.characters(LT, 0, LT_length);
    offset = i + 1;
    break;
      case '>':
    _saxHandler.characters(ch, offset, i - offset);
    _saxHandler.characters(GT, 0, GT_length);
    offset = i + 1;
    break;
      case '\u00a0':
    _saxHandler.characters(ch, offset, i - offset);
    _saxHandler.characters(NBSP, 0, NBSP_length);
    offset = i + 1;
    break;
      default:
    if ( (current >= '\u007F' && current < '\u00A0') ||
         (_is8859Encoded && (current > '\u00FF')) )
    {
        StringBuffer buf = new StringBuffer(CHAR_ESC_START);
        buf.append(Integer.toString((int)ch[i]));
        buf.append(';');
        final String esc = buf.toString();
        final char[] chars = esc.toCharArray();
        final int    strlen = esc.length();
        _saxHandler.characters(ch, offset, i - offset);
        _saxHandler.characters(chars, 0, strlen);
        offset = i + 1;
    }
      }
  }
  // Output remaining characters (that do not need escaping).
  if (offset < limit) _saxHandler.characters(ch, offset, limit - offset);
    }

    /**
     * Utility method - escape special characters and pass to SAX handler
     */
    private void escapeCDATA(char[] ch, int off, int len)
  throws SAXException {

  int limit = off + len;
  int offset = off;

  if (limit > ch.length) limit = ch.length;;

  // Step through characters and escape all special characters
  for (int i = off; i < limit; i++) {
      if (ch[i] > '\u00ff') {
    StringBuffer buf = new StringBuffer(CDATA_ESC_START);
    buf.append(Integer.toString((int)ch[i]));
    buf.append(CDATA_ESC_END);
    final String esc = buf.toString();
    final char[] chars = esc.toCharArray();
    final int    strlen = esc.length();
    _saxHandler.characters(ch, offset, i - offset);
    _saxHandler.characters(chars, 0, strlen);
    offset = i + 1;
      }
  }
  // Output remaining characters (that do not need escaping).
  if (offset < limit) _saxHandler.characters(ch, offset, limit - offset);
    }

    /**
     * This method escapes special characters used in attribute values
     */
    private String escapeString(String value) {

  int i;
  char[] ch = value.toCharArray();
  int limit = ch.length;
  int offset = 0;
  StringBuffer buf = new StringBuffer();
 
  for (i = 0; i < limit; i++) {
      switch (ch[i]) {
      case '&':
    buf.append(ch, offset, i - offset);
    buf.append(AMP);
    offset = i + 1;
    break;
      case '"':
    buf.append(ch, offset, i - offset);
    buf.append(QUOTE);
    offset = i + 1;
    break;
      case '<':
    buf.append(ch, offset, i - offset);
    buf.append(LT);
    offset = i + 1;
    break;
      case '>':
    buf.append(ch, offset, i - offset);
    buf.append(GT);
    offset = i + 1;
    break;
      case '\n':
    buf.append(ch, offset, i - offset);
    buf.append(CRLF);
    offset = i + 1;
    break;
      }
  }
  if (offset < limit) {
      buf.append(ch, offset, limit - offset);
  }
  return(buf.toString());
    }

    private String makeHHString(int i) {
  String s = Integer.toHexString(i).toUpperCase();
  if (s.length() == 1) {
      s = "0"+s;
  }
  return s;
    }

    /**
     * This method escapes special characters used in HTML attribute values
     */
    private String escapeAttr(String base) {
  final int len = base.length() - 1;
  final String str = "&quot;";
  int pos;

  char[] ch = base.toCharArray();
  StringBuffer buf = new StringBuffer();
        for(int i=0; i<base.length(); i++){
      if (ch[i] <= 0x20) {
    buf.append('%');
    buf.append(makeHHString(ch[i]));
      }
      else if (ch[i] > '\u007F') {
    int high = (ch[i] >> 6) | 0xC0;
    int low  = (ch[i] & 0x3F) | 0x80// First 6 bits + high bit
    buf.append('%');
    buf.append(makeHHString(high));
    buf.append('%');
    buf.append(makeHHString(low));
      }
      else {
    // These chars are reserved or unsafe in URLs
    // pg 196, 'HTML, The Definitive Guide" C. Musciano, et. al
          switch (ch[i]) {
        case '\u007F' :
        case '\u0022' :
        case '\u007B' :
        case '\u007D' :
        case '\u007C' :
        case '\\'     :
        case '\t'     :
        case '\u005E' :
        case '\u007E' :
        case '\u005B' :
        case '\u005D' :
        case '\u0060' :
            buf.append('%');
            buf.append(Integer.toHexString((int)ch[i]));
            break;
        case '\u0026' :
      //bug fix for customer/murphy3: buf.append("&amp;");
      buf.append("&");
      break;
        default
            buf.append(ch[i]); break;
          }
      }
    }
  base = buf.toString();

  while ((pos = base.indexOf('"')) > -1) {
      if (pos == 0) {
    final String after = base.substring(1);
    base = str + after;
      }
      else if (pos == len) {
    final String before = base.substring(0, pos);
    base = before + str;
      }
      else {
    final String before = base.substring(0, pos);
    final String after = base.substring(pos+1);
    base = before + str + after;
      }
  }
  return base;
    }

    /**
     * Replaces whitespaces in a URL with '%20'
     */
    private String quickAndDirtyUrlEncode(String base) {
  final String pst20 = "%20";
  final int len = base.length() - 1;
  int pos;
  // Slow, very slow indeed
  while ((pos = base.indexOf(' ')) > -1) {
      if (pos == 0) {
    final String after = base.substring(1);
    base = pst20 + after;
      }
      else if (pos == len) {
    final String before = base.substring(0, pos);
    base = before + pst20;
      }
      else {
    final String before = base.substring(0, pos);
    final String after = base.substring(pos+1);
    base = before + pst20 + after;
      }
  }
  return base;
    }

    /**
     * Returns the URI of an element or attribute. Note that default namespaces
     * do not apply directly to attributes.
     */
    private String getNamespaceURI(String qname, boolean isElement)
  throws TransletException
    {
  String uri = EMPTYSTRING;
  int col = qname.lastIndexOf(':');
  final String prefix = (col > 0) ? qname.substring(0, col) : EMPTYSTRING;

  if (prefix != EMPTYSTRING || isElement) {
      uri = lookupNamespace(prefix);
      if (uri == null && !prefix.equals(XMLNS_PREFIX)) {
    BasisLibrary.runTimeError(BasisLibrary.NAMESPACE_PREFIX_ERR,
            qname.substring(0, col));
      }
  }
  return uri;
    }

    /**
     * Returns the local name of a qualified name. If the name has no prefix
     * then return null.
     */
    private static String getLocalName(String qname) throws TransletException {
  final int col = qname.lastIndexOf(':');
  return (col > 0) ? qname.substring(col + 1) : null;
    }

    /**
     * TODO: This method is a HACK! Since XSLTC does not have access to the
     * XML file, it sometimes generates a NS prefix of the form "ns?" for
     * an attribute. If at runtime, when the qname of the attribute is
     * known, another prefix is specified for the attribute, then we can get
     * a qname of the form "ns?:otherprefix:name". This function patches the
     * qname by simply ignoring "otherprefix".
     */
    private static String patchQName(String qname) throws TransletException {
  final int lastColon = qname.lastIndexOf(':');
  if (lastColon > 0) {
      final int firstColon = qname.indexOf(':');
      if (firstColon != lastColon) {
    return qname.substring(0, firstColon) + qname.substring(lastColon);
      }
  }
  return qname;
    }

    /**
     * Put an attribute and its value in the start tag of an element.
     * Signal an exception if this is attempted done outside a start tag.
     */
    public void attribute(String name, final String value)
  throws TransletException {

  if (_outputType == TEXT) return;

  final String patchedName = patchQName(name);
  final String localName = getLocalName(patchedName);
  final String uri = getNamespaceURI(patchedName, false);
  final int index = (localName == null) ?
        _attributes.getIndex(name) /* don't use patchedName */
        _attributes.getIndex(uri, localName);

  switch(_outputType) {
  case XML:
      if (!_startTagOpen) {
    BasisLibrary.runTimeError(BasisLibrary.STRAY_ATTRIBUTE_ERR, patchedName);
      }

      // Output as namespace declaration
      if (name.startsWith(XMLNS_PREFIX)) {
    namespace(name.length() > 6 ? name.substring(6) : EMPTYSTRING, value);
      }
      else {
    if (index >= 0) {  // Duplicate attribute?
        _attributes.setAttribute(index, uri, localName, patchedName, "CDATA",
      escapeString(value))
    }
    else {
        _attributes.addAttribute(uri, localName, patchedName, "CDATA",
      escapeString(value));
    }
      }
      break;
  case HTML:
      if (!_startTagOpen) {
    BasisLibrary.runTimeError(BasisLibrary.STRAY_ATTRIBUTE_ERR,name);
      }

      /*
       * The following is an attempt to escape an URL stored in a href
       * attribute of HTML output. Normally URLs should be encoded at
       * the time they are created, since escaping or unescaping a
       * completed URI might change its semantics. We limit or escaping
       * to include space characters only - and nothing else. This is for
       * two reasons: (1) performance and (2) we want to make sure that
       * we do not change the meaning of the URL.
       */
      final String tmp = name.toLowerCase();
      if (tmp.equals(HREF_STR) || tmp.equals(SRC_STR) || tmp.equals(CITE_STR)) {
    if (index >= 0) {
        _attributes.setAttribute(index, EMPTYSTRING, EMPTYSTRING, name,
      "CDATA", quickAndDirtyUrlEncode(escapeAttr(value)));
    }
    else {
        _attributes.addAttribute(EMPTYSTRING, EMPTYSTRING, name, "CDATA",
      quickAndDirtyUrlEncode(escapeAttr(value)));
    }
      }
      else {
    if (index >= 0) {
        _attributes.setAttribute(index, EMPTYSTRING, EMPTYSTRING,
      name, "CDATA", escapeNonURLAttr(value));
    }
    else {
        _attributes.addAttribute(EMPTYSTRING, EMPTYSTRING,
      name, "CDATA", escapeNonURLAttr(value));
    }
      }
      break;
  }
    }

    /**
     * Escape non ASCII characters (> u007F) as &#XXX; entities.
     */
    private String escapeNonURLAttr(String base) {
  final int len = base.length() - 1;

  char[] ch = base.toCharArray();
  StringBuffer buf = new StringBuffer();
        for(int i=0; i<base.length(); i++){
      if (ch[i] > '\u007F') {
          buf.append(CHAR_ESC_START);
    buf.append(Integer.toString((int)ch[i]));
          buf.append(';');
      }
      else {
          buf.append(ch[i]);
      }
    }
  base = buf.toString();
  return base;
    }


    /**
     * End an element or CDATA section in the output document
     */
    public void endElement(String elementName) throws TransletException {
  try {
      switch(_outputType) {
      case TEXT:
    // Do not output element tags if output mode is 'text'
    return;
      case XML:
    // Close any open element
    if (_startTagOpen) closeStartTag();
    if (_cdataTagOpen) closeCDATA();

    final String qname = (String) _qnameStack.pop();
    _saxHandler.endElement(getNamespaceURI(qname, true),
        getLocalName(qname), qname);

    popNamespaces();
    if (((Integer)_cdataStack.peek()).intValue() == _depth)
        _cdataStack.pop();
    _depth--;
    return;
      case HTML:
    // Close any open element
    if (_startTagOpen) closeStartTag();
    _saxHandler.endElement(EMPTYSTRING, EMPTYSTRING,
        (String)(_qnameStack.pop()));
    popNamespaces();
    _depth--;   
    return;
      }

        } catch (SAXException e) {
            throw new TransletException(e);
        }
    }

    /**
     * Send a HTML-style comment to the output document
     */
    public void comment(String comment) throws TransletException {
  try {
      // Close any open element before emitting comment
            if (_startTagOpen) closeStartTag();
      if (_cdataTagOpen) closeCDATA();

            // Set output type to XML (the default) if still unknown.
            if (_outputType == UNKNOWN) setTypeInternal(XML);

            // ...and then output the comment.
            _saxHandler.characters(BEGCOMM, 0, BEGCOMM_length);
            characters(comment);
            _saxHandler.characters(ENDCOMM, 0, ENDCOMM_length);
  }
  catch (SAXException e) {
      throw new TransletException(e);
  }
    }
   
    /**
     * Send a processing instruction to the output document
     */
    public void processingInstruction(String target, String data)
  throws TransletException {
        try {
      // Close any open element
      if (_startTagOpen) closeStartTag();
      if (_cdataTagOpen) closeCDATA();

      // Handle document type declaration (only HTML at this point)
      if ((_lexHandler != null) && (_outputType == HTML)) {
    if ((_doctypeSystem != null) || (_doctypePublic != null))
        _lexHandler.startDTD("HTML",_doctypePublic,_doctypeSystem);
    _lexHandler = null;
      }

      // Pass the processing instruction to the SAX handler
            _saxHandler.processingInstruction(target, data);
        }
  catch (SAXException e) {
            throw new TransletException(e);
        }
    }

    /**
     * Initialize namespace stacks
     */
    private void initNamespaces() {
  _namespaces = new Hashtable();
  _nodeStack = new Stack();
  _prefixStack = new Stack();

  // Define the default namespace (initially maps to "" uri)
  Stack stack;
  _namespaces.put(EMPTYSTRING, stack = new Stack());
  stack.push(EMPTYSTRING);
  _prefixStack.push(EMPTYSTRING);

  _namespaces.put(XML_PREFIX, stack = new Stack());
  stack.push("http://www.w3.org/XML/1998/namespace");
  _prefixStack.push(XML_PREFIX);

  _nodeStack.push(new Integer(-1));
  _depth = 0;
    }

    /**
     * Declare a prefix to point to a namespace URI
     */
    private void pushNamespace(String prefix, String uri) throws SAXException {
  // Prefixes "xml" and "xmlns" cannot be redefined
  if (prefix.equals(XML_PREFIX)) return;
 
  Stack stack;
  // Get the stack that contains URIs for the specified prefix
  if ((stack = (Stack)_namespaces.get(prefix)) == null) {
      stack = new Stack();
      _namespaces.put(prefix, stack);
  }
  // Quit now if the URI the prefix currently maps to is the same as this
  if (!stack.empty() && uri.equals(stack.peek())) return;
  // Put this URI on top of the stack for this prefix
  stack.push(uri);

  _prefixStack.push(prefix);
  _nodeStack.push(new Integer(_depth));

  // Inform the SAX handler
  _saxHandler.startPrefixMapping(prefix, escapeString(uri));
    }

    /**
     * Undeclare the namespace that is currently pointed to by a given prefix
     */
    private void popNamespace(String prefix) throws SAXException {
  // Prefixes "xml" and "xmlns" cannot be redefined
  if (prefix.equals(XML_PREFIX)) return;

  Stack stack;
  if ((stack = (Stack)_namespaces.get(prefix)) != null) {
      stack.pop();
      _saxHandler.endPrefixMapping(prefix);
  }
    }

    /**
     * Pop all namespace definitions that were delcared by the current element
     */
    private void popNamespaces() throws TransletException {
  try {
      while (true) {
    if (_nodeStack.isEmpty()) return;
    Integer i = (Integer)(_nodeStack.peek());
    if (i.intValue() != _depth) return;
    _nodeStack.pop();
    popNamespace((String)_prefixStack.pop());
      }
  }
  catch (SAXException e) {
      throw new TransletException(e);
  }
    }

    /**
     * Use a namespace prefix to lookup a namespace URI
     */
    private String lookupNamespace(String prefix) {
  final Stack stack = (Stack)_namespaces.get(prefix);
  return stack != null && !stack.isEmpty() ? (String)stack.peek() : null;
    }

    /**
     * Send a namespace declaration in the output document. The namespace
     * declaration will not be include if the namespace is already in scope
     * with the same prefix.
     */
    public void namespace(final String prefix, final String uri)
  throws TransletException {
  try {
      if (_startTagOpen)
    pushNamespace(prefix, uri);
      else {
    if ((prefix == EMPTYSTRING) && (uri == EMPTYSTRING)) return;
    BasisLibrary.runTimeError(BasisLibrary.STRAY_NAMESPACE_ERR,
            prefix, uri);
      }
  }
  catch (SAXException e) {
      throw new TransletException(e);
  }
    }

    /************************************************************************
     * The following are all methods for configuring the output settings
     ************************************************************************/
    /**
     * Set the output type. The type must be wither TEXT, XML or HTML.
     */
    public void setType(int type)  {
  try {
      _outputType = type;
      if ((_outputType == HTML) || (_outputType == XML))
    _escapeChars = true;
      if (_encoding == null)
        _encoding = "UTF-8";
      if (_saxHandler instanceof DefaultSAXOutputHandler)
    ((DefaultSAXOutputHandler)_saxHandler).setOutputType(type);
  }
  catch (SAXException e) { }
    }

    /**
     * Turns output indentation on/off. Should only be set to on
     * if the output type is XML or HTML.
     */
    public void setIndent(boolean indent) {
  if (_saxHandler instanceof DefaultSAXOutputHandler) {
            ((DefaultSAXOutputHandler)_saxHandler).setIndent(indent);
  }
    }

    /**
     * Directive to turn xml header declaration  on/off.
     */
    public void omitHeader(boolean value) {
  if (_saxHandler instanceof DefaultSAXOutputHandler) {
            ((DefaultSAXOutputHandler)_saxHandler).omitHeader(value);
  }
    }

    /**
     * Set the XML output document version - should be 1.0
     */
    public void setVersion(String version) {
  if (_saxHandler instanceof DefaultSAXOutputHandler) {
            ((DefaultSAXOutputHandler)_saxHandler).setVersion(version);
  }
    }

    /**
     * Set the XML standalone attribute - must be "yes" or "no"
     */
    public void setStandalone(String standalone) {
  if (_saxHandler instanceof DefaultSAXOutputHandler) {
            ((DefaultSAXOutputHandler)_saxHandler).setStandalone(standalone);
  }
    }

    /**
     * Set the output document system/public identifiers
     */
    public void setDoctype(String system, String pub) {
  _doctypeSystem = system;
  _doctypePublic = pub;
    }

    /**
     * Set the output media type - only relevant for HTML output
     */
    public void setMediaType(String mediaType) {
  // This value does not have to be passed to the SAX handler. This
  // handler creates the HTML <meta> tag in which the media-type
  // (MIME-type) will be used.
  _mediaType = mediaType;
    }

    /**
     * The <xsl:output method="xml"/> instruction can specify that certain
     * XML elements should be output as CDATA sections. This methods allows
     * the translet to insert these elements into a hashtable of strings.
     * Every output element is looked up in this hashtable before it is
     * output.
     */
    public void setCdataElements(Hashtable elements) {
  _cdata = elements;
    }

}
TOP

Related Classes of org.apache.xalan.xsltc.runtime.TextOutput

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.