1 52 53 package freemarker.core; 54 55 import java.math.*; 56 import java.util.HashMap ; 57 import java.util.Map ; 58 59 import freemarker.template.*; 60 import freemarker.template.utility.OptimizerUtil; 61 62 67 68 public abstract class ArithmeticEngine { 69 70 74 public static final BigDecimalEngine BIGDECIMAL_ENGINE = new BigDecimalEngine(); 75 80 public static final ConservativeEngine CONSERVATIVE_ENGINE = new ConservativeEngine(); 81 82 public abstract int compareNumbers(Number first, Number second) throws TemplateException; 83 public abstract Number add(Number first, Number second) throws TemplateException; 84 public abstract Number subtract(Number first, Number second) throws TemplateException; 85 public abstract Number multiply(Number first, Number second) throws TemplateException; 86 public abstract Number divide(Number first, Number second) throws TemplateException; 87 public abstract Number modulus(Number first, Number second) throws TemplateException; 88 public abstract Number toNumber(String s); 89 90 protected int minScale = 12; 91 protected int maxScale = 12; 92 protected int roundingPolicy = BigDecimal.ROUND_HALF_UP; 93 94 98 public void setMinScale(int minScale) { 99 if(minScale < 0) { 100 throw new IllegalArgumentException ("minScale < 0"); 101 } 102 this.minScale = minScale; 103 } 104 105 109 public void setMaxScale(int maxScale) { 110 if(maxScale < minScale) { 111 throw new IllegalArgumentException ("maxScale < minScale"); 112 } 113 this.maxScale = maxScale; 114 } 115 116 public void setRoundingPolicy(int roundingPolicy) { 117 if (roundingPolicy != BigDecimal.ROUND_CEILING 118 && roundingPolicy != BigDecimal.ROUND_DOWN 119 && roundingPolicy != BigDecimal.ROUND_FLOOR 120 && roundingPolicy != BigDecimal.ROUND_HALF_DOWN 121 && roundingPolicy != BigDecimal.ROUND_HALF_EVEN 122 && roundingPolicy != BigDecimal.ROUND_HALF_UP 123 && roundingPolicy != BigDecimal.ROUND_UNNECESSARY 124 && roundingPolicy != BigDecimal.ROUND_UP) 125 { 126 throw new IllegalArgumentException ("invalid rounding policy"); 127 } 128 129 this.roundingPolicy = roundingPolicy; 130 } 131 132 137 public static class BigDecimalEngine 138 extends 139 ArithmeticEngine 140 { 141 public int compareNumbers(Number first, Number second) { 142 BigDecimal left = toBigDecimal(first); 143 BigDecimal right = toBigDecimal(second); 144 return left.compareTo(right); 145 } 146 147 public Number add(Number first, Number second) { 148 BigDecimal left = toBigDecimal(first); 149 BigDecimal right = toBigDecimal(second); 150 return left.add(right); 151 } 152 153 public Number subtract(Number first, Number second) { 154 BigDecimal left = toBigDecimal(first); 155 BigDecimal right = toBigDecimal(second); 156 return left.subtract(right); 157 } 158 159 public Number multiply(Number first, Number second) { 160 BigDecimal left = toBigDecimal(first); 161 BigDecimal right = toBigDecimal(second); 162 BigDecimal result = left.multiply(right); 163 if (result.scale() > maxScale) { 164 result = result.setScale(maxScale, roundingPolicy); 165 } 166 return result; 167 } 168 169 public Number divide(Number first, Number second) { 170 BigDecimal left = toBigDecimal(first); 171 BigDecimal right = toBigDecimal(second); 172 return divide(left, right); 173 } 174 175 public Number modulus(Number first, Number second) { 176 long left = first.longValue(); 177 long right = second.longValue(); 178 return new Long (left % right); 179 } 180 181 public Number toNumber(String s) { 182 return new BigDecimal(s); 183 } 184 185 private BigDecimal divide(BigDecimal left, BigDecimal right) { 186 int scale1 = left.scale(); 187 int scale2 = right.scale(); 188 int scale = Math.max(scale1, scale2); 189 scale = Math.max(minScale, scale); 190 return left.divide(right, scale, roundingPolicy); 191 } 192 } 193 194 212 public static class ConservativeEngine extends ArithmeticEngine { 213 private static final int INTEGER = 0; 214 private static final int LONG = 1; 215 private static final int FLOAT = 2; 216 private static final int DOUBLE = 3; 217 private static final int BIGINTEGER = 4; 218 private static final int BIGDECIMAL = 5; 219 220 private static final Map classCodes = createClassCodesMap(); 221 222 public int compareNumbers(Number first, Number second) throws TemplateException { 223 switch(getCommonClassCode(first, second)) { 224 case INTEGER: { 225 int n1 = first.intValue(); 226 int n2 = second.intValue(); 227 return n1 < n2 ? -1 : (n1 == n2 ? 0 : 1); 228 } 229 case LONG: { 230 long n1 = first.longValue(); 231 long n2 = second.longValue(); 232 return n1 < n2 ? -1 : (n1 == n2 ? 0 : 1); 233 } 234 case FLOAT: { 235 float n1 = first.floatValue(); 236 float n2 = second.floatValue(); 237 return n1 < n2 ? -1 : (n1 == n2 ? 0 : 1); 238 } 239 case DOUBLE: { 240 double n1 = first.doubleValue(); 241 double n2 = second.doubleValue(); 242 return n1 < n2 ? -1 : (n1 == n2 ? 0 : 1); 243 } 244 case BIGINTEGER: { 245 BigInteger n1 = toBigInteger(first); 246 BigInteger n2 = toBigInteger(second); 247 return n1.compareTo(n2); 248 } 249 case BIGDECIMAL: { 250 BigDecimal n1 = toBigDecimal(first); 251 BigDecimal n2 = toBigDecimal(second); 252 return n1.compareTo(n2); 253 } 254 } 255 throw new Error (); 258 } 259 260 public Number add(Number first, Number second) throws TemplateException { 261 switch(getCommonClassCode(first, second)) { 262 case INTEGER: { 263 int n1 = first.intValue(); 264 int n2 = second.intValue(); 265 int n = n1 + n2; 266 return 267 ((n ^ n1) < 0 && (n ^ n2) < 0) ? (Number )new Long (((long)n1) + n2) 269 : (Number )new Integer (n); 270 } 271 case LONG: { 272 long n1 = first.longValue(); 273 long n2 = second.longValue(); 274 long n = n1 + n2; 275 return 276 ((n ^ n1) < 0 && (n ^ n2) < 0) ? (Number )toBigInteger(first).add(toBigInteger(second)) 278 : (Number )new Long (n); 279 } 280 case FLOAT: { 281 return new Float (first.floatValue() + second.floatValue()); 282 } 283 case DOUBLE: { 284 return new Double (first.doubleValue() + second.doubleValue()); 285 } 286 case BIGINTEGER: { 287 BigInteger n1 = toBigInteger(first); 288 BigInteger n2 = toBigInteger(second); 289 return n1.add(n2); 290 } 291 case BIGDECIMAL: { 292 BigDecimal n1 = toBigDecimal(first); 293 BigDecimal n2 = toBigDecimal(second); 294 return n1.add(n2); 295 } 296 } 297 throw new Error (); 300 } 301 302 public Number subtract(Number first, Number second) throws TemplateException { 303 switch(getCommonClassCode(first, second)) { 304 case INTEGER: { 305 int n1 = first.intValue(); 306 int n2 = second.intValue(); 307 int n = n1 - n2; 308 return 309 ((n ^ n1) < 0 && (n ^ ~n2) < 0) ? (Number )new Long (((long)n1) - n2) 311 : (Number )new Integer (n); 312 } 313 case LONG: { 314 long n1 = first.longValue(); 315 long n2 = second.longValue(); 316 long n = n1 - n2; 317 return 318 ((n ^ n1) < 0 && (n ^ ~n2) < 0) ? (Number )toBigInteger(first).subtract(toBigInteger(second)) 320 : (Number )new Long (n); 321 } 322 case FLOAT: { 323 return new Float (first.floatValue() - second.floatValue()); 324 } 325 case DOUBLE: { 326 return new Double (first.doubleValue() - second.doubleValue()); 327 } 328 case BIGINTEGER: { 329 BigInteger n1 = toBigInteger(first); 330 BigInteger n2 = toBigInteger(second); 331 return n1.subtract(n2); 332 } 333 case BIGDECIMAL: { 334 BigDecimal n1 = toBigDecimal(first); 335 BigDecimal n2 = toBigDecimal(second); 336 return n1.subtract(n2); 337 } 338 } 339 throw new Error (); 342 } 343 344 public Number multiply(Number first, Number second) throws TemplateException { 345 switch(getCommonClassCode(first, second)) { 346 case INTEGER: { 347 int n1 = first.intValue(); 348 int n2 = second.intValue(); 349 int n = n1 * n2; 350 return 351 n / n1 == n2 ? (Number )new Integer (n) 353 : (Number )new Long (((long)n1) * n2); 354 } 355 case LONG: { 356 long n1 = first.longValue(); 357 long n2 = second.longValue(); 358 long n = n1 * n2; 359 return 360 n / n1 == n2 ? (Number )new Long (n) 362 : (Number )toBigInteger(first).multiply(toBigInteger(second)); 363 } 364 case FLOAT: { 365 return new Float (first.floatValue() * second.floatValue()); 366 } 367 case DOUBLE: { 368 return new Double (first.doubleValue() * second.doubleValue()); 369 } 370 case BIGINTEGER: { 371 BigInteger n1 = toBigInteger(first); 372 BigInteger n2 = toBigInteger(second); 373 return n1.multiply(n2); 374 } 375 case BIGDECIMAL: { 376 BigDecimal n1 = toBigDecimal(first); 377 BigDecimal n2 = toBigDecimal(second); 378 BigDecimal r = n1.multiply(n2); 379 return r.scale() > maxScale ? r.setScale(maxScale, roundingPolicy) : r; 380 } 381 } 382 throw new Error (); 385 } 386 387 public Number divide(Number first, Number second) throws TemplateException { 388 switch(getCommonClassCode(first, second)) { 389 case INTEGER: { 390 int n1 = first.intValue(); 391 int n2 = second.intValue(); 392 if (n1 % n2 == 0) { 393 return new Integer (n1/n2); 394 } 395 return new Double (((double)n1)/n2); 396 } 397 case LONG: { 398 long n1 = first.longValue(); 399 long n2 = second.longValue(); 400 if (n1 % n2 == 0) { 401 return new Long (n1/n2); 402 } 403 return new Double (((double)n1)/n2); 404 } 405 case FLOAT: { 406 return new Float (first.floatValue() / second.floatValue()); 407 } 408 case DOUBLE: { 409 return new Double (first.doubleValue() / second.doubleValue()); 410 } 411 case BIGINTEGER: { 412 BigInteger n1 = toBigInteger(first); 413 BigInteger n2 = toBigInteger(second); 414 BigInteger[] divmod = n1.divideAndRemainder(n2); 415 if(divmod[1].equals(BigInteger.ZERO)) { 416 return divmod[0]; 417 } 418 else { 419 BigDecimal bd1 = new BigDecimal(n1); 420 BigDecimal bd2 = new BigDecimal(n2); 421 return bd1.divide(bd2, minScale, roundingPolicy); 422 } 423 } 424 case BIGDECIMAL: { 425 BigDecimal n1 = toBigDecimal(first); 426 BigDecimal n2 = toBigDecimal(second); 427 int scale1 = n1.scale(); 428 int scale2 = n2.scale(); 429 int scale = Math.max(scale1, scale2); 430 scale = Math.max(minScale, scale); 431 return n1.divide(n2, scale, roundingPolicy); 432 } 433 } 434 throw new Error (); 437 } 438 439 public Number modulus(Number first, Number second) throws TemplateException { 440 switch(getCommonClassCode(first, second)) { 441 case INTEGER: { 442 return new Integer (first.intValue() % second.intValue()); 443 } 444 case LONG: { 445 return new Long (first.longValue() % second.longValue()); 446 } 447 case FLOAT: { 448 return new Float (first.floatValue() % second.floatValue()); 449 } 450 case DOUBLE: { 451 return new Double (first.doubleValue() % second.doubleValue()); 452 } 453 case BIGINTEGER: { 454 BigInteger n1 = toBigInteger(first); 455 BigInteger n2 = toBigInteger(second); 456 return n1.mod(n2); 457 } 458 case BIGDECIMAL: { 459 throw new TemplateException("Can't calculate remainder on BigDecimals", Environment.getCurrentEnvironment()); 460 } 461 } 462 throw new Error (); 465 } 466 467 public Number toNumber(String s) { 468 return OptimizerUtil.optimizeNumberRepresentation(new BigDecimal(s)); 469 } 470 471 private static Map createClassCodesMap() { 472 Map map = new HashMap (17); 473 Integer intcode = new Integer (INTEGER); 474 map.put(Byte .class, intcode); 475 map.put(Short .class, intcode); 476 map.put(Integer .class, intcode); 477 map.put(Long .class, new Integer (LONG)); 478 map.put(Float .class, new Integer (FLOAT)); 479 map.put(Double .class, new Integer (DOUBLE)); 480 map.put(BigInteger.class, new Integer (BIGINTEGER)); 481 map.put(BigDecimal.class, new Integer (BIGDECIMAL)); 482 return map; 483 } 484 485 private static int getClassCode(Number num) throws TemplateException { 486 try { 487 return ((Integer )classCodes.get(num.getClass())).intValue(); 488 } 489 catch(NullPointerException e) { 490 if(num == null) { 491 throw new TemplateException("Unknown number type null", Environment.getCurrentEnvironment()); 492 } 493 throw new TemplateException("Unknown number type " + num.getClass().getName(), Environment.getCurrentEnvironment()); 494 } 495 } 496 497 private static int getCommonClassCode(Number num1, Number num2) throws TemplateException { 498 int c1 = getClassCode(num1); 499 int c2 = getClassCode(num2); 500 int c = c1 > c2 ? c1 : c2; 501 switch(c) { 506 case FLOAT: { 507 if((c1 < c2 ? c1 : c2) == LONG) { 508 return DOUBLE; 509 } 510 break; 511 } 512 case BIGINTEGER: { 513 int min = c1 < c2 ? c1 : c2; 514 if(min == DOUBLE || min == FLOAT) { 515 return BIGDECIMAL; 516 } 517 break; 518 } 519 } 520 return c; 521 } 522 523 private static BigInteger toBigInteger(Number num) { 524 return num instanceof BigInteger ? (BigInteger) num : new BigInteger(num.toString()); 525 } 526 } 527 528 private static BigDecimal toBigDecimal(Number num) { 529 return num instanceof BigDecimal ? (BigDecimal) num : new BigDecimal(num.toString()); 530 } 531 } 532 | Popular Tags |