blob: f8386af3cb14db50d98f44ddfab39b7d7b15aaf1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008 Standards for Technology in Automotive Retail
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* David Carver - STAR - bug 224197 - initial API and implementation
* based on work from Apache Xalan 2.7.0
*******************************************************************************/
/*
* Copyright 1999-2004 The Apache Software Foundation.
*
* Licensed 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,v 1.3 2008/03/28 02:38:16 dacarver Exp $
*/
package org.eclipse.wst.xsl.core.internal.compiler.xslt10.processor;
import java.util.Stack;
import javax.xml.transform.ErrorListener;
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.eclipse.wst.xsl.core.internal.compiler.xslt10.extensions.ExpressionVisitor;
import org.apache.xalan.res.XSLMessages;
import org.apache.xalan.res.XSLTErrorResources;
import org.eclipse.wst.xsl.core.internal.compiler.xslt10.templates.Constants;
import org.eclipse.wst.xsl.core.internal.compiler.xslt10.templates.ElemForEach;
import org.eclipse.wst.xsl.core.internal.compiler.xslt10.templates.ElemTemplateElement;
import org.eclipse.wst.xsl.core.internal.compiler.xslt10.templates.Stylesheet;
import org.eclipse.wst.xsl.core.internal.compiler.xslt10.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.eclipse.wst.xsl.core.internal.compiler.xslt10.xpath.XPath;
import org.apache.xpath.compiler.FunctionTable;
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.
*/
protected 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 <a href="http://www.w3.org/TR/xslt#section-Expressions">Section 4
* Expressions in XSLT Specification</a>
*/
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 <a href="http://www.w3.org/TR/xslt#patterns">Section 5.2 Patterns in
* XSLT Specification</a>
*/
protected 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 <a href="mailto:scott_boag@lotus.com">Scott Boag</a>
*
*
*/
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.
*/
@Override
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.
*
* <p>
* 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.
* </p>
*
* @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
*/
@Override
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
*/
@Override
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.
*/
public 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() > org.apache.xml.utils.Constants.XSLTVERSUPPORTED)
|| (!uri
.equals(org.apache.xml.utils.Constants.S_XSLNAMESPACEURL) && currentProcessor instanceof ProcessorStylesheetElement) || getElemVersion() > org.apache.xml.utils.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
*/
@Override
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.
*/
@Override
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.
*/
@Override
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.
*
* <p>
* 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).
* </p>
*
* @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.
*/
@Override
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.
*
* <p>
* 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).
* </p>
*
* @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.
*/
@Override
public void endPrefixMapping(String prefix) throws org.xml.sax.SAXException {
// m_nsSupport.popContext();
}
/**
* Flush the characters buffer.
*
* @throws org.xml.sax.SAXException
*/
public 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
*/
@Override
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.
*/
@Override
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.
*/
@Override
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.
*/
@Override
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.
*
* <p>
* 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.
* </p>
*
* <p>
* 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.
* </p>
*
* <p>
* 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.
* </p>
*
* @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.
*/
@Override
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; //$NON-NLS-1$//$NON-NLS-2$
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) || //$NON-NLS-1$
"xalan:doc-cache-off".equals(target) || //$NON-NLS-1$
("doc-cache-off".equals(localName) && //$NON-NLS-1$
ns.equals("org.apache.xalan.xslt.extensions.Redirect")) //$NON-NLS-1$
) {
if (!(m_elems.peek() instanceof ElemForEach))
throw new TransformerException(
"xalan:doc-cache-off not allowed here!", //$NON-NLS-1$
getLocator());
ElemForEach elem = (ElemForEach) m_elems.peek();
elem.m_doc_cache_off = true;
// System.out.println("JJK***** Recognized <?
// {"+ns+"}"+prefix+":"+localName+" "+data+"?>");
}
} 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.
*
* <p>
* 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.
* </p>
*
* @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.
*/
@Override
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
*/
public 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
*/
public 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.
*/
@Override
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.
*/
@Override
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.
*/
@Override
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.
*/
protected 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.
*/
protected 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.
*/
protected org.eclipse.wst.xsl.core.internal.compiler.xslt10.templates.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.
*/
protected 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.
*/
protected 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.
*/
protected 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.
*/
protected void pushProcessor(XSLTElementProcessor processor) {
m_processors.push(processor);
}
/**
* Pop the current XSLTElementProcessor from the top of the stack.
*
* @return the XSLTElementProcessor which was popped.
*/
protected 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.
*/
public 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.
*/
public 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.
*/
public 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.
*/
public ElemTemplateElement popElemTemplateElement() {
return (ElemTemplateElement) m_elems.pop();
}
/**
* This will act as a stack to keep track of the current include base.
*/
protected 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 <a href="http://www.w3.org/TR/xslt#base-uri"> Section 3.2 Base URI
* of XSLT specification.</a>
*/
public 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.
*/
public 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();
/**
* Push an import href onto the stylesheet stack.
*
* @param hrefUrl
* non-null reference to the URL for the current imported
* stylesheet.
*/
public void pushImportURL(String hrefUrl) {
m_importStack.push(hrefUrl);
}
/**
* 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.
*/
public 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.
*/
public String popImportURL() {
return (String) m_importStack.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. */
private Stack m_nsSupportStack = new Stack();
/**
* Push a new NamespaceSupport instance.
*/
public void pushNewNamespaceSupport() {
m_nsSupportStack.push(new NamespaceSupport2());
}
/**
* Pop the current NamespaceSupport object.
*
*/
public 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.
*/
public 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.
*/
public boolean isSpacePreserve() {
return m_spacePreserveStack.peek();
}
/**
* Pop boolean value from the spacePreserve stack.
*/
public void popSpaceHandling() {
m_spacePreserveStack.pop();
}
/**
* Push boolean value on to the spacePreserve stack.
*
* @param b
* true if space should be preserved, false otherwise.
*/
public 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.
*/
public 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());
}
}
public double getElemVersion() {
ElemTemplateElement elem = getElemTemplateElement();
double version = -1;
while ((version == -1 || version == org.apache.xml.utils.Constants.XSLTVERSUPPORTED)
&& elem != null) {
try {
version = Double.valueOf(elem.getXmlVersion()).doubleValue();
} catch (Exception ex) {
version = -1;
}
elem = elem.getParentElem();
}
return (version == -1) ? org.apache.xml.utils.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;
}
}