package client.net.sf.saxon.ce.expr; import client.net.sf.saxon.ce.expr.instruct.Block; import client.net.sf.saxon.ce.expr.instruct.ValueOf; import client.net.sf.saxon.ce.om.Item; import client.net.sf.saxon.ce.om.NodeInfo; import client.net.sf.saxon.ce.om.SequenceIterator; import client.net.sf.saxon.ce.pattern.EmptySequenceTest; import client.net.sf.saxon.ce.pattern.NodeTest; import client.net.sf.saxon.ce.trans.XPathException; import client.net.sf.saxon.ce.type.BuiltInAtomicType; import client.net.sf.saxon.ce.type.ItemType; import client.net.sf.saxon.ce.type.Type; import client.net.sf.saxon.ce.type.TypeHierarchy; import client.net.sf.saxon.ce.value.AtomicValue; import client.net.sf.saxon.ce.value.Value; /** * An Atomizer is an expression corresponding essentially to the fn:data() function: it * maps a sequence by replacing nodes with their typed values */ public final class Atomizer extends UnaryExpression { /** * Constructor * @param sequence the sequence to be atomized * */ public Atomizer(Expression sequence) { super(sequence); sequence.setFlattened(true); } /** * Simplify an expression * @param visitor an expression visitor */ public Expression simplify(ExpressionVisitor visitor) throws XPathException { operand = visitor.simplify(operand); if (operand instanceof Literal) { Value val = ((Literal)operand).getValue(); if (val instanceof AtomicValue) { return operand; } SequenceIterator iter = val.iterate(); while (true) { // if all items in the sequence are atomic (they generally will be, since this is // done at compile time), then return the sequence Item i = iter.next(); if (i == null) { return operand; } if (i instanceof NodeInfo) { return this; } } } else if (operand instanceof ValueOf) { // XSLT users tend to use ValueOf unnecessarily return ((ValueOf)operand).convertToCastAsString(); } return this; } /** * Type-check the expression */ public Expression typeCheck(ExpressionVisitor visitor, ItemType contextItemType) throws XPathException { operand = visitor.typeCheck(operand, contextItemType); // If the configuration allows typed data, check whether the content type of these particular nodes is untyped final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy(); visitor.resetStaticProperties(); ItemType operandType = operand.getItemType(th); if (th.isSubType(operandType, BuiltInAtomicType.ANY_ATOMIC)) { return operand; } operand.setFlattened(true); return this; } /** * Perform optimisation of an expression and its subexpressions. * <p/> * <p>This method is called after all references to functions and variables have been resolved * to the declaration of the function or variable, and after all type checking has been done.</p> * * @param visitor an expression visitor * @param contextItemType the static type of "." at the point where this expression is invoked. * The parameter is set to null if it is known statically that the context item will be undefined. * If the type of the context item is not known statically, the argument is set to * {@link client.net.sf.saxon.ce.type.Type#ITEM_TYPE} * @return the original expression, rewritten if appropriate to optimize execution * @throws client.net.sf.saxon.ce.trans.XPathException * if an error is discovered during this phase * (typically a type error) */ public Expression optimize(ExpressionVisitor visitor, ItemType contextItemType) throws XPathException { Expression exp = super.optimize(visitor, contextItemType); if (exp == this) { final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy(); if (th.isSubType(operand.getItemType(th), BuiltInAtomicType.ANY_ATOMIC)) { return operand; } if (operand instanceof ValueOf) { // XSLT users tend to use ValueOf unnecessarily return ((ValueOf)operand).convertToCastAsString(); } if (operand instanceof Block) { // replace atomize((x,y,z)) by (atomize(x), atomize(y), atomize(z)) as some of the atomizers // may prove to be redundant. (Also, it helps streaming) Expression[] children = ((Block)operand).getChildren(); Expression[] atomizedChildren = new Expression[children.length]; for (int i=0; i<children.length; i++) { atomizedChildren[i] = new Atomizer(children[i]); } Block newBlock = new Block(); newBlock.setChildren(atomizedChildren); return newBlock.typeCheck(visitor, contextItemType).optimize(visitor, contextItemType); } } return exp; } /** * Determine the special properties of this expression * @return {@link StaticProperty#NON_CREATIVE}. */ public int computeSpecialProperties() { int p = super.computeSpecialProperties(); return p | StaticProperty.NON_CREATIVE; } /** * Iterate over the sequence of values */ public SequenceIterator iterate(XPathContext context) throws XPathException { SequenceIterator base = operand.iterate(context); return getAtomizingIterator(base); } /** * Evaluate as an Item. This should only be called if the Atomizer has cardinality zero-or-one, * which will only be the case if the underlying expression has cardinality zero-or-one. */ public Item evaluateItem(XPathContext context) throws XPathException { Item i = operand.evaluateItem(context); return (i==null ? null : i.getTypedValue()); } /** * Determine the data type of the items returned by the expression, if possible * @return a value such as Type.STRING, Type.BOOLEAN, Type.NUMBER. For this class, the * result is always an atomic type, but it might be more specific. * @param th the type hierarchy cache */ public ItemType getItemType(TypeHierarchy th) { return getAtomizedItemType(operand, true, th); } /** * Compute the type that will result from atomizing the result of a given expression * @param operand the given expression * @param alwaysUntyped true if it is known that nodes will always be untyped * @param th the type hierarchy cache * @return the item type of the result of evaluating the operand expression, after atomization */ public static ItemType getAtomizedItemType(Expression operand, boolean alwaysUntyped, TypeHierarchy th) { ItemType in = operand.getItemType(th); if (in.isAtomicType()) { return in; } if (in instanceof NodeTest) { if (in instanceof EmptySequenceTest) { return in; } int kinds = ((NodeTest)in).getNodeKindMask(); if (alwaysUntyped) { // Some node-kinds always have a typed value that's a string if ((kinds | STRING_KINDS) == STRING_KINDS) { return BuiltInAtomicType.STRING; } // Some node-kinds are always untyped atomic; some are untypedAtomic provided that the configuration // is untyped if ((kinds | UNTYPED_IF_UNTYPED_KINDS) == UNTYPED_IF_UNTYPED_KINDS) { return BuiltInAtomicType.UNTYPED_ATOMIC; } } else { if ((kinds | UNTYPED_KINDS) == UNTYPED_KINDS) { return BuiltInAtomicType.UNTYPED_ATOMIC; } } return in.getAtomizedItemType(); } return BuiltInAtomicType.ANY_ATOMIC; } /** * Node kinds whose typed value is always a string */ private static final int STRING_KINDS = (1<<Type.NAMESPACE) | (1<<Type.COMMENT) | (1<<Type.PROCESSING_INSTRUCTION); /** * Node kinds whose typed value is always untypedAtomic */ private static final int UNTYPED_KINDS = (1<<Type.TEXT) | (1<<Type.DOCUMENT); /** * Node kinds whose typed value is untypedAtomic if the configuration is untyped */ private static final int UNTYPED_IF_UNTYPED_KINDS = (1<<Type.TEXT) | (1<<Type.ELEMENT) | (1<<Type.DOCUMENT) | (1<<Type.ATTRIBUTE); /** * Determine the static cardinality of the expression */ public int computeCardinality() { return operand.getCardinality(); } /** * Get an iterator that returns the result of atomizing the sequence delivered by the supplied * iterator * @param base the supplied iterator, the input to atomization * @return an iterator that returns atomic values, the result of the atomization */ public static SequenceIterator getAtomizingIterator(SequenceIterator base) throws XPathException { ItemMappingFunction imf = new ItemMappingFunction() { public Item mapItem(Item item) throws XPathException { return item.getTypedValue(); } }; return new ItemMappingIterator(base, imf); } } // 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.