package client.net.sf.saxon.ce.expr; import client.net.sf.saxon.ce.om.SequenceIterator; import client.net.sf.saxon.ce.trans.XPathException; import client.net.sf.saxon.ce.tree.iter.EmptyIterator; import client.net.sf.saxon.ce.tree.iter.ListIterator; import client.net.sf.saxon.ce.type.BuiltInAtomicType; import client.net.sf.saxon.ce.type.ItemType; import client.net.sf.saxon.ce.type.TypeHierarchy; import client.net.sf.saxon.ce.value.*; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; /** * A RangeExpression is an expression that represents an integer sequence as * a pair of end-points (for example "x to y"). * If the end-points are equal, the sequence is of length one. * <p>From Saxon 7.8, the sequence must be ascending; if the end-point is less * than the start-point, an empty sequence is returned. This is to allow * expressions of the form "for $i in 1 to count($seq) return ...." </p> */ public class RangeExpression extends BinaryExpression { /** * Construct a RangeExpression * @param start expression that computes the start of the range * @param op represents the operator "to", needed only because this class is a subclass of * BinaryExpression which needs an operator * @param end expression that computes the end of the range */ public RangeExpression(Expression start, int op, Expression end) { super(start, op, end); } /** * Type-check the expression */ public Expression typeCheck(ExpressionVisitor visitor, ItemType contextItemType) throws XPathException { operand0 = visitor.typeCheck(operand0, contextItemType); operand1 = visitor.typeCheck(operand1, contextItemType); boolean backCompat = visitor.getStaticContext().isInBackwardsCompatibleMode(); RoleLocator role0 = new RoleLocator(RoleLocator.BINARY_EXPR, "to", 0); //role0.setSourceLocator(this); operand0 = TypeChecker.staticTypeCheck( operand0, SequenceType.OPTIONAL_INTEGER, backCompat, role0, visitor); RoleLocator role1 = new RoleLocator(RoleLocator.BINARY_EXPR, "to", 1); //role1.setSourceLocator(this); operand1 = TypeChecker.staticTypeCheck( operand1, SequenceType.OPTIONAL_INTEGER, backCompat, role1, visitor); return makeConstantRange(); } /** * 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 XPathException if an error is discovered during this phase * (typically a type error) */ public Expression optimize(ExpressionVisitor visitor, ItemType contextItemType) throws XPathException { operand0 = visitor.optimize(operand0, contextItemType); operand1 = visitor.optimize(operand1, contextItemType); return makeConstantRange(); } private Expression makeConstantRange() throws XPathException { if (operand0 instanceof Literal && operand1 instanceof Literal) { Value v0 = ((Literal)operand0).getValue(); Value v1 = ((Literal)operand1).getValue(); if (v0 instanceof IntegerValue && v1 instanceof IntegerValue && ((IntegerValue) v0).getDecimalValue().compareTo(DecimalValue.BIG_DECIMAL_MAX_INT) < 0 && ((IntegerValue) v0).getDecimalValue().compareTo(DecimalValue.BIG_DECIMAL_MIN_INT) > 0 && ((IntegerValue) v1).getDecimalValue().compareTo(DecimalValue.BIG_DECIMAL_MAX_INT) < 0 && ((IntegerValue) v1).getDecimalValue().compareTo(DecimalValue.BIG_DECIMAL_MIN_INT) > 0) { int i0 = ((IntegerValue)v0).intValue(); int i1 = ((IntegerValue)v1).intValue(); Literal result; if (i0 > i1) { result = Literal.makeEmptySequence(); } else if (i0 == i1) { result = Literal.makeLiteral(new IntegerValue(new BigDecimal(i0))); } else { result = Literal.makeLiteral(new IntegerRange(i0, i1)); } ExpressionTool.copyLocationInfo(this, result); return result; } } return this; } /** * Get the data type of the items returned * @param th the type hierarchy cache */ public ItemType getItemType(TypeHierarchy th) { return BuiltInAtomicType.INTEGER; } /** * Determine the static cardinality */ public int computeCardinality() { return StaticProperty.ALLOWS_ZERO_OR_MORE; } /** * Return an iteration over the sequence */ public SequenceIterator iterate(XPathContext context) throws XPathException { AtomicValue av1 = (AtomicValue)operand0.evaluateItem(context); if (av1 == null) { return EmptyIterator.getInstance(); } NumericValue v1 = (NumericValue)av1; AtomicValue av2 = (AtomicValue)operand1.evaluateItem(context); if (av2 == null) { return EmptyIterator.getInstance(); } NumericValue v2 = (NumericValue)av2; if (v1.compareTo(v2) > 0) { return EmptyIterator.getInstance(); } try { return new RangeIterator(v1.intValue(), v2.intValue()); } catch (XPathException err) { // values out of range for int; not much hope, but we'll try the hard way BigDecimal ds = v1.getDecimalValue(); BigDecimal de = v2.getDecimalValue(); List<IntegerValue> list = new ArrayList<IntegerValue>(); do { list.add(new IntegerValue(ds)); ds = ds.add(BigDecimal.ONE); } while (ds.compareTo(de) <= 0); return new ListIterator(list); } } } // 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.