All Downloads are FREE. Search and download functionalities are using the official Maven repository.

bsh.Types Maven / Gradle / Ivy

The newest version!
/*
 * #%L
 * The AIBench Shell Plugin
 * %%
 * Copyright (C) 2006 - 2017 Daniel Glez-Peña and Florentino Fdez-Riverola
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * #L%
 */
/*****************************************************************************
 *                                                                           *
 *  This file is part of the BeanShell Java Scripting distribution.          *
 *  Documentation and updates may be found at http://www.beanshell.org/      *
 *                                                                           *
 *  Sun Public License Notice:                                               *
 *                                                                           *
 *  The contents of this file are subject to the Sun Public License Version  *
 *  1.0 (the "License"); you may not use this file except in compliance with *
 *  the License. A copy of the License is available at http://www.sun.com    * 
 *                                                                           *
 *  The Original Code is BeanShell. The Initial Developer of the Original    *
 *  Code is Pat Niemeyer. Portions created by Pat Niemeyer are Copyright     *
 *  (C) 2000.  All Rights Reserved.                                          *
 *                                                                           *
 *  GNU Public License Notice:                                               *
 *                                                                           *
 *  Alternatively, the contents of this file may be used under the terms of  *
 *  the GNU Lesser General Public License (the "LGPL"), in which case the    *
 *  provisions of LGPL are applicable instead of those above. If you wish to *
 *  allow use of your version of this file only under the  terms of the LGPL *
 *  and not to allow others to use your version of this file under the SPL,  *
 *  indicate your decision by deleting the provisions above and replace      *
 *  them with the notice and other provisions required by the LGPL.  If you  *
 *  do not delete the provisions above, a recipient may use your version of  *
 *  this file under either the SPL or the LGPL.                              *
 *                                                                           *
 *  Patrick Niemeyer ([email protected])                                           *
 *  Author of Learning Java, O'Reilly & Associates                           *
 *  http://www.pat.net/~pat/                                                 *
 *                                                                           *
 *****************************************************************************/

package bsh;

/**
 * Static routines supporing type comparison and conversion in BeanShell.
 * 
 * The following are notes on type comparison and conversion in BeanShell.
 * 
 * 
 */
class Types {
        /*
         * Type conversion identifiers. An ASSIGNMENT allows conversions that
         * would normally happen on assignment. A CAST performs numeric
         * conversions to smaller types (as in an explicit Java cast) and things
         * allowed only in variable and array declarations (e.g. byte b = 42;)
         */
        static final int CAST = 0, ASSIGNMENT = 1;

        static final int JAVA_BASE_ASSIGNABLE = 1,
                        JAVA_BOX_TYPES_ASSIGABLE = 2,
                        JAVA_VARARGS_ASSIGNABLE = 3, BSH_ASSIGNABLE = 4;

        static final int FIRST_ROUND_ASSIGNABLE = JAVA_BASE_ASSIGNABLE,
                        LAST_ROUND_ASSIGNABLE = BSH_ASSIGNABLE;

        /**
         * Special value that indicates by identity that the result of a cast
         * operation was a valid cast. This is used by castObject() and
         * castPrimitive() in the checkOnly mode of operation. This value is a
         * Primitive type so that it can be returned by castPrimitive.
         */
        static Primitive VALID_CAST = new Primitive(1);

        static Primitive INVALID_CAST = new Primitive(-1);

        /**
         * Get the Java types of the arguments.
         */
        public static Class[] getTypes(Object[] args) {
                if (args == null)
                        return new Class[0];

                Class[] types = new Class[args.length];

                for (int i = 0; i < args.length; i++) {
                        if (args[i] == null)
                                types[i] = null;
                        else if (args[i] instanceof Primitive)
                                types[i] = ((Primitive) args[i]).getType();
                        else
                                types[i] = args[i].getClass();
                }

                return types;
        }

        /**
         * Is the 'from' signature (argument types) assignable to the 'to'
         * signature (candidate method types) This method handles the special
         * case of null values in 'to' types indicating a loose type and
         * matching anything.
         */
        /* Should check for strict java here and limit to isJavaAssignable() */
        static boolean isSignatureAssignable(Class[] from, Class[] to, int round) {
                if (round != JAVA_VARARGS_ASSIGNABLE && from.length != to.length)
                        return false;

                switch (round) {
                case JAVA_BASE_ASSIGNABLE:
                        for (int i = 0; i < from.length; i++)
                                if (!isJavaBaseAssignable(to[i], from[i]))
                                        return false;
                        return true;
                case JAVA_BOX_TYPES_ASSIGABLE:
                        for (int i = 0; i < from.length; i++)
                                if (!isJavaBoxTypesAssignable(to[i], from[i]))
                                        return false;
                        return true;
                case JAVA_VARARGS_ASSIGNABLE:
                        return isSignatureVarargsAssignable(from, to);
                case BSH_ASSIGNABLE:
                        for (int i = 0; i < from.length; i++)
                                if (!isBshAssignable(to[i], from[i]))
                                        return false;
                        return true;
                default:
                        throw new InterpreterError("bad case");
                }
        }

        private static boolean isSignatureVarargsAssignable(Class[] from, Class[] to) {
                return false;
        }

        /**
         * Test if a conversion of the rhsType type to the lhsType type is legal
         * via standard Java assignment conversion rules (i.e. without a cast).
         * The rules include Java 5 autoboxing/unboxing. 

* * For Java primitive TYPE classes this method takes primitive promotion * into account. The ordinary Class.isAssignableFrom() does not take * primitive promotion conversions into account. Note that Java allows * additional assignments without a cast in combination with variable * declarations and array allocations. Those are handled elsewhere * (maybe should be here with a flag?)

This class accepts a null * rhsType type indicating that the rhsType was the value Primitive.NULL * and allows it to be assigned to any reference lhsType type (non * primitive).

* * Note that the getAssignableForm() method is the primary bsh method * for checking assignability. It adds additional bsh conversions, etc. * * @see #isBshAssignable( Class, Class ) * @param lhsType * assigning from rhsType to lhsType * @param rhsType * assigning from rhsType to lhsType */ static boolean isJavaAssignable(Class lhsType, Class rhsType) { return isJavaBaseAssignable(lhsType, rhsType) || isJavaBoxTypesAssignable(lhsType, rhsType); } /** * Is the assignment legal via original Java (up to version 1.4) * assignment rules, not including auto-boxing/unboxing. * * @param rhsType * may be null to indicate primitive null value */ static boolean isJavaBaseAssignable(Class lhsType, Class rhsType) { /* * Assignment to loose type, defer to bsh extensions Note: we * could shortcut this here: if ( lhsType == null ) return true; * rather than forcing another round. It's not strictly a Java * issue, so does it belong here? */ if (lhsType == null) return false; // null rhs type corresponds to type of Primitive.NULL // assignable to any object type if (rhsType == null) return !lhsType.isPrimitive(); if (lhsType.isPrimitive() && rhsType.isPrimitive()) { if (lhsType == rhsType) return true; // handle primitive widening conversions - JLS 5.1.2 if ((rhsType == Byte.TYPE) && (lhsType == Short.TYPE || lhsType == Integer.TYPE || lhsType == Long.TYPE || lhsType == Float.TYPE || lhsType == Double.TYPE)) return true; if ((rhsType == Short.TYPE) && (lhsType == Integer.TYPE || lhsType == Long.TYPE || lhsType == Float.TYPE || lhsType == Double.TYPE)) return true; if ((rhsType == Character.TYPE) && (lhsType == Integer.TYPE || lhsType == Long.TYPE || lhsType == Float.TYPE || lhsType == Double.TYPE)) return true; if ((rhsType == Integer.TYPE) && (lhsType == Long.TYPE || lhsType == Float.TYPE || lhsType == Double.TYPE)) return true; if ((rhsType == Long.TYPE) && (lhsType == Float.TYPE || lhsType == Double.TYPE)) return true; if ((rhsType == Float.TYPE) && (lhsType == Double.TYPE)) return true; } else if (lhsType.isAssignableFrom(rhsType)) return true; return false; } /** * Determine if the type is assignable via Java boxing/unboxing rules. */ static boolean isJavaBoxTypesAssignable(Class lhsType, Class rhsType) { // Assignment to loose type... defer to bsh extensions if (lhsType == null) return false; // prim can be boxed and assigned to Object if (lhsType == Object.class) return true; // prim numeric type can be boxed and assigned to number if (lhsType == Number.class && rhsType != Character.TYPE && rhsType != Boolean.TYPE) return true; // General case prim type to wrapper or vice versa. // I don't know if this is faster than a flat list of 'if's like // above. // wrapperMap maps both prim to wrapper and wrapper to prim // types, // so this test is symmetric if (Primitive.wrapperMap.get(lhsType) == rhsType) return true; return false; } /** * Test if a type can be converted to another type via BeanShell * extended syntax rules (a superset of Java conversion rules). */ static boolean isBshAssignable(Class toType, Class fromType) { try { return castObject(toType, fromType, null/* fromValue */, ASSIGNMENT, true/* checkOnly */ ) == VALID_CAST; } catch (UtilEvalError e) { // This should not happen with checkOnly true throw new InterpreterError("err in cast check: " + e); } } /** * Attempt to cast an object instance to a new type if possible via * BeanShell extended syntax rules. These rules are always a superset of * Java conversion rules. If you wish to impose context sensitive * conversion rules then you must test before calling this method.

* * This method can handle fromValue Primitive types (representing * primitive casts) as well as fromValue object casts requiring * interface generation, etc. * * @param toType * the class type of the cast result, which may include * primitive types, e.g. Byte.TYPE * * @param fromValue * an Object or bsh.Primitive primitive value (including * Primitive.NULL or Primitive.VOID ) * * @see #isBshAssignable( Class, Class ) */ public static Object castObject(Object fromValue, Class toType, int operation) throws UtilEvalError { if (fromValue == null) throw new InterpreterError("null fromValue"); Class fromType = fromValue instanceof Primitive ? ((Primitive) fromValue).getType() : fromValue.getClass(); return castObject(toType, fromType, fromValue, operation, false/* checkonly */); } /** * Perform a type conversion or test if a type conversion is possible * with respect to BeanShell extended rules. These rules are always a * superset of the Java language rules, so this method can also perform * (but not test) any Java language assignment or cast conversion.

* * This method can perform the functionality of testing if an assignment * or cast is ultimately possible (with respect to BeanShell) as well as * the functionality of performing the necessary conversion of a value * based on the specified target type. This combined functionality is * done for expediency and could be separated later.

* * Other methods such as isJavaAssignable() should be used to determine * the suitability of an assignment in a fine grained or restrictive way * based on context before calling this method

* * A CAST is stronger than an ASSIGNMENT operation in that it will * attempt to perform primtive operations that cast to a smaller type. * e.g. (byte)myLong; These are used in explicit primitive casts, * primitive delclarations and array declarations. I don't believe there * are any object conversions which are different between ASSIGNMENT and * CAST (e.g. scripted object to interface proxy in bsh is done on * assignment as well as cast).

* * This method does not obey strictJava(), you must test first before * using this method if you care. (See #isJavaAssignable()).

* * @param toType * the class type of the cast result, which may include * primitive types, e.g. Byte.TYPE. toType may be null to * indicate a loose type assignment (which matches any * fromType). * * @param fromType * is the class type of the value to be cast including * java primitive TYPE classes for primitives. If * fromValue is (or would be) Primitive.NULL then * fromType should be null. * * @param fromValue * an Object or bsh.Primitive primitive value (including * Primitive.NULL or Primitive.VOID ) * * @param checkOnly * If checkOnly is true then fromValue must be null. * FromType is checked for the cast to toType... If * checkOnly is false then fromValue must be non-null * (Primitive.NULL is ok) and the actual cast is * performed. * * @throws UtilEvalError * on invalid assignment (when operation is assignment ). * * @throws UtilTargetError * wrapping ClassCastException on cast error (when * operation is cast) * * @param operation * is Types.CAST or Types.ASSIGNMENT * * @see bsh.Primitive.getType() */ /* * Notes: This method is currently responsible for auto-boxing/unboxing * conversions... Where does that need to go? */ private static Object castObject(Class toType, Class fromType, Object fromValue, int operation, boolean checkOnly) throws UtilEvalError { /* * Lots of preconditions checked here... Once things are running * smoothly we might comment these out (That's what assertions * are for). */ if (checkOnly && fromValue != null) throw new InterpreterError("bad cast params 1"); if (!checkOnly && fromValue == null) throw new InterpreterError("bad cast params 2"); if (fromType == Primitive.class) throw new InterpreterError("bad from Type, need to unwrap"); if (fromValue == Primitive.NULL && fromType != null) throw new InterpreterError("inconsistent args 1"); if (fromValue == Primitive.VOID && fromType != Void.TYPE) throw new InterpreterError("inconsistent args 2"); if (toType == Void.TYPE) throw new InterpreterError("loose toType should be null"); // assignment to loose type, void type, or exactly same type if (toType == null || toType == fromType) return checkOnly ? VALID_CAST : fromValue; // Casting to primitive type if (toType.isPrimitive()) { if (fromType == Void.TYPE || fromType == null || fromType.isPrimitive()) { // Both primitives, do primitive cast return Primitive.castPrimitive(toType, fromType, (Primitive) fromValue, checkOnly, operation); } else { if (Primitive.isWrapperType(fromType)) { // wrapper to primitive // Convert value to Primitive and // check/cast it. // Object r = checkOnly ? VALID_CAST : Class unboxedFromType = Primitive.unboxType(fromType); Primitive primFromValue; if (checkOnly) primFromValue = null; // must // be // null // in // checkOnly else primFromValue = (Primitive) Primitive.wrap(fromValue, unboxedFromType); return Primitive.castPrimitive(toType, unboxedFromType, primFromValue, checkOnly, operation); } else { // Cannot cast from arbitrary object to // primitive if (checkOnly) return INVALID_CAST; else throw castError(toType, fromType, operation); } } } // Else, casting to reference type // Casting from primitive or void (to reference type) if (fromType == Void.TYPE || fromType == null || fromType.isPrimitive()) { // cast from primitive to wrapper type if (Primitive.isWrapperType(toType) && fromType != Void.TYPE && fromType != null) { // primitive to wrapper type return checkOnly ? VALID_CAST : Primitive.castWrapper(Primitive.unboxType(toType), ((Primitive) fromValue).getValue()); } // Primitive (not null or void) to Object.class type if (toType == Object.class && fromType != Void.TYPE && fromType != null) { // box it return checkOnly ? VALID_CAST : ((Primitive) fromValue).getValue(); } // Primitive to arbitrary object type. // Allow Primitive.castToType() to handle it as well as // cases of // Primitive.NULL and Primitive.VOID return Primitive.castPrimitive(toType, fromType, (Primitive) fromValue, checkOnly, operation); } // If type already assignable no cast necessary // We do this last to allow various errors above to be caught. // e.g cast Primitive.Void to Object would pass this if (toType.isAssignableFrom(fromType)) return checkOnly ? VALID_CAST : fromValue; // Can we use the proxy mechanism to cast a bsh.This to // the correct interface? if (toType.isInterface() && bsh.This.class.isAssignableFrom(fromType) && Capabilities.canGenerateInterfaces()) return checkOnly ? VALID_CAST : ((bsh.This) fromValue).getInterface(toType); // Both numeric wrapper types? // Try numeric style promotion wrapper cast if (Primitive.isWrapperType(toType) && Primitive.isWrapperType(fromType)) return checkOnly ? VALID_CAST : Primitive.castWrapper(toType, fromValue); if (checkOnly) return INVALID_CAST; else throw castError(toType, fromType, operation); } /** * Return a UtilEvalError or UtilTargetError wrapping a * ClassCastException describing an illegal assignment or illegal cast, * respectively. */ static UtilEvalError castError(Class lhsType, Class rhsType, int operation) { return castError(Reflect.normalizeClassName(lhsType), Reflect.normalizeClassName(rhsType), operation); } static UtilEvalError castError(String lhs, String rhs, int operation) { if (operation == ASSIGNMENT) return new UtilEvalError("Can't assign " + rhs + " to " + lhs); Exception cce = new ClassCastException("Cannot cast " + rhs + " to " + lhs); return new UtilTargetError(cce); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy