001: package net.sf.clirr.core.internal.bcel;
002:
003: import net.sf.clirr.core.CheckerException;
004: import net.sf.clirr.core.spi.Scope;
005:
006: import org.apache.bcel.Constants;
007: import org.apache.bcel.classfile.Attribute;
008: import org.apache.bcel.classfile.ConstantClass;
009: import org.apache.bcel.classfile.ConstantPool;
010: import org.apache.bcel.classfile.ConstantUtf8;
011: import org.apache.bcel.classfile.InnerClass;
012: import org.apache.bcel.classfile.InnerClasses;
013: import org.apache.bcel.classfile.JavaClass;
014: import org.apache.bcel.util.Repository;
015:
016: final class BcelScopeHelper {
017:
018: /**
019: *
020: *
021: */
022: private BcelScopeHelper() {
023: }
024:
025: /**
026: * Get a Scope object representing the accessibility of the specified
027: * object.
028: * <p>
029: * Note that this method gives the wrong results for JavaClass objects
030: * which are nested classes. Use getClassScope(jclass) instead.
031: */
032: public static Scope getScope(int accessFlags) {
033: if ((accessFlags & Constants.ACC_PUBLIC) > 0) {
034: return Scope.PUBLIC;
035: }
036:
037: if ((accessFlags & Constants.ACC_PROTECTED) > 0) {
038: return Scope.PROTECTED;
039: }
040:
041: if ((accessFlags & Constants.ACC_PRIVATE) > 0) {
042: return Scope.PRIVATE;
043: }
044:
045: return Scope.PACKAGE;
046: }
047:
048: /**
049: * Java class files only ever contain scope specifiers of "public" or
050: * "package". For top-level classes, this is expected: it is not possible
051: * to have a top-level protected or private class.
052: * <p>
053: * However nested classes <i>can</i> be declared as protected or private. The
054: * way to tell the real scope of a nested class is to ignore the scope in
055: * the actual class file itself, and instead look in the "InnerClasses"
056: * attribute stored on the enclosing class. This is exactly what the java
057: * compiler does when compiling, and what the jvm does when verifying class
058: * linkage at runtime.
059: * <p>
060: * For a "top-level" class, this method just returns the access scope for
061: * the class itself. For nested classes, the enclosing class of the
062: * specified class is retrieved and its InnerClasses attribute checked to
063: * find the true scope for the specified class.
064: * <p>
065: * @throws CheckerException if the specified class is a nested class and
066: * the enclosing class could not be found, or if the supposedly enclosing
067: * class has no reference to the nested class. This exception is not
068: * expected to occur in practice, unless a truly screwed-up jar file is
069: * passed to clirr for inspection.
070: */
071: public static Scope getClassScope(JavaClass jclass)
072: throws CheckerException {
073: int dollarPos = jclass.getClassName().lastIndexOf('$');
074: if (dollarPos == -1) {
075: // not a nested class
076: return getScope(jclass.getAccessFlags());
077: }
078:
079: // ok this is a nested class
080: String jclassName = jclass.getClassName();
081: String enclosingClassName = jclassName.substring(0, dollarPos);
082: Repository repo = jclass.getRepository();
083: JavaClass enclosingClass = repo.findClass(enclosingClassName);
084:
085: if (enclosingClass == null) {
086: throw new CheckerException(
087: "Unable to locate enclosing class "
088: + enclosingClassName + " for nested class "
089: + jclassName);
090: }
091:
092: ConstantPool pool = enclosingClass.getConstantPool();
093: Attribute[] attrs = enclosingClass.getAttributes();
094: for (int i = 0; i < attrs.length; ++i) {
095: if (attrs[i] instanceof InnerClasses) {
096: InnerClasses ics = (InnerClasses) attrs[i];
097: InnerClass[] icarray = ics.getInnerClasses();
098: for (int j = 0; j < icarray.length; ++j) {
099: // in the code below, instanceof checks should not be necessary
100: // before casting Constants because the classfile format ensures
101: // that instanceof would always be true
102: InnerClass ic = icarray[j];
103: int classIndex = ic.getInnerClassIndex();
104: ConstantClass constClass = (ConstantClass) pool
105: .getConstant(classIndex);
106: int nameIndex = constClass.getNameIndex();
107: ConstantUtf8 nameconst = (ConstantUtf8) pool
108: .getConstant(nameIndex);
109: String classname = nameconst.getBytes().replace(
110: '/', '.');
111: if (jclassName.equals(classname)) {
112: return getScope(ic.getInnerAccessFlags());
113: }
114: }
115: }
116: }
117:
118: // weird; no nested class info found
119: throw new CheckerException(
120: "Unable to find information in class "
121: + enclosingClass.getClassName()
122: + " referring back to nested class "
123: + jclassName);
124:
125: }
126:
127: }
|