1 /*
   2  * Copyright 2008-2009 Sun Microsystems, Inc.  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.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any questions.
  24  */
  25 
  26 package impl.java.dyn.util;
  27 
  28 import java.util.HashMap;
  29 
  30 public class Wrappers {
  31 
  32     private Wrappers() { }  // cannot instantiate
  33 
  34     /** If {@code type} is a primitive type, return the corresponding
  35      *  wrapper type, else return {@code type} unchanged.
  36      */
  37     public static <T> Class<T> asWrapperType(Class<T> type) {
  38         if (!type.isPrimitive()) {
  39             return type;
  40         }
  41         if (wrappers.isEmpty()) {
  42             fillWrappers();
  43         }
  44         Object[] memo = wrappers.get(type);
  45         assert (memo != null);
  46         return (Class<T>) memo[0];  // unchecked warning is OK here
  47     }
  48 
  49     /** If {@code type} is a wrapper type, return the corresponding
  50      *  primitive type, else return {@code type} unchanged.
  51      */
  52     public static <T> Class<T> asPrimitiveType(Class<T> type) {
  53         if (type.isPrimitive()) {
  54             return type;
  55         }
  56         if (wrappers.isEmpty()) {
  57             fillWrappers();
  58         }
  59         Object[] memo = wrappers.get(type);
  60         if (memo == null) {
  61             return type;
  62         }
  63         return (Class<T>) memo[1];  // unchecked warning is OK here
  64     }
  65 
  66     public static boolean isWrapperType(Class<?> type) {
  67         return asPrimitiveType(type) != type;
  68     }
  69 
  70     public static boolean isPrimitiveType(Class<?> type) {
  71         return type.isPrimitive();
  72     }
  73 
  74     public static char basicTypeChar(Class<?> type) {
  75         if (!type.isPrimitive()) {
  76             return 'L';
  77         }
  78         if (wrappers.isEmpty()) {
  79             fillWrappers();
  80         }
  81         Object[] memo = wrappers.get(type);
  82         assert (memo != null);
  83         return (char) (Character) memo[2];
  84     }
  85 
  86     static final String PRIMITIVE_BITS_TABLE = "LZBCSFIZZDJ";
  87     //                                         "--01234  78"
  88 
  89     /** Return the number of bits in the given type, or zero for refs. */
  90     public static int bitWidth(Class<?> type) {
  91         return bitWidth(basicTypeChar(type));
  92     }
  93 
  94     /** Return the number of bits in the given basic type, or zero for refs. */
  95     public static int bitWidth(char c) {
  96         int i = PRIMITIVE_BITS_TABLE.indexOf(c);
  97         if (i < 0)  throw new IllegalArgumentException("not a basic type char: "+c);
  98         i -= 2;
  99         switch (i) {
 100             case -2: return 0; // L
 101             case -1: return 1; // Z
 102             case 0:  return 8; // B
 103             default: return (i + (i & 1)) * 8;
 104         }
 105     }
 106 
 107     static final String PRIMITIVE_SIGN_TABLE = "JZICSZB";
 108     //                                         "SuSuS S"
 109     /** Return whether the given type is a signed integral type. */
 110     public static boolean isSigned(Class<?> type) {
 111         return isSigned(basicTypeChar(type));
 112     }
 113 
 114     /** Return whether the given type is a signed integral type. */
 115     public static boolean isSigned(char c) {
 116         int i = PRIMITIVE_SIGN_TABLE.indexOf(c);
 117         return (i & 1) == 0;
 118     }
 119 
 120     /** Return whether the given type is a unsigned integral type. */
 121     public static boolean isUnsigned(Class<?> type) {
 122         return isUnsigned(basicTypeChar(type));
 123     }
 124 
 125     /** Return whether the given type is a unsigned integral type. */
 126     public static boolean isUnsigned(char c) {
 127         int i = PRIMITIVE_SIGN_TABLE.indexOf(c);
 128         return (i & 0x11) == 1;
 129     }
 130 
 131     /** Return whether the given type is an integral type. */
 132     public static boolean isIntegral(Class<?> type) {
 133         return isIntegral(basicTypeChar(type));
 134     }
 135 
 136     /** Return whether the given type is an integral type. */
 137     public static boolean isIntegral(char c) {
 138         int i = PRIMITIVE_SIGN_TABLE.indexOf(c);
 139         return i >= 0;
 140     }
 141 
 142     /** Report if the type is one of int, boolean, byte, char, or short. */
 143     public static boolean isSubwordOrInt(Class<?> type) {
 144         return isSubwordOrInt(basicTypeChar(type));
 145     }
 146 
 147     /** Report if the type char is one of "IZBCS". */
 148     public static boolean isSubwordOrInt(char c) {
 149         return PRIMITIVE_SIGN_TABLE.indexOf(c) > 0;
 150     }
 151 
 152     /** Return whether the given type is a floating primitive type. */
 153     public static boolean isFloating(Class<?> type) {
 154         return isFloating(basicTypeChar(type));
 155     }
 156 
 157     /** Return whether the given type is a floating primitive type. */
 158     public static boolean isFloating(char c) {
 159         return c == 'F' || c == 'D';
 160     }
 161 
 162     /** Return the primitive type that corresponds to the given bytecode
 163      *  signature character.  Return {@code Object.class} for the character
 164      *  'L', and null for any non-signature character or '['.
 165      */
 166     public static Class<?> basicTypeFromChar(char c) {
 167         if (c == 'L') {
 168             return Object.class;
 169         }
 170 //        if (c == '[') {
 171 //            return Object[].class;
 172 //        }
 173         if (wrappers.isEmpty()) {
 174             fillWrappers();
 175         }
 176         Object[] memo = wrappers.get((Character)c);
 177         if (memo == null)
 178             return null;   // random junk character
 179         return (Class<?>) memo[1];
 180     }
 181 
 182     public static Object zeroValue(Class<?> type) {
 183         if (!type.isPrimitive()) {
 184             return null;
 185         }
 186         if (wrappers.isEmpty()) {
 187             fillWrappers();
 188         }
 189         Object[] memo = wrappers.get(type);
 190         assert (memo != null);
 191         return memo[3];
 192     }
 193 
 194     public static <T> T wrap(Object x, Class<T> numClass) {
 195         if (wrappers.isEmpty()) {
 196             fillWrappers();
 197         }
 198         Object[] memo = wrappers.get(numClass);
 199         if (memo == null)  return numClass.cast(x);  // no change
 200         Class<T> wrapType = (Class<T>) memo[0];  // unchecked warning is OK here
 201         return wrapType.cast(wrap(x, (Character) memo[2]));
 202     }
 203     public static Object wrap(Object x, char c) {
 204         Number xn = numberValue(x);
 205         switch (c) {
 206         case 'I': return Integer.valueOf(xn.intValue());
 207         case 'J': return Long.valueOf(xn.longValue());
 208         case 'F': return Float.valueOf(xn.floatValue());
 209         case 'D': return Double.valueOf(xn.doubleValue());
 210         case 'S': return Short.valueOf((short) xn.intValue());
 211         case 'B': return Byte.valueOf((byte) xn.intValue());
 212         case 'C': return Character.valueOf((char) xn.intValue());
 213         case 'Z': return Boolean.valueOf(boolValue(xn.longValue()));
 214         case 'V': return null;
 215         }
 216         return xn;
 217     }
 218 
 219     private static Number numberValue(Object x) {
 220         if (x instanceof Number)     return (Number)x;
 221         if (x instanceof Character)  return (int)(Character)x;
 222         if (x instanceof Boolean)    return (Boolean)x ? 1 : 0;
 223         // Remaining allowed case of void:  Must be a null reference.
 224         return (Number)x;
 225     }
 226     private static boolean boolValue(long bits) {
 227         bits &= 1;  // simple 31-bit zero extension
 228         return (bits != 0);
 229     }
 230 
 231     private static final HashMap<Object, Object[]> wrappers
 232         = new HashMap<Object, Object[]>(20);
 233 
 234     private static void fillWrappers() {
 235         Object[][] memos = {
 236             {Boolean.class, Boolean.TYPE, 'Z', (Boolean) false},
 237             {Character.class, Character.TYPE, 'C', (Character) '\000'},
 238             {Byte.class, Byte.TYPE, 'B', (Byte) (byte) 0},
 239             {Short.class, Short.TYPE, 'S', (Short) (short) 0},
 240             {Integer.class, Integer.TYPE, 'I', (Integer) 0},
 241             {Long.class, Long.TYPE, 'J', (Long) 0L},
 242             {Float.class, Float.TYPE, 'F', (Float) 0.0F},
 243             {Double.class, Double.TYPE, 'D', (Double) 0.0},
 244             {Void.class, Void.TYPE, 'V', null}
 245         };
 246         for (Object[] memo : memos) {
 247             wrappers.put(memo[0], memo);
 248             wrappers.put(memo[1], memo);
 249             wrappers.put(memo[2], memo);
 250         }
 251     }
 252 
 253     // TO DO:  Put these into a unit test.
 254     private static Class<Integer> PTYPE = int.class, WTYPE = Integer.class;
 255     static {
 256         assert(PTYPE != WTYPE);
 257         assert(asPrimitiveType(PTYPE) == PTYPE);
 258         assert(asPrimitiveType(WTYPE) == PTYPE);
 259         assert(  asWrapperType(PTYPE) == WTYPE);
 260         assert(  asWrapperType(WTYPE) == WTYPE);
 261 
 262         assert(bitWidth(String.class) == 0);
 263         assert(bitWidth(Integer.class) == 0);
 264         assert(bitWidth(int.class) == 32);
 265         assert(bitWidth(char.class) == 16);
 266         assert(bitWidth(short.class) == 16);
 267         assert(bitWidth(byte.class) == 8);
 268         assert(bitWidth(boolean.class) == 1);
 269         assert(bitWidth(double.class) == 64);
 270         assert(bitWidth(float.class) == 32);
 271     }
 272 }