1 /* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved. 2 * 3 * This program and the accompanying materials are made available under 4 * the terms of the Common Public License v1.0 which accompanies this distribution, 5 * and is available at http://www.eclipse.org/legal/cpl-v10.html 6 * 7 * $Id: Types.java,v 1.1.1.1 2004/05/09 16:57:50 vlad_r Exp $ 8 */ 9 package com.vladium.jcd.lib; 10 11 import java.io.IOException; 12 import java.lang.reflect.*; 13 14 import com.vladium.jcd.cls.IAccessFlags; 15 16 // ---------------------------------------------------------------------------- 17 /** 18 * Utility methods for manipulating type signatures and descriptors. 19 * 20 * TODO: fix usage of chars in parsers 21 * 22 * @author (C) 2001, Vlad Roubtsov 23 */ 24 public abstract class Types 25 { 26 // public: ................................................................ 27 28 /** 29 * Returns 'c''s package name [does not include trailing '.'] or "" 30 * if 'c' is in the default package. 31 */ getClassPackageName(final Class c)32 public static String getClassPackageName (final Class c) 33 { 34 // TODO: handle array and other types 35 36 final String className = c.getName (); 37 final int lastDot = className.lastIndexOf ('.'); 38 return lastDot >= 0 ? className.substring (0, lastDot) : ""; 39 } 40 41 accessFlagsToString(final int flags, final boolean isClass)42 public static String accessFlagsToString (final int flags, final boolean isClass) 43 { 44 final StringBuffer result = new StringBuffer (); 45 46 boolean first = true; 47 48 if (isClass) 49 { 50 for (int f = 0; f < IAccessFlags.ALL_ACC.length; ++ f) 51 { 52 final int bit = IAccessFlags.ALL_ACC [f]; 53 54 if ((flags & bit) != 0) 55 { 56 if (first) 57 first = false; 58 else 59 result.append (" "); 60 61 if (bit == IAccessFlags.ACC_SUPER) 62 result.append ("super"); 63 else 64 result.append (IAccessFlags.ALL_ACC_NAMES [f]); 65 } 66 } 67 } 68 else 69 { 70 for (int f = 0; f < IAccessFlags.ALL_ACC.length; ++ f) 71 { 72 final int bit = IAccessFlags.ALL_ACC [f]; 73 74 if ((flags & bit) != 0) 75 { 76 if (first) 77 first = false; 78 else 79 result.append (" "); 80 81 result.append (IAccessFlags.ALL_ACC_NAMES [f]); 82 } 83 } 84 } 85 86 return result.toString (); 87 } 88 89 90 /** 91 * Converts Java-styled package/class name to how it would be 92 * represented in the VM.<P> 93 * 94 * Example:<BR> 95 * javaNameToVMName("java.lang.Object") = "java/lang/Object" 96 * 97 * @see #vmNameToJavaName 98 */ javaNameToVMName(final String javaName)99 public static String javaNameToVMName (final String javaName) 100 { 101 if (javaName == null) return null; 102 return javaName.replace ('.', '/'); 103 } 104 105 106 /** 107 * Converts a VM-styled package/class name to how it would be 108 * represented in Java.<P> 109 * 110 * Example:<BR> 111 * vmNameToJavaName("java/lang/Object") = "java.lang.Object" 112 * 113 * @see #javaNameToVMName 114 */ vmNameToJavaName(final String vmName)115 public static String vmNameToJavaName (final String vmName) 116 { 117 if (vmName == null) return null; 118 return vmName.replace ('/', '.'); 119 } 120 121 122 /** 123 * Converts a method signature to its VM descriptor representation. 124 * See $4.3 of the VM spec 1.0 for the descriptor grammar.<P> 125 * 126 * Example:<BR> 127 * signatureToDescriptor(new Object().getClass().getMethod("equals" ,new Class[0])) = "(Ljava/lang/Object;)Z" 128 * <P> 129 * 130 * Equivalent to 131 * <CODE>signatureToDescriptor(method.getParameterTypes (), method.getReturnType ())</CODE>. 132 */ signatureToDescriptor(Method method)133 public static String signatureToDescriptor (Method method) 134 { 135 if (method == null) throw new IllegalArgumentException ("null input: method"); 136 return signatureToDescriptor (method.getParameterTypes (), method.getReturnType ()); 137 } 138 139 140 /** 141 * Converts a method signature (parameter types + return type) to its VM descriptor 142 * representation. See $4.3 of the VM spec 1.0 for the descriptor grammar.<P> 143 */ signatureToDescriptor(Class [] parameterTypes, Class returnType)144 public static String signatureToDescriptor (Class [] parameterTypes, Class returnType) 145 { 146 return new signatureCompiler ().signatureDescriptor (parameterTypes, returnType); 147 } 148 149 150 /** 151 * Converts a type (a Class) to its VM descriptor representation.<P> 152 * 153 * Example:<BR> 154 * typeToDescriptor(Object.class) = "Ljava/lang/Object;" <BR> 155 * typeToDescriptor(boolean.class) = "Z" 156 * <P> 157 * Note the invariant typeToDescriptor(descriptorToType(desc)) == desc. 158 * 159 * @see #descriptorToType 160 */ typeToDescriptor(Class type)161 public static String typeToDescriptor (Class type) 162 { 163 return new signatureCompiler ().typeDescriptor (type); 164 } 165 166 167 /** 168 * Converts a VM descriptor to the corresponding type.<P> 169 * 170 * Example:<BR> 171 * descriptorToType("[[I") = int[][].class <BR> 172 * descriptorToType("B") = byte.class 173 * <P> 174 * Note the invariant descriptorToType(typeToDescriptor(c)) == c. 175 * 176 * @see #descriptorToType 177 */ descriptorToType(String typedescriptor)178 public static Class descriptorToType (String typedescriptor) throws ClassNotFoundException 179 { 180 return new typeDescriptorCompiler ().descriptorToClass (typedescriptor); 181 } 182 183 184 descriptorToReturnType(String methoddescriptor)185 public static String descriptorToReturnType (String methoddescriptor) 186 { 187 final int i1 = methoddescriptor.indexOf ('('); 188 final int i2 = methoddescriptor.lastIndexOf (')'); 189 190 if ((i1 < 0) || (i2 <= 0) || (i1 >= i2) || (i2 >= methoddescriptor.length () - 1)) 191 throw new IllegalArgumentException ("malformed method descriptor: [" + methoddescriptor + "]"); 192 193 return methoddescriptor.substring (i2 + 1); 194 } 195 196 descriptorToParameterTypes(String methoddescriptor)197 public static String [] descriptorToParameterTypes (String methoddescriptor) 198 { 199 //System.out.println ("METHOD DESCRIPTOR: [" + methoddescriptor + "]"); 200 201 try 202 { 203 final methodDescriptorCompiler compiler = new methodDescriptorCompiler (methoddescriptor); 204 compiler.methodDescriptor (); 205 return compiler.getResult (); 206 } 207 catch (IOException e) 208 { 209 throw new IllegalArgumentException ("error parsing [" + methoddescriptor + "]: " + e.toString ()); 210 } 211 212 /* 213 final java.util.Vector _result = new java.util.Vector (); 214 final StringBuffer token = new StringBuffer (); 215 216 char c = '*'; 217 int scan = 0; 218 219 for (int state = 0; state != 4; ) 220 { 221 try 222 { 223 switch (state) 224 { 225 case 0: 226 c = methoddescriptor.charAt (scan++); 227 if (c == '(') 228 state = 1; 229 else 230 throw new IllegalArgumentException ("malformed method descriptor: [" + methoddescriptor + "]"); 231 break; 232 233 case 1: 234 c = methoddescriptor.charAt (scan); 235 switch (c) 236 { 237 case 'B': 238 case 'C': 239 case 'D': 240 case 'F': 241 case 'I': 242 case 'J': 243 case 'S': 244 case 'Z': 245 token.append (c); 246 _result.addElement (token.toString ()); 247 token.setLength (0); 248 scan++; 249 break; 250 251 case 'L': 252 state = 2; 253 token.append (c); 254 scan++; 255 break; 256 257 case '[': 258 state = 3; 259 token.append (c); 260 scan++; 261 break; 262 263 case ')': 264 if (token.length () > 0) 265 { 266 _result.addElement (token.toString ()); 267 token.setLength (0); 268 } 269 state = 4; 270 break; 271 272 273 default: 274 throw new IllegalArgumentException ("[state = " + state + ", c = " + c + "] malformed method descriptor: [" + methoddescriptor + "]"); 275 276 } // end of nested switch 277 break; 278 279 case 2: 280 c = methoddescriptor.charAt (scan++); 281 token.append (c); 282 if (c == ';') 283 { 284 _result.addElement (token.toString ()); 285 token.setLength (0); 286 state = 1; 287 } 288 break; 289 290 case 3: 291 c = methoddescriptor.charAt (scan++); 292 token.append (c); 293 if (c != '[') 294 { 295 state = 1; 296 } 297 break; 298 299 } // end of switch 300 301 //System.out.println ("[state = " + state + ", c = " + c + "]"); 302 } 303 catch (StringIndexOutOfBoundsException e) 304 { 305 throw new IllegalArgumentException ("malformed method descriptor: [" + methoddescriptor + "]"); 306 } 307 } 308 309 String [] result = new String [_result.size ()]; 310 _result.copyInto (result); 311 312 return result; 313 */ 314 } 315 316 signatureToMethodDescriptor(final String [] parameterTypeDescriptors, final String returnTypeDescriptor)317 public static String signatureToMethodDescriptor (final String [] parameterTypeDescriptors, final String returnTypeDescriptor) 318 { 319 final StringBuffer result = new StringBuffer ("("); 320 321 for (int p = 0; p < parameterTypeDescriptors.length; p++) 322 { 323 result.append (parameterTypeDescriptors [p]); 324 } 325 326 result.append (')'); 327 result.append (returnTypeDescriptor); 328 329 return result.toString (); 330 } 331 332 typeDescriptorToUserName(final String typedescriptor)333 public static String typeDescriptorToUserName (final String typedescriptor) 334 { 335 return new typeDescriptorCompiler2 ().descriptorToClass (typedescriptor); 336 } 337 methodDescriptorToUserName(final String methoddescriptor)338 public static String methodDescriptorToUserName (final String methoddescriptor) 339 { 340 final String [] parameterTypes = descriptorToParameterTypes (methoddescriptor); 341 342 final StringBuffer result = new StringBuffer ("("); 343 344 for (int p = 0; p < parameterTypes.length; p++) 345 { 346 //System.out.println ("DESCRIPTOR: [" + parameterTypes [p] + "]"); 347 348 if (p > 0) result.append (", "); 349 350 final String typeUserName = typeDescriptorToUserName (parameterTypes [p]); 351 int lastDot = typeUserName.lastIndexOf ('.'); 352 353 if ((lastDot < 0) || ! "java.lang.".equals (typeUserName.substring (0, lastDot + 1))) 354 result.append (typeUserName); 355 else 356 result.append (typeUserName.substring (lastDot + 1)); 357 } 358 359 result.append (')'); 360 return result.toString (); 361 } 362 fullMethodDescriptorToUserName(final String classJavaName, String methodName, final String methoddescriptor)363 public static String fullMethodDescriptorToUserName (final String classJavaName, String methodName, final String methoddescriptor) 364 { 365 if ("<init>".equals (methodName)) 366 methodName = simpleClassName (classJavaName); 367 if ("<clinit>".equals (methodName)) 368 methodName = "<static class initializer>"; 369 370 return methodName + ' ' + methodDescriptorToUserName (methoddescriptor); 371 } 372 373 // TODO: added most recently fullMethodDescriptorToFullUserName(final String classJavaName, String methodName, final String methoddescriptor)374 public static String fullMethodDescriptorToFullUserName (final String classJavaName, String methodName, final String methoddescriptor) 375 { 376 if ("<init>".equals (methodName)) 377 methodName = simpleClassName (classJavaName); 378 if ("<clinit>".equals (methodName)) 379 methodName = "<static class initializer>"; 380 381 return classJavaName + '.' + methodName + ' ' + methodDescriptorToUserName (methoddescriptor); 382 } 383 384 // protected: ............................................................. 385 386 // package: ............................................................... 387 388 // private: ............................................................... 389 390 simpleClassName(final String classJavaName)391 private static String simpleClassName (final String classJavaName) 392 { 393 int lastDot = classJavaName.lastIndexOf ('.'); 394 395 if (lastDot < 0) 396 return classJavaName; 397 else 398 return classJavaName.substring (lastDot + 1); 399 } 400 401 402 403 private static final class signatureCompiler 404 { signatureDescriptor(Class [] _parameterTypes, Class _returnType)405 String signatureDescriptor (Class [] _parameterTypes, Class _returnType) 406 { 407 emit ('('); parameterTypes (_parameterTypes); emit (')'); returnType (_returnType); 408 409 return m_desc.toString (); 410 } 411 typeDescriptor(Class type)412 String typeDescriptor (Class type) 413 { 414 parameterType (type); 415 416 return m_desc.toString (); 417 } 418 419 parameterTypes(Class [] _parameterTypes)420 private void parameterTypes (Class [] _parameterTypes) 421 { 422 if (_parameterTypes != null) 423 { 424 for (int p = 0; p < _parameterTypes.length; p++) 425 { 426 parameterType (_parameterTypes [p]); 427 } 428 } 429 } 430 431 returnType(Class _returnType)432 private void returnType (Class _returnType) 433 { 434 if ((_returnType == null) || (_returnType == Void.TYPE)) 435 emit ('V'); 436 else 437 parameterType (_returnType); 438 } 439 440 parameterType(Class _parameterType)441 private void parameterType (Class _parameterType) 442 { 443 if (_parameterType != null) 444 { 445 if (_parameterType.isPrimitive ()) // base type: 446 { 447 if (byte.class == _parameterType) emit ('B'); 448 else if (char.class == _parameterType) emit ('C'); 449 else if (double.class == _parameterType) emit ('D'); 450 else if (float.class == _parameterType) emit ('F'); 451 else if (int.class == _parameterType) emit ('I'); 452 else if (long.class == _parameterType) emit ('J'); 453 else if (short.class == _parameterType) emit ('S'); 454 else if (boolean.class == _parameterType) emit ('Z'); 455 } 456 else if (_parameterType.isArray ()) // array type: 457 { 458 emit ('['); parameterType (_parameterType.getComponentType ()); 459 } 460 else // object type: 461 { 462 emit ('L'); emit (javaNameToVMName (_parameterType.getName ())); emit (';'); 463 } 464 } 465 } 466 467 emit(String s)468 private void emit (String s) 469 { 470 m_desc.append (s); 471 } 472 emit(char c)473 private void emit (char c) 474 { 475 m_desc.append (c); 476 } 477 478 479 private StringBuffer m_desc = new StringBuffer (); 480 481 } // end of static class 482 483 484 485 private static class typeDescriptorCompiler 486 { 487 /* 488 NOTE: the following would be a very simple solution to this problem 489 490 Class.forName ('[' + descriptor).getComponentType (); 491 492 except it only works in MS VM. 493 */ 494 descriptorToClass(String typedescriptor)495 Class descriptorToClass (String typedescriptor) throws ClassNotFoundException 496 { 497 char first = typedescriptor.charAt (0); 498 499 if (first == '[') 500 // array type: 501 return arrayOf (typedescriptor.substring (1)); 502 else if (first == 'L') 503 // object type: 504 return Class.forName (vmNameToJavaName (typedescriptor.substring (1, typedescriptor.length() - 1))); 505 else // primitive type 506 { 507 return primitive (first); 508 } 509 } 510 511 arrayOf(String typedescriptor)512 Class arrayOf (String typedescriptor) throws ClassNotFoundException 513 { 514 char first = typedescriptor.charAt (0); 515 Class component; 516 517 if (first == '[') 518 // array type: 519 component = arrayOf (typedescriptor.substring (1)); 520 else if (first == 'L') 521 // object type: 522 component = Class.forName (vmNameToJavaName (typedescriptor.substring (1, typedescriptor.length() - 1))); 523 else // primitive type 524 { 525 component = primitive (first); 526 } 527 528 Object array = Array.newInstance (component, 0); 529 return array.getClass (); 530 } 531 532 primitive(char c)533 Class primitive (char c) throws ClassNotFoundException 534 { 535 if (c == 'B') return byte.class; 536 else if (c == 'C') return char.class; 537 else if (c == 'D') return double.class; 538 else if (c == 'F') return float.class; 539 else if (c == 'I') return int.class; 540 else if (c == 'J') return long.class; 541 else if (c == 'S') return short.class; 542 else if (c == 'Z') return boolean.class; 543 else throw new ClassNotFoundException ("unknown base type: " + c); 544 } 545 546 } // end of static class 547 548 549 private static class typeDescriptorCompiler2 550 { descriptorToClass(String typedescriptor)551 String descriptorToClass (String typedescriptor) 552 { 553 //System.out.println ("typedesc1 -> " + typedescriptor); 554 555 char first = typedescriptor.charAt (0); 556 557 if (first == '[') 558 // array type: 559 return arrayOf (typedescriptor.substring (1)); 560 else if (first == 'L') 561 // object type: 562 return vmNameToJavaName (typedescriptor.substring (1, typedescriptor.length() - 1)); 563 else // primitive type 564 return primitive (first); 565 } 566 567 arrayOf(String typedescriptor)568 String arrayOf (String typedescriptor) 569 { 570 //System.out.println ("typedesc2 -> " + typedescriptor); 571 572 char first = typedescriptor.charAt (0); 573 String component; 574 575 if (first == '[') 576 // array type: 577 component = arrayOf (typedescriptor.substring (1)); 578 else if (first == 'L') 579 // object type: 580 component = vmNameToJavaName (typedescriptor.substring (1, typedescriptor.length() - 1)); 581 else // primitive type 582 component = primitive (first); 583 584 String array = component + " []"; 585 return array; 586 } 587 588 primitive(char c)589 String primitive (char c) 590 { 591 switch (c) 592 { 593 case 'B': return "byte"; 594 case 'C': return "char"; 595 case 'D': return "double"; 596 case 'F': return "float"; 597 case 'I': return "int"; 598 case 'J': return "long"; 599 case 'S': return "short"; 600 case 'Z': return "boolean"; 601 default: 602 throw new IllegalArgumentException ("unknown primitive: " + c); 603 } 604 } 605 606 } // end of static class 607 608 609 private static class methodDescriptorCompiler 610 { methodDescriptorCompiler(String methoddescriptor)611 methodDescriptorCompiler (String methoddescriptor) 612 { 613 m_in = new java.io.PushbackReader (new java.io.StringReader (methoddescriptor)); 614 } 615 getResult()616 String [] getResult () 617 { 618 final String [] result = new String [m_result.size ()]; 619 m_result.toArray (result); 620 621 return result; 622 } 623 methodDescriptor()624 void methodDescriptor () throws IOException 625 { 626 consume ('('); 627 628 char c; 629 while ((c = (char) m_in.read ()) != ')') 630 { 631 m_in.unread (c); 632 parameterDescriptor (); 633 } 634 returnDescriptor (); 635 } 636 parameterDescriptor()637 void parameterDescriptor () throws IOException 638 { 639 fieldType (); 640 newToken (); 641 } 642 returnDescriptor()643 void returnDescriptor () throws IOException 644 { 645 char c = (char) m_in.read (); 646 647 switch (c) 648 { 649 case 'V': 650 m_token.append (c); 651 break; 652 653 default: 654 m_in.unread (c); 655 fieldType (); 656 657 } 658 // ignore return type for now: newToken (); 659 } 660 componentType()661 void componentType () throws IOException 662 { 663 fieldType (); 664 } 665 objectType()666 void objectType () throws IOException 667 { 668 consume ('L'); 669 m_token.append ('L'); 670 671 char c; 672 while ((c = (char) m_in.read ()) != ';') 673 { 674 m_token.append (c); 675 } 676 m_token.append (';'); 677 } 678 arrayType()679 void arrayType () throws IOException 680 { 681 consume ('['); 682 m_token.append ('['); 683 684 componentType (); 685 } 686 fieldType()687 void fieldType () throws IOException 688 { 689 char c = (char) m_in.read (); 690 m_in.unread (c); 691 692 switch (c) 693 { 694 case 'L': 695 objectType (); 696 break; 697 698 case '[': 699 arrayType (); 700 break; 701 702 default: 703 baseType (); 704 break; 705 } 706 } 707 708 baseType()709 void baseType () throws IOException 710 { 711 char c = (char) m_in.read (); 712 713 switch (c) 714 { 715 case 'B': 716 case 'C': 717 case 'D': 718 case 'F': 719 case 'I': 720 case 'J': 721 case 'S': 722 case 'Z': 723 m_token.append (c); 724 break; 725 726 default: 727 throw new IllegalArgumentException ("unknown base type: " + c); 728 } 729 } 730 731 consume(char expected)732 private void consume (char expected) throws IOException 733 { 734 char c = (char) m_in.read (); 735 736 if (c != expected) 737 throw new IllegalArgumentException ("consumed '" + c + "' while expecting '" + expected + "'"); 738 } 739 740 741 newToken()742 private void newToken () 743 { 744 //System.out.println ("NEW TOKEN [" + m_token.toString () + "]"); 745 746 m_result.add (m_token.toString ()); 747 m_token.setLength (0); 748 } 749 750 final java.util.List m_result = new java.util.ArrayList (); 751 private StringBuffer m_token = new StringBuffer (); 752 private java.io.PushbackReader m_in; 753 } // end of nested class 754 755 } // end of class 756 // ---------------------------------------------------------------------------- 757