private boolean hasStructuralChanges(
      ClassFileReader reader, TypeStructure existingType, int flags) {
    if (existingType == null) {
      return true;
    }

    // modifiers
    if (!modifiersEqual(reader.getModifiers(), existingType.modifiers)) {
      return true;
    }

    // generic signature
    if (!CharOperation.equals(reader.getGenericSignature(), existingType.genericSignature)) {
      return true;
    }

    // superclass name
    if (!CharOperation.equals(reader.getSuperclassName(), existingType.superclassName)) {
      return true;
    }

    // class level annotations
    if ((flags & FLAG_ANNOTATION) != 0) {
      IBinaryAnnotation[] existingAnnotations = existingType.getAnnotations();
      IBinaryAnnotation[] newAnnotations = reader.getAnnotations();
      if (!annotationsEqual(existingAnnotations, newAnnotations, flags)) {
        return true;
      }
    }

    // tag bits; standard annotations like @Deprecated
    if (reader.getTagBits() != existingType.getTagBits()) {
      return true;
    }

    // interfaces
    char[][] existingIfs = existingType.interfaces;
    char[][] newIfsAsChars = reader.getInterfaceNames();
    if (newIfsAsChars == null) {
      newIfsAsChars = EMPTY_CHAR_ARRAY;
    } // damn I'm lazy...
    if (existingIfs == null) {
      existingIfs = EMPTY_CHAR_ARRAY;
    }
    if (existingIfs.length != newIfsAsChars.length) return true;
    new_interface_loop:
    for (int i = 0; i < newIfsAsChars.length; i++) {
      for (int j = 0; j < existingIfs.length; j++) {
        if (CharOperation.equals(existingIfs[j], newIfsAsChars[i])) {
          continue new_interface_loop;
        }
      }
      return true;
    }

    // fields
    IBinaryField[] newFields = reader.getFields();
    if (newFields == null) {
      newFields = TypeStructure.NoField;
    }

    IBinaryField[] existingFs = existingType.binFields;
    if (newFields.length != existingFs.length) return true;
    new_field_loop:
    for (int i = 0; i < newFields.length; i++) {
      IBinaryField field = newFields[i];
      char[] fieldName = field.getName();
      for (int j = 0; j < existingFs.length; j++) {
        if (CharOperation.equals(existingFs[j].getName(), fieldName)) {
          if (!modifiersEqual(field.getModifiers(), existingFs[j].getModifiers())) {
            return true;
          }
          if (!CharOperation.equals(existingFs[j].getTypeName(), field.getTypeName())) {
            return true;
          }
          if ((flags & FLAG_ANNOTATION) != 0) {
            if (!annotationsEqual(field.getAnnotations(), existingFs[j].getAnnotations(), flags)) {
              return true;
            }
          }
          continue new_field_loop;
        }
      }
      return true;
    }

    // methods
    IBinaryMethod[] newMethods = reader.getMethods();
    if (newMethods == null) {
      newMethods = TypeStructure.NoMethod;
    }

    char[] fileName = reader.getFileName();

    IBinaryMethod[] existingMs = existingType.binMethods;
    if (newMethods.length != existingMs.length) return true;
    new_method_loop:
    for (int i = 0; i < newMethods.length; i++) {
      IBinaryMethod method = newMethods[i];
      char[] methodName = method.getSelector();
      for (int j = 0; j < existingMs.length; j++) {
        if (CharOperation.equals(existingMs[j].getSelector(), methodName)) {
          // candidate match
          if (!CharOperation.equals(
              method.getMethodDescriptor(), existingMs[j].getMethodDescriptor())) {
            continue; // might be overloading
          } else {
            // matching sigs
            if (!modifiersEqual(method.getModifiers(), existingMs[j].getModifiers())) {
              return true;
            }
            if ((flags & FLAG_ANNOTATION) != 0) {
              if (!annotationsEqual(
                  method.getAnnotations(), existingMs[j].getAnnotations(), flags)) {
                return true;
              }

              if (!parameterAnnotationsEquals(method, existingMs[j], fileName, flags)) {
                return true;
              }
            }
            continue new_method_loop;
          }
        }
      }
      return true; // (no match found)
    }

    return false;
  }