View Javadoc

1   /*
2    * $Header$
3    * $Revision: 1128 $
4    * $Date: 2006-02-05 13:49:04 -0800 (Sun, 05 Feb 2006) $
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: NumberFunction.java 1128 2006-02-05 21:49:04Z elharo $
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 }