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