|
21 | 21 | */
|
22 | 22 | package org.exist.xquery.functions.fn;
|
23 | 23 |
|
24 |
| -import org.exist.dom.QName; |
25 | 24 | import org.exist.xquery.Cardinality;
|
26 |
| -import org.exist.xquery.Dependency; |
27 |
| -import org.exist.xquery.Function; |
28 | 25 | import org.exist.xquery.FunctionSignature;
|
29 |
| -import org.exist.xquery.Profiler; |
30 | 26 | import org.exist.xquery.XPathException;
|
31 | 27 | import org.exist.xquery.XQueryContext;
|
32 |
| -import org.exist.xquery.value.FunctionParameterSequenceType; |
33 |
| -import org.exist.xquery.value.FunctionReturnSequenceType; |
34 |
| -import org.exist.xquery.value.Item; |
35 |
| -import org.exist.xquery.value.NumericValue; |
36 |
| -import org.exist.xquery.value.Sequence; |
37 |
| -import org.exist.xquery.value.SequenceType; |
38 |
| -import org.exist.xquery.value.Type; |
| 28 | +import org.exist.xquery.value.*; |
39 | 29 |
|
40 |
| -public class FunRound extends Function { |
| 30 | +import java.math.RoundingMode; |
41 | 31 |
|
42 |
| - public final static FunctionSignature signature = |
43 |
| - new FunctionSignature( |
44 |
| - new QName("round", Function.BUILTIN_FUNCTION_NS), |
45 |
| - "Returns the number with no fractional part that is closest " + |
46 |
| - "to the argument $arg. If there are two such numbers, then the one " + |
47 |
| - "that is closest to positive infinity is returned. If type of " + |
48 |
| - "$arg is one of the four numeric types xs:float, xs:double, " + |
49 |
| - "xs:decimal or xs:integer the type of the result is the same " + |
50 |
| - "as the type of $arg. If the type of $arg is a type derived " + |
51 |
| - "from one of the numeric types, the result is an instance of " + |
52 |
| - "the base numeric type.\n\n" + |
53 |
| - "For xs:float and xs:double arguments, if the argument is " + |
54 |
| - "positive infinity, then positive infinity is returned. " + |
55 |
| - "If the argument is negative infinity, then negative infinity " + |
56 |
| - "is returned. If the argument is positive zero, then positive " + |
57 |
| - "zero is returned. If the argument is negative zero, then " + |
58 |
| - "negative zero is returned. If the argument is less than zero, " + |
59 |
| - "but greater than or equal to -0.5, then negative zero is returned. " + |
60 |
| - "In the cases where positive zero or negative zero is returned, " + |
61 |
| - "negative zero or positive zero may be returned as " + |
62 |
| - "[XML Schema Part 2: Datatypes Second Edition] does not " + |
63 |
| - "distinguish between the values positive zero and negative zero.", |
64 |
| - new SequenceType[] { new FunctionParameterSequenceType("arg", Type.NUMBER, Cardinality.ZERO_OR_ONE, "The input number") }, |
65 |
| - new FunctionReturnSequenceType(Type.NUMBER, Cardinality.ZERO_OR_ONE, "the rounded value") |
66 |
| - ); |
67 |
| - |
68 |
| - public FunRound(XQueryContext context) { |
| 32 | +import static org.exist.xquery.FunctionDSL.optParam; |
| 33 | +import static org.exist.xquery.functions.fn.FnModule.functionSignature; |
| 34 | + |
| 35 | +/** |
| 36 | + * Implement fn:round() function |
| 37 | + * |
| 38 | + * Shares a base class and evaluator with {@link FunRoundHalfToEven} |
| 39 | + * They differ only in the rounding mode used. |
| 40 | + */ |
| 41 | +public class FunRound extends FunRoundBase { |
| 42 | + |
| 43 | + private static final String FN_NAME = "round"; |
| 44 | + private static final String description = "The function returns the nearest (that is, numerically closest) " + |
| 45 | + "value to $arg that is a multiple of ten to the power of minus $precision. " + |
| 46 | + "If two such values are equally near (for example, if the fractional part in $arg is exactly .5), " + |
| 47 | + "the function returns the one that is closest to positive infinity. " + |
| 48 | + "For the four types xs:float, xs:double, xs:decimal and xs:integer, " + |
| 49 | + "it is guaranteed that if the type of $arg is an instance of type T " + |
| 50 | + "then the result will also be an instance of T. " + |
| 51 | + "The result may also be an instance of a type derived from one of these four by restriction. " + |
| 52 | + "For example, if $arg is an instance of xs:decimal and $precision is less than one, " + |
| 53 | + "then the result may be an instance of xs:integer. " + |
| 54 | + "The single-argument version of this function produces the same result " + |
| 55 | + "as the two-argument version with $precision=0 (that is, it rounds to a whole number). " + |
| 56 | + "When $arg is of type xs:float and xs:double: " + |
| 57 | + "If $arg is NaN, positive or negative zero, or positive or negative infinity, " + |
| 58 | + "then the result is the same as the argument. " + |
| 59 | + "For other values, the argument is cast to xs:decimal " + |
| 60 | + "using an implementation of xs:decimal that imposes no limits on the number of digits " + |
| 61 | + "that can be represented. The function is applied to this xs:decimal value, " + |
| 62 | + "and the resulting xs:decimal is cast back to xs:float or xs:double as appropriate " + |
| 63 | + "to form the function result. If the resulting xs:decimal value is zero, " + |
| 64 | + "then positive or negative zero is returned according to the sign of $arg."; |
| 65 | + private static final FunctionReturnSequenceType returnType = new FunctionReturnSequenceType(Type.NUMBER, Cardinality.ZERO_OR_ONE, "the rounded value"); |
| 66 | + |
| 67 | + public static final FunctionSignature[] FN_ROUND_SIGNATURES = { |
| 68 | + functionSignature(FN_NAME, FunRound.description, FunRound.returnType, |
| 69 | + optParam("arg", Type.NUMBER, "The input number")), |
| 70 | + functionSignature(FN_NAME, FunRound.description, FunRound.returnType, |
| 71 | + optParam("arg", Type.NUMBER, "The input number"), |
| 72 | + optParam("precision", Type.INTEGER, "The input number")) |
| 73 | + }; |
| 74 | + |
| 75 | + public FunRound(final XQueryContext context, final FunctionSignature signature) { |
69 | 76 | super(context, signature);
|
70 | 77 | }
|
71 | 78 |
|
72 | 79 | public int returnsType() {
|
73 | 80 | return Type.NUMBER;
|
74 | 81 | }
|
75 | 82 |
|
76 |
| - public Sequence eval(Sequence contextSequence, Item contextItem) throws XPathException { |
77 |
| - if (context.getProfiler().isEnabled()) { |
78 |
| - context.getProfiler().start(this); |
79 |
| - context.getProfiler().message(this, Profiler.DEPENDENCIES, "DEPENDENCIES", Dependency.getDependenciesName(this.getDependencies())); |
80 |
| - if (contextSequence != null) |
81 |
| - {context.getProfiler().message(this, Profiler.START_SEQUENCES, "CONTEXT SEQUENCE", contextSequence);} |
82 |
| - if (contextItem != null) |
83 |
| - {context.getProfiler().message(this, Profiler.START_SEQUENCES, "CONTEXT ITEM", contextItem.toSequence());} |
84 |
| - } |
85 |
| - |
86 |
| - if (contextItem != null) |
87 |
| - {contextSequence = contextItem.toSequence();} |
88 |
| - |
89 |
| - Sequence result; |
90 |
| - final Sequence seq = getArgument(0).eval(contextSequence, contextItem); |
91 |
| - if (seq.isEmpty()) |
92 |
| - {result = Sequence.EMPTY_SEQUENCE;} |
93 |
| - else { |
94 |
| - final Item item = seq.itemAt(0); |
95 |
| - NumericValue value; |
96 |
| - if (item instanceof NumericValue) { |
97 |
| - value = (NumericValue) item; |
98 |
| - } else { |
99 |
| - value = (NumericValue) item.convertTo(Type.NUMBER); |
100 |
| - } |
101 |
| - result = value.round(); |
102 |
| - } |
| 83 | + /** |
| 84 | + * Work out the rounding mode for a particular value using fn:round |
| 85 | + * |
| 86 | + * @param value that has to be rounded |
| 87 | + * @return the rounding mode to use on this value |
| 88 | + */ |
| 89 | + @Override protected final RoundingMode getFunctionRoundingMode(final NumericValue value) { |
103 | 90 |
|
104 |
| - if (context.getProfiler().isEnabled()) |
105 |
| - {context.getProfiler().end(this, "", result);} |
106 |
| - |
107 |
| - return result; |
| 91 | + if (value.isNegative()) { |
| 92 | + return RoundingMode.HALF_DOWN; |
| 93 | + } else { |
| 94 | + return RoundingMode.HALF_UP; |
| 95 | + } |
108 | 96 | }
|
109 | 97 | }
|
0 commit comments