001 package edu.rice.cs.cunit.classFile; 002 003 import edu.rice.cs.cunit.classFile.attributes.*; 004 import edu.rice.cs.cunit.classFile.constantPool.*; 005 import edu.rice.cs.cunit.classFile.constantPool.visitors.ADefaultPoolInfoVisitor; 006 import edu.rice.cs.cunit.util.ILambda; 007 import junit.framework.TestCase; 008 009 import java.io.*; 010 import java.util.*; 011 import java.util.jar.JarEntry; 012 import java.util.jar.JarFile; 013 014 /** 015 * Tools for dealing with class files. 016 * 017 * @author Mathias Ricken 018 */ 019 public class ClassFileTools { 020 /** 021 * Return a string that represents the access flags set. 022 * 023 * @param flags access flags 024 * 025 * @return string with access flags as words 026 */ 027 public static String getAccessString(short flags) { 028 StringBuilder x = new StringBuilder(); 029 030 if (0 != (flags & ClassFile.ACC_PUBLIC)) { 031 x.append("public "); 032 } 033 034 if (0 != (flags & ClassFile.ACC_PRIVATE)) { 035 x.append("private "); 036 } 037 038 if (0 != (flags & ClassFile.ACC_PROTECTED)) { 039 x.append("protected "); 040 } 041 042 if (0 != (flags & ClassFile.ACC_STATIC)) { 043 x.append("static "); 044 } 045 046 if (0 != (flags & ClassFile.ACC_FINAL)) { 047 x.append("final "); 048 } 049 050 if (0 != (flags & ClassFile.ACC_SYNCHRONIZED)) { 051 x.append("synchronized "); 052 } 053 054 if (0 != (flags & ClassFile.ACC_VOLATILE)) { 055 x.append("volatile "); 056 } 057 058 if (0 != (flags & ClassFile.ACC_TRANSIENT)) { 059 x.append("transient "); 060 } 061 062 if (0 != (flags & ClassFile.ACC_NATIVE)) { 063 x.append("native "); 064 } 065 066 if (0 != (flags & ClassFile.ACC_INTERFACE)) { 067 x.append("interface "); 068 } 069 070 if (0 != (flags & ClassFile.ACC_ABSTRACT)) { 071 x.append("abstract "); 072 } 073 074 if (0 != (flags & ClassFile.ACC_STRICT)) { 075 x.append("strictfp "); 076 } 077 078 // TODO: Handle ACC_SYNTHETIC, ACC_ANNOTATION, ACC_ENUM, ACC_BRIDGE, and ACC_VARARGS here? 079 080 return x.toString(); 081 } 082 083 /** 084 * Translate a type descriptor descriptor and a variable name into a Java declaration. 085 * 086 * @param typeString type descriptor descriptor 087 * @param varName variable name 088 * 089 * @return declaration 090 */ 091 public static String getTypeString(String typeString, String varName) { 092 int isArray = 0; 093 int ndx = 0; 094 StringBuilder x = new StringBuilder(); 095 096 while('[' == typeString.charAt(ndx)) { 097 ++isArray; 098 ++ndx; 099 } 100 101 switch(typeString.charAt(ndx)) { 102 case 'B': 103 x.append("byte"); 104 break; 105 case 'C': 106 x.append("char"); 107 break; 108 case 'D': 109 x.append("double"); 110 break; 111 case 'F': 112 x.append("float"); 113 break; 114 case 'I': 115 x.append("int"); 116 break; 117 case 'J': 118 x.append("long"); 119 break; 120 case 'L': 121 for(int i = ndx + 1; i < typeString.indexOf((int)';'); ++i) { 122 if ('/' != typeString.charAt(i)) { 123 x.append(typeString.charAt(i)); 124 } 125 else { 126 x.append('.'); 127 } 128 } 129 break; 130 case 'V': 131 x.append("void"); 132 break; 133 case 'S': 134 x.append("short"); 135 break; 136 case 'Z': 137 x.append("boolean"); 138 break; 139 } 140 while(0 < isArray) { 141 x.append("[]"); 142 isArray--; 143 } 144 x.append(' '); 145 x.append(varName); 146 return x.toString(); 147 } 148 149 /** 150 * Returns the type character associated with a primitive type name, or 0 if there was an error. 151 * @param primTypeName primitive type name, e.g. "void" or "byte" 152 * @return primitive type character, e.g. 'V' or 'B', or 0 if the type name was not recognized 153 */ 154 public static char getPrimitiveTypeChar(String primTypeName) { 155 if (primTypeName.equals("void")) { return 'V'; } 156 else if (primTypeName.equals("byte")) { return 'B'; } 157 else if (primTypeName.equals("char")) { return 'C'; } 158 else if (primTypeName.equals("double")) { return 'D'; } 159 else if (primTypeName.equals("float")) { return 'F'; } 160 else if (primTypeName.equals("int")) { return 'I'; } 161 else if (primTypeName.equals("long")) { return 'J'; } 162 else if (primTypeName.equals("short")) { return 'S'; } 163 else if (primTypeName.equals("boolean")) { return 'Z'; } 164 return 0; 165 } 166 167 /** 168 * Return true if this is a primitive type or an array of primitive types. 169 * 170 * @param typeString type descriptor descriptor 171 * 172 * @return true if primitive type or array of primitive types 173 */ 174 public static boolean isPrimitive(String typeString) { 175 int ndx = 0; 176 177 while('[' == typeString.charAt(ndx)) { 178 ++ndx; 179 } 180 181 switch(typeString.charAt(ndx)) { 182 case 'B': 183 case 'C': 184 case 'D': 185 case 'F': 186 case 'I': 187 case 'J': 188 case 'V': 189 case 'S': 190 case 'Z': 191 return true; 192 default: 193 return false; 194 } 195 } 196 197 /** 198 * Return a list of signatures. 199 * @param sig concatenated signatures 200 * @return list of signatures 201 */ 202 public static List<String> getSignatures(String sig) { 203 ArrayList<String> sigList = new ArrayList<String>(); 204 if (sig!=null) { 205 while(sig.length()>0) { 206 String next = ClassFileTools.getNextSignature(sig); 207 sigList.add(sig.substring(0, sig.length()-next.length())); 208 sig = next; 209 } 210 } 211 return sigList; 212 } 213 214 /** 215 * Return the next descriptor from a string of concatenated signatures. For example, if the descriptor was "[BII", 216 * this method would return "II". 217 * 218 * @param sig concatenated signatures 219 * 220 * @return next descriptor 221 */ 222 public static String getNextSignature(String sig) { 223 int ndx = 0; 224 String x; 225 226 while('[' == sig.charAt(ndx)) { 227 ++ndx; 228 } 229 230 if ('L' == sig.charAt(ndx)) { 231 while(';' != sig.charAt(ndx)) { 232 ++ndx; 233 } 234 } 235 ++ndx; 236 x = (sig.substring(ndx)); 237 return (x); 238 } 239 240 /** 241 * Return class name in Java form. 242 * 243 * @param s mangled class name 244 * 245 * @return Java class name 246 */ 247 public static String getClassName(String s) { 248 if ('[' == s.charAt(0)) { 249 return getTypeString(s, ""); 250 } 251 252 return s.replace('/','.'); 253 } 254 255 256 /** 257 * Return set of class names used in this mangled name. Primitives are not included. 258 * 259 * @param s mangled class name 260 * 261 * @return set of Java class names used 262 */ 263 public static Set<String> getClassNamesUsed(String s) { 264 HashSet<String> set = new HashSet<String>(); 265 final char ch = s.charAt(0); 266 267 if ('[' == ch) { 268 // only handle reference arrays 269 if ('L' == s.charAt(1)) { 270 set.addAll(getClassNamesUsed(s.substring(1,s.indexOf(';')+1))); 271 } 272 } 273 else if ('(' == ch) { 274 String p = s.substring(1,s.indexOf(')')); 275 while(0 != p.length()) { 276 String next = getNextSignature(p); 277 String param = p.substring(0, p.length()-next.length()); 278 set.addAll(getClassNamesUsed(param)); 279 p = next; 280 } 281 p = s.substring(s.indexOf(')')+1); 282 set.addAll(getClassNamesUsed(p)); 283 } 284 else if ('L' == ch) { 285 String p = s.substring(1,s.indexOf(';')).replace('/','.'); 286 set.add(p); 287 } 288 289 return set; 290 } 291 292 /** 293 * Returns a set of class names used in a class files. Primitives are not included. 294 * @param cf class file 295 * @return set of class names used 296 */ 297 public static Set<String> getClassNamesUsed(ClassFile cf) { 298 final HashSet<String> classesUsed = new HashSet<String>(); 299 for(final APoolInfo cpi : cf.getConstantPool()) { 300 if ((cpi == cf.getThisClass()) || (cpi == cf.getSuperClass())) { 301 continue; 302 } 303 cpi.execute(new ADefaultPoolInfoVisitor<Object, Object>() { 304 public Object nameAndTypeCase(NameAndTypePoolInfo host, Object param) { 305 String s = host.getDescriptor().toString(); 306 if ('[' == s.charAt(0)) { 307 // array 308 if ('L' != s.charAt(1)) { 309 // ignore primitive arrays 310 return null; 311 } 312 s = s.substring(2); 313 } 314 classesUsed.addAll(ClassFileTools.getClassNamesUsed(s)); 315 return null; 316 } 317 318 public Object classCase(ClassPoolInfo host, Object param) { 319 String s = host.getName().toString(); 320 if ('[' == s.charAt(0)) { 321 // array 322 if ('L' != s.charAt(1)) { 323 // ignore primitive arrays 324 return null; 325 } 326 s = s.substring(2); 327 } 328 if (s.endsWith(";")) { 329 s = s.substring(0, s.length()-1); 330 } 331 classesUsed.add(s.replace('/','.')); 332 return null; 333 } 334 public Object defaultCase(APoolInfo host, Object param) { 335 return null; 336 } 337 }, null); 338 } 339 340 classesUsed.add(cf.getThisClassName()); 341 classesUsed.add(cf.getSuperClassName()); 342 343 for(FieldInfo field : cf.getFields()) { 344 classesUsed.addAll(getClassNamesUsed(field.getDescriptor().toString())); 345 } 346 347 for(MethodInfo method : cf.getMethods()) { 348 classesUsed.addAll(getClassNamesUsed(method.getDescriptor().toString())); 349 } 350 351 AAttributeInfo attr = cf.getAttribute(InnerClassesAttributeInfo.getAttributeName()); 352 if (null!=attr) { 353 InnerClassesAttributeInfo ic = (InnerClassesAttributeInfo)attr; 354 for (InnerClassesAttributeInfo.InnerClassesRecord inc: ic.getInnerClasses()) { 355 inc.innerClass.execute(new ADefaultPoolInfoVisitor<Object, Object>() { 356 public Object defaultCase(APoolInfo host, Object param) { 357 return null; 358 } 359 public String emptyCase(EmptyPoolInfo host, Object param) { 360 return null; 361 } 362 public String classCase(ClassPoolInfo host, Object param) { 363 classesUsed.add(getClassName(host.getName().toString())); 364 return null; 365 } 366 }, null); 367 inc.outerClass.execute(new ADefaultPoolInfoVisitor<Object, Object>() { 368 public Object defaultCase(APoolInfo host, Object param) { 369 return null; 370 } 371 public String emptyCase(EmptyPoolInfo host, Object param) { 372 return null; 373 } 374 public String classCase(ClassPoolInfo host, Object param) { 375 classesUsed.add(getClassName(host.getName().toString())); 376 return null; 377 } 378 }, null); 379 } 380 } 381 382 attr = cf.getAttribute(RuntimeInvisibleAnnotationsAttributeInfo.getAttributeName()); 383 if (null!=attr) { 384 RuntimeInvisibleAnnotationsAttributeInfo an = (RuntimeInvisibleAnnotationsAttributeInfo)attr; 385 386 for (AAnnotationsAttributeInfo.Annotation a: an.getAnnotations()) { 387 processAnnotation(a, classesUsed); 388 389 } 390 } 391 392 attr = cf.getAttribute(RuntimeVisibleAnnotationsAttributeInfo.getAttributeName()); 393 if (null!=attr) { 394 RuntimeVisibleAnnotationsAttributeInfo an = (RuntimeVisibleAnnotationsAttributeInfo)attr; 395 396 for (AAnnotationsAttributeInfo.Annotation a: an.getAnnotations()) { 397 processAnnotation(a, classesUsed); 398 399 } 400 } 401 402 return classesUsed; 403 } 404 405 /** 406 * Helper for getClassNamesUsed that processes an annotation. 407 * @param a annotation 408 * @param classesUsed set of classes used 409 */ 410 private static void processAnnotation(AAnnotationsAttributeInfo.Annotation a, HashSet<String> classesUsed) { 411 classesUsed.add(getTypeString(a.getType(),"").trim()); 412 413 for (AAnnotationsAttributeInfo.Annotation.NameValuePair nvp: a.getPairs()) { 414 AAnnotationsAttributeInfo.Annotation.AMemberValue mv = nvp.getValue(); 415 processMemberValue(mv, classesUsed); 416 } 417 } 418 419 /** 420 * Helper for processAnnotation that processes an annotation member value. 421 * @param mv member value 422 * @param classesUsed set of classes used 423 */ 424 private static void processMemberValue(AAnnotationsAttributeInfo.Annotation.AMemberValue mv, 425 final HashSet<String> classesUsed) { 426 mv.execute(new AAnnotationsAttributeInfo.Annotation.IMemberValueVisitor<Object, Object>() { 427 public Object constantMemberCase(AAnnotationsAttributeInfo.Annotation.ConstantMemberValue host, Object param) { 428 host.getConstValue().execute(new ADefaultPoolInfoVisitor<Object, Object>() { 429 public Object unicodeCase(UnicodePoolInfo host, Object param) { 430 classesUsed.addAll(getClassNamesUsed("java.lang.String")); 431 return null; 432 } 433 434 public Object asciizCase(ASCIIPoolInfo host, Object param) { 435 classesUsed.add("java.lang.String"); 436 return null; 437 } 438 439 public Object defaultCase(APoolInfo host, Object param) { 440 return null; 441 } 442 }, null); 443 return null; 444 } 445 public Object enumMemberCase(AAnnotationsAttributeInfo.Annotation.EnumMemberValue host, Object param) { 446 classesUsed.add(getTypeString(host.getTypeName().toString(),"").trim()); 447 return null; 448 } 449 public Object classMemberCase(AAnnotationsAttributeInfo.Annotation.ClassMemberValue host, Object param) { 450 classesUsed.add("java.lang.Class"); 451 classesUsed.add(getTypeString(host.getClassName().toString(),"").trim()); 452 return null; 453 } 454 public Object annotationMemberCase(AAnnotationsAttributeInfo.Annotation.AnnotationMemberValue host, Object param) { 455 classesUsed.add("java.lang.annotation.Annotation"); 456 processAnnotation(host.getAnnotation(), classesUsed); 457 return null; 458 } 459 public Object arrayMemberCase(AAnnotationsAttributeInfo.Annotation.ArrayMemberValue host, Object param) { 460 for (AAnnotationsAttributeInfo.Annotation.AMemberValue mv: host.getEntries()) { 461 processMemberValue(mv, classesUsed); 462 } 463 return null; 464 } 465 }, null); 466 } 467 468 /** 469 * Helper class for findClassFile. 470 */ 471 public static class ClassLocation { 472 private final String _className; 473 private final InputStream _inputStream; 474 private final File _file; 475 private final ClassFile _cf; 476 private final JarFile _jarFile; // or null if not from a jar 477 478 public ClassLocation(String className, ClassFile cf, InputStream inputStream, File file) { 479 this(className, cf, inputStream, file, null); 480 } 481 482 public ClassLocation(String className, ClassFile cf, InputStream inputStream, File file, JarFile jarFile) { 483 this._className = className; 484 this._inputStream = inputStream; 485 this._file = file; 486 this._jarFile = jarFile; 487 this._cf = cf; 488 } 489 490 public InputStream getInputStream() { 491 return _inputStream; 492 } 493 494 public File getFile() { 495 return _file; 496 } 497 498 public String getClassName() { 499 return _className; 500 } 501 502 public JarFile getJarFile() { 503 return _jarFile; 504 } 505 506 public ClassFile getClassFile() { 507 return _cf; 508 } 509 510 public void close() throws IOException { 511 if (_inputStream!=null) { _inputStream.close(); } 512 if (_jarFile!=null) { _jarFile.close(); } 513 } 514 } 515 516 /** 517 * Find a class. 518 * @param className class to find 519 * @param classPath list with class path entries 520 * @return class location 521 */ 522 public static ClassLocation findClassFile(String className, List<String> classPath) { 523 for(String pathStr: classPath) { 524 File path = new File(pathStr); 525 if (path.isFile()) { 526 if (pathStr.toLowerCase().endsWith(".class")) { 527 FileInputStream stream = null; 528 try { 529 stream = new FileInputStream(path); 530 ClassFile cf = new ClassFile(stream); 531 532 if (cf.getThisClassName().equals(className)) { 533 return new ClassLocation(className, cf, new FileInputStream(path), path); 534 } 535 } 536 catch(ClassFormatError e) { 537 continue; 538 } 539 catch(IOException e) { 540 continue; 541 } 542 finally { 543 if (stream!=null) { 544 try { stream.close(); } 545 catch(IOException e1) { } 546 } 547 } 548 } 549 else if (pathStr.toLowerCase().endsWith(".jar")) { 550 JarFile jf = null; 551 InputStream stream = null; 552 try { 553 jf = new JarFile(path); 554 JarEntry je = jf.getJarEntry(className.replace('.','/')+".class"); 555 if (null != je) { 556 stream = jf.getInputStream(je); 557 ClassFile cf = new ClassFile(stream); 558 559 if (cf.getThisClassName().equals(className)) { 560 return new ClassLocation(className, cf, jf.getInputStream(je), path, jf); 561 } 562 else { 563 jf.close(); 564 } 565 } 566 else { 567 if (jf!=null) { 568 try { jf.close(); } 569 catch(IOException e1) { } 570 } 571 } 572 } 573 catch(IOException e) { 574 if (jf!=null) { 575 try { jf.close(); } 576 catch(IOException e1) { } 577 } 578 continue; 579 } 580 catch(ClassFormatError e) { 581 if (jf!=null) { 582 try { jf.close(); } 583 catch(IOException e1) { } 584 } 585 continue; 586 } 587 finally { 588 if (stream!=null) { 589 try { stream.close(); } 590 catch(IOException e1) { } 591 } 592 } 593 594 } 595 } 596 else { 597 File classFile = new File(path, className.replace('.','/')+".class"); 598 if (classFile.isFile()) { 599 FileInputStream stream = null; 600 try { 601 stream = new FileInputStream(classFile); 602 ClassFile cf = new ClassFile(stream); 603 604 if (cf.getThisClassName().equals(className)) { 605 return new ClassLocation(className, cf, new FileInputStream(classFile), classFile); 606 } 607 } 608 catch(IOException e) { 609 continue; 610 } 611 catch(ClassFormatError e) { 612 continue; 613 } 614 finally { 615 try { stream.close(); } 616 catch(IOException e1) { } 617 } 618 } 619 } 620 } 621 622 return null; 623 } 624 625 /** 626 * Generate the class dependency fix point. 627 * @param classNames list of class names 628 * @param classPath list of class path entries 629 * @param classesUsed set of used classes 630 * @param processClassNameLambda lambda to apply class names to 631 * @param processNotFoundLambda lambda to apply to missing class names to 632 * NOTE: The ClassLocation's input stream will get closed after the lambda has been invoked. 633 * That means it should not be stored and used later. 634 */ 635 public static void generateDependencyFixPoint(Set<String> classNames, 636 List<String> classPath, 637 Set<String> classesUsed, 638 ILambda<Object,ClassLocation> processClassNameLambda, 639 ILambda<Object,String> processNotFoundLambda) { 640 Queue<String> bfQueue = new LinkedList<String>(); 641 642 for(String className: classNames) { 643 bfQueue.add(className); 644 645 while(!bfQueue.isEmpty()) { 646 String curClassName = bfQueue.remove(); 647 ClassFileTools.ClassLocation cl = null; 648 try { 649 cl = findClassFile(curClassName, classPath); 650 if ((null!=cl) && (!classesUsed.contains(curClassName))) { 651 processClassNameLambda.apply(cl); 652 } 653 classesUsed.add(curClassName); 654 if (null==cl) { 655 processNotFoundLambda.apply(curClassName); 656 continue; 657 } 658 try { 659 ClassFile cf = new ClassFile(cl.getInputStream()); 660 Set<String> newClasses = getClassNamesUsed(cf); 661 for(String c: newClasses) { 662 if ((!classesUsed.contains(c)) && (!bfQueue.contains(c))) { 663 bfQueue.add(c); 664 } 665 } 666 } 667 catch(IOException e) { 668 processNotFoundLambda.apply(curClassName); 669 } 670 catch(ClassFormatError classFormatError) { 671 processNotFoundLambda.apply(curClassName); 672 } 673 } 674 finally { 675 try { if (cl!=null) cl.close(); } 676 catch(IOException e) { /* ignore; shouldn't cause any problems except on Windows with read locks */ } 677 } 678 } 679 } 680 } 681 682 /** 683 * Return the class dependency fix point. 684 * @param classNames class names 685 * @param classPath class path entries 686 * @return class dependency fix point 687 */ 688 public Set<String> getDependencyFixPoint(Set<String> classNames, 689 List<String> classPath) { 690 HashSet<String> classesUsed = new HashSet<String>(); 691 generateDependencyFixPoint(classNames, classPath, classesUsed, new ILambda<Object,ClassLocation>() { 692 public Object apply(ClassLocation param) { 693 return null; 694 } 695 },new ILambda<Object,String>() { 696 public Object apply(String param) { 697 return null; 698 } 699 }); 700 return classesUsed; 701 } 702 703 /** 704 * Find a class in the files to consider and return its class file info. 705 * 706 * @param className name of class to find 707 * @param filesToConsider set of file names (*.class and *.jar) to consider during the search. 708 * @return class file info or null if not found 709 */ 710 public static ClassFile findClassInFiles(String className, Set<String> filesToConsider) { 711 ClassFile cf = null; 712 713 for(String name : filesToConsider) { 714 try { 715 if (name.endsWith(".jar")) { 716 // this is a jar file 717 // Debug.out.println("Looking for "+className+".class in "+name); 718 JarFile jf = new JarFile(name); 719 JarEntry je = jf.getJarEntry(className + ".class"); 720 if (je != null) { 721 // Debug.out.println("\tloading..."); 722 return loadClassFromStream(jf.getInputStream(je)); 723 } 724 } 725 else { 726 // treat as directory 727 String fileName = name + File.separatorChar + className + ".class"; 728 fileName = fileName.replace('/', File.separatorChar); 729 // Debug.out.println("Looking for "+fileName); 730 File f = new File(fileName); 731 if (f.exists() && f.isFile()) { 732 // Debug.out.println("\tloading..."); 733 return loadClassFromStream(new FileInputStream(f)); 734 } 735 } 736 } 737 catch(IOException e) { 738 } 739 } 740 741 return cf; 742 } 743 744 745 /** 746 * Load class file info from the stream. 747 * 748 * @param is input stream 749 * @return class file info 750 * 751 * @throws IOException 752 */ 753 public static ClassFile loadClassFromStream(InputStream is) throws IOException { 754 byte[] b = new byte[is.available()]; 755 int offset = 0, bytesRead; 756 while((offset < b.length) && ((bytesRead = is.read(b, offset, b.length - offset)) != -1)) { 757 offset += bytesRead; 758 } 759 is.close(); 760 return new ClassFile(new ByteArrayInputStream(b)); 761 } 762 763 /** 764 * Returns true if the specified class name matches one of the patterns. 765 * ! = make sure class name does NOT match this pattern (prefix only) 766 * ? = one character<br> 767 * * = zero or more characters except '.' 768 * *** = zero or more characters including '.' java.**.lang 769 * [abc] = Either a, b or c 770 * [a-mA-M] = A letter from the ranges a-m or A-M (inclusive) 771 * The entire class name has to match the pattern, i.e. the regex characters ^ and $ are implicit. 772 * @param className class name 773 * @param classNamePatterns variable argument list of patterns 774 * @return true if the specified class name matches one of the patterns 775 */ 776 public static boolean classNameMatches(String className, String... classNamePatterns) { 777 boolean matchFound = false; 778 for (String pattern: classNamePatterns) { 779 boolean negate = false; 780 String regex = pattern; 781 if (regex.indexOf('!')==0) { 782 negate = true; 783 regex = regex.substring(1); 784 } 785 // System.out.println(regex); 786 regex = regex.replaceAll("\\.","\\\\."); 787 // System.out.println(regex); 788 regex = regex.replaceAll("\\?","."); 789 // System.out.println(regex); 790 regex = regex.replaceAll("\\$","\\\\\\$"); 791 // System.out.println(regex); 792 regex = regex.replaceAll("\\(","\\\\("); 793 // System.out.println(regex); 794 regex = regex.replaceAll("\\)","\\\\)"); 795 // System.out.println(regex); 796 regex = regex.replaceAll("\\*\\*\\*",".`"); 797 // System.out.println(regex); 798 regex = regex.replaceAll("^\\*\\*\\*",".`"); 799 // System.out.println(regex); 800 regex = regex.replaceAll("\\*","[^\\.]*"); 801 // System.out.println(regex); 802 regex = regex.replaceAll("\\.`",".*"); 803 // System.out.println(regex); 804 regex = "^"+regex+"$"; 805 // System.out.println(regex); 806 boolean matches = className.matches(regex); 807 // System.out.println(className+" matches? "+matches); 808 if (matches) { 809 if (negate) { 810 return false; 811 } 812 else { 813 matchFound = true; 814 } 815 } 816 } 817 return matchFound; 818 } 819 820 /** 821 * Get the Class instance for the given type. The type can be a class formatted "Lfoo/bar/MyClass;", 822 * a primitive type formatted "I", or an array formatted "[I" or "[Lfoo/bar/MyClass;". 823 * @param type type string 824 * @return Class instance 825 */ 826 public static Class<?> getClassFromType(String type) throws ClassNotFoundException { 827 if (type.equals("I")) { 828 return int.class; 829 } 830 if (type.equals("J")) { 831 return long.class; 832 } 833 if (type.equals("F")) { 834 return float.class; 835 } 836 if (type.equals("D")) { 837 return double.class; 838 } 839 if (type.equals("B")) { 840 return byte.class; 841 } 842 if (type.equals("C")) { 843 return char.class; 844 } 845 if (type.equals("S")) { 846 return short.class; 847 } 848 if (type.equals("Z")) { 849 return boolean.class; 850 } 851 String tn = type.substring(1,type.length()-1).replace('/','.'); 852 return Class.forName(tn); 853 } 854 855 /** 856 * Get the Class instance for the given type. The type can be a class formatted "Lfoo/bar/MyClass;", 857 * a primitive type formatted "I", or an array formatted "[I" or "[Lfoo/bar/MyClass;". 858 * @param type type string 859 * @param initialize whether the class needs to be initialized 860 * @param loader the class loader 861 * @return Class instance 862 */ 863 public static Class<?> getClassFromType(String type, boolean initialize, ClassLoader loader) throws ClassNotFoundException { 864 if (type.equals("I")) { 865 return int.class; 866 } 867 if (type.equals("J")) { 868 return long.class; 869 } 870 if (type.equals("F")) { 871 return float.class; 872 } 873 if (type.equals("D")) { 874 return double.class; 875 } 876 if (type.equals("B")) { 877 return byte.class; 878 } 879 if (type.equals("C")) { 880 return char.class; 881 } 882 if (type.equals("S")) { 883 return short.class; 884 } 885 if (type.equals("Z")) { 886 return boolean.class; 887 } 888 String tn = type.substring(1,type.length()-1).replace('/','.'); 889 return Class.forName(tn, initialize, loader); 890 } 891 892 /** 893 * Get the class/primitive name for the given type. The type can be a class formatted "Lfoo/bar/MyClass;", 894 * a primitive type formatted "I", or an array formatted "[I" or "[Lfoo/bar/MyClass;". 895 * @param type type string 896 * @return type name 897 */ 898 public static String getClassNameFromType(String type) { 899 if (type.equals("I")) { 900 return "int"; 901 } 902 if (type.equals("J")) { 903 return "long"; 904 } 905 if (type.equals("F")) { 906 return "float"; 907 } 908 if (type.equals("D")) { 909 return "double"; 910 } 911 if (type.equals("B")) { 912 return "byte"; 913 } 914 if (type.equals("C")) { 915 return "char"; 916 } 917 if (type.equals("S")) { 918 return "short"; 919 } 920 if (type.equals("Z")) { 921 return "boolean"; 922 } 923 if (type.charAt(0)=='[') { 924 return type.replace('/','.'); 925 } 926 return type.substring(1,type.length()-1).replace('/','.'); 927 } 928 929 /** 930 * Test cases. 931 */ 932 public static class ClassFileToolsTest extends TestCase { 933 public void testClassNameMatches() { 934 assertEquals(false, classNameMatches("java.lang.Number", "java.*")); 935 assertEquals(true, classNameMatches("java.lang.Number", "java.***")); 936 assertEquals(false, classNameMatches("java.lang.Number", "*java*")); 937 assertEquals(true, classNameMatches("java.lang.Number", "***java***")); 938 assertEquals(true, classNameMatches("java.lang.Number", "java.lang.Number")); 939 assertEquals(false, classNameMatches("java.lang.Number", "!java.lang.Number")); 940 assertEquals(false, classNameMatches("java.lang.Number", "lang.***")); 941 assertEquals(false, classNameMatches("java.lang.Number", 942 "java.***", 943 "sun.management.***", 944 "!java.lang.Number")); 945 assertEquals(false, classNameMatches("sun.Foobar", 946 "java.***", 947 "sun.management.***", 948 "!java.lang.Number")); 949 assertEquals(true, classNameMatches("sun.management.Foobar", 950 "java.***", 951 "sun.management.***", 952 "!java.lang.Number")); 953 assertEquals(true, classNameMatches("java.Foobar", 954 "java.***", 955 "sun.management.***", 956 "!java.lang.Number")); 957 assertEquals(true, classNameMatches("java.util.Foobar", 958 "java.lang.***", 959 "java.util.*", 960 "java.util.concurrent.***", 961 "java.util.logging.***", 962 "sun.reflect.***", 963 "!java.lang.Number")); 964 assertEquals(true, classNameMatches("java.util.concurrent.Foobar", 965 "java.lang.***", 966 "java.util.*", 967 "java.util.concurrent.***", 968 "java.util.logging.***", 969 "sun.reflect.***", 970 "!java.lang.Number")); 971 assertEquals(true, classNameMatches("java.util.logging.Foobar", 972 "java.lang.***", 973 "java.util.*", 974 "java.util.concurrent.***", 975 "java.util.logging.***", 976 "sun.reflect.***", 977 "!java.lang.Number")); 978 assertEquals(false, classNameMatches("java.util.foobar.Foobar", 979 "java.lang.***", 980 "java.util.*", 981 "java.util.concurrent.***", 982 "java.util.logging.***", 983 "sun.reflect.***", 984 "!java.lang.Number")); 985 assertEquals(true, classNameMatches("java/util/Foobar", 986 "java/util/*")); 987 assertEquals(false, classNameMatches("java.util.foo.Foobar", 988 "java.util.*")); 989 assertEquals(true, classNameMatches("java.util.foo.Foobar", 990 "java.util.foo.[a-mA-M]*")); 991 assertEquals(false, classNameMatches("java.util.foo.Foobar", 992 "java.util.foo.[n-zN-Z]*")); 993 assertEquals(true, classNameMatches("java.util.AbstractCollection", 994 "java.util.*Collection*")); 995 assertEquals(true, classNameMatches("java.lang.ref.Reference$1", 996 "java.lang.ref.Reference$*")); 997 assertEquals(false, classNameMatches("java.lang.ref.ReferenceQueue", 998 "java.lang.ref.Reference$*")); 999 assertEquals(true, classNameMatches("edu.rice.cs.cunit.instrumentors.replay.CompactSynchronizedBlockReplayStrategy", 1000 "***Synchronized*")); 1001 assertEquals(false, classNameMatches("anything", 1002 "")); 1003 assertEquals(false, classNameMatches("anything")); 1004 assertEquals(true, classNameMatches("java.lang.Integer", 1005 "***", 1006 "!java.lang.Object", 1007 "!java.lang.String", 1008 "!java.util.zip.*")); 1009 assertEquals(false, classNameMatches("java.lang.Object", 1010 "***", 1011 "!java.lang.Object", 1012 "!java.lang.String", 1013 "!java.util.zip.*")); 1014 assertEquals(false, classNameMatches("java.lang.String", 1015 "***", 1016 "!java.lang.Object", 1017 "!java.lang.String", 1018 "!java.util.zip.*")); 1019 assertEquals(false, classNameMatches("java.util.zip.Inflater", 1020 "***", 1021 "!java.lang.Object", 1022 "!java.lang.String", 1023 "!java.util.zip.*")); 1024 assertEquals(true, classNameMatches("loadClass(Ljava/lang/String;Z)Ljava/lang/Class;", 1025 "loadClass(Ljava/lang/String;Z)Ljava/lang/Class;")); 1026 assertEquals(true, classNameMatches("java.lang.ref.Finalizer$FinalizerThread", 1027 "java.lang.ref.Finalizer$FinalizerThread")); 1028 } 1029 } 1030 }