001: /*
002: * Copyright (c) 2003 The Visigoth Software Society. All rights
003: * reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions
007: * are met:
008: *
009: * 1. Redistributions of source code must retain the above copyright
010: * notice, this list of conditions and the following disclaimer.
011: *
012: * 2. Redistributions in binary form must reproduce the above copyright
013: * notice, this list of conditions and the following disclaimer in
014: * the documentation and/or other materials provided with the
015: * distribution.
016: *
017: * 3. The end-user documentation included with the redistribution, if
018: * any, must include the following acknowledgement:
019: * "This product includes software developed by the
020: * Visigoth Software Society (http://www.visigoths.org/)."
021: * Alternately, this acknowledgement may appear in the software itself,
022: * if and wherever such third-party acknowledgements normally appear.
023: *
024: * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the
025: * project contributors may be used to endorse or promote products derived
026: * from this software without prior written permission. For written
027: * permission, please contact visigoths@visigoths.org.
028: *
029: * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
030: * nor may "FreeMarker" or "Visigoth" appear in their names
031: * without prior written permission of the Visigoth Software Society.
032: *
033: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
034: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
035: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
036: * DISCLAIMED. IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
037: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
038: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
039: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
040: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
041: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
042: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
043: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
044: * SUCH DAMAGE.
045: * ====================================================================
046: *
047: * This software consists of voluntary contributions made by many
048: * individuals on behalf of the Visigoth Software Society. For more
049: * information on the Visigoth Software Society, please see
050: * http://www.visigoths.org/
051: */
052:
053: package freemarker.core;
054:
055: import java.math.*;
056: import java.util.HashMap;
057: import java.util.Map;
058:
059: import freemarker.template.*;
060: import freemarker.template.utility.OptimizerUtil;
061:
062: /**
063: * Class to perform arithmetic operations.
064: * @author <a href="mailto:jon@revusky.com">Jonathan Revusky</a>
065: * @author Attila Szegedi
066: */
067:
068: public abstract class ArithmeticEngine {
069:
070: /**
071: * Arithmetic engine that converts all numbers to {@link BigDecimal} and
072: * then operates on them. This is FreeMarker's default arithmetic engine.
073: */
074: public static final BigDecimalEngine BIGDECIMAL_ENGINE = new BigDecimalEngine();
075: /**
076: * Arithmetic engine that uses (more-or-less) the widening conversions of
077: * Java language to determine the type of result of operation, instead of
078: * converting everything to BigDecimal up front.
079: */
080: public static final ConservativeEngine CONSERVATIVE_ENGINE = new ConservativeEngine();
081:
082: public abstract int compareNumbers(Number first, Number second)
083: throws TemplateException;
084:
085: public abstract Number add(Number first, Number second)
086: throws TemplateException;
087:
088: public abstract Number subtract(Number first, Number second)
089: throws TemplateException;
090:
091: public abstract Number multiply(Number first, Number second)
092: throws TemplateException;
093:
094: public abstract Number divide(Number first, Number second)
095: throws TemplateException;
096:
097: public abstract Number modulus(Number first, Number second)
098: throws TemplateException;
099:
100: public abstract Number toNumber(String s);
101:
102: protected int minScale = 12;
103: protected int maxScale = 12;
104: protected int roundingPolicy = BigDecimal.ROUND_HALF_UP;
105:
106: /**
107: * Sets the minimal scale to use when dividing BigDecimal numbers. Default
108: * value is 12.
109: */
110: public void setMinScale(int minScale) {
111: if (minScale < 0) {
112: throw new IllegalArgumentException("minScale < 0");
113: }
114: this .minScale = minScale;
115: }
116:
117: /**
118: * Sets the maximal scale to use when multiplying BigDecimal numbers.
119: * Default value is 100.
120: */
121: public void setMaxScale(int maxScale) {
122: if (maxScale < minScale) {
123: throw new IllegalArgumentException("maxScale < minScale");
124: }
125: this .maxScale = maxScale;
126: }
127:
128: public void setRoundingPolicy(int roundingPolicy) {
129: if (roundingPolicy != BigDecimal.ROUND_CEILING
130: && roundingPolicy != BigDecimal.ROUND_DOWN
131: && roundingPolicy != BigDecimal.ROUND_FLOOR
132: && roundingPolicy != BigDecimal.ROUND_HALF_DOWN
133: && roundingPolicy != BigDecimal.ROUND_HALF_EVEN
134: && roundingPolicy != BigDecimal.ROUND_HALF_UP
135: && roundingPolicy != BigDecimal.ROUND_UNNECESSARY
136: && roundingPolicy != BigDecimal.ROUND_UP) {
137: throw new IllegalArgumentException(
138: "invalid rounding policy");
139: }
140:
141: this .roundingPolicy = roundingPolicy;
142: }
143:
144: /**
145: * This is the default arithmetic engine in FreeMarker. It converts every
146: * number it receives into {@link BigDecimal}, then operates on these
147: * converted {@link BigDecimal}s.
148: */
149: public static class BigDecimalEngine extends ArithmeticEngine {
150: public int compareNumbers(Number first, Number second) {
151: BigDecimal left = toBigDecimal(first);
152: BigDecimal right = toBigDecimal(second);
153: return left.compareTo(right);
154: }
155:
156: public Number add(Number first, Number second) {
157: BigDecimal left = toBigDecimal(first);
158: BigDecimal right = toBigDecimal(second);
159: return left.add(right);
160: }
161:
162: public Number subtract(Number first, Number second) {
163: BigDecimal left = toBigDecimal(first);
164: BigDecimal right = toBigDecimal(second);
165: return left.subtract(right);
166: }
167:
168: public Number multiply(Number first, Number second) {
169: BigDecimal left = toBigDecimal(first);
170: BigDecimal right = toBigDecimal(second);
171: BigDecimal result = left.multiply(right);
172: if (result.scale() > maxScale) {
173: result = result.setScale(maxScale, roundingPolicy);
174: }
175: return result;
176: }
177:
178: public Number divide(Number first, Number second) {
179: BigDecimal left = toBigDecimal(first);
180: BigDecimal right = toBigDecimal(second);
181: return divide(left, right);
182: }
183:
184: public Number modulus(Number first, Number second) {
185: long left = first.longValue();
186: long right = second.longValue();
187: return new Long(left % right);
188: }
189:
190: public Number toNumber(String s) {
191: return new BigDecimal(s);
192: }
193:
194: private BigDecimal divide(BigDecimal left, BigDecimal right) {
195: int scale1 = left.scale();
196: int scale2 = right.scale();
197: int scale = Math.max(scale1, scale2);
198: scale = Math.max(minScale, scale);
199: return left.divide(right, scale, roundingPolicy);
200: }
201: }
202:
203: /**
204: * An arithmetic engine that conservatively widens the operation arguments
205: * to extent that they can hold the result of the operation. Widening
206: * conversions occur in following situations:
207: * <ul>
208: * <li>byte and short are always widened to int (alike to Java language).</li>
209: * <li>To preserve magnitude: when operands are of different types, the
210: * result type is the type of the wider operand.</li>
211: * <li>to avoid overflows: if add, subtract, or multiply would overflow on
212: * integer types, the result is widened from int to long, or from long to
213: * BigInteger.</li>
214: * <li>to preserve fractional part: if a division of integer types would
215: * have a fractional part, int and long are converted to double, and
216: * BigInteger is converted to BigDecimal. An operation on a float and a
217: * long results in a double. An operation on a float or double and a
218: * BigInteger results in a BigDecimal.</li>
219: * </ul>
220: */
221: public static class ConservativeEngine extends ArithmeticEngine {
222: private static final int INTEGER = 0;
223: private static final int LONG = 1;
224: private static final int FLOAT = 2;
225: private static final int DOUBLE = 3;
226: private static final int BIGINTEGER = 4;
227: private static final int BIGDECIMAL = 5;
228:
229: private static final Map classCodes = createClassCodesMap();
230:
231: public int compareNumbers(Number first, Number second)
232: throws TemplateException {
233: switch (getCommonClassCode(first, second)) {
234: case INTEGER: {
235: int n1 = first.intValue();
236: int n2 = second.intValue();
237: return n1 < n2 ? -1 : (n1 == n2 ? 0 : 1);
238: }
239: case LONG: {
240: long n1 = first.longValue();
241: long n2 = second.longValue();
242: return n1 < n2 ? -1 : (n1 == n2 ? 0 : 1);
243: }
244: case FLOAT: {
245: float n1 = first.floatValue();
246: float n2 = second.floatValue();
247: return n1 < n2 ? -1 : (n1 == n2 ? 0 : 1);
248: }
249: case DOUBLE: {
250: double n1 = first.doubleValue();
251: double n2 = second.doubleValue();
252: return n1 < n2 ? -1 : (n1 == n2 ? 0 : 1);
253: }
254: case BIGINTEGER: {
255: BigInteger n1 = toBigInteger(first);
256: BigInteger n2 = toBigInteger(second);
257: return n1.compareTo(n2);
258: }
259: case BIGDECIMAL: {
260: BigDecimal n1 = toBigDecimal(first);
261: BigDecimal n2 = toBigDecimal(second);
262: return n1.compareTo(n2);
263: }
264: }
265: // Make the compiler happy. getCommonClassCode() is guaranteed to
266: // return only above codes, or throw an exception.
267: throw new Error();
268: }
269:
270: public Number add(Number first, Number second)
271: throws TemplateException {
272: switch (getCommonClassCode(first, second)) {
273: case INTEGER: {
274: int n1 = first.intValue();
275: int n2 = second.intValue();
276: int n = n1 + n2;
277: return ((n ^ n1) < 0 && (n ^ n2) < 0) // overflow check
278: ? (Number) new Long(((long) n1) + n2)
279: : (Number) new Integer(n);
280: }
281: case LONG: {
282: long n1 = first.longValue();
283: long n2 = second.longValue();
284: long n = n1 + n2;
285: return ((n ^ n1) < 0 && (n ^ n2) < 0) // overflow check
286: ? (Number) toBigInteger(first)
287: .add(toBigInteger(second))
288: : (Number) new Long(n);
289: }
290: case FLOAT: {
291: return new Float(first.floatValue()
292: + second.floatValue());
293: }
294: case DOUBLE: {
295: return new Double(first.doubleValue()
296: + second.doubleValue());
297: }
298: case BIGINTEGER: {
299: BigInteger n1 = toBigInteger(first);
300: BigInteger n2 = toBigInteger(second);
301: return n1.add(n2);
302: }
303: case BIGDECIMAL: {
304: BigDecimal n1 = toBigDecimal(first);
305: BigDecimal n2 = toBigDecimal(second);
306: return n1.add(n2);
307: }
308: }
309: // Make the compiler happy. getCommonClassCode() is guaranteed to
310: // return only above codes, or throw an exception.
311: throw new Error();
312: }
313:
314: public Number subtract(Number first, Number second)
315: throws TemplateException {
316: switch (getCommonClassCode(first, second)) {
317: case INTEGER: {
318: int n1 = first.intValue();
319: int n2 = second.intValue();
320: int n = n1 - n2;
321: return ((n ^ n1) < 0 && (n ^ ~n2) < 0) // overflow check
322: ? (Number) new Long(((long) n1) - n2)
323: : (Number) new Integer(n);
324: }
325: case LONG: {
326: long n1 = first.longValue();
327: long n2 = second.longValue();
328: long n = n1 - n2;
329: return ((n ^ n1) < 0 && (n ^ ~n2) < 0) // overflow check
330: ? (Number) toBigInteger(first).subtract(
331: toBigInteger(second))
332: : (Number) new Long(n);
333: }
334: case FLOAT: {
335: return new Float(first.floatValue()
336: - second.floatValue());
337: }
338: case DOUBLE: {
339: return new Double(first.doubleValue()
340: - second.doubleValue());
341: }
342: case BIGINTEGER: {
343: BigInteger n1 = toBigInteger(first);
344: BigInteger n2 = toBigInteger(second);
345: return n1.subtract(n2);
346: }
347: case BIGDECIMAL: {
348: BigDecimal n1 = toBigDecimal(first);
349: BigDecimal n2 = toBigDecimal(second);
350: return n1.subtract(n2);
351: }
352: }
353: // Make the compiler happy. getCommonClassCode() is guaranteed to
354: // return only above codes, or throw an exception.
355: throw new Error();
356: }
357:
358: public Number multiply(Number first, Number second)
359: throws TemplateException {
360: switch (getCommonClassCode(first, second)) {
361: case INTEGER: {
362: int n1 = first.intValue();
363: int n2 = second.intValue();
364: int n = n1 * n2;
365: return n1 == 0 || n / n1 == n2 // overflow check
366: ? (Number) new Integer(n) : (Number) new Long(
367: ((long) n1) * n2);
368: }
369: case LONG: {
370: long n1 = first.longValue();
371: long n2 = second.longValue();
372: long n = n1 * n2;
373: return n1 == 0L || n / n1 == n2 // overflow check
374: ? (Number) new Long(n) : (Number) toBigInteger(first)
375: .multiply(toBigInteger(second));
376: }
377: case FLOAT: {
378: return new Float(first.floatValue()
379: * second.floatValue());
380: }
381: case DOUBLE: {
382: return new Double(first.doubleValue()
383: * second.doubleValue());
384: }
385: case BIGINTEGER: {
386: BigInteger n1 = toBigInteger(first);
387: BigInteger n2 = toBigInteger(second);
388: return n1.multiply(n2);
389: }
390: case BIGDECIMAL: {
391: BigDecimal n1 = toBigDecimal(first);
392: BigDecimal n2 = toBigDecimal(second);
393: BigDecimal r = n1.multiply(n2);
394: return r.scale() > maxScale ? r.setScale(maxScale,
395: roundingPolicy) : r;
396: }
397: }
398: // Make the compiler happy. getCommonClassCode() is guaranteed to
399: // return only above codes, or throw an exception.
400: throw new Error();
401: }
402:
403: public Number divide(Number first, Number second)
404: throws TemplateException {
405: switch (getCommonClassCode(first, second)) {
406: case INTEGER: {
407: int n1 = first.intValue();
408: int n2 = second.intValue();
409: if (n1 % n2 == 0) {
410: return new Integer(n1 / n2);
411: }
412: return new Double(((double) n1) / n2);
413: }
414: case LONG: {
415: long n1 = first.longValue();
416: long n2 = second.longValue();
417: if (n1 % n2 == 0) {
418: return new Long(n1 / n2);
419: }
420: return new Double(((double) n1) / n2);
421: }
422: case FLOAT: {
423: return new Float(first.floatValue()
424: / second.floatValue());
425: }
426: case DOUBLE: {
427: return new Double(first.doubleValue()
428: / second.doubleValue());
429: }
430: case BIGINTEGER: {
431: BigInteger n1 = toBigInteger(first);
432: BigInteger n2 = toBigInteger(second);
433: BigInteger[] divmod = n1.divideAndRemainder(n2);
434: if (divmod[1].equals(BigInteger.ZERO)) {
435: return divmod[0];
436: } else {
437: BigDecimal bd1 = new BigDecimal(n1);
438: BigDecimal bd2 = new BigDecimal(n2);
439: return bd1.divide(bd2, minScale, roundingPolicy);
440: }
441: }
442: case BIGDECIMAL: {
443: BigDecimal n1 = toBigDecimal(first);
444: BigDecimal n2 = toBigDecimal(second);
445: int scale1 = n1.scale();
446: int scale2 = n2.scale();
447: int scale = Math.max(scale1, scale2);
448: scale = Math.max(minScale, scale);
449: return n1.divide(n2, scale, roundingPolicy);
450: }
451: }
452: // Make the compiler happy. getCommonClassCode() is guaranteed to
453: // return only above codes, or throw an exception.
454: throw new Error();
455: }
456:
457: public Number modulus(Number first, Number second)
458: throws TemplateException {
459: switch (getCommonClassCode(first, second)) {
460: case INTEGER: {
461: return new Integer(first.intValue() % second.intValue());
462: }
463: case LONG: {
464: return new Long(first.longValue() % second.longValue());
465: }
466: case FLOAT: {
467: return new Float(first.floatValue()
468: % second.floatValue());
469: }
470: case DOUBLE: {
471: return new Double(first.doubleValue()
472: % second.doubleValue());
473: }
474: case BIGINTEGER: {
475: BigInteger n1 = toBigInteger(first);
476: BigInteger n2 = toBigInteger(second);
477: return n1.mod(n2);
478: }
479: case BIGDECIMAL: {
480: throw new TemplateException(
481: "Can't calculate remainder on BigDecimals",
482: Environment.getCurrentEnvironment());
483: }
484: }
485: // Make the compiler happy. getCommonClassCode() is guaranteed to
486: // return only above codes, or throw an exception.
487: throw new Error();
488: }
489:
490: public Number toNumber(String s) {
491: return OptimizerUtil
492: .optimizeNumberRepresentation(new BigDecimal(s));
493: }
494:
495: private static Map createClassCodesMap() {
496: Map map = new HashMap(17);
497: Integer intcode = new Integer(INTEGER);
498: map.put(Byte.class, intcode);
499: map.put(Short.class, intcode);
500: map.put(Integer.class, intcode);
501: map.put(Long.class, new Integer(LONG));
502: map.put(Float.class, new Integer(FLOAT));
503: map.put(Double.class, new Integer(DOUBLE));
504: map.put(BigInteger.class, new Integer(BIGINTEGER));
505: map.put(BigDecimal.class, new Integer(BIGDECIMAL));
506: return map;
507: }
508:
509: private static int getClassCode(Number num)
510: throws TemplateException {
511: try {
512: return ((Integer) classCodes.get(num.getClass()))
513: .intValue();
514: } catch (NullPointerException e) {
515: if (num == null) {
516: throw new TemplateException(
517: "Unknown number type null", Environment
518: .getCurrentEnvironment());
519: }
520: throw new TemplateException("Unknown number type "
521: + num.getClass().getName(), Environment
522: .getCurrentEnvironment());
523: }
524: }
525:
526: private static int getCommonClassCode(Number num1, Number num2)
527: throws TemplateException {
528: int c1 = getClassCode(num1);
529: int c2 = getClassCode(num2);
530: int c = c1 > c2 ? c1 : c2;
531: // If BigInteger is combined with a Float or Double, the result is a
532: // BigDecimal instead of BigInteger in order not to lose the
533: // fractional parts. If Float is combined with Long, the result is a
534: // Double instead of Float to preserve the bigger bit width.
535: switch (c) {
536: case FLOAT: {
537: if ((c1 < c2 ? c1 : c2) == LONG) {
538: return DOUBLE;
539: }
540: break;
541: }
542: case BIGINTEGER: {
543: int min = c1 < c2 ? c1 : c2;
544: if (min == DOUBLE || min == FLOAT) {
545: return BIGDECIMAL;
546: }
547: break;
548: }
549: }
550: return c;
551: }
552:
553: private static BigInteger toBigInteger(Number num) {
554: return num instanceof BigInteger ? (BigInteger) num
555: : new BigInteger(num.toString());
556: }
557: }
558:
559: private static BigDecimal toBigDecimal(Number num) {
560: return num instanceof BigDecimal ? (BigDecimal) num
561: : new BigDecimal(num.toString());
562: }
563: }
|