PageRenderTime 169ms CodeModel.GetById 110ms app.highlight 54ms RepoModel.GetById 1ms app.codeStats 0ms

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

#
Java | 842 lines | 573 code | 147 blank | 122 comment | 133 complexity | 6657eea72a25e939490e137a1f66cebc 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
 37/**
 38    Wrapper for primitive types in Bsh.  This is package public because it 
 39	is used in the implementation of some bsh commands.
 40
 41    See the note in LHS.java about wrapping objects.
 42*/
 43public class Primitive implements ParserConstants, java.io.Serializable
 44{
 45    /** The primitive value stored in its java.lang wrapper class */
 46    private Object value;
 47
 48    private static class Special implements java.io.Serializable
 49    {
 50        private Special() { }
 51
 52        public static final Special NULL_VALUE = new Special();
 53        public static final Special VOID_TYPE = new Special();
 54    }
 55
 56    /*
 57        NULL means "no value".
 58        This ia a placeholder for primitive null value.
 59    */
 60    public static final Primitive NULL = new Primitive(Special.NULL_VALUE);
 61
 62    /**
 63        VOID means "no type".
 64        Strictly speaking, this makes no sense here.  But for practical
 65        reasons we'll consider the lack of a type to be a special value.
 66    */
 67    public static final Primitive VOID = new Primitive(Special.VOID_TYPE);
 68
 69    // private to prevent invocation with param that isn't a primitive-wrapper
 70    private Primitive(Object value)
 71    {
 72        if(value == null)
 73            throw new InterpreterError(
 74				"Use Primitve.NULL instead of Primitive(null)");
 75
 76        this.value = value;
 77    }
 78
 79    public Primitive(Number number) { this((Object)number); }
 80
 81    public Primitive(Boolean value) { this((Object)value); }
 82    public Primitive(Byte value) { this((Object)value); }
 83    public Primitive(Short value) { this((Object)value); }
 84    public Primitive(Character value) { this((Object)value); }
 85    public Primitive(Integer value) { this((Object)value); }
 86    public Primitive(Long value) { this((Object)value); }
 87    public Primitive(Float value) { this((Object)value); }
 88    public Primitive(Double value) { this((Object)value); }
 89
 90    public Primitive(boolean value) { this(new Boolean(value)); }
 91    public Primitive(byte value) { this(new Byte(value)); }
 92    public Primitive(short value) { this(new Short(value)); }
 93    public Primitive(char value) { this(new Character(value)); }
 94    public Primitive(int value) { this(new Integer(value)); }
 95    public Primitive(long value) { this(new Long(value)); }
 96    public Primitive(float value) { this(new Float(value)); }
 97    public Primitive(double value) { this(new Double(value)); }
 98
 99	/**
100    	Return the primitive value stored in its java.lang wrapper class 
101	*/
102    public Object getValue()
103    {
104        if ( value == Special.NULL_VALUE )
105            return null;
106        else 
107		if ( value == Special.VOID_TYPE )
108                throw new InterpreterError("attempt to unwrap void type");
109        else
110            return value;
111    }
112
113    public String toString()
114    {
115        if(value == Special.NULL_VALUE)
116            return "null";
117        else if(value == Special.VOID_TYPE)
118            return "void";
119        else
120            return value.toString();
121    }
122
123	/**
124		Get the corresponding primitive TYPE class for the Primitive
125		@return the primitive TYPE class type of the value or Void.TYPE for
126		Primitive.VOID or null value for type of Primitive.NULL
127	*/
128    public Class getType()
129    {
130		if ( this == Primitive.VOID )
131			return Void.TYPE;
132
133		// NULL return null as type... we currently use null type to indicate 
134		// loose typing throughout bsh.
135		if ( this == Primitive.NULL )
136			return null;
137
138        if ( value instanceof Boolean )
139            return Boolean.TYPE;
140        else if(value instanceof Byte)
141            return Byte.TYPE;
142        else if(value instanceof Short)
143            return Short.TYPE;
144        else if(value instanceof Character)
145            return Character.TYPE;
146        else if(value instanceof Integer)
147            return Integer.TYPE;
148        else if(value instanceof Long)
149            return Long.TYPE;
150        else if(value instanceof Float)
151            return Float.TYPE;
152        else if(value instanceof Double)
153            return Double.TYPE;
154
155		throw new InterpreterError("uknown prim: "+ this );
156    }
157
158	/**
159		Perform a binary operation on two Primitives or wrapper types.
160		If both original args were Primitives return a Primitive result
161		else it was mixed (wrapper/primitive) return the wrapper type.
162		The exception is for boolean operations where we will return the 
163		primitive type eithe way.
164	*/
165    public static Object binaryOperation(
166		Object obj1, Object obj2, int kind)
167        throws UtilEvalError
168    {
169		// special primitive types
170        if ( obj1 == NULL || obj2 == NULL )
171            throw new UtilEvalError(
172				"Null value or 'null' literal in binary operation");
173        if ( obj1 == VOID || obj2 == VOID )
174            throw new UtilEvalError(
175			"Undefined variable, class, or 'void' literal in binary operation");
176
177		// keep track of the original types
178		Class lhsOrgType = obj1.getClass();
179		Class rhsOrgType = obj2.getClass();
180
181		// Unwrap primitives
182        if ( obj1 instanceof Primitive )
183            obj1 = ((Primitive)obj1).getValue();
184        if ( obj2 instanceof Primitive )
185            obj2 = ((Primitive)obj2).getValue();
186
187        Object[] operands = promotePrimitives(obj1, obj2);
188        Object lhs = operands[0];
189        Object rhs = operands[1];
190
191        if(lhs.getClass() != rhs.getClass())
192            throw new UtilEvalError("Type mismatch in operator.  " 
193			+ lhs.getClass() + " cannot be used with " + rhs.getClass() );
194
195		Object result;
196		try {
197			result = binaryOperationImpl( lhs, rhs, kind );
198		} catch ( ArithmeticException e ) {
199			throw new UtilTargetError( "Arithemetic Exception in binary op", e);
200		}
201
202		// If both original args were Primitives return a Primitive result
203		// else it was mixed (wrapper/primitive) return the wrapper type
204		// Exception is for boolean result, return the primitive
205		if ( (lhsOrgType == Primitive.class && rhsOrgType == Primitive.class)
206			|| result instanceof Boolean
207		)
208			return new Primitive( result );
209		else
210			return result;
211    }
212
213    static Object binaryOperationImpl( Object lhs, Object rhs, int kind )
214        throws UtilEvalError
215	{
216        if(lhs instanceof Boolean)
217            return booleanBinaryOperation((Boolean)lhs, (Boolean)rhs, kind);
218        else if(lhs instanceof Integer)
219            return intBinaryOperation( (Integer)lhs, (Integer)rhs, kind );
220        else if(lhs instanceof Long)
221            return longBinaryOperation((Long)lhs, (Long)rhs, kind);
222        else if(lhs instanceof Float)
223            return floatBinaryOperation((Float)lhs, (Float)rhs, kind);
224        else if(lhs instanceof Double)
225            return doubleBinaryOperation( (Double)lhs, (Double)rhs, kind);
226        else
227            throw new UtilEvalError("Invalid types in binary operator" );
228	}
229
230
231    static Boolean booleanBinaryOperation(Boolean B1, Boolean B2, int kind)
232        throws UtilEvalError
233    {
234        boolean lhs = B1.booleanValue();
235        boolean rhs = B2.booleanValue();
236
237        switch(kind)
238        {
239            case EQ:
240                return new Boolean(lhs == rhs);
241
242            case NE:
243                return new Boolean(lhs != rhs);
244
245            case BOOL_OR:
246            case BOOL_ORX:
247                return new Boolean( lhs || rhs );
248
249            case BOOL_AND:
250            case BOOL_ANDX:
251                return new Boolean( lhs && rhs );
252
253            default:
254                throw new InterpreterError("unimplemented binary operator");
255        }
256    }
257
258    // returns Object covering both Long and Boolean return types
259    static Object longBinaryOperation(Long L1, Long L2, int kind)
260    {
261        long lhs = L1.longValue();
262        long rhs = L2.longValue();
263
264        switch(kind)
265        {
266            // boolean
267            case LT:
268            case LTX:
269                return new Boolean(lhs < rhs);
270
271            case GT:
272            case GTX:
273                return new Boolean(lhs > rhs);
274
275            case EQ:
276                return new Boolean(lhs == rhs);
277
278            case LE:
279            case LEX:
280                return new Boolean(lhs <= rhs);
281
282            case GE:
283            case GEX:
284                return new Boolean(lhs >= rhs);
285
286            case NE:
287                return new Boolean(lhs != rhs);
288
289            // arithmetic
290            case PLUS:
291                return new Long(lhs + rhs);
292
293            case MINUS:
294                return new Long(lhs - rhs);
295
296            case STAR:
297                return new Long(lhs * rhs);
298
299            case SLASH:
300                return new Long(lhs / rhs);
301
302            case MOD:
303                return new Long(lhs % rhs);
304
305            // bitwise
306            case LSHIFT:
307            case LSHIFTX:
308                return new Long(lhs << rhs);
309
310            case RSIGNEDSHIFT:
311            case RSIGNEDSHIFTX:
312                return new Long(lhs >> rhs);
313
314            case RUNSIGNEDSHIFT:
315            case RUNSIGNEDSHIFTX:
316                return new Long(lhs >>> rhs);
317
318            case BIT_AND:
319            case BIT_ANDX:
320                return new Long(lhs & rhs);
321
322            case BIT_OR:
323            case BIT_ORX:
324                return new Long(lhs | rhs);
325
326            case XOR:
327                return new Long(lhs ^ rhs);
328
329            default:
330                throw new InterpreterError("Unimplemented binary long operator");
331        }
332    }
333
334    // returns Object covering both Integer and Boolean return types
335    static Object intBinaryOperation(Integer I1, Integer I2, int kind)
336    {
337        int lhs = I1.intValue();
338        int rhs = I2.intValue();
339
340        switch(kind)
341        {
342            // boolean
343            case LT:
344            case LTX:
345                return new Boolean(lhs < rhs);
346
347            case GT:
348            case GTX:
349                return new Boolean(lhs > rhs);
350
351            case EQ:
352                return new Boolean(lhs == rhs);
353
354            case LE:
355            case LEX:
356                return new Boolean(lhs <= rhs);
357
358            case GE:
359            case GEX:
360                return new Boolean(lhs >= rhs);
361
362            case NE:
363                return new Boolean(lhs != rhs);
364
365            // arithmetic
366            case PLUS:
367                return new Integer(lhs + rhs);
368
369            case MINUS:
370                return new Integer(lhs - rhs);
371
372            case STAR:
373                return new Integer(lhs * rhs);
374
375            case SLASH:
376                return new Integer(lhs / rhs);
377
378            case MOD:
379                return new Integer(lhs % rhs);
380
381            // bitwise
382            case LSHIFT:
383            case LSHIFTX:
384                return new Integer(lhs << rhs);
385
386            case RSIGNEDSHIFT:
387            case RSIGNEDSHIFTX:
388                return new Integer(lhs >> rhs);
389
390            case RUNSIGNEDSHIFT:
391            case RUNSIGNEDSHIFTX:
392                return new Integer(lhs >>> rhs);
393
394            case BIT_AND:
395            case BIT_ANDX:
396                return new Integer(lhs & rhs);
397
398            case BIT_OR:
399            case BIT_ORX:
400                return new Integer(lhs | rhs);
401
402            case XOR:
403                return new Integer(lhs ^ rhs);
404
405            default:
406                throw new InterpreterError("Unimplemented binary integer operator");
407        }
408    }
409
410    // returns Object covering both Double and Boolean return types
411    static Object doubleBinaryOperation(Double D1, Double D2, int kind)
412        throws UtilEvalError
413    {
414        double lhs = D1.doubleValue();
415        double rhs = D2.doubleValue();
416
417        switch(kind)
418        {
419            // boolean
420            case LT:
421            case LTX:
422                return new Boolean(lhs < rhs);
423
424            case GT:
425            case GTX:
426                return new Boolean(lhs > rhs);
427
428            case EQ:
429                return new Boolean(lhs == rhs);
430
431            case LE:
432            case LEX:
433                return new Boolean(lhs <= rhs);
434
435            case GE:
436            case GEX:
437                return new Boolean(lhs >= rhs);
438
439            case NE:
440                return new Boolean(lhs != rhs);
441
442            // arithmetic
443            case PLUS:
444                return new Double(lhs + rhs);
445
446            case MINUS:
447                return new Double(lhs - rhs);
448
449            case STAR:
450                return new Double(lhs * rhs);
451
452            case SLASH:
453                return new Double(lhs / rhs);
454
455            case MOD:
456                return new Double(lhs % rhs);
457
458            // can't shift floating-point values
459            case LSHIFT:
460            case LSHIFTX:
461            case RSIGNEDSHIFT:
462            case RSIGNEDSHIFTX:
463            case RUNSIGNEDSHIFT:
464            case RUNSIGNEDSHIFTX:
465                throw new UtilEvalError("Can't shift doubles");
466
467            default:
468                throw new InterpreterError("Unimplemented binary double operator");
469        }
470    }
471    // returns Object covering both Long and Boolean return types
472    static Object floatBinaryOperation(Float F1, Float F2, int kind)
473        throws UtilEvalError
474    {
475        float lhs = F1.floatValue();
476        float rhs = F2.floatValue();
477
478        switch(kind)
479        {
480            // boolean
481            case LT:
482            case LTX:
483                return new Boolean(lhs < rhs);
484
485            case GT:
486            case GTX:
487                return new Boolean(lhs > rhs);
488
489            case EQ:
490                return new Boolean(lhs == rhs);
491
492            case LE:
493            case LEX:
494                return new Boolean(lhs <= rhs);
495
496            case GE:
497            case GEX:
498                return new Boolean(lhs >= rhs);
499
500            case NE:
501                return new Boolean(lhs != rhs);
502
503            // arithmetic
504            case PLUS:
505                return new Float(lhs + rhs);
506
507            case MINUS:
508                return new Float(lhs - rhs);
509
510            case STAR:
511                return new Float(lhs * rhs);
512
513            case SLASH:
514                return new Float(lhs / rhs);
515
516            case MOD:
517                return new Float(lhs % rhs);
518
519            // can't shift floats
520            case LSHIFT:
521            case LSHIFTX:
522            case RSIGNEDSHIFT:
523            case RSIGNEDSHIFTX:
524            case RUNSIGNEDSHIFT:
525            case RUNSIGNEDSHIFTX:
526                throw new UtilEvalError("Can't shift floats ");
527
528            default:
529                throw new InterpreterError("Unimplemented binary float operator");
530        }
531    }
532
533	/**
534		Promote primitive wrapper type to to Integer wrapper type
535		Can we use the castPrimitive() (in BSHCastExpression) for this?
536	*/
537    static Object promoteToInteger(Object primitive)
538    {
539        if(primitive instanceof Character)
540            return new Integer(((Character)primitive).charValue());
541        else if((primitive instanceof Byte) || (primitive instanceof Short))
542            return new Integer(((Number)primitive).intValue());
543
544        return primitive;
545    }
546
547	/**
548		Promote the pair of primitives to the maximum type of the two.
549		e.g. [int,long]->[long,long]
550	*/
551    static Object[] promotePrimitives(Object lhs, Object rhs)
552    {
553        lhs = promoteToInteger(lhs);
554        rhs = promoteToInteger(rhs);
555
556        if((lhs instanceof Number) && (rhs instanceof Number))
557        {
558            Number lnum = (Number)lhs;
559            Number rnum = (Number)rhs;
560
561            boolean b;
562
563            if((b = (lnum instanceof Double)) || (rnum instanceof Double))
564            {
565                if(b)
566                    rhs = new Double(rnum.doubleValue());
567                else
568                    lhs = new Double(lnum.doubleValue());
569            }
570            else if((b = (lnum instanceof Float)) || (rnum instanceof Float))
571            {
572                if(b)
573                    rhs = new Float(rnum.floatValue());
574                else
575                    lhs = new Float(lnum.floatValue());
576            }
577            else if((b = (lnum instanceof Long)) || (rnum instanceof Long))
578            {
579                if(b)
580                    rhs = new Long(rnum.longValue());
581                else
582                    lhs = new Long(lnum.longValue());
583            }
584        }
585
586        return new Object[] { lhs, rhs };
587    }
588
589    public static Primitive unaryOperation(Primitive val, int kind)
590        throws UtilEvalError
591    {
592        if (val == NULL)
593            throw new UtilEvalError(
594				"illegal use of null object or 'null' literal");
595        if (val == VOID)
596            throw new UtilEvalError(
597				"illegal use of undefined object or 'void' literal");
598
599        Class operandType = val.getType();
600        Object operand = promoteToInteger(val.getValue());
601
602        if ( operand instanceof Boolean )
603            return new Primitive(booleanUnaryOperation((Boolean)operand, kind));
604        else if(operand instanceof Integer)
605        {
606            int result = intUnaryOperation((Integer)operand, kind);
607
608            // ++ and -- must be cast back the original type
609            if(kind == INCR || kind == DECR)
610            {
611                if(operandType == Byte.TYPE)
612                    return new Primitive((byte)result);
613                if(operandType == Short.TYPE)
614                    return new Primitive((short)result);
615                if(operandType == Character.TYPE)
616                    return new Primitive((char)result);
617            }
618
619            return new Primitive(result);
620        }
621        else if(operand instanceof Long)
622            return new Primitive(longUnaryOperation((Long)operand, kind));
623        else if(operand instanceof Float)
624            return new Primitive(floatUnaryOperation((Float)operand, kind));
625        else if(operand instanceof Double)
626            return new Primitive(doubleUnaryOperation((Double)operand, kind));
627        else
628            throw new InterpreterError(
629				"An error occurred.  Please call technical support.");
630    }
631
632    static boolean booleanUnaryOperation(Boolean B, int kind) 
633		throws UtilEvalError
634    {
635        boolean operand = B.booleanValue();
636        switch(kind)
637        {
638            case BANG:
639                return !operand;
640
641            default:
642                throw new UtilEvalError("Operator inappropriate for boolean");
643        }
644    }
645
646    static int intUnaryOperation(Integer I, int kind)
647    {
648        int operand = I.intValue();
649
650        switch(kind)
651        {
652            case PLUS:
653                return operand;
654
655            case MINUS:
656                return -operand;
657
658            case TILDE:
659                return ~operand;
660
661            case INCR:
662                return operand + 1;
663
664            case DECR:
665                return operand - 1;
666
667            default:
668                throw new InterpreterError("bad integer unaryOperation");
669        }
670    }
671
672    static long longUnaryOperation(Long L, int kind)
673    {
674        long operand = L.longValue();
675
676        switch(kind)
677        {
678            case PLUS:
679                return operand;
680
681            case MINUS:
682                return -operand;
683
684            case TILDE:
685                return ~operand;
686
687            case INCR:
688                return operand + 1;
689
690            case DECR:
691                return operand - 1;
692
693            default:
694                throw new InterpreterError("bad long unaryOperation");
695        }
696    }
697
698    static float floatUnaryOperation(Float F, int kind)
699    {
700        float operand = F.floatValue();
701
702        switch(kind)
703        {
704            case PLUS:
705                return operand;
706
707            case MINUS:
708                return -operand;
709
710            default:
711                throw new InterpreterError("bad float unaryOperation");
712        }
713    }
714
715    static double doubleUnaryOperation(Double D, int kind)
716    {
717        double operand = D.doubleValue();
718
719        switch(kind)
720        {
721            case PLUS:
722                return operand;
723
724            case MINUS:
725                return -operand;
726
727            default:
728                throw new InterpreterError("bad double unaryOperation");
729        }
730    }
731
732    public int intValue() throws UtilEvalError
733    {
734        if(value instanceof Number)
735            return((Number)value).intValue();
736        else
737            throw new UtilEvalError("Primitive not a number");
738    }
739
740    public boolean booleanValue() throws UtilEvalError
741    {
742        if(value instanceof Boolean)
743            return((Boolean)value).booleanValue();
744        else
745            throw new UtilEvalError("Primitive not a boolean");
746    }
747
748	/**
749		Determine if this primitive is a numeric type.
750		i.e. not boolean, null, or void (but including char)
751	*/
752	public boolean isNumber() {
753		return ( !(value instanceof Boolean) 
754			&& !(this == NULL) && !(this == VOID) );
755	}
756
757    public Number numberValue() throws UtilEvalError
758    {
759		Object value = this.value;
760
761		// Promote character to Number type for these purposes
762		if (value instanceof Character)
763			value = new Integer(((Character)value).charValue());
764
765        if (value instanceof Number)
766            return (Number)value;
767        else
768            throw new UtilEvalError("Primitive not a number");
769    }
770
771	/**
772		Primitives compare equal with other Primitives containing an equal
773		wrapped value.
774	*/
775	public boolean equals( Object obj ) 
776	{
777		if ( obj instanceof Primitive )
778			return ((Primitive)obj).value.equals( this.value );
779		else
780			return false;
781	}
782
783	/**
784		The hash of the Primitive is tied to the hash of the wrapped value but
785		shifted so that they are not the same.
786	*/
787	public int hashCode() 
788	{
789		return this.value.hashCode() * 21; // arbitrary
790	}
791
792	/**
793		Unwrap primitive values and map voids to nulls.
794		Normal (non Primitive) types remain unchanged.
795		@param obj object type which may be bsh.Primitive
796		@return corresponding "normal" Java type, "unwrapping" 
797			any bsh.Primitive types to their wrapper types.
798	*/
799	public static Object unwrap( Object obj ) 
800	{
801        // map voids to nulls for the outside world
802        if (obj == Primitive.VOID)
803            return null;
804
805        // unwrap primitives
806        if (obj instanceof Primitive)
807            return((Primitive)obj).getValue();
808        else
809            return obj;
810	}
811
812	/**
813		Get the appropriate default value per JLS 4.5.4
814	*/
815	public static Primitive getDefaultValue( Class type )
816	{
817		if ( type != null && type.isPrimitive() )
818		{
819			if ( type == Boolean.TYPE )
820				return new Primitive(Boolean.FALSE);
821			if (type ==	Byte.TYPE)
822				return new	Primitive((byte)0);
823			if (type ==	Short.TYPE)
824				return new Primitive((short)0);
825			if (type ==	Character.TYPE)
826				return new Primitive((char)0);
827			if (type ==	Integer.TYPE)
828				return new Primitive((int)0);
829			if (type ==	Long.TYPE)
830				return new Primitive(0L);
831			if (type ==	Float.TYPE)
832				return new Primitive(0.0f);
833			if (type ==	Double.TYPE)
834				return new Primitive(0.0d);
835
836			throw new InterpreterError("unknown prim");
837		}
838		else
839			return Primitive.NULL;
840	}
841
842}