001: /*****************************************************************************
002: * *
003: * This file is part of the BeanShell Java Scripting distribution. *
004: * Documentation and updates may be found at http://www.beanshell.org/ *
005: * *
006: * Sun Public License Notice: *
007: * *
008: * The contents of this file are subject to the Sun Public License Version *
009: * 1.0 (the "License"); you may not use this file except in compliance with *
010: * the License. A copy of the License is available at http://www.sun.com *
011: * *
012: * The Original Code is BeanShell. The Initial Developer of the Original *
013: * Code is Pat Niemeyer. Portions created by Pat Niemeyer are Copyright *
014: * (C) 2000. All Rights Reserved. *
015: * *
016: * GNU Public License Notice: *
017: * *
018: * Alternatively, the contents of this file may be used under the terms of *
019: * the GNU Lesser General Public License (the "LGPL"), in which case the *
020: * provisions of LGPL are applicable instead of those above. If you wish to *
021: * allow use of your version of this file only under the terms of the LGPL *
022: * and not to allow others to use your version of this file under the SPL, *
023: * indicate your decision by deleting the provisions above and replace *
024: * them with the notice and other provisions required by the LGPL. If you *
025: * do not delete the provisions above, a recipient may use your version of *
026: * this file under either the SPL or the LGPL. *
027: * *
028: * Patrick Niemeyer (pat@pat.net) *
029: * Author of Learning Java, O'Reilly & Associates *
030: * http://www.pat.net/~pat/ *
031: * *
032: *****************************************************************************/package bsh;
033:
034: /**
035: Static routines supporing type comparison and conversion in BeanShell.
036:
037: The following are notes on type comparison and conversion in BeanShell.
038:
039:
040: */
041: class Types {
042: /*
043: Type conversion identifiers. An ASSIGNMENT allows conversions that would
044: normally happen on assignment. A CAST performs numeric conversions to smaller
045: types (as in an explicit Java cast) and things allowed only in variable and array
046: declarations (e.g. byte b = 42;)
047: */
048: static final int CAST = 0, ASSIGNMENT = 1;
049:
050: static final int JAVA_BASE_ASSIGNABLE = 1,
051: JAVA_BOX_TYPES_ASSIGABLE = 2, JAVA_VARARGS_ASSIGNABLE = 3,
052: BSH_ASSIGNABLE = 4;
053:
054: static final int FIRST_ROUND_ASSIGNABLE = JAVA_BASE_ASSIGNABLE,
055: LAST_ROUND_ASSIGNABLE = BSH_ASSIGNABLE;
056:
057: /**
058: Special value that indicates by identity that the result of a cast
059: operation was a valid cast. This is used by castObject() and
060: castPrimitive() in the checkOnly mode of operation. This value is a
061: Primitive type so that it can be returned by castPrimitive.
062: */
063: static Primitive VALID_CAST = new Primitive(1);
064: static Primitive INVALID_CAST = new Primitive(-1);
065:
066: /**
067: Get the Java types of the arguments.
068: */
069: public static Class[] getTypes(Object[] args) {
070: if (args == null)
071: return new Class[0];
072:
073: Class[] types = new Class[args.length];
074:
075: for (int i = 0; i < args.length; i++) {
076: if (args[i] == null)
077: types[i] = null;
078: else if (args[i] instanceof Primitive)
079: types[i] = ((Primitive) args[i]).getType();
080: else
081: types[i] = args[i].getClass();
082: }
083:
084: return types;
085: }
086:
087: /**
088: Is the 'from' signature (argument types) assignable to the 'to'
089: signature (candidate method types)
090: This method handles the special case of null values in 'to' types
091: indicating a loose type and matching anything.
092: */
093: /* Should check for strict java here and limit to isJavaAssignable() */
094: static boolean isSignatureAssignable(Class[] from, Class[] to,
095: int round) {
096: if (round != JAVA_VARARGS_ASSIGNABLE
097: && from.length != to.length)
098: return false;
099:
100: switch (round) {
101: case JAVA_BASE_ASSIGNABLE:
102: for (int i = 0; i < from.length; i++)
103: if (!isJavaBaseAssignable(to[i], from[i]))
104: return false;
105: return true;
106: case JAVA_BOX_TYPES_ASSIGABLE:
107: for (int i = 0; i < from.length; i++)
108: if (!isJavaBoxTypesAssignable(to[i], from[i]))
109: return false;
110: return true;
111: case JAVA_VARARGS_ASSIGNABLE:
112: return isSignatureVarargsAssignable(from, to);
113: case BSH_ASSIGNABLE:
114: for (int i = 0; i < from.length; i++)
115: if (!isBshAssignable(to[i], from[i]))
116: return false;
117: return true;
118: default:
119: throw new InterpreterError("bad case");
120: }
121: }
122:
123: /**
124: * Are the two signatures exactly equal? This is checked for a special
125: * case in overload resolution.
126: */
127: static boolean areSignaturesEqual(Class[] from, Class[] to) {
128: if (from.length != to.length)
129: return false;
130:
131: for (int i = 0; i < from.length; i++)
132: if (from[i] != to[i])
133: return false;
134:
135: return true;
136: }
137:
138: private static boolean isSignatureVarargsAssignable(Class[] from,
139: Class[] to) {
140: return false;
141: }
142:
143: /**
144: Test if a conversion of the rhsType type to the lhsType type is legal via
145: standard Java assignment conversion rules (i.e. without a cast).
146: The rules include Java 5 autoboxing/unboxing.
147: <p/>
148:
149: For Java primitive TYPE classes this method takes primitive promotion
150: into account. The ordinary Class.isAssignableFrom() does not take
151: primitive promotion conversions into account. Note that Java allows
152: additional assignments without a cast in combination with variable
153: declarations and array allocations. Those are handled elsewhere
154: (maybe should be here with a flag?)
155: <p/>
156: This class accepts a null rhsType type indicating that the rhsType was the
157: value Primitive.NULL and allows it to be assigned to any reference lhsType
158: type (non primitive).
159: <p/>
160:
161: Note that the getAssignableForm() method is the primary bsh method for
162: checking assignability. It adds additional bsh conversions, etc.
163:
164: @see #isBshAssignable( Class, Class )
165: @param lhsType assigning from rhsType to lhsType
166: @param rhsType assigning from rhsType to lhsType
167: */
168: static boolean isJavaAssignable(Class lhsType, Class rhsType) {
169: return isJavaBaseAssignable(lhsType, rhsType)
170: || isJavaBoxTypesAssignable(lhsType, rhsType);
171: }
172:
173: /**
174: Is the assignment legal via original Java (up to version 1.4)
175: assignment rules, not including auto-boxing/unboxing.
176: @param rhsType may be null to indicate primitive null value
177: */
178: static boolean isJavaBaseAssignable(Class lhsType, Class rhsType) {
179: /*
180: Assignment to loose type, defer to bsh extensions
181: Note: we could shortcut this here:
182: if ( lhsType == null ) return true;
183: rather than forcing another round. It's not strictly a Java issue,
184: so does it belong here?
185: */
186: if (lhsType == null)
187: return false;
188:
189: // null rhs type corresponds to type of Primitive.NULL
190: // assignable to any object type
191: if (rhsType == null)
192: return !lhsType.isPrimitive();
193:
194: if (lhsType.isPrimitive() && rhsType.isPrimitive()) {
195: if (lhsType == rhsType)
196: return true;
197:
198: // handle primitive widening conversions - JLS 5.1.2
199: if ((rhsType == Byte.TYPE)
200: && (lhsType == Short.TYPE
201: || lhsType == Integer.TYPE
202: || lhsType == Long.TYPE
203: || lhsType == Float.TYPE || lhsType == Double.TYPE))
204: return true;
205:
206: if ((rhsType == Short.TYPE)
207: && (lhsType == Integer.TYPE || lhsType == Long.TYPE
208: || lhsType == Float.TYPE || lhsType == Double.TYPE))
209: return true;
210:
211: if ((rhsType == Character.TYPE)
212: && (lhsType == Integer.TYPE || lhsType == Long.TYPE
213: || lhsType == Float.TYPE || lhsType == Double.TYPE))
214: return true;
215:
216: if ((rhsType == Integer.TYPE)
217: && (lhsType == Long.TYPE || lhsType == Float.TYPE || lhsType == Double.TYPE))
218: return true;
219:
220: if ((rhsType == Long.TYPE)
221: && (lhsType == Float.TYPE || lhsType == Double.TYPE))
222: return true;
223:
224: if ((rhsType == Float.TYPE) && (lhsType == Double.TYPE))
225: return true;
226: } else if (lhsType.isAssignableFrom(rhsType))
227: return true;
228:
229: return false;
230: }
231:
232: /**
233: Determine if the type is assignable via Java boxing/unboxing rules.
234: */
235: static boolean isJavaBoxTypesAssignable(Class lhsType, Class rhsType) {
236: // Assignment to loose type... defer to bsh extensions
237: if (lhsType == null)
238: return false;
239:
240: // prim can be boxed and assigned to Object
241: if (lhsType == Object.class)
242: return true;
243:
244: // prim numeric type can be boxed and assigned to number
245: if (lhsType == Number.class && rhsType != Character.TYPE
246: && rhsType != Boolean.TYPE)
247: return true;
248:
249: // General case prim type to wrapper or vice versa.
250: // I don't know if this is faster than a flat list of 'if's like above.
251: // wrapperMap maps both prim to wrapper and wrapper to prim types,
252: // so this test is symmetric
253: if (Primitive.wrapperMap.get(lhsType) == rhsType)
254: return true;
255:
256: return false;
257: }
258:
259: /**
260: Test if a type can be converted to another type via BeanShell
261: extended syntax rules (a superset of Java conversion rules).
262: */
263: static boolean isBshAssignable(Class toType, Class fromType) {
264: try {
265: return castObject(toType, fromType, null/*fromValue*/,
266: ASSIGNMENT, true/*checkOnly*/
267: ) == VALID_CAST;
268: } catch (UtilEvalError e) {
269: // This should not happen with checkOnly true
270: throw new InterpreterError("err in cast check: " + e);
271: }
272: }
273:
274: /**
275: Attempt to cast an object instance to a new type if possible via
276: BeanShell extended syntax rules. These rules are always a superset of
277: Java conversion rules. If you wish to impose context sensitive
278: conversion rules then you must test before calling this method.
279: <p/>
280:
281: This method can handle fromValue Primitive types (representing
282: primitive casts) as well as fromValue object casts requiring interface
283: generation, etc.
284:
285: @param toType the class type of the cast result, which may include
286: primitive types, e.g. Byte.TYPE
287:
288: @param fromValue an Object or bsh.Primitive primitive value (including
289: Primitive.NULL or Primitive.VOID )
290:
291: @see #isBshAssignable( Class, Class )
292: */
293: public static Object castObject(Object fromValue, Class toType,
294: int operation) throws UtilEvalError {
295: if (fromValue == null)
296: throw new InterpreterError("null fromValue");
297:
298: Class fromType = fromValue instanceof Primitive ? ((Primitive) fromValue)
299: .getType()
300: : fromValue.getClass();
301:
302: return castObject(toType, fromType, fromValue, operation, false/*checkonly*/);
303: }
304:
305: /**
306: Perform a type conversion or test if a type conversion is possible with
307: respect to BeanShell extended rules. These rules are always a superset of
308: the Java language rules, so this method can also perform (but not test)
309: any Java language assignment or cast conversion.
310: <p/>
311:
312: This method can perform the functionality of testing if an assignment
313: or cast is ultimately possible (with respect to BeanShell) as well as the
314: functionality of performing the necessary conversion of a value based
315: on the specified target type. This combined functionality is done for
316: expediency and could be separated later.
317: <p/>
318:
319: Other methods such as isJavaAssignable() should be used to determine the
320: suitability of an assignment in a fine grained or restrictive way based
321: on context before calling this method
322: <p/>
323:
324: A CAST is stronger than an ASSIGNMENT operation in that it will attempt to
325: perform primtive operations that cast to a smaller type. e.g. (byte)myLong;
326: These are used in explicit primitive casts, primitive delclarations and
327: array declarations. I don't believe there are any object conversions which are
328: different between ASSIGNMENT and CAST (e.g. scripted object to interface proxy
329: in bsh is done on assignment as well as cast).
330: <p/>
331:
332: This method does not obey strictJava(), you must test first before
333: using this method if you care. (See #isJavaAssignable()).
334: <p/>
335:
336: @param toType the class type of the cast result, which may include
337: primitive types, e.g. Byte.TYPE. toType may be null to indicate a
338: loose type assignment (which matches any fromType).
339:
340: @param fromType is the class type of the value to be cast including
341: java primitive TYPE classes for primitives.
342: If fromValue is (or would be) Primitive.NULL then fromType should be null.
343:
344: @param fromValue an Object or bsh.Primitive primitive value (including
345: Primitive.NULL or Primitive.VOID )
346:
347: @param checkOnly If checkOnly is true then fromValue must be null.
348: FromType is checked for the cast to toType...
349: If checkOnly is false then fromValue must be non-null
350: (Primitive.NULL is ok) and the actual cast is performed.
351:
352: @throws UtilEvalError on invalid assignment (when operation is
353: assignment ).
354:
355: @throws UtilTargetError wrapping ClassCastException on cast error
356: (when operation is cast)
357:
358: @param operation is Types.CAST or Types.ASSIGNMENT
359:
360: @see bsh.Primitive.getType()
361: */
362: /*
363: Notes: This method is currently responsible for auto-boxing/unboxing
364: conversions... Where does that need to go?
365: */
366: private static Object castObject(Class toType, Class fromType,
367: Object fromValue, int operation, boolean checkOnly)
368: throws UtilEvalError {
369: /*
370: Lots of preconditions checked here...
371: Once things are running smoothly we might comment these out
372: (That's what assertions are for).
373: */
374: if (checkOnly && fromValue != null)
375: throw new InterpreterError("bad cast params 1");
376: if (!checkOnly && fromValue == null)
377: throw new InterpreterError("bad cast params 2");
378: if (fromType == Primitive.class)
379: throw new InterpreterError("bad from Type, need to unwrap");
380: if (fromValue == Primitive.NULL && fromType != null)
381: throw new InterpreterError("inconsistent args 1");
382: if (fromValue == Primitive.VOID && fromType != Void.TYPE)
383: throw new InterpreterError("inconsistent args 2");
384: if (toType == Void.TYPE)
385: throw new InterpreterError("loose toType should be null");
386:
387: // assignment to loose type, void type, or exactly same type
388: if (toType == null || toType == fromType)
389: return checkOnly ? VALID_CAST : fromValue;
390:
391: // Casting to primitive type
392: if (toType.isPrimitive()) {
393: if (fromType == Void.TYPE || fromType == null
394: || fromType.isPrimitive()) {
395: // Both primitives, do primitive cast
396: return Primitive.castPrimitive(toType, fromType,
397: (Primitive) fromValue, checkOnly, operation);
398: } else {
399: if (Primitive.isWrapperType(fromType)) {
400: // wrapper to primitive
401: // Convert value to Primitive and check/cast it.
402:
403: //Object r = checkOnly ? VALID_CAST :
404: Class unboxedFromType = Primitive
405: .unboxType(fromType);
406: Primitive primFromValue;
407: if (checkOnly)
408: primFromValue = null; // must be null in checkOnly
409: else
410: primFromValue = (Primitive) Primitive.wrap(
411: fromValue, unboxedFromType);
412:
413: return Primitive.castPrimitive(toType,
414: unboxedFromType, primFromValue, checkOnly,
415: operation);
416: } else {
417: // Cannot cast from arbitrary object to primitive
418: if (checkOnly)
419: return INVALID_CAST;
420: else
421: throw castError(toType, fromType, operation);
422: }
423: }
424: }
425:
426: // Else, casting to reference type
427:
428: // Casting from primitive or void (to reference type)
429: if (fromType == Void.TYPE || fromType == null
430: || fromType.isPrimitive()) {
431: // cast from primitive to wrapper type
432: if (Primitive.isWrapperType(toType)
433: && fromType != Void.TYPE && fromType != null) {
434: // primitive to wrapper type
435: return checkOnly ? VALID_CAST : Primitive.castWrapper(
436: Primitive.unboxType(toType),
437: ((Primitive) fromValue).getValue());
438: }
439:
440: // Primitive (not null or void) to Object.class type
441: if (toType == Object.class && fromType != Void.TYPE
442: && fromType != null) {
443: // box it
444: return checkOnly ? VALID_CAST : ((Primitive) fromValue)
445: .getValue();
446: }
447:
448: // Primitive to arbitrary object type.
449: // Allow Primitive.castToType() to handle it as well as cases of
450: // Primitive.NULL and Primitive.VOID
451: return Primitive.castPrimitive(toType, fromType,
452: (Primitive) fromValue, checkOnly, operation);
453: }
454:
455: // If type already assignable no cast necessary
456: // We do this last to allow various errors above to be caught.
457: // e.g cast Primitive.Void to Object would pass this
458: if (toType.isAssignableFrom(fromType))
459: return checkOnly ? VALID_CAST : fromValue;
460:
461: // Can we use the proxy mechanism to cast a bsh.This to
462: // the correct interface?
463: if (toType.isInterface()
464: && bsh.This.class.isAssignableFrom(fromType)
465: && Capabilities.canGenerateInterfaces())
466: return checkOnly ? VALID_CAST : ((bsh.This) fromValue)
467: .getInterface(toType);
468:
469: // Both numeric wrapper types?
470: // Try numeric style promotion wrapper cast
471: if (Primitive.isWrapperType(toType)
472: && Primitive.isWrapperType(fromType))
473: return checkOnly ? VALID_CAST : Primitive.castWrapper(
474: toType, fromValue);
475:
476: if (checkOnly)
477: return INVALID_CAST;
478: else
479: throw castError(toType, fromType, operation);
480: }
481:
482: /**
483: Return a UtilEvalError or UtilTargetError wrapping a ClassCastException
484: describing an illegal assignment or illegal cast, respectively.
485: */
486: static UtilEvalError castError(Class lhsType, Class rhsType,
487: int operation) {
488: return castError(Reflect.normalizeClassName(lhsType), Reflect
489: .normalizeClassName(rhsType), operation);
490: }
491:
492: static UtilEvalError castError(String lhs, String rhs, int operation) {
493: if (operation == ASSIGNMENT)
494: return new UtilEvalError("Can't assign " + rhs + " to "
495: + lhs);
496:
497: Exception cce = new ClassCastException("Cannot cast " + rhs
498: + " to " + lhs);
499: return new UtilTargetError(cce);
500: }
501:
502: }
|