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 org.gjt.sp.jedit.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: private static boolean isSignatureVarargsAssignable(Class[] from,
124: Class[] to) {
125: return false;
126: }
127:
128: /**
129: Test if a conversion of the rhsType type to the lhsType type is legal via
130: standard Java assignment conversion rules (i.e. without a cast).
131: The rules include Java 5 autoboxing/unboxing.
132: <p/>
133:
134: For Java primitive TYPE classes this method takes primitive promotion
135: into account. The ordinary Class.isAssignableFrom() does not take
136: primitive promotion conversions into account. Note that Java allows
137: additional assignments without a cast in combination with variable
138: declarations and array allocations. Those are handled elsewhere
139: (maybe should be here with a flag?)
140: <p/>
141: This class accepts a null rhsType type indicating that the rhsType was the
142: value Primitive.NULL and allows it to be assigned to any reference lhsType
143: type (non primitive).
144: <p/>
145:
146: Note that the getAssignableForm() method is the primary bsh method for
147: checking assignability. It adds additional bsh conversions, etc.
148:
149: @see #isBshAssignable( Class, Class )
150: @param lhsType assigning from rhsType to lhsType
151: @param rhsType assigning from rhsType to lhsType
152: */
153: static boolean isJavaAssignable(Class lhsType, Class rhsType) {
154: return isJavaBaseAssignable(lhsType, rhsType)
155: || isJavaBoxTypesAssignable(lhsType, rhsType);
156: }
157:
158: /**
159: Is the assignment legal via original Java (up to version 1.4)
160: assignment rules, not including auto-boxing/unboxing.
161: @param rhsType may be null to indicate primitive null value
162: */
163: static boolean isJavaBaseAssignable(Class lhsType, Class rhsType) {
164: /*
165: Assignment to loose type, defer to bsh extensions
166: Note: we could shortcut this here:
167: if ( lhsType == null ) return true;
168: rather than forcing another round. It's not strictly a Java issue,
169: so does it belong here?
170: */
171: if (lhsType == null)
172: return false;
173:
174: // null rhs type corresponds to type of Primitive.NULL
175: // assignable to any object type
176: if (rhsType == null)
177: return !lhsType.isPrimitive();
178:
179: if (lhsType.isPrimitive() && rhsType.isPrimitive()) {
180: if (lhsType == rhsType)
181: return true;
182:
183: // handle primitive widening conversions - JLS 5.1.2
184: if ((rhsType == Byte.TYPE)
185: && (lhsType == Short.TYPE
186: || lhsType == Integer.TYPE
187: || lhsType == Long.TYPE
188: || lhsType == Float.TYPE || lhsType == Double.TYPE))
189: return true;
190:
191: if ((rhsType == Short.TYPE)
192: && (lhsType == Integer.TYPE || lhsType == Long.TYPE
193: || lhsType == Float.TYPE || lhsType == Double.TYPE))
194: return true;
195:
196: if ((rhsType == Character.TYPE)
197: && (lhsType == Integer.TYPE || lhsType == Long.TYPE
198: || lhsType == Float.TYPE || lhsType == Double.TYPE))
199: return true;
200:
201: if ((rhsType == Integer.TYPE)
202: && (lhsType == Long.TYPE || lhsType == Float.TYPE || lhsType == Double.TYPE))
203: return true;
204:
205: if ((rhsType == Long.TYPE)
206: && (lhsType == Float.TYPE || lhsType == Double.TYPE))
207: return true;
208:
209: if ((rhsType == Float.TYPE) && (lhsType == Double.TYPE))
210: return true;
211: } else if (lhsType.isAssignableFrom(rhsType))
212: return true;
213:
214: return false;
215: }
216:
217: /**
218: Determine if the type is assignable via Java boxing/unboxing rules.
219: */
220: static boolean isJavaBoxTypesAssignable(Class lhsType, Class rhsType) {
221: // Assignment to loose type... defer to bsh extensions
222: if (lhsType == null)
223: return false;
224:
225: // prim can be boxed and assigned to Object
226: if (lhsType == Object.class)
227: return true;
228:
229: // prim numeric type can be boxed and assigned to number
230: if (lhsType == Number.class && rhsType != Character.TYPE
231: && rhsType != Boolean.TYPE)
232: return true;
233:
234: // General case prim type to wrapper or vice versa.
235: // I don't know if this is faster than a flat list of 'if's like above.
236: // wrapperMap maps both prim to wrapper and wrapper to prim types,
237: // so this test is symmetric
238: if (Primitive.wrapperMap.get(lhsType) == rhsType)
239: return true;
240:
241: return false;
242: }
243:
244: /**
245: Test if a type can be converted to another type via BeanShell
246: extended syntax rules (a superset of Java conversion rules).
247: */
248: static boolean isBshAssignable(Class toType, Class fromType) {
249: try {
250: return castObject(toType, fromType, null/*fromValue*/,
251: ASSIGNMENT, true/*checkOnly*/
252: ) == VALID_CAST;
253: } catch (UtilEvalError e) {
254: // This should not happen with checkOnly true
255: throw new InterpreterError("err in cast check: " + e);
256: }
257: }
258:
259: /**
260: Attempt to cast an object instance to a new type if possible via
261: BeanShell extended syntax rules. These rules are always a superset of
262: Java conversion rules. If you wish to impose context sensitive
263: conversion rules then you must test before calling this method.
264: <p/>
265:
266: This method can handle fromValue Primitive types (representing
267: primitive casts) as well as fromValue object casts requiring interface
268: generation, etc.
269:
270: @param toType the class type of the cast result, which may include
271: primitive types, e.g. Byte.TYPE
272:
273: @param fromValue an Object or bsh.Primitive primitive value (including
274: Primitive.NULL or Primitive.VOID )
275:
276: @see #isBshAssignable( Class, Class )
277: */
278: public static Object castObject(Object fromValue, Class toType,
279: int operation) throws UtilEvalError {
280: if (fromValue == null)
281: throw new InterpreterError("null fromValue");
282:
283: Class fromType = fromValue instanceof Primitive ? ((Primitive) fromValue)
284: .getType()
285: : fromValue.getClass();
286:
287: return castObject(toType, fromType, fromValue, operation, false/*checkonly*/);
288: }
289:
290: /**
291: Perform a type conversion or test if a type conversion is possible with
292: respect to BeanShell extended rules. These rules are always a superset of
293: the Java language rules, so this method can also perform (but not test)
294: any Java language assignment or cast conversion.
295: <p/>
296:
297: This method can perform the functionality of testing if an assignment
298: or cast is ultimately possible (with respect to BeanShell) as well as the
299: functionality of performing the necessary conversion of a value based
300: on the specified target type. This combined functionality is done for
301: expediency and could be separated later.
302: <p/>
303:
304: Other methods such as isJavaAssignable() should be used to determine the
305: suitability of an assignment in a fine grained or restrictive way based
306: on context before calling this method
307: <p/>
308:
309: A CAST is stronger than an ASSIGNMENT operation in that it will attempt to
310: perform primtive operations that cast to a smaller type. e.g. (byte)myLong;
311: These are used in explicit primitive casts, primitive delclarations and
312: array declarations. I don't believe there are any object conversions which are
313: different between ASSIGNMENT and CAST (e.g. scripted object to interface proxy
314: in bsh is done on assignment as well as cast).
315: <p/>
316:
317: This method does not obey strictJava(), you must test first before
318: using this method if you care. (See #isJavaAssignable()).
319: <p/>
320:
321: @param toType the class type of the cast result, which may include
322: primitive types, e.g. Byte.TYPE. toType may be null to indicate a
323: loose type assignment (which matches any fromType).
324:
325: @param fromType is the class type of the value to be cast including
326: java primitive TYPE classes for primitives.
327: If fromValue is (or would be) Primitive.NULL then fromType should be null.
328:
329: @param fromValue an Object or bsh.Primitive primitive value (including
330: Primitive.NULL or Primitive.VOID )
331:
332: @param checkOnly If checkOnly is true then fromValue must be null.
333: FromType is checked for the cast to toType...
334: If checkOnly is false then fromValue must be non-null
335: (Primitive.NULL is ok) and the actual cast is performed.
336:
337: @throws UtilEvalError on invalid assignment (when operation is
338: assignment ).
339:
340: @throws UtilTargetError wrapping ClassCastException on cast error
341: (when operation is cast)
342:
343: @param operation is Types.CAST or Types.ASSIGNMENT
344:
345: @see org.gjt.sp.jedit.bsh.Primitive.getType()
346: */
347: /*
348: Notes: This method is currently responsible for auto-boxing/unboxing
349: conversions... Where does that need to go?
350: */
351: private static Object castObject(Class toType, Class fromType,
352: Object fromValue, int operation, boolean checkOnly)
353: throws UtilEvalError {
354: /*
355: Lots of preconditions checked here...
356: Once things are running smoothly we might comment these out
357: (That's what assertions are for).
358: */
359: if (checkOnly && fromValue != null)
360: throw new InterpreterError("bad cast params 1");
361: if (!checkOnly && fromValue == null)
362: throw new InterpreterError("bad cast params 2");
363: if (fromType == Primitive.class)
364: throw new InterpreterError("bad from Type, need to unwrap");
365: if (fromValue == Primitive.NULL && fromType != null)
366: throw new InterpreterError("inconsistent args 1");
367: if (fromValue == Primitive.VOID && fromType != Void.TYPE)
368: throw new InterpreterError("inconsistent args 2");
369: if (toType == Void.TYPE)
370: throw new InterpreterError("loose toType should be null");
371:
372: // assignment to loose type, void type, or exactly same type
373: if (toType == null || toType == fromType)
374: return checkOnly ? VALID_CAST : fromValue;
375:
376: // Casting to primitive type
377: if (toType.isPrimitive()) {
378: if (fromType == Void.TYPE || fromType == null
379: || fromType.isPrimitive()) {
380: // Both primitives, do primitive cast
381: return Primitive.castPrimitive(toType, fromType,
382: (Primitive) fromValue, checkOnly, operation);
383: } else {
384: if (Primitive.isWrapperType(fromType)) {
385: // wrapper to primitive
386: // Convert value to Primitive and check/cast it.
387:
388: //Object r = checkOnly ? VALID_CAST :
389: Class unboxedFromType = Primitive
390: .unboxType(fromType);
391: Primitive primFromValue;
392: if (checkOnly)
393: primFromValue = null; // must be null in checkOnly
394: else
395: primFromValue = (Primitive) Primitive.wrap(
396: fromValue, unboxedFromType);
397:
398: return Primitive.castPrimitive(toType,
399: unboxedFromType, primFromValue, checkOnly,
400: operation);
401: } else {
402: // Cannot cast from arbitrary object to primitive
403: if (checkOnly)
404: return INVALID_CAST;
405: else
406: throw castError(toType, fromType, operation);
407: }
408: }
409: }
410:
411: // Else, casting to reference type
412:
413: // Casting from primitive or void (to reference type)
414: if (fromType == Void.TYPE || fromType == null
415: || fromType.isPrimitive()) {
416: // cast from primitive to wrapper type
417: if (Primitive.isWrapperType(toType)
418: && fromType != Void.TYPE && fromType != null) {
419: // primitive to wrapper type
420: return checkOnly ? VALID_CAST : Primitive.castWrapper(
421: Primitive.unboxType(toType),
422: ((Primitive) fromValue).getValue());
423: }
424:
425: // Primitive (not null or void) to Object.class type
426: if (toType == Object.class && fromType != Void.TYPE
427: && fromType != null) {
428: // box it
429: return checkOnly ? VALID_CAST : ((Primitive) fromValue)
430: .getValue();
431: }
432:
433: // Primitive to arbitrary object type.
434: // Allow Primitive.castToType() to handle it as well as cases of
435: // Primitive.NULL and Primitive.VOID
436: return Primitive.castPrimitive(toType, fromType,
437: (Primitive) fromValue, checkOnly, operation);
438: }
439:
440: // If type already assignable no cast necessary
441: // We do this last to allow various errors above to be caught.
442: // e.g cast Primitive.Void to Object would pass this
443: if (toType.isAssignableFrom(fromType))
444: return checkOnly ? VALID_CAST : fromValue;
445:
446: // Can we use the proxy mechanism to cast a bsh.This to
447: // the correct interface?
448: if (toType.isInterface()
449: && org.gjt.sp.jedit.bsh.This.class
450: .isAssignableFrom(fromType)
451: && Capabilities.canGenerateInterfaces())
452: return checkOnly ? VALID_CAST
453: : ((org.gjt.sp.jedit.bsh.This) fromValue)
454: .getInterface(toType);
455:
456: // Both numeric wrapper types?
457: // Try numeric style promotion wrapper cast
458: if (Primitive.isWrapperType(toType)
459: && Primitive.isWrapperType(fromType))
460: return checkOnly ? VALID_CAST : Primitive.castWrapper(
461: toType, fromValue);
462:
463: if (checkOnly)
464: return INVALID_CAST;
465: else
466: throw castError(toType, fromType, operation);
467: }
468:
469: /**
470: Return a UtilEvalError or UtilTargetError wrapping a ClassCastException
471: describing an illegal assignment or illegal cast, respectively.
472: */
473: static UtilEvalError castError(Class lhsType, Class rhsType,
474: int operation) {
475: return castError(Reflect.normalizeClassName(lhsType), Reflect
476: .normalizeClassName(rhsType), operation);
477: }
478:
479: static UtilEvalError castError(String lhs, String rhs, int operation) {
480: if (operation == ASSIGNMENT)
481: return new UtilEvalError("Can't assign " + rhs + " to "
482: + lhs);
483:
484: Exception cce = new ClassCastException("Cannot cast " + rhs
485: + " to " + lhs);
486: return new UtilTargetError(cce);
487: }
488:
489: }
|