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 }