001: package net.sf.saxon.expr;
002:
003: import net.sf.saxon.Configuration;
004: import net.sf.saxon.instruct.Executable;
005: import net.sf.saxon.om.*;
006: import net.sf.saxon.pattern.NodeTest;
007: import net.sf.saxon.pattern.NoNodeTest;
008: import net.sf.saxon.trans.XPathException;
009: import net.sf.saxon.type.*;
010: import net.sf.saxon.value.AtomicValue;
011: import net.sf.saxon.value.Cardinality;
012: import net.sf.saxon.value.Value;
013:
014: /**
015: * An Atomizer is an expression corresponding essentially to the fn:data() function: it
016: * maps a sequence by replacing nodes with their typed values
017: */
018:
019: public final class Atomizer extends UnaryExpression {
020:
021: private boolean untyped; //set to true if it is known that the nodes being atomized will be untyped
022:
023: /**
024: * Constructor
025: * @param sequence the sequence to be atomized
026: * @param config the Configuration. Used only for optimization, may be null. Atomization is faster if
027: * it is known in advance that all nodes will be untyped.
028: */
029:
030: public Atomizer(Expression sequence, Configuration config) {
031: super (sequence);
032: if (config == null) {
033: untyped = false;
034: } else {
035: untyped = config.areAllNodesUntyped();
036: }
037: }
038:
039: /**
040: * Simplify an expression
041: */
042:
043: public Expression simplify(StaticContext env) throws XPathException {
044: if (!env.getConfiguration().isSchemaAware(
045: Configuration.XML_SCHEMA)) {
046: untyped = true;
047: }
048: ;
049: operand = operand.simplify(env);
050: if (operand instanceof AtomicValue) {
051: return operand;
052: } else if (operand instanceof Value) {
053: SequenceIterator iter = operand.iterate(env
054: .makeEarlyEvaluationContext());
055: while (true) {
056: // if all items in the sequence are atomic (they generally will be, since this is
057: // done at compile time), then return the sequence
058: Item i = iter.next();
059: if (i == null) {
060: return operand;
061: }
062: if (i instanceof NodeInfo) {
063: return this ;
064: }
065: }
066: }
067: return this ;
068: }
069:
070: /**
071: * Type-check the expression
072: */
073:
074: public Expression typeCheck(StaticContext env,
075: ItemType contextItemType) throws XPathException {
076: if (!env.getConfiguration().isSchemaAware(
077: Configuration.XML_SCHEMA)) {
078: untyped = true;
079: }
080: ;
081: operand = operand.typeCheck(env, contextItemType);
082: resetStaticProperties();
083: final TypeHierarchy th = env.getNamePool().getTypeHierarchy();
084: if (th.isSubType(operand.getItemType(th), Type.ANY_ATOMIC_TYPE)) {
085: return operand;
086: }
087: return this ;
088: }
089:
090: /**
091: * Determine the special properties of this expression
092: * @return {@link StaticProperty#NON_CREATIVE}.
093: */
094:
095: public int computeSpecialProperties() {
096: int p = super .computeSpecialProperties();
097: return p | StaticProperty.NON_CREATIVE;
098: }
099:
100: /**
101: * Iterate over the sequence of values
102: */
103:
104: public SequenceIterator iterate(XPathContext context)
105: throws XPathException {
106: SequenceIterator base = operand.iterate(context);
107: if ((base.getProperties() & SequenceIterator.ATOMIZABLE) != 0) {
108: ((AtomizableIterator) base).setIsAtomizing(true);
109: }
110: return AtomizingFunction.getAtomizingIterator(base);
111: }
112:
113: /**
114: * Evaluate as an Item. This should only be called if the Atomizer has cardinality zero-or-one,
115: * which will only be the case if the underlying expression has cardinality zero-or-one.
116: */
117:
118: public Item evaluateItem(XPathContext context)
119: throws XPathException {
120: Item i = operand.evaluateItem(context);
121: if (i == null) {
122: return null;
123: }
124: if (i instanceof NodeInfo) {
125: SequenceIterator it = i.getTypedValue();
126: return it.next();
127: } else {
128: return i;
129: }
130: }
131:
132: /**
133: * Implement the mapping function. This is stateless, so there is a singleton instance.
134: */
135:
136: public static class AtomizingFunction implements MappingFunction {
137:
138: /**
139: * Private constructor, ensuring that everyone uses the singleton instance
140: */
141:
142: private AtomizingFunction() {
143: };
144:
145: private static final AtomizingFunction theInstance = new AtomizingFunction();
146:
147: /**
148: * Get the singleton instance
149: * @return the singleton instance of this mapping function
150: */
151:
152: public static AtomizingFunction getInstance() {
153: return theInstance;
154: }
155:
156: public static SequenceIterator getAtomizingIterator(
157: SequenceIterator base) {
158: return new MappingIterator(base, theInstance, null);
159: }
160:
161: public Object map(Item item, XPathContext context)
162: throws XPathException {
163: if (item instanceof NodeInfo) {
164: return item.getTypedValue();
165: } else {
166: return item;
167: }
168: }
169: }
170:
171: /**
172: * Determine the data type of the items returned by the expression, if possible
173: * @return a value such as Type.STRING, Type.BOOLEAN, Type.NUMBER. For this class, the
174: * result is always an atomic type, but it might be more specific.
175: * @param th
176: */
177:
178: public ItemType getItemType(TypeHierarchy th) {
179: return getAtomizedItemType(operand, untyped, th);
180: }
181:
182: /**
183: * Compute the type that will result from atomizing the result of a given expression
184: * @param operand the given expression
185: * @param alwaysUntyped true if it is known that nodes will always be untyped
186: * @return the item type of the result of evaluating the operand expression, after atomization
187: */
188:
189: public static final ItemType getAtomizedItemType(
190: Expression operand, boolean alwaysUntyped, TypeHierarchy th) {
191: ItemType in = operand.getItemType(th);
192: if (in instanceof AtomicType) {
193: return in;
194: }
195: if (in instanceof NodeTest) {
196:
197: if (in instanceof NoNodeTest) {
198: return in;
199: }
200: int kinds = ((NodeTest) in).getNodeKindMask();
201: if (alwaysUntyped) {
202: // Some node-kinds always have a typed value that's a string
203:
204: if ((kinds | STRING_KINDS) == STRING_KINDS) {
205: return Type.STRING_TYPE;
206: }
207: // Some node-kinds are always untyped atomic; some are untypedAtomic provided that the configuration
208: // is untyped
209:
210: if ((kinds | UNTYPED_IF_UNTYPED_KINDS) == UNTYPED_IF_UNTYPED_KINDS) {
211: return Type.UNTYPED_ATOMIC_TYPE;
212: }
213: } else {
214: if ((kinds | UNTYPED_KINDS) == UNTYPED_KINDS) {
215: return Type.UNTYPED_ATOMIC_TYPE;
216: }
217: }
218:
219: SchemaType schemaType = ((NodeTest) in).getContentType();
220: if (schemaType instanceof SimpleType) {
221: return ((SimpleType) schemaType).getCommonAtomicType();
222: } else if (((ComplexType) schemaType).isSimpleContent()) {
223: return ((ComplexType) schemaType)
224: .getSimpleContentType().getCommonAtomicType();
225: } else if (schemaType instanceof AnyType) {
226: // AnyType includes AnySimpleType as a subtype, so the atomized value can be any atomic type
227: // including untypedAtomic
228: return Type.ANY_ATOMIC_TYPE;
229: } else {
230: // if a complex type with complex content (other than AnyType) can be atomized at all,
231: // then it will return untypedAtomic values
232: return Type.UNTYPED_ATOMIC_TYPE;
233: }
234: }
235: return Type.ANY_ATOMIC_TYPE;
236: }
237:
238: /**
239: * Node kinds whose typed value is always a string
240: */
241: private static final int STRING_KINDS = (1 << Type.NAMESPACE)
242: | (1 << Type.COMMENT) | (1 << Type.PROCESSING_INSTRUCTION);
243:
244: /**
245: * Node kinds whose typed value is always untypedAtomic
246: */
247:
248: private static final int UNTYPED_KINDS = (1 << Type.TEXT)
249: | (1 << Type.DOCUMENT);
250:
251: /**
252: * Node kinds whose typed value is untypedAtomic if the configuration is untyped
253: */
254:
255: private static final int UNTYPED_IF_UNTYPED_KINDS = (1 << Type.TEXT)
256: | (1 << Type.ELEMENT)
257: | (1 << Type.DOCUMENT)
258: | (1 << Type.ATTRIBUTE);
259:
260: /**
261: * Determine the static cardinality of the expression
262: */
263:
264: public int computeCardinality() {
265: if (untyped) {
266: return operand.getCardinality();
267: } else {
268: if (Cardinality.allowsMany(operand.getCardinality())) {
269: return StaticProperty.ALLOWS_ZERO_OR_MORE;
270: }
271: final Executable exec = getExecutable();
272: if (exec == null) {
273: return StaticProperty.ALLOWS_ZERO_OR_MORE;
274: }
275: ItemType in = operand.getItemType(exec.getConfiguration()
276: .getNamePool().getTypeHierarchy());
277: if (in instanceof AtomicType) {
278: return operand.getCardinality();
279: }
280: if (in instanceof NodeTest) {
281: SchemaType schemaType = ((NodeTest) in)
282: .getContentType();
283: if (schemaType instanceof AtomicType) {
284: // can return at most one atomic value per node
285: return operand.getCardinality();
286: }
287: }
288: return StaticProperty.ALLOWS_ZERO_OR_MORE;
289: }
290: }
291:
292: /**
293: * Give a string representation of the operator for use in diagnostics
294: * @return the operator, as a string
295: */
296:
297: protected String displayOperator(NamePool pool) {
298: return "atomize";
299: }
300:
301: }
302:
303: //
304: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
305: // you may not use this file except in compliance with the License. You may obtain a copy of the
306: // License at http://www.mozilla.org/MPL/
307: //
308: // Software distributed under the License is distributed on an "AS IS" basis,
309: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
310: // See the License for the specific language governing rights and limitations under the License.
311: //
312: // The Original Code is: all this file.
313: //
314: // The Initial Developer of the Original Code is Michael H. Kay
315: //
316: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
317: //
318: // Contributor(s): none.
319: //
|