1 /* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the Free 9 * Software Foundation; either version 2 of the License, or (at your option) 10 * any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, write to the Free Software Foundation, Inc., 19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21 package proguard.classfile.util; 22 23 import proguard.classfile.ClassConstants; 24 25 import java.util.List; 26 27 /** 28 * Utility methods for converting between internal and external representations 29 * of names and descriptions. 30 * 31 * @author Eric Lafortune 32 */ 33 public class ClassUtil 34 { 35 private static final String EMPTY_STRING = ""; 36 37 38 /** 39 * Checks whether the given class magic number is correct. 40 * @param magicNumber the magic number. 41 * @throws UnsupportedOperationException when the magic number is incorrect. 42 */ checkMagicNumber(int magicNumber)43 public static void checkMagicNumber(int magicNumber) throws UnsupportedOperationException 44 { 45 if (magicNumber != ClassConstants.MAGIC) 46 { 47 throw new UnsupportedOperationException("Invalid magic number ["+Integer.toHexString(magicNumber)+"] in class"); 48 } 49 } 50 51 52 /** 53 * Returns the combined class version number. 54 * @param majorVersion the major part of the class version number. 55 * @param minorVersion the minor part of the class version number. 56 * @return the combined class version number. 57 */ internalClassVersion(int majorVersion, int minorVersion)58 public static int internalClassVersion(int majorVersion, int minorVersion) 59 { 60 return (majorVersion << 16) | minorVersion; 61 } 62 63 64 /** 65 * Returns the major part of the given class version number. 66 * @param classVersion the combined class version number. 67 * @return the major part of the class version number. 68 */ internalMajorClassVersion(int classVersion)69 public static int internalMajorClassVersion(int classVersion) 70 { 71 return classVersion >>> 16; 72 } 73 74 75 /** 76 * Returns the internal class version number. 77 * @param classVersion the external class version number. 78 * @return the internal class version number. 79 */ internalMinorClassVersion(int classVersion)80 public static int internalMinorClassVersion(int classVersion) 81 { 82 return classVersion & 0xffff; 83 } 84 85 86 /** 87 * Returns the internal class version number. 88 * @param classVersion the external class version number. 89 * @return the internal class version number. 90 */ internalClassVersion(String classVersion)91 public static int internalClassVersion(String classVersion) 92 { 93 return 94 classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_0) || 95 classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_1) ? ClassConstants.INTERNAL_CLASS_VERSION_1_0 : 96 classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_2) ? ClassConstants.INTERNAL_CLASS_VERSION_1_2 : 97 classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_3) ? ClassConstants.INTERNAL_CLASS_VERSION_1_3 : 98 classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_4) ? ClassConstants.INTERNAL_CLASS_VERSION_1_4 : 99 classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_5_ALIAS) || 100 classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_5) ? ClassConstants.INTERNAL_CLASS_VERSION_1_5 : 101 classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_6_ALIAS) || 102 classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_6) ? ClassConstants.INTERNAL_CLASS_VERSION_1_6 : 103 0; 104 } 105 106 107 /** 108 * Returns the minor part of the given class version number. 109 * @param classVersion the combined class version number. 110 * @return the minor part of the class version number. 111 */ externalClassVersion(int classVersion)112 public static String externalClassVersion(int classVersion) 113 { 114 switch (classVersion) 115 { 116 case ClassConstants.INTERNAL_CLASS_VERSION_1_0: return ClassConstants.EXTERNAL_CLASS_VERSION_1_0; 117 case ClassConstants.INTERNAL_CLASS_VERSION_1_2: return ClassConstants.EXTERNAL_CLASS_VERSION_1_2; 118 case ClassConstants.INTERNAL_CLASS_VERSION_1_3: return ClassConstants.EXTERNAL_CLASS_VERSION_1_3; 119 case ClassConstants.INTERNAL_CLASS_VERSION_1_4: return ClassConstants.EXTERNAL_CLASS_VERSION_1_4; 120 case ClassConstants.INTERNAL_CLASS_VERSION_1_5: return ClassConstants.EXTERNAL_CLASS_VERSION_1_5; 121 case ClassConstants.INTERNAL_CLASS_VERSION_1_6: return ClassConstants.EXTERNAL_CLASS_VERSION_1_6; 122 default: return null; 123 } 124 } 125 126 127 /** 128 * Checks whether the given class version number is supported. 129 * @param classVersion the combined class version number. 130 * @throws UnsupportedOperationException when the version is not supported. 131 */ checkVersionNumbers(int classVersion)132 public static void checkVersionNumbers(int classVersion) throws UnsupportedOperationException 133 { 134 if (classVersion < ClassConstants.INTERNAL_CLASS_VERSION_1_0 || 135 classVersion > ClassConstants.INTERNAL_CLASS_VERSION_1_6) 136 { 137 throw new UnsupportedOperationException("Unsupported version number ["+ 138 internalMajorClassVersion(classVersion)+"."+ 139 internalMinorClassVersion(classVersion)+"] for class format"); 140 } 141 } 142 143 144 /** 145 * Converts an external class name into an internal class name. 146 * @param externalClassName the external class name, 147 * e.g. "<code>java.lang.Object</code>" 148 * @return the internal class name, 149 * e.g. "<code>java/lang/Object</code>". 150 */ internalClassName(String externalClassName)151 public static String internalClassName(String externalClassName) 152 { 153 return externalClassName.replace(ClassConstants.EXTERNAL_PACKAGE_SEPARATOR, 154 ClassConstants.INTERNAL_PACKAGE_SEPARATOR); 155 } 156 157 158 /** 159 * Converts an internal class description into an external class description. 160 * @param accessFlags the access flags of the class. 161 * @param internalClassName the internal class name, 162 * e.g. "<code>java/lang/Object</code>". 163 * @return the external class description, 164 * e.g. "<code>public java.lang.Object</code>". 165 */ externalFullClassDescription(int accessFlags, String internalClassName)166 public static String externalFullClassDescription(int accessFlags, 167 String internalClassName) 168 { 169 return externalClassAccessFlags(accessFlags) + 170 externalClassName(internalClassName); 171 } 172 173 174 /** 175 * Converts an internal class name into an external class name. 176 * @param internalClassName the internal class name, 177 * e.g. "<code>java/lang/Object</code>". 178 * @return the external class name, 179 * e.g. "<code>java.lang.Object</code>". 180 */ externalClassName(String internalClassName)181 public static String externalClassName(String internalClassName) 182 { 183 return //internalClassName.startsWith(ClassConstants.INTERNAL_PACKAGE_JAVA_LANG) && 184 //internalClassName.indexOf(ClassConstants.INTERNAL_PACKAGE_SEPARATOR, ClassConstants.INTERNAL_PACKAGE_JAVA_LANG.length() + 1) < 0 ? 185 //internalClassName.substring(ClassConstants.INTERNAL_PACKAGE_JAVA_LANG.length()) : 186 internalClassName.replace(ClassConstants.INTERNAL_PACKAGE_SEPARATOR, 187 ClassConstants.EXTERNAL_PACKAGE_SEPARATOR); 188 } 189 190 191 /** 192 * Converts an internal class name into an external short class name, without 193 * package specification. 194 * @param externalClassName the external class name, 195 * e.g. "<code>java.lang.Object</code>" 196 * @return the external short class name, 197 * e.g. "<code>Object</code>". 198 */ externalShortClassName(String externalClassName)199 public static String externalShortClassName(String externalClassName) 200 { 201 int index = externalClassName.lastIndexOf(ClassConstants.EXTERNAL_PACKAGE_SEPARATOR); 202 return externalClassName.substring(index+1); 203 } 204 205 206 /** 207 * Returns whether the given internal type is an array type. 208 * @param internalType the internal type, 209 * e.g. "<code>[[Ljava/lang/Object;</code>". 210 * @return <code>true</code> if the given type is an array type, 211 * <code>false</code> otherwise. 212 */ isInternalArrayType(String internalType)213 public static boolean isInternalArrayType(String internalType) 214 { 215 return internalType.length() > 1 && 216 internalType.charAt(0) == ClassConstants.INTERNAL_TYPE_ARRAY; 217 } 218 219 220 /** 221 * Returns the number of dimensions of the given internal type. 222 * @param internalType the internal type, 223 * e.g. "<code>[[Ljava/lang/Object;</code>". 224 * @return the number of dimensions, e.g. 2. 225 */ internalArrayTypeDimensionCount(String internalType)226 public static int internalArrayTypeDimensionCount(String internalType) 227 { 228 int dimensions = 0; 229 while (internalType.charAt(dimensions) == ClassConstants.INTERNAL_TYPE_ARRAY) 230 { 231 dimensions++; 232 } 233 234 return dimensions; 235 } 236 237 238 /** 239 * Returns whether the given internal class name is one of the interfaces 240 * that is implemented by all array types. These class names are 241 * "<code>java/lang/Object</code>", "<code>java/lang/Cloneable</code>", and 242 * "<code>java/io/Serializable</code>" 243 * @param internalClassName the internal class name, 244 * e.g. "<code>java/lang/Object</code>". 245 * @return <code>true</code> if the given type is an array interface name, 246 * <code>false</code> otherwise. 247 */ isInternalArrayInterfaceName(String internalClassName)248 public static boolean isInternalArrayInterfaceName(String internalClassName) 249 { 250 return ClassConstants.INTERNAL_NAME_JAVA_LANG_OBJECT.equals(internalClassName) || 251 ClassConstants.INTERNAL_NAME_JAVA_LANG_CLONEABLE.equals(internalClassName) || 252 ClassConstants.INTERNAL_NAME_JAVA_IO_SERIALIZABLE.equals(internalClassName); 253 } 254 255 256 /** 257 * Returns whether the given internal type is a plain primitive type 258 * (not void). 259 * @param internalType the internal type, 260 * e.g. "<code>I</code>". 261 * @return <code>true</code> if the given type is a class type, 262 * <code>false</code> otherwise. 263 */ isInternalPrimitiveType(char internalType)264 public static boolean isInternalPrimitiveType(char internalType) 265 { 266 return internalType == ClassConstants.INTERNAL_TYPE_BOOLEAN || 267 internalType == ClassConstants.INTERNAL_TYPE_BYTE || 268 internalType == ClassConstants.INTERNAL_TYPE_CHAR || 269 internalType == ClassConstants.INTERNAL_TYPE_SHORT || 270 internalType == ClassConstants.INTERNAL_TYPE_INT || 271 internalType == ClassConstants.INTERNAL_TYPE_FLOAT || 272 internalType == ClassConstants.INTERNAL_TYPE_LONG || 273 internalType == ClassConstants.INTERNAL_TYPE_DOUBLE; 274 } 275 276 277 /** 278 * Returns whether the given internal type is a primitive Category 2 type. 279 * @param internalType the internal type, 280 * e.g. "<code>L</code>". 281 * @return <code>true</code> if the given type is a Category 2 type, 282 * <code>false</code> otherwise. 283 */ isInternalCategory2Type(String internalType)284 public static boolean isInternalCategory2Type(String internalType) 285 { 286 return internalType.length() == 1 && 287 (internalType.charAt(0) == ClassConstants.INTERNAL_TYPE_LONG || 288 internalType.charAt(0) == ClassConstants.INTERNAL_TYPE_DOUBLE); 289 } 290 291 292 /** 293 * Returns whether the given internal type is a plain class type 294 * (including an array type of a plain class type). 295 * @param internalType the internal type, 296 * e.g. "<code>Ljava/lang/Object;</code>". 297 * @return <code>true</code> if the given type is a class type, 298 * <code>false</code> otherwise. 299 */ isInternalClassType(String internalType)300 public static boolean isInternalClassType(String internalType) 301 { 302 int length = internalType.length(); 303 return length > 1 && 304 // internalType.charAt(0) == ClassConstants.INTERNAL_TYPE_CLASS_START && 305 internalType.charAt(length-1) == ClassConstants.INTERNAL_TYPE_CLASS_END; 306 } 307 308 309 /** 310 * Returns the internal type of a given class name. 311 * @param internalClassName the internal class name, 312 * e.g. "<code>java/lang/Object</code>". 313 * @return the internal type, 314 * e.g. "<code>Ljava/lang/Object;</code>". 315 */ internalTypeFromClassName(String internalClassName)316 public static String internalTypeFromClassName(String internalClassName) 317 { 318 return internalArrayTypeFromClassName(internalClassName, 0); 319 } 320 321 322 /** 323 * Returns the internal array type of a given class name with a given number 324 * of dimensions. If the number of dimensions is 0, the class name itself is 325 * returned. 326 * @param internalClassName the internal class name, 327 * e.g. "<code>java/lang/Object</code>". 328 * @param dimensionCount the number of array dimensions. 329 * @return the internal array type of the array elements, 330 * e.g. "<code>Ljava/lang/Object;</code>". 331 */ internalArrayTypeFromClassName(String internalClassName, int dimensionCount)332 public static String internalArrayTypeFromClassName(String internalClassName, 333 int dimensionCount) 334 { 335 StringBuffer buffer = new StringBuffer(internalClassName.length() + dimensionCount + 2); 336 337 for (int dimension = 0; dimension < dimensionCount; dimension++) 338 { 339 buffer.append(ClassConstants.INTERNAL_TYPE_ARRAY); 340 } 341 342 return buffer.append(ClassConstants.INTERNAL_TYPE_CLASS_START) 343 .append(internalClassName) 344 .append(ClassConstants.INTERNAL_TYPE_CLASS_END) 345 .toString(); 346 } 347 348 349 /** 350 * Returns the internal element type of a given internal array type. 351 * @param internalArrayType the internal array type, 352 * e.g. "<code>[[Ljava/lang/Object;</code>" or 353 * "<code>[I</code>". 354 * @return the internal type of the array elements, 355 * e.g. "<code>Ljava/lang/Object;</code>" or 356 * "<code>I</code>". 357 */ internalTypeFromArrayType(String internalArrayType)358 public static String internalTypeFromArrayType(String internalArrayType) 359 { 360 int index = internalArrayType.lastIndexOf(ClassConstants.INTERNAL_TYPE_ARRAY); 361 return internalArrayType.substring(index+1); 362 } 363 364 365 /** 366 * Returns the internal class name of a given internal class type 367 * (including an array type). Types involving primitive types are returned 368 * unchanged. 369 * @param internalClassType the internal class type, 370 * e.g. "<code>[Ljava/lang/Object;</code>", 371 * "<code>Ljava/lang/Object;</code>", or 372 * "<code>java/lang/Object</code>". 373 * @return the internal class name, 374 * e.g. "<code>java/lang/Object</code>". 375 */ internalClassNameFromClassType(String internalClassType)376 public static String internalClassNameFromClassType(String internalClassType) 377 { 378 return isInternalClassType(internalClassType) ? 379 internalClassType.substring(internalClassType.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_START)+1, 380 internalClassType.length()-1) : 381 internalClassType; 382 } 383 384 385 /** 386 * Returns the internal class name of any given internal descriptor type, 387 * disregarding array prefixes. 388 * @param internalClassType the internal class type, 389 * e.g. "<code>Ljava/lang/Object;</code>" or 390 * "<code>[[I</code>". 391 * @return the internal class name, 392 * e.g. "<code>java/lang/Object</code>" or 393 * <code>null</code>. 394 */ internalClassNameFromType(String internalClassType)395 public static String internalClassNameFromType(String internalClassType) 396 { 397 if (!isInternalClassType(internalClassType)) 398 { 399 return null; 400 } 401 402 // Is it an array type? 403 if (isInternalArrayType(internalClassType)) 404 { 405 internalClassType = internalTypeFromArrayType(internalClassType); 406 } 407 408 return internalClassNameFromClassType(internalClassType); 409 } 410 411 412 /** 413 * Returns the internal type of the given internal method descriptor. 414 * @param internalMethodDescriptor the internal method descriptor, 415 * e.g. "<code>(II)Z</code>". 416 * @return the internal return type, 417 * e.g. "<code>Z</code>". 418 */ internalMethodReturnType(String internalMethodDescriptor)419 public static String internalMethodReturnType(String internalMethodDescriptor) 420 { 421 int index = internalMethodDescriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); 422 return internalMethodDescriptor.substring(index + 1); 423 } 424 425 426 /** 427 * Returns the number of parameters of the given internal method descriptor. 428 * @param internalMethodDescriptor the internal method descriptor, 429 * e.g. "<code>(ID)Z</code>". 430 * @return the number of parameters, 431 * e.g. 2. 432 */ internalMethodParameterCount(String internalMethodDescriptor)433 public static int internalMethodParameterCount(String internalMethodDescriptor) 434 { 435 InternalTypeEnumeration internalTypeEnumeration = 436 new InternalTypeEnumeration(internalMethodDescriptor); 437 438 int counter = 0; 439 while (internalTypeEnumeration.hasMoreTypes()) 440 { 441 internalTypeEnumeration.nextType(); 442 443 counter++; 444 } 445 446 return counter; 447 } 448 449 450 /** 451 * Returns the size taken up on the stack by the parameters of the given 452 * internal method descriptor. This accounts for long and double parameters 453 * taking up two entries. 454 * @param internalMethodDescriptor the internal method descriptor, 455 * e.g. "<code>(ID)Z</code>". 456 * @return the size taken up on the stack, 457 * e.g. 3. 458 */ internalMethodParameterSize(String internalMethodDescriptor)459 public static int internalMethodParameterSize(String internalMethodDescriptor) 460 { 461 return internalMethodParameterSize(internalMethodDescriptor, true); 462 } 463 464 465 /** 466 * Returns the size taken up on the stack by the parameters of the given 467 * internal method descriptor. This accounts for long and double parameters 468 * taking up two entries, and a non-static method taking up an additional 469 * entry. 470 * @param internalMethodDescriptor the internal method descriptor, 471 * e.g. "<code>(ID)Z</code>". 472 * @param accessFlags the access flags of the method, 473 * e.g. 0. 474 * @return the size taken up on the stack, 475 * e.g. 4. 476 */ internalMethodParameterSize(String internalMethodDescriptor, int accessFlags)477 public static int internalMethodParameterSize(String internalMethodDescriptor, 478 int accessFlags) 479 { 480 return internalMethodParameterSize(internalMethodDescriptor, 481 (accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0); 482 } 483 484 485 /** 486 * Returns the size taken up on the stack by the parameters of the given 487 * internal method descriptor. This accounts for long and double parameters 488 * taking up two spaces, and a non-static method taking up an additional 489 * entry. 490 * @param internalMethodDescriptor the internal method descriptor, 491 * e.g. "<code>(ID)Z</code>". 492 * @param isStatic specifies whether the method is static, 493 * e.g. false. 494 * @return the size taken up on the stack, 495 * e.g. 4. 496 */ internalMethodParameterSize(String internalMethodDescriptor, boolean isStatic)497 public static int internalMethodParameterSize(String internalMethodDescriptor, 498 boolean isStatic) 499 { 500 InternalTypeEnumeration internalTypeEnumeration = 501 new InternalTypeEnumeration(internalMethodDescriptor); 502 503 int size = isStatic ? 0 : 1; 504 while (internalTypeEnumeration.hasMoreTypes()) 505 { 506 String internalType = internalTypeEnumeration.nextType(); 507 508 size += internalTypeSize(internalType); 509 } 510 511 return size; 512 } 513 514 515 /** 516 * Returns the size taken up on the stack by the given internal type. 517 * The size is 1, except for long and double types, for which it is 2, 518 * and for the void type, for which 0 is returned. 519 * @param internalType the internal type, 520 * e.g. "<code>I</code>". 521 * @return the size taken up on the stack, 522 * e.g. 1. 523 */ internalTypeSize(String internalType)524 public static int internalTypeSize(String internalType) 525 { 526 if (internalType.length() == 1) 527 { 528 char internalPrimitiveType = internalType.charAt(0); 529 if (internalPrimitiveType == ClassConstants.INTERNAL_TYPE_LONG || 530 internalPrimitiveType == ClassConstants.INTERNAL_TYPE_DOUBLE) 531 { 532 return 2; 533 } 534 else if (internalPrimitiveType == ClassConstants.INTERNAL_TYPE_VOID) 535 { 536 return 0; 537 } 538 } 539 540 return 1; 541 } 542 543 544 /** 545 * Converts an external type into an internal type. 546 * @param externalType the external type, 547 * e.g. "<code>java.lang.Object[][]</code>" or 548 * "<code>int[]</code>". 549 * @return the internal type, 550 * e.g. "<code>[[Ljava/lang/Object;</code>" or 551 * "<code>[I</code>". 552 */ internalType(String externalType)553 public static String internalType(String externalType) 554 { 555 // Strip the array part, if any. 556 int dimensionCount = externalArrayTypeDimensionCount(externalType); 557 if (dimensionCount > 0) 558 { 559 externalType = externalType.substring(0, externalType.length() - dimensionCount * ClassConstants.EXTERNAL_TYPE_ARRAY.length()); 560 } 561 562 // Analyze the actual type part. 563 char internalTypeChar = 564 externalType.equals(ClassConstants.EXTERNAL_TYPE_VOID ) ? 565 ClassConstants.INTERNAL_TYPE_VOID : 566 externalType.equals(ClassConstants.EXTERNAL_TYPE_BOOLEAN) ? 567 ClassConstants.INTERNAL_TYPE_BOOLEAN : 568 externalType.equals(ClassConstants.EXTERNAL_TYPE_BYTE ) ? 569 ClassConstants.INTERNAL_TYPE_BYTE : 570 externalType.equals(ClassConstants.EXTERNAL_TYPE_CHAR ) ? 571 ClassConstants.INTERNAL_TYPE_CHAR : 572 externalType.equals(ClassConstants.EXTERNAL_TYPE_SHORT ) ? 573 ClassConstants.INTERNAL_TYPE_SHORT : 574 externalType.equals(ClassConstants.EXTERNAL_TYPE_INT ) ? 575 ClassConstants.INTERNAL_TYPE_INT : 576 externalType.equals(ClassConstants.EXTERNAL_TYPE_FLOAT ) ? 577 ClassConstants.INTERNAL_TYPE_FLOAT : 578 externalType.equals(ClassConstants.EXTERNAL_TYPE_LONG ) ? 579 ClassConstants.INTERNAL_TYPE_LONG : 580 externalType.equals(ClassConstants.EXTERNAL_TYPE_DOUBLE ) ? 581 ClassConstants.INTERNAL_TYPE_DOUBLE : 582 externalType.equals("%" ) ? 583 '%' : 584 (char)0; 585 586 String internalType = 587 internalTypeChar != 0 ? String.valueOf(internalTypeChar) : 588 ClassConstants.INTERNAL_TYPE_CLASS_START + 589 internalClassName(externalType) + 590 ClassConstants.INTERNAL_TYPE_CLASS_END; 591 592 // Prepend the array part, if any. 593 for (int count = 0; count < dimensionCount; count++) 594 { 595 internalType = ClassConstants.INTERNAL_TYPE_ARRAY + internalType; 596 } 597 598 return internalType; 599 } 600 601 602 /** 603 * Returns the number of dimensions of the given external type. 604 * @param externalType the external type, 605 * e.g. "<code>[[Ljava/lang/Object;</code>". 606 * @return the number of dimensions, e.g. 2. 607 */ externalArrayTypeDimensionCount(String externalType)608 public static int externalArrayTypeDimensionCount(String externalType) 609 { 610 int dimensions = 0; 611 int length = ClassConstants.EXTERNAL_TYPE_ARRAY.length(); 612 int offset = externalType.length() - length; 613 while (externalType.regionMatches(offset, 614 ClassConstants.EXTERNAL_TYPE_ARRAY, 615 0, 616 length)) 617 { 618 dimensions++; 619 offset -= length; 620 } 621 622 return dimensions; 623 } 624 625 626 /** 627 * Converts an internal type into an external type. 628 * @param internalType the internal type, 629 * e.g. "<code>[[Ljava/lang/Object;</code>" or 630 * "<code>[I</code>". 631 * @return the external type, 632 * e.g. "<code>java.lang.Object[][]</code>" or 633 * "<code>int[]</code>". 634 */ externalType(String internalType)635 public static String externalType(String internalType) 636 { 637 // Strip the array part, if any. 638 int dimensionCount = internalArrayTypeDimensionCount(internalType); 639 if (dimensionCount > 0) 640 { 641 internalType = internalType.substring(dimensionCount); 642 } 643 644 // Analyze the actual type part. 645 char internalTypeChar = internalType.charAt(0); 646 647 String externalType = 648 internalTypeChar == ClassConstants.INTERNAL_TYPE_VOID ? 649 ClassConstants.EXTERNAL_TYPE_VOID : 650 internalTypeChar == ClassConstants.INTERNAL_TYPE_BOOLEAN ? 651 ClassConstants.EXTERNAL_TYPE_BOOLEAN : 652 internalTypeChar == ClassConstants.INTERNAL_TYPE_BYTE ? 653 ClassConstants.EXTERNAL_TYPE_BYTE : 654 internalTypeChar == ClassConstants.INTERNAL_TYPE_CHAR ? 655 ClassConstants.EXTERNAL_TYPE_CHAR : 656 internalTypeChar == ClassConstants.INTERNAL_TYPE_SHORT ? 657 ClassConstants.EXTERNAL_TYPE_SHORT : 658 internalTypeChar == ClassConstants.INTERNAL_TYPE_INT ? 659 ClassConstants.EXTERNAL_TYPE_INT : 660 internalTypeChar == ClassConstants.INTERNAL_TYPE_FLOAT ? 661 ClassConstants.EXTERNAL_TYPE_FLOAT : 662 internalTypeChar == ClassConstants.INTERNAL_TYPE_LONG ? 663 ClassConstants.EXTERNAL_TYPE_LONG : 664 internalTypeChar == ClassConstants.INTERNAL_TYPE_DOUBLE ? 665 ClassConstants.EXTERNAL_TYPE_DOUBLE : 666 internalTypeChar == '%' ? 667 "%" : 668 internalTypeChar == ClassConstants.INTERNAL_TYPE_CLASS_START ? 669 externalClassName(internalType.substring(1, internalType.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_END))) : 670 null; 671 672 if (externalType == null) 673 { 674 throw new IllegalArgumentException("Unknown type ["+internalType+"]"); 675 } 676 677 // Append the array part, if any. 678 for (int count = 0; count < dimensionCount; count++) 679 { 680 externalType += ClassConstants.EXTERNAL_TYPE_ARRAY; 681 } 682 683 return externalType; 684 } 685 686 687 /** 688 * Returns whether the given internal descriptor String represents a method 689 * descriptor. 690 * @param internalDescriptor the internal descriptor String, 691 * e.g. "<code>(II)Z</code>". 692 * @return <code>true</code> if the given String is a method descriptor, 693 * <code>false</code> otherwise. 694 */ isInternalMethodDescriptor(String internalDescriptor)695 public static boolean isInternalMethodDescriptor(String internalDescriptor) 696 { 697 return internalDescriptor.charAt(0) == ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN; 698 } 699 700 701 /** 702 * Returns whether the given member String represents an external method 703 * name with arguments. 704 * @param externalMemberNameAndArguments the external member String, 705 * e.g. "<code>myField</code>" or 706 * e.g. "<code>myMethod(int,int)</code>". 707 * @return <code>true</code> if the given String refers to a method, 708 * <code>false</code> otherwise. 709 */ isExternalMethodNameAndArguments(String externalMemberNameAndArguments)710 public static boolean isExternalMethodNameAndArguments(String externalMemberNameAndArguments) 711 { 712 return externalMemberNameAndArguments.indexOf(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_OPEN) > 0; 713 } 714 715 716 /** 717 * Returns the name part of the given external method name and arguments. 718 * @param externalMethodNameAndArguments the external method name and arguments, 719 * e.g. "<code>myMethod(int,int)</code>". 720 * @return the name part of the String, e.g. "<code>myMethod</code>". 721 */ externalMethodName(String externalMethodNameAndArguments)722 public static String externalMethodName(String externalMethodNameAndArguments) 723 { 724 ExternalTypeEnumeration externalTypeEnumeration = 725 new ExternalTypeEnumeration(externalMethodNameAndArguments); 726 727 return externalTypeEnumeration.methodName(); 728 } 729 730 731 /** 732 * Converts the given external method return type and name and arguments to 733 * an internal method descriptor. 734 * @param externalReturnType the external method return type, 735 * e.g. "<code>boolean</code>". 736 * @param externalMethodNameAndArguments the external method name and arguments, 737 * e.g. "<code>myMethod(int,int)</code>". 738 * @return the internal method descriptor, 739 * e.g. "<code>(II)Z</code>". 740 */ internalMethodDescriptor(String externalReturnType, String externalMethodNameAndArguments)741 public static String internalMethodDescriptor(String externalReturnType, 742 String externalMethodNameAndArguments) 743 { 744 StringBuffer internalMethodDescriptor = new StringBuffer(); 745 internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN); 746 747 ExternalTypeEnumeration externalTypeEnumeration = 748 new ExternalTypeEnumeration(externalMethodNameAndArguments); 749 750 while (externalTypeEnumeration.hasMoreTypes()) 751 { 752 internalMethodDescriptor.append(internalType(externalTypeEnumeration.nextType())); 753 } 754 755 internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); 756 internalMethodDescriptor.append(internalType(externalReturnType)); 757 758 return internalMethodDescriptor.toString(); 759 } 760 761 762 /** 763 * Converts the given external method return type and List of arguments to 764 * an internal method descriptor. 765 * @param externalReturnType the external method return type, 766 * e.g. "<code>boolean</code>". 767 * @param externalArguments the external method arguments, 768 * e.g. <code>{ "int", "int" }</code>. 769 * @return the internal method descriptor, 770 * e.g. "<code>(II)Z</code>". 771 */ internalMethodDescriptor(String externalReturnType, List externalArguments)772 public static String internalMethodDescriptor(String externalReturnType, 773 List externalArguments) 774 { 775 StringBuffer internalMethodDescriptor = new StringBuffer(); 776 internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN); 777 778 for (int index = 0; index < externalArguments.size(); index++) 779 { 780 internalMethodDescriptor.append(internalType((String)externalArguments.get(index))); 781 } 782 783 internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); 784 internalMethodDescriptor.append(internalType(externalReturnType)); 785 786 return internalMethodDescriptor.toString(); 787 } 788 789 790 /** 791 * Converts an internal field description into an external full field description. 792 * @param accessFlags the access flags of the field. 793 * @param fieldName the field name, 794 * e.g. "<code>myField</code>". 795 * @param internalFieldDescriptor the internal field descriptor, 796 * e.g. "<code>Z</code>". 797 * @return the external full field description, 798 * e.g. "<code>public boolean myField</code>". 799 */ externalFullFieldDescription(int accessFlags, String fieldName, String internalFieldDescriptor)800 public static String externalFullFieldDescription(int accessFlags, 801 String fieldName, 802 String internalFieldDescriptor) 803 { 804 return externalFieldAccessFlags(accessFlags) + 805 externalType(internalFieldDescriptor) + 806 ' ' + 807 fieldName; 808 } 809 810 811 /** 812 * Converts an internal method description into an external full method description. 813 * @param internalClassName the internal name of the class of the method, 814 * e.g. "<code>mypackage/MyClass</code>". 815 * @param accessFlags the access flags of the method. 816 * @param internalMethodName the internal method name, 817 * e.g. "<code>myMethod</code>" or 818 * "<code><init></code>". 819 * @param internalMethodDescriptor the internal method descriptor, 820 * e.g. "<code>(II)Z</code>". 821 * @return the external full method description, 822 * e.g. "<code>public boolean myMethod(int,int)</code>" or 823 * "<code>public MyClass(int,int)</code>". 824 */ externalFullMethodDescription(String internalClassName, int accessFlags, String internalMethodName, String internalMethodDescriptor)825 public static String externalFullMethodDescription(String internalClassName, 826 int accessFlags, 827 String internalMethodName, 828 String internalMethodDescriptor) 829 { 830 return externalMethodAccessFlags(accessFlags) + 831 externalMethodReturnTypeAndName(internalClassName, 832 internalMethodName, 833 internalMethodDescriptor) + 834 ClassConstants.EXTERNAL_METHOD_ARGUMENTS_OPEN + 835 externalMethodArguments(internalMethodDescriptor) + 836 ClassConstants.EXTERNAL_METHOD_ARGUMENTS_CLOSE; 837 } 838 839 840 /** 841 * Converts internal class access flags into an external access description. 842 * @param accessFlags the class access flags. 843 * @return the external class access description, 844 * e.g. "<code>public final </code>". 845 */ externalClassAccessFlags(int accessFlags)846 public static String externalClassAccessFlags(int accessFlags) 847 { 848 return externalClassAccessFlags(accessFlags, ""); 849 } 850 851 852 /** 853 * Converts internal class access flags into an external access description. 854 * @param accessFlags the class access flags. 855 * @param prefix a prefix that is added to each access modifier. 856 * @return the external class access description, 857 * e.g. "<code>public final </code>". 858 */ externalClassAccessFlags(int accessFlags, String prefix)859 public static String externalClassAccessFlags(int accessFlags, String prefix) 860 { 861 if (accessFlags == 0) 862 { 863 return EMPTY_STRING; 864 } 865 866 StringBuffer string = new StringBuffer(50); 867 868 if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0) 869 { 870 string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PUBLIC).append(' '); 871 } 872 if ((accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0) 873 { 874 string.append(prefix).append(ClassConstants.EXTERNAL_ACC_FINAL).append(' '); 875 } 876 if ((accessFlags & ClassConstants.INTERNAL_ACC_ANNOTATTION) != 0) 877 { 878 string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ANNOTATION); 879 } 880 if ((accessFlags & ClassConstants.INTERNAL_ACC_INTERFACE) != 0) 881 { 882 string.append(prefix).append(ClassConstants.EXTERNAL_ACC_INTERFACE).append(' '); 883 } 884 else if ((accessFlags & ClassConstants.INTERNAL_ACC_ENUM) != 0) 885 { 886 string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ENUM).append(' '); 887 } 888 else if ((accessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0) 889 { 890 string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ABSTRACT).append(' '); 891 } 892 893 return string.toString(); 894 } 895 896 897 /** 898 * Converts internal field access flags into an external access description. 899 * @param accessFlags the field access flags. 900 * @return the external field access description, 901 * e.g. "<code>public volatile </code>". 902 */ externalFieldAccessFlags(int accessFlags)903 public static String externalFieldAccessFlags(int accessFlags) 904 { 905 return externalFieldAccessFlags(accessFlags, ""); 906 } 907 908 909 /** 910 * Converts internal field access flags into an external access description. 911 * @param accessFlags the field access flags. 912 * @param prefix a prefix that is added to each access modifier. 913 * @return the external field access description, 914 * e.g. "<code>public volatile </code>". 915 */ externalFieldAccessFlags(int accessFlags, String prefix)916 public static String externalFieldAccessFlags(int accessFlags, String prefix) 917 { 918 if (accessFlags == 0) 919 { 920 return EMPTY_STRING; 921 } 922 923 StringBuffer string = new StringBuffer(50); 924 925 if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0) 926 { 927 string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PUBLIC).append(' '); 928 } 929 if ((accessFlags & ClassConstants.INTERNAL_ACC_PRIVATE) != 0) 930 { 931 string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PRIVATE).append(' '); 932 } 933 if ((accessFlags & ClassConstants.INTERNAL_ACC_PROTECTED) != 0) 934 { 935 string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PROTECTED).append(' '); 936 } 937 if ((accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0) 938 { 939 string.append(prefix).append(ClassConstants.EXTERNAL_ACC_STATIC).append(' '); 940 } 941 if ((accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0) 942 { 943 string.append(prefix).append(ClassConstants.EXTERNAL_ACC_FINAL).append(' '); 944 } 945 if ((accessFlags & ClassConstants.INTERNAL_ACC_VOLATILE) != 0) 946 { 947 string.append(prefix).append(ClassConstants.EXTERNAL_ACC_VOLATILE).append(' '); 948 } 949 if ((accessFlags & ClassConstants.INTERNAL_ACC_TRANSIENT) != 0) 950 { 951 string.append(prefix).append(ClassConstants.EXTERNAL_ACC_TRANSIENT).append(' '); 952 } 953 954 return string.toString(); 955 } 956 957 958 /** 959 * Converts internal method access flags into an external access description. 960 * @param accessFlags the method access flags. 961 * @return the external method access description, 962 * e.g. "<code>public synchronized </code>". 963 */ externalMethodAccessFlags(int accessFlags)964 public static String externalMethodAccessFlags(int accessFlags) 965 { 966 return externalMethodAccessFlags(accessFlags, ""); 967 } 968 969 970 /** 971 * Converts internal method access flags into an external access description. 972 * @param accessFlags the method access flags. 973 * @param prefix a prefix that is added to each access modifier. 974 * @return the external method access description, 975 * e.g. "public synchronized ". 976 */ externalMethodAccessFlags(int accessFlags, String prefix)977 public static String externalMethodAccessFlags(int accessFlags, String prefix) 978 { 979 if (accessFlags == 0) 980 { 981 return EMPTY_STRING; 982 } 983 984 StringBuffer string = new StringBuffer(50); 985 986 if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0) 987 { 988 string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PUBLIC).append(' '); 989 } 990 if ((accessFlags & ClassConstants.INTERNAL_ACC_PRIVATE) != 0) 991 { 992 string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PRIVATE).append(' '); 993 } 994 if ((accessFlags & ClassConstants.INTERNAL_ACC_PROTECTED) != 0) 995 { 996 string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PROTECTED).append(' '); 997 } 998 if ((accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0) 999 { 1000 string.append(prefix).append(ClassConstants.EXTERNAL_ACC_STATIC).append(' '); 1001 } 1002 if ((accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0) 1003 { 1004 string.append(prefix).append(ClassConstants.EXTERNAL_ACC_FINAL).append(' '); 1005 } 1006 if ((accessFlags & ClassConstants.INTERNAL_ACC_SYNCHRONIZED) != 0) 1007 { 1008 string.append(prefix).append(ClassConstants.EXTERNAL_ACC_SYNCHRONIZED).append(' '); 1009 } 1010 if ((accessFlags & ClassConstants.INTERNAL_ACC_NATIVE) != 0) 1011 { 1012 string.append(prefix).append(ClassConstants.EXTERNAL_ACC_NATIVE).append(' '); 1013 } 1014 if ((accessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0) 1015 { 1016 string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ABSTRACT).append(' '); 1017 } 1018 if ((accessFlags & ClassConstants.INTERNAL_ACC_STRICT) != 0) 1019 { 1020 string.append(prefix).append(ClassConstants.EXTERNAL_ACC_STRICT).append(' '); 1021 } 1022 1023 return string.toString(); 1024 } 1025 1026 1027 /** 1028 * Converts an internal method descriptor into an external method return type. 1029 * @param internalMethodDescriptor the internal method descriptor, 1030 * e.g. "<code>(II)Z</code>". 1031 * @return the external method return type, 1032 * e.g. "<code>boolean</code>". 1033 */ externalMethodReturnType(String internalMethodDescriptor)1034 public static String externalMethodReturnType(String internalMethodDescriptor) 1035 { 1036 return externalType(internalMethodReturnType(internalMethodDescriptor)); 1037 } 1038 1039 1040 /** 1041 * Converts an internal class name, method name, and method descriptor to 1042 * an external method return type and name. 1043 * @param internalClassName the internal name of the class of the method, 1044 * e.g. "<code>mypackage/MyClass</code>". 1045 * @param internalMethodName the internal method name, 1046 * e.g. "<code>myMethod</code>" or 1047 * "<code><init></code>". 1048 * @param internalMethodDescriptor the internal method descriptor, 1049 * e.g. "<code>(II)Z</code>". 1050 * @return the external method return type and name, 1051 * e.g. "<code>boolean myMethod</code>" or 1052 * "<code>MyClass</code>". 1053 */ externalMethodReturnTypeAndName(String internalClassName, String internalMethodName, String internalMethodDescriptor)1054 private static String externalMethodReturnTypeAndName(String internalClassName, 1055 String internalMethodName, 1056 String internalMethodDescriptor) 1057 { 1058 return internalMethodName.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ? 1059 externalShortClassName(externalClassName(internalClassName)) : 1060 (externalMethodReturnType(internalMethodDescriptor) + 1061 ' ' + 1062 internalMethodName); 1063 } 1064 1065 1066 /** 1067 * Converts an internal method descriptor into an external method argument 1068 * description. 1069 * @param internalMethodDescriptor the internal method descriptor, 1070 * e.g. "<code>(II)Z</code>". 1071 * @return the external method argument description, 1072 * e.g. "<code>int,int</code>". 1073 */ externalMethodArguments(String internalMethodDescriptor)1074 public static String externalMethodArguments(String internalMethodDescriptor) 1075 { 1076 StringBuffer externalMethodNameAndArguments = new StringBuffer(); 1077 1078 InternalTypeEnumeration internalTypeEnumeration = 1079 new InternalTypeEnumeration(internalMethodDescriptor); 1080 1081 while (internalTypeEnumeration.hasMoreTypes()) 1082 { 1083 externalMethodNameAndArguments.append(externalType(internalTypeEnumeration.nextType())); 1084 if (internalTypeEnumeration.hasMoreTypes()) 1085 { 1086 externalMethodNameAndArguments.append(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_SEPARATOR); 1087 } 1088 } 1089 1090 return externalMethodNameAndArguments.toString(); 1091 } 1092 1093 1094 /** 1095 * Returns the internal package name of the given internal class name. 1096 * @param internalClassName the internal class name, 1097 * e.g. "<code>java/lang/Object</code>". 1098 * @return the internal package name, 1099 * e.g. "<code>java/lang</code>". 1100 */ internalPackageName(String internalClassName)1101 public static String internalPackageName(String internalClassName) 1102 { 1103 String internalPackagePrefix = internalPackagePrefix(internalClassName); 1104 int length = internalPackagePrefix.length(); 1105 return length > 0 ? 1106 internalPackagePrefix.substring(0, length - 1) : 1107 ""; 1108 } 1109 1110 1111 /** 1112 * Returns the internal package prefix of the given internal class name. 1113 * @param internalClassName the internal class name, 1114 * e.g. "<code>java/lang/Object</code>". 1115 * @return the internal package prefix, 1116 * e.g. "<code>java/lang/</code>". 1117 */ internalPackagePrefix(String internalClassName)1118 public static String internalPackagePrefix(String internalClassName) 1119 { 1120 return internalClassName.substring(0, internalClassName.lastIndexOf(ClassConstants.INTERNAL_PACKAGE_SEPARATOR, 1121 internalClassName.length() - 2) + 1); 1122 } 1123 1124 1125 /** 1126 * Returns the external package name of the given external class name. 1127 * @param externalClassName the external class name, 1128 * e.g. "<code>java.lang.Object</code>". 1129 * @return the external package name, 1130 * e.g. "<code>java.lang</code>". 1131 */ externalPackageName(String externalClassName)1132 public static String externalPackageName(String externalClassName) 1133 { 1134 String externalPackagePrefix = externalPackagePrefix(externalClassName); 1135 int length = externalPackagePrefix.length(); 1136 return length > 0 ? 1137 externalPackagePrefix.substring(0, length - 1) : 1138 ""; 1139 } 1140 1141 1142 /** 1143 * Returns the external package prefix of the given external class name. 1144 * @param externalClassName the external class name, 1145 * e.g. "<code>java.lang.Object</code>". 1146 * @return the external package prefix, 1147 * e.g. "<code>java.lang.</code>". 1148 */ externalPackagePrefix(String externalClassName)1149 public static String externalPackagePrefix(String externalClassName) 1150 { 1151 return externalClassName.substring(0, externalClassName.lastIndexOf(ClassConstants.EXTERNAL_PACKAGE_SEPARATOR, 1152 externalClassName.length() - 2) + 1); 1153 } 1154 } 1155