001: package net.sf.saxon.expr;
002:
003: import net.sf.saxon.om.Item;
004: import net.sf.saxon.om.NamePool;
005: import net.sf.saxon.om.NodeInfo;
006: import net.sf.saxon.om.SequenceIterator;
007: import net.sf.saxon.pattern.NoNodeTest;
008: import net.sf.saxon.pattern.NodeTest;
009: import net.sf.saxon.trans.XPathException;
010: import net.sf.saxon.type.*;
011: import net.sf.saxon.value.AtomicValue;
012: import net.sf.saxon.value.EmptySequence;
013: import net.sf.saxon.value.Value;
014: import net.sf.saxon.instruct.Executable;
015: import net.sf.saxon.Configuration;
016:
017: /**
018: * A SingletonAtomizer combines the functions of an Atomizer and a CardinalityChecker: it is used to
019: * atomize a sequence of nodes, checking that the result of the atomization contains zero or one atomic
020: * values. Note that the input may be a sequence of nodes or atomic values, even though the result must
021: * contain at most one atomic value.
022: */
023:
024: public final class SingletonAtomizer extends UnaryExpression {
025:
026: private boolean allowEmpty;
027: private RoleLocator role;
028:
029: /**
030: * Constructor
031: * @param sequence the sequence to be atomized
032: * @param allowEmpty true if the result sequence is allowed to be empty.
033: */
034:
035: public SingletonAtomizer(Expression sequence, RoleLocator role,
036: boolean allowEmpty) {
037: super (sequence);
038: this .allowEmpty = allowEmpty;
039: this .role = role;
040: }
041:
042: /**
043: * Simplify an expression
044: */
045:
046: public Expression simplify(StaticContext env) throws XPathException {
047: operand = operand.simplify(env);
048: if (operand instanceof AtomicValue) {
049: return operand;
050: }
051: return this ;
052: }
053:
054: /**
055: * Type-check the expression
056: */
057:
058: public Expression typeCheck(StaticContext env,
059: ItemType contextItemType) throws XPathException {
060: operand = operand.typeCheck(env, contextItemType);
061: resetStaticProperties();
062: if (operand instanceof EmptySequence) {
063: if (!allowEmpty) {
064: typeError("An empty sequence is not allowed as the "
065: + role.getMessage(), role.getErrorCode(), null);
066: }
067: return operand;
068: }
069: final TypeHierarchy th = env.getNamePool().getTypeHierarchy();
070: if (operand.getItemType(th).isAtomicType()) {
071: return operand;
072: }
073: return this ;
074: }
075:
076: /**
077: * Determine the special properties of this expression
078: * @return {@link StaticProperty#NON_CREATIVE}.
079: */
080:
081: public int computeSpecialProperties() {
082: int p = super .computeSpecialProperties();
083: return p | StaticProperty.NON_CREATIVE;
084: }
085:
086: /**
087: * Evaluate as an Item. This should only be called if the Atomizer has cardinality zero-or-one,
088: * which will only be the case if the underlying expression has cardinality zero-or-one.
089: */
090:
091: public Item evaluateItem(XPathContext context)
092: throws XPathException {
093: int found = 0;
094: Item result = null;
095: SequenceIterator iter = operand.iterate(context);
096: while (true) {
097: Item item = iter.next();
098: if (item == null) {
099: break;
100: }
101: if (item instanceof AtomicValue) {
102: if (found++ > 0) {
103: typeError(
104: "A sequence of more than one item is not allowed as the "
105: + role.getMessage(), role
106: .getErrorCode(), context);
107: }
108: result = item;
109: } else {
110: Value value = ((NodeInfo) item).atomize();
111: found += value.getLength();
112: if (found > 1) {
113: typeError(
114: "A sequence of more than one item is not allowed as the "
115: + role.getMessage(), role
116: .getErrorCode(), context);
117: }
118: result = value.itemAt(0);
119: }
120: }
121: if (found == 0 && !allowEmpty) {
122: typeError("An empty sequence is not allowed as the "
123: + role.getMessage(), role.getErrorCode(), null);
124: }
125: return result;
126: }
127:
128: /**
129: * Determine the data type of the items returned by the expression, if possible
130: * @return a value such as Type.STRING, Type.BOOLEAN, Type.NUMBER. For this class, the
131: * result is always an atomic type, but it might be more specific.
132: * @param th
133: */
134:
135: public ItemType getItemType(TypeHierarchy th) {
136: boolean isSchemaAware = true;
137: Executable exec = getExecutable();
138: if (exec != null
139: && !exec.getConfiguration().isSchemaAware(
140: Configuration.XML_SCHEMA)) {
141: isSchemaAware = false;
142: }
143: ItemType in = operand.getItemType(th);
144: if (in instanceof AtomicType) {
145: return in;
146: }
147: if (in instanceof NodeTest) {
148:
149: if (in instanceof NoNodeTest) {
150: return in;
151: }
152:
153: int kinds = ((NodeTest) in).getNodeKindMask();
154: if (!isSchemaAware) {
155: // Some node-kinds always have a typed value that's a string
156:
157: if ((kinds | STRING_KINDS) == STRING_KINDS) {
158: return Type.STRING_TYPE;
159: }
160: // Some node-kinds are always untyped atomic; some are untypedAtomic provided that the configuration
161: // is untyped
162:
163: if ((kinds | UNTYPED_IF_UNTYPED_KINDS) == UNTYPED_IF_UNTYPED_KINDS) {
164: return Type.UNTYPED_ATOMIC_TYPE;
165: }
166: } else {
167: if ((kinds | UNTYPED_KINDS) == UNTYPED_KINDS) {
168: return Type.UNTYPED_ATOMIC_TYPE;
169: }
170: }
171:
172: SchemaType schemaType = ((NodeTest) in).getContentType();
173: if (schemaType instanceof SimpleType) {
174: return ((SimpleType) schemaType).getCommonAtomicType();
175: } else if (((ComplexType) schemaType).isSimpleContent()) {
176: return ((ComplexType) schemaType)
177: .getSimpleContentType().getCommonAtomicType();
178: } else if (schemaType instanceof AnyType) {
179: // AnyType includes AnySimpleType as a subtype, so the atomized value can be any atomic type
180: // including untypedAtomic
181: return Type.ANY_ATOMIC_TYPE;
182: } else {
183: // if a complex type with complex content (other than AnyType) can be atomized at all,
184: // then it will return untypedAtomic values
185: return Type.UNTYPED_ATOMIC_TYPE;
186: }
187: }
188: return Type.ANY_ATOMIC_TYPE;
189: }
190:
191: /**
192: * Node kinds whose typed value is always a string
193: */
194: private static final int STRING_KINDS = (1 << Type.NAMESPACE)
195: | (1 << Type.COMMENT) | (1 << Type.PROCESSING_INSTRUCTION);
196:
197: /**
198: * Node kinds whose typed value is always untypedAtomic
199: */
200:
201: private static final int UNTYPED_KINDS = (1 << Type.TEXT)
202: | (1 << Type.DOCUMENT);
203:
204: /**
205: * Node kinds whose typed value is untypedAtomic if the configuration is untyped
206: */
207:
208: private static final int UNTYPED_IF_UNTYPED_KINDS = (1 << Type.TEXT)
209: | (1 << Type.ELEMENT)
210: | (1 << Type.DOCUMENT)
211: | (1 << Type.ATTRIBUTE);
212:
213: /**
214: * Determine the static cardinality of the expression
215: */
216:
217: public int computeCardinality() {
218: if (allowEmpty) {
219: return StaticProperty.ALLOWS_ZERO_OR_ONE;
220: } else {
221: return StaticProperty.EXACTLY_ONE;
222: }
223: }
224:
225: /**
226: * Give a string representation of the operator for use in diagnostics
227: * @return the operator, as a string
228: */
229:
230: protected String displayOperator(NamePool pool) {
231: return "atomize singleton";
232: }
233:
234: }
235:
236: //
237: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
238: // you may not use this file except in compliance with the License. You may obtain a copy of the
239: // License at http://www.mozilla.org/MPL/
240: //
241: // Software distributed under the License is distributed on an "AS IS" basis,
242: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
243: // See the License for the specific language governing rights and limitations under the License.
244: //
245: // The Original Code is: all this file.
246: //
247: // The Initial Developer of the Original Code is Michael H. Kay
248: //
249: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
250: //
251: // Contributor(s): none.
252: //
|