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;
  }
Example #3
0
 /**
  * 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);
 }