/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

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