001: package net.sf.saxon.value;
002:
003: import net.sf.saxon.expr.ExpressionTool;
004: import net.sf.saxon.expr.StaticProperty;
005: import net.sf.saxon.expr.XPathContext;
006: import net.sf.saxon.expr.Token;
007: import net.sf.saxon.om.*;
008: import net.sf.saxon.pattern.*;
009: import net.sf.saxon.trans.XPathException;
010: import net.sf.saxon.type.ItemType;
011: import net.sf.saxon.type.Type;
012: import net.sf.saxon.type.TypeHierarchy;
013: import net.sf.saxon.style.StandardNames;
014:
015: import java.io.PrintStream;
016:
017: /**
018: * A value that is a sequence containing zero or one nodes
019: */
020:
021: public class SingletonNode extends Value {
022:
023: protected NodeInfo node = null;
024:
025: /**
026: * Create a node-set containing zero or one nodes
027: * @param node The node to be contained in the node-set, or null if the node-set
028: * is to be empty
029: */
030:
031: public SingletonNode(NodeInfo node) {
032: this .node = node;
033: }
034:
035: /**
036: * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
037: * This method indicates which of these methods is prefered.
038: */
039:
040: public int getImplementationMethod() {
041: return EVALUATE_METHOD;
042: }
043:
044: /**
045: * Process the instruction, without returning any tail calls
046: * @param context The dynamic context, giving access to the current node,
047: * the current variables, etc.
048: */
049:
050: public void process(XPathContext context) throws XPathException {
051: if (node != null) {
052: context.getReceiver().append(node, 0,
053: NodeInfo.ALL_NAMESPACES);
054: }
055: }
056:
057: /**
058: * Determine the data type of the items in the expression. This method determines the most
059: * precise type that it can, because it is called when testing that the node conforms to a required
060: * type.
061: * @return the most precise possible type of the node.
062: * @param th
063: */
064:
065: public ItemType getItemType(TypeHierarchy th) {
066: switch (node.getNodeKind()) {
067: case Type.DOCUMENT:
068: // Need to know whether the document is well-formed and if so what the element type is
069: AxisIterator iter = node.iterateAxis(Axis.CHILD);
070: ItemType elementType = null;
071: while (true) {
072: NodeInfo n = (NodeInfo) iter.next();
073: if (n == null) {
074: break;
075: }
076: int kind = n.getNodeKind();
077: if (kind == Type.TEXT) {
078: elementType = null;
079: break;
080: } else if (kind == Type.ELEMENT) {
081: if (elementType != null) {
082: elementType = null;
083: break;
084: }
085: elementType = new SingletonNode(n).getItemType(th);
086: }
087: }
088: if (elementType == null) {
089: return NodeKindTest.DOCUMENT;
090: } else {
091: return new DocumentNodeTest((NodeTest) elementType);
092: }
093:
094: case Type.ELEMENT:
095: int eltype = node.getTypeAnnotation();
096: if (eltype == -1 || eltype == StandardNames.XDT_UNTYPED
097: || eltype == StandardNames.XS_ANY_TYPE) {
098: return new NameTest(Type.ELEMENT,
099: node.getFingerprint(), node.getNamePool());
100: } else {
101: return new CombinedNodeTest(new NameTest(Type.ELEMENT,
102: node.getFingerprint(), node.getNamePool()),
103: Token.INTERSECT, new ContentTypeTest(
104: Type.ELEMENT, node.getConfiguration()
105: .getSchemaType(eltype), node
106: .getConfiguration()));
107: }
108:
109: case Type.ATTRIBUTE:
110: int attype = node.getTypeAnnotation();
111: if (attype == -1
112: || attype == StandardNames.XDT_UNTYPED_ATOMIC) {
113: return new NameTest(Type.ATTRIBUTE, node
114: .getFingerprint(), node.getNamePool());
115: } else {
116: return new CombinedNodeTest(new NameTest(
117: Type.ATTRIBUTE, node.getFingerprint(), node
118: .getNamePool()), Token.INTERSECT,
119: new ContentTypeTest(Type.ATTRIBUTE, node
120: .getConfiguration().getSchemaType(
121: attype), node
122: .getConfiguration()));
123: }
124:
125: case Type.TEXT:
126: return NodeKindTest.TEXT;
127:
128: case Type.COMMENT:
129: return NodeKindTest.COMMENT;
130:
131: case Type.PROCESSING_INSTRUCTION:
132: return NodeKindTest.PROCESSING_INSTRUCTION;
133:
134: case Type.NAMESPACE:
135: return NodeKindTest.NAMESPACE;
136:
137: default:
138: throw new IllegalArgumentException("Unknown node kind "
139: + node.getNodeKind());
140: }
141: }
142:
143: /**
144: * Determine the static cardinality
145: */
146:
147: public int getCardinality() {
148: if (node == null) {
149: return StaticProperty.EMPTY;
150: } else {
151: return StaticProperty.EXACTLY_ONE;
152: }
153: }
154:
155: /**
156: * Get the length of the sequence
157: */
158:
159: public int getLength() throws XPathException {
160: return (node == null ? 0 : 1);
161: }
162:
163: /**
164: * Get the n'th item in the sequence (starting from 0). This is defined for all
165: * SequenceValues, but its real benefits come for a SequenceValue stored extensionally
166: * (or for a MemoClosure, once all the values have been read)
167: */
168:
169: public Item itemAt(int n) throws XPathException {
170: if (n == 0 && node != null) {
171: return node;
172: } else {
173: return null;
174: }
175:
176: }
177:
178: /**
179: * Get the node that forms the node-set. Return null if there is none.
180: */
181:
182: public NodeInfo getNode() {
183: return node;
184: }
185:
186: /**
187: * Get the static properties of this expression (other than its type). The result is
188: * bit-signficant. These properties are used for optimizations. In general, if
189: * property bit is set, it is true, but if it is unset, the value is unknown.
190: */
191:
192: public int getSpecialProperties() {
193: return StaticProperty.ORDERED_NODESET
194: | StaticProperty.NON_CREATIVE;
195: }
196:
197: /**
198: * Return an enumeration of this nodeset value.
199: */
200:
201: public SequenceIterator iterate(XPathContext context) {
202: return SingletonIterator.makeIterator(node);
203: }
204:
205: /**
206: * Evaluate as an item
207: */
208:
209: public Item evaluateItem(XPathContext context) {
210: return node;
211: }
212:
213: /**
214: * Get the effective boolean value
215: */
216:
217: public boolean effectiveBooleanValue(XPathContext context) {
218: return (node != null);
219: }
220:
221: /**
222: * Convert the value to a string, using the serialization rules.
223: * For atomic values this is the same as a cast; for sequence values
224: * it gives a space-separated list. For QNames and NOTATIONS, or lists
225: * containing them, it fails.
226: */
227:
228: public String getStringValue() {
229: return (node == null ? "" : node.getStringValue());
230: }
231:
232: /**
233: * Evaluate an expression as a String. This function must only be called in contexts
234: * where it is known that the expression will return a single string (or where an empty sequence
235: * is to be treated as a zero-length string). Implementations should not attempt to convert
236: * the result to a string, other than converting () to "". This method is used mainly to
237: * evaluate expressions produced by compiling an attribute value template.
238: *
239: * @exception XPathException if any dynamic error occurs evaluating the
240: * expression
241: * @param context The context in which the expression is to be evaluated
242: * @return the value of the expression, evaluated in the current context.
243: * The expression must return a string or (); if the value of the
244: * expression is (), this method returns "".
245: */
246:
247: public String evaluateAsString(XPathContext context)
248: throws XPathException {
249: if (node == null)
250: return "";
251: return node.getStringValue();
252: }
253:
254: /**
255: * Diagnostic display
256: */
257:
258: public void display(int depth, NamePool pool, PrintStream out) {
259: if (node == null) {
260: out
261: .println(ExpressionTool.indent(depth)
262: + "Empty node-set");
263: } else {
264: out.println(ExpressionTool.indent(depth) + "Node "
265: + Navigator.getPath(node));
266: }
267: }
268:
269: /**
270: * Convert to Java object (for passing to external functions)
271: */
272:
273: public Object convertToJava(Class target, XPathContext context)
274: throws XPathException {
275: if (node == null) {
276: return null;
277: }
278: if (target.isAssignableFrom(node.getClass())) {
279: return node;
280: }
281: if (target == String.class) {
282: return node.getStringValue();
283: }
284: return super .convertToJava(target, context);
285: }
286:
287: }
288:
289: //
290: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
291: // you may not use this file except in compliance with the License. You may obtain a copy of the
292: // License at http://www.mozilla.org/MPL/
293: //
294: // Software distributed under the License is distributed on an "AS IS" basis,
295: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
296: // See the License for the specific language governing rights and limitations under the License.
297: //
298: // The Original Code is: all this file.
299: //
300: // The Initial Developer of the Original Code is Michael H. Kay.
301: //
302: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
303: //
304: // Contributor(s): none.
305: //
|