@Override
 public void visitField(Field obj) {
   if (!isInnerClass && obj.isPrivate() && !obj.isSynthetic()) {
     String sig = obj.getSignature();
     if (sig.startsWith(Values.SIG_QUALIFIED_CLASS_PREFIX)) {
       String type = SignatureUtils.stripSignature(sig);
       if (doesObjectNeedToBeWatched(type)) {
         fieldSpecialObjects.put(obj.getName(), obj.getSignature());
       }
     }
   }
 }
  public void addAllDefinitions(JavaClass obj) {
    String className2 = obj.getClassName();

    defined.add(className2);
    for (Method m : obj.getMethods())
      if (!m.isPrivate()) {
        String name = getMemberName(obj, className2, m.getNameIndex(), m.getSignatureIndex());
        defined.add(name);
      }
    for (Field f : obj.getFields())
      if (!f.isPrivate()) {
        String name = getMemberName(obj, className2, f.getNameIndex(), f.getSignatureIndex());
        defined.add(name);
      }
  }
  /**
   * Find a field with given name defined in given class.
   *
   * @param className the name of the class
   * @param fieldName the name of the field
   * @return the Field, or null if no such field could be found
   */
  public static Field findField(String className, String fieldName) throws ClassNotFoundException {
    JavaClass jclass = Repository.lookupClass(className);

    while (jclass != null) {
      Field[] fieldList = jclass.getFields();
      for (Field field : fieldList) {
        if (field.getName().equals(fieldName)) {
          return field;
        }
      }

      jclass = jclass.getSuperClass();
    }

    return null;
  }
  /**
   * looks to see the field has a runtime visible annotation, if it does it might be autowired or
   * some other mechanism attached that makes them less interesting for a toString call.
   *
   * @param f the field to check
   * @return if the field has a runtime visible annotation
   */
  private static boolean fieldHasRuntimeVisibleAnnotation(Field f) {
    AnnotationEntry[] annotations = f.getAnnotationEntries();
    if (annotations != null) {
      for (AnnotationEntry annotation : annotations) {
        if (annotation.isRuntimeVisible()) {
          return true;
        }
      }
    }

    return false;
  }
  @Override
  public void visit(Field obj) {
    int flags = obj.getAccessFlags();
    if (isEjbImplClass) {
      ClassDescriptor fieldType =
          DescriptorFactory.createClassDescriptorFromFieldSignature(obj.getSignature());
      if (fieldType != null) {
        if (Subtypes2.instanceOf(fieldType, "javax.ejb.SessionContext")
            || Subtypes2.instanceOf(fieldType, "javax.transaction.UserTransaction")
            || Subtypes2.instanceOf(fieldType, "javax.ejb.EJBHome")
            || Subtypes2.instanceOf(fieldType, "javax.ejb.EJBObject")
            || Subtypes2.instanceOf(fieldType, "javax.naming.Context")) {
          if (obj.isTransient())
            bugReporter.reportBug(
                new BugInstance(this, "UNKNOWN", NORMAL_PRIORITY)
                    .addClass(this)
                    .addVisitedField(this));
          return;
        }
      }
    }

    if (obj.isTransient()) {
      if (isSerializable && !isExternalizable) {
        seenTransientField = true;
        transientFieldsUpdates.put(getXField(), 0);
      } else if (reportTransientFieldOfNonSerializableClass) {
        bugReporter.reportBug(
            new BugInstance(this, "SE_TRANSIENT_FIELD_OF_NONSERIALIZABLE_CLASS", NORMAL_PRIORITY)
                .addClass(this)
                .addVisitedField(this));
      }
    } else if (getClassName().indexOf("ObjectStreamClass") == -1
        && isSerializable
        && !isExternalizable
        && getFieldSig().indexOf("L") >= 0
        && !obj.isTransient()
        && !obj.isStatic()) {
      if (DEBUG) {
        System.out.println(
            "Examining non-transient field with name: "
                + getFieldName()
                + ", sig: "
                + getFieldSig());
      }
      try {

        double isSerializable = DeepSubtypeAnalysis.isDeepSerializable(getFieldSig());
        if (DEBUG) {
          System.out.println("  isSerializable: " + isSerializable);
        }
        if (isSerializable < 1.0)
          fieldsThatMightBeAProblem.put(obj.getName(), XFactory.createXField(this));
        if (isSerializable < 0.9) {

          // Priority is LOW for GUI classes (unless explicitly marked Serializable),
          // HIGH if the class directly implements Serializable,
          // NORMAL otherwise.
          int priority = computePriority(isSerializable, 0);
          if (!strongEvidenceForIntendedSerialization()) {
            if (obj.getName().startsWith("this$")) priority = Math.max(priority, NORMAL_PRIORITY);
            else if (innerClassHasOuterInstance) {
              if (isAnonymousInnerClass) priority += 2;
              else priority += 1;
            }
            if (isGUIClass || isEjbImplClass) priority++;
          } else if (isGUIClass || isEjbImplClass) priority = Math.max(priority, NORMAL_PRIORITY);
          if (DEBUG)
            System.out.println(
                "SE_BAD_FIELD: "
                    + getThisClass().getClassName()
                    + " "
                    + obj.getName()
                    + " "
                    + isSerializable
                    + " "
                    + implementsSerializableDirectly
                    + " "
                    + sawSerialVersionUID
                    + " "
                    + isGUIClass
                    + " "
                    + isEjbImplClass);
          // Report is queued until after the entire class has been seen.

          if (obj.getName().equals("this$0"))
            fieldWarningList.add(
                new BugInstance(this, "SE_BAD_FIELD_INNER_CLASS", priority)
                    .addClass(getThisClass().getClassName()));
          else if (isSerializable < 0.9)
            fieldWarningList.add(
                new BugInstance(this, "SE_BAD_FIELD", priority)
                    .addClass(getThisClass().getClassName())
                    .addField(getDottedClassName(), obj.getName(), getFieldSig(), false));
        } else if (!isGUIClass && !isEjbImplClass && obj.getName().equals("this$0"))
          fieldWarningList.add(
              new BugInstance(
                      this,
                      "SE_INNER_CLASS",
                      implementsSerializableDirectly ? NORMAL_PRIORITY : LOW_PRIORITY)
                  .addClass(getThisClass().getClassName()));
      } catch (ClassNotFoundException e) {
        if (DEBUG) {
          System.out.println("Caught ClassNotFoundException");
        }
        bugReporter.reportMissingClass(e);
      }
    }

    if (!getFieldName().startsWith("this") && isSynthetic(obj)) foundSynthetic = true;
    if (!getFieldName().equals("serialVersionUID")) return;
    int mask = ACC_STATIC | ACC_FINAL;
    if (!getFieldSig().equals("I") && !getFieldSig().equals("J")) return;
    if ((flags & mask) == mask && getFieldSig().equals("I")) {
      bugReporter.reportBug(
          new BugInstance(this, "SE_NONLONG_SERIALVERSIONID", LOW_PRIORITY)
              .addClass(this)
              .addVisitedField(this));
      sawSerialVersionUID = true;
      return;
    } else if ((flags & ACC_STATIC) == 0) {
      bugReporter.reportBug(
          new BugInstance(this, "SE_NONSTATIC_SERIALVERSIONID", NORMAL_PRIORITY)
              .addClass(this)
              .addVisitedField(this));
      return;
    } else if ((flags & ACC_FINAL) == 0) {
      bugReporter.reportBug(
          new BugInstance(this, "SE_NONFINAL_SERIALVERSIONID", NORMAL_PRIORITY)
              .addClass(this)
              .addVisitedField(this));
      return;
    }
    sawSerialVersionUID = true;
  }
  @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;
      }
    }
  }
  /**
   * overrides the visitor to report on classes without toStrings that have fields
   *
   * @param classContext the context object of the currently parsed class
   */
  @Override
  public void visitClassContext(ClassContext classContext) {
    JavaClass cls = classContext.getJavaClass();

    if (cls.getPackageName().isEmpty()) {
      bugReporter.reportBug(
          new BugInstance(this, BugType.IMC_IMMATURE_CLASS_NO_PACKAGE.name(), LOW_PRIORITY)
              .addClass(cls));
    }

    if ((!cls.isAbstract())
        && (!cls.isEnum())
        && !cls.getClassName().contains("$")
        && !isTestClass(cls)) {

      try {
        boolean clsHasRuntimeAnnotation = classHasRuntimeVisibleAnnotation(cls);
        HEStatus heStatus = HEStatus.UNKNOWN;

        checkIDEGeneratedParmNames(cls);

        for (Field f : cls.getFields()) {
          if (!f.isStatic() && !f.isSynthetic()) {

            boolean fieldHasRuntimeAnnotation = fieldHasRuntimeVisibleAnnotation(f);
            if (!fieldHasRuntimeAnnotation) {
              /* only report one of these, so as not to flood the report */
              if (!hasMethodInHierarchy(cls, "toString", "()Ljava/lang/String;")) {
                bugReporter.reportBug(
                    new BugInstance(
                            this, BugType.IMC_IMMATURE_CLASS_NO_TOSTRING.name(), LOW_PRIORITY)
                        .addClass(cls));
                return;
              }
              if (heStatus != HEStatus.NOT_NEEDED) {
                String fieldSig = f.getSignature();
                if (fieldSig.startsWith("L")) {
                  if (!fieldSig.startsWith("Ljava")) {
                    JavaClass fieldClass =
                        Repository.lookupClass(fieldSig.substring(1, fieldSig.length() - 1));
                    if (!hasMethodInHierarchy(fieldClass, "equals", "(Ljava/lang/Object)Z")) {
                      heStatus = HEStatus.NOT_NEEDED;
                    }
                  } else if (!fieldSig.startsWith("Ljava/lang/")
                      && !fieldSig.startsWith("Ljava/util/")) {
                    heStatus = HEStatus.NOT_NEEDED;
                  }
                } else if (!fieldSig.startsWith("[")) {
                  heStatus = HEStatus.NEEDED;
                }
              }
            } else {
              heStatus = HEStatus.NOT_NEEDED;
            }
          }
        }

        if (!clsHasRuntimeAnnotation && (heStatus == HEStatus.NEEDED)) {
          if (!hasMethodInHierarchy(cls, "equals", "(Ljava/lang/Object;)Z")) {
            bugReporter.reportBug(
                new BugInstance(this, BugType.IMC_IMMATURE_CLASS_NO_EQUALS.name(), LOW_PRIORITY)
                    .addClass(cls));
          } else if (!hasMethodInHierarchy(cls, "hashCode", "()I")) {
            bugReporter.reportBug(
                new BugInstance(this, BugType.IMC_IMMATURE_CLASS_NO_HASHCODE.name(), LOW_PRIORITY)
                    .addClass(cls));
          }
        }

      } catch (ClassNotFoundException cnfe) {
        bugReporter.reportMissingClass(cnfe);
      }
    }
  }