PageRenderTime 68ms CodeModel.GetById 41ms app.highlight 23ms RepoModel.GetById 0ms app.codeStats 0ms

/core/src/com/bluemarsh/jswat/core/util/Types.java

http://jswat.googlecode.com/
Java | 656 lines | 412 code | 45 blank | 199 comment | 255 complexity | a7125b7c01ebdae62bf428e1ba28f003 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception
  1/*
  2 * The contents of this file are subject to the terms of the Common Development
  3 * and Distribution License (the License). You may not use this file except in
  4 * compliance with the License.
  5 *
  6 * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
  7 * or http://www.netbeans.org/cddl.txt.
  8 *
  9 * When distributing Covered Code, include this CDDL Header Notice in each file
 10 * and include the License file at http://www.netbeans.org/cddl.txt.
 11 * If applicable, add the following below the CDDL Header, with the fields
 12 * enclosed by brackets [] replaced by your own identifying information:
 13 * "Portions Copyrighted [year] [name of copyright owner]"
 14 *
 15 * The Original Software is the JSwat Core Module. The Initial Developer of the
 16 * Software is Nathan L. Fiedler. Portions created by Nathan L. Fiedler
 17 * are Copyright (C) 2003-2010. All Rights Reserved.
 18 *
 19 * Contributor(s): Nathan L. Fiedler.
 20 *
 21 * $Id: Types.java 285 2010-11-20 23:56:08Z nathanfiedler $
 22 */
 23package com.bluemarsh.jswat.core.util;
 24
 25import com.sun.jdi.ByteType;
 26import com.sun.jdi.ClassType;
 27import com.sun.jdi.DoubleType;
 28import com.sun.jdi.FloatType;
 29import com.sun.jdi.IntegerType;
 30import com.sun.jdi.InterfaceType;
 31import com.sun.jdi.LongType;
 32import com.sun.jdi.PrimitiveValue;
 33import com.sun.jdi.ReferenceType;
 34import com.sun.jdi.ShortType;
 35import com.sun.jdi.Type;
 36import com.sun.jdi.Value;
 37import com.sun.jdi.VirtualMachine;
 38import java.util.HashMap;
 39import java.util.LinkedList;
 40import java.util.List;
 41import java.util.Map;
 42import java.util.Stack;
 43
 44/**
 45 * Class Types provides utility methods for handling types of various
 46 * forms, especially converting to and from JNI-style type signatures.
 47 *
 48 * @author  Nathan Fiedler
 49 */
 50public class Types {
 51
 52    /** Sizes of numeric types, keyed by Character. */
 53    private static final Map<Character, Integer> SIZES_BY_CHAR;
 54    /** Sizes of numeric types, keyed by Class. */
 55    private static final Map<Class<?>, Integer> SIZES_BY_CLASS;
 56
 57    static {
 58        // Sizes of numbers by primitive JNI signature.
 59        SIZES_BY_CHAR = new HashMap<Character, Integer>();
 60        SIZES_BY_CHAR.put(new Character('B'), new Integer(1));
 61        SIZES_BY_CHAR.put(new Character('S'), new Integer(2));
 62        SIZES_BY_CHAR.put(new Character('I'), new Integer(4));
 63        SIZES_BY_CHAR.put(new Character('J'), new Integer(8));
 64        SIZES_BY_CHAR.put(new Character('F'), new Integer(4));
 65        SIZES_BY_CHAR.put(new Character('D'), new Integer(8));
 66
 67        // Sizes of numbers by wrapper class.
 68        SIZES_BY_CLASS = new HashMap<Class<?>, Integer>();
 69        SIZES_BY_CLASS.put(Byte.class, new Integer(1));
 70        SIZES_BY_CLASS.put(Short.class, new Integer(2));
 71        SIZES_BY_CLASS.put(Integer.class, new Integer(4));
 72        SIZES_BY_CLASS.put(Long.class, new Integer(8));
 73        SIZES_BY_CLASS.put(Float.class, new Integer(4));
 74        SIZES_BY_CLASS.put(Double.class, new Integer(8));
 75    }
 76
 77    /**
 78     * We are not instantiated.
 79     */
 80    private Types() {
 81    }
 82
 83    /**
 84     * Compares the desired type with the actual type and determines if the
 85     * actual type can be widened to the desired type. For instance, a byte
 86     * can be widened to any other primitive number. This applies only in
 87     * the case where the actual type is a Number and the desired type is
 88     * a byte, double, float, integer, long, or short. Wrapper classes
 89     * (Byte, Double, etc.) are not included.
 90     *
 91     * @param  desiredType   the desired JNI type signature.
 92     * @param  actualType    the actual numeric type to widen.
 93     * @return  true if type can be widened to desired type, false otherwise.
 94     */
 95    public static boolean canWiden(String desiredType, Class<?> actualType) {
 96        char dc = desiredType.charAt(0);
 97        Integer desiredSize = SIZES_BY_CHAR.get(new Character(dc));
 98        Integer actualSize = SIZES_BY_CLASS.get(actualType);
 99        boolean desiredInt = dc == 'B' || dc == 'S' || dc == 'I' || dc == 'J';
100        boolean actualFloat = actualType.equals(Float.class)
101                || actualType.equals(Double.class);
102        if (desiredSize == null || actualSize == null) {
103            // They were not numeric types.
104            return false;
105        } else if (desiredInt && actualFloat) {
106            // Floats to integers require a cast.
107            return false;
108        } else {
109            return desiredSize.intValue() >= actualSize.intValue();
110        }
111    }
112
113    /**
114     * Compares the desired type with the actual type and determines if the
115     * actual type can be widened to the desired type. For instance, a byte
116     * can be widened to any other primitive number. This applies only in
117     * the case where the actual type is a Number and the desired type is
118     * a byte, double, float, integer, long, or short. Wrapper classes
119     * (Byte, Double, etc.) are not included.
120     *
121     * @param  desiredType   the desired JNI type signature.
122     * @param  actualType    the actual numeric type to widen.
123     * @return  true if type can be widened to desired type, false otherwise.
124     */
125    public static boolean canWiden(String desiredType, Type actualType) {
126        char dc = desiredType.charAt(0);
127        Integer desiredSize = SIZES_BY_CHAR.get(new Character(dc));
128        Integer actualSize = null;
129        boolean desiredInt = dc == 'B' || dc == 'S' || dc == 'I' || dc == 'J';
130        boolean actualFloat = actualType instanceof FloatType
131                || actualType instanceof DoubleType;
132        if (actualType instanceof ByteType) {
133            actualSize = new Integer(1);
134        } else if (actualType instanceof ShortType) {
135            actualSize = new Integer(2);
136        } else if (actualType instanceof IntegerType) {
137            actualSize = new Integer(4);
138        } else if (actualType instanceof LongType) {
139            actualSize = new Integer(8);
140        } else if (actualType instanceof FloatType) {
141            actualSize = new Integer(4);
142        } else if (actualType instanceof DoubleType) {
143            actualSize = new Integer(8);
144        }
145        if (desiredSize == null || actualSize == null) {
146            // They were not numeric types.
147            return false;
148        } else if (desiredInt && actualFloat) {
149            // Floats to integers require a cast.
150            return false;
151        } else {
152            return desiredSize.intValue() >= actualSize.intValue();
153        }
154    }
155
156    /**
157     * Attempts to cast the value up to the desired type, if this is a
158     * compatible operation.
159     *
160     * @param  type   the desired JNI type signature.
161     * @param  value  the value to be cast.
162     * @return  the cast value, or null if incompatible.
163     */
164    public static Object cast(String type, Object value) {
165        char ch = type.charAt(0);
166        if (ch == 'L') {
167            // See if this is really a primitive type in disguise.
168            if (type.equals("Ljava/lang/Byte;")) {
169                ch = 'B';
170            } else if (type.equals("Ljava/lang/Character;")) {
171                ch = 'C';
172            } else if (type.equals("Ljava/lang/Double;")) {
173                ch = 'D';
174            } else if (type.equals("Ljava/lang/Float;")) {
175                ch = 'F';
176            } else if (type.equals("Ljava/lang/Integer;")) {
177                ch = 'I';
178            } else if (type.equals("Ljava/lang/Long;")) {
179                ch = 'J';
180            } else if (type.equals("Ljava/lang/Short;")) {
181                ch = 'S';
182            }
183            // Else we do not convert the value.
184        }
185
186        // We can cast any number to any other number.
187        if (ch == 'B') {
188            if (value instanceof Number) {
189                return new Byte(((Number) value).byteValue());
190            }
191        } else if (ch == 'C') {
192            if (value instanceof Number) {
193                int i = ((Number) value).intValue();
194                return new Character((char) i);
195            }
196        } else if (ch == 'D') {
197            if (value instanceof Number) {
198                return new Double(((Number) value).doubleValue());
199            }
200        } else if (ch == 'F') {
201            if (value instanceof Number) {
202                return new Float(((Number) value).floatValue());
203            }
204        } else if (ch == 'I') {
205            if (value instanceof Number) {
206                return new Integer(((Number) value).intValue());
207            }
208        } else if (ch == 'J') {
209            if (value instanceof Number) {
210                return new Long(((Number) value).longValue());
211            }
212        } else if (ch == 'S') {
213            if (value instanceof Number) {
214                return new Short(((Number) value).shortValue());
215            }
216        }
217
218        // Else, it was an incompatible translation.
219        return null;
220    }
221
222    /**
223     * Attempts to cast the value up to the desired type, if this is a
224     * compatible operation.
225     *
226     * @param  type   the desired JNI type signature.
227     * @param  value  the value to be cast.
228     * @param  vm     used to create new value objects.
229     * @return  the cast value, or null if incompatible.
230     */
231    public static Value cast(String type, Value value, VirtualMachine vm) {
232        char ch = type.charAt(0);
233        if (ch == 'L') {
234            // See if this is really a primitive type in disguise.
235            if (type.equals("Ljava/lang/Byte;")) {
236                ch = 'B';
237            } else if (type.equals("Ljava/lang/Character;")) {
238                ch = 'C';
239            } else if (type.equals("Ljava/lang/Double;")) {
240                ch = 'D';
241            } else if (type.equals("Ljava/lang/Float;")) {
242                ch = 'F';
243            } else if (type.equals("Ljava/lang/Integer;")) {
244                ch = 'I';
245            } else if (type.equals("Ljava/lang/Long;")) {
246                ch = 'J';
247            } else if (type.equals("Ljava/lang/Short;")) {
248                ch = 'S';
249            }
250            // Else we do not convert the value.
251        }
252
253        // We can cast any number to any other number.
254        if (ch == 'B') {
255            if (value instanceof PrimitiveValue) {
256                return vm.mirrorOf(((PrimitiveValue) value).byteValue());
257            }
258        } else if (ch == 'C') {
259            if (value instanceof PrimitiveValue) {
260                int i = ((PrimitiveValue) value).intValue();
261                return vm.mirrorOf((char) i);
262            }
263        } else if (ch == 'D') {
264            if (value instanceof PrimitiveValue) {
265                return vm.mirrorOf(((PrimitiveValue) value).doubleValue());
266            }
267        } else if (ch == 'F') {
268            if (value instanceof PrimitiveValue) {
269                return vm.mirrorOf(((PrimitiveValue) value).floatValue());
270            }
271        } else if (ch == 'I') {
272            if (value instanceof PrimitiveValue) {
273                return vm.mirrorOf(((PrimitiveValue) value).intValue());
274            }
275        } else if (ch == 'J') {
276            if (value instanceof PrimitiveValue) {
277                return vm.mirrorOf(((PrimitiveValue) value).longValue());
278            }
279        } else if (ch == 'S') {
280            if (value instanceof PrimitiveValue) {
281                return vm.mirrorOf(((PrimitiveValue) value).shortValue());
282            }
283        }
284
285        // Else, it was an incompatible translation.
286        return null;
287    }
288
289    /**
290     * Returns true if the class is assignment compatible with the
291     * provided signature. Compares superclasses and implemented
292     * interfaces to find a match.
293     *
294     * @param  desiredSig  JNI signature of desired type.
295     * @param  actualType  the actual type to compare.
296     * @return  true if the actual type is assignment compatible with the
297     *          desired type.
298     */
299    public static boolean isCompatible(String desiredSig, Class<?> actualType) {
300        // Prime the stack with the initial type.
301        Stack<Class<?>> stack = new Stack<Class<?>>();
302        stack.push(actualType);
303        // Convert the signature to a name (e.g. "Ljava/lang/String;"
304        // becomes "java.lang.String").
305        String desiredName = jniToName(desiredSig);
306
307        while (!stack.empty()) {
308            Class<?> type = stack.pop();
309
310            // Check if the type names match.
311            if (desiredName.equals(type.getName())) {
312                return true;
313            }
314
315            // Push the superclass to the stack.
316            Class<?> superType = type.getSuperclass();
317            if (superType != null) {
318                stack.push(superType);
319            }
320
321            // Push the implementing interfaces to the stack.
322            Class<?>[] interfaces = type.getInterfaces();
323            for (int ii = 0; ii < interfaces.length; ii++) {
324                stack.push(interfaces[ii]);
325            }
326        }
327
328        // That's all, nothing matched.
329        return false;
330    }
331
332    /**
333     * Returns true if the class is assignment compatible with the
334     * provided signature. Compares superclasses and implemented
335     * interfaces to find a match.
336     *
337     * @param  desiredSig  JNI signature of desired type.
338     * @param  actualType  the actual type to compare.
339     * @return  true if the actual type is assignment compatible with the
340     *          desired type.
341     */
342    public static boolean isCompatible(String desiredSig,
343            ReferenceType actualType) {
344        // Prime the stack with the initial type.
345        Stack<ReferenceType> stack = new Stack<ReferenceType>();
346        stack.push(actualType);
347
348        while (!stack.empty()) {
349            ReferenceType type = stack.pop();
350
351            // Check if the type signatures match.
352            if (desiredSig.equals(type.signature())) {
353                return true;
354            }
355
356            // Check for supertypes and implemented interfaces.
357            if (type instanceof ClassType) {
358                // Push the superclass to the stack.
359                ClassType ctype = (ClassType) type;
360                ClassType superType = ctype.superclass();
361                if (superType != null) {
362                    stack.push(superType);
363                }
364
365                // Push the implementing interfaces to the stack.
366                List<InterfaceType> interfaces = ctype.interfaces();
367                for (InterfaceType iface : interfaces) {
368                    stack.push(iface);
369                }
370
371            } else if (type instanceof InterfaceType) {
372                // Push the extended interfaces to the stack.
373                InterfaceType itype = (InterfaceType) type;
374                List<InterfaceType> interfaces = itype.superinterfaces();
375                for (InterfaceType iface : interfaces) {
376                    stack.push(iface);
377                }
378            }
379        }
380
381        // That's all, nothing matched.
382        return false;
383    }
384
385    /**
386     * Converts a JNI type signature for a class to just the class name.
387     * This strips one character from the beginning and end of the given
388     * string, and replaces all occurances of '/' with '.'.
389     *
390     * @param  jni  JNI signature of a class type.
391     * @return  class name.
392     */
393    public static String jniToName(String jni) {
394        return jni.substring(1, jni.length() - 1).replace('/', '.');
395    }
396
397    /**
398     * Converts a JNI type signature to a simple type name.
399     *
400     * @param  sig  type signature (e.g. "Z", "Ljava/net/URL;",
401     *              "Ljava/lang/String;", "[[I").
402     * @param  nb   true to discard array brackets in return value.
403     * @return  type name (e.g. "boolean", "java.net.URL",
404     *          "java.lang.String", "int[][]"), or null if not recognized.
405     */
406    public static String jniToTypeName(String sig, boolean nb) {
407        if (sig == null || sig.trim().isEmpty()) {
408            return null;
409        }
410
411        // Handle multi-dimensional arrays.
412        int arrayDepth = 0;
413        String jni = sig;
414        while (jni.charAt(0) == '[') {
415            arrayDepth++;
416            jni = jni.substring(1);
417        }
418
419        StringBuilder name = new StringBuilder();
420        // Check if it is a primitive type.
421        if (jni.equals("Z")) {
422            name.append("boolean");
423        } else if (jni.equals("B")) {
424            name.append("byte");
425        } else if (jni.equals("C")) {
426            name.append("char");
427        } else if (jni.equals("D")) {
428            name.append("double");
429        } else if (jni.equals("F")) {
430            name.append("float");
431        } else if (jni.equals("I")) {
432            name.append("int");
433        } else if (jni.equals("J")) {
434            name.append("long");
435        } else if (jni.equals("S")) {
436            name.append("short");
437        } else if (jni.equals("V")) {
438            name.append("void");
439
440        } else if (jni.charAt(0) == 'L') {
441            name.append(jniToName(jni));
442        } else {
443            return null;
444        }
445
446        while (!nb && arrayDepth > 0) {
447            name.append("[]");
448            arrayDepth--;
449        }
450        return name.toString();
451    }
452
453    /**
454     * Creates a mirror of the given object in the given VM.
455     *
456     * @param  o   object to make a mirror of.
457     * @param  vm  virtual machine in which to create mirror.
458     * @return  mirrored value, or null if not mirrorable.
459     */
460    public static Value mirrorOf(Object o, VirtualMachine vm) {
461        if (o instanceof String) {
462            return vm.mirrorOf((String) o);
463        } else if (o instanceof Boolean) {
464            return vm.mirrorOf(((Boolean) o).booleanValue());
465        } else if (o instanceof Character) {
466            return vm.mirrorOf(((Character) o).charValue());
467        } else if (o instanceof Double) {
468            return vm.mirrorOf(((Double) o).doubleValue());
469        } else if (o instanceof Float) {
470            return vm.mirrorOf(((Float) o).floatValue());
471        } else if (o instanceof Integer) {
472            return vm.mirrorOf(((Integer) o).intValue());
473        } else if (o instanceof Long) {
474            return vm.mirrorOf(((Long) o).longValue());
475        } else if (o instanceof Short) {
476            return vm.mirrorOf(((Short) o).shortValue());
477        } else if (o instanceof Byte) {
478            return vm.mirrorOf(((Byte) o).byteValue());
479        } else if (o instanceof Value) {
480            return (Value) o;
481        } else {
482            return null;
483        }
484    }
485
486    /**
487     * Converts a class name to a JNI type signature. This simply adds
488     * an 'L' prefix, a ';' suffix, and replaces all occurances of '.'
489     * with '/'.
490     *
491     * @param  name  class name.
492     * @return  JNI signature for class.
493     */
494    public static String nameToJni(String name) {
495        return 'L' + name.replace('.', '/') + ';';
496    }
497
498    /**
499     * Finds the Type for the given signature. If the signature refers
500     * to a class, this method returns the first matching class in the
501     * list returned by the virtual machine.
502     *
503     * @param  sig  signature for which to find type.
504     * @param  vm   virtual machine to search in.
505     * @return  type for signature, or null if unrecognized.
506     */
507    public static Type signatureToType(String sig, VirtualMachine vm) {
508        if (sig == null || sig.trim().isEmpty()) {
509            return null;
510        }
511
512        String _sig = sig;
513        // Strip away the leading array brackets.
514        while (_sig.charAt(0) == '[') {
515            _sig = _sig.substring(1);
516        }
517
518        if (_sig.charAt(0) == 'L') {
519            // The superclass search is done against the given
520            // type (reverse of typecasting).
521            _sig = jniToName(_sig);
522            List<ReferenceType> list = vm.classesByName(_sig);
523            if (list.size() > 0) {
524                return (Type) list.get(0);
525            }
526        } else if (_sig.equals("B")) {
527            return vm.mirrorOf((byte) 0).type();
528        } else if (_sig.equals("C")) {
529            return vm.mirrorOf(' ').type();
530        } else if (_sig.equals("D")) {
531            return vm.mirrorOf(0.0D).type();
532        } else if (_sig.equals("F")) {
533            return vm.mirrorOf(0.0F).type();
534        } else if (_sig.equals("I")) {
535            return vm.mirrorOf(0).type();
536        } else if (_sig.equals("J")) {
537            return vm.mirrorOf(0L).type();
538        } else if (_sig.equals("S")) {
539            return vm.mirrorOf((short) 0).type();
540        } else if (_sig.equals("Z")) {
541            return vm.mirrorOf(false).type();
542        }
543        // Either void or unknown type.
544        return null;
545    }
546
547    /**
548     * Converts a type name into a JNI type signature.
549     *
550     * @param  type  type name (e.g. "boolean", "java.net.URL", "String").
551     * @return  JNI type signature (e.g. "Z", "Ljava/net/URL;",
552     *          "Ljava/lang/String;"), or null if not recognized.
553     */
554    public static String typeNameToJNI(String type) {
555        if (type == null || type.trim().isEmpty()) {
556            return null;
557        }
558
559        // Handle multi-dimensional arrays.
560        int arrayDepth = 0;
561        String _type = type;
562        while (_type.endsWith("[]")) {
563            arrayDepth++;
564            _type = _type.substring(0, _type.length() - 2);
565        }
566
567        String sig = null;
568        // Check if it is a primitive type.
569        if (_type.equals("boolean")) {
570            sig = "Z";
571        } else if (_type.equals("byte")) {
572            sig = "B";
573        } else if (_type.equals("char")) {
574            sig = "C";
575        } else if (_type.equals("double")) {
576            sig = "D";
577        } else if (_type.equals("float")) {
578            sig = "F";
579        } else if (_type.equals("int")) {
580            sig = "I";
581        } else if (_type.equals("long")) {
582            sig = "J";
583        } else if (_type.equals("short")) {
584            sig = "S";
585        } else if (_type.equals("void")) {
586            sig = "V";
587
588        } else {
589            try {
590                // See if it is a core class.
591                Class.forName("java.lang." + _type);
592                sig = "Ljava/lang/" + _type + ';';
593            } catch (ClassNotFoundException cnfe) {
594                // Must be some other class.
595                sig = nameToJni(_type);
596            }
597        }
598
599        while (arrayDepth > 0) {
600            sig = '[' + sig;
601            arrayDepth--;
602        }
603        return sig;
604    }
605
606    /**
607     * Converts a list of type names to a list of JNI type signatures.
608     *
609     * @param  types  type names (e.g. "boolean", "java.net.URL", "String").
610     * @return  JNI type signatures (e.g. "Z", "Ljava/net/URL;",
611     *          "Ljava/lang/String;"); unrecognized names will be
612     *          null entries in the list.
613     */
614    public static List<String> typeNamesToJNI(List<String> types) {
615        List<String> results = new LinkedList<String>();
616        for (String type : types) {
617            results.add(typeNameToJNI(type));
618        }
619        return results;
620    }
621
622    /**
623     * Returns the primitive type signature for the given wrapper type.
624     * That is, if type represents one of the wrapper classes (Byte,
625     * Short, etc) then this method returns the equivalent primitive
626     * type signature ('B', 'S', etc).
627     *
628     * @param  type  the JNI signature of the wrapper class to convert
629     *               to a primitive type signature (e.g. "Ljava/lang/Byte;").
630     * @return  primitive type signature, or empty string if type was not
631     *          a wrapper for a primitive type.
632     */
633    public static String wrapperToPrimitive(String type) {
634        if (type.equals("Ljava/lang/Boolean;")) {
635            return "Z";
636        } else if (type.equals("Ljava/lang/Byte;")) {
637            return "B";
638        } else if (type.equals("Ljava/lang/Character;")) {
639            return "C";
640        } else if (type.equals("Ljava/lang/Double;")) {
641            return "D";
642        } else if (type.equals("Ljava/lang/Float;")) {
643            return "F";
644        } else if (type.equals("Ljava/lang/Integer;")) {
645            return "I";
646        } else if (type.equals("Ljava/lang/Long;")) {
647            return "J";
648        } else if (type.equals("Ljava/lang/Short;")) {
649            return "S";
650        } else if (type.equals("Ljava/lang/Void;")) {
651            return "V";
652        } else {
653            return "";
654        }
655    }
656}