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

net.sf.saxon.style.PrincipalStylesheetModule Maven / Gradle / Ivy

There is a newer version: 12.5
Show newest version
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2015 Saxonica Limited.
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

package net.sf.saxon.style;

import net.sf.saxon.Configuration;
import net.sf.saxon.expr.Component;
import net.sf.saxon.expr.Expression;
import net.sf.saxon.expr.instruct.*;
import net.sf.saxon.expr.parser.ExpressionTool;
import net.sf.saxon.expr.parser.Optimizer;
import net.sf.saxon.functions.ExecutableFunctionLibrary;
import net.sf.saxon.lib.SaxonOutputKeys;
import net.sf.saxon.om.DocumentURI;
import net.sf.saxon.om.NamespaceBinding;
import net.sf.saxon.om.StandardNames;
import net.sf.saxon.om.StructuredQName;
import net.sf.saxon.query.XQueryFunction;
import net.sf.saxon.query.XQueryFunctionLibrary;
import net.sf.saxon.serialize.CharacterMap;
import net.sf.saxon.serialize.CharacterMapIndex;
import net.sf.saxon.trans.*;
import net.sf.saxon.value.Whitespace;
import net.sf.saxon.z.IntHashMap;

import java.util.*;


/**
 * Represents both the stylesheet module at the root of the import tree, that is, the module
 * that includes or imports all the others, and also the XSLT package that has this stylesheet
 * module as its root.
 * 

* This version of the StylesheetPackage class represents a trivial package that is constructed * by the system to wrap an ordinary (non-package) stylesheet, as available in XSLT 2.0. A subclass * is used for "real" packages, currently available only in Saxon-EE. */ public class PrincipalStylesheetModule extends StylesheetModule implements GlobalVariableManager { /** * A class that simply encapsulates a callback action of any kind */ public static abstract class Action { public abstract void doAction() throws XPathException; } private StylesheetPackage stylesheetPackage; private boolean declaredModes; // table of functions imported from XQuery library modules //private XQueryFunctionLibrary queryFunctions; // library of functions that are in-scope for XPath expressions in this stylesheet //private FunctionLibraryList functionLibrary; // index of global variables and parameters, by StructuredQName // (overridden variables are excluded). private HashMap globalVariableIndex = new HashMap(20); // index of templates - only includes those actually declared within this package private HashMap templateIndex = new HashMap(20); // Table of named stylesheet functions. private HashMap functionIndex = new HashMap(8); // map for allocating unique numbers to local parameter names. Key is a // StructuredQName; value is a boxed int. /*@Nullable*/ private HashMap localParameterNumbers = null; // key manager for all the xsl:key definitions in this package private KeyManager keyManager; // decimal format manager for all the xsl:decimal-format definitions in this package private DecimalFormatManager decimalFormatManager; // rule manager for template rules private RuleManager ruleManager; // manager class for accumulator rules (XSLT 3.0 only) private IAccumulatorRegistry accumulatorManager = null; // namespace aliases. This information is needed at compile-time only private int numberOfAliases = 0; private List namespaceAliasList = new ArrayList(5); private HashMap namespaceAliasMap; private Set aliasResultUriSet; // attribute sets. A package can contain several declarations attribute sets with the same name. // They are indexed in the order they will be applied, that is, highest precedence first private Map> attributeSetDeclarations = new HashMap>(); // cache of stylesheet documents. Note that multiple imports of the same URI // lead to the stylesheet tree being reused private HashMap moduleCache = new HashMap(4); private CharacterMapIndex characterMapIndex; private List fixupActions = new ArrayList(); private List completionActions = new ArrayList(); //private boolean createsSecondaryResultDocuments = false; private boolean needsDynamicOutputProperties = false; /** * Create a PrincipalStylesheetModule * * @param sourceElement the xsl:package element at the root of the package manifest */ public PrincipalStylesheetModule(XSLPackage sourceElement) throws XPathException { super(sourceElement, 0); declaredModes = sourceElement.isDeclaredModes(); stylesheetPackage = getConfiguration().makeStylesheetPackage(); int xPathVersion = sourceElement.getProcessorVersion() >= 30 ? 31 : 20; stylesheetPackage.setXPathVersion(xPathVersion); stylesheetPackage.setTargetEdition(sourceElement.getCompilation().getCompilerInfo().getTargetEdition()); keyManager = stylesheetPackage.getKeyManager(); decimalFormatManager = stylesheetPackage.getDecimalFormatManager(); ruleManager = new RuleManager(stylesheetPackage, sourceElement.getCompilation().getCompilerInfo()); ruleManager.getUnnamedMode().makeDeclaringComponent(Visibility.PRIVATE, stylesheetPackage); stylesheetPackage.setRuleManager(ruleManager); stylesheetPackage.setDefaultMode(sourceElement.getDefaultMode()); characterMapIndex = new CharacterMapIndex(); stylesheetPackage.setCharacterMapIndex(characterMapIndex); try { setInputTypeAnnotations(sourceElement.getInputTypeAnnotationsAttribute()); } catch (XPathException err) { // it will be reported some other time } } /** * Search the package for a component with a given name * * @param name the symbolic name of the required component * @return the requested component if found, or null otherwise */ public Component getComponent(SymbolicName name) { return stylesheetPackage.getComponentIndex().get(name); } /** * Remove a global variable if it is found to be redundant, i.e. it is private to the package * and not referenced * * @param decl the global variable to be removed */ public void removeGlobalVariable(ComponentDeclaration decl) { StructuredQName name = decl.getSourceElement().getObjectName(); SymbolicName sName = new SymbolicName(StandardNames.XSL_VARIABLE, name); if (globalVariableIndex.get(name) == decl) { stylesheetPackage.getComponentIndex().remove(sName); globalVariableIndex.remove(name); } } /** * Get the outermost stylesheet module in a package * * @return this module (this class represents both the package and its outermost module) */ /*@NotNull*/ @Override public PrincipalStylesheetModule getPrincipalStylesheetModule() { return this; } /** * Get the stylesheet package * * @return the associated stylesheet package */ public StylesheetPackage getStylesheetPackage() { return stylesheetPackage; } /** * Get the key manager used to manage xsl:key declarations in this package * * @return the key manager */ public KeyManager getKeyManager() { return keyManager; } /** * Get the decimal format manager used to manage xsl:decimal-format declarations in this package * * @return the decimal format manager */ public DecimalFormatManager getDecimalFormatManager() { return decimalFormatManager; } /** * Get the rule manager used to manage modes declared explicitly or implicitly in this package * * @return the rule manager */ public RuleManager getRuleManager() { return ruleManager; } /** * Ask whether it is required that modes be explicitly declared * * @return true if modes referenced within this package must be explicitly declared */ public boolean isDeclaredModes() { return declaredModes; } /** * Register a callback action to be performed during the fixup phase * * @param action the callback action */ public void addFixupAction(Action action) { fixupActions.add(action); } /** * Register a callback action to be performed during the completion phase * * @param action the callback action */ public void addCompletionAction(Action action) { completionActions.add(action); } /** * Say that this stylesheet package needs dynamic output properties * * @param b true if this stylesheet needs dynamic output properties */ public void setNeedsDynamicOutputProperties(boolean b) { needsDynamicOutputProperties = b; } /** * Get an index of character maps declared using xsl:character-map entries in this package * * @return the character map index */ public CharacterMapIndex getCharacterMapIndex() { return characterMapIndex; } /** * Declare an imported XQuery function * * @param function the imported function * @throws net.sf.saxon.trans.XPathException if an error occurs */ public void declareXQueryFunction(XQueryFunction function) throws XPathException { getStylesheetPackage().getXQueryFunctionLibrary().declareFunction(function); } /** * Add a module to the cache of stylesheet modules * * @param key the key to be used (based on the absolute URI) * @param module the stylesheet document tree corresponding to this absolute URI */ public void putStylesheetDocument(DocumentURI key, XSLStylesheet module) { moduleCache.put(key, module); } /** * Get a module from the cache of stylesheet modules * * @param key the key to be used (based on the absolute URI) * @return the stylesheet document tree corresponding to this absolute URI */ public XSLModuleRoot getStylesheetDocument(DocumentURI key) { XSLModuleRoot sheet = moduleCache.get(key); if (sheet != null) { XPathException warning = new XPathException( "Stylesheet module " + key + " is included or imported more than once. " + "This is permitted, but may lead to errors or unexpected behavior"); sheet.issueWarning(warning); } return sheet; } /** * Preprocess does all the processing possible before the source document is available. * It is done once per stylesheet, so the stylesheet can be reused for multiple source * documents. The method is called only on the XSLStylesheet element representing the * principal stylesheet module * * @throws net.sf.saxon.trans.XPathException if errors are found in the stylesheet */ public void preprocess() throws XPathException { net.sf.saxon.trans.Timer timer; if (Compilation.TIMING) { timer = new net.sf.saxon.trans.Timer(); } // process any xsl:use-package, xsl:include and xsl:import elements spliceUsePackages((XSLPackage) getRootElement(), getRootElement().getCompilation()); if (Compilation.TIMING) { timer.report("spliceIncludes"); } // import schema documents importSchemata(); if (Compilation.TIMING) { timer.report("importSchemata"); } // build indexes for selected top-level elements buildIndexes(); if (Compilation.TIMING) { timer.report("buildIndexes"); } // check for use of schema-aware constructs checkForSchemaAwareness(); if (Compilation.TIMING) { timer.report("checkForSchemaAwareness"); } // process the attributes of every node in the tree processAllAttributes(); if (Compilation.TIMING) { timer.report("processAllAttributes"); } // collect any namespace aliases collectNamespaceAliases(); if (Compilation.TIMING) { timer.report("collectNamespaceAliases"); } // fix up references from XPath expressions to variables and functions, for static typing for (ComponentDeclaration decl : topLevel) { StyleElement inst = decl.getSourceElement(); if (!inst.isActionCompleted(StyleElement.ACTION_FIXUP)) { inst.setActionCompleted(StyleElement.ACTION_FIXUP); // if (inst instanceof XSLVariableDeclaration) { // System.err.println("Fixup global variable " + ((XSLVariableDeclaration)inst).getVariableQName()); // } inst.fixupReferences(); } } if (Compilation.TIMING) { timer.report("fixupReferences"); } // Validate the whole package (i.e. with included and imported stylesheet modules) XSLPackage top = (XSLPackage) getStylesheetElement(); //setInputTypeAnnotations(top.getInputTypeAnnotationsAttribute()); ComponentDeclaration decl = new ComponentDeclaration(this, top); if (!top.isActionCompleted(StyleElement.ACTION_VALIDATE)) { top.setActionCompleted(StyleElement.ACTION_VALIDATE); //top.validate(decl); getRootElement().validate(decl); top.validate(null); for (ComponentDeclaration d : topLevel) { d.getSourceElement().validateSubtree(d, false); } } if (Compilation.TIMING) { timer.report("validate"); timer.reportCumulative("total preprocess"); } // Gather the output properties Properties props = gatherOutputProperties(null); props.setProperty(SaxonOutputKeys.STYLESHEET_VERSION, getStylesheetPackage().getVersion() + ""); getStylesheetPackage().setDefaultOutputProperties(props); // Handle named output formats for use at run-time HashSet outputNames = new HashSet(5); for (ComponentDeclaration outputDecl : topLevel) { if (outputDecl.getSourceElement() instanceof XSLOutput) { XSLOutput out = (XSLOutput) outputDecl.getSourceElement(); StructuredQName qName = out.getFormatQName(); if (qName != null) { outputNames.add(qName); } } } if (outputNames.isEmpty()) { if (needsDynamicOutputProperties) { throw new XPathException( "The stylesheet contains xsl:result-document instructions that calculate the output " + "format name at run-time, but there are no named xsl:output declarations", "XTDE1460"); } } else { for (StructuredQName qName : outputNames) { Properties oprops = gatherOutputProperties(qName); if (needsDynamicOutputProperties) { getStylesheetPackage().setNamedOutputProperties(qName, oprops); } } } // Index the character maps for (ComponentDeclaration d : topLevel) { StyleElement inst = d.getSourceElement(); if (inst instanceof XSLCharacterMap) { XSLCharacterMap xcm = (XSLCharacterMap) inst; StructuredQName qn = xcm.getCharacterMapName(); IntHashMap map = new IntHashMap(); xcm.assemble(map); characterMapIndex.putCharacterMap(xcm.getCharacterMapName(), new CharacterMap(qn, map)); } } } /** * Incorporate declarations from used packages * * @param xslpackage the used package * @param compilation this compilation * @throws XPathException if any error is detected in the used package */ protected void spliceUsePackages(XSLPackage xslpackage, Compilation compilation) throws XPathException { CompilerInfo info = compilation.getCompilerInfo(); if (info.isVersionWarning() && xslpackage.getEffectiveVersion() != compilation.getStyleNodeFactory(true).getXsltProcessorVersion()) { XPathException w = new XPathException( "Running an XSLT " + xslpackage.getEffectiveVersion() + " stylesheet with an XSLT " + xslpackage.getCompilation().getStyleNodeFactory(true).getXsltProcessorVersion() + " processor"); w.setLocator(xslpackage); compilation.reportWarning(w); } // Preprocess the stylesheet, performing validation and preparing template definitions spliceIncludes(); } /** * Process import-schema declarations * * @throws net.sf.saxon.trans.XPathException if errors are detected */ protected void importSchemata() throws XPathException { // Outside Saxon-EE, xsl:import-schemas are an error for (int i = topLevel.size() - 1; i >= 0; i--) { ComponentDeclaration decl = topLevel.get(i); if (decl.getSourceElement() instanceof XSLImportSchema) { XPathException xe = new XPathException("xsl:import-schema requires Saxon-EE"); xe.setErrorCode("XTSE1650"); xe.setLocator(decl.getSourceElement()); throw xe; } } } /** * Build indexes for selected top-level declarations * * @throws net.sf.saxon.trans.XPathException if errors are detected */ private void buildIndexes() throws XPathException { // Scan the declarations in reverse order, that is, highest precedence first for (int i = topLevel.size() - 1; i >= 0; i--) { ComponentDeclaration decl = topLevel.get(i); decl.getSourceElement().index(decl, this); } } /** * Process the attributes of every node in the stylesheet * * @throws net.sf.saxon.trans.XPathException if static errors are found in the stylesheet */ public void processAllAttributes() throws XPathException { getRootElement().processDefaultCollationAttribute(); getRootElement().processDefaultMode(); getRootElement().prepareAttributes(); for (XSLModuleRoot xss : moduleCache.values()) { xss.prepareAttributes(); } for (ComponentDeclaration decl : topLevel) { StyleElement inst = decl.getSourceElement(); if (!inst.isActionCompleted(StyleElement.ACTION_PROCESS_ATTRIBUTES)) { inst.setActionCompleted(StyleElement.ACTION_PROCESS_ATTRIBUTES); try { inst.processAllAttributes(); } catch (XPathException err) { decl.getSourceElement().compileError(err); } } } } /** * Add a stylesheet function to the index * * @param decl The declaration wrapping an XSLFunction object * @throws XPathException if errors are found */ protected void indexFunction(ComponentDeclaration decl) throws XPathException { HashMap componentIndex = stylesheetPackage.getComponentIndex(); XSLFunction sourceFunction = (XSLFunction) decl.getSourceElement(); UserFunction compiledFunction = sourceFunction.getCompiledFunction(); Component declaringComponent = compiledFunction.getDeclaringComponent(); if (declaringComponent == null) { declaringComponent = compiledFunction.makeDeclaringComponent(sourceFunction.getVisibility(), getStylesheetPackage()); } SymbolicName sName = sourceFunction.getSymbolicName(); //StructuredQName qName = template.getTemplateName(); if (sName != null) { // see if there is already a named function with this precedence Component other = componentIndex.get(sName); if (other == null) { // this is the first componentIndex.put(sName, declaringComponent); functionIndex.put(sName, decl); } else { if (other.getDeclaringPackage() == getStylesheetPackage()) { // check the precedences int thisPrecedence = decl.getPrecedence(); ComponentDeclaration otherFunction = functionIndex.get(sName); int otherPrecedence = otherFunction.getPrecedence(); if (thisPrecedence == otherPrecedence) { sourceFunction.compileError("Duplicate named function (see line " + otherFunction.getSourceElement().getLineNumber() + " of " + otherFunction.getSourceElement().getSystemId() + ')', "XTSE0770"); } else if (thisPrecedence < otherPrecedence) { //template.setRedundantNamedTemplate(); } else { // can't happen, but we'll play safe //other.setRedundantNamedTemplate(); componentIndex.put(sName, declaringComponent); functionIndex.put(sName, decl); } } else if (sourceFunction.findAncestorElement(StandardNames.XSL_OVERRIDE) != null) { // the new one wins componentIndex.put(sName, declaringComponent); functionIndex.put(sName, decl); } else { sourceFunction.compileError("Function " + sName.getComponentName().getDisplayName() + "#" + sName.getArity() + " conflicts with a public function in package " + other.getDeclaringPackage().getPackageName(), "XTSE3050"); } } } } /** * Index a global xsl:variable or xsl:param element * * @param decl The Declaration referencing the XSLVariable or XSLParam element * @throws XPathException if an error occurs */ protected void indexVariableDeclaration(ComponentDeclaration decl) throws XPathException { XSLGlobalVariable varDecl = (XSLGlobalVariable) decl.getSourceElement(); StructuredQName qName = varDecl.getSourceBinding().getVariableQName(); GlobalVariable compiledVariable = (GlobalVariable)varDecl.getCompiledProcedure(); Component declaringComponent = compiledVariable.getDeclaringComponent(); if (declaringComponent == null) { declaringComponent = compiledVariable.makeDeclaringComponent(varDecl.getDeclaredVisibility(), getStylesheetPackage()); } HashMap componentIndex = stylesheetPackage.getComponentIndex(); if (qName != null) { // see if there is already a global variable with this precedence SymbolicName sName = varDecl.getSymbolicName(); Component other = componentIndex.get(sName); if (other == null) { // this is the first globalVariableIndex.put(qName, decl); componentIndex.put(new SymbolicName(StandardNames.XSL_VARIABLE, qName), varDecl.getCompiledProcedure().getDeclaringComponent()); } else { if (other.getDeclaringPackage() == getStylesheetPackage()) { // check the precedences int thisPrecedence = decl.getPrecedence(); ComponentDeclaration otherVarDecl = globalVariableIndex.get(sName.getComponentName()); int otherPrecedence = otherVarDecl.getPrecedence(); if (thisPrecedence == otherPrecedence) { StyleElement v2 = otherVarDecl.getSourceElement(); if (v2 == varDecl) { varDecl.compileError( "Global variable or parameter $" + qName.getDisplayName() + " is declared more than once " + "(caused by including the containing module more than once)", "XTSE0630"); } else { varDecl.compileError("Duplicate global variable/parameter declaration (see line " + v2.getLineNumber() + " of " + v2.getSystemId() + ')', "XTSE0630"); } } else if (thisPrecedence < otherPrecedence && varDecl != otherVarDecl.getSourceElement()) { varDecl.setRedundant(true); } else if (varDecl != otherVarDecl.getSourceElement()) { ((XSLGlobalVariable) otherVarDecl.getSourceElement()).setRedundant(true); globalVariableIndex.put(qName, decl); componentIndex.put(new SymbolicName(StandardNames.XSL_VARIABLE, qName), varDecl.getCompiledProcedure().getDeclaringComponent()); } } else if (varDecl.findAncestorElement(StandardNames.XSL_OVERRIDE) != null) { // the new one wins componentIndex.put(sName, declaringComponent); globalVariableIndex.put(sName.getComponentName(), decl); } else { String kind = varDecl instanceof XSLGlobalParam ? "parameter" : "variable"; varDecl.compileError("Global " + kind + " $" + sName.getComponentName().getDisplayName() + " conflicts with a public variable/parameter in package " + other.getDeclaringPackage().getPackageName(), "XTSE3050"); } } } } /** * Get the global variable or parameter with a given name (taking * precedence rules into account). This will only return global variables * declared in the same package where they are used. * * @param qName name of the global variable or parameter * @return the variable declaration, or null if it does not exist */ public SourceBinding getGlobalVariableBinding(StructuredQName qName) { ComponentDeclaration decl = globalVariableIndex.get(qName); return decl == null ? null : ((XSLGlobalVariable) decl.getSourceElement()).getSourceBinding(); } /** * Allocate a unique number to a local parameter name. This should only be called on the principal * stylesheet module. * * @param qName the local parameter name * @return an integer that uniquely identifies this parameter name within the stylesheet */ public int allocateUniqueParameterNumber(StructuredQName qName) { HashMap params = localParameterNumbers; if (params == null) { localParameterNumbers = new HashMap(50); params = localParameterNumbers; } Integer x = params.get(qName); if (x == null) { x = params.size(); params.put(qName, x); } return x; } /** * Add a named template to the index * * @param decl the declaration of the Template object * @throws XPathException if an error occurs */ protected void indexNamedTemplate(ComponentDeclaration decl) throws XPathException { HashMap componentIndex = stylesheetPackage.getComponentIndex(); XSLTemplate sourceTemplate = (XSLTemplate) decl.getSourceElement(); if (sourceTemplate.getTemplateName() == null) { return; // Not a named template } NamedTemplate compiledTemplate = sourceTemplate.getCompiledNamedTemplate(); Component declaringComponent = compiledTemplate.getDeclaringComponent(); if (declaringComponent == null) { declaringComponent = compiledTemplate.makeDeclaringComponent(sourceTemplate.getDeclaredVisibility(), getStylesheetPackage()); } SymbolicName sName = sourceTemplate.getSymbolicName(); if (sName != null) { // see if there is already a named template with this precedence Component other = componentIndex.get(sName); if (other == null) { // this is the first componentIndex.put(sName, declaringComponent); templateIndex.put(sName.getComponentName(), decl); } else { if (other.getDeclaringPackage() == getStylesheetPackage()) { // check the precedences int thisPrecedence = decl.getPrecedence(); ComponentDeclaration otherTemplate = templateIndex.get(sName.getComponentName()); int otherPrecedence = otherTemplate.getPrecedence(); if (thisPrecedence == otherPrecedence) { sourceTemplate.compileError("Duplicate named template (see line " + otherTemplate.getSourceElement().getLineNumber() + " of " + otherTemplate.getSourceElement().getSystemId() + ')', "XTSE0660"); } else if (thisPrecedence < otherPrecedence) { //template.setRedundantNamedTemplate(); } else { // can't happen, but we'll play safe //other.setRedundantNamedTemplate(); componentIndex.put(sName, declaringComponent); templateIndex.put(sName.getComponentName(), decl); } } else if (sourceTemplate.findAncestorElement(StandardNames.XSL_OVERRIDE) != null) { // the new one wins componentIndex.put(sName, declaringComponent); templateIndex.put(sName.getComponentName(), decl); } else { sourceTemplate.compileError("Named template " + sName.getComponentName().getDisplayName() + " conflicts with a public named template in package " + other.getDeclaringPackage().getPackageName(), "XTSE3050"); } } } } /** * Get the named template with a given name * * @param name the name of the required template * @return the template with the given name, if there is one, or null otherwise. If there * are several templates with the same name, the one with highest import precedence * is returned. Note that this template may subsequently be overridden in another package, * but only by another template with a compatible signature. */ public NamedTemplate getNamedTemplate(StructuredQName name) { HashMap componentIndex = stylesheetPackage.getComponentIndex(); Component component = componentIndex.get(new SymbolicName(StandardNames.XSL_TEMPLATE, name)); return component == null ? null : (NamedTemplate) component.getCode(); } /** * Add an attribute set to the index * * @param decl the declaration of the attribute set object * @throws XPathException if an error occurs */ protected void indexAttributeSet(ComponentDeclaration decl) throws XPathException { //HashMap componentIndex = stylesheetPackage.getComponentIndex(); XSLAttributeSet sourceAttributeSet = (XSLAttributeSet) decl.getSourceElement(); StructuredQName name = sourceAttributeSet.getAttributeSetName(); List entries = attributeSetDeclarations.get(name); if (entries == null) { entries = new ArrayList(); attributeSetDeclarations.put(name, entries); } else { String thisVis = Whitespace.trim(sourceAttributeSet.getAttributeValue("", "visibility")); String firstVis = Whitespace.trim(entries.get(0).getSourceElement().getAttributeValue("", "visibility")); if (!(thisVis == null ? firstVis == null : thisVis.equals(firstVis))) { throw new XPathException("Visibility attributes on attribute-sets sharing the same name must all be the same", "XTSE0010"); } } entries.add(0, decl); } /** * Return all the attribute set declarations in the package having a particular QName * * @param name the name of the required declarations * @return the set of declarations having this name, or null if there are none */ public List getAttributeSetDeclarations(StructuredQName name) { return attributeSetDeclarations.get(name); } /** * Ask whether a particular attribute set exists and is declared streamable * * @param name the name of the required attribute set * @return true if it exists and is declared streamable */ public boolean isAttributeSetDeclaredStreamable(StructuredQName name) { List entries = attributeSetDeclarations.get(name); if (entries == null || entries.isEmpty()) { return false; } // if one is declared streamable, they all must be XSLAttributeSet aSet = (XSLAttributeSet) entries.get(0).getSourceElement(); return aSet.isDeclaredStreamable(); } /** * Combine all like-named xsl:attribute-set declarations in this package into a single Component, whose body * is an AttributeSet object. The resulting attribute set Components are saved in the StylesheetPackage * object. * * @throws XPathException if a failure occurs */ public void combineAttributeSets(Compilation compilation) throws XPathException { Map index = new HashMap(); for (Map.Entry> entry : attributeSetDeclarations.entrySet()) { AttributeSet as = new AttributeSet(); as.setName(entry.getKey()); as.setPackageData(stylesheetPackage); StyleElement firstDecl = entry.getValue().get(0).getSourceElement(); as.setSystemId(firstDecl.getSystemId()); as.setLineNumber(firstDecl.getLineNumber()); index.put(entry.getKey(), as); Component declaringComponent = as.getDeclaringComponent(); if (declaringComponent == null) { declaringComponent = as.makeDeclaringComponent(Visibility.PRIVATE, stylesheetPackage); } stylesheetPackage.addComponent(declaringComponent); } for (Map.Entry> entry : attributeSetDeclarations.entrySet()) { List content = new ArrayList(); Visibility vis = null; boolean explicitVisibility = false; boolean streamable = false; for (ComponentDeclaration decl : entry.getValue()) { XSLAttributeSet src = (XSLAttributeSet) decl.getSourceElement(); streamable |= src.isDeclaredStreamable(); src.compileDeclaration(compilation, decl); content.addAll(src.getContainedInstructions()); vis = src.getVisibility(); explicitVisibility = explicitVisibility || src.getAttributeValue("", "visibility") != null; } AttributeSet aSet = index.get(entry.getKey()); aSet.setDeclaredStreamable(streamable); Expression block = Block.makeBlock(content); aSet.setBody(block); SlotManager frame = new SlotManager(); ExpressionTool.allocateSlots(block, 0, frame); aSet.setStackFrameMap(frame); aSet.getDeclaringComponent().setVisibility(vis, explicitVisibility); if (streamable) { checkStreamability(aSet); } } } /** * Check the streamability of an attribute set declared within this stylesheet module. * (Null implementation for Saxon-HE). * @param aSet the attribute set to be checked * @throws XPathException if the streamability rules are not satisifed */ protected void checkStreamability(AttributeSet aSet) throws XPathException { } /** * Get the list of attribute-set declarations associated with a given QName. * This is used for xsl:element, xsl:copy, xsl:attribute-set, and on literal * result elements * * @param name the name of the required attribute set * @param list a list to hold the list of XSLAttributeSet elements in the stylesheet tree. * @return true if any declarations were found and added to the list; false if none were found * @throws net.sf.saxon.trans.XPathException if an error occurs */ protected boolean getAttributeSets(StructuredQName name, List list) throws XPathException { boolean found = false; // search for the named attribute set, using all of them if there are several with the // same name for (ComponentDeclaration decl : topLevel) { if (decl.getSourceElement() instanceof XSLAttributeSet) { XSLAttributeSet t = (XSLAttributeSet) decl.getSourceElement(); if (t.getAttributeSetName().equals(name)) { list.add(decl); found = true; } } } return found; } /** * Handle an explicitly-declared mode * * @param decl the declaration of the Mode object * @throws XPathException if an error occurs */ public void indexMode(ComponentDeclaration decl) throws XPathException { // Work done in subclass } /** * Check that it is legitimate to add a given template rule to a given mode * * @param template the template rule * @param mode the mode * @return true if all is well * @throws XPathException if the mode is declared in a used package and the template * is not declared within the relevant xsl:mode declaration */ public boolean checkAcceptableModeForPackage(XSLTemplate template, Mode mode) throws XPathException { // no action except in subclass return true; } /** * Check for schema-awareness. * Typed input nodes are recognized if and only if the stylesheet contains an import-schema declaration. */ private void checkForSchemaAwareness() { Compilation compilation = getRootElement().getCompilation(); if (!compilation.isSchemaAware() && getConfiguration().isLicensedFeature(Configuration.LicenseFeature.ENTERPRISE_XSLT)) { for (ComponentDeclaration decl : topLevel) { StyleElement node = decl.getSourceElement(); if (node instanceof XSLImportSchema) { compilation.setSchemaAware(true); return; } } } } /** * Get the class that manages accumulator functions * * @return the class that manages accumulators. Always null in Saxon-HE */ public IAccumulatorRegistry getAccumulatorManager() { return accumulatorManager; } /** * Set the class that manages accumulator functions * * @param accumulatorManager the manager of accumulator functions */ public void setAccumulatorManager(IAccumulatorRegistry accumulatorManager) { this.accumulatorManager = accumulatorManager; stylesheetPackage.setAccumulatorRegistry(accumulatorManager); } protected void addNamespaceAlias(ComponentDeclaration node) { namespaceAliasList.add(node); numberOfAliases++; } /** * Get the declared namespace alias for a given namespace URI code if there is one. * If there is more than one, we get the last. * * @param uri The uri used in the stylesheet. * @return The namespace binding to be used (prefix and uri): return null * if no alias is defined */ protected NamespaceBinding getNamespaceAlias(String uri) { return namespaceAliasMap.get(uri); } /** * Determine if a namespace is included in the result-prefix of a namespace-alias * * @param uri the namespace URI * @return true if an xsl:namespace-alias has been defined for this namespace URI */ protected boolean isAliasResultNamespace(String uri) { return aliasResultUriSet.contains(uri); } /** * Collect any namespace aliases * * @throws net.sf.saxon.trans.XPathException if an error occurs */ private void collectNamespaceAliases() throws XPathException { namespaceAliasMap = new HashMap(numberOfAliases); aliasResultUriSet = new HashSet(numberOfAliases); HashSet aliasesAtThisPrecedence = new HashSet(); int currentPrecedence = -1; // Note that we are processing the list in reverse stylesheet order, // that is, highest precedence first. for (int i = 0; i < numberOfAliases; i++) { ComponentDeclaration decl = namespaceAliasList.get(i); XSLNamespaceAlias xna = (XSLNamespaceAlias) decl.getSourceElement(); String scode = xna.getStylesheetURI(); NamespaceBinding resultBinding = xna.getResultNamespaceBinding(); int prec = decl.getPrecedence(); // check that there isn't a conflict with another xsl:namespace-alias // at the same precedence if (currentPrecedence != prec) { currentPrecedence = prec; aliasesAtThisPrecedence.clear(); //precedenceBoundary = i; } if (aliasesAtThisPrecedence.contains(scode)) { if (!namespaceAliasMap.get(scode).getURI().equals(resultBinding.getURI())) { xna.compileError("More than one alias is defined for the same namespace", "XTSE0810"); } } if (namespaceAliasMap.get(scode) == null) { namespaceAliasMap.put(scode, resultBinding); aliasResultUriSet.add(resultBinding.getURI()); } aliasesAtThisPrecedence.add(scode); } namespaceAliasList = null; // throw it in the garbage } protected boolean hasNamespaceAliases() { return numberOfAliases > 0; } /** * Create an output properties object representing the xsl:output elements in the stylesheet. * * @param formatQName The name of the output format required. If set to null, gathers * information for the unnamed output format * @return the Properties object containing the details of the specified output format * @throws XPathException if a named output format does not exist in * the stylesheet */ public Properties gatherOutputProperties(/*@Nullable*/ StructuredQName formatQName) throws XPathException { boolean found = formatQName == null; Configuration config = getConfiguration(); Properties details = new Properties(config.getDefaultSerializationProperties()); HashMap precedences = new HashMap(10); for (int i = topLevel.size() - 1; i >= 0; i--) { ComponentDeclaration decl = topLevel.get(i); if (decl.getSourceElement() instanceof XSLOutput) { XSLOutput xo = (XSLOutput) decl.getSourceElement(); if (formatQName == null ? xo.getFormatQName() == null : formatQName.equals(xo.getFormatQName())) { found = true; xo.gatherOutputProperties(details, precedences, decl.getPrecedence()); } } } if (!found) { compileError("Requested output format " + formatQName.getDisplayName() + " has not been defined", "XTDE1460"); } return details; } /** * Compile the source XSLT stylesheet package * * @param compilation the compilation episode * @throws XPathException if compilation fails for any reason */ protected void compile(Compilation compilation) throws XPathException { try { net.sf.saxon.trans.Timer timer; if (Compilation.TIMING) { timer = new net.sf.saxon.trans.Timer(); } //PreparedStylesheet pss = getPreparedStylesheet(); Configuration config = getConfiguration(); // If any XQuery functions were imported, fix up all function calls // registered against these functions. XQueryFunctionLibrary queryFunctions = stylesheetPackage.getXQueryFunctionLibrary(); try { Iterator qf = queryFunctions.getFunctionDefinitions(); while (qf.hasNext()) { XQueryFunction f = (XQueryFunction) qf.next(); f.fixupReferences(); } } catch (XPathException e) { getRootElement().compileError(e); } if (Compilation.TIMING) { timer.report("fixup Query functions"); } // Register template rules with the rule manager for (ComponentDeclaration decl : topLevel) { StyleElement snode = decl.getSourceElement(); if (snode instanceof XSLTemplate) { ((XSLTemplate) snode).register(decl); } } if (Compilation.TIMING) { timer.report("register templates"); } // Compile groups of like-named attribute sets into a single attributeSet object //combineAttributeSets(compilation); // Call compile method for each top-level object in the stylesheet // Note, some declarations (templates) need to be compiled repeatedly if the module // is imported repeatedly; others (variables, functions) do not for (ComponentDeclaration decl : topLevel) { StyleElement snode = decl.getSourceElement(); if (!snode.isActionCompleted(StyleElement.ACTION_COMPILE)) { snode.setActionCompleted(StyleElement.ACTION_COMPILE); snode.compileDeclaration(compilation, decl); } } if (Compilation.TIMING) { timer.report("compile top-level objects"); } // Call type-check method for each user-defined function in the stylesheet. This is no longer // done during the optimize step, to avoid functions being inlined before they are type-checked. for (ComponentDeclaration decl : functionIndex.values()) { StyleElement node = decl.getSourceElement(); if (!node.isActionCompleted(StyleElement.ACTION_TYPECHECK)) { node.setActionCompleted(StyleElement.ACTION_TYPECHECK); if (node.getVisibility() != Visibility.ABSTRACT) { ((XSLFunction) node).getCompiledFunction().typeCheck(node.makeExpressionVisitor()); } } } if (Compilation.TIMING) { timer.report("typeCheck functions"); } if (compilation.getErrorCount() > 0) { // not much point carrying on return; } // Call optimize() method for each top-level declaration optimizeTopLevel(); // optimize functions that aren't overridden for (ComponentDeclaration decl : functionIndex.values()) { StyleElement node = decl.getSourceElement(); if (!node.isActionCompleted(StyleElement.ACTION_OPTIMIZE)) { node.setActionCompleted(StyleElement.ACTION_OPTIMIZE); ((StylesheetComponent) node).optimize(decl); } } if (Compilation.TIMING) { timer.report("optimize"); } if (config.isTiming() && config.isGenerateByteCode(Configuration.XSLT)) { config.getStandardErrorOutput().println("Generating byte code..."); } if (Compilation.TIMING) { timer.report("miscellanea"); } // Check consistency of decimal formats getDecimalFormatManager().checkConsistency(); // Check consistency of modes getRuleManager().checkConsistency(); if (Compilation.TIMING) { timer.report("build template rule tables"); } // Build a run-time function library. This supports the use of function-available() // with a dynamic argument, and extensions such as saxon:evaluate(). The run-time // function library differs from the compile-time function library in that both // the StylesheetFunctionLibrary's on the library list are replaced by equivalent // ExecutableFunctionLibrary's. This is to prevent the retaining of run-time links // to the stylesheet document tree. ExecutableFunctionLibrary overriding = new ExecutableFunctionLibrary(config); ExecutableFunctionLibrary underriding = new ExecutableFunctionLibrary(config); for (ComponentDeclaration decl : topLevel) { if (decl.getSourceElement() instanceof XSLFunction) { XSLFunction func = (XSLFunction) decl.getSourceElement(); if (func.isOverrideExtensionFunction()) { overriding.addFunction(func.getCompiledFunction()); } else { underriding.addFunction(func.getCompiledFunction()); } } } getStylesheetPackage().setFunctionLibraryDetails(null, overriding, underriding); if (Compilation.TIMING) { timer.report("build runtime function tables"); timer.reportCumulative("total compile phase"); } // Allocate binding slots to named templates for (ComponentDeclaration decl : topLevel) { StyleElement inst = decl.getSourceElement(); if (inst instanceof XSLTemplate) { NamedTemplate proc = ((XSLTemplate) inst).getCompiledProcedure(); if (proc != null && proc.getTemplateName() == null) { proc.allocateAllBindingSlots(stylesheetPackage); } } } // Allocate binding slots to template rules // getRuleManager().getUnnamedMode().allocateAllBindingSlots(stylesheetPackage); // for (Mode m : getRuleManager().getAllNamedModes()) { // if (m.getDeclaringComponent() == null) { // // This is temporary scaffolding, so long as modes are local to a package // m.makeDeclaringComponent(Visibility.PRIVATE, stylesheetPackage); // } // m.allocateAllBindingSlots(stylesheetPackage); // } // Allocate binding slots to component reference expressions HashMap componentIndex = stylesheetPackage.getComponentIndex(); for (Component decl : componentIndex.values()) { ComponentCode proc = decl.getCode(); if (proc != null) { proc.allocateAllBindingSlots(stylesheetPackage); } } // Allocate binding slots in /*optimizer-generated*/ key definitions KeyManager keyMan = getKeyManager(); for (KeyDefinitionSet keySet : keyMan.getAllKeyDefinitionSets()) { //if (keySet.getKeyName().hasURI(NamespaceConstant.SAXON)) { for (KeyDefinition keyDef : keySet.getKeyDefinitions()) { keyDef.makeDeclaringComponent(Visibility.PRIVATE, getStylesheetPackage()); keyDef.allocateAllBindingSlots(stylesheetPackage); } //} } // Allocate binding slots in accumulators IAccumulatorRegistry accMan = getAccumulatorManager(); if (accMan != null) { for (ComponentCode acc : accMan.getAllAccumulators()) { acc.allocateAllBindingSlots(stylesheetPackage); } } // Generate byte code where appropriate Optimizer opt = config.obtainOptimizer(); for (ComponentDeclaration decl : topLevel) { StyleElement inst = decl.getSourceElement(); if (inst instanceof StylesheetComponent) { ((StylesheetComponent) inst).generateByteCode(opt); } } } catch (RuntimeException err) { // if syntax errors were reported earlier, then exceptions may occur during this phase // due to inconsistency of data structures. We can ignore these exceptions as they // will go away when the user corrects the stylesheet if (compilation.getErrorCount() == 0) { // rethrow the exception throw err; } } } public void optimizeTopLevel() throws XPathException { // Call optimize method for each top-level object in the stylesheet // But for functions, do it only for those of highest precedence. for (ComponentDeclaration decl : topLevel) { StyleElement node = decl.getSourceElement(); if (node instanceof StylesheetComponent && !(node instanceof XSLFunction) && !node.isActionCompleted(StyleElement.ACTION_OPTIMIZE)) { node.setActionCompleted(StyleElement.ACTION_OPTIMIZE); ((StylesheetComponent) node).optimize(decl); } if (node instanceof XSLTemplate) { ((XSLTemplate) node).allocatePatternSlotNumbers(); } } } /** * Get an imported schema with a given namespace * * @param targetNamespace The target namespace of the required schema. * Supply an empty string for the default namespace * @return the required Schema, or null if no such schema has been imported */ protected boolean isImportedSchema(String targetNamespace) { return stylesheetPackage.getSchemaNamespaces().contains(targetNamespace); } protected void addImportedSchema(String targetNamespace) { stylesheetPackage.getSchemaNamespaces().add(targetNamespace); } protected Set getImportedSchemaTable() { return stylesheetPackage.getSchemaNamespaces(); } /** * Get a character map, identified by the fingerprint of its name. * Search backwards through the stylesheet. * * @param name The character map name being sought * @return the identified character map, or null if not found */ public ComponentDeclaration getCharacterMap(StructuredQName name) { for (int i = topLevel.size() - 1; i >= 0; i--) { ComponentDeclaration decl = topLevel.get(i); if (decl.getSourceElement() instanceof XSLCharacterMap) { XSLCharacterMap t = (XSLCharacterMap) decl.getSourceElement(); if (t.getCharacterMapName().equals(name)) { return decl; } } } return null; } /** * Adjust visibility of components by applying xsl:expose rules */ public void adjustExposedVisibility() throws XPathException { // no action in HE } /** * Compile time error, specifying an error code * * @param message the error message * @param errorCode the error code. May be null if not known or not defined * @throws XPathException unconditionally */ protected void compileError(String message, String errorCode) throws XPathException { XPathException tce = new XPathException(message, errorCode); compileError(tce); } /** * Report an error with diagnostic information * * @param error contains information about the error * @throws XPathException unconditionally, after reporting the error to the ErrorListener */ protected void compileError(XPathException error) throws XPathException { error.setIsStaticError(true); getRootElement().compileError(error); } public boolean isRootPackage() { return true; } protected void fixup() throws XPathException { // Perform the fixup actions for (Action a : fixupActions) { a.doAction(); } } protected void complete() throws XPathException { // Perform the completion actions for (Action a : completionActions) { a.doAction(); } //adjustExposedVisibility(); stylesheetPackage.complete(); } public SlotManager getSlotManager() { return null; } @Override public Collection getGlobalVariableNames() { return globalVariableIndex.keySet(); } @Override public GlobalVariable getGlobalVariable(StructuredQName name) { ComponentDeclaration decl = globalVariableIndex.get(name); if (decl != null) { return ((XSLGlobalVariable) decl.getSourceElement()).getCompiledVariable(); } else { return null; } } @Override public void addGlobalVariable(GlobalVariable variable) { Component component = variable.makeDeclaringComponent(Visibility.PRIVATE, getStylesheetPackage()); if (variable.getPackageData() == null) { variable.setPackageData(stylesheetPackage); } stylesheetPackage.getComponentIndex().put( new SymbolicName(StandardNames.XSL_VARIABLE, variable.getVariableQName()), component); } //project:preconditions /** * Find possibly keyable patterns, accumulating them in the PatternOptimization * and then emitting the necessary key definitions * * @param compilation * @throws XPathException */ public void findKeyablePatterns(Compilation compilation) throws XPathException { // No action in Saxon-HE; overridden for Saxon-EE } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy