net.sf.saxon.functions.NumberFn Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of saxon-he Show documentation
Show all versions of saxon-he Show documentation
An OSGi bundle for Saxon-HE
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2013 Saxonica Limited.
// 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.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
package net.sf.saxon.functions;
import net.sf.saxon.Configuration;
import net.sf.saxon.expr.Callable;
import net.sf.saxon.expr.Expression;
import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.expr.parser.ExpressionVisitor;
import net.sf.saxon.expr.parser.PathMap;
import net.sf.saxon.lib.ConversionRules;
import net.sf.saxon.om.AtomicSequence;
import net.sf.saxon.om.Item;
import net.sf.saxon.om.NodeInfo;
import net.sf.saxon.om.Sequence;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.*;
import net.sf.saxon.value.*;
import net.sf.saxon.value.StringValue;
/**
* Implements the XPath number() function.
*/
public class NumberFn extends SystemFunctionCall implements Callable {
/**
* Simplify and validate.
* This is a pure function so it can be simplified in advance if the arguments are known
* @param visitor an expression visitor
*/
/*@NotNull*/
public Expression simplify(ExpressionVisitor visitor) throws XPathException {
useContextItemAsDefault(visitor);
argument[0].setFlattened(true);
return simplifyArguments(visitor);
}
/**
* Type-check the expression. This also calls preEvaluate() to evaluate the function
* if all the arguments are constant; functions that do not require this behavior
* can override the preEvaluate method.
*/
/*@NotNull*/
public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
Expression e2 = super.typeCheck(visitor, contextItemType);
if (e2 != this) {
return e2;
}
if (argument[0] instanceof NumberFn) {
// happens through repeated rewriting
argument[0] = ((NumberFn)argument[0]).argument[0];
}
return this;
}
/**
* Add a representation of a doc() call or similar function to a PathMap.
* This is a convenience method called by the addToPathMap() methods for doc(), document(), collection()
* and similar functions. These all create a new root expression in the path map.
*
* @param pathMap the PathMap to which the expression should be added
* @param pathMapNodes the node in the PathMap representing the focus at the point where this expression
* is called. Set to null if this expression appears at the top level.
* @return the pathMapNode representing the focus established by this expression, in the case where this
* expression is the first operand of a path expression or filter expression
*/
/*@Nullable*/ public PathMap.PathMapNodeSet addDocToPathMap(/*@NotNull*/ PathMap pathMap, PathMap.PathMapNodeSet pathMapNodes) {
PathMap.PathMapNodeSet result = argument[0].addToPathMap(pathMap, pathMapNodes);
if (result != null) {
result.setAtomized();
}
return null;
}
/**
* Evaluate in a general context
*/
public DoubleValue evaluateItem(XPathContext context) throws XPathException {
Item arg0 = argument[0].evaluateItem(context);
if (arg0==null) {
return DoubleValue.NaN;
}
ConversionRules rules = context.getConfiguration().getConversionRules();
if (arg0 instanceof BooleanValue) {
return (DoubleValue)Converter.BOOLEAN_TO_DOUBLE.convert(((AtomicValue) arg0)).asAtomic();
} else if (arg0 instanceof NumericValue) {
return (DoubleValue)Converter.NUMERIC_TO_DOUBLE.convert(((AtomicValue) arg0)).asAtomic();
} else if (arg0 instanceof StringValue && !(arg0 instanceof AnyURIValue)) {
StringConverter sc = rules.getStringConverter(BuiltInAtomicType.DOUBLE);
ConversionResult cr = sc.convert((AtomicValue)arg0);
if (cr instanceof ValidationFailure) {
return DoubleValue.NaN;
} else {
return (DoubleValue)cr;
}
} else {
return DoubleValue.NaN;
}
}
/**
* Static method to perform the same conversion as the number() function. This is different from the
* convert(Type.DOUBLE) in that it produces NaN rather than an error for non-numeric operands.
* @param value the value to be converted
* @param config
* @return the result of the conversion
*/
public static DoubleValue convert(AtomicValue value, Configuration config) {
try {
if (value==null) {
return DoubleValue.NaN;
}
if (value instanceof BooleanValue) {
return new DoubleValue(((BooleanValue)value).getBooleanValue() ? 1.0e0 : 0.0e0);
}
if (value instanceof DoubleValue) {
return ((DoubleValue)value);
}
if (value instanceof NumericValue) {
return new DoubleValue(((NumericValue)value).getDoubleValue());
}
if (value instanceof StringValue && !(value instanceof AnyURIValue)) {
double d = config.getConversionRules().getStringToDoubleConverter().stringToNumber(value.getStringValueCS());
return new DoubleValue(d);
}
return DoubleValue.NaN;
} catch (NumberFormatException e) {
return DoubleValue.NaN;
}
}
/**
* Evaluate the expression
*
* @param context the dynamic evaluation context
* @param arguments the values of the arguments, supplied as Sequences
* @return the result of the evaluation, in the form of a Sequence
* @throws net.sf.saxon.trans.XPathException
* if a dynamic error occurs during the evaluation of the expression
*/
public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
AtomicValue in;
if (arguments.length == 0) {
Item c = context.getContextItem();
if (c == null) {
throw new XPathException("Context item for number() is absent");
}
if (c instanceof AtomicValue) {
in = (AtomicValue)c;
} else if (c instanceof NodeInfo) {
AtomicSequence as = ((NodeInfo)c).atomize();
if (as.getLength() == 0) {
return DoubleValue.NaN;
} else if (as.getLength() == 1) {
in = as.head();
} else {
throw new XPathException("Atomization of context item for number() is a sequence containing more than one item", "XPTY0004");
}
} else {
throw new XPathException("Context item for number() cannot be atomized", "XPTY0004");
}
} else {
in = (AtomicValue)arguments[0].head();
if (in == null) {
return EmptySequence.getInstance();
}
}
return convert(in, context.getConfiguration());
}
}