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 }