KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > bsh > Types


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 package bsh;
35
36 /**
37     Static routines supporing type comparison and conversion in BeanShell.
38
39  The following are notes on type comparison and conversion in BeanShell.
40
41
42 */

43 class Types
44 {
45     /*
46         Type conversion identifiers. An ASSIGNMENT allows conversions that would
47         normally happen on assignment. A CAST performs numeric conversions to smaller
48         types (as in an explicit Java cast) and things allowed only in variable and array
49         declarations (e.g. byte b = 42;)
50     */

51     static final int CAST=0, ASSIGNMENT=1;
52     
53     static final int
54         JAVA_BASE_ASSIGNABLE = 1,
55         JAVA_BOX_TYPES_ASSIGABLE = 2,
56         JAVA_VARARGS_ASSIGNABLE = 3,
57         BSH_ASSIGNABLE = 4;
58
59     static final int
60         FIRST_ROUND_ASSIGNABLE = JAVA_BASE_ASSIGNABLE,
61         LAST_ROUND_ASSIGNABLE = BSH_ASSIGNABLE;
62
63     /**
64         Special value that indicates by identity that the result of a cast
65         operation was a valid cast. This is used by castObject() and
66         castPrimitive() in the checkOnly mode of operation. This value is a
67         Primitive type so that it can be returned by castPrimitive.
68     */

69     static Primitive VALID_CAST = new Primitive(1);
70     static Primitive INVALID_CAST = new Primitive(-1);
71
72     /**
73         Get the Java types of the arguments.
74     */

75     public static Class JavaDoc[] getTypes( Object JavaDoc[] args )
76     {
77         if ( args == null )
78             return new Class JavaDoc[0];
79
80         Class JavaDoc[] types = new Class JavaDoc[ args.length ];
81
82         for( int i=0; i<args.length; i++ )
83         {
84             if ( args[i] == null )
85                 types[i] = null;
86             else
87             if ( args[i] instanceof Primitive )
88                 types[i] = ((Primitive)args[i]).getType();
89             else
90                 types[i] = args[i].getClass();
91         }
92
93         return types;
94     }
95
96     /**
97      Is the 'from' signature (argument types) assignable to the 'to'
98      signature (candidate method types)
99      This method handles the special case of null values in 'to' types
100      indicating a loose type and matching anything.
101      */

102     /* Should check for strict java here and limit to isJavaAssignable() */
103     static boolean isSignatureAssignable( Class JavaDoc[] from, Class JavaDoc[] to, int round )
104     {
105         if ( round != JAVA_VARARGS_ASSIGNABLE && from.length != to.length )
106             return false;
107
108         switch ( round )
109         {
110             case JAVA_BASE_ASSIGNABLE:
111                 for( int i=0; i<from.length; i++ )
112                     if ( !isJavaBaseAssignable( to[i], from[i] ) )
113                         return false;
114                 return true;
115             case JAVA_BOX_TYPES_ASSIGABLE:
116                 for( int i=0; i<from.length; i++ )
117                     if ( !isJavaBoxTypesAssignable( to[i], from[i] ) )
118                         return false;
119                 return true;
120             case JAVA_VARARGS_ASSIGNABLE:
121                 return isSignatureVarargsAssignable( from, to );
122             case BSH_ASSIGNABLE:
123                 for( int i=0; i<from.length; i++ )
124                     if ( !isBshAssignable( to[i], from[i] ) )
125                         return false;
126                 return true;
127             default:
128                 throw new InterpreterError("bad case");
129         }
130     }
131
132     private static boolean isSignatureVarargsAssignable(
133         Class JavaDoc[] from, Class JavaDoc[] to )
134     {
135         return false;
136     }
137
138     /**
139         Test if a conversion of the rhsType type to the lhsType type is legal via
140      standard Java assignment conversion rules (i.e. without a cast).
141      The rules include Java 5 autoboxing/unboxing.
142         <p/>
143
144         For Java primitive TYPE classes this method takes primitive promotion
145         into account. The ordinary Class.isAssignableFrom() does not take
146         primitive promotion conversions into account. Note that Java allows
147         additional assignments without a cast in combination with variable
148         declarations and array allocations. Those are handled elsewhere
149         (maybe should be here with a flag?)
150         <p/>
151         This class accepts a null rhsType type indicating that the rhsType was the
152         value Primitive.NULL and allows it to be assigned to any reference lhsType
153         type (non primitive).
154         <p/>
155
156         Note that the getAssignableForm() method is the primary bsh method for
157         checking assignability. It adds additional bsh conversions, etc.
158
159         @see #isBshAssignable( Class, Class )
160         @param lhsType assigning from rhsType to lhsType
161         @param rhsType assigning from rhsType to lhsType
162     */

163     static boolean isJavaAssignable( Class JavaDoc lhsType, Class JavaDoc rhsType ) {
164         return isJavaBaseAssignable( lhsType, rhsType )
165             || isJavaBoxTypesAssignable( lhsType, rhsType );
166     }
167
168     /**
169         Is the assignment legal via original Java (up to version 1.4)
170         assignment rules, not including auto-boxing/unboxing.
171      @param rhsType may be null to indicate primitive null value
172     */

173     static boolean isJavaBaseAssignable( Class JavaDoc lhsType, Class JavaDoc rhsType )
174     {
175         /*
176             Assignment to loose type, defer to bsh extensions
177             Note: we could shortcut this here:
178             if ( lhsType == null ) return true;
179             rather than forcing another round. It's not strictly a Java issue,
180             so does it belong here?
181         */

182         if ( lhsType == null )
183             return false;
184
185         // null rhs type corresponds to type of Primitive.NULL
186
// assignable to any object type
187
if ( rhsType == null )
188             return !lhsType.isPrimitive();
189
190         if ( lhsType.isPrimitive() && rhsType.isPrimitive() )
191         {
192             if ( lhsType == rhsType )
193                 return true;
194
195             // handle primitive widening conversions - JLS 5.1.2
196
if ( (rhsType == Byte.TYPE) &&
197                 (lhsType == Short.TYPE || lhsType == Integer.TYPE
198                 || lhsType == Long.TYPE || lhsType == Float.TYPE
199                 || lhsType == Double.TYPE))
200                     return true;
201
202             if ( (rhsType == Short.TYPE) &&
203                 (lhsType == Integer.TYPE || lhsType == Long.TYPE ||
204                 lhsType == Float.TYPE || lhsType == Double.TYPE))
205                     return true;
206
207             if ((rhsType == Character.TYPE) &&
208                 (lhsType == Integer.TYPE || lhsType == Long.TYPE ||
209                 lhsType == Float.TYPE || lhsType == Double.TYPE))
210                     return true;
211
212             if ((rhsType == Integer.TYPE) &&
213                 (lhsType == Long.TYPE || lhsType == Float.TYPE ||
214                 lhsType == Double.TYPE))
215                     return true;
216
217             if ((rhsType == Long.TYPE) &&
218                 (lhsType == Float.TYPE || lhsType == Double.TYPE))
219                 return true;
220
221             if ((rhsType == Float.TYPE) && (lhsType == Double.TYPE))
222                 return true;
223         }
224         else
225             if ( lhsType.isAssignableFrom(rhsType) )
226                 return true;
227
228         return false;
229     }
230
231     /**
232         Determine if the type is assignable via Java boxing/unboxing rules.
233     */

234     static boolean isJavaBoxTypesAssignable(
235         Class JavaDoc lhsType, Class JavaDoc rhsType )
236     {
237         // Assignment to loose type... defer to bsh extensions
238
if ( lhsType == null )
239             return false;
240
241         // prim can be boxed and assigned to Object
242
if ( lhsType == Object JavaDoc.class )
243             return true;
244
245         // prim numeric type can be boxed and assigned to number
246
if ( lhsType == Number JavaDoc.class
247             && rhsType != Character.TYPE
248             && rhsType != Boolean.TYPE
249         )
250             return true;
251
252         // General case prim type to wrapper or vice versa.
253
// I don't know if this is faster than a flat list of 'if's like above.
254
// wrapperMap maps both prim to wrapper and wrapper to prim types,
255
// so this test is symmetric
256
if ( Primitive.wrapperMap.get( lhsType ) == rhsType )
257             return true;
258
259         return false;
260     }
261
262     /**
263      Test if a type can be converted to another type via BeanShell
264      extended syntax rules (a superset of Java conversion rules).
265      */

266     static boolean isBshAssignable( Class JavaDoc toType, Class JavaDoc fromType )
267     {
268         try {
269             return castObject(
270                 toType, fromType, null/*fromValue*/,
271                 ASSIGNMENT, true/*checkOnly*/
272             ) == VALID_CAST;
273         } catch ( UtilEvalError e ) {
274             // This should not happen with checkOnly true
275
throw new InterpreterError("err in cast check: "+e);
276         }
277     }
278
279     /**
280         Attempt to cast an object instance to a new type if possible via
281      BeanShell extended syntax rules. These rules are always a superset of
282      Java conversion rules. If you wish to impose context sensitive
283      conversion rules then you must test before calling this method.
284      <p/>
285
286         This method can handle fromValue Primitive types (representing
287         primitive casts) as well as fromValue object casts requiring interface
288         generation, etc.
289
290         @param toType the class type of the cast result, which may include
291         primitive types, e.g. Byte.TYPE
292
293         @param fromValue an Object or bsh.Primitive primitive value (including
294             Primitive.NULL or Primitive.VOID )
295
296         @see #isBshAssignable( Class, Class )
297     */

298     public static Object JavaDoc castObject(
299         Object JavaDoc fromValue, Class JavaDoc toType, int operation )
300         throws UtilEvalError
301     {
302         if ( fromValue == null )
303             throw new InterpreterError("null fromValue");
304
305         Class JavaDoc fromType =
306             fromValue instanceof Primitive ?
307                 ((Primitive)fromValue).getType()
308                 : fromValue.getClass();
309
310         return castObject(
311             toType, fromType, fromValue, operation, false/*checkonly*/ );
312     }
313
314     /**
315      Perform a type conversion or test if a type conversion is possible with
316      respect to BeanShell extended rules. These rules are always a superset of
317      the Java language rules, so this method can also perform (but not test)
318      any Java language assignment or cast conversion.
319      <p/>
320
321      This method can perform the functionality of testing if an assignment
322      or cast is ultimately possible (with respect to BeanShell) as well as the
323      functionality of performing the necessary conversion of a value based
324      on the specified target type. This combined functionality is done for
325      expediency and could be separated later.
326      <p/>
327
328      Other methods such as isJavaAssignable() should be used to determine the
329      suitability of an assignment in a fine grained or restrictive way based
330      on context before calling this method
331      <p/>
332
333      A CAST is stronger than an ASSIGNMENT operation in that it will attempt to
334      perform primtive operations that cast to a smaller type. e.g. (byte)myLong;
335      These are used in explicit primitive casts, primitive delclarations and
336      array declarations. I don't believe there are any object conversions which are
337      different between ASSIGNMENT and CAST (e.g. scripted object to interface proxy
338      in bsh is done on assignment as well as cast).
339      <p/>
340
341      This method does not obey strictJava(), you must test first before
342      using this method if you care. (See #isJavaAssignable()).
343      <p/>
344
345         @param toType the class type of the cast result, which may include
346             primitive types, e.g. Byte.TYPE. toType may be null to indicate a
347             loose type assignment (which matches any fromType).
348
349         @param fromType is the class type of the value to be cast including
350             java primitive TYPE classes for primitives.
351             If fromValue is (or would be) Primitive.NULL then fromType should be null.
352
353         @param fromValue an Object or bsh.Primitive primitive value (including
354             Primitive.NULL or Primitive.VOID )
355
356         @param checkOnly If checkOnly is true then fromValue must be null.
357             FromType is checked for the cast to toType...
358             If checkOnly is false then fromValue must be non-null
359             (Primitive.NULL is ok) and the actual cast is performed.
360
361         @throws UtilEvalError on invalid assignment (when operation is
362             assignment ).
363
364         @throws UtilTargetError wrapping ClassCastException on cast error
365             (when operation is cast)
366
367         @param operation is Types.CAST or Types.ASSIGNMENT
368
369         @see bsh.Primitive.getType()
370     */

371     /*
372         Notes: This method is currently responsible for auto-boxing/unboxing
373         conversions... Where does that need to go?
374     */

375     private static Object JavaDoc castObject(
376         Class JavaDoc toType, Class JavaDoc fromType, Object JavaDoc fromValue,
377         int operation, boolean checkOnly )
378         throws UtilEvalError
379     {
380         /*
381             Lots of preconditions checked here...
382             Once things are running smoothly we might comment these out
383             (That's what assertions are for).
384         */

385         if ( checkOnly && fromValue != null )
386             throw new InterpreterError("bad cast params 1");
387         if ( !checkOnly && fromValue == null )
388             throw new InterpreterError("bad cast params 2");
389         if ( fromType == Primitive.class )
390             throw new InterpreterError("bad from Type, need to unwrap");
391         if ( fromValue == Primitive.NULL && fromType != null )
392             throw new InterpreterError("inconsistent args 1");
393         if ( fromValue == Primitive.VOID && fromType != Void.TYPE )
394             throw new InterpreterError("inconsistent args 2");
395         if ( toType == Void.TYPE )
396             throw new InterpreterError("loose toType should be null");
397         
398         // assignment to loose type, void type, or exactly same type
399
if ( toType == null || toType == fromType )
400             return checkOnly ? VALID_CAST :
401                 fromValue;
402
403         // Casting to primitive type
404
if ( toType.isPrimitive() )
405         {
406             if ( fromType == Void.TYPE || fromType == null
407                 || fromType.isPrimitive() )
408             {
409                 // Both primitives, do primitive cast
410
return Primitive.castPrimitive(
411                     toType, fromType, (Primitive)fromValue,
412                     checkOnly, operation );
413             } else
414             {
415                 if ( Primitive.isWrapperType( fromType ) )
416                 {
417                     // wrapper to primitive
418
// Convert value to Primitive and check/cast it.
419

420                     //Object r = checkOnly ? VALID_CAST :
421
Class JavaDoc unboxedFromType = Primitive.unboxType( fromType );
422                     Primitive primFromValue;
423                     if ( checkOnly )
424                         primFromValue = null; // must be null in checkOnly
425
else
426                         primFromValue = (Primitive)Primitive.wrap(
427                             fromValue, unboxedFromType );
428
429                     return Primitive.castPrimitive(
430                         toType, unboxedFromType, primFromValue,
431                         checkOnly, operation );
432                 } else
433                 {
434                     // Cannot cast from arbitrary object to primitive
435
if ( checkOnly )
436                         return INVALID_CAST;
437                     else
438                         throw castError( toType, fromType, operation );
439                 }
440             }
441         }
442
443         // Else, casting to reference type
444

445         // Casting from primitive or void (to reference type)
446
if ( fromType == Void.TYPE || fromType == null
447             || fromType.isPrimitive() )
448         {
449             // cast from primitive to wrapper type
450
if ( Primitive.isWrapperType( toType )
451                 && fromType != Void.TYPE && fromType != null )
452             {
453                 // primitive to wrapper type
454
return checkOnly ? VALID_CAST :
455                     Primitive.castWrapper(
456                         Primitive.unboxType(toType),
457                         ((Primitive)fromValue).getValue() );
458             }
459
460             // Primitive (not null or void) to Object.class type
461
if ( toType == Object JavaDoc.class
462                 && fromType != Void.TYPE && fromType != null )
463             {
464                 // box it
465
return checkOnly ? VALID_CAST :
466                     ((Primitive)fromValue).getValue();
467             }
468
469             // Primitive to arbitrary object type.
470
// Allow Primitive.castToType() to handle it as well as cases of
471
// Primitive.NULL and Primitive.VOID
472
return Primitive.castPrimitive(
473                 toType, fromType, (Primitive)fromValue, checkOnly, operation );
474         }
475
476         // If type already assignable no cast necessary
477
// We do this last to allow various errors above to be caught.
478
// e.g cast Primitive.Void to Object would pass this
479
if ( toType.isAssignableFrom( fromType ) )
480             return checkOnly ? VALID_CAST :
481                 fromValue;
482
483         // Can we use the proxy mechanism to cast a bsh.This to
484
// the correct interface?
485
if ( toType.isInterface()
486             && bsh.This.class.isAssignableFrom( fromType )
487             && Capabilities.canGenerateInterfaces()
488         )
489             return checkOnly ? VALID_CAST :
490                 ((bsh.This)fromValue).getInterface( toType );
491
492         // Both numeric wrapper types?
493
// Try numeric style promotion wrapper cast
494
if ( Primitive.isWrapperType( toType )
495             && Primitive.isWrapperType( fromType )
496         )
497             return checkOnly ? VALID_CAST :
498                 Primitive.castWrapper( toType, fromValue );
499         
500         if ( checkOnly )
501             return INVALID_CAST;
502         else
503             throw castError( toType, fromType , operation );
504     }
505
506     /**
507         Return a UtilEvalError or UtilTargetError wrapping a ClassCastException
508         describing an illegal assignment or illegal cast, respectively.
509     */

510     static UtilEvalError castError(
511         Class JavaDoc lhsType, Class JavaDoc rhsType, int operation )
512     {
513         return castError(
514             Reflect.normalizeClassName(lhsType),
515             Reflect.normalizeClassName(rhsType), operation );
516     }
517
518     static UtilEvalError castError(
519         String JavaDoc lhs, String JavaDoc rhs, int operation )
520     {
521         if ( operation == ASSIGNMENT )
522             return new UtilEvalError (
523                 "Can't assign " + rhs + " to "+ lhs );
524
525         Exception JavaDoc cce = new ClassCastException JavaDoc(
526             "Cannot cast " + rhs + " to " + lhs );
527         return new UtilTargetError( cce );
528     }
529
530 }
531
Popular Tags