1 /*
2 * $Header$
3 * $Revision$
4 * $Date$
5 *
6 * ====================================================================
7 *
8 * Copyright 2000-2002 bob mcwhirter & James Strachan.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions are
13 * met:
14 *
15 * * Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 *
18 * * Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 *
22 * * Neither the name of the Jaxen Project nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
27 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
29 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
30 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 *
38 * ====================================================================
39 * This software consists of voluntary contributions made by many
40 * individuals on behalf of the Jaxen Project and was originally
41 * created by bob mcwhirter <bob@werken.com> and
42 * James Strachan <jstrachan@apache.org>. For more information on the
43 * Jaxen Project, please see <http://www.jaxen.org/>.
44 *
45 * $Id$
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 cite="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="https://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="https://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="https://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 || nav.isText( obj ) || nav.isComment( obj ) || nav.isProcessingInstruction( obj )
200 || nav.isDocument( obj ) || nav.isNamespace( obj ))
201 {
202 return evaluate( StringFunction.evaluate( obj, nav ), nav );
203 }
204 else if ( obj instanceof Boolean )
205 {
206 if ( Boolean.TRUE.equals(obj) )
207 {
208 return new Double( 1 );
209 }
210 else
211 {
212 return new Double( 0 );
213 }
214 }
215 return NaN;
216 }
217
218 /**
219 * Determines whether the argument is not a number (NaN) as defined
220 * by IEEE 754.
221 *
222 * @param val the double to test
223 * @return true if the value is NaN, false otherwise
224 */
225 public static boolean isNaN( double val )
226 {
227 return Double.isNaN(val);
228 }
229
230 /**
231 * Determines whether the argument is not a number (NaN) as defined
232 * by IEEE 754.
233 *
234 * @param val the <code>Double</code> to test
235 * @return true if the value is NaN, false otherwise
236 */
237 public static boolean isNaN( Double val )
238 {
239 return val.equals( NaN );
240 }
241
242 }