001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: ExsltMath.java 468639 2006-10-28 06:52:33Z minchau $
020     */
021    package org.apache.xalan.lib;
022    
023    import org.apache.xpath.NodeSet;
024    
025    import org.w3c.dom.Node;
026    import org.w3c.dom.NodeList;
027    
028    /**
029     * This class contains EXSLT math extension functions.
030     * It is accessed by specifying a namespace URI as follows:
031     * <pre>
032     *    xmlns:math="http://exslt.org/math"
033     * </pre>
034     * 
035     * The documentation for each function has been copied from the relevant
036     * EXSLT Implementer page.
037     * 
038     * @see <a href="http://www.exslt.org/">EXSLT</a>
039    
040     * @xsl.usage general
041     */
042    public class ExsltMath extends ExsltBase
043    {
044      // Constants
045      private static String PI = "3.1415926535897932384626433832795028841971693993751";
046      private static String E  = "2.71828182845904523536028747135266249775724709369996";
047      private static String SQRRT2 = "1.41421356237309504880168872420969807856967187537694";
048      private static String LN2 = "0.69314718055994530941723212145817656807550013436025";
049      private static String LN10 = "2.302585092994046";
050      private static String LOG2E = "1.4426950408889633";
051      private static String SQRT1_2 = "0.7071067811865476";
052            
053      /**
054       * The math:max function returns the maximum value of the nodes passed as the argument. 
055       * The maximum value is defined as follows. The node set passed as an argument is sorted 
056       * in descending order as it would be by xsl:sort with a data type of number. The maximum 
057       * is the result of converting the string value of the first node in this sorted list to 
058       * a number using the number function. 
059       * <p>
060       * If the node set is empty, or if the result of converting the string values of any of the 
061       * nodes to a number is NaN, then NaN is returned.
062       * 
063       * @param nl The NodeList for the node-set to be evaluated.
064       * 
065       * @return the maximum value found, NaN if any node cannot be converted to a number.
066       * 
067       * @see <a href="http://www.exslt.org/">EXSLT</a>
068       */
069      public static double max (NodeList nl)
070      {
071        if (nl == null || nl.getLength() == 0)
072          return Double.NaN;
073          
074        double m = - Double.MAX_VALUE;
075        for (int i = 0; i < nl.getLength(); i++)
076        {
077          Node n = nl.item(i);
078          double d = toNumber(n);
079          if (Double.isNaN(d))
080            return Double.NaN;
081          else if (d > m)
082            m = d;
083        }
084            
085        return m;   
086      }
087    
088      /**
089       * The math:min function returns the minimum value of the nodes passed as the argument. 
090       * The minimum value is defined as follows. The node set passed as an argument is sorted 
091       * in ascending order as it would be by xsl:sort with a data type of number. The minimum 
092       * is the result of converting the string value of the first node in this sorted list to 
093       * a number using the number function. 
094       * <p>
095       * If the node set is empty, or if the result of converting the string values of any of 
096       * the nodes to a number is NaN, then NaN is returned.
097       * 
098       * @param nl The NodeList for the node-set to be evaluated.
099       * 
100       * @return the minimum value found, NaN if any node cannot be converted to a number.
101       * 
102       * @see <a href="http://www.exslt.org/">EXSLT</a>
103       */
104      public static double min (NodeList nl)
105      {
106        if (nl == null || nl.getLength() == 0)
107          return Double.NaN;
108    
109        double m = Double.MAX_VALUE;
110        for (int i = 0; i < nl.getLength(); i++)
111        {
112          Node n = nl.item(i);
113          double d = toNumber(n);
114          if (Double.isNaN(d))
115            return Double.NaN;
116          else if (d < m)
117            m = d;
118        }
119        
120        return m;
121      }
122      
123      /**
124       * The math:highest function returns the nodes in the node set whose value is the maximum 
125       * value for the node set. The maximum value for the node set is the same as the value as 
126       * calculated by math:max. A node has this maximum value if the result of converting its 
127       * string value to a number as if by the number function is equal to the maximum value, 
128       * where the equality comparison is defined as a numerical comparison using the = operator.
129       * <p>
130       * If any of the nodes in the node set has a non-numeric value, the math:max function will 
131       * return NaN. The definition numeric comparisons entails that NaN != NaN. Therefore if any 
132       * of the nodes in the node set has a non-numeric value, math:highest will return an empty 
133       * node set. 
134       * 
135       * @param nl The NodeList for the node-set to be evaluated.
136       * 
137       * @return node-set with nodes containing the maximum value found, an empty node-set
138       * if any node cannot be converted to a number.
139       */
140      public static NodeList highest (NodeList nl)
141      {        
142        double maxValue = max(nl);
143    
144        NodeSet highNodes = new NodeSet();
145        highNodes.setShouldCacheNodes(true);    
146        
147        if (Double.isNaN(maxValue))
148          return highNodes;  // empty Nodeset
149        
150        for (int i = 0; i < nl.getLength(); i++)
151        {
152          Node n = nl.item(i);
153          double d = toNumber(n); 
154          if (d == maxValue)
155            highNodes.addElement(n);
156        }
157        return highNodes;
158      }
159      
160      /**
161       * The math:lowest function returns the nodes in the node set whose value is the minimum value 
162       * for the node set. The minimum value for the node set is the same as the value as calculated 
163       * by math:min. A node has this minimum value if the result of converting its string value to 
164       * a number as if by the number function is equal to the minimum value, where the equality 
165       * comparison is defined as a numerical comparison using the = operator.
166       * <p>
167       * If any of the nodes in the node set has a non-numeric value, the math:min function will return 
168       * NaN. The definition numeric comparisons entails that NaN != NaN. Therefore if any of the nodes 
169       * in the node set has a non-numeric value, math:lowest will return an empty node set.
170       * 
171       * @param nl The NodeList for the node-set to be evaluated.
172       * 
173       * @return node-set with nodes containing the minimum value found, an empty node-set
174       * if any node cannot be converted to a number.
175       * 
176       */
177      public static NodeList lowest (NodeList nl)
178      {
179        double minValue = min(nl);
180    
181        NodeSet lowNodes = new NodeSet();
182        lowNodes.setShouldCacheNodes(true);
183        
184        if (Double.isNaN(minValue))
185          return lowNodes;  // empty Nodeset
186        
187        for (int i = 0; i < nl.getLength(); i++)
188        {
189          Node n = nl.item(i);
190          double d = toNumber(n); 
191          if (d == minValue)
192            lowNodes.addElement(n);
193        }
194        return lowNodes;
195      }
196      
197      /**
198       * The math:abs function returns the absolute value of a number.
199       *
200       * @param num A number
201       * @return The absolute value of the number
202       */
203       public static double abs(double num)
204       {
205         return Math.abs(num);
206       }
207    
208      /**
209       * The math:acos function returns the arccosine value of a number.
210       *
211       * @param num A number
212       * @return The arccosine value of the number
213       */
214       public static double acos(double num)
215       {
216         return Math.acos(num);
217       }
218    
219      /**
220       * The math:asin function returns the arcsine value of a number. 
221       *
222       * @param num A number
223       * @return The arcsine value of the number
224       */
225       public static double asin(double num)
226       {
227         return Math.asin(num);
228       }
229    
230      /**
231       * The math:atan function returns the arctangent value of a number. 
232       *
233       * @param num A number
234       * @return The arctangent value of the number
235       */
236       public static double atan(double num)
237       {
238         return Math.atan(num);
239       }
240      
241      /**
242       * The math:atan2 function returns the angle ( in radians ) from the X axis to a point (y,x). 
243       *
244       * @param num1 The X axis value
245       * @param num2 The Y axis value
246       * @return The angle (in radians) from the X axis to a point (y,x)
247       */
248       public static double atan2(double num1, double num2)
249       {
250         return Math.atan2(num1, num2);
251       }
252    
253      /**
254       * The math:cos function returns cosine of the passed argument. 
255       *
256       * @param num A number
257       * @return The cosine value of the number
258       */
259       public static double cos(double num)
260       {
261         return Math.cos(num);
262       }
263    
264      /**
265       * The math:exp function returns e (the base of natural logarithms) raised to a power. 
266       *
267       * @param num A number
268       * @return The value of e raised to the given power
269       */
270       public static double exp(double num)
271       {
272         return Math.exp(num);
273       }
274    
275      /**
276       * The math:log function returns the natural logarithm of a number. 
277       *
278       * @param num A number
279       * @return The natural logarithm of the number
280       */
281       public static double log(double num)
282       {
283         return Math.log(num);
284       }
285    
286      /**
287       * The math:power function returns the value of a base expression taken to a specified power. 
288       *
289       * @param num1 The base
290       * @param num2 The power
291       * @return The value of the base expression taken to the specified power
292       */
293       public static double power(double num1, double num2)
294       {
295         return Math.pow(num1, num2);
296       }
297    
298      /**
299       * The math:random function returns a random number from 0 to 1. 
300       *
301       * @return A random double from 0 to 1
302       */
303       public static double random()
304       {
305         return Math.random();
306       }
307    
308      /**
309       * The math:sin function returns the sine of the number. 
310       *
311       * @param num A number
312       * @return The sine value of the number
313       */
314       public static double sin(double num)
315       {
316         return Math.sin(num);
317       }
318    
319      /**
320       * The math:sqrt function returns the square root of a number. 
321       *
322       * @param num A number
323       * @return The square root of the number
324       */
325       public static double sqrt(double num)
326       {
327         return Math.sqrt(num);
328       }
329    
330      /**
331       * The math:tan function returns the tangent of the number passed as an argument. 
332       *
333       * @param num A number
334       * @return The tangent value of the number
335       */
336       public static double tan(double num)
337       {
338         return Math.tan(num);
339       }
340    
341      /**
342       * The math:constant function returns the specified constant to a set precision. 
343       * The possible constants are:
344       * <pre>
345       *  PI
346       *  E
347       *  SQRRT2
348       *  LN2
349       *  LN10
350       *  LOG2E
351       *  SQRT1_2
352       * </pre>
353       * @param name The name of the constant
354       * @param precision The precision
355       * @return The value of the specified constant to the given precision
356       */
357       public static double constant(String name, double precision)
358       {
359         String value = null;
360         if (name.equals("PI"))
361           value = PI;
362         else if (name.equals("E"))
363           value = E;
364         else if (name.equals("SQRRT2"))
365           value = SQRRT2;
366         else if (name.equals("LN2"))
367           value = LN2;
368         else if (name.equals("LN10"))
369           value = LN10;
370         else if (name.equals("LOG2E"))
371           value = LOG2E;
372         else if (name.equals("SQRT1_2"))
373           value = SQRT1_2;
374         
375         if (value != null)
376         {
377           int bits = new Double(precision).intValue();
378           
379           if (bits <= value.length())
380             value = value.substring(0, bits);
381             
382           return new Double(value).doubleValue();
383         }
384         else
385           return Double.NaN;
386                
387       }
388          
389    }