bsh.Primitive Maven / Gradle / Ivy
The newest version!
/*
* #%L
* The AIBench Shell Plugin
* %%
* Copyright (C) 2006 - 2017 Daniel Glez-Peña and Florentino Fdez-Riverola
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
/*****************************************************************************
* *
* This file is part of the BeanShell Java Scripting distribution. *
* Documentation and updates may be found at http://www.beanshell.org/ *
* *
* Sun Public License Notice: *
* *
* The contents of this file are subject to the Sun Public License Version *
* 1.0 (the "License"); you may not use this file except in compliance with *
* the License. A copy of the License is available at http://www.sun.com *
* *
* The Original Code is BeanShell. The Initial Developer of the Original *
* Code is Pat Niemeyer. Portions created by Pat Niemeyer are Copyright *
* (C) 2000. All Rights Reserved. *
* *
* GNU Public License Notice: *
* *
* Alternatively, the contents of this file may be used under the terms of *
* the GNU Lesser General Public License (the "LGPL"), in which case the *
* provisions of LGPL are applicable instead of those above. If you wish to *
* allow use of your version of this file only under the terms of the LGPL *
* and not to allow others to use your version of this file under the SPL, *
* indicate your decision by deleting the provisions above and replace *
* them with the notice and other provisions required by the LGPL. If you *
* do not delete the provisions above, a recipient may use your version of *
* this file under either the SPL or the LGPL. *
* *
* Patrick Niemeyer ([email protected]) *
* Author of Learning Java, O'Reilly & Associates *
* http://www.pat.net/~pat/ *
* *
*****************************************************************************/
package bsh;
import java.util.Hashtable;
/**
* Wrapper for primitive types in Bsh. This is package public because it is used
* in the implementation of some bsh commands.
*
* See the note in LHS.java about wrapping objects.
*/
/*
* Note: this class is final because we may test == Primitive.class in places.
* If we need to change that search for those tests.
*/
public final class Primitive implements ParserConstants, java.io.Serializable {
private static final long serialVersionUID = 1L;
/*
* static Hashtable primitiveToWrapper = new Hashtable(); static Hashtable
* wrapperToPrimitive = new Hashtable(); static { primitiveToWrapper.put(
* Boolean.TYPE, Boolean.class ); primitiveToWrapper.put( Byte.TYPE,
* Byte.class ); primitiveToWrapper.put( Short.TYPE, Short.class );
* primitiveToWrapper.put( Character.TYPE, Character.class );
* primitiveToWrapper.put( Integer.TYPE, Integer.class );
* primitiveToWrapper.put( Long.TYPE, Long.class ); primitiveToWrapper.put(
* Float.TYPE, Float.class ); primitiveToWrapper.put( Double.TYPE,
* Double.class ); wrapperToPrimitive.put( Boolean.class, Boolean.TYPE );
* wrapperToPrimitive.put( Byte.class, Byte.TYPE ); wrapperToPrimitive.put(
* Short.class, Short.TYPE ); wrapperToPrimitive.put( Character.class,
* Character.TYPE ); wrapperToPrimitive.put( Integer.class, Integer.TYPE );
* wrapperToPrimitive.put( Long.class, Long.TYPE ); wrapperToPrimitive.put(
* Float.class, Float.TYPE ); wrapperToPrimitive.put( Double.class,
* Double.TYPE ); }
*/
static Hashtable wrapperMap = new Hashtable();
static {
wrapperMap.put(Boolean.TYPE, Boolean.class);
wrapperMap.put(Byte.TYPE, Byte.class);
wrapperMap.put(Short.TYPE, Short.class);
wrapperMap.put(Character.TYPE, Character.class);
wrapperMap.put(Integer.TYPE, Integer.class);
wrapperMap.put(Long.TYPE, Long.class);
wrapperMap.put(Float.TYPE, Float.class);
wrapperMap.put(Double.TYPE, Double.class);
wrapperMap.put(Boolean.class, Boolean.TYPE);
wrapperMap.put(Byte.class, Byte.TYPE);
wrapperMap.put(Short.class, Short.TYPE);
wrapperMap.put(Character.class, Character.TYPE);
wrapperMap.put(Integer.class, Integer.TYPE);
wrapperMap.put(Long.class, Long.TYPE);
wrapperMap.put(Float.class, Float.TYPE);
wrapperMap.put(Double.class, Double.TYPE);
}
/** The primitive value stored in its java.lang wrapper class */
private Object value;
private static class Special implements java.io.Serializable {
private Special() {}
public static final Special NULL_VALUE = new Special();
public static final Special VOID_TYPE = new Special();
}
/*
* NULL means "no value". This ia a placeholder for primitive null value.
*/
public static final Primitive NULL = new Primitive(Special.NULL_VALUE);
/**
* VOID means "no type". Strictly speaking, this makes no sense here. But
* for practical reasons we'll consider the lack of a type to be a special
* value.
*/
public static final Primitive VOID = new Primitive(Special.VOID_TYPE);
// private to prevent invocation with param that isn't a
// primitive-wrapper
public Primitive(Object value) {
if (value == null)
throw new InterpreterError("Use Primitve.NULL instead of Primitive(null)");
if (value != Special.NULL_VALUE && value != Special.VOID_TYPE && !isWrapperType(value.getClass()))
throw new InterpreterError("Not a wrapper type: " + value);
this.value = value;
}
public Primitive(boolean value) {
this(new Boolean(value));
}
public Primitive(byte value) {
this(new Byte(value));
}
public Primitive(short value) {
this(new Short(value));
}
public Primitive(char value) {
this(new Character(value));
}
public Primitive(int value) {
this(new Integer(value));
}
public Primitive(long value) {
this(new Long(value));
}
public Primitive(float value) {
this(new Float(value));
}
public Primitive(double value) {
this(new Double(value));
}
/**
* Returns the primitive value stored in its java.lang wrapper class.
*
* @return the primitive value stored in its java.lang wrapper class.
*/
public Object getValue() {
if (value == Special.NULL_VALUE)
return null;
else if (value == Special.VOID_TYPE)
throw new InterpreterError("attempt to unwrap void type");
else
return value;
}
public String toString() {
if (value == Special.NULL_VALUE)
return "null";
else if (value == Special.VOID_TYPE)
return "void";
else
return value.toString();
}
/**
* Returns the corresponding Java primitive TYPE class for this Primitive.
*
* @return the primitive TYPE class type of the value or Void.TYPE for
* Primitive.VOID or null value for type of Primitive.NULL
*/
public Class getType() {
if (this == Primitive.VOID)
return Void.TYPE;
// NULL return null as type... we currently use null type to
// indicate
// loose typing throughout bsh.
if (this == Primitive.NULL)
return null;
return unboxType(value.getClass());
}
/**
* Perform a binary operation on two Primitives or wrapper types. If both
* original args were Primitives return a Primitive result else it was mixed
* (wrapper/primitive) return the wrapper type. The exception is for boolean
* operations where we will return the primitive type either way.
*
* @param obj1
* the first operator.
* @param obj2
* the second operator.
* @param kind
* the type of operation.
* @return the result of the operation.
* @throws UtilEvalError
* if an error occurs during evaluation.
*/
public static Object binaryOperation(Object obj1, Object obj2, int kind) throws UtilEvalError {
// special primitive types
if (obj1 == NULL || obj2 == NULL)
throw new UtilEvalError("Null value or 'null' literal in binary operation");
if (obj1 == VOID || obj2 == VOID)
throw new UtilEvalError("Undefined variable, class, or 'void' literal in binary operation");
// keep track of the original types
Class lhsOrgType = obj1.getClass();
Class rhsOrgType = obj2.getClass();
// Unwrap primitives
if (obj1 instanceof Primitive)
obj1 = ((Primitive) obj1).getValue();
if (obj2 instanceof Primitive)
obj2 = ((Primitive) obj2).getValue();
Object[] operands = promotePrimitives(obj1, obj2);
Object lhs = operands[0];
Object rhs = operands[1];
if (lhs.getClass() != rhs.getClass())
throw new UtilEvalError("Type mismatch in operator. " + lhs.getClass() + " cannot be used with " + rhs.getClass());
Object result;
try {
result = binaryOperationImpl(lhs, rhs, kind);
} catch (ArithmeticException e) {
throw new UtilTargetError("Arithemetic Exception in binary op", e);
}
// If both original args were Primitives return a Primitive
// result
// else it was mixed (wrapper/primitive) return the wrapper type
// Exception is for boolean result, return the primitive
if ((lhsOrgType == Primitive.class && rhsOrgType == Primitive.class) || result instanceof Boolean)
return new Primitive(result);
else
return result;
}
static Object binaryOperationImpl(Object lhs, Object rhs, int kind) throws UtilEvalError {
if (lhs instanceof Boolean)
return booleanBinaryOperation((Boolean) lhs, (Boolean) rhs, kind);
else if (lhs instanceof Integer)
return intBinaryOperation((Integer) lhs, (Integer) rhs, kind);
else if (lhs instanceof Long)
return longBinaryOperation((Long) lhs, (Long) rhs, kind);
else if (lhs instanceof Float)
return floatBinaryOperation((Float) lhs, (Float) rhs, kind);
else if (lhs instanceof Double)
return doubleBinaryOperation((Double) lhs, (Double) rhs, kind);
else
throw new UtilEvalError("Invalid types in binary operator");
}
static Boolean booleanBinaryOperation(Boolean B1, Boolean B2, int kind) {
boolean lhs = B1.booleanValue();
boolean rhs = B2.booleanValue();
switch (kind) {
case EQ:
return new Boolean(lhs == rhs);
case NE:
return new Boolean(lhs != rhs);
case BOOL_OR:
case BOOL_ORX:
return new Boolean(lhs || rhs);
case BOOL_AND:
case BOOL_ANDX:
return new Boolean(lhs && rhs);
default:
throw new InterpreterError("unimplemented binary operator");
}
}
// returns Object covering both Long and Boolean return types
static Object longBinaryOperation(Long L1, Long L2, int kind) {
long lhs = L1.longValue();
long rhs = L2.longValue();
switch (kind) {
// boolean
case LT:
case LTX:
return new Boolean(lhs < rhs);
case GT:
case GTX:
return new Boolean(lhs > rhs);
case EQ:
return new Boolean(lhs == rhs);
case LE:
case LEX:
return new Boolean(lhs <= rhs);
case GE:
case GEX:
return new Boolean(lhs >= rhs);
case NE:
return new Boolean(lhs != rhs);
// arithmetic
case PLUS:
return new Long(lhs + rhs);
case MINUS:
return new Long(lhs - rhs);
case STAR:
return new Long(lhs * rhs);
case SLASH:
return new Long(lhs / rhs);
case MOD:
return new Long(lhs % rhs);
// bitwise
case LSHIFT:
case LSHIFTX:
return new Long(lhs << rhs);
case RSIGNEDSHIFT:
case RSIGNEDSHIFTX:
return new Long(lhs >> rhs);
case RUNSIGNEDSHIFT:
case RUNSIGNEDSHIFTX:
return new Long(lhs >>> rhs);
case BIT_AND:
case BIT_ANDX:
return new Long(lhs & rhs);
case BIT_OR:
case BIT_ORX:
return new Long(lhs | rhs);
case XOR:
return new Long(lhs ^ rhs);
default:
throw new InterpreterError("Unimplemented binary long operator");
}
}
// returns Object covering both Integer and Boolean return types
static Object intBinaryOperation(Integer I1, Integer I2, int kind) {
int lhs = I1.intValue();
int rhs = I2.intValue();
switch (kind) {
// boolean
case LT:
case LTX:
return new Boolean(lhs < rhs);
case GT:
case GTX:
return new Boolean(lhs > rhs);
case EQ:
return new Boolean(lhs == rhs);
case LE:
case LEX:
return new Boolean(lhs <= rhs);
case GE:
case GEX:
return new Boolean(lhs >= rhs);
case NE:
return new Boolean(lhs != rhs);
// arithmetic
case PLUS:
return new Integer(lhs + rhs);
case MINUS:
return new Integer(lhs - rhs);
case STAR:
return new Integer(lhs * rhs);
case SLASH:
return new Integer(lhs / rhs);
case MOD:
return new Integer(lhs % rhs);
// bitwise
case LSHIFT:
case LSHIFTX:
return new Integer(lhs << rhs);
case RSIGNEDSHIFT:
case RSIGNEDSHIFTX:
return new Integer(lhs >> rhs);
case RUNSIGNEDSHIFT:
case RUNSIGNEDSHIFTX:
return new Integer(lhs >>> rhs);
case BIT_AND:
case BIT_ANDX:
return new Integer(lhs & rhs);
case BIT_OR:
case BIT_ORX:
return new Integer(lhs | rhs);
case XOR:
return new Integer(lhs ^ rhs);
default:
throw new InterpreterError("Unimplemented binary integer operator");
}
}
// returns Object covering both Double and Boolean return types
static Object doubleBinaryOperation(Double D1, Double D2, int kind) throws UtilEvalError {
double lhs = D1.doubleValue();
double rhs = D2.doubleValue();
switch (kind) {
// boolean
case LT:
case LTX:
return new Boolean(lhs < rhs);
case GT:
case GTX:
return new Boolean(lhs > rhs);
case EQ:
return new Boolean(lhs == rhs);
case LE:
case LEX:
return new Boolean(lhs <= rhs);
case GE:
case GEX:
return new Boolean(lhs >= rhs);
case NE:
return new Boolean(lhs != rhs);
// arithmetic
case PLUS:
return new Double(lhs + rhs);
case MINUS:
return new Double(lhs - rhs);
case STAR:
return new Double(lhs * rhs);
case SLASH:
return new Double(lhs / rhs);
case MOD:
return new Double(lhs % rhs);
// can't shift floating-point values
case LSHIFT:
case LSHIFTX:
case RSIGNEDSHIFT:
case RSIGNEDSHIFTX:
case RUNSIGNEDSHIFT:
case RUNSIGNEDSHIFTX:
throw new UtilEvalError("Can't shift doubles");
default:
throw new InterpreterError("Unimplemented binary double operator");
}
}
// returns Object covering both Long and Boolean return types
static Object floatBinaryOperation(Float F1, Float F2, int kind) throws UtilEvalError {
float lhs = F1.floatValue();
float rhs = F2.floatValue();
switch (kind) {
// boolean
case LT:
case LTX:
return new Boolean(lhs < rhs);
case GT:
case GTX:
return new Boolean(lhs > rhs);
case EQ:
return new Boolean(lhs == rhs);
case LE:
case LEX:
return new Boolean(lhs <= rhs);
case GE:
case GEX:
return new Boolean(lhs >= rhs);
case NE:
return new Boolean(lhs != rhs);
// arithmetic
case PLUS:
return new Float(lhs + rhs);
case MINUS:
return new Float(lhs - rhs);
case STAR:
return new Float(lhs * rhs);
case SLASH:
return new Float(lhs / rhs);
case MOD:
return new Float(lhs % rhs);
// can't shift floats
case LSHIFT:
case LSHIFTX:
case RSIGNEDSHIFT:
case RSIGNEDSHIFTX:
case RUNSIGNEDSHIFT:
case RUNSIGNEDSHIFTX:
throw new UtilEvalError("Can't shift floats ");
default:
throw new InterpreterError("Unimplemented binary float operator");
}
}
/**
* Promote primitive wrapper type to to Integer wrapper type
*/
static Object promoteToInteger(Object wrapper) {
if (wrapper instanceof Character)
return new Integer(((Character) wrapper).charValue());
else if ((wrapper instanceof Byte) || (wrapper instanceof Short))
return new Integer(((Number) wrapper).intValue());
return wrapper;
}
/**
* Promote the pair of primitives to the maximum type of the two. e.g.
* [int,long]->[long,long]
*/
static Object[] promotePrimitives(Object lhs, Object rhs) {
lhs = promoteToInteger(lhs);
rhs = promoteToInteger(rhs);
if ((lhs instanceof Number) && (rhs instanceof Number)) {
Number lnum = (Number) lhs;
Number rnum = (Number) rhs;
boolean b;
if ((b = (lnum instanceof Double)) || (rnum instanceof Double)) {
if (b)
rhs = new Double(rnum.doubleValue());
else
lhs = new Double(lnum.doubleValue());
} else if ((b = (lnum instanceof Float)) || (rnum instanceof Float)) {
if (b)
rhs = new Float(rnum.floatValue());
else
lhs = new Float(lnum.floatValue());
} else if ((b = (lnum instanceof Long)) || (rnum instanceof Long)) {
if (b)
rhs = new Long(rnum.longValue());
else
lhs = new Long(lnum.longValue());
}
}
return new Object[] {
lhs, rhs
};
}
public static Primitive unaryOperation(Primitive val, int kind) throws UtilEvalError {
if (val == NULL)
throw new UtilEvalError("illegal use of null object or 'null' literal");
if (val == VOID)
throw new UtilEvalError("illegal use of undefined object or 'void' literal");
Class operandType = val.getType();
Object operand = promoteToInteger(val.getValue());
if (operand instanceof Boolean)
return new Primitive(booleanUnaryOperation((Boolean) operand, kind));
else if (operand instanceof Integer) {
int result = intUnaryOperation((Integer) operand, kind);
// ++ and -- must be cast back the original type
if (kind == INCR || kind == DECR) {
if (operandType == Byte.TYPE)
return new Primitive((byte) result);
if (operandType == Short.TYPE)
return new Primitive((short) result);
if (operandType == Character.TYPE)
return new Primitive((char) result);
}
return new Primitive(result);
} else if (operand instanceof Long)
return new Primitive(longUnaryOperation((Long) operand, kind));
else if (operand instanceof Float)
return new Primitive(floatUnaryOperation((Float) operand, kind));
else if (operand instanceof Double)
return new Primitive(doubleUnaryOperation((Double) operand, kind));
else
throw new InterpreterError("An error occurred. Please call technical support.");
}
static boolean booleanUnaryOperation(Boolean B, int kind) throws UtilEvalError {
boolean operand = B.booleanValue();
switch (kind) {
case BANG:
return !operand;
default:
throw new UtilEvalError("Operator inappropriate for boolean");
}
}
static int intUnaryOperation(Integer I, int kind) {
int operand = I.intValue();
switch (kind) {
case PLUS:
return operand;
case MINUS:
return -operand;
case TILDE:
return ~operand;
case INCR:
return operand + 1;
case DECR:
return operand - 1;
default:
throw new InterpreterError("bad integer unaryOperation");
}
}
static long longUnaryOperation(Long L, int kind) {
long operand = L.longValue();
switch (kind) {
case PLUS:
return operand;
case MINUS:
return -operand;
case TILDE:
return ~operand;
case INCR:
return operand + 1;
case DECR:
return operand - 1;
default:
throw new InterpreterError("bad long unaryOperation");
}
}
static float floatUnaryOperation(Float F, int kind) {
float operand = F.floatValue();
switch (kind) {
case PLUS:
return operand;
case MINUS:
return -operand;
default:
throw new InterpreterError("bad float unaryOperation");
}
}
static double doubleUnaryOperation(Double D, int kind) {
double operand = D.doubleValue();
switch (kind) {
case PLUS:
return operand;
case MINUS:
return -operand;
default:
throw new InterpreterError("bad double unaryOperation");
}
}
public int intValue() throws UtilEvalError {
if (value instanceof Number)
return ((Number) value).intValue();
else
throw new UtilEvalError("Primitive not a number");
}
public boolean booleanValue() throws UtilEvalError {
if (value instanceof Boolean)
return ((Boolean) value).booleanValue();
else
throw new UtilEvalError("Primitive not a boolean");
}
/**
* Determine if this primitive is a numeric type. i.e. not boolean, null, or
* void (but including char)
*
* @return {@code true} if this primitive is a numeric type. {@code false}
* otherwise.
*/
public boolean isNumber() {
return (!(value instanceof Boolean) && !(this == NULL) && !(this == VOID));
}
public Number numberValue() throws UtilEvalError {
Object value = this.value;
// Promote character to Number type for these purposes
if (value instanceof Character)
value = new Integer(((Character) value).charValue());
if (value instanceof Number)
return (Number) value;
else
throw new UtilEvalError("Primitive not a number");
}
/**
* Primitives compare equal with other Primitives containing an equal
* wrapped value.
*/
public boolean equals(Object obj) {
if (obj instanceof Primitive)
return ((Primitive) obj).value.equals(this.value);
else
return false;
}
/**
* The hash of the Primitive is tied to the hash of the wrapped value but
* shifted so that they are not the same.
*/
public int hashCode() {
return this.value.hashCode() * 21; // arbitrary
}
/**
* Unwrap primitive values and map voids to nulls. Non Primitive types
* remain unchanged.
*
* @param obj
* object type which may be bsh.Primitive
* @return corresponding "normal" Java type, "unwrapping" any bsh.Primitive
* types to their wrapper types.
*/
public static Object unwrap(Object obj) {
// map voids to nulls for the outside world
if (obj == Primitive.VOID)
return null;
// unwrap primitives
if (obj instanceof Primitive)
return ((Primitive) obj).getValue();
else
return obj;
}
/*
* Unwrap Primitive wrappers to their java.lang wrapper values. e.g.
* Primitive(42) becomes Integer(42)
*
* @see #unwrap( Object )
*/
public static Object[] unwrap(Object[] args) {
Object[] oa = new Object[args.length];
for (int i = 0; i < args.length; i++)
oa[i] = unwrap(args[i]);
return oa;
}
/*
*/
public static Object[] wrap(Object[] args, Class[] paramTypes) {
if (args == null)
return null;
Object[] oa = new Object[args.length];
for (int i = 0; i < args.length; i++)
oa[i] = wrap(args[i], paramTypes[i]);
return oa;
}
/**
* Wrap primitive values (as indicated by type param) and nulls in the
* Primitive class. Values not primitive or null are left unchanged.
* Primitive values are represented by their wrapped values in param value.
*
* The value null is mapped to Primitive.NULL. Any value specified with type
* Void.TYPE is mapped to Primitive.VOID.
*
*
* @param value
* the value to be wrapped.
* @param type
* the wrapper type.
* @return the value wrapped.
*/
public static Object wrap(Object value, Class type) {
if (type == Void.TYPE)
return Primitive.VOID;
if (value == null)
return Primitive.NULL;
if (type.isPrimitive())
return new Primitive(value);
return value;
}
/**
* Get the appropriate default value per JLS 4.5.4.
*
* @param type
* the type which default value is wanted.
* @return the default primitive value of the type.
*/
public static Primitive getDefaultValue(Class type) {
if (type == null || !type.isPrimitive())
return Primitive.NULL;
if (type == Boolean.TYPE)
return new Primitive(false);
// non boolean primitive, get appropriate flavor of zero
try {
return new Primitive((int) 0).castToType(type, Types.CAST);
} catch (UtilEvalError e) {
throw new InterpreterError("bad cast");
}
}
/**
* Get the corresponding java.lang wrapper class for the primitive TYPE
* class. e.g. Integer.TYPE -> Integer.class
*
* @param primitiveType
* the type to be boxed.
* @return the boxed type.
*/
public static Class boxType(Class primitiveType) {
Class c = (Class) wrapperMap.get(primitiveType);
if (c != null)
return c;
throw new InterpreterError("Not a primitive type: " + primitiveType);
}
/**
* Get the corresponding primitive TYPE class for the java.lang wrapper
* class type. e.g. Integer.class -> Integer.TYPE
*
* @param wrapperType
* the type to be unboxed.
* @return the unboxed type.
*/
public static Class unboxType(Class wrapperType) {
Class c = (Class) wrapperMap.get(wrapperType);
if (c != null)
return c;
throw new InterpreterError("Not a primitive wrapper type: " + wrapperType);
}
/**
* Cast this bsh.Primitive value to a new bsh.Primitive value This is
* usually a numeric type cast. Other cases include: A boolean can be cast
* to boolen null can be cast to any object type and remains null Attempting
* to cast a void causes an exception
*
* @param toType
* is the java object or primitive TYPE class
* @param operation
* the operation to be performed.
* @throws UtilEvalError
* if an error occurs during evaluation.
* @return the type casted.
*/
public Primitive castToType(Class toType, int operation) throws UtilEvalError {
return castPrimitive(toType, getType()/* fromType */, this/* fromValue */, false/* checkOnly */, operation);
}
/*
* Cast or check a cast of a primitive type to another type. Normally both
* types are primitive (e.g. numeric), but a null value (no type) may be
* cast to any type.
*
* @param toType is the target type of the cast. It is normally a java
* primitive TYPE, but in the case of a null cast can be any object type.
*
* @param fromType is the java primitive TYPE type of the primitive to be
* cast or null, to indicate that the fromValue was null or void.
*
* @param fromValue is, optionally, the value to be converted. If checkOnly
* is true fromValue must be null. If checkOnly is false, fromValue must be
* non-null (Primitive.NULL is of course valid).
*/
static Primitive castPrimitive(Class toType, Class fromType, Primitive fromValue, boolean checkOnly, int operation) throws UtilEvalError {
/*
* Lots of preconditions checked here... Once things are running
* smoothly we might comment these out (That's what assertions are for).
*/
if (checkOnly && fromValue != null)
throw new InterpreterError("bad cast param 1");
if (!checkOnly && fromValue == null)
throw new InterpreterError("bad cast param 2");
if (fromType != null && !fromType.isPrimitive())
throw new InterpreterError("bad fromType:" + fromType);
if (fromValue == Primitive.NULL && fromType != null)
throw new InterpreterError("inconsistent args 1");
if (fromValue == Primitive.VOID && fromType != Void.TYPE)
throw new InterpreterError("inconsistent args 2");
// can't cast void to anything
if (fromType == Void.TYPE)
if (checkOnly)
return Types.INVALID_CAST;
else
throw Types.castError(Reflect.normalizeClassName(toType), "void value", operation);
// unwrap Primitive fromValue to its wrapper value, etc.
Object value = null;
if (fromValue != null)
value = fromValue.getValue();
if (toType.isPrimitive()) {
// Trying to cast null to primitive type?
if (fromType == null)
if (checkOnly)
return Types.INVALID_CAST;
else
throw Types.castError("primitive type:" + toType, "Null value", operation);
// fall through
} else {
// Trying to cast primitive to an object type
// Primitive.NULL can be cast to any object type
if (fromType == null)
return checkOnly ? Types.VALID_CAST : Primitive.NULL;
if (checkOnly)
return Types.INVALID_CAST;
else
throw Types.castError("object type:" + toType, "primitive value", operation);
}
// can only cast boolean to boolean
if (fromType == Boolean.TYPE) {
if (toType != Boolean.TYPE)
if (checkOnly)
return Types.INVALID_CAST;
else
throw Types.castError(toType, fromType, operation);
return checkOnly ? Types.VALID_CAST : fromValue;
}
// Do numeric cast
// Only allow legal Java assignment unless we're a CAST
// operation
if (operation == Types.ASSIGNMENT && !Types.isJavaAssignable(toType, fromType)) {
if (checkOnly)
return Types.INVALID_CAST;
else
throw Types.castError(toType, fromType, operation);
}
return checkOnly ? Types.VALID_CAST : new Primitive(castWrapper(toType, value));
}
public static boolean isWrapperType(Class type) {
return wrapperMap.get(type) != null && !type.isPrimitive();
}
/**
* Cast a primitive value represented by its java.lang wrapper type to the
* specified java.lang wrapper type. e.g. Byte(5) to Integer(5) or
* Integer(5) to Byte(5)
*
* @param toType
* is the java TYPE type
* @param value
* is the value in java.lang wrapper. value may not be
* {@code null}.
* @return the casted value.
*/
static Object castWrapper(Class toType, Object value) {
if (!toType.isPrimitive())
throw new InterpreterError("invalid type in castWrapper: " + toType);
if (value == null)
throw new InterpreterError("null value in castWrapper, guard");
if (value instanceof Boolean) {
if (toType != Boolean.TYPE)
throw new InterpreterError("bad wrapper cast of boolean");
else
return value;
}
// first promote char to Number type to avoid duplicating code
if (value instanceof Character)
value = new Integer(((Character) value).charValue());
if (!(value instanceof Number))
throw new InterpreterError("bad type in cast");
Number number = (Number) value;
if (toType == Byte.TYPE)
return new Byte(number.byteValue());
if (toType == Short.TYPE)
return new Short(number.shortValue());
if (toType == Character.TYPE)
return new Character((char) number.intValue());
if (toType == Integer.TYPE)
return new Integer(number.intValue());
if (toType == Long.TYPE)
return new Long(number.longValue());
if (toType == Float.TYPE)
return new Float(number.floatValue());
if (toType == Double.TYPE)
return new Double(number.doubleValue());
throw new InterpreterError("error in wrapper cast");
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy