package client.net.sf.saxon.ce.style; import client.net.sf.saxon.ce.trans.XPathException; import client.net.sf.saxon.ce.tree.iter.AxisIterator; import client.net.sf.saxon.ce.om.Axis; import client.net.sf.saxon.ce.om.NodeInfo; import client.net.sf.saxon.ce.value.Whitespace; import client.net.sf.saxon.ce.type.Type; import java.util.ArrayList; import java.util.List; /** * A stylesheet module represents a module of a stylesheet. It is possible for two modules * to share the same stylesheet tree in the case where two includes or imports reference * the same URI; in this case the two modules will typically have a different import precedence. */ public class StylesheetModule { private XSLStylesheet sourceElement; private int precedence; private int minImportPrecedence; private StylesheetModule importer; boolean wasIncluded; // the value of the inputTypeAnnotations attribute on this module, combined with the values // on all imported/included modules. This is a combination of the bit-significant values // ANNOTATION_STRIP and ANNOTATION_PRESERVE. private int inputTypeAnnotations = 0; // A list of all the declarations in the stylesheet and its descendants, in increasing precedence order protected List<Declaration> topLevel = new ArrayList<Declaration>(); public StylesheetModule(XSLStylesheet sourceElement, int precedence) { this.sourceElement = sourceElement; this.precedence = precedence; } public void setImporter(StylesheetModule importer) { this.importer = importer; } public StylesheetModule getImporter() { return importer; } public PrincipalStylesheetModule getPrincipalStylesheetModule() { return importer.getPrincipalStylesheetModule(); } public XSLStylesheet getSourceElement() { return sourceElement; } public int getPrecedence() { return (wasIncluded ? importer.getPrecedence() : precedence); } /** * Indicate that this stylesheet was included (by its "importer") using an xsl:include * statement as distinct from xsl:import */ public void setWasIncluded() { wasIncluded = true; } /** * Set the minimum import precedence of this module, that is, the lowest import precedence of the modules * that it imports. This information is used to decide which template rules are eligible for consideration * by xsl:apply-imports * @param min the minimum import precedence */ public void setMinImportPrecedence(int min) { this.minImportPrecedence = min; } /** * Get the minimum import precedence of this module, that is, the lowest import precedence of the modules * that it imports. This information is used to decide which template rules are eligible for consideration * by xsl:apply-imports * @return the minimum import precedence */ public int getMinImportPrecedence() { return this.minImportPrecedence; } /** * Process xsl:include and xsl:import elements. */ public void spliceIncludes() throws XPathException { boolean foundNonImport = false; topLevel = new ArrayList<Declaration>(50); minImportPrecedence = precedence; StyleElement previousElement = sourceElement; AxisIterator kids = sourceElement.iterateAxis(Axis.CHILD); while (true) { NodeInfo child = (NodeInfo) kids.next(); if (child == null) { break; } if (child.getNodeKind() == Type.TEXT) { // in an embedded stylesheet, white space nodes may still be there if (!Whitespace.isWhite(child.getStringValueCS())) { previousElement.compileError( "No character data is allowed between top-level elements", "XTSE0120"); } } else if (child instanceof DataElement) { foundNonImport = true; } else { previousElement = (StyleElement) child; if (child instanceof XSLGeneralIncorporate) { XSLGeneralIncorporate xslinc = (XSLGeneralIncorporate) child; xslinc.processAttributes(); if (xslinc.isImport()) { if (foundNonImport) { xslinc.compileError("xsl:import elements must come first", "XTSE0200"); } } else { foundNonImport = true; } // get the included stylesheet. This follows the URL, builds a tree, and splices // in any indirectly-included stylesheets. xslinc.validateInstruction(); int errors = sourceElement.getPreparedStylesheet().getErrorCount(); StylesheetModule inc = xslinc.getIncludedStylesheet(this, precedence); if (inc == null) { return; // error has been reported } errors = sourceElement.getPreparedStylesheet().getErrorCount() - errors; if (errors > 0) { xslinc.compileError("Reported " + errors + (errors==1 ? " error" : " errors") + " in " + (xslinc.isImport() ? "imported" : "included") + " stylesheet module", "XTSE0165"); } // after processing the imported stylesheet and any others it brought in, // adjust the import precedence of this stylesheet if necessary if (xslinc.isImport()) { precedence = inc.getPrecedence() + 1; } else { precedence = inc.getPrecedence(); inc.setMinImportPrecedence(minImportPrecedence); inc.setWasIncluded(); } // copy the top-level elements of the included stylesheet into the top level of this // stylesheet. Normally we add these elements at the end, in order, but if the precedence // of an element is less than the precedence of the previous element, we promote it. // This implements the requirement in the spec that when xsl:include is used to // include a stylesheet, any xsl:import elements in the included document are moved // up in the including document to after any xsl:import elements in the including // document. List<Declaration> incchildren = inc.topLevel; for (int j = 0; j < incchildren.size(); j++) { Declaration decl = incchildren.get(j); //StyleElement elem = decl.getSourceElement(); int last = topLevel.size() - 1; if (last < 0 || decl.getPrecedence() >= topLevel.get(last).getPrecedence()) { topLevel.add(decl); } else { while (last >= 0 && decl.getPrecedence() < topLevel.get(last).getPrecedence()) { last--; } topLevel.add(last + 1, decl); } } } else { foundNonImport = true; Declaration decl = new Declaration(this, (StyleElement)child); topLevel.add(decl); } } } } /** * Get the value of the input-type-annotations attribute, for this module combined with that * of all included/imported modules. The value is an or-ed combination of the two bits * {@link XSLStylesheet#ANNOTATION_STRIP} and {@link XSLStylesheet#ANNOTATION_PRESERVE} * @return the value of the input-type-annotations attribute, for this module combined with that * of all included/imported modules */ public int getInputTypeAnnotations() { return inputTypeAnnotations; } /** * Set the value of the input-type-annotations attribute, for this module combined with that * of all included/imported modules. The value is an or-ed combination of the two bits * {@link XSLStylesheet#ANNOTATION_STRIP} and {@link XSLStylesheet#ANNOTATION_PRESERVE} * @param annotations the value of the input-type-annotations attribute, for this module combined with that * of all included/imported modules. */ public void setInputTypeAnnotations(int annotations) throws XPathException { inputTypeAnnotations |= annotations; if (inputTypeAnnotations == (XSLStylesheet.ANNOTATION_STRIP | XSLStylesheet.ANNOTATION_PRESERVE)) { getPrincipalStylesheetModule().compileError( "One stylesheet module specifies input-type-annotations='strip', " + "another specifies input-type-annotations='preserve'", "XTSE0265"); } } } // 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.