package client.net.sf.saxon.ce.expr.instruct; import client.net.sf.saxon.ce.Controller; import client.net.sf.saxon.ce.event.*; import client.net.sf.saxon.ce.expr.*; import client.net.sf.saxon.ce.om.Item; import client.net.sf.saxon.ce.om.NodeInfo; import client.net.sf.saxon.ce.om.StandardNames; import client.net.sf.saxon.ce.pattern.NodeKindTest; import client.net.sf.saxon.ce.pattern.NodeTest; import client.net.sf.saxon.ce.trans.XPathException; import client.net.sf.saxon.ce.tree.util.NamespaceIterator; 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 client.net.sf.saxon.ce.value.EmptySequence; import java.util.Arrays; import java.util.Iterator; /** * Handler for xsl:copy elements in stylesheet. */ public class Copy extends ElementCreator { private boolean copyNamespaces; private ItemType resultItemType; private Expression select; /** * Create a shallow copy instruction * @param select selects the node (or other item) to be copied * @param copyNamespaces true if namespace nodes are to be copied when copying an element * @param inheritNamespaces true if child elements are to inherit the namespace nodes of their parent */ public Copy(Expression select, boolean copyNamespaces, boolean inheritNamespaces) { this.copyNamespaces = copyNamespaces; this.inheritNamespaces = inheritNamespaces; this.select = select; } /** * Simplify an expression. This performs any static optimization (by rewriting the expression * as a different expression). The default implementation does nothing. * * @return the simplified expression * @throws client.net.sf.saxon.ce.trans.XPathException * if an error is discovered during expression rewriting * @param visitor an expression visitor */ public Expression simplify(ExpressionVisitor visitor) throws XPathException { select = visitor.simplify(select); return super.simplify(visitor); } public Expression typeCheck(ExpressionVisitor visitor, ItemType contextItemType) throws XPathException { try { select = visitor.typeCheck(select, contextItemType); adoptChildExpression(select); } catch (XPathException err) { if (err.getErrorCodeLocalPart().equals("XPDY0002")) { // See spec bug 7624, test case copy903err err.setErrorCode("XTTE0945"); err.maybeSetLocation(getSourceLocator()); } select = new Literal(EmptySequence.getInstance()); // to prevent duplicate error reporting throw err; } ItemType selectItemType = select.getItemType(visitor.getConfiguration().getTypeHierarchy()); if (selectItemType instanceof NodeTest) { switch (selectItemType.getPrimitiveType()) { // For elements and attributes, assume the type annotation will change case Type.ELEMENT: this.resultItemType = NodeKindTest.ELEMENT; break; case Type.ATTRIBUTE: this.resultItemType = NodeKindTest.ATTRIBUTE; break; case Type.DOCUMENT: this.resultItemType = NodeKindTest.DOCUMENT; break; default: this.resultItemType = selectItemType; } } else { this.resultItemType = selectItemType; } return super.typeCheck(visitor, contextItemType); } /** * Determine which aspects of the context the expression depends on. The result is * a bitwise-or'ed value composed from constants such as XPathContext.VARIABLES and * XPathContext.CURRENT_NODE. The default implementation combines the intrinsic * dependencies of this expression with the dependencies of the subexpressions, * computed recursively. This is overridden for expressions such as FilterExpression * where a subexpression's dependencies are not necessarily inherited by the parent * expression. * @return a set of bit-significant flags identifying the dependencies of * the expression */ public int getIntrinsicDependencies() { return StaticProperty.DEPENDS_ON_CONTEXT_ITEM; } /** * Get the name of this instruction for diagnostic and tracing purposes */ public int getInstructionNameCode() { return StandardNames.XSL_COPY; } /** * Get the immediate sub-expressions of this expression. * @return an iterator containing the sub-expressions of this expression */ public Iterator<Expression> iterateSubExpressions() { return Arrays.asList((new Expression[]{select, content})).iterator(); } /** * Get the item type of the result of this instruction. * @return The context item type. * @param th the type hierarchy cache */ public ItemType getItemType(TypeHierarchy th) { if (resultItemType != null) { return resultItemType; } else { resultItemType = computeItemType(th); return resultItemType; } } private ItemType computeItemType(TypeHierarchy th) { return select.getItemType(th); } public Expression optimize(ExpressionVisitor visitor, ItemType contextItemType) throws XPathException { select = visitor.optimize(select, contextItemType); Expression exp = super.optimize(visitor, contextItemType); if (exp == this) { if (resultItemType == null) { resultItemType = computeItemType(visitor.getConfiguration().getTypeHierarchy()); } if (resultItemType.isAtomicType()) { return select; } } return exp; } /** * Callback from ElementCreator when constructing an element * @param context XPath dynamic evaluation context * @param copiedNode the node being copied * @return the namecode of the element to be constructed * @throws XPathException */ public int getNameCode(XPathContext context, NodeInfo copiedNode) throws XPathException { return copiedNode.getNameCode(); } /** * Get the base URI of a copied element node (the base URI is retained in the new copy) * @param context XPath dynamic evaluation context * @param copiedNode * @return the base URI */ public String getNewBaseURI(XPathContext context, NodeInfo copiedNode) { return copiedNode.getBaseURI(); } /** * Callback to output namespace nodes for the new element. * @param context The execution context * @param receiver the Receiver where the namespace nodes are to be written * @param nameCode * @param copiedNode * @throws XPathException */ protected void outputNamespaceNodes(XPathContext context, Receiver receiver, int nameCode, NodeInfo copiedNode) throws XPathException { if (copyNamespaces) { NamespaceIterator.sendNamespaces(copiedNode, receiver); } else { // Always output the namespace of the element name itself receiver.namespace(context.getNamePool().getNamespaceBinding(nameCode), 0); } } public TailCall processLeavingTail(XPathContext context) throws XPathException { SequenceReceiver out = context.getReceiver(); Item item = select.evaluateItem(context); if (item == null) { // do nothing (XSLT 3.0) // // See spec bug 7624, test case copy904err // XPathException err = new XPathException("Context item for xsl:copy is undefined", "XTTE0945"); // err.setLocator(this); // throw err; } if (!(item instanceof NodeInfo)) { out.append(item, NodeInfo.ALL_NAMESPACES); return null; } NodeInfo source = (NodeInfo)item; //out.getPipelineConfiguration().setBaseURI(source.getBaseURI()); // Processing depends on the node kind. switch(source.getNodeKind()) { case Type.ELEMENT: // use the generic code for creating new elements return super.processLeavingTail(context, (NodeInfo)item); case Type.ATTRIBUTE: try { context.getReceiver().attribute(source.getNameCode(), source.getStringValueCS()); } catch (NoOpenStartTagException err) { err.setXPathContext(context); throw dynamicError(getSourceLocator(), err, context); } break; case Type.TEXT: out.characters(source.getStringValueCS()); break; case Type.PROCESSING_INSTRUCTION: 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.startDocument(); content.process(context); out.endDocument(); break; default: throw new IllegalArgumentException("Unknown node kind " + source.getNodeKind()); } return null; } /** * Evaluate as an expression. We rely on the fact that when these instructions * are generated by XQuery, there will always be a valueExpression to evaluate * the content */ public Item evaluateItem(XPathContext context) throws XPathException { Controller controller = context.getController(); XPathContext c2 = context.newMinorContext(); SequenceOutputter seq = controller.allocateSequenceOutputter(1); PipelineConfiguration pipe = controller.makePipelineConfiguration(); seq.setPipelineConfiguration(pipe); c2.setTemporaryReceiver(seq); process(c2); seq.close(); Item item = seq.getFirstItem(); seq.reset(); return item; } } // 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.