package client.net.sf.saxon.ce.value;
import client.net.sf.saxon.ce.expr.Token;
import client.net.sf.saxon.ce.expr.XPathContext;
import client.net.sf.saxon.ce.js.IXSLFunction;
import client.net.sf.saxon.ce.js.JSObjectType;
import client.net.sf.saxon.ce.js.JSObjectValue;
import client.net.sf.saxon.ce.om.*;
import client.net.sf.saxon.ce.tree.iter.AxisIterator;
import client.net.sf.saxon.ce.tree.iter.SingletonIterator;
import client.net.sf.saxon.ce.pattern.*;
import client.net.sf.saxon.ce.trans.XPathException;
import client.net.sf.saxon.ce.type.ItemType;
import client.net.sf.saxon.ce.type.Type;
import client.net.sf.saxon.ce.type.TypeHierarchy;
/**
* A value that is a sequence containing zero or one items. Used only for items that are not atomic values
* (that is, nodes, and function items)
*/
public class SingletonItem extends Value implements GroundedValue{
protected Item item = null;
/**
* Create a node-set containing zero or one nodes
* @param item The node or function-item to be contained in the node-set, or null if the sequence
* is to be empty
*/
public SingletonItem(Item item) {
this.item = item;
}
/**
* Return the value in the form of an Item
* @return the value in the form of an Item
*/
public Item asItem() {
return item;
}
/**
* Process the instruction, without returning any tail calls
* @param context The dynamic context, giving access to the current node,
* the current variables, etc.
*/
public void process(XPathContext context) throws XPathException {
if (item != null) {
context.getReceiver().append(item, NodeInfo.ALL_NAMESPACES);
}
}
/**
* Determine the data type of the items in the expression. This method determines the most
* precise type that it can, because it is called when testing that the node conforms to a required
* type.
* @return the most precise possible type of the node.
* @param th the type hierarchy cache
*/
public ItemType getItemType(TypeHierarchy th) {
if (item instanceof NodeInfo) {
NodeInfo node = ((NodeInfo)item);
switch (node.getNodeKind()) {
case Type.DOCUMENT:
// Need to know whether the document is well-formed and if so what the element type is
AxisIterator iter = node.iterateAxis(Axis.CHILD);
ItemType elementType = null;
while (true) {
NodeInfo n = (NodeInfo)iter.next();
if (n==null) {
break;
}
int kind = n.getNodeKind();
if (kind==Type.TEXT) {
elementType = null;
break;
} else if (kind==Type.ELEMENT) {
if (elementType != null) {
elementType = null;
break;
}
elementType = new SingletonItem(n).getItemType(th);
}
}
if (elementType == null) {
return NodeKindTest.DOCUMENT;
} else {
return new DocumentNodeTest((NodeTest)elementType);
}
case Type.ELEMENT:
int eltype = node.getTypeAnnotation();
if (eltype == -1 || eltype == StandardNames.XS_UNTYPED || eltype == StandardNames.XS_ANY_TYPE) {
return new NameTest(Type.ELEMENT, node.getFingerprint(), node.getNamePool());
} else {
return new CombinedNodeTest(
new NameTest(Type.ELEMENT, node.getFingerprint(), node.getNamePool()),
Token.INTERSECT,
new ContentTypeTest(Type.ELEMENT, node.getConfiguration().getSchemaType(eltype),
node.getConfiguration()));
}
case Type.ATTRIBUTE:
int attype = node.getTypeAnnotation();
if (attype == -1 || attype == StandardNames.XS_UNTYPED_ATOMIC) {
return new NameTest(Type.ATTRIBUTE, node.getFingerprint(), node.getNamePool());
} else {
return new CombinedNodeTest(
new NameTest(Type.ATTRIBUTE, node.getFingerprint(), node.getNamePool()),
Token.INTERSECT,
new ContentTypeTest(Type.ATTRIBUTE, node.getConfiguration().getSchemaType(attype),
node.getConfiguration()));
}
case Type.TEXT:
return NodeKindTest.TEXT;
case Type.COMMENT:
return NodeKindTest.COMMENT;
case Type.PROCESSING_INSTRUCTION:
return NodeKindTest.PROCESSING_INSTRUCTION;
case Type.NAMESPACE:
return NodeKindTest.NAMESPACE;
default:
throw new IllegalArgumentException("Unknown node kind " + node.getNodeKind());
}
// context item may be a JSObjectValue for non-DOM event handlers
} else if (item instanceof JSObjectValue){
return new JSObjectType();
} else {
// it must be an atomic value, though we don't use this option
return ((AtomicValue)item).getTypeLabel();
}
}
/**
* Get the length of the sequence
*/
public int getLength() throws XPathException {
return (item ==null ? 0 : 1);
}
/**
* Get the n'th item in the sequence (starting from 0). This is defined for all
* SequenceValues, but its real benefits come for a SequenceValue stored extensionally
* (or for a MemoClosure, once all the values have been read)
*/
public Item itemAt(int n) {
if (n==0 && item !=null) {
return item;
} else {
return null;
}
}
/**
* Get a subsequence of the value
*
* @param start the index of the first item to be included in the result, counting from zero.
* A negative value is taken as zero. If the value is beyond the end of the sequence, an empty
* sequence is returned
* @param length the number of items to be included in the result. Specify Integer.MAX_VALUE to
* get the subsequence up to the end of the base sequence. If the value is negative, an empty sequence
* is returned. If the value goes off the end of the sequence, the result returns items up to the end
* of the sequence
* @return the required subsequence. If min is
*/
public GroundedValue subsequence(int start, int length) {
if (item != null && start <= 0 && start+length > 0) {
return this;
} else {
return EmptySequence.getInstance();
}
}
/**
* Get the node that forms the node-set. Return null if there is none.
*/
public Item getItem() {
return item;
}
/**
* Return an enumeration of this nodeset value.
*/
public SequenceIterator iterate() {
return SingletonIterator.makeIterator(item);
}
/**
* Get the effective boolean value
*/
public boolean effectiveBooleanValue() {
return (item != null);
}
/**
* Convert the value to a string, using the serialization rules.
* For atomic values this is the same as a cast; for sequence values
* it gives a space-separated list. For QNames and NOTATIONS, or lists
* containing them, it fails.
*/
public String getStringValue() {
return (item ==null ? "" : item.getStringValue());
}
}
// 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.