/core/src/com/bluemarsh/jswat/core/util/Types.java
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}