package client.net.sf.saxon.ce.style; import client.net.sf.saxon.ce.LogController; import client.net.sf.saxon.ce.PreparedStylesheet; import client.net.sf.saxon.ce.expr.Expression; import client.net.sf.saxon.ce.expr.Literal; import client.net.sf.saxon.ce.expr.TraceExpression; import client.net.sf.saxon.ce.expr.instruct.*; import client.net.sf.saxon.ce.lib.NamespaceConstant; import client.net.sf.saxon.ce.lib.Validation; import client.net.sf.saxon.ce.om.*; import client.net.sf.saxon.ce.trace.Location; import client.net.sf.saxon.ce.trans.XPathException; import client.net.sf.saxon.ce.tree.linked.DocumentImpl; import client.net.sf.saxon.ce.tree.linked.LinkedTreeBuilder; import client.net.sf.saxon.ce.tree.util.NamespaceIterator; import client.net.sf.saxon.ce.tree.util.Navigator; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import com.google.gwt.logging.client.LogConfiguration; /** * This class represents a literal result element in the style sheet * (typically an HTML element to be output). <br> * It is also used to represent unknown top-level elements, which are ignored. */ public class LiteralResultElement extends StyleElement { private int resultNameCode; private int[] attributeNames; private Expression[] attributeValues; private int numberOfAttributes; private boolean toplevel; private List<NamespaceBinding> namespaceCodes = new ArrayList(); private AttributeSet[] attributeSets; private boolean inheritNamespaces = true; /** * Determine whether this type of element is allowed to contain a sequence constructor * @return true: yes, it may contain a sequence constructor */ public boolean mayContainSequenceConstructor() { return true; } /** * Specify that this is an instruction */ public boolean isInstruction() { return true; } /** * Process the attribute list */ public void prepareAttributes() throws XPathException { // Process the values of all attributes. At this stage we deal with attribute // values (especially AVTs), but we do not apply namespace aliasing to the // attribute names. AttributeCollection atts = getAttributeList(); int num = atts.getLength(); if (num == 0) { numberOfAttributes = 0; } else { NamePool namePool = getNamePool(); attributeNames = new int[num]; attributeValues = new Expression[num]; numberOfAttributes = 0; for (int i=0; i<num; i++) { int anameCode = atts.getNameCode(i); short attURIcode = namePool.getURICode(anameCode); if (attURIcode==NamespaceConstant.XSLT_CODE) { int fp = anameCode & NamePool.FP_MASK; if (fp == StandardNames.XSL_USE_ATTRIBUTE_SETS) { // deal with this later } else if (fp == StandardNames.XSL_DEFAULT_COLLATION) { // already dealt with } else if (fp == StandardNames.XSL_EXTENSION_ELEMENT_PREFIXES) { // already dealt with } else if (fp == StandardNames.XSL_EXCLUDE_RESULT_PREFIXES) { // already dealt with } else if (fp == StandardNames.XSL_VERSION) { // already dealt with } else if (fp == StandardNames.XSL_XPATH_DEFAULT_NAMESPACE) { // already dealt with } else if (fp == StandardNames.XSL_TYPE) { // deal with this later } else if (fp == StandardNames.XSL_USE_WHEN) { // already dealt with } else if (fp == StandardNames.XSL_VALIDATION) { // deal with this later } else if (fp == StandardNames.XSL_INHERIT_NAMESPACES) { String inheritAtt = atts.getValue(i); if (inheritAtt.equals("yes")) { inheritNamespaces = true; } else if (inheritAtt.equals("no")) { inheritNamespaces = false; } else { compileError("The xsl:inherit-namespaces attribute has permitted values (yes, no)", "XTSE0020"); } } else { compileError("Unknown XSL attribute " + namePool.getDisplayName(anameCode), "XTSE0805"); } } else { attributeNames[numberOfAttributes] = anameCode; Expression exp = makeAttributeValueTemplate(atts.getValue(i)); attributeValues[numberOfAttributes] = exp; numberOfAttributes++; } } // now shorten the arrays if necessary. This is necessary if there are [xsl:]-prefixed // attributes that weren't copied into the arrays. if (numberOfAttributes < attributeNames.length) { int[] attributeNames2 = new int[numberOfAttributes]; System.arraycopy(attributeNames, 0, attributeNames2, 0, numberOfAttributes); attributeNames = attributeNames2; Expression[] attributeValues2 = new Expression[numberOfAttributes]; System.arraycopy(attributeValues, 0, attributeValues2, 0, numberOfAttributes); attributeValues = attributeValues2; } } } /** * Validate that this node is OK * @param decl */ public void validate(Declaration decl) throws XPathException { toplevel = (getParent() instanceof XSLStylesheet); resultNameCode = getNameCode(); NamePool namePool = getNamePool(); String elementURI = namePool.getURI(resultNameCode); if (toplevel) { // A top-level element can never be a "real" literal result element, // but this class gets used for unknown elements found at the top level if (elementURI.isEmpty()) { compileError("Top level elements must have a non-null namespace URI", "XTSE0130"); } } else { // Build the list of output namespace nodes. Note we no longer optimize this list. // See comments in the 9.1 source code for some history of this decision. Iterator<NamespaceBinding> inscope = NamespaceIterator.iterateNamespaces(this); while (inscope.hasNext()) { namespaceCodes.add(inscope.next()); } // Spec bug 5857: if there is no other binding for the default namespace, add an undeclaration String defaultNamespace = getURIForPrefix("", true); if (defaultNamespace.isEmpty()) { namespaceCodes.add(NamespaceBinding.DEFAULT_UNDECLARATION); } // apply any aliases required to create the list of output namespaces PrincipalStylesheetModule sheet = getPrincipalStylesheetModule(); if (sheet.hasNamespaceAliases()) { for (int i=0; i<namespaceCodes.size(); i++) { // System.err.println("Examining namespace " + namespaceCodes[i]); String suri = namespaceCodes.get(i).getURI(); NamespaceBinding ncode = sheet.getNamespaceAlias(suri); if (ncode != null && !ncode.getURI().equals(suri)) { // apply the namespace alias. Change in 7.3: use the prefix associated // with the new namespace, not the old prefix. namespaceCodes.set(i, ncode); } } // determine if there is an alias for the namespace of the element name NamespaceBinding elementAlias = sheet.getNamespaceAlias(elementURI); if (elementAlias != null && !elementAlias.getURI().equals(elementURI)) { resultNameCode = namePool.allocate(elementAlias.getPrefix(), elementAlias.getURI(), getLocalPart()); } } // deal with special attributes String useAttSets = Navigator.getAttributeValue(this, NamespaceConstant.XSLT, "use-attribute-sets"); if (useAttSets != null) { attributeSets = getAttributeSets(useAttSets, null); } String type = Navigator.getAttributeValue(this, NamespaceConstant.XSLT, "type"); if (type != null) { compileError("The xsl:type attribute is available only with a schema-aware XSLT processor", "XTSE1660"); } String validate = Navigator.getAttributeValue(this, NamespaceConstant.XSLT, "validation"); if (validate != null && Validation.getCode(validate)!= Validation.STRIP) { compileError("To perform validation, a schema-aware XSLT processor is needed", "XTSE1660"); } // establish the names to be used for all the output attributes; // also type-check the AVT expressions if (numberOfAttributes > 0) { for (int i=0; i<numberOfAttributes; i++) { int anameCode = attributeNames[i]; int alias = anameCode; String attURIcode = namePool.getURI(anameCode); if (!attURIcode.isEmpty()) { // attribute has a namespace prefix NamespaceBinding newNSCode = sheet.getNamespaceAlias(attURIcode); if ((newNSCode != null && !newNSCode.getURI().equals(attURIcode))) { alias = namePool.allocate( newNSCode.getPrefix(), newNSCode.getURI(), getAttributeList().getLocalName(i)); } } attributeNames[i] = alias; attributeValues[i] = typeCheck(attributeValues[i]); } } // remove any namespaces that are on the exclude-result-prefixes list. // The namespace is excluded even if it is the namespace of the element or an attribute, // though in that case namespace fixup will reinstate it. for (int n=namespaceCodes.size()-1; n>=0; n--) { String uri = namespaceCodes.get(n).getURI(); if (isExcludedNamespace(uri) && !sheet.isAliasResultNamespace(uri)) { namespaceCodes.remove(n); } } } } /** * Validate the children of this node, recursively. Overridden for top-level * data elements. * @param decl */ protected void validateChildren(Declaration decl) throws XPathException { if (!toplevel) { super.validateChildren(decl); } } /** * Compile code to process the literal result element at runtime */ public Expression compile(Executable exec, Declaration decl) throws XPathException { // top level elements in the stylesheet are ignored if (toplevel) return null; NamespaceBinding[] bindings = namespaceCodes.toArray(new NamespaceBinding[namespaceCodes.size()]); FixedElement inst = new FixedElement(resultNameCode, bindings, inheritNamespaces); inst.setBaseURI(getBaseURI()); Expression content = compileSequenceConstructor(exec, decl, iterateAxis(Axis.CHILD)); if (numberOfAttributes > 0) { for (int i=attributeNames.length - 1; i>=0; i--) { FixedAttribute att = new FixedAttribute(attributeNames[i]); try { att.setSelect(attributeValues[i], exec.getConfiguration()); } catch (XPathException err) { compileError(err); } att.setSourceLocator(this); Expression exp = att; if (LogConfiguration.loggingIsEnabled() && LogController.traceIsEnabled()) { TraceExpression trace = new TraceExpression(exp); trace.setNamespaceResolver(this); trace.setConstructType(Location.LITERAL_RESULT_ATTRIBUTE); trace.setSourceLocator(this); trace.setObjectName(this.getNamePool().getStructuredQName(attributeNames[i])); exp = trace; } if (content == null) { content = exp; } else { content = Block.makeBlock(exp, content); content.setSourceLocator(this); } } } if (attributeSets != null) { UseAttributeSets use = new UseAttributeSets(attributeSets); if (content == null) { content = use; } else { content = Block.makeBlock(use, content); content.setSourceLocator(this); } } if (content == null) { content = Literal.makeEmptySequence(); } inst.setContentExpression(content); return inst; } /** * Make a top-level literal result element into a stylesheet. This implements * the "Simplified Stylesheet" facility. * @param pss the PreparedStylesheet (the compiled stylesheet as provided) * @return the reconstructed stylesheet with an xsl:stylesheet and xsl:template element added */ public DocumentImpl makeStylesheet(PreparedStylesheet pss) throws XPathException { // the implementation grafts the LRE node onto a containing xsl:template and // xsl:stylesheet StyleNodeFactory nodeFactory = pss.getStyleNodeFactory(); NamePool pool = getNamePool(); String xslPrefix = getPrefixForURI(NamespaceConstant.XSLT); if (xslPrefix==null) { String message; if (getLocalPart().equals("stylesheet") || getLocalPart().equals("transform")) { if (getPrefixForURI(NamespaceConstant.MICROSOFT_XSL) != null) { message = "Saxon is not able to process Microsoft's WD-xsl dialect"; } else { message = "Namespace for stylesheet element should be " + NamespaceConstant.XSLT; } } else { message = "The supplied file does not appear to be a stylesheet"; } XPathException err = new XPathException(message); err.setLocator(this); err.setErrorCode("XTSE0150"); err.setIsStaticError(true); pss.reportError(err); throw err; } // check there is an xsl:version attribute (it's mandatory), and copy // it to the new xsl:stylesheet element String version = Navigator.getAttributeValue(this, NamespaceConstant.XSLT, "version"); if (version==null) { XPathException err = new XPathException("Simplified stylesheet: xsl:version attribute is missing"); err.setErrorCode("XTSE0150"); err.setIsStaticError(true); err.setLocator(this); pss.reportError(err); throw err; } try { DocumentImpl oldRoot = (DocumentImpl)getDocumentRoot(); LinkedTreeBuilder builder = new LinkedTreeBuilder(); builder.setPipelineConfiguration(pss.getConfiguration().makePipelineConfiguration()); builder.setNodeFactory(nodeFactory); builder.setSystemId(this.getSystemId()); builder.open(); builder.startDocument(); int st = StandardNames.XSL_STYLESHEET; builder.startElement(st, 0); builder.namespace(new NamespaceBinding("xsl", NamespaceConstant.XSLT), 0); builder.attribute(pool.allocate("", "", "version"), version); builder.startContent(); int te = StandardNames.XSL_TEMPLATE; builder.startElement(te, 0); builder.attribute(pool.allocate("", "", "match"), "/"); builder.startContent(); builder.graftElement(this); builder.endElement(); builder.endElement(); builder.endDocument(); builder.close(); DocumentImpl newRoot = (DocumentImpl)builder.getCurrentRoot(); newRoot.graftLocationMap(oldRoot); return newRoot; } catch (XPathException err) { //TransformerConfigurationException e = new TransformerConfigurationException(err); err.setLocator(this); throw err; } } } // 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.