public class CbeckMustOverrideSuperAnnotation extends OpcodeStackDetector { BugReporter bugReporter; ClassDescriptor mustOverrideAnnotation = DescriptorFactory.createClassDescriptor(OverridingMethodsMustInvokeSuper.class); public CbeckMustOverrideSuperAnnotation(BugReporter bugReporter) { this.bugReporter = bugReporter; } private boolean sawCallToSuper; @Override public void visit(Code code) { if (getMethod().isStatic() || getMethod().isPrivate()) return; XMethod overrides = Lookup.findSuperImplementorAsXMethod( getThisClass(), getMethodName(), getMethodSig(), bugReporter); if (overrides == null) return; AnnotationValue annotation = overrides.getAnnotation(mustOverrideAnnotation); if (annotation == null) return; sawCallToSuper = false; super.visit(code); if (!sawCallToSuper) bugReporter.reportBug( new BugInstance(this, "TESTING", NORMAL_PRIORITY).addClassAndMethod(this)); } /* * (non-Javadoc) * * @see edu.umd.cs.findbugs.bcel.OpcodeStackDetector#sawOpcode(int) */ @Override public void sawOpcode(int seen) { if (seen != INVOKESPECIAL) return; String calledClassName = getClassConstantOperand(); String calledMethodName = getNameConstantOperand(); String calledMethodSig = getSigConstantOperand(); if (calledClassName.equals(getSuperclassName()) && calledMethodName.equals(getMethodName()) && calledMethodSig.equals(getMethodSig())) { sawCallToSuper = true; } } }
private boolean isGenericCollection(ClassDescriptor operandClass) { String dottedClassName = operandClass.getDottedClassName(); if (baseGenericTypes.contains(dottedClassName)) return true; String found = null; for (String c : baseGenericTypes) { if (Subtypes2.instanceOf(operandClass, c)) { found = c; break; } } if (found == null) return false; if (dottedClassName.startsWith("java.util.") || dottedClassName.startsWith("com.google.common.collect.")) return true; try { XClass xclass = Global.getAnalysisCache().getClassAnalysis(XClass.class, operandClass); String sig = xclass.getSourceSignature(); if (sig == null) return false; String typeParameter = null; List<String> split = GenericUtilities.split(sig, true); if (sig.charAt(0) == '<') { int end = sig.indexOf(':'); if (end > 0) typeParameter = sig.substring(1, end); } if (DEBUG) System.out.println(dottedClassName + " " + typeParameter + " " + split); for (String s : split) { int i = s.indexOf('<'); if (i < 0) continue; if (s.charAt(0) != 'L') throw new IllegalStateException("unexpected non signature: " + s); ClassDescriptor c = DescriptorFactory.createClassDescriptor(s.substring(1, i)); String superTypeParameter = s.substring(i + 1); if (isGenericCollection(c) && (typeParameter == null || superTypeParameter.startsWith("T" + typeParameter))) { if (DEBUG) System.out.println(operandClass + " is a subtype of " + s); return true; } } if (DEBUG) System.out.println("Not a subtype"); } catch (CheckedAnalysisException e1) { AnalysisContext.logError( "Error checking for weird generic parameterization of " + operandClass, e1); } return false; }
/** * Return whether or not the given class is an application class. * * @param cls the class to lookup * @return true if the class is an application class, false if not an application class or if the * class cannot be located */ public boolean isApplicationClass(JavaClass cls) { // return getSubtypes().isApplicationClass(cls); return getSubtypes2().isApplicationClass(DescriptorFactory.createClassDescriptor(cls)); }
public TypeQualifierNullnessAnnotationDatabase() { ClassDescriptor nonnullClassDesc = DescriptorFactory.createClassDescriptor(javax.annotation.Nonnull.class); this.nonnullTypeQualifierValue = TypeQualifierValue.getValue(nonnullClassDesc, null); }
/** * Implementation of INullnessAnnotationDatabase that is based on JSR-305 type qualifiers. * * @author David Hovemeyer */ public class TypeQualifierNullnessAnnotationDatabase implements INullnessAnnotationDatabase { private static final boolean DEBUG = SystemProperties.getBoolean("findbugs.npe.tq.debug"); public final TypeQualifierValue nonnullTypeQualifierValue; public TypeQualifierNullnessAnnotationDatabase() { ClassDescriptor nonnullClassDesc = DescriptorFactory.createClassDescriptor(javax.annotation.Nonnull.class); this.nonnullTypeQualifierValue = TypeQualifierValue.getValue(nonnullClassDesc, null); } /* (non-Javadoc) * @see edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#getResolvedAnnotation(java.lang.Object, boolean) */ public NullnessAnnotation getResolvedAnnotation(Object o, boolean getMinimal) { Profiler profiler = Global.getAnalysisCache().getProfiler(); profiler.start(this.getClass()); try { if (DEBUG) { System.out.println("getResolvedAnnotation: o=" + o + "..."); } TypeQualifierAnnotation tqa = null; if (o instanceof XMethodParameter) { XMethodParameter param = (XMethodParameter) o; tqa = TypeQualifierApplications.getEffectiveTypeQualifierAnnotation( param.getMethod(), param.getParameterNumber(), nonnullTypeQualifierValue); } else if (o instanceof XMethod || o instanceof XField) { tqa = TypeQualifierApplications.getEffectiveTypeQualifierAnnotation( (AnnotatedObject) o, nonnullTypeQualifierValue); } NullnessAnnotation result = toNullnessAnnotation(tqa); if (DEBUG) { System.out.println(" ==> " + (result != null ? result.toString() : "not found")); } return result; } finally { profiler.end(this.getClass()); } } /* (non-Javadoc) * @see edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#parameterMustBeNonNull(edu.umd.cs.findbugs.ba.XMethod, int) */ public boolean parameterMustBeNonNull(XMethod m, int param) { if (DEBUG) { System.out.print("Checking " + m + " param " + param + " for @Nonnull..."); } TypeQualifierAnnotation tqa = TypeQualifierApplications.getEffectiveTypeQualifierAnnotation( m, param, nonnullTypeQualifierValue); if (tqa == null && param == 0) { String name = m.getName(); String signature = m.getSignature(); if (name.equals("main") && signature.equals("([Ljava/lang/String;)V") && m.isStatic() && m.isPublic()) return true; else if (NullnessAnnotationDatabase.assertsFirstParameterIsNonnull(m)) return true; else if (name.equals("compareTo") && signature.substring(signature.indexOf(";") + 1).equals(")Z") && !m.isStatic()) return true; } boolean answer = (tqa != null) && tqa.when == When.ALWAYS; if (DEBUG) { System.out.println(answer ? "yes" : "no"); } return answer; } // NOTE: // The way we handle adding default annotations is to actually add AnnotationValues // to the corresponding XFoo objects, giving the illusion that the annotations // were actually read from the underlying class files. /** * Convert a NullnessAnnotation into the ClassDescriptor of the equivalent JSR-305 nullness type * qualifier. * * @param n a NullnessAnnotation * @return ClassDescriptor of the equivalent JSR-305 nullness type qualifier */ private ClassDescriptor getNullnessAnnotationClassDescriptor(NullnessAnnotation n) { if (n == NullnessAnnotation.CHECK_FOR_NULL) { return JSR305NullnessAnnotations.CHECK_FOR_NULL; } else if (n == NullnessAnnotation.NONNULL) { return JSR305NullnessAnnotations.NONNULL; } else if (n == NullnessAnnotation.NULLABLE) { return JSR305NullnessAnnotations.NULLABLE; } else if (n == NullnessAnnotation.UNKNOWN_NULLNESS) { return JSR305NullnessAnnotations.NULLABLE; } else { throw new IllegalArgumentException("Unknown NullnessAnnotation: " + n); } } private static final ClassDescriptor PARAMETERS_ARE_NONNULL_BY_DEFAULT = DescriptorFactory.createClassDescriptor(javax.annotation.ParametersAreNonnullByDefault.class); private static final ClassDescriptor RETURN_VALUES_ARE_NONNULL_BY_DEFAULT = DescriptorFactory.createClassDescriptor( edu.umd.cs.findbugs.annotations.ReturnValuesAreNonnullByDefault.class); /* (non-Javadoc) * @see edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#addDefaultAnnotation(java.lang.String, java.lang.String, edu.umd.cs.findbugs.ba.NullnessAnnotation) */ public void addDefaultAnnotation(Target target, String c, NullnessAnnotation n) { if (DEBUG) { System.out.println("addDefaultAnnotation: target=" + target + ", c=" + c + ", n=" + n); } ClassDescriptor classDesc = DescriptorFactory.instance().getClassDescriptorForDottedClassName(c); ClassInfo xclass; // Get the XClass (really a ClassInfo object) try { xclass = (ClassInfo) Global.getAnalysisCache().getClassAnalysis(XClass.class, classDesc); } catch (MissingClassException e) { // // AnalysisContext.currentAnalysisContext().getLookupFailureCallback().reportMissingClass(e.getClassDescriptor()); return; } catch (CheckedAnalysisException e) { // AnalysisContext.logError("Error adding built-in nullness annotation", e); return; } if (n == NullnessAnnotation.NONNULL && target == AnnotationDatabase.Target.PARAMETER) { xclass.addAnnotation(new AnnotationValue(PARAMETERS_ARE_NONNULL_BY_DEFAULT)); return; } else if (n == NullnessAnnotation.NONNULL && target == AnnotationDatabase.Target.METHOD) { xclass.addAnnotation(new AnnotationValue(RETURN_VALUES_ARE_NONNULL_BY_DEFAULT)); return; } // Get the default annotation type ClassDescriptor defaultAnnotationType; if (target == AnnotationDatabase.Target.ANY) { defaultAnnotationType = FindBugsDefaultAnnotations.DEFAULT_ANNOTATION; } else if (target == AnnotationDatabase.Target.FIELD) { defaultAnnotationType = FindBugsDefaultAnnotations.DEFAULT_ANNOTATION_FOR_FIELDS; } else if (target == AnnotationDatabase.Target.METHOD) { defaultAnnotationType = FindBugsDefaultAnnotations.DEFAULT_ANNOTATION_FOR_METHODS; } else if (target == AnnotationDatabase.Target.PARAMETER) { defaultAnnotationType = FindBugsDefaultAnnotations.DEFAULT_ANNOTATION_FOR_PARAMETERS; } else { throw new IllegalArgumentException("Unknown target for default annotation: " + target); } // Get the JSR-305 nullness annotation type ClassDescriptor nullnessAnnotationType = getNullnessAnnotationClassDescriptor(n); // Construct an AnnotationValue containing the default annotation AnnotationValue annotationValue = new AnnotationValue(defaultAnnotationType); AnnotationVisitor v = annotationValue.getAnnotationVisitor(); v.visit("value", Type.getObjectType(nullnessAnnotationType.getClassName())); v.visitEnd(); if (DEBUG) { System.out.println("Adding AnnotationValue " + annotationValue + " to class " + xclass); } // Destructively add the annotation to the ClassInfo object xclass.addAnnotation(annotationValue); } // /* (non-Javadoc) // * @see // edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#addDefaultMethodAnnotation(java.lang.String, // edu.umd.cs.findbugs.ba.NullnessAnnotation) // */ // public void addDefaultMethodAnnotation(String name, NullnessAnnotation annotation) { // } /* (non-Javadoc) * @see edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#addFieldAnnotation(java.lang.String, java.lang.String, java.lang.String, boolean, edu.umd.cs.findbugs.ba.NullnessAnnotation) */ public void addFieldAnnotation( String cName, String mName, String mSig, boolean isStatic, NullnessAnnotation annotation) { if (DEBUG) { System.out.println( "addFieldAnnotation: annotate " + cName + "." + mName + " with " + annotation); } XField xfield = XFactory.createXField(cName, mName, mSig, isStatic); if (!(xfield instanceof FieldInfo)) { if (DEBUG) { System.out.println( " Field not found! " + cName + "." + mName + ":" + mSig + " " + isStatic + " " + annotation); } return; } // Get JSR-305 nullness annotation type ClassDescriptor nullnessAnnotationType = getNullnessAnnotationClassDescriptor(annotation); // Create an AnnotationValue AnnotationValue annotationValue = new AnnotationValue(nullnessAnnotationType); // Destructively add the annotation to the FieldInfo object ((FieldInfo) xfield).addAnnotation(annotationValue); } public @CheckForNull XMethod getXMethod( String cName, String mName, String sig, boolean isStatic) { ClassDescriptor classDesc = DescriptorFactory.instance().getClassDescriptorForDottedClassName(cName); ClassInfo xclass; // Get the XClass (really a ClassInfo object) try { xclass = (ClassInfo) Global.getAnalysisCache().getClassAnalysis(XClass.class, classDesc); } catch (MissingClassException e) { if (DEBUG) { System.out.println(" Class not found!"); } // // AnalysisContext.currentAnalysisContext().getLookupFailureCallback().reportMissingClass(e.getClassDescriptor()); return null; } catch (CheckedAnalysisException e) { if (DEBUG) { System.out.println(" Class not found!"); } // AnalysisContext.logError("Error adding built-in nullness annotation", e); return null; } XMethod xmethod = xclass.findMethod(mName, sig, isStatic); if (xmethod == null) xmethod = XFactory.createXMethod(cName, mName, sig, isStatic); return xmethod; } /* (non-Javadoc) * @see edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#addMethodAnnotation(java.lang.String, java.lang.String, java.lang.String, boolean, edu.umd.cs.findbugs.ba.NullnessAnnotation) */ public void addMethodAnnotation( String cName, String mName, String sig, boolean isStatic, NullnessAnnotation annotation) { if (DEBUG) { System.out.println( "addMethodAnnotation: annotate " + cName + "." + mName + " with " + annotation); } XMethod xmethod = getXMethod(cName, mName, sig, isStatic); if (xmethod == null) return; // Get JSR-305 nullness annotation type ClassDescriptor nullnessAnnotationType = getNullnessAnnotationClassDescriptor(annotation); // Create an AnnotationValue AnnotationValue annotationValue = new AnnotationValue(nullnessAnnotationType); // Destructively add the annotation to the MethodInfo object xmethod.addAnnotation(annotationValue); } /* (non-Javadoc) * @see edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#addMethodParameterAnnotation(java.lang.String, java.lang.String, java.lang.String, boolean, int, edu.umd.cs.findbugs.ba.NullnessAnnotation) */ public void addMethodParameterAnnotation( @DottedClassName String cName, String mName, String sig, boolean isStatic, int param, NullnessAnnotation annotation) { if (DEBUG) { System.out.println( "addMethodParameterAnnotation: annotate " + cName + "." + mName + " param " + param + " with " + annotation); } XMethod xmethod = getXMethod(cName, mName, sig, isStatic); if (xmethod == null) return; // Get JSR-305 nullness annotation type ClassDescriptor nullnessAnnotationType = getNullnessAnnotationClassDescriptor(annotation); // Create an AnnotationValue AnnotationValue annotationValue = new AnnotationValue(nullnessAnnotationType); if (!xmethod.getClassName().equals(cName)) { if (false) AnalysisContext.logError( "Could not fully resolve method " + cName + "." + mName + sig + " to apply annotation " + annotation); return; } // Destructively add the annotation to the MethodInfo object xmethod.addParameterAnnotation(param, annotationValue); } /* (non-Javadoc) * @see edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#loadAuxiliaryAnnotations() */ public void loadAuxiliaryAnnotations() { DefaultNullnessAnnotations.addDefaultNullnessAnnotations(this); } /** * Convert a Nonnull-based TypeQualifierAnnotation into a NullnessAnnotation. * * @param tqa Nonnull-based TypeQualifierAnnotation * @return corresponding NullnessAnnotation */ private NullnessAnnotation toNullnessAnnotation(@CheckForNull TypeQualifierAnnotation tqa) { if (tqa == null) { return null; } switch (tqa.when) { case ALWAYS: return NullnessAnnotation.NONNULL; case MAYBE: return NullnessAnnotation.CHECK_FOR_NULL; case NEVER: return NullnessAnnotation.CHECK_FOR_NULL; case UNKNOWN: return NullnessAnnotation.UNKNOWN_NULLNESS; } throw new IllegalStateException(); } }
public InconsistentAnnotations(BugReporter reporter) { ClassDescriptor nonnullClassDesc = DescriptorFactory.createClassDescriptor(javax.annotation.Nonnull.class); this.nonnullTypeQualifierValue = TypeQualifierValue.getValue(nonnullClassDesc, null); this.reporter = reporter; }
@Override public void visit(JavaClass obj) { String superClassname = obj.getSuperclassName(); // System.out.println("superclass of " + getClassName() + " is " + superClassname); isEnum = superClassname.equals("java.lang.Enum"); if (isEnum) return; int flags = obj.getAccessFlags(); isAbstract = (flags & ACC_ABSTRACT) != 0 || (flags & ACC_INTERFACE) != 0; isAnonymousInnerClass = anonymousInnerClassNamePattern.matcher(getClassName()).matches(); innerClassHasOuterInstance = false; for (Field f : obj.getFields()) { if (f.getName().equals("this$0")) { innerClassHasOuterInstance = true; break; } } sawSerialVersionUID = false; isSerializable = implementsSerializableDirectly = false; isExternalizable = false; directlyImplementsExternalizable = false; isGUIClass = false; isEjbImplClass = false; seenTransientField = false; // boolean isEnum = obj.getSuperclassName().equals("java.lang.Enum"); fieldsThatMightBeAProblem.clear(); transientFieldsUpdates.clear(); transientFieldsSetInConstructor.clear(); transientFieldsSetToDefaultValueInConstructor.clear(); // isRemote = false; // Does this class directly implement Serializable? String[] interface_names = obj.getInterfaceNames(); for (String interface_name : interface_names) { if (interface_name.equals("java.io.Externalizable")) { directlyImplementsExternalizable = true; isExternalizable = true; if (DEBUG) { System.out.println("Directly implements Externalizable: " + getClassName()); } } else if (interface_name.equals("java.io.Serializable")) { implementsSerializableDirectly = true; isSerializable = true; if (DEBUG) { System.out.println("Directly implements Serializable: " + getClassName()); } break; } } // Does this class indirectly implement Serializable? if (!isSerializable) { if (Subtypes2.instanceOf(obj, "java.io.Externalizable")) { isExternalizable = true; if (DEBUG) { System.out.println("Indirectly implements Externalizable: " + getClassName()); } } if (Subtypes2.instanceOf(obj, "java.io.Serializable")) { isSerializable = true; if (DEBUG) { System.out.println("Indirectly implements Serializable: " + getClassName()); } } } hasPublicVoidConstructor = false; superClassHasVoidConstructor = true; superClassHasReadObject = false; superClassImplementsSerializable = isSerializable && !implementsSerializableDirectly; ClassDescriptor superclassDescriptor = getXClass().getSuperclassDescriptor(); if (superclassDescriptor != null) try { XClass superXClass = Global.getAnalysisCache().getClassAnalysis(XClass.class, superclassDescriptor); if (superXClass != null) { superClassImplementsSerializable = AnalysisContext.currentAnalysisContext() .getSubtypes2() .isSubtype( superXClass.getClassDescriptor(), DescriptorFactory.createClassDescriptor(java.io.Serializable.class)); superClassHasVoidConstructor = false; for (XMethod m : superXClass.getXMethods()) { if (m.getName().equals("<init>") && m.getSignature().equals("()V") && !m.isPrivate()) { superClassHasVoidConstructor = true; } if (m.getName().equals("readObject") && m.getSignature().equals("(Ljava/io/ObjectInputStream;)V") && m.isPrivate()) superClassHasReadObject = true; } } } catch (ClassNotFoundException e) { bugReporter.reportMissingClass(e); } catch (CheckedAnalysisException e) { bugReporter.logError("huh", e); } // Is this a GUI or other class that is rarely serialized? isGUIClass = false; isEjbImplClass = false; if (true || !directlyImplementsExternalizable && !implementsSerializableDirectly) { isEjbImplClass = Subtypes2.instanceOf(obj, "javax.ejb.SessionBean"); isGUIClass = (Subtypes2.instanceOf(obj, "java.lang.Throwable") || Subtypes2.instanceOf(obj, "java.awt.Component") || Subtypes2.instanceOf(obj, "java.awt.Component$AccessibleAWTComponent") || Subtypes2.instanceOf(obj, "java.awt.event.ActionListener") || Subtypes2.instanceOf(obj, "java.util.EventListener")); if (!isGUIClass) { JavaClass o = obj; while (o != null) { if (o.getClassName().startsWith("java.awt") || o.getClassName().startsWith("javax.swing")) { isGUIClass = true; break; } try { o = o.getSuperClass(); } catch (ClassNotFoundException e) { break; } } } } foundSynthetic = false; foundSynchronizedMethods = false; writeObjectIsSynchronized = false; sawReadExternal = sawWriteExternal = sawReadObject = sawReadResolve = sawWriteObject = false; if (isSerializable) { for (Method m : obj.getMethods()) { if (m.getName().equals("readObject") && m.getSignature().equals("(Ljava/io/ObjectInputStream;)V")) sawReadObject = true; else if (m.getName().equals("readResolve") && m.getSignature().startsWith("()")) sawReadResolve = true; else if (m.getName().equals("readObjectNoData") && m.getSignature().equals("()V")) sawReadObject = true; else if (m.getName().equals("writeObject") && m.getSignature().equals("(Ljava/io/ObjectOutputStream;)V")) sawWriteObject = true; } for (Field f : obj.getFields()) { if (f.isTransient()) seenTransientField = true; } } }
public static double isDeepSerializable(JavaClass x) throws ClassNotFoundException { if (storedException != null) throw storedException; if (x.getClassName().equals("java.lang.Object")) return 0.4; if (DEBUG) { System.out.println("checking " + x.getClassName()); } double result = Analyze.deepInstanceOf(x, serializable); if (result >= 0.9) { if (DEBUG) { System.out.println("Direct high serializable result: " + result); } return result; } if (x.isFinal()) return result; double collectionResult = Analyze.deepInstanceOf(x, collection); double mapResult = Analyze.deepInstanceOf(x, map); if (x.isInterface() || x.isAbstract()) { result = Math.max(result, Math.max(mapResult, collectionResult) * 0.95); if (result >= 0.9) { return result; } } ClassDescriptor classDescriptor = DescriptorFactory.createClassDescriptor(x); Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2(); Set<ClassDescriptor> directSubtypes = subtypes2.getDirectSubtypes(classDescriptor); directSubtypes.remove(classDescriptor); double confidence = 0.6; if (x.isAbstract() || x.isInterface()) { confidence = 0.8; result = Math.max(result, 0.4); } else if (directSubtypes.isEmpty()) confidence = 0.2; double confidence2 = (1 + confidence) / 2; result = Math.max(result, confidence2 * collectionResult); if (result >= 0.9) { if (DEBUG) { System.out.println("High collection result: " + result); } return result; } result = Math.max(result, confidence2 * mapResult); if (result >= 0.9) { if (DEBUG) { System.out.println("High map result: " + result); } return result; } result = Math.max(result, confidence2 * 0.5 * Analyze.deepInstanceOf(x, comparator)); if (result >= 0.9) { if (DEBUG) { System.out.println("High comparator result: " + result); } return result; } for (ClassDescriptor subtype : directSubtypes) { JavaClass subJavaClass = Repository.lookupClass(subtype.getDottedClassName()); result = Math.max(result, confidence * Analyze.deepInstanceOf(subJavaClass, serializable)); // result = Math.max(result, confidence * isDeepSerializable(subJavaClass)); if (result >= 0.9) { return result; } } if (DEBUG) { System.out.println("No high results; max: " + result); } return result; }
private TypeQualifierValue(ClassDescriptor typeQualifier, @CheckForNull Object value) { this.typeQualifier = typeQualifier; this.value = value; boolean isStrict = false; // will be set to true if this is a strict // type qualifier value boolean isExclusive = false; // will be set to true if this is an // exclusive type qualifier value boolean isExhaustive = false; // will be set to true if this is an // exhaustive type qualifier value TypeQualifierValidator<A> validator = null; Class<A> qualifierClass = null; A proxy = null; try { XClass xclass = Global.getAnalysisCache().getClassAnalysis(XClass.class, typeQualifier); // Annotation elements appear as abstract methods in the annotation // class (interface). // So, if the type qualifier annotation has specified a default When // value, // it will appear as an abstract method called "when". XMethod whenMethod = xclass.findMethod("when", "()Ljavax/annotation/meta/When;", false); if (whenMethod == null) { isStrict = true; } for (XMethod xmethod : xclass.getXMethods()) { if (xmethod.getName().equals("value") && xmethod.getSignature().startsWith("()")) { isExhaustive = xmethod.getAnnotation(EXHAUSTIVE_ANNOTATION) != null; if (isExhaustive) { // exhaustive qualifiers are automatically exclusive isExclusive = true; } else { // see if there is an explicit @Exclusive annotation isExclusive = xmethod.getAnnotation(EXCLUSIVE_ANNOTATION) != null; } break; } } } catch (MissingClassException e) { AnalysisContext.currentAnalysisContext() .getLookupFailureCallback() .reportMissingClass(e.getClassNotFoundException()); } catch (CheckedAnalysisException e) { AnalysisContext.logError( "Error looking up annotation class " + typeQualifier.toDottedClassName(), e); } this.isStrict = isStrict; this.isExclusive = isExclusive; this.isExhaustive = isExhaustive; ClassDescriptor checkerName = DescriptorFactory.createClassDescriptor(typeQualifier.getClassName() + "$Checker"); try { Global.getAnalysisCache().getClassAnalysis(ClassData.class, checkerName); // found it. // System.out.println(checkerName); SecurityManager m = System.getSecurityManager(); if (m == null) System.setSecurityManager(new ValidationSecurityManager()); Class<?> c = validatorLoader.loadClass(checkerName.getDottedClassName()); if (TypeQualifierValidator.class.isAssignableFrom(c)) { Class<? extends TypeQualifierValidator> checkerClass = c.asSubclass(TypeQualifierValidator.class); validator = getValidator(checkerClass); qualifierClass = getQualifierClass(typeQualifier); InvocationHandler handler = new InvocationHandler() { public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable { if (arg1.getName() == "value") return TypeQualifierValue.this.value; throw new UnsupportedOperationException("Can't handle " + arg1); } }; proxy = qualifierClass.cast( Proxy.newProxyInstance(validatorLoader, new Class[] {qualifierClass}, handler)); } } catch (ClassNotFoundException e) { assert true; // ignore } catch (CheckedAnalysisException e) { assert true; // ignore } catch (Exception e) { AnalysisContext.logError("Unable to construct type qualifier checker " + checkerName, e); } catch (Throwable e) { AnalysisContext.logError( "Unable to construct type qualifier checker " + checkerName + " due to " + e.getClass().getSimpleName() + ":" + e.getMessage()); } this.validator = validator; this.typeQualifierClass = qualifierClass; this.proxy = proxy; }
@SuppressWarnings("unchecked") public static @Nonnull <A extends Annotation> TypeQualifierValue<A> getValue( Class<A> clazz, Object value) { return (TypeQualifierValue<A>) getValue(DescriptorFactory.createClassDescriptor(clazz), value); }