nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/TypeUtilities.java
changeset 33344 cc4a221f42b7
parent 33025 16b4968f9bb8
parent 33343 23abd10384a5
child 33345 ef8c859f7992
equal deleted inserted replaced
33025:16b4968f9bb8 33344:cc4a221f42b7
     1 /*
       
     2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 /*
       
    27  * This file is available under and governed by the GNU General Public
       
    28  * License version 2 only, as published by the Free Software Foundation.
       
    29  * However, the following notice accompanied the original version of this
       
    30  * file, and Oracle licenses the original version of this file under the BSD
       
    31  * license:
       
    32  */
       
    33 /*
       
    34    Copyright 2009-2013 Attila Szegedi
       
    35 
       
    36    Licensed under both the Apache License, Version 2.0 (the "Apache License")
       
    37    and the BSD License (the "BSD License"), with licensee being free to
       
    38    choose either of the two at their discretion.
       
    39 
       
    40    You may not use this file except in compliance with either the Apache
       
    41    License or the BSD License.
       
    42 
       
    43    If you choose to use this file in compliance with the Apache License, the
       
    44    following notice applies to you:
       
    45 
       
    46        You may obtain a copy of the Apache License at
       
    47 
       
    48            http://www.apache.org/licenses/LICENSE-2.0
       
    49 
       
    50        Unless required by applicable law or agreed to in writing, software
       
    51        distributed under the License is distributed on an "AS IS" BASIS,
       
    52        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
       
    53        implied. See the License for the specific language governing
       
    54        permissions and limitations under the License.
       
    55 
       
    56    If you choose to use this file in compliance with the BSD License, the
       
    57    following notice applies to you:
       
    58 
       
    59        Redistribution and use in source and binary forms, with or without
       
    60        modification, are permitted provided that the following conditions are
       
    61        met:
       
    62        * Redistributions of source code must retain the above copyright
       
    63          notice, this list of conditions and the following disclaimer.
       
    64        * Redistributions in binary form must reproduce the above copyright
       
    65          notice, this list of conditions and the following disclaimer in the
       
    66          documentation and/or other materials provided with the distribution.
       
    67        * Neither the name of the copyright holder nor the names of
       
    68          contributors may be used to endorse or promote products derived from
       
    69          this software without specific prior written permission.
       
    70 
       
    71        THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
       
    72        IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
       
    73        TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
       
    74        PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
       
    75        BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
       
    76        CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
       
    77        SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
       
    78        BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
       
    79        WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
       
    80        OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
       
    81        ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    82 */
       
    83 
       
    84 package jdk.internal.dynalink.support;
       
    85 
       
    86 import java.util.ArrayList;
       
    87 import java.util.Collection;
       
    88 import java.util.Collections;
       
    89 import java.util.HashMap;
       
    90 import java.util.HashSet;
       
    91 import java.util.IdentityHashMap;
       
    92 import java.util.Iterator;
       
    93 import java.util.List;
       
    94 import java.util.Map;
       
    95 import java.util.Set;
       
    96 
       
    97 /**
       
    98  * Various static utility methods for testing type relationships.
       
    99  */
       
   100 public class TypeUtilities {
       
   101     static final Class<Object> OBJECT_CLASS = Object.class;
       
   102 
       
   103     private TypeUtilities() {
       
   104     }
       
   105 
       
   106     /**
       
   107      * Given two types represented by c1 and c2, returns a type that is their most specific common supertype for
       
   108      * purposes of lossless conversions.
       
   109      *
       
   110      * @param c1 one type
       
   111      * @param c2 another type
       
   112      * @return their most common superclass or superinterface for purposes of lossless conversions. If they have several
       
   113      * unrelated superinterfaces as their most specific common type, or the types themselves are completely
       
   114      * unrelated interfaces, {@link java.lang.Object} is returned.
       
   115      */
       
   116     public static Class<?> getCommonLosslessConversionType(final Class<?> c1, final Class<?> c2) {
       
   117         if(c1 == c2) {
       
   118             return c1;
       
   119         } else if (c1 == void.class || c2 == void.class) {
       
   120             return Object.class;
       
   121         } else if(isConvertibleWithoutLoss(c2, c1)) {
       
   122             return c1;
       
   123         } else if(isConvertibleWithoutLoss(c1, c2)) {
       
   124             return c2;
       
   125         } else if(c1.isPrimitive() && c2.isPrimitive()) {
       
   126             if((c1 == byte.class && c2 == char.class) || (c1 == char.class && c2 == byte.class)) {
       
   127                 // byte + char = int
       
   128                 return int.class;
       
   129             } else if((c1 == short.class && c2 == char.class) || (c1 == char.class && c2 == short.class)) {
       
   130                 // short + char = int
       
   131                 return int.class;
       
   132             } else if((c1 == int.class && c2 == float.class) || (c1 == float.class && c2 == int.class)) {
       
   133                 // int + float = double
       
   134                 return double.class;
       
   135             }
       
   136         }
       
   137         // For all other cases. This will handle long + (float|double) = Number case as well as boolean + anything = Object case too.
       
   138         return getMostSpecificCommonTypeUnequalNonprimitives(c1, c2);
       
   139     }
       
   140 
       
   141     private static Class<?> getMostSpecificCommonTypeUnequalNonprimitives(final Class<?> c1, final Class<?> c2) {
       
   142         final Class<?> npc1 = c1.isPrimitive() ? getWrapperType(c1) : c1;
       
   143         final Class<?> npc2 = c2.isPrimitive() ? getWrapperType(c2) : c2;
       
   144         final Set<Class<?>> a1 = getAssignables(npc1, npc2);
       
   145         final Set<Class<?>> a2 = getAssignables(npc2, npc1);
       
   146         a1.retainAll(a2);
       
   147         if(a1.isEmpty()) {
       
   148             // Can happen when at least one of the arguments is an interface,
       
   149             // as they don't have Object at the root of their hierarchy.
       
   150             return Object.class;
       
   151         }
       
   152         // Gather maximally specific elements. Yes, there can be more than one
       
   153         // thank to interfaces. I.e., if you call this method for String.class
       
   154         // and Number.class, you'll have Comparable, Serializable, and Object
       
   155         // as maximal elements.
       
   156         final List<Class<?>> max = new ArrayList<>();
       
   157         outer: for(final Class<?> clazz: a1) {
       
   158             for(final Iterator<Class<?>> maxiter = max.iterator(); maxiter.hasNext();) {
       
   159                 final Class<?> maxClazz = maxiter.next();
       
   160                 if(isSubtype(maxClazz, clazz)) {
       
   161                     // It can't be maximal, if there's already a more specific
       
   162                     // maximal than it.
       
   163                     continue outer;
       
   164                 }
       
   165                 if(isSubtype(clazz, maxClazz)) {
       
   166                     // If it's more specific than a currently maximal element,
       
   167                     // that currently maximal is no longer a maximal.
       
   168                     maxiter.remove();
       
   169                 }
       
   170             }
       
   171             // If we get here, no current maximal is more specific than the
       
   172             // current class, so it is considered maximal as well
       
   173             max.add(clazz);
       
   174         }
       
   175         if(max.size() > 1) {
       
   176             return Object.class;
       
   177         }
       
   178         return max.get(0);
       
   179     }
       
   180 
       
   181     private static Set<Class<?>> getAssignables(final Class<?> c1, final Class<?> c2) {
       
   182         final Set<Class<?>> s = new HashSet<>();
       
   183         collectAssignables(c1, c2, s);
       
   184         return s;
       
   185     }
       
   186 
       
   187     private static void collectAssignables(final Class<?> c1, final Class<?> c2, final Set<Class<?>> s) {
       
   188         if(c1.isAssignableFrom(c2)) {
       
   189             s.add(c1);
       
   190         }
       
   191         final Class<?> sc = c1.getSuperclass();
       
   192         if(sc != null) {
       
   193             collectAssignables(sc, c2, s);
       
   194         }
       
   195         final Class<?>[] itf = c1.getInterfaces();
       
   196         for(int i = 0; i < itf.length; ++i) {
       
   197             collectAssignables(itf[i], c2, s);
       
   198         }
       
   199     }
       
   200 
       
   201     private static final Map<Class<?>, Class<?>> WRAPPER_TYPES = createWrapperTypes();
       
   202     private static final Map<Class<?>, Class<?>> PRIMITIVE_TYPES = invertMap(WRAPPER_TYPES);
       
   203     private static final Map<String, Class<?>> PRIMITIVE_TYPES_BY_NAME = createClassNameMapping(WRAPPER_TYPES.keySet());
       
   204 
       
   205     private static Map<Class<?>, Class<?>> createWrapperTypes() {
       
   206         final Map<Class<?>, Class<?>> wrapperTypes = new IdentityHashMap<>(8);
       
   207         wrapperTypes.put(Boolean.TYPE, Boolean.class);
       
   208         wrapperTypes.put(Byte.TYPE, Byte.class);
       
   209         wrapperTypes.put(Character.TYPE, Character.class);
       
   210         wrapperTypes.put(Short.TYPE, Short.class);
       
   211         wrapperTypes.put(Integer.TYPE, Integer.class);
       
   212         wrapperTypes.put(Long.TYPE, Long.class);
       
   213         wrapperTypes.put(Float.TYPE, Float.class);
       
   214         wrapperTypes.put(Double.TYPE, Double.class);
       
   215         return Collections.unmodifiableMap(wrapperTypes);
       
   216     }
       
   217 
       
   218     private static Map<String, Class<?>> createClassNameMapping(final Collection<Class<?>> classes) {
       
   219         final Map<String, Class<?>> map = new HashMap<>();
       
   220         for(final Class<?> clazz: classes) {
       
   221             map.put(clazz.getName(), clazz);
       
   222         }
       
   223         return map;
       
   224     }
       
   225 
       
   226     private static <K, V> Map<V, K> invertMap(final Map<K, V> map) {
       
   227         final Map<V, K> inverted = new IdentityHashMap<>(map.size());
       
   228         for(final Map.Entry<K, V> entry: map.entrySet()) {
       
   229             inverted.put(entry.getValue(), entry.getKey());
       
   230         }
       
   231         return Collections.unmodifiableMap(inverted);
       
   232     }
       
   233 
       
   234     /**
       
   235      * Determines whether one type can be converted to another type using a method invocation conversion, as per JLS 5.3
       
   236      * "Method Invocation Conversion". This is basically all conversions allowed by subtyping (see
       
   237      * {@link #isSubtype(Class, Class)}) as well as boxing conversion (JLS 5.1.7) optionally followed by widening
       
   238      * reference conversion and unboxing conversion (JLS 5.1.8) optionally followed by widening primitive conversion.
       
   239      *
       
   240      * @param sourceType the type being converted from (call site type for parameter types, method type for return types)
       
   241      * @param targetType the parameter type being converted to (method type for parameter types, call site type for return types)
       
   242      * @return true if source type is method invocation convertible to target type.
       
   243      */
       
   244     public static boolean isMethodInvocationConvertible(final Class<?> sourceType, final Class<?> targetType) {
       
   245         if(targetType.isAssignableFrom(sourceType)) {
       
   246             return true;
       
   247         }
       
   248         if(sourceType.isPrimitive()) {
       
   249             if(targetType.isPrimitive()) {
       
   250                 return isProperPrimitiveSubtype(sourceType, targetType);
       
   251             }
       
   252             // Boxing + widening reference conversion
       
   253             assert WRAPPER_TYPES.get(sourceType) != null : sourceType.getName();
       
   254             return targetType.isAssignableFrom(WRAPPER_TYPES.get(sourceType));
       
   255         }
       
   256         if(targetType.isPrimitive()) {
       
   257             final Class<?> unboxedCallSiteType = PRIMITIVE_TYPES.get(sourceType);
       
   258             return unboxedCallSiteType != null
       
   259                     && (unboxedCallSiteType == targetType || isProperPrimitiveSubtype(unboxedCallSiteType, targetType));
       
   260         }
       
   261         return false;
       
   262     }
       
   263 
       
   264     /**
       
   265      * Determines whether a type can be converted to another without losing any precision. As a special case,
       
   266      * void is considered convertible only to Object and void, while anything can be converted to void. This
       
   267      * is because a target type of void means we don't care about the value, so the conversion is always
       
   268      * permissible.
       
   269      *
       
   270      * @param sourceType the source type
       
   271      * @param targetType the target type
       
   272      * @return true if lossless conversion is possible
       
   273      */
       
   274     public static boolean isConvertibleWithoutLoss(final Class<?> sourceType, final Class<?> targetType) {
       
   275         if(targetType.isAssignableFrom(sourceType) || targetType == void.class) {
       
   276             return true;
       
   277         }
       
   278         if(sourceType.isPrimitive()) {
       
   279             if(sourceType == void.class) {
       
   280                 // Void should be losslessly representable by Object, either as null or as a custom value that
       
   281                 // can be set with DynamicLinkerFactory.setAutoConversionStrategy.
       
   282                 return targetType == Object.class;
       
   283             }
       
   284             if(targetType.isPrimitive()) {
       
   285                 return isProperPrimitiveLosslessSubtype(sourceType, targetType);
       
   286             }
       
   287             // Boxing + widening reference conversion
       
   288             assert WRAPPER_TYPES.get(sourceType) != null : sourceType.getName();
       
   289             return targetType.isAssignableFrom(WRAPPER_TYPES.get(sourceType));
       
   290         }
       
   291         // Can't convert from any non-primitive type to any primitive type without data loss because of null.
       
   292         // Also, can't convert non-assignable reference types.
       
   293         return false;
       
   294     }
       
   295 
       
   296     /**
       
   297      * Determines whether one type can be potentially converted to another type at runtime. Allows a conversion between
       
   298      * any subtype and supertype in either direction, and also allows a conversion between any two primitive types, as
       
   299      * well as between any primitive type and any reference type that can hold a boxed primitive.
       
   300      *
       
   301      * @param callSiteType the parameter type at the call site
       
   302      * @param methodType the parameter type in the method declaration
       
   303      * @return true if callSiteType is potentially convertible to the methodType.
       
   304      */
       
   305     public static boolean isPotentiallyConvertible(final Class<?> callSiteType, final Class<?> methodType) {
       
   306         // Widening or narrowing reference conversion
       
   307         if(areAssignable(callSiteType, methodType)) {
       
   308             return true;
       
   309         }
       
   310         if(callSiteType.isPrimitive()) {
       
   311             // Allow any conversion among primitives, as well as from any
       
   312             // primitive to any type that can receive a boxed primitive.
       
   313             // TODO: narrow this a bit, i.e. allow, say, boolean to Character?
       
   314             // MethodHandles.convertArguments() allows it, so we might need to
       
   315             // too.
       
   316             return methodType.isPrimitive() || isAssignableFromBoxedPrimitive(methodType);
       
   317         }
       
   318         if(methodType.isPrimitive()) {
       
   319             // Allow conversion from any reference type that can contain a
       
   320             // boxed primitive to any primitive.
       
   321             // TODO: narrow this a bit too?
       
   322             return isAssignableFromBoxedPrimitive(callSiteType);
       
   323         }
       
   324         return false;
       
   325     }
       
   326 
       
   327     /**
       
   328      * Returns true if either of the types is assignable from the other.
       
   329      * @param c1 one of the types
       
   330      * @param c2 another one of the types
       
   331      * @return true if either c1 is assignable from c2 or c2 is assignable from c1.
       
   332      */
       
   333     public static boolean areAssignable(final Class<?> c1, final Class<?> c2) {
       
   334         return c1.isAssignableFrom(c2) || c2.isAssignableFrom(c1);
       
   335     }
       
   336 
       
   337     /**
       
   338      * Determines whether one type is a subtype of another type, as per JLS 4.10 "Subtyping". Note: this is not strict
       
   339      * or proper subtype, therefore true is also returned for identical types; to be completely precise, it allows
       
   340      * identity conversion (JLS 5.1.1), widening primitive conversion (JLS 5.1.2) and widening reference conversion (JLS
       
   341      * 5.1.5).
       
   342      *
       
   343      * @param subType the supposed subtype
       
   344      * @param superType the supposed supertype of the subtype
       
   345      * @return true if subType can be converted by identity conversion, widening primitive conversion, or widening
       
   346      * reference conversion to superType.
       
   347      */
       
   348     public static boolean isSubtype(final Class<?> subType, final Class<?> superType) {
       
   349         // Covers both JLS 4.10.2 "Subtyping among Class and Interface Types"
       
   350         // and JLS 4.10.3 "Subtyping among Array Types", as well as primitive
       
   351         // type identity.
       
   352         if(superType.isAssignableFrom(subType)) {
       
   353             return true;
       
   354         }
       
   355         // JLS 4.10.1 "Subtyping among Primitive Types". Note we don't test for
       
   356         // identity, as identical types were taken care of in the
       
   357         // isAssignableFrom test. As per 4.10.1, the supertype relation is as
       
   358         // follows:
       
   359         // double > float
       
   360         // float > long
       
   361         // long > int
       
   362         // int > short
       
   363         // int > char
       
   364         // short > byte
       
   365         if(superType.isPrimitive() && subType.isPrimitive()) {
       
   366             return isProperPrimitiveSubtype(subType, superType);
       
   367         }
       
   368         return false;
       
   369     }
       
   370 
       
   371     /**
       
   372      * Returns true if a supposed primitive subtype is a proper subtype ( meaning, subtype and not identical) of the
       
   373      * supposed primitive supertype
       
   374      *
       
   375      * @param subType the supposed subtype
       
   376      * @param superType the supposed supertype
       
   377      * @return true if subType is a proper (not identical to) primitive subtype of the superType
       
   378      */
       
   379     private static boolean isProperPrimitiveSubtype(final Class<?> subType, final Class<?> superType) {
       
   380         if(superType == boolean.class || subType == boolean.class) {
       
   381             return false;
       
   382         }
       
   383         if(subType == byte.class) {
       
   384             return superType != char.class;
       
   385         }
       
   386         if(subType == char.class) {
       
   387             return superType != short.class && superType != byte.class;
       
   388         }
       
   389         if(subType == short.class) {
       
   390             return superType != char.class && superType != byte.class;
       
   391         }
       
   392         if(subType == int.class) {
       
   393             return superType == long.class || superType == float.class || superType == double.class;
       
   394         }
       
   395         if(subType == long.class) {
       
   396             return superType == float.class || superType == double.class;
       
   397         }
       
   398         if(subType == float.class) {
       
   399             return superType == double.class;
       
   400         }
       
   401         return false;
       
   402     }
       
   403 
       
   404     /**
       
   405      * Similar to {@link #isProperPrimitiveSubtype(Class, Class)}, except it disallows conversions from int and long to
       
   406      * float, and from long to double, as those can lose precision. It also disallows conversion from and to char and
       
   407      * anything else (similar to boolean) as char is not meant to be an arithmetic type.
       
   408      * @param subType the supposed subtype
       
   409      * @param superType the supposed supertype
       
   410      * @return true if subType is a proper (not identical to) primitive subtype of the superType that can be represented
       
   411      * by the supertype without no precision loss.
       
   412      */
       
   413     private static boolean isProperPrimitiveLosslessSubtype(final Class<?> subType, final Class<?> superType) {
       
   414         if(superType == boolean.class || subType == boolean.class) {
       
   415             return false;
       
   416         }
       
   417         if(superType == char.class || subType == char.class) {
       
   418             return false;
       
   419         }
       
   420         if(subType == byte.class) {
       
   421             return true;
       
   422         }
       
   423         if(subType == short.class) {
       
   424             return superType != byte.class;
       
   425         }
       
   426         if(subType == int.class) {
       
   427             return superType == long.class || superType == double.class;
       
   428         }
       
   429         if(subType == float.class) {
       
   430             return superType == double.class;
       
   431         }
       
   432         return false;
       
   433     }
       
   434 
       
   435     private static final Map<Class<?>, Class<?>> WRAPPER_TO_PRIMITIVE_TYPES = createWrapperToPrimitiveTypes();
       
   436 
       
   437     private static Map<Class<?>, Class<?>> createWrapperToPrimitiveTypes() {
       
   438         final Map<Class<?>, Class<?>> classes = new IdentityHashMap<>();
       
   439         classes.put(Void.class, Void.TYPE);
       
   440         classes.put(Boolean.class, Boolean.TYPE);
       
   441         classes.put(Byte.class, Byte.TYPE);
       
   442         classes.put(Character.class, Character.TYPE);
       
   443         classes.put(Short.class, Short.TYPE);
       
   444         classes.put(Integer.class, Integer.TYPE);
       
   445         classes.put(Long.class, Long.TYPE);
       
   446         classes.put(Float.class, Float.TYPE);
       
   447         classes.put(Double.class, Double.TYPE);
       
   448         return classes;
       
   449     }
       
   450 
       
   451     private static final Set<Class<?>> PRIMITIVE_WRAPPER_TYPES = createPrimitiveWrapperTypes();
       
   452 
       
   453     private static Set<Class<?>> createPrimitiveWrapperTypes() {
       
   454         final Map<Class<?>, Class<?>> classes = new IdentityHashMap<>();
       
   455         addClassHierarchy(classes, Boolean.class);
       
   456         addClassHierarchy(classes, Byte.class);
       
   457         addClassHierarchy(classes, Character.class);
       
   458         addClassHierarchy(classes, Short.class);
       
   459         addClassHierarchy(classes, Integer.class);
       
   460         addClassHierarchy(classes, Long.class);
       
   461         addClassHierarchy(classes, Float.class);
       
   462         addClassHierarchy(classes, Double.class);
       
   463         return classes.keySet();
       
   464     }
       
   465 
       
   466     private static void addClassHierarchy(final Map<Class<?>, Class<?>> map, final Class<?> clazz) {
       
   467         if(clazz == null) {
       
   468             return;
       
   469         }
       
   470         map.put(clazz, clazz);
       
   471         addClassHierarchy(map, clazz.getSuperclass());
       
   472         for(final Class<?> itf: clazz.getInterfaces()) {
       
   473             addClassHierarchy(map, itf);
       
   474         }
       
   475     }
       
   476 
       
   477     /**
       
   478      * Returns true if the class can be assigned from any boxed primitive.
       
   479      *
       
   480      * @param clazz the class
       
   481      * @return true if the class can be assigned from any boxed primitive. Basically, it is true if the class is any
       
   482      * primitive wrapper class, or a superclass or superinterface of any primitive wrapper class.
       
   483      */
       
   484     private static boolean isAssignableFromBoxedPrimitive(final Class<?> clazz) {
       
   485         return PRIMITIVE_WRAPPER_TYPES.contains(clazz);
       
   486     }
       
   487 
       
   488     /**
       
   489      * Given a name of a primitive type (except "void"), returns the class representing it. I.e. when invoked with
       
   490      * "int", returns {@link Integer#TYPE}.
       
   491      * @param name the name of the primitive type
       
   492      * @return the class representing the primitive type, or null if the name does not correspond to a primitive type
       
   493      * or is "void".
       
   494      */
       
   495     public static Class<?> getPrimitiveTypeByName(final String name) {
       
   496         return PRIMITIVE_TYPES_BY_NAME.get(name);
       
   497     }
       
   498 
       
   499     /**
       
   500      * When passed a class representing a wrapper for a primitive type, returns the class representing the corresponding
       
   501      * primitive type. I.e. calling it with {@code Integer.class} will return {@code Integer.TYPE}. If passed a class
       
   502      * that is not a wrapper for primitive type, returns null.
       
   503      * @param wrapperType the class object representing a wrapper for a primitive type
       
   504      * @return the class object representing the primitive type, or null if the passed class is not a primitive wrapper.
       
   505      */
       
   506     public static Class<?> getPrimitiveType(final Class<?> wrapperType) {
       
   507         return WRAPPER_TO_PRIMITIVE_TYPES.get(wrapperType);
       
   508     }
       
   509 
       
   510 
       
   511     /**
       
   512      * When passed a class representing a primitive type, returns the class representing the corresponding
       
   513      * wrapper type. I.e. calling it with {@code int.class} will return {@code Integer.class}. If passed a class
       
   514      * that is not a primitive type, returns null.
       
   515      * @param primitiveType the class object representing a primitive type
       
   516      * @return the class object representing the wrapper type, or null if the passed class is not a primitive.
       
   517      */
       
   518     public static Class<?> getWrapperType(final Class<?> primitiveType) {
       
   519         return WRAPPER_TYPES.get(primitiveType);
       
   520     }
       
   521 
       
   522     /**
       
   523      * Returns true if the passed type is a wrapper for a primitive type.
       
   524      * @param type the examined type
       
   525      * @return true if the passed type is a wrapper for a primitive type.
       
   526      */
       
   527     public static boolean isWrapperType(final Class<?> type) {
       
   528         return PRIMITIVE_TYPES.containsKey(type);
       
   529     }
       
   530 }