PageRenderTime 130ms CodeModel.GetById 93ms app.highlight 31ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-2-pre14/bsh/Primitive.java

#
Java | 1068 lines | 714 code | 161 blank | 193 comment | 189 complexity | e9a91a6ea4d6101ff5adec39963b6394 MD5 | raw file
   1/*****************************************************************************
   2 *                                                                           *
   3 *  This file is part of the BeanShell Java Scripting distribution.          *
   4 *  Documentation and updates may be found at http://www.beanshell.org/      *
   5 *                                                                           *
   6 *  Sun Public License Notice:                                               *
   7 *                                                                           *
   8 *  The contents of this file are subject to the Sun Public License Version  *
   9 *  1.0 (the "License"); you may not use this file except in compliance with *
  10 *  the License. A copy of the License is available at http://www.sun.com    * 
  11 *                                                                           *
  12 *  The Original Code is BeanShell. The Initial Developer of the Original    *
  13 *  Code is Pat Niemeyer. Portions created by Pat Niemeyer are Copyright     *
  14 *  (C) 2000.  All Rights Reserved.                                          *
  15 *                                                                           *
  16 *  GNU Public License Notice:                                               *
  17 *                                                                           *
  18 *  Alternatively, the contents of this file may be used under the terms of  *
  19 *  the GNU Lesser General Public License (the "LGPL"), in which case the    *
  20 *  provisions of LGPL are applicable instead of those above. If you wish to *
  21 *  allow use of your version of this file only under the  terms of the LGPL *
  22 *  and not to allow others to use your version of this file under the SPL,  *
  23 *  indicate your decision by deleting the provisions above and replace      *
  24 *  them with the notice and other provisions required by the LGPL.  If you  *
  25 *  do not delete the provisions above, a recipient may use your version of  *
  26 *  this file under either the SPL or the LGPL.                              *
  27 *                                                                           *
  28 *  Patrick Niemeyer (pat@pat.net)                                           *
  29 *  Author of Learning Java, O'Reilly & Associates                           *
  30 *  http://www.pat.net/~pat/                                                 *
  31 *                                                                           *
  32 *****************************************************************************/
  33
  34
  35package bsh;
  36
  37import java.util.Hashtable;
  38
  39/**
  40    Wrapper for primitive types in Bsh.  This is package public because it 
  41	is used in the implementation of some bsh commands.
  42
  43    See the note in LHS.java about wrapping objects.
  44*/
  45/*
  46	Note: this class is final because we may test == Primitive.class in places.
  47	If we need to change that search for those tests.
  48*/
  49public final class Primitive implements ParserConstants, java.io.Serializable
  50{
  51	static Hashtable primitiveToWrapper = new Hashtable();
  52	static Hashtable wrapperToPrimitive = new Hashtable();
  53	static {
  54		primitiveToWrapper.put( Boolean.TYPE, Boolean.class );
  55		primitiveToWrapper.put( Byte.TYPE, Byte.class );
  56		primitiveToWrapper.put( Short.TYPE, Short.class );
  57		primitiveToWrapper.put( Character.TYPE, Character.class );
  58		primitiveToWrapper.put( Integer.TYPE, Integer.class );
  59		primitiveToWrapper.put( Long.TYPE, Long.class );
  60		primitiveToWrapper.put( Float.TYPE, Float.class );
  61		primitiveToWrapper.put( Double.TYPE, Double.class );
  62		wrapperToPrimitive.put( Boolean.class, Boolean.TYPE );
  63		wrapperToPrimitive.put( Byte.class, Byte.TYPE );
  64		wrapperToPrimitive.put( Short.class, Short.TYPE );
  65		wrapperToPrimitive.put( Character.class, Character.TYPE );
  66		wrapperToPrimitive.put( Integer.class, Integer.TYPE );
  67		wrapperToPrimitive.put( Long.class, Long.TYPE );
  68		wrapperToPrimitive.put( Float.class, Float.TYPE );
  69		wrapperToPrimitive.put( Double.class, Double.TYPE );
  70	}
  71
  72    /** The primitive value stored in its java.lang wrapper class */
  73    private Object value;
  74
  75    private static class Special implements java.io.Serializable
  76    {
  77        private Special() { }
  78
  79        public static final Special NULL_VALUE = new Special();
  80        public static final Special VOID_TYPE = new Special();
  81    }
  82
  83    /*
  84        NULL means "no value".
  85        This ia a placeholder for primitive null value.
  86    */
  87    public static final Primitive NULL = new Primitive(Special.NULL_VALUE);
  88
  89    /**
  90        VOID means "no type".
  91        Strictly speaking, this makes no sense here.  But for practical
  92        reasons we'll consider the lack of a type to be a special value.
  93    */
  94    public static final Primitive VOID = new Primitive(Special.VOID_TYPE);
  95
  96    // private to prevent invocation with param that isn't a primitive-wrapper
  97    public Primitive( Object value )
  98    {
  99        if ( value == null )
 100            throw new InterpreterError(
 101				"Use Primitve.NULL instead of Primitive(null)");
 102
 103		if ( value != Special.NULL_VALUE 
 104			&& value != Special.VOID_TYPE &&
 105			!isWrapperType( value.getClass() ) 
 106		)
 107            throw new InterpreterError( "Not a wrapper type: "+value);
 108
 109        this.value = value;
 110    }
 111
 112    public Primitive(boolean value) { this(new Boolean(value)); }
 113    public Primitive(byte value) { this(new Byte(value)); }
 114    public Primitive(short value) { this(new Short(value)); }
 115    public Primitive(char value) { this(new Character(value)); }
 116    public Primitive(int value) { this(new Integer(value)); }
 117    public Primitive(long value) { this(new Long(value)); }
 118    public Primitive(float value) { this(new Float(value)); }
 119    public Primitive(double value) { this(new Double(value)); }
 120
 121	/**
 122    	Return the primitive value stored in its java.lang wrapper class 
 123	*/
 124    public Object getValue()
 125    {
 126        if ( value == Special.NULL_VALUE )
 127            return null;
 128        else 
 129		if ( value == Special.VOID_TYPE )
 130                throw new InterpreterError("attempt to unwrap void type");
 131        else
 132            return value;
 133    }
 134
 135    public String toString()
 136    {
 137        if(value == Special.NULL_VALUE)
 138            return "null";
 139        else if(value == Special.VOID_TYPE)
 140            return "void";
 141        else
 142            return value.toString();
 143    }
 144
 145	/**
 146		Get the corresponding Java primitive TYPE class for this Primitive.
 147		@return the primitive TYPE class type of the value or Void.TYPE for
 148		Primitive.VOID or null value for type of Primitive.NULL
 149	*/
 150    public Class getType()
 151    {
 152		if ( this == Primitive.VOID )
 153			return Void.TYPE;
 154
 155		// NULL return null as type... we currently use null type to indicate 
 156		// loose typing throughout bsh.
 157		if ( this == Primitive.NULL )
 158			return null;
 159
 160		return unboxType( value.getClass() );
 161    }
 162
 163	/**
 164		Perform a binary operation on two Primitives or wrapper types.
 165		If both original args were Primitives return a Primitive result
 166		else it was mixed (wrapper/primitive) return the wrapper type.
 167		The exception is for boolean operations where we will return the 
 168		primitive type eithe way.
 169	*/
 170    public static Object binaryOperation(
 171		Object obj1, Object obj2, int kind)
 172        throws UtilEvalError
 173    {
 174		// special primitive types
 175        if ( obj1 == NULL || obj2 == NULL )
 176            throw new UtilEvalError(
 177				"Null value or 'null' literal in binary operation");
 178        if ( obj1 == VOID || obj2 == VOID )
 179            throw new UtilEvalError(
 180			"Undefined variable, class, or 'void' literal in binary operation");
 181
 182		// keep track of the original types
 183		Class lhsOrgType = obj1.getClass();
 184		Class rhsOrgType = obj2.getClass();
 185
 186		// Unwrap primitives
 187        if ( obj1 instanceof Primitive )
 188            obj1 = ((Primitive)obj1).getValue();
 189        if ( obj2 instanceof Primitive )
 190            obj2 = ((Primitive)obj2).getValue();
 191
 192        Object[] operands = promotePrimitives(obj1, obj2);
 193        Object lhs = operands[0];
 194        Object rhs = operands[1];
 195
 196        if(lhs.getClass() != rhs.getClass())
 197            throw new UtilEvalError("Type mismatch in operator.  " 
 198			+ lhs.getClass() + " cannot be used with " + rhs.getClass() );
 199
 200		Object result;
 201		try {
 202			result = binaryOperationImpl( lhs, rhs, kind );
 203		} catch ( ArithmeticException e ) {
 204			throw new UtilTargetError( "Arithemetic Exception in binary op", e);
 205		}
 206
 207		// If both original args were Primitives return a Primitive result
 208		// else it was mixed (wrapper/primitive) return the wrapper type
 209		// Exception is for boolean result, return the primitive
 210		if ( (lhsOrgType == Primitive.class && rhsOrgType == Primitive.class)
 211			|| result instanceof Boolean
 212		)
 213			return new Primitive( result );
 214		else
 215			return result;
 216    }
 217
 218    static Object binaryOperationImpl( Object lhs, Object rhs, int kind )
 219        throws UtilEvalError
 220	{
 221        if(lhs instanceof Boolean)
 222            return booleanBinaryOperation((Boolean)lhs, (Boolean)rhs, kind);
 223        else if(lhs instanceof Integer)
 224            return intBinaryOperation( (Integer)lhs, (Integer)rhs, kind );
 225        else if(lhs instanceof Long)
 226            return longBinaryOperation((Long)lhs, (Long)rhs, kind);
 227        else if(lhs instanceof Float)
 228            return floatBinaryOperation((Float)lhs, (Float)rhs, kind);
 229        else if(lhs instanceof Double)
 230            return doubleBinaryOperation( (Double)lhs, (Double)rhs, kind);
 231        else
 232            throw new UtilEvalError("Invalid types in binary operator" );
 233	}
 234
 235    static Boolean booleanBinaryOperation(Boolean B1, Boolean B2, int kind)
 236        throws UtilEvalError
 237    {
 238        boolean lhs = B1.booleanValue();
 239        boolean rhs = B2.booleanValue();
 240
 241        switch(kind)
 242        {
 243            case EQ:
 244                return new Boolean(lhs == rhs);
 245
 246            case NE:
 247                return new Boolean(lhs != rhs);
 248
 249            case BOOL_OR:
 250            case BOOL_ORX:
 251                return new Boolean( lhs || rhs );
 252
 253            case BOOL_AND:
 254            case BOOL_ANDX:
 255                return new Boolean( lhs && rhs );
 256
 257            default:
 258                throw new InterpreterError("unimplemented binary operator");
 259        }
 260    }
 261
 262    // returns Object covering both Long and Boolean return types
 263    static Object longBinaryOperation(Long L1, Long L2, int kind)
 264    {
 265        long lhs = L1.longValue();
 266        long rhs = L2.longValue();
 267
 268        switch(kind)
 269        {
 270            // boolean
 271            case LT:
 272            case LTX:
 273                return new Boolean(lhs < rhs);
 274
 275            case GT:
 276            case GTX:
 277                return new Boolean(lhs > rhs);
 278
 279            case EQ:
 280                return new Boolean(lhs == rhs);
 281
 282            case LE:
 283            case LEX:
 284                return new Boolean(lhs <= rhs);
 285
 286            case GE:
 287            case GEX:
 288                return new Boolean(lhs >= rhs);
 289
 290            case NE:
 291                return new Boolean(lhs != rhs);
 292
 293            // arithmetic
 294            case PLUS:
 295                return new Long(lhs + rhs);
 296
 297            case MINUS:
 298                return new Long(lhs - rhs);
 299
 300            case STAR:
 301                return new Long(lhs * rhs);
 302
 303            case SLASH:
 304                return new Long(lhs / rhs);
 305
 306            case MOD:
 307                return new Long(lhs % rhs);
 308
 309            // bitwise
 310            case LSHIFT:
 311            case LSHIFTX:
 312                return new Long(lhs << rhs);
 313
 314            case RSIGNEDSHIFT:
 315            case RSIGNEDSHIFTX:
 316                return new Long(lhs >> rhs);
 317
 318            case RUNSIGNEDSHIFT:
 319            case RUNSIGNEDSHIFTX:
 320                return new Long(lhs >>> rhs);
 321
 322            case BIT_AND:
 323            case BIT_ANDX:
 324                return new Long(lhs & rhs);
 325
 326            case BIT_OR:
 327            case BIT_ORX:
 328                return new Long(lhs | rhs);
 329
 330            case XOR:
 331                return new Long(lhs ^ rhs);
 332
 333            default:
 334                throw new InterpreterError(
 335					"Unimplemented binary long operator");
 336        }
 337    }
 338
 339    // returns Object covering both Integer and Boolean return types
 340    static Object intBinaryOperation(Integer I1, Integer I2, int kind)
 341    {
 342        int lhs = I1.intValue();
 343        int rhs = I2.intValue();
 344
 345        switch(kind)
 346        {
 347            // boolean
 348            case LT:
 349            case LTX:
 350                return new Boolean(lhs < rhs);
 351
 352            case GT:
 353            case GTX:
 354                return new Boolean(lhs > rhs);
 355
 356            case EQ:
 357                return new Boolean(lhs == rhs);
 358
 359            case LE:
 360            case LEX:
 361                return new Boolean(lhs <= rhs);
 362
 363            case GE:
 364            case GEX:
 365                return new Boolean(lhs >= rhs);
 366
 367            case NE:
 368                return new Boolean(lhs != rhs);
 369
 370            // arithmetic
 371            case PLUS:
 372                return new Integer(lhs + rhs);
 373
 374            case MINUS:
 375                return new Integer(lhs - rhs);
 376
 377            case STAR:
 378                return new Integer(lhs * rhs);
 379
 380            case SLASH:
 381                return new Integer(lhs / rhs);
 382
 383            case MOD:
 384                return new Integer(lhs % rhs);
 385
 386            // bitwise
 387            case LSHIFT:
 388            case LSHIFTX:
 389                return new Integer(lhs << rhs);
 390
 391            case RSIGNEDSHIFT:
 392            case RSIGNEDSHIFTX:
 393                return new Integer(lhs >> rhs);
 394
 395            case RUNSIGNEDSHIFT:
 396            case RUNSIGNEDSHIFTX:
 397                return new Integer(lhs >>> rhs);
 398
 399            case BIT_AND:
 400            case BIT_ANDX:
 401                return new Integer(lhs & rhs);
 402
 403            case BIT_OR:
 404            case BIT_ORX:
 405                return new Integer(lhs | rhs);
 406
 407            case XOR:
 408                return new Integer(lhs ^ rhs);
 409
 410            default:
 411                throw new InterpreterError(
 412					"Unimplemented binary integer operator");
 413        }
 414    }
 415
 416    // returns Object covering both Double and Boolean return types
 417    static Object doubleBinaryOperation(Double D1, Double D2, int kind)
 418        throws UtilEvalError
 419    {
 420        double lhs = D1.doubleValue();
 421        double rhs = D2.doubleValue();
 422
 423        switch(kind)
 424        {
 425            // boolean
 426            case LT:
 427            case LTX:
 428                return new Boolean(lhs < rhs);
 429
 430            case GT:
 431            case GTX:
 432                return new Boolean(lhs > rhs);
 433
 434            case EQ:
 435                return new Boolean(lhs == rhs);
 436
 437            case LE:
 438            case LEX:
 439                return new Boolean(lhs <= rhs);
 440
 441            case GE:
 442            case GEX:
 443                return new Boolean(lhs >= rhs);
 444
 445            case NE:
 446                return new Boolean(lhs != rhs);
 447
 448            // arithmetic
 449            case PLUS:
 450                return new Double(lhs + rhs);
 451
 452            case MINUS:
 453                return new Double(lhs - rhs);
 454
 455            case STAR:
 456                return new Double(lhs * rhs);
 457
 458            case SLASH:
 459                return new Double(lhs / rhs);
 460
 461            case MOD:
 462                return new Double(lhs % rhs);
 463
 464            // can't shift floating-point values
 465            case LSHIFT:
 466            case LSHIFTX:
 467            case RSIGNEDSHIFT:
 468            case RSIGNEDSHIFTX:
 469            case RUNSIGNEDSHIFT:
 470            case RUNSIGNEDSHIFTX:
 471                throw new UtilEvalError("Can't shift doubles");
 472
 473            default:
 474                throw new InterpreterError(
 475					"Unimplemented binary double operator");
 476        }
 477    }
 478    // returns Object covering both Long and Boolean return types
 479    static Object floatBinaryOperation(Float F1, Float F2, int kind)
 480        throws UtilEvalError
 481    {
 482        float lhs = F1.floatValue();
 483        float rhs = F2.floatValue();
 484
 485        switch(kind)
 486        {
 487            // boolean
 488            case LT:
 489            case LTX:
 490                return new Boolean(lhs < rhs);
 491
 492            case GT:
 493            case GTX:
 494                return new Boolean(lhs > rhs);
 495
 496            case EQ:
 497                return new Boolean(lhs == rhs);
 498
 499            case LE:
 500            case LEX:
 501                return new Boolean(lhs <= rhs);
 502
 503            case GE:
 504            case GEX:
 505                return new Boolean(lhs >= rhs);
 506
 507            case NE:
 508                return new Boolean(lhs != rhs);
 509
 510            // arithmetic
 511            case PLUS:
 512                return new Float(lhs + rhs);
 513
 514            case MINUS:
 515                return new Float(lhs - rhs);
 516
 517            case STAR:
 518                return new Float(lhs * rhs);
 519
 520            case SLASH:
 521                return new Float(lhs / rhs);
 522
 523            case MOD:
 524                return new Float(lhs % rhs);
 525
 526            // can't shift floats
 527            case LSHIFT:
 528            case LSHIFTX:
 529            case RSIGNEDSHIFT:
 530            case RSIGNEDSHIFTX:
 531            case RUNSIGNEDSHIFT:
 532            case RUNSIGNEDSHIFTX:
 533                throw new UtilEvalError("Can't shift floats ");
 534
 535            default:
 536                throw new InterpreterError(
 537					"Unimplemented binary float operator");
 538        }
 539    }
 540
 541	/**
 542		Promote primitive wrapper type to to Integer wrapper type
 543	*/
 544    static Object promoteToInteger(Object wrapper )
 545    {
 546        if(wrapper instanceof Character)
 547            return new Integer(((Character)wrapper).charValue());
 548        else if((wrapper instanceof Byte) || (wrapper instanceof Short))
 549            return new Integer(((Number)wrapper).intValue());
 550
 551        return wrapper;
 552    }
 553
 554	/**
 555		Promote the pair of primitives to the maximum type of the two.
 556		e.g. [int,long]->[long,long]
 557	*/
 558    static Object[] promotePrimitives(Object lhs, Object rhs)
 559    {
 560        lhs = promoteToInteger(lhs);
 561        rhs = promoteToInteger(rhs);
 562
 563        if((lhs instanceof Number) && (rhs instanceof Number))
 564        {
 565            Number lnum = (Number)lhs;
 566            Number rnum = (Number)rhs;
 567
 568            boolean b;
 569
 570            if((b = (lnum instanceof Double)) || (rnum instanceof Double))
 571            {
 572                if(b)
 573                    rhs = new Double(rnum.doubleValue());
 574                else
 575                    lhs = new Double(lnum.doubleValue());
 576            }
 577            else if((b = (lnum instanceof Float)) || (rnum instanceof Float))
 578            {
 579                if(b)
 580                    rhs = new Float(rnum.floatValue());
 581                else
 582                    lhs = new Float(lnum.floatValue());
 583            }
 584            else if((b = (lnum instanceof Long)) || (rnum instanceof Long))
 585            {
 586                if(b)
 587                    rhs = new Long(rnum.longValue());
 588                else
 589                    lhs = new Long(lnum.longValue());
 590            }
 591        }
 592
 593        return new Object[] { lhs, rhs };
 594    }
 595
 596    public static Primitive unaryOperation(Primitive val, int kind)
 597        throws UtilEvalError
 598    {
 599        if (val == NULL)
 600            throw new UtilEvalError(
 601				"illegal use of null object or 'null' literal");
 602        if (val == VOID)
 603            throw new UtilEvalError(
 604				"illegal use of undefined object or 'void' literal");
 605
 606        Class operandType = val.getType();
 607        Object operand = promoteToInteger(val.getValue());
 608
 609        if ( operand instanceof Boolean )
 610            return new Primitive(booleanUnaryOperation((Boolean)operand, kind));
 611        else if(operand instanceof Integer)
 612        {
 613            int result = intUnaryOperation((Integer)operand, kind);
 614
 615            // ++ and -- must be cast back the original type
 616            if(kind == INCR || kind == DECR)
 617            {
 618                if(operandType == Byte.TYPE)
 619                    return new Primitive((byte)result);
 620                if(operandType == Short.TYPE)
 621                    return new Primitive((short)result);
 622                if(operandType == Character.TYPE)
 623                    return new Primitive((char)result);
 624            }
 625
 626            return new Primitive(result);
 627        }
 628        else if(operand instanceof Long)
 629            return new Primitive(longUnaryOperation((Long)operand, kind));
 630        else if(operand instanceof Float)
 631            return new Primitive(floatUnaryOperation((Float)operand, kind));
 632        else if(operand instanceof Double)
 633            return new Primitive(doubleUnaryOperation((Double)operand, kind));
 634        else
 635            throw new InterpreterError(
 636				"An error occurred.  Please call technical support.");
 637    }
 638
 639    static boolean booleanUnaryOperation(Boolean B, int kind) 
 640		throws UtilEvalError
 641    {
 642        boolean operand = B.booleanValue();
 643        switch(kind)
 644        {
 645            case BANG:
 646                return !operand;
 647            default:
 648                throw new UtilEvalError("Operator inappropriate for boolean");
 649        }
 650    }
 651
 652    static int intUnaryOperation(Integer I, int kind)
 653    {
 654        int operand = I.intValue();
 655
 656        switch(kind)
 657        {
 658            case PLUS:
 659                return operand;
 660            case MINUS:
 661                return -operand;
 662            case TILDE:
 663                return ~operand;
 664            case INCR:
 665                return operand + 1;
 666            case DECR:
 667                return operand - 1;
 668            default:
 669                throw new InterpreterError("bad integer unaryOperation");
 670        }
 671    }
 672
 673    static long longUnaryOperation(Long L, int kind)
 674    {
 675        long operand = L.longValue();
 676
 677        switch(kind)
 678        {
 679            case PLUS:
 680                return operand;
 681            case MINUS:
 682                return -operand;
 683            case TILDE:
 684                return ~operand;
 685            case INCR:
 686                return operand + 1;
 687            case DECR:
 688                return operand - 1;
 689            default:
 690                throw new InterpreterError("bad long unaryOperation");
 691        }
 692    }
 693
 694    static float floatUnaryOperation(Float F, int kind)
 695    {
 696        float operand = F.floatValue();
 697
 698        switch(kind)
 699        {
 700            case PLUS:
 701                return operand;
 702            case MINUS:
 703                return -operand;
 704            default:
 705                throw new InterpreterError("bad float unaryOperation");
 706        }
 707    }
 708
 709    static double doubleUnaryOperation(Double D, int kind)
 710    {
 711        double operand = D.doubleValue();
 712
 713        switch(kind)
 714        {
 715            case PLUS:
 716                return operand;
 717            case MINUS:
 718                return -operand;
 719            default:
 720                throw new InterpreterError("bad double unaryOperation");
 721        }
 722    }
 723
 724    public int intValue() throws UtilEvalError
 725    {
 726        if(value instanceof Number)
 727            return((Number)value).intValue();
 728        else
 729            throw new UtilEvalError("Primitive not a number");
 730    }
 731
 732    public boolean booleanValue() throws UtilEvalError
 733    {
 734        if(value instanceof Boolean)
 735            return((Boolean)value).booleanValue();
 736        else
 737            throw new UtilEvalError("Primitive not a boolean");
 738    }
 739
 740	/**
 741		Determine if this primitive is a numeric type.
 742		i.e. not boolean, null, or void (but including char)
 743	*/
 744	public boolean isNumber() {
 745		return ( !(value instanceof Boolean) 
 746			&& !(this == NULL) && !(this == VOID) );
 747	}
 748
 749    public Number numberValue() throws UtilEvalError
 750    {
 751		Object value = this.value;
 752
 753		// Promote character to Number type for these purposes
 754		if (value instanceof Character)
 755			value = new Integer(((Character)value).charValue());
 756
 757        if (value instanceof Number)
 758            return (Number)value;
 759        else
 760            throw new UtilEvalError("Primitive not a number");
 761    }
 762
 763	/**
 764		Primitives compare equal with other Primitives containing an equal
 765		wrapped value.
 766	*/
 767	public boolean equals( Object obj ) 
 768	{
 769		if ( obj instanceof Primitive )
 770			return ((Primitive)obj).value.equals( this.value );
 771		else
 772			return false;
 773	}
 774
 775	/**
 776		The hash of the Primitive is tied to the hash of the wrapped value but
 777		shifted so that they are not the same.
 778	*/
 779	public int hashCode() 
 780	{
 781		return this.value.hashCode() * 21; // arbitrary
 782	}
 783
 784	/**
 785		Unwrap primitive values and map voids to nulls.
 786		Non Primitive types remain unchanged.
 787
 788		@param obj object type which may be bsh.Primitive
 789		@return corresponding "normal" Java type, "unwrapping" 
 790			any bsh.Primitive types to their wrapper types.
 791	*/
 792	public static Object unwrap( Object obj ) 
 793	{
 794        // map voids to nulls for the outside world
 795        if (obj == Primitive.VOID)
 796            return null;
 797
 798        // unwrap primitives
 799        if (obj instanceof Primitive)
 800            return((Primitive)obj).getValue();
 801        else
 802            return obj;
 803	}
 804
 805    /*
 806        Unwrap Primitive wrappers to their java.lang wrapper values.
 807		e.g. Primitive(42) becomes Integer(42)
 808		@see #unwrap( Object )
 809    */
 810    public static Object [] unwrap( Object[] args )
 811    {
 812		Object [] oa = new Object[ args.length ];
 813        for(int i=0; i<args.length; i++)
 814            oa[i] = unwrap( args[i] );
 815		return oa;
 816    }
 817
 818    /*
 819    */
 820    public static Object [] wrap( Object[] args, Class [] paramTypes )
 821    {
 822		if ( args == null )
 823			return null;
 824
 825		Object [] oa = new Object[ args.length ];
 826        for(int i=0; i<args.length; i++)
 827            oa[i] = wrap( args[i], paramTypes[i] );
 828		return oa;
 829    }
 830
 831	/**
 832		Wrap primitive values (as indicated by type param) and nulls in the 
 833		Primitive class.  Values not primitive or null are left unchanged.
 834		Primitive values are represented by their wrapped values in param value.
 835		<p/>
 836		The value null is mapped to Primitive.NULL.
 837		Any value specified with type Void.TYPE is mapped to Primitive.VOID.
 838	*/
 839    public static Object wrap(
 840		Object value, Class type )
 841    {
 842        if ( type == Void.TYPE )
 843            return Primitive.VOID;
 844
 845        if ( value == null )
 846            return Primitive.NULL;
 847
 848		if ( type.isPrimitive() )
 849			return new Primitive( value );
 850
 851		return value;
 852    }
 853
 854
 855	/**
 856		Get the appropriate default value per JLS 4.5.4
 857	*/
 858	public static Primitive getDefaultValue( Class type )
 859	{
 860		if ( type == null || !type.isPrimitive() )
 861			return Primitive.NULL;
 862		if ( type == Boolean.TYPE )
 863			return new Primitive( false );
 864
 865		// non boolean primitive, get appropriate flavor of zero
 866		try {
 867			return new Primitive((int)0).castToType( type, Types.CAST );
 868		} catch ( UtilEvalError e ) {
 869			throw new InterpreterError( "bad cast" );
 870		}
 871	}
 872
 873	/**
 874		Get the corresponding java.lang wrapper class for the primitive TYPE
 875		class.
 876		e.g.  Integer.TYPE -> Integer.class
 877	*/
 878	public static Class boxType( Class primitiveType )
 879	{
 880		Class c = (Class)primitiveToWrapper.get( primitiveType );
 881		if ( c != null )
 882			return c;
 883		throw new InterpreterError( 
 884			"Not a primitive type: "+ primitiveType );
 885	}
 886
 887	/**
 888		Get the corresponding primitive TYPE class for the java.lang wrapper
 889		class type.
 890		e.g.  Integer.class -> Integer.TYPE
 891	*/
 892	public static Class unboxType( Class wrapperType )
 893	{
 894		Class c = (Class)wrapperToPrimitive.get( wrapperType );
 895		if ( c != null )
 896			return c;
 897		throw new InterpreterError( 
 898			"Not a primitive wrapper type: "+wrapperType );
 899	}
 900
 901	/**
 902		Cast this bsh.Primitive value to a new bsh.Primitive value
 903		This is usually a numeric type cast.  Other cases include:
 904			A boolean can be cast to boolen
 905			null can be cast to any object type and remains null
 906			Attempting to cast a void causes an exception
 907		@param toType is the java object or primitive TYPE class
 908	*/
 909	public Primitive castToType( Class toType, int operation ) 
 910		throws UtilEvalError
 911	{
 912		return castPrimitive( 
 913			toType, getType()/*fromType*/, this/*fromValue*/, 
 914			false/*checkOnly*/, operation );
 915	}
 916
 917	/**
 918		@param toType is the java primitive TYPE type of the primitive to be
 919		cast to.
 920		@param fromType is the java primitive TYPE type of the primitive to be
 921		cast.  fromType should be null to indicate that the fromValue was null
 922		or void.
 923		@param fromValue is, optionally, the value to be converted.  If
 924		checkOnly is true fromValue must be null.  If checkOnly is false,
 925		fromValue must be non-null (Primitive.NULL is of course valid).
 926	*/
 927	static Primitive castPrimitive( 
 928		Class toType, Class fromType, Primitive fromValue, 
 929		boolean checkOnly, int operation ) 
 930		throws UtilEvalError
 931	{
 932		/*
 933			Lots of preconditions checked here...
 934			Once things are running smoothly we might comment these out
 935			(That's what assertions are for).
 936		*/
 937		if ( checkOnly && fromValue != null )
 938			throw new InterpreterError("bad cast param 1");
 939		if ( !checkOnly && fromValue == null )
 940			throw new InterpreterError("bad cast param 2");
 941		if ( fromType != null && !fromType.isPrimitive() )
 942			throw new InterpreterError("bad fromType:" +fromType);
 943		if ( fromValue == Primitive.NULL && fromType != null )
 944			throw new InterpreterError("inconsistent args 1");
 945		if ( fromValue == Primitive.VOID && fromType != Void.TYPE )
 946			throw new InterpreterError("inconsistent args 2");
 947
 948		// can't cast void to anything
 949		if ( fromType == Void.TYPE )
 950			if ( checkOnly )
 951				return Types.INVALID_CAST;
 952			else
 953				throw Types.castError( Reflect.normalizeClassName(toType), 
 954					"void value", operation );
 955
 956		// unwrap, etc.
 957		Object value = null; 
 958		if ( fromValue != null )
 959			value = fromValue.getValue();
 960
 961		if ( toType.isPrimitive() )
 962		{
 963			// Trying to cast null to primitive type?
 964			if ( fromType == null )
 965				if ( checkOnly )
 966					return Types.INVALID_CAST;
 967				else
 968					throw Types.castError(
 969						"primitive type:" + toType, "Null value", operation );
 970
 971			// fall through
 972		} else
 973		{
 974			// Trying to cast primitive to an object type
 975			// Primitive.NULL can be cast to any object type
 976			if ( fromType == null )
 977				return checkOnly ? Types.VALID_CAST : 
 978					Primitive.NULL;
 979
 980			if ( checkOnly )
 981				return Types.INVALID_CAST;
 982			else
 983				throw Types.castError(
 984						"object type:" + toType, "primitive value", operation);
 985		}
 986
 987		// can only cast boolean to boolean
 988		if ( fromType == Boolean.TYPE )
 989		{
 990			if ( toType != Boolean.TYPE )
 991				if ( checkOnly )
 992					return Types.INVALID_CAST;
 993				else
 994					throw Types.castError( toType, fromType, operation );
 995
 996			return checkOnly ? Types.VALID_CAST :
 997				fromValue;
 998		}
 999
1000		// Do numeric cast
1001
1002		// Only allow legal Java assignment unless we're a CAST operation
1003		if ( operation == Types.ASSIGNMENT 
1004			&& !Types.isJavaAssignable( toType, fromType ) 
1005		) {
1006			if ( checkOnly )
1007				return Types.INVALID_CAST;
1008			else
1009				throw Types.castError( toType, fromType, operation );
1010		}
1011
1012		return checkOnly ? Types.VALID_CAST :
1013			new Primitive( castWrapper(toType, value) );
1014	}
1015
1016	public static boolean isWrapperType( Class type )
1017	{
1018		return wrapperToPrimitive.get( type ) != null;
1019	}
1020
1021	/**
1022		Cast a primitive value represented by its java.lang wrapper type to the
1023		specified java.lang wrapper type.  e.g.  Byte(5) to Integer(5) or
1024		Integer(5) to Byte(5) 
1025		@param toType is the java TYPE type
1026		@param value is the value in java.lang wrapper.
1027		value may not be null.
1028	*/
1029	static Object castWrapper( 
1030		Class toType, Object value ) 
1031	{
1032		if ( !toType.isPrimitive() )
1033			throw new InterpreterError("invalid type in castWrapper: "+toType);
1034		if ( value == null )
1035			throw new InterpreterError("null value in castWrapper, guard");
1036		if ( value instanceof Boolean && toType != Boolean.TYPE )
1037			throw new InterpreterError("bad wrapper cast of boolean");
1038
1039		Class fromType = value.getClass();
1040
1041		// first promote char to Number type to avoid duplicating code
1042		if ( value instanceof Character )
1043			value = new Integer(((Character)value).charValue());
1044
1045		if ( !(value instanceof Number) )
1046			throw new InterpreterError("bad type in cast");
1047
1048		Number number = (Number)value;
1049
1050		if (toType == Byte.TYPE)
1051			return new Byte(number.byteValue());
1052		if (toType == Short.TYPE)
1053			return new Short(number.shortValue());
1054		if (toType == Character.TYPE)
1055			return new Character((char)number.intValue());
1056		if (toType == Integer.TYPE)
1057			return new Integer(number.intValue());
1058		if (toType == Long.TYPE)
1059			return new Long(number.longValue());
1060		if (toType == Float.TYPE)
1061			return new Float(number.floatValue());
1062		if (toType == Double.TYPE)
1063			return new Double(number.doubleValue());
1064
1065		throw new InterpreterError("error in wrapper cast");
1066	}
1067
1068}