View Javadoc

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              // not a nested class
83              return getScope(jclass.getAccessFlags());
84          }
85  
86          // ok this is a nested class
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                     // in the code below, instanceof checks should not be necessary
110                     // before casting Constants because the classfile format ensures
111                     // that instanceof would always be true
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         // weird; no nested class info found
127         throw new CheckerException(
128             "Unable to find information in class " + enclosingClass.getClassName()
129             + " referring back to nested class " + jclassName);
130 
131     }
132 
133 }