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.Expression;
import client.net.sf.saxon.ce.expr.StaticProperty;
import client.net.sf.saxon.ce.expr.XPathContext;
import client.net.sf.saxon.ce.om.Item;
import client.net.sf.saxon.ce.om.NodeInfo;
import client.net.sf.saxon.ce.pattern.NodeKindTest;
import client.net.sf.saxon.ce.trans.XPathException;
import client.net.sf.saxon.ce.type.ItemType;
import client.net.sf.saxon.ce.type.TypeHierarchy;
/**
* An instruction that creates an element node. There are two subtypes, FixedElement
* for use where the name is known statically, and Element where it is computed
* dynamically. To allow use in both XSLT and XQuery, the class acts both as an
* Instruction and as an Expression.
*/
public abstract class ElementCreator extends ParentNodeConstructor {
/**
* The inheritNamespaces flag indicates that the namespace nodes on the element created by this instruction
* are to be inherited (copied) on the children of this element. That is, if this flag is false, the child
* elements must carry a namespace undeclaration for all the namespaces on the parent, unless they are
* redeclared in some way.
*/
protected boolean inheritNamespaces = true;
/**
* Construct an ElementCreator. Exists for the benefit of subclasses.
*/
public ElementCreator() { }
/**
* Get the item type of the value returned by this instruction
* @return the item type
* @param th the type hierarchy cache
*/
public ItemType getItemType(TypeHierarchy th) {
return NodeKindTest.ELEMENT;
}
/**
* Get the static properties of this expression (other than its type). The result is
* bit-signficant. These properties are used for optimizations. In general, if
* property bit is set, it is true, but if it is unset, the value is unknown.
*
* @return a set of flags indicating static properties of this expression
*/
public int computeSpecialProperties() {
return super.computeSpecialProperties() |
StaticProperty.SINGLE_DOCUMENT_NODESET;
}
/**
* Determine (at run-time) the name code of the element being constructed
* @param context the XPath dynamic evaluation context
* @param copiedNode
* @return the integer name code representing the element name
* @throws XPathException if a failure occurs
*/
public abstract int getNameCode(XPathContext context, NodeInfo copiedNode)
throws XPathException;
/**
* Get the base URI for the element being constructed
* @param context the XPath dynamic evaluation context
* @param copiedNode the node being copied (for xsl:copy), otherwise null
* @return the base URI of the constructed element
*/
protected abstract String getNewBaseURI(XPathContext context, NodeInfo copiedNode);
/**
* Callback to output namespace nodes for the new element. This method is responsible
* for ensuring that a namespace node is always generated for the namespace of the element
* name itself.
* @param context The execution context
* @param receiver the Receiver where the namespace nodes are to be written
* @param nameCode the name code of the element being created
* @param copiedNode the node being copied (for xsl:copy) or null otherwise
* @throws client.net.sf.saxon.ce.trans.XPathException
*/
protected abstract void outputNamespaceNodes(XPathContext context, Receiver receiver, int nameCode, NodeInfo copiedNode)
throws XPathException;
/**
* An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
* This method indicates which of these methods is prefered. For instructions this is the process() method.
*/
public int getImplementationMethod() {
return Expression.PROCESS_METHOD | Expression.EVALUATE_METHOD;
}
/**
* Evaluate the instruction to produce a new element node. This method is typically used when there is
* a parent element or document in a result tree, to which the new element is added.
* @param context XPath dynamic evaluation context
* @return null (this instruction never returns a tail call)
* @throws XPathException
*/
public TailCall processLeavingTail(XPathContext context)
throws XPathException {
return processLeavingTail(context, null);
}
/**
* Evaluate the instruction to produce a new element node. This method is typically used when there is
* a parent element or document in a result tree, to which the new element is added.
* @param context XPath dynamic evaluation context
* @param copiedNode null except in the case of xsl:copy, when it is the node being copied
* @return null (this instruction never returns a tail call)
* @throws XPathException
*/
protected final TailCall processLeavingTail(XPathContext context, NodeInfo copiedNode)
throws XPathException {
try {
int nameCode = getNameCode(context, copiedNode);
SequenceReceiver out = context.getReceiver();
if (out.getSystemId() == null) {
out.setSystemId(getNewBaseURI(context, copiedNode));
}
int properties = (inheritNamespaces ? 0 : ReceiverOptions.DISINHERIT_NAMESPACES);
out.startElement(nameCode, properties);
// output the required namespace nodes via a callback
outputNamespaceNodes(context, out, nameCode, copiedNode);
// process subordinate instructions to generate attributes and content
content.process(context);
// output the element end tag (which will fail if validation fails)
out.endElement();
return null;
} catch (XPathException e) {
e.maybeSetLocation(getSourceLocator());
e.maybeSetContext(context);
throw e;
}
}
/**
* Evaluate the constructor, returning the constructed element node. If lazy construction
* mode is in effect, then an UnconstructedParent object is returned instead.
*/
public Item evaluateItem(XPathContext context) throws XPathException {
return constructElement(context, null);
}
/**
* Construct the element node as a free-standing (parentless) node in a tiny tree
* @param context XPath dynamic evaluation context
* @return the constructed element node
* @throws XPathException
*/
private NodeInfo constructElement(XPathContext context, NodeInfo copiedNode) throws XPathException {
try {
Controller controller = context.getController();
XPathContext c2 = context.newMinorContext();
SequenceOutputter seq = controller.allocateSequenceOutputter(1);
PipelineConfiguration pipe = controller.makePipelineConfiguration();
seq.setPipelineConfiguration(pipe);
int nameCode = getNameCode(c2, copiedNode);
c2.setTemporaryReceiver(seq);
if (seq.getSystemId() == null) {
seq.setSystemId(getNewBaseURI(c2, copiedNode));
}
seq.open();
int properties = (inheritNamespaces ? 0 : ReceiverOptions.DISINHERIT_NAMESPACES);
seq.startElement(nameCode, properties);
// output the namespace nodes for the new element
outputNamespaceNodes(c2, seq, nameCode, null);
content.process(c2);
seq.endElement();
seq.close();
// the constructed element is the first and only item in the sequence
NodeInfo result = (NodeInfo)seq.popLastItem();
seq.reset();
return result;
} catch (XPathException err) {
err.maybeSetLocation(getSourceLocator());
err.maybeSetContext(context);
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.