001 package org.apache.commons.ognl; 002 003 /* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022 import org.apache.commons.ognl.enhance.UnsupportedCompilationException; 023 024 import java.lang.reflect.Array; 025 import java.math.BigDecimal; 026 import java.math.BigInteger; 027 import java.util.Enumeration; 028 029 /** 030 * This is an abstract class with static methods that define the operations of OGNL. 031 * 032 * @author Luke Blanshard (blanshlu@netscape.net) 033 * @author Drew Davidson (drew@ognl.org) 034 */ 035 public abstract class OgnlOps 036 implements NumericTypes 037 { 038 039 /** 040 * Compares two objects for equality, even if it has to convert one of them to the other type. If both objects are 041 * numeric they are converted to the widest type and compared. If one is non-numeric and one is numeric the 042 * non-numeric is converted to double and compared to the double numeric value. If both are non-numeric and 043 * Comparable and the types are compatible (i.e. v1 is of the same or superclass of v2's type) they are compared 044 * with Comparable.compareTo(). If both values are non-numeric and not Comparable or of incompatible classes this 045 * will throw and IllegalArgumentException. 046 * 047 * @param v1 First value to compare 048 * @param v2 second value to compare 049 * @return integer describing the comparison between the two objects. A negative number indicates that v1 < v2. 050 * Positive indicates that v1 > v2. Zero indicates v1 == v2. 051 * @throws IllegalArgumentException if the objects are both non-numeric yet of incompatible types or do not 052 * implement Comparable. 053 */ 054 public static int compareWithConversion( Object v1, Object v2 ) 055 { 056 int result; 057 058 if ( v1 == v2 ) 059 { 060 result = 0; 061 } 062 else 063 { 064 int t1 = getNumericType( v1 ), t2 = getNumericType( v2 ), type = getNumericType( t1, t2, true ); 065 066 switch ( type ) 067 { 068 case BIGINT: 069 result = bigIntValue( v1 ).compareTo( bigIntValue( v2 ) ); 070 break; 071 072 case BIGDEC: 073 result = bigDecValue( v1 ).compareTo( bigDecValue( v2 ) ); 074 break; 075 076 case NONNUMERIC: 077 if ( ( t1 == NONNUMERIC ) && ( t2 == NONNUMERIC ) ) 078 { 079 if ( ( v1 instanceof Comparable ) && v1.getClass().isAssignableFrom( v2.getClass() ) ) 080 { 081 result = ( (Comparable) v1 ).compareTo( v2 ); 082 break; 083 } 084 throw new IllegalArgumentException( "invalid comparison: " + v1.getClass().getName() 085 + " and " + v2.getClass().getName() ); 086 } 087 // else fall through 088 case FLOAT: 089 case DOUBLE: 090 double dv1 = doubleValue( v1 ), 091 dv2 = doubleValue( v2 ); 092 093 return ( dv1 == dv2 ) ? 0 : ( ( dv1 < dv2 ) ? -1 : 1 ); 094 095 default: 096 long lv1 = longValue( v1 ), 097 lv2 = longValue( v2 ); 098 099 return ( lv1 == lv2 ) ? 0 : ( ( lv1 < lv2 ) ? -1 : 1 ); 100 } 101 } 102 return result; 103 } 104 105 /** 106 * Returns true if object1 is equal to object2 in either the sense that they are the same object or, if both are 107 * non-null if they are equal in the <CODE>equals()</CODE> sense. 108 * 109 * @param object1 First object to compare 110 * @param object2 Second object to compare 111 * @return true if v1 == v2 112 */ 113 public static boolean isEqual( Object object1, Object object2 ) 114 { 115 boolean result = false; 116 117 if ( object1 == object2 ) 118 { 119 result = true; 120 } 121 else 122 { 123 if ( ( object1 != null ) && object1.getClass().isArray() ) 124 { 125 if ( ( object2 != null ) && object2.getClass().isArray() && ( object2.getClass() 126 == object1.getClass() ) ) 127 { 128 result = ( Array.getLength( object1 ) == Array.getLength( object2 ) ); 129 if ( result ) 130 { 131 for ( int i = 0, icount = Array.getLength( object1 ); result && ( i < icount ); i++ ) 132 { 133 result = isEqual( Array.get( object1, i ), Array.get( object2, i ) ); 134 } 135 } 136 } 137 } 138 else 139 { 140 // Check for converted equivalence first, then equals() equivalence 141 result = 142 ( object1 != null ) && ( object2 != null ) 143 && ( object1.equals( object2 ) || ( compareWithConversion( object1, object2 ) == 0 ) ); 144 } 145 } 146 return result; 147 } 148 149 public static boolean booleanValue( boolean value ) 150 { 151 return value; 152 } 153 154 public static boolean booleanValue( int value ) 155 { 156 return value > 0; 157 } 158 159 public static boolean booleanValue( float value ) 160 { 161 return value > 0; 162 } 163 164 public static boolean booleanValue( long value ) 165 { 166 return value > 0; 167 } 168 169 public static boolean booleanValue( double value ) 170 { 171 return value > 0; 172 } 173 174 /** 175 * Evaluates the given object as a boolean: if it is a Boolean object, it's easy; if it's a Number or a Character, 176 * returns true for non-zero objects; and otherwise returns true for non-null objects. 177 * 178 * @param value an object to interpret as a boolean 179 * @return the boolean value implied by the given object 180 */ 181 public static boolean booleanValue( Object value ) 182 { 183 if ( value == null ) 184 { 185 return false; 186 } 187 Class<?> c = value.getClass(); 188 189 if ( c == Boolean.class ) 190 { 191 return (Boolean) value; 192 } 193 // if ( c == String.class ) 194 // return ((String)value).length() > 0; 195 196 if ( c == Character.class ) 197 { 198 return (Character) value != 0; 199 } 200 return !( value instanceof Number ) || ( (Number) value ).doubleValue() != 0; 201 202 } 203 204 /** 205 * Evaluates the given object as a long integer. 206 * 207 * @param value an object to interpret as a long integer 208 * @return the long integer value implied by the given object 209 * @throws NumberFormatException if the given object can't be understood as a long integer 210 */ 211 public static long longValue( Object value ) 212 { 213 if ( value == null ) 214 { 215 return 0L; 216 } 217 Class<?> c = value.getClass(); 218 if ( c.getSuperclass() == Number.class ) 219 { 220 return ( (Number) value ).longValue(); 221 } 222 if ( c == Boolean.class ) 223 { 224 return (Boolean) value ? 1 : 0; 225 } 226 if ( c == Character.class ) 227 { 228 return (Character) value; 229 } 230 return Long.parseLong( stringValue( value, true ) ); 231 } 232 233 /** 234 * Evaluates the given object as a double-precision floating-point number. 235 * 236 * @param value an object to interpret as a double 237 * @return the double value implied by the given object 238 * @throws NumberFormatException if the given object can't be understood as a double 239 */ 240 public static double doubleValue( Object value ) 241 { 242 if ( value == null ) 243 { 244 return 0.0; 245 } 246 Class<?> c = value.getClass(); 247 if ( c.getSuperclass() == Number.class ) 248 { 249 return ( (Number) value ).doubleValue(); 250 } 251 if ( c == Boolean.class ) 252 { 253 return (Boolean) value ? 1 : 0; 254 } 255 if ( c == Character.class ) 256 { 257 return (Character) value; 258 } 259 String s = stringValue( value, true ); 260 261 return ( s.length() == 0 ) ? 0.0 : Double.parseDouble( s ); 262 } 263 264 /** 265 * Evaluates the given object as a BigInteger. 266 * 267 * @param value an object to interpret as a BigInteger 268 * @return the BigInteger value implied by the given object 269 * @throws NumberFormatException if the given object can't be understood as a BigInteger 270 */ 271 public static BigInteger bigIntValue( Object value ) 272 { 273 if ( value == null ) 274 { 275 return BigInteger.valueOf( 0L ); 276 } 277 Class<?> c = value.getClass(); 278 if ( c == BigInteger.class ) 279 { 280 return (BigInteger) value; 281 } 282 if ( c == BigDecimal.class ) 283 { 284 return ( (BigDecimal) value ).toBigInteger(); 285 } 286 if ( c.getSuperclass() == Number.class ) 287 { 288 return BigInteger.valueOf( ( (Number) value ).longValue() ); 289 } 290 if ( c == Boolean.class ) 291 { 292 return BigInteger.valueOf( (Boolean) value ? 1 : 0 ); 293 } 294 if ( c == Character.class ) 295 { 296 return BigInteger.valueOf( (Character) value ); 297 } 298 return new BigInteger( stringValue( value, true ) ); 299 } 300 301 /** 302 * Evaluates the given object as a BigDecimal. 303 * 304 * @param value an object to interpret as a BigDecimal 305 * @return the BigDecimal value implied by the given object 306 * @throws NumberFormatException if the given object can't be understood as a BigDecimal 307 */ 308 public static BigDecimal bigDecValue( Object value ) 309 { 310 if ( value == null ) 311 { 312 return BigDecimal.valueOf( 0L ); 313 } 314 Class<?> c = value.getClass(); 315 if ( c == BigDecimal.class ) 316 { 317 return (BigDecimal) value; 318 } 319 if ( c == BigInteger.class ) 320 { 321 return new BigDecimal( (BigInteger) value ); 322 } 323 if ( c == Boolean.class ) 324 { 325 return BigDecimal.valueOf( (Boolean) value ? 1 : 0 ); 326 } 327 if ( c == Character.class ) 328 { 329 return BigDecimal.valueOf( ( (Character) value ).charValue() ); 330 } 331 return new BigDecimal( stringValue( value, true ) ); 332 } 333 334 /** 335 * Evaluates the given object as a String and trims it if the trim flag is true. 336 * 337 * @param value an object to interpret as a String 338 * @param trim if true trims the result 339 * @return the String value implied by the given object as returned by the toString() method, or "null" if the 340 * object is null. 341 */ 342 public static String stringValue( Object value, boolean trim ) 343 { 344 String result; 345 346 if ( value == null ) 347 { 348 result = OgnlRuntime.NULL_STRING; 349 } 350 else 351 { 352 result = value.toString(); 353 if ( trim ) 354 { 355 result = result.trim(); 356 } 357 } 358 return result; 359 } 360 361 /** 362 * Evaluates the given object as a String. 363 * 364 * @param value an object to interpret as a String 365 * @return the String value implied by the given object as returned by the toString() method, or "null" if the 366 * object is null. 367 */ 368 public static String stringValue( Object value ) 369 { 370 return stringValue( value, false ); 371 } 372 373 /** 374 * Returns a constant from the NumericTypes interface that represents the numeric type of the given object. 375 * 376 * @param value an object that needs to be interpreted as a number 377 * @return the appropriate constant from the NumericTypes interface 378 */ 379 public static int getNumericType( Object value ) 380 { 381 if ( value != null ) 382 { 383 Class<?> c = value.getClass(); 384 if ( c == Integer.class ) 385 { 386 return INT; 387 } 388 if ( c == Double.class ) 389 { 390 return DOUBLE; 391 } 392 if ( c == Boolean.class ) 393 { 394 return BOOL; 395 } 396 if ( c == Byte.class ) 397 { 398 return BYTE; 399 } 400 if ( c == Character.class ) 401 { 402 return CHAR; 403 } 404 if ( c == Short.class ) 405 { 406 return SHORT; 407 } 408 if ( c == Long.class ) 409 { 410 return LONG; 411 } 412 if ( c == Float.class ) 413 { 414 return FLOAT; 415 } 416 if ( c == BigInteger.class ) 417 { 418 return BIGINT; 419 } 420 if ( c == BigDecimal.class ) 421 { 422 return BIGDEC; 423 } 424 } 425 return NONNUMERIC; 426 } 427 428 public static Object toArray( char value, Class<?> toType ) 429 throws OgnlException 430 { 431 return toArray( Character.valueOf( value ), toType ); 432 } 433 434 public static Object toArray( byte value, Class<?> toType ) 435 throws OgnlException 436 { 437 return toArray( Byte.valueOf( value ), toType ); 438 } 439 440 public static Object toArray( int value, Class<?> toType ) 441 throws OgnlException 442 { 443 return toArray( Integer.valueOf( value ), toType ); 444 } 445 446 public static Object toArray( long value, Class<?> toType ) 447 throws OgnlException 448 { 449 return toArray( Long.valueOf( value ), toType ); 450 } 451 452 public static Object toArray( float value, Class<?> toType ) 453 throws OgnlException 454 { 455 return toArray( Float.valueOf( value ), toType ); 456 } 457 458 public static Object toArray( double value, Class<?> toType ) 459 throws OgnlException 460 { 461 return toArray( Double.valueOf( value ), toType ); 462 } 463 464 public static Object toArray( boolean value, Class<?> toType ) 465 throws OgnlException 466 { 467 return toArray( Boolean.valueOf( value ), toType ); 468 } 469 470 public static <T> Object convertValue( char value, Class<T> toType ) 471 throws OgnlException 472 { 473 return convertValue( Character.valueOf(value), toType ); 474 } 475 476 public static <T> Object convertValue( byte value, Class<T> toType ) 477 throws OgnlException 478 { 479 return convertValue( Byte.valueOf( value ), toType ); 480 } 481 482 public static <T> Object convertValue( int value, Class<T> toType ) 483 throws OgnlException 484 { 485 return convertValue( Integer.valueOf( value ), toType ); 486 } 487 488 public static <T> Object convertValue( long value, Class<T> toType ) 489 throws OgnlException 490 { 491 return convertValue( Long.valueOf( value ), toType ); 492 } 493 494 public static <T> Object convertValue( float value, Class<T> toType ) 495 throws OgnlException 496 { 497 return convertValue( Float.valueOf( value ), toType ); 498 } 499 500 public static <T> Object convertValue( double value, Class<T> toType ) 501 throws OgnlException 502 { 503 return convertValue( Double.valueOf( value ), toType ); 504 } 505 506 public static <T> Object convertValue( boolean value, Class<T> toType ) 507 throws OgnlException 508 { 509 return convertValue( Boolean.valueOf( value ), toType ); 510 } 511 512 // ////////////////////////////////////////////////////////////// 513 514 public static <T> Object convertValue( char value, Class<T> toType, boolean preventNull ) 515 throws OgnlException 516 { 517 return convertValue( Character.valueOf( value ), toType, preventNull ); 518 } 519 520 public static <T> Object convertValue( byte value, Class<T> toType, boolean preventNull ) 521 throws OgnlException 522 { 523 return convertValue( Byte.valueOf( value ), toType, preventNull ); 524 } 525 526 public static <T> Object convertValue( int value, Class<T> toType, boolean preventNull ) 527 throws OgnlException 528 { 529 return convertValue( Integer.valueOf( value ), toType, preventNull ); 530 } 531 532 public static <T> Object convertValue( long value, Class<T> toType, boolean preventNull ) 533 throws OgnlException 534 { 535 return convertValue( Long.valueOf( value ), toType, preventNull ); 536 } 537 538 public static <T> Object convertValue( float value, Class<T> toType, boolean preventNull ) 539 throws OgnlException 540 { 541 return convertValue( new Float( value ), toType, preventNull ); 542 } 543 544 public static <T> Object convertValue( double value, Class<T> toType, boolean preventNull ) 545 throws OgnlException 546 { 547 return convertValue( new Double( value ), toType, preventNull ); 548 } 549 550 public static <T> Object convertValue( boolean value, Class<T> toType, boolean preventNull ) 551 throws OgnlException 552 { 553 return convertValue( Boolean.valueOf( value ), toType, preventNull ); 554 } 555 556 // /////////////////////////////////////////////////////////////// 557 558 public static Object toArray( char value, Class<?> toType, boolean preventNull ) 559 throws OgnlException 560 { 561 return toArray( Character.valueOf( value ), toType, preventNull ); 562 } 563 564 public static Object toArray( byte value, Class<?> toType, boolean preventNull ) 565 throws OgnlException 566 { 567 return toArray( Byte.valueOf( value ), toType, preventNull ); 568 } 569 570 public static Object toArray( int value, Class<?> toType, boolean preventNull ) 571 throws OgnlException 572 { 573 return toArray( Integer.valueOf( value ), toType, preventNull ); 574 } 575 576 public static Object toArray( long value, Class<?> toType, boolean preventNull ) 577 throws OgnlException 578 { 579 return toArray( Long.valueOf(value), toType, preventNull ); 580 } 581 582 public static Object toArray( float value, Class<?> toType, boolean preventNull ) 583 throws OgnlException 584 { 585 return toArray( Float.valueOf( value ), toType, preventNull ); 586 } 587 588 public static Object toArray( double value, Class<?> toType, boolean preventNull ) 589 throws OgnlException 590 { 591 return toArray( Double.valueOf(value), toType, preventNull ); 592 } 593 594 public static Object toArray( boolean value, Class<?> toType, boolean preventNull ) 595 throws OgnlException 596 { 597 return toArray( Boolean.valueOf( value ), toType, preventNull ); 598 } 599 600 /** 601 * Returns the value converted numerically to the given class type This method also detects when arrays are being 602 * converted and converts the components of one array to the type of the other. 603 * 604 * @param value an object to be converted to the given type 605 * @param toType class type to be converted to 606 * @return converted value of the type given, or value if the value cannot be converted to the given type. 607 */ 608 public static Object convertValue( Object value, Class<?> toType ) 609 { 610 return convertValue( value, toType, false ); 611 } 612 613 public static Object toArray( Object value, Class<?> toType ) 614 throws OgnlException 615 { 616 return toArray( value, toType, false ); 617 } 618 619 public static Object toArray( Object value, Class<?> toType, boolean preventNulls ) 620 throws OgnlException 621 { 622 if ( value == null ) 623 { 624 return null; 625 } 626 627 Object result; 628 629 Class<?> aClass = value.getClass(); 630 if ( aClass.isArray() && toType.isAssignableFrom( aClass.getComponentType() ) ) 631 { 632 return value; 633 } 634 635 if ( !aClass.isArray() ) 636 { 637 638 if ( toType == Character.TYPE ) 639 { 640 return stringValue( value ).toCharArray(); 641 } 642 643 Object arr = Array.newInstance( toType, 1 ); 644 Array.set( arr, 0, convertValue( value, toType, preventNulls ) ); 645 646 return arr; 647 } 648 649 result = Array.newInstance( toType, Array.getLength( value ) ); 650 for ( int i = 0, icount = Array.getLength( value ); i < icount; i++ ) 651 { 652 Array.set( result, i, convertValue( Array.get( value, i ), toType ) ); 653 } 654 655 if ( result == null && preventNulls ) 656 { 657 return value; 658 } 659 660 return result; 661 } 662 663 public static <T> Object convertValue( Object value, Class<T> toType, boolean preventNulls ) 664 { 665 Object result = null; 666 667 if ( value != null && toType.isAssignableFrom( value.getClass() ) ) 668 { 669 return value; 670 } 671 672 if ( value != null ) 673 { 674 /* If array -> array then convert components of array individually */ 675 boolean classIsArray = value.getClass().isArray(); 676 boolean toTypeIsArray = toType.isArray(); 677 if ( classIsArray && toTypeIsArray) 678 { 679 Class<?> componentType = toType.getComponentType(); 680 681 result = Array.newInstance( componentType, Array.getLength( value ) ); 682 for ( int i = 0, icount = Array.getLength( value ); i < icount; i++ ) 683 { 684 Array.set( result, i, convertValue( Array.get( value, i ), componentType ) ); 685 } 686 } 687 else if ( classIsArray && !toTypeIsArray) 688 { 689 690 return convertValue( Array.get( value, 0 ), toType ); 691 } 692 else if ( toTypeIsArray ) 693 { 694 695 if ( toType.getComponentType() == Character.TYPE ) 696 { 697 698 result = stringValue( value ).toCharArray(); 699 } 700 else if ( toType.getComponentType() == Object.class ) 701 { 702 return new Object[] { value }; 703 } 704 } 705 else 706 { 707 if ( ( toType == Integer.class ) || ( toType == Integer.TYPE ) ) 708 { 709 result = (int) longValue( value ); 710 } 711 else if ( ( toType == Double.class ) || ( toType == Double.TYPE ) ) 712 { 713 result = doubleValue( value ); 714 } 715 else if ( ( toType == Boolean.class ) || ( toType == Boolean.TYPE ) ) 716 { 717 result = booleanValue( value ) ? Boolean.TRUE : Boolean.FALSE; 718 } 719 else if ( ( toType == Byte.class ) || ( toType == Byte.TYPE ) ) 720 { 721 result = (byte) longValue( value ); 722 } 723 else if ( ( toType == Character.class ) || ( toType == Character.TYPE ) ) 724 { 725 result = (char) longValue( value ); 726 } 727 else if ( ( toType == Short.class ) || ( toType == Short.TYPE ) ) 728 { 729 result = (short) longValue( value ); 730 } 731 else if ( ( toType == Long.class ) || ( toType == Long.TYPE ) ) 732 { 733 result = longValue( value ); 734 } 735 else if ( ( toType == Float.class ) || ( toType == Float.TYPE ) ) 736 { 737 result = new Float( doubleValue( value ) ); 738 } 739 else if ( toType == BigInteger.class ) 740 { 741 result = bigIntValue( value ); 742 } 743 else if ( toType == BigDecimal.class ) 744 { 745 result = bigDecValue( value ); 746 } 747 else if ( toType == String.class ) 748 { 749 result = stringValue( value ); 750 } 751 } 752 } 753 else 754 { 755 if ( toType.isPrimitive() ) 756 { 757 result = OgnlRuntime.getPrimitiveDefaultValue( toType ); 758 } 759 else if ( preventNulls && toType == Boolean.class ) 760 { 761 result = Boolean.FALSE; 762 } 763 else if ( preventNulls && Number.class.isAssignableFrom( toType ) ) 764 { 765 result = OgnlRuntime.getNumericDefaultValue( toType ); 766 } 767 } 768 769 if ( result == null && preventNulls ) 770 { 771 return value; 772 } 773 774 if ( value != null && result == null ) 775 { 776 777 throw new IllegalArgumentException( "Unable to convert type " + value.getClass().getName() + " of " + value 778 + " to type of " + toType.getName() ); 779 } 780 781 return result; 782 } 783 784 /** 785 * Converts the specified value to a primitive integer value. 786 * <ul> 787 * <li>Null values will cause a -1 to be returned.</li> 788 * <li>{@link Number} instances have their intValue() methods invoked.</li> 789 * <li>All other types result in calling Integer.parseInt(value.toString());</li> 790 * </ul> 791 * 792 * @param value The object to get the value of. 793 * @return A valid integer. 794 */ 795 public static int getIntValue( Object value ) 796 { 797 try 798 { 799 if ( value == null ) 800 { 801 return -1; 802 } 803 804 if ( Number.class.isInstance( value ) ) 805 { 806 807 return ( (Number) value ).intValue(); 808 } 809 810 String str = String.class.isInstance( value ) ? (String) value : value.toString(); 811 812 return Integer.parseInt( str ); 813 } 814 catch ( Throwable t ) 815 { 816 throw new RuntimeException( "Error converting " + value + " to integer:", t ); 817 } 818 } 819 820 /** 821 * Returns the constant from the NumericTypes interface that best expresses the type of a numeric operation on the 822 * two given objects. 823 * 824 * @param v1 one argument to a numeric operator 825 * @param v2 the other argument 826 * @return the appropriate constant from the NumericTypes interface 827 */ 828 public static int getNumericType( Object v1, Object v2 ) 829 { 830 return getNumericType( v1, v2, false ); 831 } 832 833 /** 834 * Returns the constant from the NumericTypes interface that best expresses the type of an operation, which can be 835 * either numeric or not, on the two given types. 836 * 837 * @param t1 type of one argument to an operator 838 * @param t2 type of the other argument 839 * @param canBeNonNumeric whether the operator can be interpreted as non-numeric 840 * @return the appropriate constant from the NumericTypes interface 841 */ 842 public static int getNumericType( int t1, int t2, boolean canBeNonNumeric ) 843 { 844 if ( t1 == t2 ) 845 { 846 return t1; 847 } 848 849 if ( canBeNonNumeric && ( t1 == NONNUMERIC || t2 == NONNUMERIC || t1 == CHAR || t2 == CHAR ) ) 850 { 851 return NONNUMERIC; 852 } 853 854 if ( t1 == NONNUMERIC ) 855 { 856 t1 = DOUBLE; // Try to interpret strings as doubles... 857 } 858 if ( t2 == NONNUMERIC ) 859 { 860 t2 = DOUBLE; // Try to interpret strings as doubles... 861 } 862 863 if ( t1 >= MIN_REAL_TYPE ) 864 { 865 if ( t2 >= MIN_REAL_TYPE ) 866 { 867 return Math.max( t1, t2 ); 868 } 869 if ( t2 < INT ) 870 { 871 return t1; 872 } 873 if ( t2 == BIGINT ) 874 { 875 return BIGDEC; 876 } 877 return Math.max( DOUBLE, t1 ); 878 } 879 else if ( t2 >= MIN_REAL_TYPE ) 880 { 881 if ( t1 < INT ) 882 { 883 return t2; 884 } 885 if ( t1 == BIGINT ) 886 { 887 return BIGDEC; 888 } 889 return Math.max( DOUBLE, t2 ); 890 } 891 else 892 { 893 return Math.max( t1, t2 ); 894 } 895 } 896 897 /** 898 * Returns the constant from the NumericTypes interface that best expresses the type of an operation, which can be 899 * either numeric or not, on the two given objects. 900 * 901 * @param v1 one argument to an operator 902 * @param v2 the other argument 903 * @param canBeNonNumeric whether the operator can be interpreted as non-numeric 904 * @return the appropriate constant from the NumericTypes interface 905 */ 906 public static int getNumericType( Object v1, Object v2, boolean canBeNonNumeric ) 907 { 908 return getNumericType( getNumericType( v1 ), getNumericType( v2 ), canBeNonNumeric ); 909 } 910 911 /** 912 * Returns a new Number object of an appropriate type to hold the given integer value. The type of the returned 913 * object is consistent with the given type argument, which is a constant from the NumericTypes interface. 914 * 915 * @param type the nominal numeric type of the result, a constant from the NumericTypes interface 916 * @param value the integer value to convert to a Number object 917 * @return a Number object with the given value, of type implied by the type argument 918 */ 919 public static Number newInteger( int type, long value ) 920 { 921 switch ( type ) 922 { 923 case BOOL: 924 case CHAR: 925 case INT: 926 return (int) value; 927 928 case FLOAT: 929 if ( (long) (float) value == value ) 930 { 931 return (float) value; 932 } 933 // else fall through: 934 case DOUBLE: 935 if ( (long) (double) value == value ) 936 { 937 return (double) value; 938 } 939 // else fall through: 940 case LONG: 941 return value; 942 943 case BYTE: 944 return (byte) value; 945 946 case SHORT: 947 return (short) value; 948 949 default: 950 return BigInteger.valueOf( value ); 951 } 952 } 953 954 /** 955 * Returns a new Number object of an appropriate type to hold the given real value. The type of the returned object 956 * is always either Float or Double, and is only Float if the given type tag (a constant from the NumericTypes 957 * interface) is FLOAT. 958 * 959 * @param type the nominal numeric type of the result, a constant from the NumericTypes interface 960 * @param value the real value to convert to a Number object 961 * @return a Number object with the given value, of type implied by the type argument 962 */ 963 public static Number newReal( int type, double value ) 964 { 965 if ( type == FLOAT ) 966 { 967 return (float) value; 968 } 969 return value; 970 } 971 972 public static Object binaryOr( Object v1, Object v2 ) 973 { 974 int type = getNumericType( v1, v2 ); 975 if ( type == BIGINT || type == BIGDEC ) 976 { 977 return bigIntValue( v1 ).or( bigIntValue( v2 ) ); 978 } 979 return newInteger( type, longValue( v1 ) | longValue( v2 ) ); 980 } 981 982 public static Object binaryXor( Object v1, Object v2 ) 983 { 984 int type = getNumericType( v1, v2 ); 985 if ( type == BIGINT || type == BIGDEC ) 986 { 987 return bigIntValue( v1 ).xor( bigIntValue( v2 ) ); 988 } 989 return newInteger( type, longValue( v1 ) ^ longValue( v2 ) ); 990 } 991 992 public static Object binaryAnd( Object v1, Object v2 ) 993 { 994 int type = getNumericType( v1, v2 ); 995 if ( type == BIGINT || type == BIGDEC ) 996 { 997 return bigIntValue( v1 ).and( bigIntValue( v2 ) ); 998 } 999 return newInteger( type, longValue( v1 ) & longValue( v2 ) ); 1000 } 1001 1002 public static boolean equal( Object v1, Object v2 ) 1003 { 1004 if ( v1 == null ) 1005 { 1006 return v2 == null; 1007 } 1008 if ( v1 == v2 || isEqual( v1, v2 ) ) 1009 { 1010 return true; 1011 } 1012 if ( v1 instanceof Number && v2 instanceof Number ) 1013 { 1014 return ( (Number) v1 ).doubleValue() == ( (Number) v2 ).doubleValue(); 1015 } 1016 return false; 1017 } 1018 1019 public static boolean less( Object v1, Object v2 ) 1020 { 1021 return compareWithConversion( v1, v2 ) < 0; 1022 } 1023 1024 public static boolean greater( Object v1, Object v2 ) 1025 { 1026 return compareWithConversion( v1, v2 ) > 0; 1027 } 1028 1029 public static boolean in( Object v1, Object v2 ) 1030 throws OgnlException 1031 { 1032 if ( v2 == null ) // A null collection is always treated as empty 1033 { 1034 return false; 1035 } 1036 1037 ElementsAccessor elementsAccessor = OgnlRuntime.getElementsAccessor( OgnlRuntime.getTargetClass( v2 ) ); 1038 1039 // FIXME O(n) is there a better way?! 1040 for ( Enumeration<?> e = elementsAccessor.getElements( v2 ); e.hasMoreElements(); ) 1041 { 1042 Object o = e.nextElement(); 1043 1044 if ( equal( v1, o ) ) 1045 { 1046 return true; 1047 } 1048 } 1049 1050 return false; 1051 } 1052 1053 public static Object shiftLeft( Object v1, Object v2 ) 1054 { 1055 int type = getNumericType( v1 ); 1056 if ( type == BIGINT || type == BIGDEC ) 1057 { 1058 return bigIntValue( v1 ).shiftLeft( (int) longValue( v2 ) ); 1059 } 1060 return newInteger( type, longValue( v1 ) << (int) longValue( v2 ) ); 1061 } 1062 1063 public static Object shiftRight( Object v1, Object v2 ) 1064 { 1065 int type = getNumericType( v1 ); 1066 if ( type == BIGINT || type == BIGDEC ) 1067 { 1068 return bigIntValue( v1 ).shiftRight( (int) longValue( v2 ) ); 1069 } 1070 return newInteger( type, longValue( v1 ) >> (int) longValue( v2 ) ); 1071 } 1072 1073 public static Object unsignedShiftRight( Object v1, Object v2 ) 1074 { 1075 int type = getNumericType( v1 ); 1076 if ( type == BIGINT || type == BIGDEC ) 1077 { 1078 return bigIntValue( v1 ).shiftRight( (int) longValue( v2 ) ); 1079 } 1080 if ( type <= INT ) 1081 { 1082 return newInteger( INT, ( (int) longValue( v1 ) ) >>> (int) longValue( v2 ) ); 1083 } 1084 return newInteger( type, longValue( v1 ) >>> (int) longValue( v2 ) ); 1085 } 1086 1087 public static Object add( Object v1, Object v2 ) 1088 { 1089 int type = getNumericType( v1, v2, true ); 1090 switch ( type ) 1091 { 1092 case BIGINT: 1093 return bigIntValue( v1 ).add( bigIntValue( v2 ) ); 1094 case BIGDEC: 1095 return bigDecValue( v1 ).add( bigDecValue( v2 ) ); 1096 case FLOAT: 1097 case DOUBLE: 1098 return newReal( type, doubleValue( v1 ) + doubleValue( v2 ) ); 1099 case NONNUMERIC: 1100 int t1 = getNumericType( v1 ), 1101 t2 = getNumericType( v2 ); 1102 1103 if ( ( ( t1 != NONNUMERIC ) && ( v2 == null ) ) || ( ( t2 != NONNUMERIC ) && ( v1 == null ) ) ) 1104 { 1105 throw new NullPointerException( "Can't add values " + v1 + " , " + v2 ); 1106 } 1107 1108 return stringValue( v1 ) + stringValue( v2 ); 1109 default: 1110 return newInteger( type, longValue( v1 ) + longValue( v2 ) ); 1111 } 1112 } 1113 1114 public static Object subtract( Object v1, Object v2 ) 1115 { 1116 int type = getNumericType( v1, v2 ); 1117 switch ( type ) 1118 { 1119 case BIGINT: 1120 return bigIntValue( v1 ).subtract( bigIntValue( v2 ) ); 1121 case BIGDEC: 1122 return bigDecValue( v1 ).subtract( bigDecValue( v2 ) ); 1123 case FLOAT: 1124 case DOUBLE: 1125 return newReal( type, doubleValue( v1 ) - doubleValue( v2 ) ); 1126 default: 1127 return newInteger( type, longValue( v1 ) - longValue( v2 ) ); 1128 } 1129 } 1130 1131 public static Object multiply( Object v1, Object v2 ) 1132 { 1133 int type = getNumericType( v1, v2 ); 1134 switch ( type ) 1135 { 1136 case BIGINT: 1137 return bigIntValue( v1 ).multiply( bigIntValue( v2 ) ); 1138 case BIGDEC: 1139 return bigDecValue( v1 ).multiply( bigDecValue( v2 ) ); 1140 case FLOAT: 1141 case DOUBLE: 1142 return newReal( type, doubleValue( v1 ) * doubleValue( v2 ) ); 1143 default: 1144 return newInteger( type, longValue( v1 ) * longValue( v2 ) ); 1145 } 1146 } 1147 1148 public static Object divide( Object v1, Object v2 ) 1149 { 1150 int type = getNumericType( v1, v2 ); 1151 switch ( type ) 1152 { 1153 case BIGINT: 1154 return bigIntValue( v1 ).divide( bigIntValue( v2 ) ); 1155 case BIGDEC: 1156 return bigDecValue( v1 ).divide( bigDecValue( v2 ), BigDecimal.ROUND_HALF_EVEN ); 1157 case FLOAT: 1158 case DOUBLE: 1159 return newReal( type, doubleValue( v1 ) / doubleValue( v2 ) ); 1160 default: 1161 return newInteger( type, longValue( v1 ) / longValue( v2 ) ); 1162 } 1163 } 1164 1165 public static Object remainder( Object v1, Object v2 ) 1166 { 1167 int type = getNumericType( v1, v2 ); 1168 switch ( type ) 1169 { 1170 case BIGDEC: 1171 case BIGINT: 1172 return bigIntValue( v1 ).remainder( bigIntValue( v2 ) ); 1173 default: 1174 return newInteger( type, longValue( v1 ) % longValue( v2 ) ); 1175 } 1176 } 1177 1178 public static Object negate( Object value ) 1179 { 1180 int type = getNumericType( value ); 1181 switch ( type ) 1182 { 1183 case BIGINT: 1184 return bigIntValue( value ).negate(); 1185 case BIGDEC: 1186 return bigDecValue( value ).negate(); 1187 case FLOAT: 1188 case DOUBLE: 1189 return newReal( type, -doubleValue( value ) ); 1190 default: 1191 return newInteger( type, -longValue( value ) ); 1192 } 1193 } 1194 1195 public static Object bitNegate( Object value ) 1196 { 1197 int type = getNumericType( value ); 1198 switch ( type ) 1199 { 1200 case BIGDEC: 1201 case BIGINT: 1202 return bigIntValue( value ).not(); 1203 default: 1204 return newInteger( type, ~longValue( value ) ); 1205 } 1206 } 1207 1208 public static String getEscapeString( String value ) 1209 { 1210 StringBuilder result = new StringBuilder(); 1211 1212 int length = value.length(); 1213 for ( int i = 0; i < length; i++ ) 1214 { 1215 result.append( getEscapedChar( value.charAt( i ) ) ); 1216 } 1217 return result.toString(); 1218 } 1219 1220 public static String getEscapedChar( char ch ) 1221 { 1222 String result; 1223 1224 switch ( ch ) 1225 { 1226 case '\b': 1227 result = "\b"; 1228 break; 1229 case '\t': 1230 result = "\\t"; 1231 break; 1232 case '\n': 1233 result = "\\n"; 1234 break; 1235 case '\f': 1236 result = "\\f"; 1237 break; 1238 case '\r': 1239 result = "\\r"; 1240 break; 1241 case '\"': 1242 result = "\\\""; 1243 break; 1244 case '\'': 1245 result = "\\\'"; 1246 break; 1247 case '\\': 1248 result = "\\\\"; 1249 break; 1250 default: 1251 if ( Character.isISOControl( ch ) ) 1252 { 1253 1254 String hc = Integer.toString( (int) ch, 16 ); 1255 int hcl = hc.length(); 1256 1257 result = "\\u"; 1258 if ( hcl < 4 ) 1259 { 1260 if ( hcl == 3 ) 1261 { 1262 result = result + "0"; 1263 } 1264 else 1265 { 1266 if ( hcl == 2 ) 1267 { 1268 result = result + "00"; 1269 } 1270 else 1271 { 1272 result = result + "000"; 1273 } 1274 } 1275 } 1276 1277 result = result + hc; 1278 } 1279 else 1280 { 1281 result = ch + ""; 1282 } 1283 break; 1284 } 1285 return result; 1286 } 1287 1288 public static Object returnValue( Object ignore, Object returnValue ) 1289 { 1290 return returnValue; 1291 } 1292 1293 /** 1294 * Utility method that converts incoming exceptions to {@link RuntimeException} instances - or casts them if they 1295 * already are. 1296 * 1297 * @param t The exception to cast. 1298 * @return The exception cast to a {@link RuntimeException}. 1299 */ 1300 public static RuntimeException castToRuntime( Throwable t ) 1301 { 1302 if ( RuntimeException.class.isInstance( t ) ) 1303 { 1304 return (RuntimeException) t; 1305 } 1306 1307 if ( OgnlException.class.isInstance( t ) ) 1308 { 1309 throw new UnsupportedCompilationException( "Error evluating expression: " + t.getMessage(), t ); 1310 } 1311 1312 return new RuntimeException( t ); 1313 } 1314 }