/******************************************************************************* | |
* 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: StylesheetRoot.java,v 1.4 2008/03/28 02:38:15 dacarver Exp $ | |
*/ | |
package org.eclipse.wst.xsl.core.internal.compiler.xslt10.templates; | |
import java.text.DecimalFormatSymbols; | |
import java.util.Hashtable; | |
import java.util.Properties; | |
import java.util.Vector; | |
import javax.xml.transform.ErrorListener; | |
import javax.xml.transform.Templates; | |
import javax.xml.transform.Transformer; | |
import javax.xml.transform.TransformerConfigurationException; | |
import javax.xml.transform.TransformerException; | |
import org.eclipse.wst.xsl.core.compiler.xslt10.res.XSLTErrorResources; | |
import org.eclipse.wst.xsl.core.internal.compiler.xslt10.extensions.ExtensionNamespacesManager; | |
import org.eclipse.wst.xsl.core.internal.compiler.xslt10.processor.XSLTSchema; | |
import org.eclipse.wst.xsl.core.compiler.xslt10.res.Messages; | |
import org.eclipse.wst.xsl.core.internal.compiler.xslt10.transformer.TransformerImpl; | |
import org.apache.xml.dtm.DTM; | |
import org.apache.xml.dtm.ref.ExpandedNameTable; | |
import org.apache.xml.utils.IntStack; | |
import org.apache.xml.utils.QName; | |
import org.eclipse.wst.xsl.core.internal.compiler.xslt10.xpath.XPath; | |
import org.apache.xpath.XPathContext; | |
/** | |
* This class represents the root object of the stylesheet tree. | |
* | |
* @xsl.usage general | |
*/ | |
public class StylesheetRoot extends StylesheetComposed implements | |
java.io.Serializable, Templates { | |
static final long serialVersionUID = 3875353123529147855L; | |
/** | |
* The flag for the setting of the optimize feature; | |
*/ | |
private boolean m_optimizer = 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; | |
/** | |
* State of the secure processing feature. | |
*/ | |
private boolean m_isSecureProcessing = false; | |
private ErrorListener m_errorListener = null; | |
/** | |
* Uses an XSL stylesheet document. | |
* | |
* @throws TransformerConfigurationException | |
* if the baseIdentifier can not be resolved to a URL. | |
*/ | |
public StylesheetRoot(ErrorListener errorListener) | |
throws TransformerConfigurationException { | |
super(null); | |
setStylesheetRoot(this); | |
m_errorListener = errorListener; | |
try { | |
m_selectDefault = new XPath("node()", this, this, XPath.SELECT, | |
errorListener); | |
initDefaultRule(errorListener); | |
} catch (TransformerException se) { | |
throw new TransformerConfigurationException(Messages.createMessage( | |
XSLTErrorResources.ER_CANNOT_INIT_DEFAULT_TEMPLATES, null), | |
se); // "Can't init default templates!", se); | |
} | |
} | |
/** | |
* The schema used when creating this StylesheetRoot | |
* | |
* @serial | |
*/ | |
private Hashtable m_availElems; | |
/** | |
* Creates a StylesheetRoot and retains a pointer to the schema used to | |
* create this StylesheetRoot. The schema may be needed later for an | |
* element-available() function call. | |
* | |
* @param schema | |
* The schema used to create this stylesheet | |
* @throws TransformerConfigurationException | |
* if the baseIdentifier can not be resolved to a URL. | |
*/ | |
public StylesheetRoot(XSLTSchema schema, ErrorListener listener) | |
throws TransformerConfigurationException { | |
this(listener); | |
m_availElems = schema.getElemsAvailable(); | |
} | |
/** | |
* Tell if this is the root of the stylesheet tree. | |
* | |
* @return True since this is the root of the stylesheet tree. | |
*/ | |
@Override | |
public boolean isRoot() { | |
return true; | |
} | |
/** | |
* Set the state of the secure processing feature. | |
*/ | |
public void setSecureProcessing(boolean flag) { | |
m_isSecureProcessing = flag; | |
} | |
/** | |
* Return the state of the secure processing feature. | |
*/ | |
public boolean isSecureProcessing() { | |
return m_isSecureProcessing; | |
} | |
/** | |
* Get the hashtable of available elements. | |
* | |
* @return table of available elements, keyed by qualified names, and with | |
* values of the same qualified names. | |
*/ | |
public Hashtable getAvailableElements() { | |
return m_availElems; | |
} | |
private transient ExtensionNamespacesManager m_extNsMgr = null; | |
/** | |
* Only instantiate an ExtensionNamespacesManager if one is called for | |
* (i.e., if the stylesheet contains extension functions and/or elements). | |
*/ | |
public ExtensionNamespacesManager getExtensionNamespacesManager() { | |
if (m_extNsMgr == null) | |
m_extNsMgr = new ExtensionNamespacesManager(); | |
return m_extNsMgr; | |
} | |
/** | |
* Get the vector of extension namespaces. Used to provide the extensions | |
* table access to a list of extension namespaces encountered during | |
* composition of a stylesheet. | |
*/ | |
public Vector getExtensions() { | |
return m_extNsMgr != null ? m_extNsMgr.getExtensions() : null; | |
} | |
/* | |
* public void runtimeInit(TransformerImpl transformer) throws | |
* TransformerException { | |
* System.out.println("StylesheetRoot.runtimeInit()"); | |
* // try{throw new Exception("StylesheetRoot.runtimeInit()");} | |
* catch(Exception e){e.printStackTrace();} | |
* } | |
*/ | |
// ============== Templates Interface ================ | |
/** | |
* Create a new transformation context for this Templates object. | |
* | |
* @return A Transformer instance, never null. | |
*/ | |
public Transformer newTransformer() { | |
return new TransformerImpl(this); | |
} | |
public Properties getDefaultOutputProps() { | |
return m_outputProperties.getProperties(); | |
} | |
/** | |
* Get the static properties for xsl:output. The object returned will be a | |
* clone of the internal values, and thus it can be mutated without mutating | |
* the Templates object, and then handed in to the process method. | |
* | |
* <p> | |
* For XSLT, Attribute Value Templates attribute values will be returned | |
* unexpanded (since there is no context at this point). | |
* </p> | |
* | |
* @return A Properties object, not null. | |
*/ | |
public Properties getOutputProperties() { | |
return (Properties) getDefaultOutputProps().clone(); | |
} | |
// ============== End Templates Interface ================ | |
/** | |
* Recompose the values of all "composed" properties, meaning properties | |
* that need to be combined or calculated from the combination of imported | |
* and included stylesheets. This method determines the proper import | |
* precedence of all imported stylesheets. It then iterates through all of | |
* the elements and properties in the proper order and triggers the | |
* individual recompose methods. | |
* | |
* @throws TransformerException | |
*/ | |
public void recompose() throws TransformerException { | |
// Now we make a Vector that is going to hold all of the recomposable | |
// elements | |
Vector recomposableElements = new Vector(); | |
// First, we build the global import tree. | |
if (null == m_globalImportList) { | |
Vector importList = new Vector(); | |
addImports(this, true, importList); | |
// Now we create an array and reverse the order of the importList | |
// vector. | |
// We built the importList vector backwards so that we could use | |
// addElement | |
// to append to the end of the vector instead of constantly pushing | |
// new | |
// stylesheets onto the front of the vector and having to shift the | |
// rest | |
// of the vector each time. | |
m_globalImportList = new StylesheetComposed[importList.size()]; | |
for (int i = 0, j = importList.size() - 1; i < importList.size(); i++) { | |
m_globalImportList[j] = (StylesheetComposed) importList | |
.elementAt(i); | |
// Build the global include list for this stylesheet. | |
// This needs to be done ahead of the recomposeImports | |
// because we need the info from the composed includes. | |
m_globalImportList[j].recomposeIncludes(m_globalImportList[j]); | |
// Calculate the number of this import. | |
m_globalImportList[j--].recomposeImports(); | |
} | |
} | |
// Next, we walk the import tree and add all of the recomposable | |
// elements to the vector. | |
int n = getGlobalImportCount(); | |
for (int i = 0; i < n; i++) { | |
StylesheetComposed imported = getGlobalImport(i); | |
imported.recompose(recomposableElements); | |
} | |
// We sort the elements into ascending order. | |
QuickSort2(recomposableElements, 0, recomposableElements.size() - 1); | |
// We set up the global variables that will hold the recomposed | |
// information. | |
m_outputProperties = new OutputProperties( | |
org.apache.xml.serializer.Method.UNKNOWN); | |
// m_outputProperties = new OutputProperties(Method.XML); | |
m_attrSets = new Hashtable(); | |
m_decimalFormatSymbols = new Hashtable(); | |
m_keyDecls = new Vector(); | |
m_namespaceAliasComposed = new Hashtable(); | |
m_templateList = new TemplateList(); | |
m_variables = new Vector(); | |
// Now we sequence through the sorted elements, | |
// calling the recompose() function on each one. This will call back | |
// into the | |
// appropriate routine here to actually do the recomposition. | |
// Note that we're going backwards, encountering the highest precedence | |
// items first. | |
for (int i = recomposableElements.size() - 1; i >= 0; i--) | |
((ElemTemplateElement) recomposableElements.elementAt(i)) | |
.recompose(this); | |
/* | |
* Backing out REE again, as it seems to cause some new failures which | |
* need to be investigated. -is | |
*/ | |
// This has to be done before the initialization of the compose state, | |
// because | |
// eleminateRedundentGlobals will add variables to the m_variables | |
// vector, which | |
// it then copied in the ComposeState constructor. | |
// if(true && | |
// org.apache.xalan.processor.TransformerFactoryImpl.m_optimize) | |
// { | |
// RedundentExprEliminator ree = new RedundentExprEliminator(); | |
// callVisitors(ree); | |
// ree.eleminateRedundentGlobals(this); | |
// } | |
initComposeState(); | |
// Need final composition of TemplateList. This adds the wild cards onto | |
// the chains. | |
m_templateList.compose(this); | |
// Need to clear check for properties at the same import level. | |
m_outputProperties.compose(this); | |
m_outputProperties.endCompose(this); | |
// Now call the compose() method on every element to give it a chance to | |
// adjust | |
// based on composed values. | |
n = getGlobalImportCount(); | |
for (int i = 0; i < n; i++) { | |
StylesheetComposed imported = this.getGlobalImport(i); | |
int includedCount = imported.getIncludeCountComposed(); | |
for (int j = -1; j < includedCount; j++) { | |
Stylesheet included = imported.getIncludeComposed(j); | |
composeTemplates(included); | |
} | |
} | |
// Attempt to register any remaining unregistered extension namespaces. | |
if (m_extNsMgr != null) | |
m_extNsMgr.registerUnregisteredNamespaces(); | |
clearComposeState(); | |
} | |
/** | |
* Call the compose function for each ElemTemplateElement. | |
* | |
* @param templ | |
* non-null reference to template element that will have the | |
* composed method called on it, and will have it's children's | |
* composed methods called. | |
*/ | |
void composeTemplates(ElemTemplateElement templ) | |
throws TransformerException { | |
try { | |
templ.compose(this); | |
} catch (TransformerException ex) { | |
m_errorListener.fatalError(ex); | |
} | |
for (ElemTemplateElement child = templ.getFirstChildElem(); child != null; child = child | |
.getNextSiblingElem()) { | |
try { | |
composeTemplates(child); | |
} catch (TransformerException ex) { | |
m_errorListener.fatalError(ex); | |
} | |
} | |
templ.endCompose(this); | |
} | |
/** | |
* The combined list of imports. The stylesheet with the highest import | |
* precedence will be at element 0. The one with the lowest import | |
* precedence will be at element length - 1. | |
* | |
* @serial | |
*/ | |
private StylesheetComposed[] m_globalImportList; | |
/** | |
* Add the imports in the given sheet to the working importList vector. The | |
* will be added from highest import precedence to least import precedence. | |
* This is a post-order traversal of the import tree as described in <a | |
* href="http://www.w3.org/TR/xslt.html#import">the XSLT Recommendation</a>. | |
* <p> | |
* For example, suppose | |
* </p> | |
* <p> | |
* stylesheet A imports stylesheets B and C in that order; | |
* </p> | |
* <p> | |
* stylesheet B imports stylesheet D; | |
* </p> | |
* <p> | |
* stylesheet C imports stylesheet E. | |
* </p> | |
* <p> | |
* Then the order of import precedence (highest first) is A, C, E, B, D. | |
* </p> | |
* | |
* @param stylesheet | |
* Stylesheet to examine for imports. | |
* @param addToList | |
* <code>true</code> if this template should be added to the | |
* import list | |
* @param importList | |
* The working import list. Templates are added here in the | |
* reverse order of priority. When we're all done, we'll reverse | |
* this to the correct priority in an array. | |
*/ | |
protected void addImports(Stylesheet stylesheet, boolean addToList, | |
Vector importList) { | |
// Get the direct imports of this sheet. | |
int n = stylesheet.getImportCount(); | |
if (n > 0) { | |
for (int i = 0; i < n; i++) { | |
Stylesheet imported = stylesheet.getImport(i); | |
addImports(imported, true, importList); | |
} | |
} | |
n = stylesheet.getIncludeCount(); | |
if (n > 0) { | |
for (int i = 0; i < n; i++) { | |
Stylesheet included = stylesheet.getInclude(i); | |
addImports(included, false, importList); | |
} | |
} | |
if (addToList) | |
importList.addElement(stylesheet); | |
} | |
/** | |
* Get a stylesheet from the global import list. TODO: JKESS PROPOSES | |
* SPECIAL-CASE FOR NO IMPORT LIST, TO MATCH COUNT. | |
* | |
* @param i | |
* Index of stylesheet to get from global import list | |
* | |
* @return The stylesheet at the given index | |
*/ | |
public StylesheetComposed getGlobalImport(int i) { | |
return m_globalImportList[i]; | |
} | |
/** | |
* Get the total number of imports in the global import list. | |
* | |
* @return The total number of imported stylesheets, including the root | |
* stylesheet, thus the number will always be 1 or greater. TODO: | |
* JKESS PROPOSES SPECIAL-CASE FOR NO IMPORT LIST, TO MATCH | |
* DESCRIPTION. | |
*/ | |
public int getGlobalImportCount() { | |
return (m_globalImportList != null) ? m_globalImportList.length : 1; | |
} | |
/** | |
* Given a stylesheet, return the number of the stylesheet in the global | |
* import list. | |
* | |
* @param sheet | |
* The stylesheet which will be located in the global import | |
* list. | |
* @return The index into the global import list of the given stylesheet, or | |
* -1 if it is not found (which should never happen). | |
*/ | |
public int getImportNumber(StylesheetComposed sheet) { | |
if (this == sheet) | |
return 0; | |
int n = getGlobalImportCount(); | |
for (int i = 0; i < n; i++) { | |
if (sheet == getGlobalImport(i)) | |
return i; | |
} | |
return -1; | |
} | |
/** | |
* This will be set up with the default values, and then the values will be | |
* set as stylesheets are encountered. | |
* | |
* @serial | |
*/ | |
private OutputProperties m_outputProperties; | |
/** | |
* Recompose the output format object from the included elements. | |
* | |
* @param oprops | |
* non-null reference to xsl:output properties representation. | |
*/ | |
void recomposeOutput(OutputProperties oprops) throws TransformerException { | |
m_outputProperties.copyFrom(oprops); | |
} | |
/** | |
* Get the combined "xsl:output" property with the properties combined from | |
* the included stylesheets. If a xsl:output is not declared in this | |
* stylesheet or an included stylesheet, look in the imports. Please note | |
* that this returns a reference to the OutputProperties object, not a | |
* cloned object, like getOutputProperties does. | |
* | |
* @see <a href="http://www.w3.org/TR/xslt#output">output in XSLT | |
* Specification</a> | |
* | |
* @return non-null reference to composed output properties object. | |
*/ | |
public OutputProperties getOutputComposed() { | |
// System.out.println("getOutputComposed.getIndent: | |
// "+m_outputProperties.getIndent()); | |
// System.out.println("getOutputComposed.getIndenting: | |
// "+m_outputProperties.getIndenting()); | |
return m_outputProperties; | |
} | |
/** | |
* Flag indicating whether an output method has been set by the user. | |
* | |
* @serial | |
*/ | |
private boolean m_outputMethodSet = false; | |
/** | |
* Find out if an output method has been set by the user. | |
* | |
* @return Value indicating whether an output method has been set by the | |
* user | |
* @xsl.usage internal | |
*/ | |
public boolean isOutputMethodSet() { | |
return m_outputMethodSet; | |
} | |
/** | |
* Composed set of all included and imported attribute set properties. Each | |
* entry is a vector of ElemAttributeSet objects. | |
* | |
* @serial | |
*/ | |
private Hashtable m_attrSets; | |
/** | |
* Recompose the attribute-set declarations. | |
* | |
* @param attrSet | |
* An attribute-set to add to the hashtable of attribute sets. | |
*/ | |
void recomposeAttributeSets(ElemAttributeSet attrSet) { | |
Vector attrSetList = (Vector) m_attrSets.get(attrSet.getName()); | |
if (null == attrSetList) { | |
attrSetList = new Vector(); | |
m_attrSets.put(attrSet.getName(), attrSetList); | |
} | |
attrSetList.addElement(attrSet); | |
} | |
/** | |
* Get a list "xsl:attribute-set" properties that match the qname. | |
* | |
* @see <a href="http://www.w3.org/TR/xslt#attribute-sets">attribute-sets in | |
* XSLT Specification</a> | |
* | |
* @param name | |
* Qualified name of attribute set properties to get | |
* | |
* @return A vector of attribute sets matching the given name | |
* | |
* @throws ArrayIndexOutOfBoundsException | |
*/ | |
public Vector getAttributeSetComposed(QName name) | |
throws ArrayIndexOutOfBoundsException { | |
return (Vector) m_attrSets.get(name); | |
} | |
/** | |
* Table of DecimalFormatSymbols, keyed by QName. | |
* | |
* @serial | |
*/ | |
private Hashtable m_decimalFormatSymbols; | |
/** | |
* Recompose the decimal-format declarations. | |
* | |
* @param dfp | |
* A DecimalFormatProperties to add to the hashtable of decimal | |
* formats. | |
*/ | |
void recomposeDecimalFormats(DecimalFormatProperties dfp) { | |
DecimalFormatSymbols oldDfs = (DecimalFormatSymbols) m_decimalFormatSymbols | |
.get(dfp.getName()); | |
if (null == oldDfs) { | |
m_decimalFormatSymbols.put(dfp.getName(), dfp | |
.getDecimalFormatSymbols()); | |
} else if (!dfp.getDecimalFormatSymbols().equals(oldDfs)) { | |
String themsg; | |
if (dfp.getName().equals(new QName(""))) { | |
// "Only one default xsl:decimal-format declaration is allowed." | |
themsg = Messages | |
.createMessage( | |
XSLTErrorResources.WG_ONE_DEFAULT_XSLDECIMALFORMAT_ALLOWED, | |
new Object[0]); | |
} else { | |
// "xsl:decimal-format names must be unique. Name {0} has been | |
// duplicated." | |
themsg = Messages | |
.createMessage( | |
XSLTErrorResources.WG_XSLDECIMALFORMAT_NAMES_MUST_BE_UNIQUE, | |
new Object[] { dfp.getName() }); | |
} | |
error(themsg); // Should we throw TransformerException instead? | |
} | |
} | |
/** | |
* Given a valid element decimal-format name, return the | |
* decimalFormatSymbols with that name. | |
* <p> | |
* It is an error to declare either the default decimal-format or a | |
* decimal-format with a given name more than once (even with different | |
* import precedence), unless it is declared every time with the same value | |
* for all attributes (taking into account any default values). | |
* </p> | |
* <p> | |
* Which means, as far as I can tell, the decimal-format properties are not | |
* additive. | |
* </p> | |
* | |
* @param name | |
* Qualified name of the decimal format to find | |
* @return DecimalFormatSymbols object matching the given name or null if | |
* name is not found. | |
*/ | |
public DecimalFormatSymbols getDecimalFormatComposed(QName name) { | |
return (DecimalFormatSymbols) m_decimalFormatSymbols.get(name); | |
} | |
/** | |
* A list of all key declarations visible from this stylesheet and all | |
* lesser stylesheets. | |
* | |
* @serial | |
*/ | |
private Vector m_keyDecls; | |
/** | |
* Recompose the key declarations. | |
* | |
* @param keyDecl | |
* A KeyDeclaration to be added to the vector of key | |
* declarations. | |
*/ | |
void recomposeKeys(KeyDeclaration keyDecl) { | |
m_keyDecls.addElement(keyDecl); | |
} | |
/** | |
* Get the composed "xsl:key" properties. | |
* | |
* @see <a href="http://www.w3.org/TR/xslt#key">key in XSLT Specification</a> | |
* | |
* @return A vector of the composed "xsl:key" properties. | |
*/ | |
public Vector getKeysComposed() { | |
return m_keyDecls; | |
} | |
/** | |
* Composed set of all namespace aliases. | |
* | |
* @serial | |
*/ | |
private Hashtable m_namespaceAliasComposed; | |
/** | |
* Recompose the namespace-alias declarations. | |
* | |
* @param nsAlias | |
* A NamespaceAlias object to add to the hashtable of namespace | |
* aliases. | |
*/ | |
void recomposeNamespaceAliases(NamespaceAlias nsAlias) { | |
m_namespaceAliasComposed.put(nsAlias.getStylesheetNamespace(), nsAlias); | |
} | |
/** | |
* Get the "xsl:namespace-alias" property. Return the NamespaceAlias for a | |
* given namespace uri. | |
* | |
* @see <a | |
* href="http://www.w3.org/TR/xslt#literal-result-element">literal-result-element | |
* in XSLT Specification</a> | |
* | |
* @param uri | |
* non-null reference to namespace that is to be aliased. | |
* | |
* @return NamespaceAlias that matches uri, or null if no match. | |
*/ | |
public NamespaceAlias getNamespaceAliasComposed(String uri) { | |
return (NamespaceAlias) ((null == m_namespaceAliasComposed) ? null | |
: m_namespaceAliasComposed.get(uri)); | |
} | |
/** | |
* The "xsl:template" properties. | |
* | |
* @serial | |
*/ | |
private TemplateList m_templateList; | |
/** | |
* Recompose the template declarations. | |
* | |
* @param template | |
* An ElemTemplate object to add to the template list. | |
*/ | |
void recomposeTemplates(ElemTemplate template) { | |
m_templateList.setTemplate(template); | |
} | |
/** | |
* Accessor method to retrieve the <code>TemplateList</code> associated | |
* with this StylesheetRoot. | |
* | |
* @return The composed <code>TemplateList</code>. | |
*/ | |
public final TemplateList getTemplateListComposed() { | |
return m_templateList; | |
} | |
/** | |
* Mutator method to set the <code>TemplateList</code> associated with | |
* this StylesheetRoot. This method should only be used by the compiler. | |
* Normally, the template list is built during the recompose process and | |
* should not be altered by the user. | |
* | |
* @param templateList | |
* The new <code>TemplateList</code> for this StylesheetRoot. | |
*/ | |
public final void setTemplateListComposed(TemplateList templateList) { | |
m_templateList = templateList; | |
} | |
/** | |
* Get an "xsl:template" property by node match. This looks in the imports | |
* as well as this stylesheet. | |
* | |
* @see <a | |
* href="http://www.w3.org/TR/xslt#section-Defining-Template-Rules">section-Defining-Template-Rules | |
* in XSLT Specification</a> | |
* | |
* @param xctxt | |
* non-null reference to XPath runtime execution context. | |
* @param targetNode | |
* non-null reference of node that the template must match. | |
* @param mode | |
* qualified name of the node, or null. | |
* @param quietConflictWarnings | |
* true if conflict warnings should not be reported. | |
* | |
* @return reference to ElemTemplate that is the best match for targetNode, | |
* or null if no match could be made. | |
* | |
* @throws TransformerException | |
*/ | |
public ElemTemplate getTemplateComposed(XPathContext xctxt, int targetNode, | |
QName mode, boolean quietConflictWarnings, DTM dtm) | |
throws TransformerException { | |
return m_templateList.getTemplate(xctxt, targetNode, mode, | |
quietConflictWarnings, dtm); | |
} | |
/** | |
* Get an "xsl:template" property by node match. This looks in the imports | |
* as well as this stylesheet. | |
* | |
* @see <a | |
* href="http://www.w3.org/TR/xslt#section-Defining-Template-Rules">section-Defining-Template-Rules | |
* in XSLT Specification</a> | |
* | |
* @param xctxt | |
* non-null reference to XPath runtime execution context. | |
* @param targetNode | |
* non-null reference of node that the template must match. | |
* @param mode | |
* qualified name of the node, or null. | |
* @param maxImportLevel | |
* The maximum importCountComposed that we should consider or -1 | |
* if we should consider all import levels. This is used by | |
* apply-imports to access templates that have been overridden. | |
* @param endImportLevel | |
* The count of composed imports | |
* @param quietConflictWarnings | |
* true if conflict warnings should not be reported. | |
* | |
* @return reference to ElemTemplate that is the best match for targetNode, | |
* or null if no match could be made. | |
* | |
* @throws TransformerException | |
*/ | |
public ElemTemplate getTemplateComposed(XPathContext xctxt, int targetNode, | |
QName mode, int maxImportLevel, int endImportLevel, | |
boolean quietConflictWarnings, DTM dtm) throws TransformerException { | |
return m_templateList.getTemplate(xctxt, targetNode, mode, | |
maxImportLevel, endImportLevel, quietConflictWarnings, dtm); | |
} | |
/** | |
* Get an "xsl:template" property. This looks in the imports as well as this | |
* stylesheet. | |
* | |
* @see <a | |
* href="http://www.w3.org/TR/xslt#section-Defining-Template-Rules">section-Defining-Template-Rules | |
* in XSLT Specification</a> | |
* | |
* @param qname | |
* non-null reference to qualified name of template. | |
* | |
* @return reference to named template, or null if not found. | |
*/ | |
public ElemTemplate getTemplateComposed(QName qname) { | |
return m_templateList.getTemplate(qname); | |
} | |
/** | |
* Composed set of all variables and params. | |
* | |
* @serial | |
*/ | |
private Vector m_variables; | |
/** | |
* Recompose the top level variable and parameter declarations. | |
* | |
* @param elemVar | |
* A top level variable or parameter to be added to the Vector. | |
*/ | |
void recomposeVariables(ElemVariable elemVar) { | |
// Don't overide higher priority variable | |
if (getVariableOrParamComposed(elemVar.getName()) == null) { | |
elemVar.setIsTopLevel(true); // Mark as a top-level variable or | |
// param | |
elemVar.setIndex(m_variables.size()); | |
m_variables.addElement(elemVar); | |
} | |
} | |
/** | |
* Get an "xsl:variable" property. | |
* | |
* @see <a | |
* href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables | |
* in XSLT Specification</a> | |
* | |
* @param qname | |
* Qualified name of variable or param | |
* | |
* @return The ElemVariable with the given qualified name | |
*/ | |
public ElemVariable getVariableOrParamComposed(QName qname) { | |
if (null != m_variables) { | |
int n = m_variables.size(); | |
for (int i = 0; i < n; i++) { | |
ElemVariable var = (ElemVariable) m_variables.elementAt(i); | |
if (var.getName().equals(qname)) | |
return var; | |
} | |
} | |
return null; | |
} | |
/** | |
* Get all global "xsl:variable" properties in scope for this stylesheet. | |
* | |
* @see <a | |
* href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables | |
* in XSLT Specification</a> | |
* | |
* @return Vector of all variables and params in scope | |
*/ | |
public Vector getVariablesAndParamsComposed() { | |
return m_variables; | |
} | |
/** | |
* A list of properties that specify how to do space stripping. This uses | |
* the same exact mechanism as Templates. | |
* | |
* @serial | |
*/ | |
private TemplateList m_whiteSpaceInfoList; | |
/** | |
* Recompose the strip-space and preserve-space declarations. | |
* | |
* @param wsi | |
* A WhiteSpaceInfo element to add to the list of WhiteSpaceInfo | |
* elements. | |
*/ | |
void recomposeWhiteSpaceInfo(WhiteSpaceInfo wsi) { | |
if (null == m_whiteSpaceInfoList) | |
m_whiteSpaceInfoList = new TemplateList(); | |
m_whiteSpaceInfoList.setTemplate(wsi); | |
} | |
/** | |
* Check to see if the caller should bother with check for whitespace nodes. | |
* | |
* @return Whether the caller should bother with check for whitespace nodes. | |
*/ | |
public boolean shouldCheckWhitespace() { | |
return null != m_whiteSpaceInfoList; | |
} | |
/** | |
* Get information about whether or not an element should strip whitespace. | |
* | |
* @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT | |
* Specification</a> | |
* | |
* @param support | |
* The XPath runtime state. | |
* @param targetElement | |
* Element to check | |
* | |
* @return WhiteSpaceInfo for the given element | |
* | |
* @throws TransformerException | |
*/ | |
public WhiteSpaceInfo getWhiteSpaceInfo(XPathContext support, | |
int targetElement, DTM dtm) throws TransformerException { | |
if (null != m_whiteSpaceInfoList) | |
return (WhiteSpaceInfo) m_whiteSpaceInfoList.getTemplate(support, | |
targetElement, null, false, dtm); | |
else | |
return null; | |
} | |
/** | |
* Get information about whether or not an element should strip whitespace. | |
* | |
* @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT | |
* Specification</a> | |
* | |
* @param support | |
* The XPath runtime state. | |
* @param targetElement | |
* Element to check | |
* | |
* @return true if the whitespace should be stripped. | |
* | |
* @throws TransformerException | |
*/ | |
public boolean shouldStripWhiteSpace(XPathContext support, int targetElement) | |
throws TransformerException { | |
if (null != m_whiteSpaceInfoList) { | |
while (DTM.NULL != targetElement) { | |
DTM dtm = support.getDTM(targetElement); | |
WhiteSpaceInfo info = (WhiteSpaceInfo) m_whiteSpaceInfoList | |
.getTemplate(support, targetElement, null, false, dtm); | |
if (null != info) | |
return info.getShouldStripSpace(); | |
int parent = dtm.getParent(targetElement); | |
if (DTM.NULL != parent | |
&& DTM.ELEMENT_NODE == dtm.getNodeType(parent)) | |
targetElement = parent; | |
else | |
targetElement = DTM.NULL; | |
} | |
} | |
return false; | |
} | |
/** | |
* Get information about whether or not whitespace can be stripped. | |
* | |
* @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT | |
* Specification</a> | |
* | |
* @return true if the whitespace can be stripped. | |
*/ | |
@Override | |
public boolean canStripWhiteSpace() { | |
return (null != m_whiteSpaceInfoList); | |
} | |
/** | |
* The default template to use for text nodes if we don't find anything | |
* else. This is initialized in initDefaultRule(). | |
* | |
* @serial | |
* @xsl.usage advanced | |
*/ | |
private ElemTemplate m_defaultTextRule; | |
/** | |
* Get the default template for text. | |
* | |
* @return the default template for text. | |
* @xsl.usage advanced | |
*/ | |
public final ElemTemplate getDefaultTextRule() { | |
return m_defaultTextRule; | |
} | |
/** | |
* The default template to use if we don't find anything else. This is | |
* initialized in initDefaultRule(). | |
* | |
* @serial | |
* @xsl.usage advanced | |
*/ | |
private ElemTemplate m_defaultRule; | |
/** | |
* Get the default template for elements. | |
* | |
* @return the default template for elements. | |
* @xsl.usage advanced | |
*/ | |
public final ElemTemplate getDefaultRule() { | |
return m_defaultRule; | |
} | |
/** | |
* The default template to use for the root if we don't find anything else. | |
* This is initialized in initDefaultRule(). We kind of need this because | |
* the defaultRule isn't good enough because it doesn't supply a document | |
* context. For now, I default the root document element to "HTML". Don't | |
* know if this is really a good idea or not. I suspect it is not. | |
* | |
* @serial | |
* @xsl.usage advanced | |
*/ | |
private ElemTemplate m_defaultRootRule; | |
/** | |
* Get the default template for a root node. | |
* | |
* @return The default template for a root node. | |
* @xsl.usage advanced | |
*/ | |
public final ElemTemplate getDefaultRootRule() { | |
return m_defaultRootRule; | |
} | |
/** | |
* The start rule to kick off the transformation. | |
* | |
* @serial | |
* @xsl.usage advanced | |
*/ | |
private ElemTemplate m_startRule; | |
/** | |
* Get the default template for a root node. | |
* | |
* @return The default template for a root node. | |
* @xsl.usage advanced | |
*/ | |
public final ElemTemplate getStartRule() { | |
return m_startRule; | |
} | |
/** | |
* Used for default selection. | |
* | |
* @serial | |
*/ | |
XPath m_selectDefault; | |
/** | |
* Create the default rule if needed. | |
* | |
* @throws TransformerException | |
*/ | |
private void initDefaultRule(ErrorListener errorListener) | |
throws TransformerException { | |
// Then manufacture a default | |
m_defaultRule = new ElemTemplate(); | |
m_defaultRule.setStylesheet(this); | |
XPath defMatch = new XPath("*", this, this, XPath.MATCH, errorListener); | |
m_defaultRule.setMatch(defMatch); | |
ElemApplyTemplates childrenElement = new ElemApplyTemplates(); | |
childrenElement.setIsDefaultTemplate(true); | |
childrenElement.setSelect(m_selectDefault); | |
m_defaultRule.appendChild(childrenElement); | |
m_startRule = m_defaultRule; | |
// ----------------------------- | |
m_defaultTextRule = new ElemTemplate(); | |
m_defaultTextRule.setStylesheet(this); | |
defMatch = new XPath("text() | @*", this, this, XPath.MATCH, | |
errorListener); | |
m_defaultTextRule.setMatch(defMatch); | |
ElemValueOf elemValueOf = new ElemValueOf(); | |
m_defaultTextRule.appendChild(elemValueOf); | |
XPath selectPattern = new XPath(".", this, this, XPath.SELECT, | |
errorListener); | |
elemValueOf.setSelect(selectPattern); | |
// -------------------------------- | |
m_defaultRootRule = new ElemTemplate(); | |
m_defaultRootRule.setStylesheet(this); | |
defMatch = new XPath("/", this, this, XPath.MATCH, errorListener); | |
m_defaultRootRule.setMatch(defMatch); | |
childrenElement = new ElemApplyTemplates(); | |
childrenElement.setIsDefaultTemplate(true); | |
m_defaultRootRule.appendChild(childrenElement); | |
childrenElement.setSelect(m_selectDefault); | |
} | |
/** | |
* This is a generic version of C.A.R Hoare's Quick Sort algorithm. This | |
* will handle arrays that are already sorted, and arrays with duplicate | |
* keys. It was lifted from the NodeSorter class but should probably be | |
* eliminated and replaced with a call to Collections.sort when we migrate | |
* to Java2.<BR> | |
* | |
* If you think of a one dimensional array as going from the lowest index on | |
* the left to the highest index on the right then the parameters to this | |
* function are lowest index or left and highest index or right. The first | |
* time you call this function it will be with the parameters 0, a.length - | |
* 1. | |
* | |
* @param v | |
* a vector of ElemTemplateElement elements | |
* @param lo0 | |
* left boundary of partition | |
* @param hi0 | |
* right boundary of partition | |
* | |
*/ | |
private void QuickSort2(Vector v, int lo0, int hi0) { | |
int lo = lo0; | |
int hi = hi0; | |
if (hi0 > lo0) { | |
// Arbitrarily establishing partition element as the midpoint of | |
// the array. | |
ElemTemplateElement midNode = (ElemTemplateElement) v | |
.elementAt((lo0 + hi0) / 2); | |
// loop through the array until indices cross | |
while (lo <= hi) { | |
// find the first element that is greater than or equal to | |
// the partition element starting from the left Index. | |
while ((lo < hi0) | |
&& (((ElemTemplateElement) v.elementAt(lo)) | |
.compareTo(midNode) < 0)) { | |
++lo; | |
} // end while | |
// find an element that is smaller than or equal to | |
// the partition element starting from the right Index. | |
while ((hi > lo0) | |
&& (((ElemTemplateElement) v.elementAt(hi)) | |
.compareTo(midNode) > 0)) { | |
--hi; | |
} | |
// if the indexes have not crossed, swap | |
if (lo <= hi) { | |
ElemTemplateElement node = (ElemTemplateElement) v | |
.elementAt(lo); | |
v.setElementAt(v.elementAt(hi), lo); | |
v.setElementAt(node, hi); | |
++lo; | |
--hi; | |
} | |
} | |
// If the right index has not reached the left side of array | |
// must now sort the left partition. | |
if (lo0 < hi) { | |
QuickSort2(v, lo0, hi); | |
} | |
// If the left index has not reached the right side of array | |
// must now sort the right partition. | |
if (lo < hi0) { | |
QuickSort2(v, lo, hi0); | |
} | |
} | |
} // end QuickSort2 */ | |
private transient ComposeState m_composeState; | |
/** | |
* Initialize a new ComposeState. | |
*/ | |
void initComposeState() { | |
m_composeState = new ComposeState(); | |
} | |
/** | |
* Return class to track state global state during the compose() operation. | |
* | |
* @return ComposeState reference, or null if endCompose has been called. | |
*/ | |
ComposeState getComposeState() { | |
return m_composeState; | |
} | |
/** | |
* Clear the compose state. | |
*/ | |
private void clearComposeState() { | |
m_composeState = null; | |
} | |
private String m_extensionHandlerClass = "org.apache.xalan.extensions.ExtensionHandlerExsltFunction"; | |
/** | |
* This internal method allows the setting of the java class to handle the | |
* extension function (if other than the default one). | |
* | |
* @xsl.usage internal | |
*/ | |
public String setExtensionHandlerClass(String handlerClassName) { | |
String oldvalue = m_extensionHandlerClass; | |
m_extensionHandlerClass = handlerClassName; | |
return oldvalue; | |
} | |
/** | |
* | |
* @xsl.usage internal | |
*/ | |
public String getExtensionHandlerClass() { | |
return m_extensionHandlerClass; | |
} | |
/** | |
* Class to track state global state during the compose() operation. | |
*/ | |
class ComposeState { | |
ComposeState() { | |
int size = m_variables.size(); | |
for (int i = 0; i < size; i++) { | |
ElemVariable ev = (ElemVariable) m_variables.elementAt(i); | |
m_variableNames.addElement(ev.getName()); | |
} | |
} | |
private ExpandedNameTable m_ent = new ExpandedNameTable(); | |
/** | |
* Given a qualified name, return an integer ID that can be quickly | |
* compared. | |
* | |
* @param qname | |
* a qualified name object, must not be null. | |
* | |
* @return the expanded-name id of the qualified name. | |
*/ | |
public int getQNameID(QName qname) { | |
return m_ent.getExpandedTypeID(qname.getNamespace(), qname | |
.getLocalName(), | |
// The type doesn't matter for our | |
// purposes. | |
org.apache.xml.dtm.DTM.ELEMENT_NODE); | |
} | |
/** | |
* A Vector of the current params and QNames within the current | |
* template. Set by ElemTemplate and used by ProcessorVariable. | |
*/ | |
private java.util.Vector m_variableNames = new java.util.Vector(); | |
/** | |
* Add the name of a qualified name within the template. The position in | |
* the vector is its ID. | |
* | |
* @param qname | |
* A qualified name of a param or variable, should be | |
* non-null. | |
* @return the index where the variable was added. | |
*/ | |
int addVariableName(final org.apache.xml.utils.QName qname) { | |
int pos = m_variableNames.size(); | |
m_variableNames.addElement(qname); | |
int frameSize = m_variableNames.size() - getGlobalsSize(); | |
if (frameSize > m_maxStackFrameSize) | |
m_maxStackFrameSize++; | |
return pos; | |
} | |
void resetStackFrameSize() { | |
m_maxStackFrameSize = 0; | |
} | |
int getFrameSize() { | |
return m_maxStackFrameSize; | |
} | |
/** | |
* Get the current size of the stack frame. Use this to record the | |
* position in a template element at startElement, so that it can be | |
* popped at endElement. | |
*/ | |
int getCurrentStackFrameSize() { | |
return m_variableNames.size(); | |
} | |
/** | |
* Set the current size of the stack frame. | |
*/ | |
void setCurrentStackFrameSize(int sz) { | |
m_variableNames.setSize(sz); | |
} | |
int getGlobalsSize() { | |
return m_variables.size(); | |
} | |
IntStack m_marks = new IntStack(); | |
void pushStackMark() { | |
m_marks.push(getCurrentStackFrameSize()); | |
} | |
void popStackMark() { | |
int mark = m_marks.pop(); | |
setCurrentStackFrameSize(mark); | |
} | |
/** | |
* Get the Vector of the current params and QNames to be collected | |
* within the current template. | |
* | |
* @return A reference to the vector of variable names. The reference | |
* returned is owned by this class, and so should not really be | |
* mutated, or stored anywhere. | |
*/ | |
java.util.Vector getVariableNames() { | |
return m_variableNames; | |
} | |
private int m_maxStackFrameSize; | |
} | |
/** | |
* @return Optimization flag | |
*/ | |
public boolean getOptimizer() { | |
return m_optimizer; | |
} | |
/** | |
* @param b | |
* Optimization flag | |
*/ | |
public void setOptimizer(boolean b) { | |
m_optimizer = b; | |
} | |
/** | |
* @return Incremental flag | |
*/ | |
public boolean getIncremental() { | |
return m_incremental; | |
} | |
/** | |
* @return source location flag | |
*/ | |
public boolean getSource_location() { | |
return m_source_location; | |
} | |
/** | |
* @param b | |
* Incremental flag | |
*/ | |
public void setIncremental(boolean b) { | |
m_incremental = b; | |
} | |
/** | |
* @param b | |
* Source location flag | |
*/ | |
public void setSource_location(boolean b) { | |
m_source_location = b; | |
} | |
} |