package client.net.sf.saxon.ce.expr.instruct; import client.net.sf.saxon.ce.event.NoOpenStartTagException; import client.net.sf.saxon.ce.event.SequenceReceiver; import client.net.sf.saxon.ce.expr.*; import client.net.sf.saxon.ce.lib.NamespaceConstant; import client.net.sf.saxon.ce.om.*; import client.net.sf.saxon.ce.trans.XPathException; import client.net.sf.saxon.ce.tree.util.Navigator; import client.net.sf.saxon.ce.tree.util.URI; import client.net.sf.saxon.ce.type.ItemType; import client.net.sf.saxon.ce.type.Type; import client.net.sf.saxon.ce.type.TypeHierarchy; import java.util.Iterator; /** * An xsl:copy-of element in the stylesheet. */ public class CopyOf extends Instruction { private Expression select; private boolean copyNamespaces; private String staticBaseUri; /** * Create an xsl:copy-of instruction (also used in XQuery for implicit copying) * @param select expression that selects the nodes to be copied * @param copyNamespaces true if namespaces are to be copied * if duplicates are handled by discarding all but the first (XSLT). */ public CopyOf(Expression select, boolean copyNamespaces) { this.select = select; this.copyNamespaces = copyNamespaces; adoptChildExpression(select); } /** * Set the static base URI of the xsl:copy-of instruction * @param base the static base URI */ public void setStaticBaseUri(String base) { staticBaseUri = base; } /** * Determine whether this instruction creates new nodes. * The result depends on the type of the select expression. */ public final boolean createsNewNodes() { Executable exec = getExecutable(); if (exec == null) { return true; // This shouldn't happen, but we err on the safe side } final TypeHierarchy th = exec.getConfiguration().getTypeHierarchy(); return !select.getItemType(th).isAtomicType(); } /** * Get the name of this instruction, for diagnostics and tracing */ public int getInstructionNameCode() { return StandardNames.XSL_COPY_OF; } /** * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process(). * This method indicates which of these methods is provided. This implementation provides both iterate() and * process() methods natively. */ public int getImplementationMethod() { return PROCESS_METHOD; } public Expression simplify(ExpressionVisitor visitor) throws XPathException { select = visitor.simplify(select); return this; } public ItemType getItemType(TypeHierarchy th) { return select.getItemType(th); } public int getCardinality() { return select.getCardinality(); } public int getDependencies() { return select.getDependencies(); } protected void promoteInst(PromotionOffer offer) throws XPathException { select = doPromotion(select, offer); } public Expression typeCheck(ExpressionVisitor visitor, ItemType contextItemType) throws XPathException { select = visitor.typeCheck(select, contextItemType); adoptChildExpression(select); return this; } public Expression optimize(ExpressionVisitor visitor, ItemType contextItemType) throws XPathException { select = visitor.optimize(select, contextItemType); adoptChildExpression(select); final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy(); if (select.getItemType(th).isAtomicType()) { return select; } return this; } public Iterator<Expression> iterateSubExpressions() { return monoIterator(select); } /** * Replace one subexpression by a replacement subexpression * @param original the original subexpression * @param replacement the replacement subexpression * @return true if the original subexpression is found */ public boolean replaceSubExpression(Expression original, Expression replacement) { boolean found = false; if (select == original) { select = replacement; found = true; } return found; } /** * Process this xsl:copy-of instruction * * @param context the dynamic context for the transformation * @return null - this implementation of the method never returns a TailCall */ public TailCall processLeavingTail(XPathContext context) throws XPathException { SequenceReceiver out = context.getReceiver(); boolean copyBaseURI = (out.getSystemId() == null); // if the copy is being attached to an existing parent, it inherits the base URI of the parent int copyOptions = CopyOptions.TYPE_ANNOTATIONS; if (copyNamespaces) { copyOptions |= CopyOptions.ALL_NAMESPACES; } //int whichNamespaces = (copyNamespaces ? NodeInfo.ALL_NAMESPACES : NodeInfo.NO_NAMESPACES); SequenceIterator iter = select.iterate(context); while (true) { Item item = iter.next(); if (item == null) { break; } if (item instanceof NodeInfo) { NodeInfo source = (NodeInfo) item; int kind = source.getNodeKind(); switch (kind) { case Type.ELEMENT: { if (copyBaseURI) { out.setSystemId(computeNewBaseUri(source)); } source.copy(out, copyOptions); break; } case Type.ATTRIBUTE: try { context.getReceiver().attribute(source.getNameCode(), source.getStringValueCS()); } catch (NoOpenStartTagException err) { dynamicError(err.getMessage(), err.getErrorCodeLocalPart(), context); } break; case Type.TEXT: out.characters(source.getStringValueCS()); break; case Type.PROCESSING_INSTRUCTION: if (copyBaseURI) { out.setSystemId(source.getBaseURI()); } out.processingInstruction(source.getDisplayName(), source.getStringValueCS()); break; case Type.COMMENT: out.comment(source.getStringValueCS()); break; case Type.NAMESPACE: try { source.copy(out, 0); } catch (NoOpenStartTagException err) { dynamicError(err.getMessage(), err.getErrorCodeLocalPart(), context); } break; case Type.DOCUMENT: { out.setPipelineConfiguration(out.getPipelineConfiguration()); if (copyBaseURI) { out.setSystemId(source.getBaseURI()); } source.copy(out, copyOptions); break; } default: throw new IllegalArgumentException("Unknown node kind " + source.getNodeKind()); } } else { out.append(item, NodeInfo.ALL_NAMESPACES); } } return null; } private String computeNewBaseUri(NodeInfo source) { // These rules are the rules for xsl:copy-of instruction in XSLT. The same code is used to support the // validate{} expression in XQuery. XQuery says nothing about the base URI of a node that results // from a validate{} expression, so until it does, we might as well use the same logic. String newBaseUri; String xmlBase = Navigator.getAttributeValue(source, NamespaceConstant.XML, "base"); if (xmlBase != null) { try { URI xmlBaseUri = new URI(xmlBase, true); if (xmlBaseUri.isAbsolute()) { newBaseUri = xmlBase; } else if (staticBaseUri != null) { URI sbu = new URI(staticBaseUri); URI abs = sbu.resolve(xmlBaseUri.toString()); newBaseUri = abs.toString(); } else { newBaseUri = source.getBaseURI(); } } catch (URI.URISyntaxException err) { newBaseUri = source.getBaseURI(); } } else { newBaseUri = source.getBaseURI(); } return newBaseUri; } } // 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.