1 package net.sf.clirr.core.internal.bcel;
2
3 import net.sf.clirr.core.CheckerException;
4 import net.sf.clirr.core.spi.Scope;
5
6 import org.apache.bcel.Constants;
7 import org.apache.bcel.classfile.Attribute;
8 import org.apache.bcel.classfile.ConstantClass;
9 import org.apache.bcel.classfile.ConstantPool;
10 import org.apache.bcel.classfile.ConstantUtf8;
11 import org.apache.bcel.classfile.InnerClass;
12 import org.apache.bcel.classfile.InnerClasses;
13 import org.apache.bcel.classfile.JavaClass;
14 import org.apache.bcel.util.Repository;
15
16 final class BcelScopeHelper
17 {
18
19 /***
20 *
21 *
22 */
23 private BcelScopeHelper()
24 {
25 }
26
27 /***
28 * Get a Scope object representing the accessibility of the specified
29 * object.
30 * <p>
31 * Note that this method gives the wrong results for JavaClass objects
32 * which are nested classes. Use getClassScope(jclass) instead.
33 */
34 public static Scope getScope(int accessFlags)
35 {
36 if ((accessFlags & Constants.ACC_PUBLIC) > 0)
37 {
38 return Scope.PUBLIC;
39 }
40
41 if ((accessFlags & Constants.ACC_PROTECTED) > 0)
42 {
43 return Scope.PROTECTED;
44 }
45
46 if ((accessFlags & Constants.ACC_PRIVATE) > 0)
47 {
48 return Scope.PRIVATE;
49 }
50
51 return Scope.PACKAGE;
52 }
53
54 /***
55 * Java class files only ever contain scope specifiers of "public" or
56 * "package". For top-level classes, this is expected: it is not possible
57 * to have a top-level protected or private class.
58 * <p>
59 * However nested classes <i>can</i> be declared as protected or private. The
60 * way to tell the real scope of a nested class is to ignore the scope in
61 * the actual class file itself, and instead look in the "InnerClasses"
62 * attribute stored on the enclosing class. This is exactly what the java
63 * compiler does when compiling, and what the jvm does when verifying class
64 * linkage at runtime.
65 * <p>
66 * For a "top-level" class, this method just returns the access scope for
67 * the class itself. For nested classes, the enclosing class of the
68 * specified class is retrieved and its InnerClasses attribute checked to
69 * find the true scope for the specified class.
70 * <p>
71 * @throws CheckerException if the specified class is a nested class and
72 * the enclosing class could not be found, or if the supposedly enclosing
73 * class has no reference to the nested class. This exception is not
74 * expected to occur in practice, unless a truly screwed-up jar file is
75 * passed to clirr for inspection.
76 */
77 public static Scope getClassScope(JavaClass jclass) throws CheckerException
78 {
79 int dollarPos = jclass.getClassName().lastIndexOf('$');
80 if (dollarPos == -1)
81 {
82
83 return getScope(jclass.getAccessFlags());
84 }
85
86
87 String jclassName = jclass.getClassName();
88 String enclosingClassName = jclassName.substring(0, dollarPos);
89 Repository repo = jclass.getRepository();
90 JavaClass enclosingClass = repo.findClass(enclosingClassName);
91
92 if (enclosingClass == null)
93 {
94 throw new CheckerException(
95 "Unable to locate enclosing class " + enclosingClassName
96 + " for nested class " + jclassName);
97 }
98
99 ConstantPool pool = enclosingClass.getConstantPool();
100 Attribute[] attrs = enclosingClass.getAttributes();
101 for (int i = 0; i < attrs.length; ++i)
102 {
103 if (attrs[i] instanceof InnerClasses)
104 {
105 InnerClasses ics = (InnerClasses) attrs[i];
106 InnerClass[] icarray = ics.getInnerClasses();
107 for (int j = 0; j < icarray.length; ++j)
108 {
109
110
111
112 InnerClass ic = icarray[j];
113 int classIndex = ic.getInnerClassIndex();
114 ConstantClass constClass = (ConstantClass) pool.getConstant(classIndex);
115 int nameIndex = constClass.getNameIndex();
116 ConstantUtf8 nameconst = (ConstantUtf8) pool.getConstant(nameIndex);
117 String classname = nameconst.getBytes().replace('/', '.');
118 if (jclassName.equals(classname))
119 {
120 return getScope(ic.getInnerAccessFlags());
121 }
122 }
123 }
124 }
125
126
127 throw new CheckerException(
128 "Unable to find information in class " + enclosingClass.getClassName()
129 + " referring back to nested class " + jclassName);
130
131 }
132
133 }