blob: a6b9eeca07233b95f6bd681985154f803b39498d [file] [log] [blame]
package org.apache.commons.ognl;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import org.apache.commons.ognl.enhance.UnsupportedCompilationException;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Enumeration;
/**
* This is an abstract class with static methods that define the operations of OGNL.
*/
public abstract class OgnlOps
implements NumericTypes
{
/**
* Compares two objects for equality, even if it has to convert one of them to the other type. If both objects are
* numeric they are converted to the widest type and compared. If one is non-numeric and one is numeric the
* non-numeric is converted to double and compared to the double numeric value. If both are non-numeric and
* Comparable and the types are compatible (i.e. v1 is of the same or superclass of v2's type) they are compared
* with Comparable.compareTo(). If both values are non-numeric and not Comparable or of incompatible classes this
* will throw and IllegalArgumentException.
*
* @param v1 First value to compare
* @param v2 second value to compare
* @return integer describing the comparison between the two objects. A negative number indicates that v1 < v2.
* Positive indicates that v1 > v2. Zero indicates v1 == v2.
* @throws IllegalArgumentException if the objects are both non-numeric yet of incompatible types or do not
* implement Comparable.
*/
public static int compareWithConversion( Object v1, Object v2 )
{
int result;
if ( v1 == v2 )
{
result = 0;
}
else
{
int t1 = getNumericType( v1 ), t2 = getNumericType( v2 ), type = getNumericType( t1, t2, true );
switch ( type )
{
case BIGINT:
result = bigIntValue( v1 ).compareTo( bigIntValue( v2 ) );
break;
case BIGDEC:
result = bigDecValue( v1 ).compareTo( bigDecValue( v2 ) );
break;
case NONNUMERIC:
if ( ( t1 == NONNUMERIC ) && ( t2 == NONNUMERIC ) )
{
if ( ( v1 instanceof Comparable ) && v1.getClass().isAssignableFrom( v2.getClass() ) )
{
result = ( (Comparable) v1 ).compareTo( v2 );
break;
}
throw new IllegalArgumentException( "invalid comparison: " + v1.getClass().getName()
+ " and " + v2.getClass().getName() );
}
// else fall through
case FLOAT:
case DOUBLE:
double dv1 = doubleValue( v1 ),
dv2 = doubleValue( v2 );
return Double.compare(dv1, dv2);
default:
long lv1 = longValue( v1 ),
lv2 = longValue( v2 );
return Long.compare(lv1, lv2);
}
}
return result;
}
/**
* Returns true if object1 is equal to object2 in either the sense that they are the same object or, if both are
* non-null if they are equal in the <CODE>equals()</CODE> sense.
*
* @param object1 First object to compare
* @param object2 Second object to compare
* @return true if v1 == v2
*/
public static boolean isEqual( Object object1, Object object2 )
{
boolean result = false;
if ( object1 == object2 )
{
result = true;
}
else
{
if ( ( object1 != null ) && object1.getClass().isArray() )
{
if ( ( object2 != null ) && object2.getClass().isArray() && ( object2.getClass()
== object1.getClass() ) )
{
result = ( Array.getLength( object1 ) == Array.getLength( object2 ) );
if ( result )
{
for ( int i = 0, icount = Array.getLength( object1 ); result && ( i < icount ); i++ )
{
result = isEqual( Array.get( object1, i ), Array.get( object2, i ) );
}
}
}
}
else
{
// Check for converted equivalence first, then equals() equivalence
result =
( object1 != null ) && ( object2 != null )
&& ( object1.equals( object2 ) || ( compareWithConversion( object1, object2 ) == 0 ) );
}
}
return result;
}
public static boolean booleanValue( boolean value )
{
return value;
}
public static boolean booleanValue( int value )
{
return value > 0;
}
public static boolean booleanValue( float value )
{
return value > 0;
}
public static boolean booleanValue( long value )
{
return value > 0;
}
public static boolean booleanValue( double value )
{
return value > 0;
}
/**
* Evaluates the given object as a boolean: if it is a Boolean object, it's easy; if it's a Number or a Character,
* returns true for non-zero objects; and otherwise returns true for non-null objects.
*
* @param value an object to interpret as a boolean
* @return the boolean value implied by the given object
*/
public static boolean booleanValue( Object value )
{
if ( value == null )
{
return false;
}
Class<?> c = value.getClass();
if ( c == Boolean.class )
{
return (Boolean) value;
}
// if ( c == String.class )
// return ((String)value).length() > 0;
if ( c == Character.class )
{
return (Character) value != 0;
}
return !( value instanceof Number ) || ( (Number) value ).doubleValue() != 0;
}
/**
* Evaluates the given object as a long integer.
*
* @param value an object to interpret as a long integer
* @return the long integer value implied by the given object
* @throws NumberFormatException if the given object can't be understood as a long integer
*/
public static long longValue( Object value )
{
if ( value == null )
{
return 0L;
}
Class<?> c = value.getClass();
if ( c.getSuperclass() == Number.class )
{
return ( (Number) value ).longValue();
}
if ( c == Boolean.class )
{
return (Boolean) value ? 1 : 0;
}
if ( c == Character.class )
{
return (Character) value;
}
return Long.parseLong( stringValue( value, true ) );
}
/**
* Evaluates the given object as a double-precision floating-point number.
*
* @param value an object to interpret as a double
* @return the double value implied by the given object
* @throws NumberFormatException if the given object can't be understood as a double
*/
public static double doubleValue( Object value )
{
if ( value == null )
{
return 0.0;
}
Class<?> c = value.getClass();
if ( c.getSuperclass() == Number.class )
{
return ( (Number) value ).doubleValue();
}
if ( c == Boolean.class )
{
return (Boolean) value ? 1 : 0;
}
if ( c == Character.class )
{
return (Character) value;
}
String s = stringValue( value, true );
return ( s.isEmpty() ) ? 0.0 : Double.parseDouble( s );
}
/**
* Evaluates the given object as a BigInteger.
*
* @param value an object to interpret as a BigInteger
* @return the BigInteger value implied by the given object
* @throws NumberFormatException if the given object can't be understood as a BigInteger
*/
public static BigInteger bigIntValue( Object value )
{
if ( value == null )
{
return BigInteger.valueOf( 0L );
}
Class<?> c = value.getClass();
if ( c == BigInteger.class )
{
return (BigInteger) value;
}
if ( c == BigDecimal.class )
{
return ( (BigDecimal) value ).toBigInteger();
}
if ( c.getSuperclass() == Number.class )
{
return BigInteger.valueOf( ( (Number) value ).longValue() );
}
if ( c == Boolean.class )
{
return BigInteger.valueOf( (Boolean) value ? 1 : 0 );
}
if ( c == Character.class )
{
return BigInteger.valueOf( (Character) value );
}
return new BigInteger( stringValue( value, true ) );
}
/**
* Evaluates the given object as a BigDecimal.
*
* @param value an object to interpret as a BigDecimal
* @return the BigDecimal value implied by the given object
* @throws NumberFormatException if the given object can't be understood as a BigDecimal
*/
public static BigDecimal bigDecValue( Object value )
{
if ( value == null )
{
return BigDecimal.valueOf( 0L );
}
Class<?> c = value.getClass();
if ( c == BigDecimal.class )
{
return (BigDecimal) value;
}
if ( c == BigInteger.class )
{
return new BigDecimal( (BigInteger) value );
}
if ( c == Boolean.class )
{
return BigDecimal.valueOf( (Boolean) value ? 1 : 0 );
}
if ( c == Character.class )
{
return BigDecimal.valueOf( ( (Character) value ).charValue() );
}
return new BigDecimal( stringValue( value, true ) );
}
/**
* Evaluates the given object as a String and trims it if the trim flag is true.
*
* @param value an object to interpret as a String
* @param trim if true trims the result
* @return the String value implied by the given object as returned by the toString() method, or "null" if the
* object is null.
*/
public static String stringValue( Object value, boolean trim )
{
String result;
if ( value == null )
{
result = OgnlRuntime.NULL_STRING;
}
else
{
result = value.toString();
if ( trim )
{
result = result.trim();
}
}
return result;
}
/**
* Evaluates the given object as a String.
*
* @param value an object to interpret as a String
* @return the String value implied by the given object as returned by the toString() method, or "null" if the
* object is null.
*/
public static String stringValue( Object value )
{
return stringValue( value, false );
}
/**
* Returns a constant from the NumericTypes interface that represents the numeric type of the given object.
*
* @param value an object that needs to be interpreted as a number
* @return the appropriate constant from the NumericTypes interface
*/
public static int getNumericType( Object value )
{
if ( value != null )
{
Class<?> c = value.getClass();
if ( c == Integer.class )
{
return INT;
}
if ( c == Double.class )
{
return DOUBLE;
}
if ( c == Boolean.class )
{
return BOOL;
}
if ( c == Byte.class )
{
return BYTE;
}
if ( c == Character.class )
{
return CHAR;
}
if ( c == Short.class )
{
return SHORT;
}
if ( c == Long.class )
{
return LONG;
}
if ( c == Float.class )
{
return FLOAT;
}
if ( c == BigInteger.class )
{
return BIGINT;
}
if ( c == BigDecimal.class )
{
return BIGDEC;
}
}
return NONNUMERIC;
}
public static Object toArray( char value, Class<?> toType )
throws OgnlException
{
return toArray( Character.valueOf( value ), toType );
}
public static Object toArray( byte value, Class<?> toType )
throws OgnlException
{
return toArray( Byte.valueOf( value ), toType );
}
public static Object toArray( int value, Class<?> toType )
throws OgnlException
{
return toArray( Integer.valueOf( value ), toType );
}
public static Object toArray( long value, Class<?> toType )
throws OgnlException
{
return toArray( Long.valueOf( value ), toType );
}
public static Object toArray( float value, Class<?> toType )
throws OgnlException
{
return toArray( Float.valueOf( value ), toType );
}
public static Object toArray( double value, Class<?> toType )
throws OgnlException
{
return toArray( Double.valueOf( value ), toType );
}
public static Object toArray( boolean value, Class<?> toType )
throws OgnlException
{
return toArray( Boolean.valueOf( value ), toType );
}
public static <T> Object convertValue( char value, Class<T> toType )
throws OgnlException
{
return convertValue( Character.valueOf(value), toType );
}
public static <T> Object convertValue( byte value, Class<T> toType )
throws OgnlException
{
return convertValue( Byte.valueOf( value ), toType );
}
public static <T> Object convertValue( int value, Class<T> toType )
throws OgnlException
{
return convertValue( Integer.valueOf( value ), toType );
}
public static <T> Object convertValue( long value, Class<T> toType )
throws OgnlException
{
return convertValue( Long.valueOf( value ), toType );
}
public static <T> Object convertValue( float value, Class<T> toType )
throws OgnlException
{
return convertValue( Float.valueOf( value ), toType );
}
public static <T> Object convertValue( double value, Class<T> toType )
throws OgnlException
{
return convertValue( Double.valueOf( value ), toType );
}
public static <T> Object convertValue( boolean value, Class<T> toType )
throws OgnlException
{
return convertValue( Boolean.valueOf( value ), toType );
}
// //////////////////////////////////////////////////////////////
public static <T> Object convertValue( char value, Class<T> toType, boolean preventNull )
throws OgnlException
{
return convertValue( Character.valueOf( value ), toType, preventNull );
}
public static <T> Object convertValue( byte value, Class<T> toType, boolean preventNull )
throws OgnlException
{
return convertValue( Byte.valueOf( value ), toType, preventNull );
}
public static <T> Object convertValue( int value, Class<T> toType, boolean preventNull )
throws OgnlException
{
return convertValue( Integer.valueOf( value ), toType, preventNull );
}
public static <T> Object convertValue( long value, Class<T> toType, boolean preventNull )
throws OgnlException
{
return convertValue( Long.valueOf( value ), toType, preventNull );
}
public static <T> Object convertValue( float value, Class<T> toType, boolean preventNull )
throws OgnlException
{
return convertValue( new Float( value ), toType, preventNull );
}
public static <T> Object convertValue( double value, Class<T> toType, boolean preventNull )
throws OgnlException
{
return convertValue( new Double( value ), toType, preventNull );
}
public static <T> Object convertValue( boolean value, Class<T> toType, boolean preventNull )
throws OgnlException
{
return convertValue( Boolean.valueOf( value ), toType, preventNull );
}
// ///////////////////////////////////////////////////////////////
public static Object toArray( char value, Class<?> toType, boolean preventNull )
throws OgnlException
{
return toArray( Character.valueOf( value ), toType, preventNull );
}
public static Object toArray( byte value, Class<?> toType, boolean preventNull )
throws OgnlException
{
return toArray( Byte.valueOf( value ), toType, preventNull );
}
public static Object toArray( int value, Class<?> toType, boolean preventNull )
throws OgnlException
{
return toArray( Integer.valueOf( value ), toType, preventNull );
}
public static Object toArray( long value, Class<?> toType, boolean preventNull )
throws OgnlException
{
return toArray( Long.valueOf(value), toType, preventNull );
}
public static Object toArray( float value, Class<?> toType, boolean preventNull )
throws OgnlException
{
return toArray( Float.valueOf( value ), toType, preventNull );
}
public static Object toArray( double value, Class<?> toType, boolean preventNull )
throws OgnlException
{
return toArray( Double.valueOf(value), toType, preventNull );
}
public static Object toArray( boolean value, Class<?> toType, boolean preventNull )
throws OgnlException
{
return toArray( Boolean.valueOf( value ), toType, preventNull );
}
/**
* Returns the value converted numerically to the given class type This method also detects when arrays are being
* converted and converts the components of one array to the type of the other.
*
* @param value an object to be converted to the given type
* @param toType class type to be converted to
* @return converted value of the type given, or value if the value cannot be converted to the given type.
*/
public static Object convertValue( Object value, Class<?> toType )
{
return convertValue( value, toType, false );
}
public static Object toArray( Object value, Class<?> toType )
throws OgnlException
{
return toArray( value, toType, false );
}
public static Object toArray( Object value, Class<?> toType, boolean preventNulls )
throws OgnlException
{
if ( value == null )
{
return null;
}
Object result;
Class<?> aClass = value.getClass();
if ( aClass.isArray() && toType.isAssignableFrom( aClass.getComponentType() ) )
{
return value;
}
if ( !aClass.isArray() )
{
if ( toType == Character.TYPE )
{
return stringValue( value ).toCharArray();
}
Object arr = Array.newInstance( toType, 1 );
Array.set( arr, 0, convertValue( value, toType, preventNulls ) );
return arr;
}
result = Array.newInstance( toType, Array.getLength( value ) );
for ( int i = 0, icount = Array.getLength( value ); i < icount; i++ )
{
Array.set( result, i, convertValue( Array.get( value, i ), toType ) );
}
if ( result == null && preventNulls )
{
return value;
}
return result;
}
public static <T> Object convertValue( Object value, Class<T> toType, boolean preventNulls )
{
Object result = null;
if ( value != null && toType.isAssignableFrom( value.getClass() ) )
{
return value;
}
if ( value != null )
{
/* If array -> array then convert components of array individually */
boolean classIsArray = value.getClass().isArray();
boolean toTypeIsArray = toType.isArray();
if ( classIsArray && toTypeIsArray)
{
Class<?> componentType = toType.getComponentType();
result = Array.newInstance( componentType, Array.getLength( value ) );
for ( int i = 0, icount = Array.getLength( value ); i < icount; i++ )
{
Array.set( result, i, convertValue( Array.get( value, i ), componentType ) );
}
}
else if ( classIsArray && !toTypeIsArray)
{
return convertValue( Array.get( value, 0 ), toType );
}
else if ( toTypeIsArray )
{
if ( toType.getComponentType() == Character.TYPE )
{
result = stringValue( value ).toCharArray();
}
else if ( toType.getComponentType() == Object.class )
{
return new Object[] { value };
}
}
else
{
if ( ( toType == Integer.class ) || ( toType == Integer.TYPE ) )
{
result = (int) longValue( value );
}
else if ( ( toType == Double.class ) || ( toType == Double.TYPE ) )
{
result = doubleValue( value );
}
else if ( ( toType == Boolean.class ) || ( toType == Boolean.TYPE ) )
{
result = booleanValue( value ) ? Boolean.TRUE : Boolean.FALSE;
}
else if ( ( toType == Byte.class ) || ( toType == Byte.TYPE ) )
{
result = (byte) longValue( value );
}
else if ( ( toType == Character.class ) || ( toType == Character.TYPE ) )
{
result = (char) longValue( value );
}
else if ( ( toType == Short.class ) || ( toType == Short.TYPE ) )
{
result = (short) longValue( value );
}
else if ( ( toType == Long.class ) || ( toType == Long.TYPE ) )
{
result = longValue( value );
}
else if ( ( toType == Float.class ) || ( toType == Float.TYPE ) )
{
result = new Float( doubleValue( value ) );
}
else if ( toType == BigInteger.class )
{
result = bigIntValue( value );
}
else if ( toType == BigDecimal.class )
{
result = bigDecValue( value );
}
else if ( toType == String.class )
{
result = stringValue( value );
}
}
}
else
{
if ( toType.isPrimitive() )
{
result = OgnlRuntime.getPrimitiveDefaultValue( toType );
}
else if ( preventNulls && toType == Boolean.class )
{
result = Boolean.FALSE;
}
else if ( preventNulls && Number.class.isAssignableFrom( toType ) )
{
result = OgnlRuntime.getNumericDefaultValue( toType );
}
}
if ( result == null && preventNulls )
{
return value;
}
if ( value != null && result == null )
{
throw new IllegalArgumentException( "Unable to convert type " + value.getClass().getName() + " of " + value
+ " to type of " + toType.getName() );
}
return result;
}
/**
* Converts the specified value to a primitive integer value.
* <ul>
* <li>Null values will cause a -1 to be returned.</li>
* <li>{@link Number} instances have their intValue() methods invoked.</li>
* <li>All other types result in calling Integer.parseInt(value.toString());</li>
* </ul>
*
* @param value The object to get the value of.
* @return A valid integer.
*/
public static int getIntValue( Object value )
{
try
{
if ( value == null )
{
return -1;
}
if ( Number.class.isInstance( value ) )
{
return ( (Number) value ).intValue();
}
String str = String.class.isInstance( value ) ? (String) value : value.toString();
return Integer.parseInt( str );
}
catch ( Throwable t )
{
throw new RuntimeException( "Error converting " + value + " to integer:", t );
}
}
/**
* Returns the constant from the NumericTypes interface that best expresses the type of a numeric operation on the
* two given objects.
*
* @param v1 one argument to a numeric operator
* @param v2 the other argument
* @return the appropriate constant from the NumericTypes interface
*/
public static int getNumericType( Object v1, Object v2 )
{
return getNumericType( v1, v2, false );
}
/**
* Returns the constant from the NumericTypes interface that best expresses the type of an operation, which can be
* either numeric or not, on the two given types.
*
* @param t1 type of one argument to an operator
* @param t2 type of the other argument
* @param canBeNonNumeric whether the operator can be interpreted as non-numeric
* @return the appropriate constant from the NumericTypes interface
*/
public static int getNumericType( int t1, int t2, boolean canBeNonNumeric )
{
if ( t1 == t2 )
{
return t1;
}
if ( canBeNonNumeric && ( t1 == NONNUMERIC || t2 == NONNUMERIC || t1 == CHAR || t2 == CHAR ) )
{
return NONNUMERIC;
}
if ( t1 == NONNUMERIC )
{
t1 = DOUBLE; // Try to interpret strings as doubles...
}
if ( t2 == NONNUMERIC )
{
t2 = DOUBLE; // Try to interpret strings as doubles...
}
if ( t1 >= MIN_REAL_TYPE )
{
if ( t2 >= MIN_REAL_TYPE )
{
return Math.max( t1, t2 );
}
if ( t2 < INT )
{
return t1;
}
if ( t2 == BIGINT )
{
return BIGDEC;
}
return Math.max( DOUBLE, t1 );
}
if ( t2 < MIN_REAL_TYPE ) {
return Math.max( t1, t2 );
}
if ( t1 < INT )
{
return t2;
}
if ( t1 == BIGINT )
{
return BIGDEC;
}
return Math.max( DOUBLE, t2 );
}
/**
* Returns the constant from the NumericTypes interface that best expresses the type of an operation, which can be
* either numeric or not, on the two given objects.
*
* @param v1 one argument to an operator
* @param v2 the other argument
* @param canBeNonNumeric whether the operator can be interpreted as non-numeric
* @return the appropriate constant from the NumericTypes interface
*/
public static int getNumericType( Object v1, Object v2, boolean canBeNonNumeric )
{
return getNumericType( getNumericType( v1 ), getNumericType( v2 ), canBeNonNumeric );
}
/**
* Returns a new Number object of an appropriate type to hold the given integer value. The type of the returned
* object is consistent with the given type argument, which is a constant from the NumericTypes interface.
*
* @param type the nominal numeric type of the result, a constant from the NumericTypes interface
* @param value the integer value to convert to a Number object
* @return a Number object with the given value, of type implied by the type argument
*/
public static Number newInteger( int type, long value )
{
switch ( type )
{
case BOOL:
case CHAR:
case INT:
return (int) value;
case FLOAT:
if ( (long) (float) value == value )
{
return (float) value;
}
// else fall through:
case DOUBLE:
if ( (long) (double) value == value )
{
return (double) value;
}
// else fall through:
case LONG:
return value;
case BYTE:
return (byte) value;
case SHORT:
return (short) value;
default:
return BigInteger.valueOf( value );
}
}
/**
* Returns a new Number object of an appropriate type to hold the given real value. The type of the returned object
* is always either Float or Double, and is only Float if the given type tag (a constant from the NumericTypes
* interface) is FLOAT.
*
* @param type the nominal numeric type of the result, a constant from the NumericTypes interface
* @param value the real value to convert to a Number object
* @return a Number object with the given value, of type implied by the type argument
*/
public static Number newReal( int type, double value )
{
if ( type == FLOAT )
{
return (float) value;
}
return value;
}
public static Object binaryOr( Object v1, Object v2 )
{
int type = getNumericType( v1, v2 );
if ( type == BIGINT || type == BIGDEC )
{
return bigIntValue( v1 ).or( bigIntValue( v2 ) );
}
return newInteger( type, longValue( v1 ) | longValue( v2 ) );
}
public static Object binaryXor( Object v1, Object v2 )
{
int type = getNumericType( v1, v2 );
if ( type == BIGINT || type == BIGDEC )
{
return bigIntValue( v1 ).xor( bigIntValue( v2 ) );
}
return newInteger( type, longValue( v1 ) ^ longValue( v2 ) );
}
public static Object binaryAnd( Object v1, Object v2 )
{
int type = getNumericType( v1, v2 );
if ( type == BIGINT || type == BIGDEC )
{
return bigIntValue( v1 ).and( bigIntValue( v2 ) );
}
return newInteger( type, longValue( v1 ) & longValue( v2 ) );
}
public static boolean equal( Object v1, Object v2 )
{
if ( v1 == null )
{
return v2 == null;
}
if ( v1 == v2 || isEqual( v1, v2 ) )
{
return true;
}
if ( v1 instanceof Number && v2 instanceof Number )
{
return ( (Number) v1 ).doubleValue() == ( (Number) v2 ).doubleValue();
}
return false;
}
public static boolean less( Object v1, Object v2 )
{
return compareWithConversion( v1, v2 ) < 0;
}
public static boolean greater( Object v1, Object v2 )
{
return compareWithConversion( v1, v2 ) > 0;
}
public static boolean in( Object v1, Object v2 )
throws OgnlException
{
if ( v2 == null ) // A null collection is always treated as empty
{
return false;
}
ElementsAccessor elementsAccessor = OgnlRuntime.getElementsAccessor( OgnlRuntime.getTargetClass( v2 ) );
// FIXME O(n) is there a better way?!
for ( Enumeration<?> e = elementsAccessor.getElements( v2 ); e.hasMoreElements(); )
{
Object o = e.nextElement();
if ( equal( v1, o ) )
{
return true;
}
}
return false;
}
public static Object shiftLeft( Object v1, Object v2 )
{
int type = getNumericType( v1 );
if ( type == BIGINT || type == BIGDEC )
{
return bigIntValue( v1 ).shiftLeft( (int) longValue( v2 ) );
}
return newInteger( type, longValue( v1 ) << (int) longValue( v2 ) );
}
public static Object shiftRight( Object v1, Object v2 )
{
int type = getNumericType( v1 );
if ( type == BIGINT || type == BIGDEC )
{
return bigIntValue( v1 ).shiftRight( (int) longValue( v2 ) );
}
return newInteger( type, longValue( v1 ) >> (int) longValue( v2 ) );
}
public static Object unsignedShiftRight( Object v1, Object v2 )
{
int type = getNumericType( v1 );
if ( type == BIGINT || type == BIGDEC )
{
return bigIntValue( v1 ).shiftRight( (int) longValue( v2 ) );
}
if ( type <= INT )
{
return newInteger( INT, ( (int) longValue( v1 ) ) >>> (int) longValue( v2 ) );
}
return newInteger( type, longValue( v1 ) >>> (int) longValue( v2 ) );
}
public static Object add( Object v1, Object v2 )
{
int type = getNumericType( v1, v2, true );
switch ( type )
{
case BIGINT:
return bigIntValue( v1 ).add( bigIntValue( v2 ) );
case BIGDEC:
return bigDecValue( v1 ).add( bigDecValue( v2 ) );
case FLOAT:
case DOUBLE:
return newReal( type, doubleValue( v1 ) + doubleValue( v2 ) );
case NONNUMERIC:
int t1 = getNumericType( v1 ),
t2 = getNumericType( v2 );
if ( ( ( t1 != NONNUMERIC ) && ( v2 == null ) ) || ( ( t2 != NONNUMERIC ) && ( v1 == null ) ) )
{
throw new NullPointerException( "Can't add values " + v1 + " , " + v2 );
}
return stringValue( v1 ) + stringValue( v2 );
default:
return newInteger( type, longValue( v1 ) + longValue( v2 ) );
}
}
public static Object subtract( Object v1, Object v2 )
{
int type = getNumericType( v1, v2 );
switch ( type )
{
case BIGINT:
return bigIntValue( v1 ).subtract( bigIntValue( v2 ) );
case BIGDEC:
return bigDecValue( v1 ).subtract( bigDecValue( v2 ) );
case FLOAT:
case DOUBLE:
return newReal( type, doubleValue( v1 ) - doubleValue( v2 ) );
default:
return newInteger( type, longValue( v1 ) - longValue( v2 ) );
}
}
public static Object multiply( Object v1, Object v2 )
{
int type = getNumericType( v1, v2 );
switch ( type )
{
case BIGINT:
return bigIntValue( v1 ).multiply( bigIntValue( v2 ) );
case BIGDEC:
return bigDecValue( v1 ).multiply( bigDecValue( v2 ) );
case FLOAT:
case DOUBLE:
return newReal( type, doubleValue( v1 ) * doubleValue( v2 ) );
default:
return newInteger( type, longValue( v1 ) * longValue( v2 ) );
}
}
public static Object divide( Object v1, Object v2 )
{
int type = getNumericType( v1, v2 );
switch ( type )
{
case BIGINT:
return bigIntValue( v1 ).divide( bigIntValue( v2 ) );
case BIGDEC:
return bigDecValue( v1 ).divide( bigDecValue( v2 ), BigDecimal.ROUND_HALF_EVEN );
case FLOAT:
case DOUBLE:
return newReal( type, doubleValue( v1 ) / doubleValue( v2 ) );
default:
return newInteger( type, longValue( v1 ) / longValue( v2 ) );
}
}
public static Object remainder( Object v1, Object v2 )
{
int type = getNumericType( v1, v2 );
switch ( type )
{
case BIGDEC:
case BIGINT:
return bigIntValue( v1 ).remainder( bigIntValue( v2 ) );
default:
return newInteger( type, longValue( v1 ) % longValue( v2 ) );
}
}
public static Object negate( Object value )
{
int type = getNumericType( value );
switch ( type )
{
case BIGINT:
return bigIntValue( value ).negate();
case BIGDEC:
return bigDecValue( value ).negate();
case FLOAT:
case DOUBLE:
return newReal( type, -doubleValue( value ) );
default:
return newInteger( type, -longValue( value ) );
}
}
public static Object bitNegate( Object value )
{
int type = getNumericType( value );
switch ( type )
{
case BIGDEC:
case BIGINT:
return bigIntValue( value ).not();
default:
return newInteger( type, ~longValue( value ) );
}
}
public static String getEscapeString( String value )
{
StringBuilder result = new StringBuilder();
int length = value.length();
for ( int i = 0; i < length; i++ )
{
result.append( getEscapedChar( value.charAt( i ) ) );
}
return result.toString();
}
public static String getEscapedChar( char ch )
{
String result;
switch ( ch )
{
case '\b':
result = "\b";
break;
case '\t':
result = "\\t";
break;
case '\n':
result = "\\n";
break;
case '\f':
result = "\\f";
break;
case '\r':
result = "\\r";
break;
case '\"':
result = "\\\"";
break;
case '\'':
result = "\\\'";
break;
case '\\':
result = "\\\\";
break;
default:
if ( Character.isISOControl( ch ) )
{
String hc = Integer.toString( (int) ch, 16 );
int hcl = hc.length();
result = "\\u";
if ( hcl < 4 )
{
if ( hcl == 3 )
{
result = result + "0";
}
else
{
if ( hcl == 2 )
{
result = result + "00";
}
else
{
result = result + "000";
}
}
}
result = result + hc;
}
else
{
result = ch + "";
}
break;
}
return result;
}
public static Object returnValue( Object ignore, Object returnValue )
{
return returnValue;
}
/**
* Utility method that converts incoming exceptions to {@link RuntimeException} instances - or casts them if they
* already are.
*
* @param t The exception to cast.
* @return The exception cast to a {@link RuntimeException}.
*/
public static RuntimeException castToRuntime( Throwable t )
{
if ( RuntimeException.class.isInstance( t ) )
{
return (RuntimeException) t;
}
if ( OgnlException.class.isInstance( t ) )
{
throw new UnsupportedCompilationException( "Error evluating expression: " + t.getMessage(), t );
}
return new RuntimeException( t );
}
}