1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62 package org.jaxen.function;
63
64 import java.util.Iterator;
65 import java.util.List;
66
67 import org.jaxen.Context;
68 import org.jaxen.Function;
69 import org.jaxen.FunctionCallException;
70 import org.jaxen.Navigator;
71
72 /***
73 * <p>
74 * <b>4.4</b> <code><i>number</i> number(<i>object</i>)</code>
75 *
76 *
77 * <blockquote src="http://www.w3.org/TR/xpath#function-number">
78 * <p>
79 * The <b>number</b> function converts
80 * its argument to a number as follows:
81 * </p>
82 *
83 * <ul>
84 *
85 * <li>
86 * <p>
87 * a string that consists of optional whitespace followed by an optional
88 * minus sign followed by a <a href="#NT-Number">Number</a> followed by
89 * whitespace is converted to the IEEE 754 number that is nearest
90 * (according to the IEEE 754 round-to-nearest rule) to the mathematical
91 * value represented by the string; any other string is converted to NaN
92 * </p>
93 * </li>
94 *
95 * <li>
96 * <p>
97 * boolean true is converted to 1; boolean false is converted to 0
98 * </p>
99 * </li>
100 *
101 * <li>
102 *
103 * <p>
104 * a node-set is first converted to a string as if by a call to the <b><a
105 * href="http://www.w3.org/TR/xpath#function-string" target="_top">string</a></b> function and then converted
106 * in the same way as a string argument
107 * </p>
108 *
109 * </li>
110 *
111 * <li>
112 * <p>
113 * an object of a type other than the four basic types is converted to a
114 * number in a way that is dependent on that type
115 * </p>
116 * </li>
117 *
118 * </ul>
119 *
120 * <p>
121 * If the argument is omitted, it defaults to a node-set with the
122 * context node as its only member.
123 * </p>
124 *
125 * <blockquote> <b>NOTE: </b>The <b>number</b>
126 * function should not be used for conversion of numeric data occurring
127 * in an element in an XML document unless the element is of a type that
128 * represents numeric data in a language-neutral format (which would
129 * typically be transformed into a language-specific format for
130 * presentation to a user). In addition, the <b>number</b> function cannot be used
131 * unless the language-neutral format used by the element is consistent
132 * with the XPath syntax for a <a href="http://www.w3.org/TR/xpath#NT-Number">Number</a>.</blockquote>
133 *
134 * </blockquote>
135 *
136 * @author bob mcwhirter (bob @ werken.com)
137 *
138 * @see <a href="http://www.w3.org/TR/xpath#function-number"
139 * target="_top">Section 4.4 of the XPath Specification</a>
140 */
141 public class NumberFunction implements Function
142 {
143
144 private final static Double NaN = new Double( Double.NaN );
145
146
147 /***
148 * Create a new <code>NumberFunction</code> object.
149 */
150 public NumberFunction() {}
151
152 /***
153 * Returns the number value of <code>args.get(0)</code>,
154 * or the number value of the context node if <code>args</code>
155 * is empty.
156 *
157 * @param context the context at the point in the
158 * expression when the function is called
159 * @param args a list containing the single item to be converted to a
160 * <code>Double</code>
161 *
162 * @return a <code>Double</code>
163 *
164 * @throws FunctionCallException if <code>args</code> has more than one item
165 */
166 public Object call(Context context, List args) throws FunctionCallException
167 {
168 if (args.size() == 1)
169 {
170 return evaluate( args.get(0), context.getNavigator() );
171 }
172 else if (args.size() == 0)
173 {
174 return evaluate( context.getNodeSet(), context.getNavigator() );
175 }
176
177 throw new FunctionCallException( "number() takes at most one argument." );
178 }
179
180 /***
181 * Returns the number value of <code>obj</code>.
182 *
183 * @param obj the object to be converted to a number
184 * @param nav the <code>Navigator</code> used to calculate the string-value
185 * of node-sets
186 *
187 * @return a <code>Double</code>
188 */
189 public static Double evaluate(Object obj, Navigator nav)
190 {
191 if( obj instanceof Double )
192 {
193 return (Double) obj;
194 }
195 else if ( obj instanceof String )
196 {
197 String str = (String) obj;
198 try
199 {
200 Double doubleValue = new Double( str );
201 return doubleValue;
202 }
203 catch (NumberFormatException e)
204 {
205 return NaN;
206 }
207 }
208 else if ( obj instanceof List || obj instanceof Iterator )
209 {
210 return evaluate( StringFunction.evaluate( obj, nav ), nav );
211 }
212 else if ( nav.isElement( obj ) || nav.isAttribute( obj ) )
213 {
214 return evaluate( StringFunction.evaluate( obj, nav ), nav );
215 }
216 else if ( obj instanceof Boolean )
217 {
218 if ( obj == Boolean.TRUE )
219 {
220 return new Double( 1 );
221 }
222 else
223 {
224 return new Double( 0 );
225 }
226 }
227 return NaN;
228 }
229
230 /***
231 * Determines whether the argument is not a number (NaN) as defined
232 * by IEEE 754.
233 *
234 * @param val the double to test
235 * @return true if the value is NaN, false otherwise
236 */
237 public static boolean isNaN( double val )
238 {
239 return Double.isNaN(val);
240 }
241
242 /***
243 * Determines whether the argument is not a number (NaN) as defined
244 * by IEEE 754.
245 *
246 * @param val the <code>Double</code> to test
247 * @return true if the value is NaN, false otherwise
248 */
249 public static boolean isNaN( Double val )
250 {
251 return val.equals( NaN );
252 }
253
254 }