|
What this is
Other links
The source code/***************************************************************************** * * * 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 (pat@pat.net) * * 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 { 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 ); } /** 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)); } /** 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(); } /** Get 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 eithe way. */ 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) throws UtilEvalError { 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) */ 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 |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.