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) 2018-2023 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.accum.Accumulator;
import net.sf.saxon.expr.accum.AccumulatorRegistry;
import net.sf.saxon.expr.instruct.*;
import net.sf.saxon.expr.parser.ExpressionTool;
import net.sf.saxon.expr.parser.OptimizerOptions;
import net.sf.saxon.functions.DocumentFn;
import net.sf.saxon.functions.ExecutableFunctionLibrary;
import net.sf.saxon.lib.SaxonOutputKeys;
import net.sf.saxon.om.*;
import net.sf.saxon.pattern.NameTest;
import net.sf.saxon.pattern.NodeKindTest;
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.Timer;
import net.sf.saxon.trans.*;
import net.sf.saxon.trans.rules.RuleManager;
import net.sf.saxon.tree.iter.AxisIterator;
import net.sf.saxon.tree.linked.DocumentImpl;
import net.sf.saxon.type.Type;
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 { private final StylesheetPackage stylesheetPackage; private final 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 final HashMap globalVariableIndex = new HashMap<>(20); // index of templates - only includes those actually declared within this package private final HashMap templateIndex = new HashMap<>(20); // Table of named stylesheet functions. private final HashMap functionIndex = new HashMap<>(8); // key manager for all the xsl:key definitions in this package private final KeyManager keyManager; // decimal format manager for all the xsl:decimal-format definitions in this package private final DecimalFormatManager decimalFormatManager; // rule manager for template rules private final RuleManager ruleManager; // manager class for accumulator rules (XSLT 3.0 only) private AccumulatorRegistry 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 final Map> attributeSetDeclarations = new HashMap<>(); // cache of stylesheet documents. Note that multiple imports of the same URI // lead to the stylesheet tree being reused private final HashMap moduleCache = new HashMap<>(4); private final TypeAliasManager typeAliasManager; private final CharacterMapIndex characterMapIndex; private final List fixupActions = new ArrayList<>(); private boolean needsDynamicOutputProperties = false; /** * Create a PrincipalStylesheetModule * * @param sourceElement the xsl:package element at the root of the package manifest * @throws XPathException if things go wrong */ public PrincipalStylesheetModule(XSLPackage sourceElement) throws XPathException { super(sourceElement, 0); declaredModes = sourceElement.isDeclaredModes(); stylesheetPackage = getConfiguration().makeStylesheetPackage(); CompilerInfo compilerInfo = sourceElement.getCompilation().getCompilerInfo(); stylesheetPackage.setTargetEdition(compilerInfo.getTargetEdition()); stylesheetPackage.setRelocatable(compilerInfo.isRelocatable()); stylesheetPackage.setJustInTimeCompilation(compilerInfo.isJustInTimeCompilation()); stylesheetPackage.setImplicitPackage(!sourceElement.getLocalPart().equals("package")); keyManager = stylesheetPackage.getKeyManager(); decimalFormatManager = stylesheetPackage.getDecimalFormatManager(); ruleManager = new RuleManager(stylesheetPackage, compilerInfo); ruleManager.getUnnamedMode().makeDeclaringComponent(Visibility.PRIVATE, stylesheetPackage); stylesheetPackage.setRuleManager(ruleManager); stylesheetPackage.setDeclaredModes(declaredModes); StructuredQName defaultMode = sourceElement.getDefaultMode(); stylesheetPackage.setDefaultMode(sourceElement.getDefaultMode()); if (defaultMode != null) { ruleManager.obtainMode(defaultMode, !declaredModes); } characterMapIndex = new CharacterMapIndex(); stylesheetPackage.setCharacterMapIndex(characterMapIndex); typeAliasManager = getConfiguration().makeTypeAliasManager(); stylesheetPackage.setTypeAliasManager(typeAliasManager); 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); } /** * 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); } /** * 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; } /** * Get the type alias manager (type aliases are a Saxon extension) * * @return the type alias manager */ public TypeAliasManager getTypeAliasManager() { return typeAliasManager; } /** * 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 { XQueryFunctionLibrary lib = getStylesheetPackage().getXQueryFunctionLibrary(); if (getStylesheetPackage().getFunction(function.getUserFunction().getSymbolicName()) != null) { throw new XPathException("Duplicate declaration of " + function.getUserFunction().getSymbolicName(), "XQST0034"); } lib.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(DocumentKey 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(DocumentKey key) { XSLModuleRoot sheet = moduleCache.get(key); if (sheet != null) { sheet.issueWarning("Stylesheet module " + key + " is included or imported more than once. " + "This is permitted, but may lead to errors or unexpected behavior", SaxonErrorCode.SXWN9019); } 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 * * @param compilation the XSLT compilation in progress * @throws net.sf.saxon.trans.XPathException if errors are found in the stylesheet */ public void preprocess(Compilation compilation) throws XPathException { Timer timer = compilation.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"); } // process type aliases getTypeAliasManager().processAllDeclarations(topLevel); // 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 topLevelDecl : topLevel) { StyleElement inst = topLevelDecl.getSourceElement(); if (!inst.isActionCompleted(StyleElement.ACTION_FIXUP)) { inst.setActionCompleted(StyleElement.ACTION_FIXUP); inst.fixupReferences(); } } if (Compilation.TIMING) { timer.report("fixupReferences"); } // Validate the whole package (i.e. with included and imported stylesheet modules) XSLPackage top = (XSLPackage) getStylesheetElement(); if (!top.isActionCompleted(StyleElement.ACTION_VALIDATE)) { top.setActionCompleted(StyleElement.ACTION_VALIDATE); top.validate(null); for (ComponentDeclaration d : topLevel) { d.getSourceElement().validateSubtree(d, false); } } if (Compilation.TIMING) { timer.report("validate"); } // Gather the output properties Properties props = gatherOutputProperties(null); props.setProperty(SaxonOutputKeys.STYLESHEET_VERSION, top.getEffectiveVersion() + ""); 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) { // needed for saxon:serialize getStylesheetPackage().setNamedOutputProperties(qName, oprops); //} } } if (Compilation.TIMING) { timer.report("Register output formats"); } // 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)); } } if (Compilation.TIMING) { timer.report("Index character maps"); } } /** * 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(); // Warning message deleted by bug 3278 // if (info.isVersionWarning() && // xslpackage.getEffectiveVersion() != 30) { // XPathException w = new XPathException( // "Running an XSLT " + xslpackage.getEffectiveVersionAsString() + " stylesheet with an XSLT 3.0 processor"); // w.setLocator(xslpackage); // compilation.reportWarning(w); // } List useDeclarations = new ArrayList<>(); gatherUsePackageDeclarations(compilation, xslpackage, useDeclarations); // First pass: gather all the named overriding declarations and add them to the topLevel declaration list Set overrides = new HashSet<>(); for (XSLUsePackage use : useDeclarations) { gatherOverridingDeclarations(use, compilation, overrides); } //gatherOverridingDeclarations(compilation, xslpackage, overrides); // Second pass: make modified copies of the named components in the used packages StylesheetPackage thisPackage = getStylesheetPackage(); for (XSLUsePackage use : useDeclarations) { List acceptors = use.getAcceptors(); thisPackage.addComponentsFromUsedPackage(use.getUsedPackage(), acceptors, overrides); } // Third pass: process the overriding template rules, creating new mode objects for (XSLUsePackage use : useDeclarations) { use.gatherRuleOverrides(this, overrides); } // Now process the declarations contained within this package, both in the top-level module // and within its included and imported modules spliceIncludes(); } private static void gatherUsePackageDeclarations(Compilation compilation, StyleElement wrapper, List declarations) throws XPathException { for (NodeInfo use : wrapper.children()) { if (use instanceof XSLUsePackage) { declarations.add((XSLUsePackage) use); } else if (use instanceof XSLInclude) { String href = Whitespace.trim(use.getAttributeValue(NamespaceUri.NULL, "href")); DocumentKey key = DocumentFn.computeDocumentKey(href, use.getBaseURI(), compilation.getPackageData(), false); TreeInfo includedTree = compilation.getStylesheetModules().get(key); if (includedTree == null) { throw new XPathException("Internal problem: the included stylesheet module '" + href + "' should be in the compiler's module store, but was not found"); } StyleElement incWrapper = (StyleElement) ((DocumentImpl) includedTree.getRootNode()).getDocumentElement(); gatherUsePackageDeclarations(compilation, incWrapper, declarations); } } } private void gatherOverridingDeclarations(XSLUsePackage use, Compilation compilation, Set overrides) throws XPathException { use.findUsedPackage(compilation.getCompilerInfo()); use.gatherNamedOverrides(this, topLevel, overrides); } /** * 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() { 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 */ protected void indexFunction(ComponentDeclaration decl) { HashMap componentIndex = stylesheetPackage.getComponentIndex(); XSLFunction sourceFunction = (XSLFunction) decl.getSourceElement(); UserFunction compiledFunction = sourceFunction.getCompiledFunction(); Component declaringComponent = compiledFunction.obtainDeclaringComponent(sourceFunction); int maxArity = sourceFunction.getNumberOfParameters(); int minArity = maxArity - sourceFunction.getNumberOfOptionalParameters(); for (int arity = minArity; arity <= maxArity; arity++) { SymbolicName.F sName = new SymbolicName.F(sourceFunction.getObjectName(), arity); // see if there is already a named function with this precedence ComponentDeclaration otherDecl = functionIndex.get(sName); Component otherComp = componentIndex.get(sName); if (otherDecl == null && otherComp == null) { // this is the first one in this stylesheet; and there is none in a used package if (arity == maxArity) { componentIndex.put(sName, declaringComponent); } functionIndex.put(sName, decl); } else if (otherDecl != null) { // there is another function in this stylesheet int thisPrecedence = decl.getPrecedence(); int otherPrecedence = otherDecl.getPrecedence(); if (thisPrecedence == otherPrecedence) { UserFunction otherFunction = ((XSLFunction) otherDecl.getSourceElement()).getCompiledFunction(); if (minArity == maxArity && otherFunction.getMinimumArity() == otherFunction.getArity()) { sourceFunction.compileError("Function " + sName.getShortName() + " is declared twice - see " + Err.show(otherFunction.getLocation()), "XTSE0770"); } else { sourceFunction.compileError("Function " + sName.getComponentName().getDisplayName() + " has overlapping arity range " + showArityRanges(compiledFunction, otherFunction) + " with another function of the same name - see " + Err.show(otherFunction.getLocation()), "XTSE0770"); } } break; } else { Component other = componentIndex.get(new SymbolicName.F(sourceFunction.getObjectName(), maxArity)); if (other != null && other.getDeclaringPackage() == getStylesheetPackage()) { // check the precedences int thisPrecedence = decl.getPrecedence(); ComponentDeclaration otherFunction = functionIndex.get(sName); int otherPrecedence = otherFunction.getPrecedence(); if (thisPrecedence == otherPrecedence) { String message = "Duplicate named function (see line " + otherFunction.getSourceElement().getLineNumber() + " of " + otherFunction.getSourceElement().getSystemId() + ')'; if (maxArity != ((UserFunction) other.getActor()).getNumberOfParameters()) { message += ". The arity ranges of the two functions overlap"; } sourceFunction.compileError(message, "XTSE0770"); break; } 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.getShortName() + " conflicts with a public function in package " + other.getDeclaringPackage().getPackageName(), "XTSE3050"); } } } } private static String showArityRanges(UserFunction fn1, UserFunction fn2) { return "(" + showArityRange(fn1) + "; " + showArityRange(fn2) + ")"; } private static String showArityRange(UserFunction fn) { return fn.getMinimumArity() + "-" + fn.getArity(); } /** * 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.getActor(); Component declaringComponent = compiledVariable.obtainDeclaringComponent(varDecl); 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.getActor().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 $" + qName.getDisplayName() + " (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.getActor().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(); } /** * Add a named template to the index * * @param decl the declaration of the Template object */ protected void indexNamedTemplate(ComponentDeclaration decl) { HashMap componentIndex = stylesheetPackage.getComponentIndex(); XSLTemplate sourceTemplate = (XSLTemplate) decl.getSourceElement(); 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 //NamedTemplate compiledTemplate = new NamedTemplate(); NamedTemplate compiledTemplate = ((XSLTemplate) decl.getSourceElement()).getCompiledNamedTemplate(); Component declaringComponent = compiledTemplate.obtainDeclaringComponent(sourceTemplate); componentIndex.put(sName, declaringComponent); setLocalParamDetails(sourceTemplate, compiledTemplate); 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) { String errorCode = sourceTemplate.getParent() instanceof XSLOverride ? "XTSE3055" : "XTSE0660"; sourceTemplate.compileError("Duplicate named template (see line " + otherTemplate.getSourceElement().getLineNumber() + " of " + otherTemplate.getSourceElement().getSystemId() + ')', errorCode); } else //noinspection StatementWithEmptyBody if (thisPrecedence < otherPrecedence) { //return; } else { // can't happen, but we'll play safe //other.setRedundantNamedTemplate(); NamedTemplate compiledTemplate = new NamedTemplate(sName.getComponentName(), getConfiguration()); Component declaringComponent = compiledTemplate.obtainDeclaringComponent(sourceTemplate); componentIndex.put(sName, declaringComponent); templateIndex.put(sName.getComponentName(), decl); setLocalParamDetails(sourceTemplate, compiledTemplate); } } else if (sourceTemplate.findAncestorElement(StandardNames.XSL_OVERRIDE) != null) { // the new one wins NamedTemplate compiledTemplate = sourceTemplate.getCompiledNamedTemplate();//new NamedTemplate(); Component declaringComponent = compiledTemplate.obtainDeclaringComponent(sourceTemplate); 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"); } } } } private static void setLocalParamDetails(XSLTemplate source, NamedTemplate nt) { AxisIterator kids = source.iterateAxis(AxisInfo.CHILD, NodeKindTest.ELEMENT); List details = new ArrayList<>(); SequenceTool.supply(kids, (ItemConsumer) child -> { if (child instanceof XSLLocalParam) { XSLLocalParam lp = (XSLLocalParam) child; lp.prepareTemplateSignatureAttributes(); NamedTemplate.LocalParamInfo info = new NamedTemplate.LocalParamInfo(); info.name = lp.getVariableQName(); info.requiredType = lp.getRequiredType(); info.isRequired = lp.isRequiredParam(); info.isTunnel = lp.isTunnelParam(); details.add(info); } }); nt.setLocalParamDetails(details); } /** * 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.getActor(); } /** * 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(NamespaceUri.NULL, "visibility")); String firstVis = Whitespace.trim(entries.get(0).getSourceElement().getAttributeValue(NamespaceUri.NULL, "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); } /** * 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. * * @param compilation the XSLT compilation in progress * @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()); as.setColumnNumber(firstDecl.getColumnNumber()); 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 = Visibility.UNDEFINED; boolean explicitVisibility = false; boolean streamable = false; // Bug 3195. If the same xsl:attribute-set element is present more than // once in the list, we need to remove all but the last occurrence, otherwise // the same expression will be present more than once in the tree. This can // happen when a stylesheet module is included/imported more than once. List entries = new ArrayList<>(); Set elements = new HashSet<>(); for (int i = entry.getValue().size() - 1; i >= 0; i--) { ComponentDeclaration attSetDecl = entry.getValue().get(i); XSLAttributeSet src = (XSLAttributeSet) attSetDecl.getSourceElement(); if (!elements.contains(src)) { entries.add(0, attSetDecl); elements.add(src); } } for (ComponentDeclaration decl : entries) { XSLAttributeSet src = (XSLAttributeSet) decl.getSourceElement(); streamable |= src.isDeclaredStreamable(); src.compileDeclaration(compilation, decl); content.addAll(src.getContainedInstructions()); vis = src.getVisibility(); explicitVisibility = explicitVisibility || src.getAttributeValue(NamespaceUri.NULL, "visibility") != null; } AttributeSet aSet = index.get(entry.getKey()); aSet.setDeclaredStreamable(streamable); Expression block = Block.makeBlock(content); aSet.setBody(block); SlotManager frame = getConfiguration().makeSlotManager(); ExpressionTool.allocateSlots(block, 0, frame); aSet.setStackFrameMap(frame); VisibilityProvenance provenance = explicitVisibility ? VisibilityProvenance.EXPLICIT : VisibilityProvenance.DEFAULTED; aSet.getDeclaringComponent().setVisibility(vis, provenance); 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. */ protected void getAttributeSets(StructuredQName name, List list) { // 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); } } } } /** * Handle an explicitly-declared mode * * @param decl the declaration of the Mode object */ public void indexMode(ComponentDeclaration decl) { XSLMode sourceMode = (XSLMode) decl.getSourceElement(); StructuredQName modeName = sourceMode.getObjectName(); if (modeName == null) { return; // Not a named mode } SymbolicName sName = new SymbolicName(StandardNames.XSL_MODE, modeName); // see if there is already a named mode with this precedence Mode other = getStylesheetPackage().getRuleManager().obtainMode(modeName, false); if (other != null && other.getDeclaringComponent().getDeclaringPackage() != getStylesheetPackage()) { sourceMode.compileError("Mode " + sName.getComponentName().getDisplayName() + " conflicts with a public mode declared in package " + other.getDeclaringComponent().getDeclaringPackage().getPackageName(), "XTSE3050"); } } /** * 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, false if the mode cannot be extended (in which case an error will * have been reported) */ public boolean checkAcceptableModeForPackage(XSLTemplate template, Mode mode) { StylesheetPackage templatePack = template.getPackageData(); if (mode.getDeclaringComponent() == null) { return true; } StylesheetPackage modePack = mode.getDeclaringComponent().getDeclaringPackage(); if (templatePack != modePack) { NodeInfo parent = template.getParent(); boolean bad = false; if (!(parent instanceof XSLOverride)) { bad = true; } else { NodeInfo grandParent = parent.getParent(); if (!(grandParent instanceof XSLUsePackage)) { bad = true; } else { SymbolicName modeName = mode.getSymbolicName(); Component.M usedMode = (Component.M) ((XSLUsePackage) grandParent).getUsedPackage().getComponent(modeName); if (usedMode == null) { bad = true; } else if (usedMode.getVisibility() == Visibility.FINAL) { bad = true; } } } if (bad) { template.compileError("A template rule cannot be added to a mode declared in a used package " + "unless the xsl:template declaration appears within an xsl:override child of the appropriate xsl:use-package element", "XTSE3050"); return false; } } 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 AccumulatorRegistry getAccumulatorManager() { return accumulatorManager; } /** * Set the class that manages accumulator functions * * @param accumulatorManager the manager of accumulator functions */ public void setAccumulatorManager(AccumulatorRegistry 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(NamespaceUri 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(NamespaceUri uri) { return aliasResultUriSet.contains(uri); } /** * Collect any namespace aliases */ private void collectNamespaceAliases() { 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(); NamespaceUri 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).getNamespaceUri().equals(resultBinding.getNamespaceUri())) { 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.getNamespaceUri()); } 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 { Timer timer = compilation.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(); for (XQueryFunction f : queryFunctions.getFunctionDefinitions()) { f.fixupReferences(); } if (Compilation.TIMING) { timer.report("fixup Query functions"); } // Register all modes with the rule manager boolean allowImplicit = !getStylesheetPackage().isDeclaredModes(); for (ComponentDeclaration decl : topLevel) { StyleElement snode = decl.getSourceElement(); if (snode instanceof XSLMode) { getRuleManager().obtainMode(snode.getObjectName(), true); } if (allowImplicit) { registerImplicitModes(snode, getRuleManager()); } } getRuleManager().checkConsistency(); // 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"); } // Adjust the visibility of components based on xsl:expose declarations adjustExposedVisibility(); if (Compilation.TIMING) { timer.report("adjust exposed visibility"); } // 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 (" + topLevel.size() + ")"); } // 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 (" + functionIndex.size() + ")"); } if (compilation.getErrorCount() > 0) { // not much point carrying on return; } // Call optimize() method for each top-level declaration optimizeTopLevel(); if (Compilation.TIMING) { timer.report("optimize top level"); } // 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 functions"); } // Check consistency of decimal formats getDecimalFormatManager().checkConsistency(); if (Compilation.TIMING) { timer.report("check decimal formats"); } // Check consistency of modes RuleManager ruleManager = getRuleManager(); //ruleManager.checkConsistency(); Now done earlier ruleManager.computeRankings(); if (!compilation.isFallbackToNonStreaming()) { ruleManager.invertStreamableTemplates(); } if (config.obtainOptimizer().isOptionSet(OptimizerOptions.RULE_SET)) { ruleManager.optimizeRules(); } 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 (Component decl : stylesheetPackage.getComponentIndex().values()) { if (decl.getActor() instanceof UserFunction) { UserFunction f = (UserFunction) decl.getActor(); if (f.isOverrideExtensionFunction()) { overriding.addFunction(f); } else { underriding.addFunction(f); } } } getStylesheetPackage().setFunctionLibraryDetails(null, overriding, underriding); if (Compilation.TIMING) { timer.report("build runtime function tables"); } // Allocate binding slots to named templates for (ComponentDeclaration decl : topLevel) { StyleElement inst = decl.getSourceElement(); if (inst instanceof XSLTemplate) { NamedTemplate proc = ((XSLTemplate) inst).getActor(); if (proc != null && proc.getTemplateName() == null) { proc.allocateAllBindingSlots(stylesheetPackage); } } } if (Compilation.TIMING) { timer.report("allocate binding slots to named templates"); } // Allocate binding slots to component reference expressions HashMap componentIndex = stylesheetPackage.getComponentIndex(); for (Component decl : componentIndex.values()) { Actor proc = decl.getActor(); if (proc != null) { proc.allocateAllBindingSlots(stylesheetPackage); } } if (Compilation.TIMING) { timer.report("allocate binding slots to component references"); } // Allocate binding slots in key definitions KeyManager keyMan = getKeyManager(); for (KeyDefinitionSet keySet : keyMan.getAllKeyDefinitionSets()) { for (KeyDefinition keyDef : keySet.getKeyDefinitions()) { keyDef.makeDeclaringComponent(Visibility.PRIVATE, getStylesheetPackage()); keyDef.allocateAllBindingSlots(stylesheetPackage); } } if (Compilation.TIMING) { timer.report("allocate binding slots to key definitions"); } // Allocate binding slots in accumulators AccumulatorRegistry accMan = getAccumulatorManager(); if (accMan != null) { for (Accumulator acc : accMan.getAllAccumulators()) { acc.allocateAllBindingSlots(stylesheetPackage); } } if (Compilation.TIMING) { timer.report("allocate binding slots to accumulators"); } } 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; } } } private void registerImplicitModes(StyleElement element, RuleManager manager) { if (element instanceof XSLApplyTemplates || element instanceof XSLTemplate) { String modeAtt = element.getAttributeValue("mode"); if (modeAtt != null) { String[] tokens = Whitespace.trim(modeAtt).split("[ \t\n\r]+"); for (String s : tokens) { if (!s.startsWith("#")) { StructuredQName modeName = element.makeQName(s, null, "mode"); SymbolicName sName = new SymbolicName(StandardNames.XSL_MODE, modeName); HashMap componentIndex = getStylesheetPackage().getComponentIndex(); Component existing = componentIndex.get(sName); if (existing != null && existing.getDeclaringPackage() != getStylesheetPackage()) { if (element instanceof XSLTemplate && !(element.getParent() instanceof XSLOverride)) { element.compileError("A template rule cannot be added to a mode declared in a used package " + "unless the xsl:template declaration appears within an xsl:override child of the appropriate xsl:use-package element", "XTSE3050"); } } else { manager.obtainMode(modeName, true); } } } } } NodeInfo child; AxisIterator kids = element.iterateAxis(AxisInfo.CHILD); while ((child = kids.next()) != null) { if (child instanceof StyleElement) { registerImplicitModes((StyleElement) child, manager); } } } 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(NamespaceUri targetNamespace) { return stylesheetPackage.getSchemaNamespaces().contains(targetNamespace); } protected void addImportedSchema(NamespaceUri 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 * * @throws XPathException if this detects an error or inconsistency */ public void adjustExposedVisibility() throws XPathException { List exposeDeclarations = new ArrayList<>(); // xsl:expose declarations in reverse order for (ComponentDeclaration decl : topLevel) { if (decl.getSourceElement() instanceof XSLExpose) { exposeDeclarations.add(0, (XSLExpose) decl.getSourceElement()); } } if (exposeDeclarations.isEmpty()) { return; } NamePool pool = getConfiguration().getNamePool(); Map componentIndex = stylesheetPackage.getComponentIndex(); for (Component component : componentIndex.values()) { int fp = component.getComponentKind(); if (fp == StandardNames.XSL_MODE && ((Mode) component.getActor()).isUnnamedMode()) { continue; } ComponentTest exactNameTest = new ComponentTest(fp, new NameTest(Type.ELEMENT, new FingerprintedQName(component.getActor().getComponentName(), pool), pool), -1); ComponentTest exactFunctionTest = null; if (fp == StandardNames.XSL_FUNCTION) { FunctionItem fn = (FunctionItem) component.getActor(); exactFunctionTest = new ComponentTest(fp, new NameTest(Type.ELEMENT, new FingerprintedQName(fn.getFunctionName(), pool), pool), fn.getArity()); } boolean matched = false; for (XSLExpose exposure : exposeDeclarations) { Set explicitComponentTests = exposure.getExplicitComponentTests(); if (explicitComponentTests.contains(exactNameTest) || (exactFunctionTest != null && explicitComponentTests.contains(exactFunctionTest))) { component.setVisibility(exposure.getVisibility(), VisibilityProvenance.EXPOSED); matched = true; break; } } if (!matched && component.getVisibilityProvenance() == VisibilityProvenance.DEFAULTED) { matched = lookForMatchingWildcard(exposeDeclarations, component, matched); if (!matched) { lookForAnyWildcard(exposeDeclarations, component); } } } } private void lookForAnyWildcard(List exposeDeclarations, Component component) throws XPathException { for (XSLExpose exposure : exposeDeclarations) { for (ComponentTest test : exposure.getWildcardComponentTests()) { if (test.matches(component.getActor())) { if (exposure.getVisibility() == Visibility.ABSTRACT && component.getVisibility() != Visibility.ABSTRACT) { XPathException err = new XPathException( "The non-abstract component " + component.getActor().getSymbolicName() + " cannot be made abstract by means of xsl:expose", "XTSE3025"); err.setLocation(exposure); throw err; } component.setVisibility(exposure.getVisibility(), VisibilityProvenance.EXPOSED); return; } } } } private boolean lookForMatchingWildcard(List exposeDeclarations, Component component, boolean matched) throws XPathException { // Look for a matching wildcard for (XSLExpose exposure : exposeDeclarations) { for (ComponentTest test : exposure.getWildcardComponentTests()) { if (test.isPartialWildcard() && test.matches(component.getActor())) { if (exposure.getVisibility() == Visibility.ABSTRACT && component.getVisibility() != Visibility.ABSTRACT) { XPathException err = new XPathException( "The non-abstract component " + component.getActor().getSymbolicName() + " cannot be made abstract by means of xsl:expose", "XTSE3025"); err.setLocation(exposure); throw err; } component.setVisibility(exposure.getVisibility(), VisibilityProvenance.EXPOSED); return true; } } } return matched; } /** * 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 */ protected void compileError(XPathException error) { error.setIsStaticError(true); getRootElement().compileError(error); } protected void fixup() throws XPathException { // Perform the fixup actions for (Action a : fixupActions) { a.doAction(); } } protected void complete() throws XPathException { stylesheetPackage.complete(); } public SlotManager getSlotManager() { return null; } @Override public GlobalVariable getEquivalentVariable(Expression select) { return null; // implemented in Saxon-EE } @Override public void addGlobalVariable(GlobalVariable variable) { addGlobalVariable(variable, Visibility.PRIVATE); } public void addGlobalVariable(GlobalVariable variable, Visibility visibility) { Component component = variable.makeDeclaringComponent(visibility, getStylesheetPackage()); if (variable.getPackageData() == null) { variable.setPackageData(stylesheetPackage); } if (visibility == Visibility.HIDDEN) { stylesheetPackage.addHiddenComponent(component); } else { stylesheetPackage.getComponentIndex().put( new SymbolicName(StandardNames.XSL_VARIABLE, variable.getVariableQName()), component); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy