KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > velocity > tools > generic > MathTool


1 /*
2  * Copyright 2003-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.apache.velocity.tools.generic;
18
19 /**
20  * <p>Tool for performing math in Velocity.</p>
21  *
22  * <p>Some things should be noted here:</p>
23  * <ul>
24  * <li>This class does not have methods that take
25  * primitives. This is simply because Velocity
26  * wraps all primitives for us automagically.</li>
27  *
28  * <li>No null pointer, number format, or divide by zero
29  * exceptions are thrown here. This is because such exceptions
30  * thrown in template halt rendering. It should be sufficient
31  * debugging feedback that Velocity will render the reference
32  * literally. (e.g. $math.div(1, 0) renders as '$math.div(1, 0)')</li>
33  * </ul>
34  * <p><pre>
35  * Example toolbox.xml config (if you want to use this with VelocityView):
36  * &lt;tool&gt;
37  * &lt;key&gt;math&lt;/key&gt;
38  * &lt;scope&gt;application&lt;/scope&gt;
39  * &lt;class&gt;org.apache.velocity.tools.generic.MathTool&lt;/class&gt;
40  * &lt;/tool&gt;
41  * </pre></p>
42  *
43  * @author <a HREF="mailto:nathan@esha.com">Nathan Bubna</a>
44  * @version $Revision: 1.6.2.1 $ $Date: 2004/03/12 20:16:27 $
45  */

46 public class MathTool
47 {
48     /**
49      * @param num1 the first number
50      * @param num2 the second number
51      * @return the sum of the numbers or
52      * <code>null</code> if they're invalid
53      * @see #toNumber
54      */

55     public Number JavaDoc add(Object JavaDoc num1, Object JavaDoc num2)
56     {
57         Number JavaDoc n1 = toNumber(num1);
58         Number JavaDoc n2 = toNumber(num2);
59         if (n1 == null || n2 == null)
60         {
61             return null;
62         }
63         double value = n1.doubleValue() + n2.doubleValue();
64         return matchType(n1, n2, value);
65     }
66
67
68     /**
69      * @param num1 the first number
70      * @param num2 the second number
71      * @return the difference of the numbers or
72      * <code>null</code> if they're invalid
73      * @see #toNumber
74      */

75     public Number JavaDoc sub(Object JavaDoc num1, Object JavaDoc num2)
76     {
77         Number JavaDoc n1 = toNumber(num1);
78         Number JavaDoc n2 = toNumber(num2);
79         if (n1 == null || n2 == null)
80         {
81             return null;
82         }
83         double value = n1.doubleValue() - n2.doubleValue();
84         return matchType(n1, n2, value);
85     }
86
87
88     /**
89      * @param num1 the first number
90      * @param num2 the second number
91      * @return the product of the numbers or
92      * <code>null</code> if they're invalid
93      * @see #toNumber
94      */

95     public Number JavaDoc mul(Object JavaDoc num1, Object JavaDoc num2)
96     {
97         Number JavaDoc n1 = toNumber(num1);
98         Number JavaDoc n2 = toNumber(num2);
99         if (n1 == null || n2 == null)
100         {
101             return null;
102         }
103         double value = n1.doubleValue() * n2.doubleValue();
104         return matchType(n1, n2, value);
105     }
106
107
108     /**
109      * @param num1 the first number
110      * @param num2 the second number
111      * @return the quotient of the numbers or
112      * <code>null</code> if they're invalid
113      * @see #toNumber
114      */

115     public Number JavaDoc div(Object JavaDoc num1, Object JavaDoc num2)
116     {
117         Number JavaDoc n1 = toNumber(num1);
118         Number JavaDoc n2 = toNumber(num2);
119         if (n1 == null || n2 == null || n2.doubleValue() == 0.0)
120         {
121             return null;
122         }
123         double value = n1.doubleValue() / n2.doubleValue();
124         return matchType(n1, n2, value);
125     }
126
127
128     /**
129      * @param num1 the first number
130      * @param num2 the second number
131      * @return the first number raised to the power of the
132      * second or <code>null</code> if they're invalid
133      * @see #toNumber
134      */

135     public Number JavaDoc pow(Object JavaDoc num1, Object JavaDoc num2)
136     {
137         Number JavaDoc n1 = toNumber(num1);
138         Number JavaDoc n2 = toNumber(num2);
139         if (n1 == null || n2 == null)
140         {
141             return null;
142         }
143         double value = Math.pow(n1.doubleValue(), n2.doubleValue());
144         return matchType(n1, n2, value);
145     }
146
147
148     /**
149      * @param num1 the first number
150      * @param num2 the second number
151      * @return the largest of the numbers or
152      * <code>null</code> if they're invalid
153      * @see #toNumber
154      */

155     public Number JavaDoc max(Object JavaDoc num1, Object JavaDoc num2)
156     {
157         Number JavaDoc n1 = toNumber(num1);
158         Number JavaDoc n2 = toNumber(num2);
159         if (n1 == null || n2 == null)
160         {
161             return null;
162         }
163         double value = Math.max(n1.doubleValue(), n2.doubleValue());
164         return matchType(n1, n2, value);
165     }
166
167
168     /**
169      * @param num1 the first number
170      * @param num2 the second number
171      * @return the smallest of the numbers or
172      * <code>null</code> if they're invalid
173      * @see #toNumber
174      */

175     public Number JavaDoc min(Object JavaDoc num1, Object JavaDoc num2)
176     {
177         Number JavaDoc n1 = toNumber(num1);
178         Number JavaDoc n2 = toNumber(num2);
179         if (n1 == null || n2 == null)
180         {
181             return null;
182         }
183         double value = Math.min(n1.doubleValue(), n2.doubleValue());
184         return matchType(n1, n2, value);
185     }
186
187
188     /**
189      * @param num the number
190      * @return the absolute value of the number or
191      * <code>null</code> if it's invalid
192      * @see #toDouble
193      */

194     public Number JavaDoc abs(Object JavaDoc num)
195     {
196         Number JavaDoc n = toNumber(num);
197         if (n == null)
198         {
199             return null;
200         }
201         double value = Math.abs(n.doubleValue());
202         return matchType(n, value);
203     }
204
205
206     /**
207      * @param num the number
208      * @return the smallest integer that is not
209      * less than the given number
210      */

211     public Integer JavaDoc ceil(Object JavaDoc num)
212     {
213         Number JavaDoc n = toNumber(num);
214         if (n == null)
215         {
216             return null;
217         }
218         return new Integer JavaDoc((int)Math.ceil(n.doubleValue()));
219     }
220
221
222     /**
223      * @param num the number
224      * @return the integer portion of the number
225      */

226     public Integer JavaDoc floor(Object JavaDoc num)
227     {
228         Number JavaDoc n = toNumber(num);
229         if (n == null)
230         {
231             return null;
232         }
233         return new Integer JavaDoc((int)Math.floor(n.doubleValue()));
234     }
235
236
237     /**
238      * Rounds a number to the nearest whole Integer
239      *
240      * @param num the number to round
241      * @return the number rounded to the nearest whole Integer
242      * or <code>null</code> if it's invalid
243      * @see java.lang.Math#rint(double)
244      */

245     public Integer JavaDoc round(Object JavaDoc num)
246     {
247         Number JavaDoc n = toNumber(num);
248         if (n == null)
249         {
250             return null;
251         }
252         return new Integer JavaDoc((int)Math.rint(n.doubleValue()));
253     }
254
255
256     /**
257      * Rounds a number to the specified number of decimal places.
258      * This is particulary useful for simple display formatting.
259      * If you want to round an number to the nearest integer, it
260      * is better to use {@link #round}, as that will return
261      * an {@link Integer} rather than a {@link Double}.
262      *
263      * @param decimals the number of decimal places
264      * @param num the number to round
265      * @return the value rounded to the specified number of
266      * decimal places or <code>null</code> if it's invalid
267      * @see #toNumber
268      */

269     public Double JavaDoc roundTo(Object JavaDoc decimals, Object JavaDoc num)
270     {
271         Number JavaDoc i = toNumber(decimals);
272         Number JavaDoc d = toNumber(num);
273         if (i == null || d == null)
274         {
275             return null;
276         }
277         //ok, go ahead and do the rounding
278
int places = i.intValue();
279         double value = d.doubleValue();
280         if (places == 0)
281         {
282             value = (int)(value + .5);
283         }
284         else
285         {
286             double shift = Math.pow(10, places);
287             value = value * shift;
288             value = (int)(value + .5);
289             value = value / shift;
290         }
291         return new Double JavaDoc(value);
292     }
293
294
295     /**
296      * Rounds a number to the nearest whole Integer
297      *
298      * @param num the number to round
299      * @return the number rounded to the nearest whole Integer
300      * or <code>null</code> if it's invalid
301      * @see #toDouble
302      * @deprecated This will be removed in VelocityTools 1.2.
303      * Use #round(Object num) instead.
304      */

305     public Integer JavaDoc roundToInt(Object JavaDoc num)
306     {
307         return round(num);
308     }
309
310
311     /**
312      * @return a pseudo-random {@link Double} greater
313      * than or equal to 0.0 and less than 1.0
314      * @see Math#random()
315      */

316     public Double JavaDoc getRandom()
317     {
318         return new Double JavaDoc(Math.random());
319     }
320
321
322     /**
323      * This returns a random {@link Number} within the
324      * specified range. The returned value will be
325      * greater than or equal to the first number
326      * and less than the second number. If both arguments
327      * are whole numbers then the returned number will
328      * also be, otherwise a {@link Double} will
329      * be returned.
330      *
331      * @param num1 the first number
332      * @param num2 the second number
333      * @return a pseudo-random {@link Number} greater than
334      * or equal to the first number and less than
335      * the second
336      * @see Math#random()
337      */

338     public Number JavaDoc random(Object JavaDoc num1, Object JavaDoc num2)
339     {
340         Number JavaDoc n1 = toNumber(num1);
341         Number JavaDoc n2 = toNumber(num2);
342         if (n1 == null || n2 == null)
343         {
344             return null;
345         }
346
347         double diff = n2.doubleValue() - n1.doubleValue();
348         // multiply the difference by a pseudo-random double from
349
// 0.0 to 1.0, round to the nearest int, and add the first
350
// value to the random int and return as an Integer
351
double random = (diff * Math.random()) + n1.doubleValue();
352
353         // check if either of the args were floating points
354
String JavaDoc in = n1.toString() + n2.toString();
355         if (in.indexOf('.') < 0)
356         {
357             // args were whole numbers, so return the same
358
return matchType(n1, n2, Math.floor(random));
359         }
360         // one of the args was a floating point,
361
// so don't floor the result
362
return new Double JavaDoc(random);
363     }
364
365
366     // --------------- public type conversion methods ---------
367

368     /**
369      * Converts an object with a numeric value into an Integer
370      * Valid formats are {@link Number} or a {@link String}
371      * representation of a number
372      *
373      * @param num the number to be converted
374      * @return a {@link Integer} representation of the number
375      * or <code>null</code> if it's invalid
376      */

377     public Integer JavaDoc toInteger(Object JavaDoc num)
378     {
379         Number JavaDoc n = toNumber(num);
380         if (n == null)
381         {
382             return null;
383         }
384         return new Integer JavaDoc(n.intValue());
385     }
386
387
388     /**
389      * Converts an object with a numeric value into a Double
390      * Valid formats are {@link Number} or a {@link String}
391      * representation of a number
392      *
393      * @param num the number to be converted
394      * @return a {@link Double} representation of the number
395      * or <code>null</code> if it's invalid
396      */

397     public Double JavaDoc toDouble(Object JavaDoc num)
398     {
399         Number JavaDoc n = toNumber(num);
400         if (n == null)
401         {
402             return null;
403         }
404         return new Double JavaDoc(n.intValue());
405     }
406
407         
408     /**
409      * Converts an object with a numeric value into a Number
410      * Valid formats are {@link Number} or a {@link String}
411      * representation of a number. Note that this does not
412      * handle localized number formats. Use the {@link NumberTool}
413      * to handle such conversions.
414      *
415      * @param num the number to be converted
416      * @return a {@link Number} representation of the number
417      * or <code>null</code> if it's invalid
418      */

419     public Number JavaDoc toNumber(Object JavaDoc num)
420     {
421         if (num == null)
422         {
423             return null;
424         }
425         if (num instanceof Number JavaDoc)
426         {
427             return (Number JavaDoc)num;
428         }
429         try
430         {
431             return parseNumber(String.valueOf(num));
432         }
433         catch (NumberFormatException JavaDoc nfe)
434         {
435             return null;
436         }
437     }
438
439
440     // --------------------------- protected methods ------------------
441

442     /**
443      * @see #matchType(Number,Number,double)
444      */

445     protected Number JavaDoc matchType(Number JavaDoc in, double out)
446     {
447         return matchType(in, null, out);
448     }
449
450
451     /**
452      * Takes the original argument(s) and returns the resulting value as
453      * an instance of the best matching type (Integer, Long, or Double).
454      * If either an argument or the result is not an integer (i.e. has no
455      * decimal when rendered) the result will be returned as a Double.
456      * If not and the result is < -2147483648 or > 2147483647, then a
457      * Long will be returned. Otherwise, an Integer will be returned.
458      */

459     protected Number JavaDoc matchType(Number JavaDoc in1, Number JavaDoc in2, double out)
460     {
461         //NOTE: if we just checked class types, we could miss custom
462
// extensions of java.lang.Number, and if we only checked
463
// the mathematical value, $math.div('3.0', 1) would render
464
// as '3'. To get the expected result, we check what we're
465
// concerned about: the rendered string.
466

467         // first check if the result is a whole number
468
boolean isWhole = (Math.rint(out) == out);
469
470         if (isWhole)
471         {
472             // assume that 1st arg is not null,
473
// check for floating points
474
String JavaDoc in = in1.toString();
475             isWhole = (in.indexOf('.') < 0);
476
477             // if we don't have a decimal yet but do have a second arg
478
if (isWhole && in2 != null)
479             {
480                 in = in2.toString();
481                 isWhole = (in.indexOf('.') < 0);
482             }
483         }
484
485         if (!isWhole)
486         {
487             return new Double JavaDoc(out);
488         }
489         else if (out > Integer.MAX_VALUE || out < Integer.MIN_VALUE)
490         {
491             return new Long JavaDoc((long)out);
492         }
493         else
494         {
495             return new Integer JavaDoc((int)out);
496         }
497     }
498
499
500     /**
501      * Converts an object into a {@link Number} (if it can)
502      * This is used as the base for all numeric parsing methods. So,
503      * sub-classes can override to allow for customized number parsing.
504      * (e.g. for i18n, fractions, compound numbers, bigger numbers, etc.)
505      *
506      * @param value the string to be parsed
507      * @return the value as a {@link Number}
508      */

509     protected Number JavaDoc parseNumber(String JavaDoc value) throws NumberFormatException JavaDoc
510     {
511         // check for the floating point
512
if (value.indexOf('.') < 0)
513         {
514             // check for large numbers
515
long i = new Long JavaDoc(value).longValue();
516             if (i > Integer.MAX_VALUE || i < Integer.MIN_VALUE)
517             {
518                 return new Long JavaDoc(i);
519             }
520             else
521             {
522                 return new Integer JavaDoc((int)i);
523             }
524         }
525         else
526         {
527             return new Double JavaDoc(value);
528         }
529     }
530
531 }
532
Popular Tags