All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.xalan.processor.StylesheetHandler Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the  "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * $Id: StylesheetHandler.java 468640 2006-10-28 06:53:53Z minchau $
 */
package org.apache.xalan.processor;

import java.util.Stack;

import javax.xml.transform.ErrorListener;
import javax.xml.transform.Source;
import javax.xml.transform.SourceLocator;
import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.sax.TemplatesHandler;

import org.apache.xalan.extensions.ExpressionVisitor;
import org.apache.xalan.res.XSLMessages;
import org.apache.xalan.res.XSLTErrorResources;
import org.apache.xalan.templates.Constants;
import org.apache.xalan.templates.ElemForEach;
import org.apache.xalan.templates.ElemTemplateElement;
import org.apache.xalan.templates.Stylesheet;
import org.apache.xalan.templates.StylesheetRoot;
import org.apache.xml.utils.BoolStack;
import org.apache.xml.utils.NamespaceSupport2;
import org.apache.xml.utils.NodeConsumer;
import org.apache.xml.utils.PrefixResolver;
import org.apache.xml.utils.SAXSourceLocator;
import org.apache.xml.utils.XMLCharacterRecognizer;
import org.apache.xpath.XPath;
import org.apache.xpath.compiler.FunctionTable;
import org.apache.xpath.functions.Function;

import org.w3c.dom.Node;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.NamespaceSupport;

/**
 * Initializes and processes a stylesheet via SAX events.
 * This class acts as essentially a state machine, maintaining
 * a ContentHandler stack, and pushing appropriate content
 * handlers as parse events occur.
 * @xsl.usage advanced
 */
public class StylesheetHandler extends DefaultHandler
        implements TemplatesHandler, PrefixResolver, NodeConsumer
{


  /**
   * The function table of XPath and XSLT;
   */
  private FunctionTable m_funcTable = new FunctionTable();
  
  /**
   * The flag for the setting of the optimize feature;
   */
  private boolean m_optimize = true;
  
  /**
   * The flag for the setting of the incremental feature;
   */
  private boolean m_incremental = false;
  
  /**
   * The flag for the setting of the source_location feature;
   */
  private boolean m_source_location = false;
  
  /**
   * Create a StylesheetHandler object, creating a root stylesheet
   * as the target.
   *
   * @param processor non-null reference to the transformer factory that owns this handler.
   *
   * @throws TransformerConfigurationException if a StylesheetRoot
   * can not be constructed for some reason.
   */
  public StylesheetHandler(TransformerFactoryImpl processor)
          throws TransformerConfigurationException
  {
    Class func = org.apache.xalan.templates.FuncDocument.class;
    m_funcTable.installFunction("document", func);

    // func = new org.apache.xalan.templates.FuncKey();
    // FunctionTable.installFunction("key", func);
    func = org.apache.xalan.templates.FuncFormatNumb.class;

    m_funcTable.installFunction("format-number", func);

    m_optimize =((Boolean) processor.getAttribute(
            TransformerFactoryImpl.FEATURE_OPTIMIZE)).booleanValue();
    m_incremental = ((Boolean) processor.getAttribute(
            TransformerFactoryImpl.FEATURE_INCREMENTAL)).booleanValue();
    m_source_location = ((Boolean) processor.getAttribute(
            TransformerFactoryImpl.FEATURE_SOURCE_LOCATION)).booleanValue();
    // m_schema = new XSLTSchema();
    init(processor);
    
  }

  /**
   * Do common initialization.
   *
   * @param processor non-null reference to the transformer factory that owns this handler.
   */
  void init(TransformerFactoryImpl processor)
  {
    m_stylesheetProcessor = processor;

    // Set the initial content handler.
    m_processors.push(m_schema.getElementProcessor());
    this.pushNewNamespaceSupport();

    // m_includeStack.push(SystemIDResolver.getAbsoluteURI(this.getBaseIdentifier(), null));
    // initXPath(processor, null);
  }

  /**
   * Process an expression string into an XPath.
   * Must be public for access by the AVT class.
   *
   * @param str A non-null reference to a valid or invalid XPath expression string.
   *
   * @return A non-null reference to an XPath object that represents the string argument.
   *
   * @throws javax.xml.transform.TransformerException if the expression can not be processed.
   * @see Section 4 Expressions in XSLT Specification
   */
  public XPath createXPath(String str, ElemTemplateElement owningTemplate)
          throws javax.xml.transform.TransformerException
  {
    ErrorListener handler = m_stylesheetProcessor.getErrorListener();
    XPath xpath = new XPath(str, owningTemplate, this, XPath.SELECT, handler, 
            m_funcTable);
    // Visit the expression, registering namespaces for any extension functions it includes.
    xpath.callVisitors(xpath, new ExpressionVisitor(getStylesheetRoot()));
    return xpath;
  }

  /**
   * Process an expression string into an XPath.
   *
   * @param str A non-null reference to a valid or invalid match pattern string.
   *
   * @return A non-null reference to an XPath object that represents the string argument.
   *
   * @throws javax.xml.transform.TransformerException if the pattern can not be processed.
   * @see Section 5.2 Patterns in XSLT Specification
   */
  XPath createMatchPatternXPath(String str, ElemTemplateElement owningTemplate)
          throws javax.xml.transform.TransformerException
  {
    ErrorListener handler = m_stylesheetProcessor.getErrorListener();
    XPath xpath = new XPath(str, owningTemplate, this, XPath.MATCH, handler, 
        m_funcTable);
    // Visit the expression, registering namespaces for any extension functions it includes.
    xpath.callVisitors(xpath, new ExpressionVisitor(getStylesheetRoot()));
    return xpath;    
  }

  /**
   * Given a namespace, get the corrisponding prefix from the current
   * namespace support context.
   *
   * @param prefix The prefix to look up, which may be an empty string ("") for the default Namespace.
   *
   * @return The associated Namespace URI, or null if the prefix
   *         is undeclared in this context.
   */
  public String getNamespaceForPrefix(String prefix)
  {
    return this.getNamespaceSupport().getURI(prefix);
  }

  /**
   * Given a namespace, get the corrisponding prefix.  This is here only
   * to support the {@link org.apache.xml.utils.PrefixResolver} interface,
   * and will throw an error if invoked on this object.
   *
   * @param prefix The prefix to look up, which may be an empty string ("") for the default Namespace.
   * @param context The node context from which to look up the URI.
   *
   * @return The associated Namespace URI, or null if the prefix
   *         is undeclared in this context.
   */
  public String getNamespaceForPrefix(String prefix, org.w3c.dom.Node context)
  {

    // Don't need to support this here.  Return the current URI for the prefix,
    // ignoring the context.
    assertion(true, "can't process a context node in StylesheetHandler!");

    return null;
  }

  /**
   * Utility function to see if the stack contains the given URL.
   *
   * @param stack non-null reference to a Stack.
   * @param url URL string on which an equality test will be performed.
   *
   * @return true if the stack contains the url argument.
   */
  private boolean stackContains(Stack stack, String url)
  {

    int n = stack.size();
    boolean contains = false;

    for (int i = 0; i < n; i++)
    {
      String url2 = (String) stack.elementAt(i);

      if (url2.equals(url))
      {
        contains = true;

        break;
      }
    }

    return contains;
  }

  ////////////////////////////////////////////////////////////////////
  // Implementation of the TRAX TemplatesBuilder interface.
  ////////////////////////////////////////////////////////////////////

  /**
   * When this object is used as a ContentHandler or ContentHandler, it will
   * create a Templates object, which the caller can get once
   * the SAX events have been completed.
   * @return The stylesheet object that was created during
   * the SAX event process, or null if no stylesheet has
   * been created.
   * 
   * Author Scott Boag
   *
   *
   */
  public Templates getTemplates()
  {
    return getStylesheetRoot();
  }

  /**
   * Set the base ID (URL or system ID) for the stylesheet
   * created by this builder.  This must be set in order to
   * resolve relative URLs in the stylesheet.
   *
   * @param baseID Base URL for this stylesheet.
   */
  public void setSystemId(String baseID)
  {
    pushBaseIndentifier(baseID);
  }

  /**
   * Get the base ID (URI or system ID) from where relative
   * URLs will be resolved.
   *
   * @return The systemID that was set with {@link #setSystemId}.
   */
  public String getSystemId()
  {
    return this.getBaseIdentifier();
  }

  ////////////////////////////////////////////////////////////////////
  // Implementation of the EntityResolver interface.
  ////////////////////////////////////////////////////////////////////

  /**
   * Resolve an external entity.
   *
   * @param publicId The public identifer, or null if none is
   *                 available.
   * @param systemId The system identifier provided in the XML
   *                 document.
   * @return The new input source, or null to require the
   *         default behaviour.
   *
   * @throws org.xml.sax.SAXException if the entity can not be resolved.
   */
  public InputSource resolveEntity(String publicId, String systemId)
          throws org.xml.sax.SAXException
  {
    return getCurrentProcessor().resolveEntity(this, publicId, systemId);
  }

  ////////////////////////////////////////////////////////////////////
  // Implementation of DTDHandler interface.
  ////////////////////////////////////////////////////////////////////

  /**
   * Receive notification of a notation declaration.
   *
   * 

By default, do nothing. Application writers may override this * method in a subclass if they wish to keep track of the notations * declared in a document.

* * @param name The notation name. * @param publicId The notation public identifier, or null if not * available. * @param systemId The notation system identifier. * @see org.xml.sax.DTDHandler#notationDecl */ public void notationDecl(String name, String publicId, String systemId) { getCurrentProcessor().notationDecl(this, name, publicId, systemId); } /** * Receive notification of an unparsed entity declaration. * * @param name The entity name. * @param publicId The entity public identifier, or null if not * available. * @param systemId The entity system identifier. * @param notationName The name of the associated notation. * @see org.xml.sax.DTDHandler#unparsedEntityDecl */ public void unparsedEntityDecl(String name, String publicId, String systemId, String notationName) { getCurrentProcessor().unparsedEntityDecl(this, name, publicId, systemId, notationName); } /** * Given a namespace URI, and a local name or a node type, get the processor * for the element, or return null if not allowed. * * @param uri The Namespace URI, or an empty string. * @param localName The local name (without prefix), or empty string if not namespace processing. * @param rawName The qualified name (with prefix). * * @return A non-null reference to a element processor. * * @throws org.xml.sax.SAXException if the element is not allowed in the * found position in the stylesheet. */ XSLTElementProcessor getProcessorFor( String uri, String localName, String rawName) throws org.xml.sax.SAXException { XSLTElementProcessor currentProcessor = getCurrentProcessor(); XSLTElementDef def = currentProcessor.getElemDef(); XSLTElementProcessor elemProcessor = def.getProcessorFor(uri, localName); if (null == elemProcessor && !(currentProcessor instanceof ProcessorStylesheetDoc) && ((null == getStylesheet() || Double.valueOf(getStylesheet().getVersion()).doubleValue() > Constants.XSLTVERSUPPORTED) ||(!uri.equals(Constants.S_XSLNAMESPACEURL) && currentProcessor instanceof ProcessorStylesheetElement) || getElemVersion() > Constants.XSLTVERSUPPORTED )) { elemProcessor = def.getProcessorForUnknown(uri, localName); } if (null == elemProcessor) error(XSLMessages.createMessage(XSLTErrorResources.ER_NOT_ALLOWED_IN_POSITION, new Object[]{rawName}),null);//rawName + " is not allowed in this position in the stylesheet!", return elemProcessor; } //////////////////////////////////////////////////////////////////// // Implementation of ContentHandler interface. //////////////////////////////////////////////////////////////////// /** * Receive a Locator object for document events. * This is called by the parser to push a locator for the * stylesheet being parsed. The stack needs to be popped * after the stylesheet has been parsed. We pop in * popStylesheet. * * @param locator A locator for all SAX document events. * @see org.xml.sax.ContentHandler#setDocumentLocator * @see org.xml.sax.Locator */ public void setDocumentLocator(Locator locator) { // System.out.println("pushing locator for: "+locator.getSystemId()); m_stylesheetLocatorStack.push(new SAXSourceLocator(locator)); } /** * The level of the stylesheet we are at. */ private int m_stylesheetLevel = -1; /** * Receive notification of the beginning of the document. * * @see org.xml.sax.ContentHandler#startDocument * * @throws org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. */ public void startDocument() throws org.xml.sax.SAXException { m_stylesheetLevel++; pushSpaceHandling(false); } /** m_parsingComplete becomes true when the top-level stylesheet and all * its included/imported stylesheets have been been fully parsed, as an * indication that composition/optimization/compilation can begin. * @see isStylesheetParsingComplete */ private boolean m_parsingComplete = false; /** * Test whether the _last_ endDocument() has been processed. * This is needed as guidance for stylesheet optimization * and compilation engines, which generally don't want to start * until all included and imported stylesheets have been fully * parsed. * * @return true iff the complete stylesheet tree has been built. */ public boolean isStylesheetParsingComplete() { return m_parsingComplete; } /** * Receive notification of the end of the document. * * @see org.xml.sax.ContentHandler#endDocument * * @throws org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. */ public void endDocument() throws org.xml.sax.SAXException { try { if (null != getStylesheetRoot()) { if (0 == m_stylesheetLevel) getStylesheetRoot().recompose(); } else throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_NO_STYLESHEETROOT, null)); //"Did not find the stylesheet root!"); XSLTElementProcessor elemProcessor = getCurrentProcessor(); if (null != elemProcessor) elemProcessor.startNonText(this); m_stylesheetLevel--; popSpaceHandling(); // WARNING: This test works only as long as stylesheets are parsed // more or less recursively. If we switch to an iterative "work-list" // model, this will become true prematurely. In that case, // isStylesheetParsingComplete() will have to be adjusted to be aware // of the worklist. m_parsingComplete = (m_stylesheetLevel < 0); } catch (TransformerException te) { throw new org.xml.sax.SAXException(te); } } private java.util.Vector m_prefixMappings = new java.util.Vector(); /** * Receive notification of the start of a Namespace mapping. * *

By default, do nothing. Application writers may override this * method in a subclass to take specific actions at the start of * each element (such as allocating a new tree node or writing * output to a file).

* * @param prefix The Namespace prefix being declared. * @param uri The Namespace URI mapped to the prefix. * @see org.xml.sax.ContentHandler#startPrefixMapping * * @throws org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. */ public void startPrefixMapping(String prefix, String uri) throws org.xml.sax.SAXException { // m_nsSupport.pushContext(); // this.getNamespaceSupport().declarePrefix(prefix, uri); //m_prefixMappings.add(prefix); // JDK 1.2+ only -sc //m_prefixMappings.add(uri); // JDK 1.2+ only -sc m_prefixMappings.addElement(prefix); // JDK 1.1.x compat -sc m_prefixMappings.addElement(uri); // JDK 1.1.x compat -sc } /** * Receive notification of the end of a Namespace mapping. * *

By default, do nothing. Application writers may override this * method in a subclass to take specific actions at the start of * each element (such as allocating a new tree node or writing * output to a file).

* * @param prefix The Namespace prefix being declared. * @see org.xml.sax.ContentHandler#endPrefixMapping * * @throws org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. */ public void endPrefixMapping(String prefix) throws org.xml.sax.SAXException { // m_nsSupport.popContext(); } /** * Flush the characters buffer. * * @throws org.xml.sax.SAXException */ private void flushCharacters() throws org.xml.sax.SAXException { XSLTElementProcessor elemProcessor = getCurrentProcessor(); if (null != elemProcessor) elemProcessor.startNonText(this); } /** * Receive notification of the start of an element. * * @param uri The Namespace URI, or an empty string. * @param localName The local name (without prefix), or empty string if not namespace processing. * @param rawName The qualified name (with prefix). * @param attributes The specified or defaulted attributes. * * @throws org.xml.sax.SAXException */ public void startElement( String uri, String localName, String rawName, Attributes attributes) throws org.xml.sax.SAXException { NamespaceSupport nssupport = this.getNamespaceSupport(); nssupport.pushContext(); int n = m_prefixMappings.size(); for (int i = 0; i < n; i++) { String prefix = (String)m_prefixMappings.elementAt(i++); String nsURI = (String)m_prefixMappings.elementAt(i); nssupport.declarePrefix(prefix, nsURI); } //m_prefixMappings.clear(); // JDK 1.2+ only -sc m_prefixMappings.removeAllElements(); // JDK 1.1.x compat -sc m_elementID++; // This check is currently done for all elements. We should possibly consider // limiting this check to xsl:stylesheet elements only since that is all it really // applies to. Also, it could be bypassed if m_shouldProcess is already true. // In other words, the next two statements could instead look something like this: // if (!m_shouldProcess) // { // if (localName.equals(Constants.ELEMNAME_STYLESHEET_STRING) && // url.equals(Constants.S_XSLNAMESPACEURL)) // { // checkForFragmentID(attributes); // if (!m_shouldProcess) // return; // } // else // return; // } // I didn't include this code statement at this time because in practice // it is a small performance hit and I was waiting to see if its absence // caused a problem. - GLP checkForFragmentID(attributes); if (!m_shouldProcess) return; flushCharacters(); pushSpaceHandling(attributes); XSLTElementProcessor elemProcessor = getProcessorFor(uri, localName, rawName); if(null != elemProcessor) // defensive, for better multiple error reporting. -sb { this.pushProcessor(elemProcessor); elemProcessor.startElement(this, uri, localName, rawName, attributes); } else { m_shouldProcess = false; popSpaceHandling(); } } /** * Receive notification of the end of an element. * * @param uri The Namespace URI, or an empty string. * @param localName The local name (without prefix), or empty string if not namespace processing. * @param rawName The qualified name (with prefix). * @see org.xml.sax.ContentHandler#endElement * * @throws org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. */ public void endElement(String uri, String localName, String rawName) throws org.xml.sax.SAXException { m_elementID--; if (!m_shouldProcess) return; if ((m_elementID + 1) == m_fragmentID) m_shouldProcess = false; flushCharacters(); popSpaceHandling(); XSLTElementProcessor p = getCurrentProcessor(); p.endElement(this, uri, localName, rawName); this.popProcessor(); this.getNamespaceSupport().popContext(); } /** * Receive notification of character data inside an element. * * @param ch The characters. * @param start The start position in the character array. * @param length The number of characters to use from the * character array. * @see org.xml.sax.ContentHandler#characters * * @throws org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. */ public void characters(char ch[], int start, int length) throws org.xml.sax.SAXException { if (!m_shouldProcess) return; XSLTElementProcessor elemProcessor = getCurrentProcessor(); XSLTElementDef def = elemProcessor.getElemDef(); if (def.getType() != XSLTElementDef.T_PCDATA) elemProcessor = def.getProcessorFor(null, "text()"); if (null == elemProcessor) { // If it's whitespace, just ignore it, otherwise flag an error. if (!XMLCharacterRecognizer.isWhiteSpace(ch, start, length)) error( XSLMessages.createMessage(XSLTErrorResources.ER_NONWHITESPACE_NOT_ALLOWED_IN_POSITION, null),null);//"Non-whitespace text is not allowed in this position in the stylesheet!", } else elemProcessor.characters(this, ch, start, length); } /** * Receive notification of ignorable whitespace in element content. * * @param ch The whitespace characters. * @param start The start position in the character array. * @param length The number of characters to use from the * character array. * @see org.xml.sax.ContentHandler#ignorableWhitespace * * @throws org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. */ public void ignorableWhitespace(char ch[], int start, int length) throws org.xml.sax.SAXException { if (!m_shouldProcess) return; getCurrentProcessor().ignorableWhitespace(this, ch, start, length); } /** * Receive notification of a processing instruction. * *

The Parser will invoke this method once for each processing * instruction found: note that processing instructions may occur * before or after the main document element.

* *

A SAX parser should never report an XML declaration (XML 1.0, * section 2.8) or a text declaration (XML 1.0, section 4.3.1) * using this method.

* *

By default, do nothing. Application writers may override this * method in a subclass to take specific actions for each * processing instruction, such as setting status variables or * invoking other methods.

* * @param target The processing instruction target. * @param data The processing instruction data, or null if * none is supplied. * @see org.xml.sax.ContentHandler#processingInstruction * * @throws org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. */ public void processingInstruction(String target, String data) throws org.xml.sax.SAXException { if (!m_shouldProcess) return; // Recreating Scott's kluge: // A xsl:for-each or xsl:apply-templates may have a special // PI that tells us not to cache the document. This PI // should really be namespaced. // String localName = getLocalName(target); // String ns = m_stylesheet.getNamespaceFromStack(target); // // %REVIEW%: We need a better PI architecture String prefix="",ns="", localName=target; int colon=target.indexOf(':'); if(colon>=0) { ns=getNamespaceForPrefix(prefix=target.substring(0,colon)); localName=target.substring(colon+1); } try { // A xsl:for-each or xsl:apply-templates may have a special // PI that tells us not to cache the document. This PI // should really be namespaced... but since the XML Namespaces // spec never defined namespaces as applying to PI's, and since // the testcase we're trying to support is inconsistant in whether // it binds the prefix, I'm going to make this sloppy for // testing purposes. if( "xalan-doc-cache-off".equals(target) || "xalan:doc-cache-off".equals(target) || ("doc-cache-off".equals(localName) && ns.equals("org.apache.xalan.xslt.extensions.Redirect") ) ) { if(!(m_elems.peek() instanceof ElemForEach)) throw new TransformerException ("xalan:doc-cache-off not allowed here!", getLocator()); ElemForEach elem = (ElemForEach)m_elems.peek(); elem.m_doc_cache_off = true; //System.out.println("JJK***** Recognized "); } } catch(Exception e) { // JJK: Officially, unknown PIs can just be ignored. // Do we want to issue a warning? } flushCharacters(); getCurrentProcessor().processingInstruction(this, target, data); } /** * Receive notification of a skipped entity. * *

By default, do nothing. Application writers may override this * method in a subclass to take specific actions for each * processing instruction, such as setting status variables or * invoking other methods.

* * @param name The name of the skipped entity. * @see org.xml.sax.ContentHandler#processingInstruction * * @throws org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. */ public void skippedEntity(String name) throws org.xml.sax.SAXException { if (!m_shouldProcess) return; getCurrentProcessor().skippedEntity(this, name); } /** * Warn the user of an problem. * * @param msg An key into the {@link org.apache.xalan.res.XSLTErrorResources} * table, that is one of the WG_ prefixed definitions. * @param args An array of arguments for the given warning. * * @throws org.xml.sax.SAXException that wraps a * {@link javax.xml.transform.TransformerException} if the current * {@link javax.xml.transform.ErrorListener#warning} * method chooses to flag this condition as an error. * @xsl.usage internal */ public void warn(String msg, Object args[]) throws org.xml.sax.SAXException { String formattedMsg = XSLMessages.createWarning(msg, args); SAXSourceLocator locator = getLocator(); ErrorListener handler = m_stylesheetProcessor.getErrorListener(); try { if (null != handler) handler.warning(new TransformerException(formattedMsg, locator)); } catch (TransformerException te) { throw new org.xml.sax.SAXException(te); } } /** * Assert that a condition is true. If it is not true, throw an error. * * @param condition false if an error should not be thrown, otherwise true. * @param msg Error message to be passed to the RuntimeException as an * argument. * @throws RuntimeException if the condition is not true. * @xsl.usage internal */ private void assertion(boolean condition, String msg) throws RuntimeException { if (!condition) throw new RuntimeException(msg); } /** * Tell the user of an error, and probably throw an * exception. * * @param msg An error message. * @param e An error which the SAXException should wrap. * * @throws org.xml.sax.SAXException that wraps a * {@link javax.xml.transform.TransformerException} if the current * {@link javax.xml.transform.ErrorListener#error} * method chooses to flag this condition as an error. * @xsl.usage internal */ protected void error(String msg, Exception e) throws org.xml.sax.SAXException { SAXSourceLocator locator = getLocator(); ErrorListener handler = m_stylesheetProcessor.getErrorListener(); TransformerException pe; if (!(e instanceof TransformerException)) { pe = (null == e) ? new TransformerException(msg, locator) : new TransformerException(msg, locator, e); } else pe = (TransformerException) e; if (null != handler) { try { handler.error(pe); } catch (TransformerException te) { throw new org.xml.sax.SAXException(te); } } else throw new org.xml.sax.SAXException(pe); } /** * Tell the user of an error, and probably throw an * exception. * * @param msg A key into the {@link org.apache.xalan.res.XSLTErrorResources} * table, that is one of the WG_ prefixed definitions. * @param args An array of arguments for the given warning. * @param e An error which the SAXException should wrap. * * @throws org.xml.sax.SAXException that wraps a * {@link javax.xml.transform.TransformerException} if the current * {@link javax.xml.transform.ErrorListener#error} * method chooses to flag this condition as an error. * @xsl.usage internal */ protected void error(String msg, Object args[], Exception e) throws org.xml.sax.SAXException { String formattedMsg = XSLMessages.createMessage(msg, args); error(formattedMsg, e); } /** * Receive notification of a XSLT processing warning. * * @param e The warning information encoded as an exception. * * @throws org.xml.sax.SAXException that wraps a * {@link javax.xml.transform.TransformerException} if the current * {@link javax.xml.transform.ErrorListener#warning} * method chooses to flag this condition as an error. */ public void warning(org.xml.sax.SAXParseException e) throws org.xml.sax.SAXException { String formattedMsg = e.getMessage(); SAXSourceLocator locator = getLocator(); ErrorListener handler = m_stylesheetProcessor.getErrorListener(); try { handler.warning(new TransformerException(formattedMsg, locator)); } catch (TransformerException te) { throw new org.xml.sax.SAXException(te); } } /** * Receive notification of a recoverable XSLT processing error. * * @param e The error information encoded as an exception. * * @throws org.xml.sax.SAXException that wraps a * {@link javax.xml.transform.TransformerException} if the current * {@link javax.xml.transform.ErrorListener#error} * method chooses to flag this condition as an error. */ public void error(org.xml.sax.SAXParseException e) throws org.xml.sax.SAXException { String formattedMsg = e.getMessage(); SAXSourceLocator locator = getLocator(); ErrorListener handler = m_stylesheetProcessor.getErrorListener(); try { handler.error(new TransformerException(formattedMsg, locator)); } catch (TransformerException te) { throw new org.xml.sax.SAXException(te); } } /** * Report a fatal XSLT processing error. * * @param e The error information encoded as an exception. * * @throws org.xml.sax.SAXException that wraps a * {@link javax.xml.transform.TransformerException} if the current * {@link javax.xml.transform.ErrorListener#fatalError} * method chooses to flag this condition as an error. */ public void fatalError(org.xml.sax.SAXParseException e) throws org.xml.sax.SAXException { String formattedMsg = e.getMessage(); SAXSourceLocator locator = getLocator(); ErrorListener handler = m_stylesheetProcessor.getErrorListener(); try { handler.fatalError(new TransformerException(formattedMsg, locator)); } catch (TransformerException te) { throw new org.xml.sax.SAXException(te); } } /** * If we have a URL to a XML fragment, this is set * to false until the ID is found. * (warning: I worry that this should be in a stack). */ private boolean m_shouldProcess = true; /** * If we have a URL to a XML fragment, the value is stored * in this string, and the m_shouldProcess flag is set to * false until we match an ID with this string. * (warning: I worry that this should be in a stack). */ private String m_fragmentIDString; /** * Keep track of the elementID, so we can tell when * is has completed. This isn't a real ID, but rather * a nesting level. However, it's good enough for * our purposes. * (warning: I worry that this should be in a stack). */ private int m_elementID = 0; /** * The ID of the fragment that has been found * (warning: I worry that this should be in a stack). */ private int m_fragmentID = 0; /** * Check to see if an ID attribute matched the #id, called * from startElement. * * @param attributes The specified or defaulted attributes. */ private void checkForFragmentID(Attributes attributes) { if (!m_shouldProcess) { if ((null != attributes) && (null != m_fragmentIDString)) { int n = attributes.getLength(); for (int i = 0; i < n; i++) { String name = attributes.getQName(i); if (name.equals(Constants.ATTRNAME_ID)) { String val = attributes.getValue(i); if (val.equalsIgnoreCase(m_fragmentIDString)) { m_shouldProcess = true; m_fragmentID = m_elementID; } } } } } } /** * The XSLT TransformerFactory for needed services. */ private TransformerFactoryImpl m_stylesheetProcessor; /** * Get the XSLT TransformerFactoryImpl for needed services. * TODO: This method should be renamed. * * @return The TransformerFactoryImpl that owns this handler. */ public TransformerFactoryImpl getStylesheetProcessor() { return m_stylesheetProcessor; } /** * If getStylesheetType returns this value, the current stylesheet * is a root stylesheet. * @xsl.usage internal */ public static final int STYPE_ROOT = 1; /** * If getStylesheetType returns this value, the current stylesheet * is an included stylesheet. * @xsl.usage internal */ public static final int STYPE_INCLUDE = 2; /** * If getStylesheetType returns this value, the current stylesheet * is an imported stylesheet. * @xsl.usage internal */ public static final int STYPE_IMPORT = 3; /** The current stylesheet type. */ private int m_stylesheetType = STYPE_ROOT; /** * Get the type of stylesheet that should be built * or is being processed. * * @return one of STYPE_ROOT, STYPE_INCLUDE, or STYPE_IMPORT. */ int getStylesheetType() { return m_stylesheetType; } /** * Set the type of stylesheet that should be built * or is being processed. * * @param type Must be one of STYPE_ROOT, STYPE_INCLUDE, or STYPE_IMPORT. */ void setStylesheetType(int type) { m_stylesheetType = type; } /** * The stack of stylesheets being processed. */ private Stack m_stylesheets = new Stack(); /** * Return the stylesheet that this handler is constructing. * * @return The current stylesheet that is on top of the stylesheets stack, * or null if no stylesheet is on the stylesheets stack. */ Stylesheet getStylesheet() { return (m_stylesheets.size() == 0) ? null : (Stylesheet) m_stylesheets.peek(); } /** * Return the last stylesheet that was popped off the stylesheets stack. * * @return The last popped stylesheet, or null. */ Stylesheet getLastPoppedStylesheet() { return m_lastPoppedStylesheet; } /** * Return the stylesheet root that this handler is constructing. * * @return The root stylesheet of the stylesheets tree. */ public StylesheetRoot getStylesheetRoot() { if (m_stylesheetRoot != null){ m_stylesheetRoot.setOptimizer(m_optimize); m_stylesheetRoot.setIncremental(m_incremental); m_stylesheetRoot.setSource_location(m_source_location); } return m_stylesheetRoot; } /** The root stylesheet of the stylesheets tree. */ StylesheetRoot m_stylesheetRoot; /** The last stylesheet that was popped off the stylesheets stack. */ Stylesheet m_lastPoppedStylesheet; /** * Push the current stylesheet being constructed. If no other stylesheets * have been pushed onto the stack, assume the argument is a stylesheet * root, and also set the stylesheet root member. * * @param s non-null reference to a stylesheet. */ public void pushStylesheet(Stylesheet s) { if (m_stylesheets.size() == 0) m_stylesheetRoot = (StylesheetRoot) s; m_stylesheets.push(s); } /** * Pop the last stylesheet pushed, and return the stylesheet that this * handler is constructing, and set the last popped stylesheet member. * Also pop the stylesheet locator stack. * * @return The stylesheet popped off the stack, or the last popped stylesheet. */ Stylesheet popStylesheet() { // The stylesheetLocatorStack needs to be popped because // a locator was pushed in for this stylesheet by the SAXparser by calling // setDocumentLocator(). if (!m_stylesheetLocatorStack.isEmpty()) m_stylesheetLocatorStack.pop(); if (!m_stylesheets.isEmpty()) m_lastPoppedStylesheet = (Stylesheet) m_stylesheets.pop(); // Shouldn't this be null if stylesheets is empty? -sb return m_lastPoppedStylesheet; } /** * The stack of current processors. */ private Stack m_processors = new Stack(); /** * Get the current XSLTElementProcessor at the top of the stack. * * @return Valid XSLTElementProcessor, which should never be null. */ XSLTElementProcessor getCurrentProcessor() { return (XSLTElementProcessor) m_processors.peek(); } /** * Push the current XSLTElementProcessor onto the top of the stack. * * @param processor non-null reference to the current element processor. */ void pushProcessor(XSLTElementProcessor processor) { m_processors.push(processor); } /** * Pop the current XSLTElementProcessor from the top of the stack. * @return the XSLTElementProcessor which was popped. */ XSLTElementProcessor popProcessor() { return (XSLTElementProcessor) m_processors.pop(); } /** * The root of the XSLT Schema, which tells us how to * transition content handlers, create elements, etc. * For the moment at least, this can't be static, since * the processors store state. */ private XSLTSchema m_schema = new XSLTSchema(); /** * Get the root of the XSLT Schema, which tells us how to * transition content handlers, create elements, etc. * * @return The root XSLT Schema, which should never be null. * @xsl.usage internal */ public XSLTSchema getSchema() { return m_schema; } /** * The stack of elements, pushed and popped as events occur. */ private Stack m_elems = new Stack(); /** * Get the current ElemTemplateElement at the top of the stack. * @return Valid ElemTemplateElement, which may be null. */ ElemTemplateElement getElemTemplateElement() { try { return (ElemTemplateElement) m_elems.peek(); } catch (java.util.EmptyStackException ese) { return null; } } /** An increasing number that is used to indicate the order in which this element * was encountered during the parse of the XSLT tree. */ private int m_docOrderCount = 0; /** * Returns the next m_docOrderCount number and increments the number for future use. */ int nextUid() { return m_docOrderCount++; } /** * Push the current XSLTElementProcessor to the top of the stack. As a * side-effect, set the document order index (simply because this is a * convenient place to set it). * * @param elem Should be a non-null reference to the intended current * template element. */ void pushElemTemplateElement(ElemTemplateElement elem) { if (elem.getUid() == -1) elem.setUid(nextUid()); m_elems.push(elem); } /** * Get the current XSLTElementProcessor from the top of the stack. * @return the ElemTemplateElement which was popped. */ ElemTemplateElement popElemTemplateElement() { return (ElemTemplateElement) m_elems.pop(); } /** * This will act as a stack to keep track of the * current include base. */ Stack m_baseIdentifiers = new Stack(); /** * Push a base identifier onto the base URI stack. * * @param baseID The current base identifier for this position in the * stylesheet, which may be a fragment identifier, or which may be null. * @see * Section 3.2 Base URI of XSLT specification. */ void pushBaseIndentifier(String baseID) { if (null != baseID) { int posOfHash = baseID.indexOf('#'); if (posOfHash > -1) { m_fragmentIDString = baseID.substring(posOfHash + 1); m_shouldProcess = false; } else m_shouldProcess = true; } else m_shouldProcess = true; m_baseIdentifiers.push(baseID); } /** * Pop a base URI from the stack. * @return baseIdentifier. */ String popBaseIndentifier() { return (String) m_baseIdentifiers.pop(); } /** * Return the base identifier. * * @return The base identifier of the current stylesheet. */ public String getBaseIdentifier() { // Try to get the baseIdentifier from the baseIdentifier's stack, // which may not be the same thing as the value found in the // SourceLocators stack. String base = (String) (m_baseIdentifiers.isEmpty() ? null : m_baseIdentifiers.peek()); // Otherwise try the stylesheet. if (null == base) { SourceLocator locator = getLocator(); base = (null == locator) ? "" : locator.getSystemId(); } return base; } /** * The top of this stack should contain the currently processed * stylesheet SAX locator object. */ private Stack m_stylesheetLocatorStack = new Stack(); /** * Get the current stylesheet Locator object. * * @return non-null reference to the current locator object. */ public SAXSourceLocator getLocator() { if (m_stylesheetLocatorStack.isEmpty()) { SAXSourceLocator locator = new SAXSourceLocator(); locator.setSystemId(this.getStylesheetProcessor().getDOMsystemID()); return locator; // m_stylesheetLocatorStack.push(locator); } return ((SAXSourceLocator) m_stylesheetLocatorStack.peek()); } /** * A stack of URL hrefs for imported stylesheets. This is * used to diagnose circular imports. */ private Stack m_importStack = new Stack(); /** * A stack of Source objects obtained from a URIResolver, * for each element in this stack there is a 1-1 correspondence * with an element in the m_importStack. */ private Stack m_importSourceStack = new Stack(); /** * Push an import href onto the stylesheet stack. * * @param hrefUrl non-null reference to the URL for the current imported * stylesheet. */ void pushImportURL(String hrefUrl) { m_importStack.push(hrefUrl); } /** * Push the Source of an import href onto the stylesheet stack, * obtained from a URIResolver, null if there is no URIResolver, * or if that resolver returned null. */ void pushImportSource(Source sourceFromURIResolver) { m_importSourceStack.push(sourceFromURIResolver); } /** * See if the imported stylesheet stack already contains * the given URL. Used to test for recursive imports. * * @param hrefUrl non-null reference to a URL string. * * @return true if the URL is on the import stack. */ boolean importStackContains(String hrefUrl) { return stackContains(m_importStack, hrefUrl); } /** * Pop an import href from the stylesheet stack. * * @return non-null reference to the import URL that was popped. */ String popImportURL() { return (String) m_importStack.pop(); } String peekImportURL() { return (String) m_importStack.peek(); } Source peekSourceFromURIResolver() { return (Source) m_importSourceStack.peek(); } /** * Pop a Source from a user provided URIResolver, corresponding * to the URL popped from the m_importStack. */ Source popImportSource() { return (Source) m_importSourceStack.pop(); } /** * If this is set to true, we've already warned about using the * older XSLT namespace URL. */ private boolean warnedAboutOldXSLTNamespace = false; /** Stack of NamespaceSupport objects. */ Stack m_nsSupportStack = new Stack(); /** * Push a new NamespaceSupport instance. */ void pushNewNamespaceSupport() { m_nsSupportStack.push(new NamespaceSupport2()); } /** * Pop the current NamespaceSupport object. * */ void popNamespaceSupport() { m_nsSupportStack.pop(); } /** * Get the current NamespaceSupport object. * * @return a non-null reference to the current NamespaceSupport object, * which is the top of the namespace support stack. */ NamespaceSupport getNamespaceSupport() { return (NamespaceSupport) m_nsSupportStack.peek(); } /** * The originating node if the current stylesheet is being created * from a DOM. * @see org.apache.xml.utils.NodeConsumer */ private Node m_originatingNode; /** * Set the node that is originating the SAX event. * * @param n Reference to node that originated the current event. * @see org.apache.xml.utils.NodeConsumer */ public void setOriginatingNode(Node n) { m_originatingNode = n; } /** * Set the node that is originating the SAX event. * * @return Reference to node that originated the current event. * @see org.apache.xml.utils.NodeConsumer */ public Node getOriginatingNode() { return m_originatingNode; } /** * Stack of booleans that are pushed and popped in start/endElement depending * on the value of xml:space=default/preserve. */ private BoolStack m_spacePreserveStack = new BoolStack(); /** * Return boolean value from the spacePreserve stack depending on the value * of xml:space=default/preserve. * * @return true if space should be preserved, false otherwise. */ boolean isSpacePreserve() { return m_spacePreserveStack.peek(); } /** * Pop boolean value from the spacePreserve stack. */ void popSpaceHandling() { m_spacePreserveStack.pop(); } /** * Push boolean value on to the spacePreserve stack. * * @param b true if space should be preserved, false otherwise. */ void pushSpaceHandling(boolean b) throws org.xml.sax.SAXParseException { m_spacePreserveStack.push(b); } /** * Push boolean value on to the spacePreserve stack depending on the value * of xml:space=default/preserve. * * @param attrs list of attributes that were passed to startElement. */ void pushSpaceHandling(Attributes attrs) throws org.xml.sax.SAXParseException { String value = attrs.getValue("xml:space"); if(null == value) { m_spacePreserveStack.push(m_spacePreserveStack.peekOrFalse()); } else if(value.equals("preserve")) { m_spacePreserveStack.push(true); } else if(value.equals("default")) { m_spacePreserveStack.push(false); } else { SAXSourceLocator locator = getLocator(); ErrorListener handler = m_stylesheetProcessor.getErrorListener(); try { handler.error(new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_ILLEGAL_XMLSPACE_VALUE, null), locator)); //"Illegal value for xml:space", locator)); } catch (TransformerException te) { throw new org.xml.sax.SAXParseException(te.getMessage(), locator, te); } m_spacePreserveStack.push(m_spacePreserveStack.peek()); } } private double getElemVersion() { ElemTemplateElement elem = getElemTemplateElement(); double version = -1; while ((version == -1 || version == Constants.XSLTVERSUPPORTED) && elem != null) { try{ version = Double.valueOf(elem.getXmlVersion()).doubleValue(); } catch (Exception ex) { version = -1; } elem = elem.getParentElem(); } return (version == -1)? Constants.XSLTVERSUPPORTED : version; } /** * @see PrefixResolver#handlesNullPrefixes() */ public boolean handlesNullPrefixes() { return false; } /** * @return Optimization flag */ public boolean getOptimize() { return m_optimize; } /** * @return Incremental flag */ public boolean getIncremental() { return m_incremental; } /** * @return Source Location flag */ public boolean getSource_location() { return m_source_location; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy