private void printStaticInterface(
     AbstractTypeDeclaration node,
     String typeName,
     List<FieldDeclaration> fields,
     List<MethodDeclaration> methods) {
   List<IVariableBinding> staticFields =
       getStaticFieldsNeedingAccessors(fields, /* isInterface */ true);
   if (staticFields.isEmpty()) {
     if (!Options.stripReflection()) {
       printf("\n@interface %s : NSObject\n@end\n", typeName);
     } else {
       return;
     }
   }
   printf("\n@implementation %s\n\n", typeName);
   printStaticVars(fields, /* isInterface */ true);
   printStaticFieldAccessors(staticFields, methods);
   for (MethodDeclaration method : methods) {
     if (method.getBody() != null) {
       printNormalMethod(method);
     }
   }
   if (!Options.stripReflection()) {
     printMetadata(node);
   }
   println("@end");
 }
  @Override
  protected void generate(AnnotationTypeDeclaration node) {
    syncLineNumbers(node.getName()); // avoid doc-comment

    String typeName = NameTable.getFullName(node);
    printf("@implementation %s\n", typeName);
    if (BindingUtil.isRuntimeAnnotation(Types.getTypeBinding(node))) {
      List<AnnotationTypeMemberDeclaration> members = Lists.newArrayList();
      for (BodyDeclaration decl : ASTUtil.getBodyDeclarations(node)) {
        if (decl instanceof AnnotationTypeMemberDeclaration) {
          members.add((AnnotationTypeMemberDeclaration) decl);
        }
      }
      printAnnotationProperties(members);
      if (!members.isEmpty()) {
        printAnnotationConstructor(Types.getTypeBinding(node));
      }
      printAnnotationAccessors(members);
    }
    List<FieldDeclaration> fields = ASTUtil.getFieldDeclarations(node);
    List<IVariableBinding> fieldsNeedingAccessors =
        getStaticFieldsNeedingAccessors(fields, /* isInterface */ true);
    printStaticVars(fields, /* isInterface */ true);
    printStaticFieldAccessors(fieldsNeedingAccessors, Collections.<MethodDeclaration>emptyList());
    println("- (IOSClass *)annotationType {");
    printf("  return [IOSClass classWithProtocol:@protocol(%s)];\n", typeName);
    println("}\n");
    if (!Options.stripReflection()) {
      printTypeAnnotationsMethod(node);
      printMetadata(node);
    }
    println("@end\n");
  }
  @Override
  public void generate(TypeDeclaration node) {
    syncLineNumbers(node.getName()); // avoid doc-comment

    String typeName = NameTable.getFullName(node);
    List<FieldDeclaration> fields = Lists.newArrayList(node.getFields());
    List<MethodDeclaration> methods = Lists.newArrayList(node.getMethods());
    fieldHiders = HiddenFieldDetector.getFieldNameConflicts(node);
    if (node.isInterface()) {
      printStaticInterface(node, typeName, fields, methods);
    } else {
      printf("@implementation %s\n\n", typeName);
      printStaticReferencesMethod(fields);
      printStaticVars(fields, /* isInterface */ false);
      printStaticFieldAccessors(fields, methods, /* isInterface */ false);
      printMethods(node);
      if (!Options.stripReflection()) {
        printTypeAnnotationsMethod(node);
        printMethodAnnotationMethods(Lists.newArrayList(node.getMethods()));
        printFieldAnnotationMethods(Lists.newArrayList(node.getFields()));
        printMetadata(node);
      }

      println("@end");
    }
  }
  @Override
  protected void generate(EnumDeclaration node) {
    List<EnumConstantDeclaration> constants = ASTUtil.getEnumConstants(node);
    List<MethodDeclaration> methods = Lists.newArrayList();
    List<FieldDeclaration> fields = Lists.newArrayList();
    MethodDeclaration initializeMethod = null;
    for (BodyDeclaration decl : ASTUtil.getBodyDeclarations(node)) {
      if (decl instanceof FieldDeclaration) {
        fields.add((FieldDeclaration) decl);
      } else if (decl instanceof MethodDeclaration) {
        MethodDeclaration md = (MethodDeclaration) decl;
        if (md.getName().getIdentifier().equals("initialize")) {
          initializeMethod = md;
        } else {
          methods.add(md);
        }
      }
    }
    syncLineNumbers(node.getName()); // avoid doc-comment

    String typeName = NameTable.getFullName(node);
    newline();
    for (EnumConstantDeclaration constant : constants) {
      IVariableBinding var = Types.getVariableBinding(constant.getName());
      printf("static %s *%s;\n", typeName, NameTable.getStaticVarQualifiedName(var));
    }
    printf("IOSObjectArray *%s_values;\n", typeName);
    newline();

    printf("@implementation %s\n\n", typeName);
    printStaticVars(fields, /* isInterface */ false);
    printStaticReferencesMethod(fields, typeName + "_values");

    for (EnumConstantDeclaration constant : constants) {
      String name = NameTable.getName(constant.getName());
      printf("+ (%s *)%s {\n", typeName, name);
      printf("  return %s_%s;\n", typeName, name);
      println("}");
    }
    newline();

    // Enum constants needs to implement NSCopying.  Being singletons, they
    // can just return self, as long the retain count is incremented.
    String selfString = Options.useReferenceCounting() ? "[self retain]" : "self";
    printf("- (id)copyWithZone:(NSZone *)zone {\n  return %s;\n}\n\n", selfString);

    printStaticFieldAccessors(fields, methods, /* isInterface */ false);
    printMethodsAndOcni(node, methods, blockComments.get(node));

    printf("+ (void)initialize {\n  if (self == [%s class]) {\n", typeName);
    for (int i = 0; i < constants.size(); i++) {
      EnumConstantDeclaration constant = constants.get(i);
      List<Expression> args = ASTUtil.getArguments(constant);
      String name = NameTable.getName(constant.getName());
      String constantTypeName =
          NameTable.getFullName(Types.getMethodBinding(constant).getDeclaringClass());
      printf("    %s_%s = [[%s alloc] init", typeName, name, constantTypeName);

      if (args.isEmpty()) {
        print("With");
      } else {
        print(
            StatementGenerator.generateArguments(
                Types.getMethodBinding(constant),
                args,
                fieldHiders,
                getBuilder().getSourcePosition()));
        print(" with");
      }
      printf("NSString:@\"%s\" withInt:%d];\n", name, i);
    }
    printf("    %s_values = [[IOSObjectArray alloc] initWithObjects:(id[]){ ", typeName);
    for (EnumConstantDeclaration constant : constants) {
      printf("%s_%s, ", typeName, NameTable.getName(constant.getName()));
    }
    printf(
        "nil } count:%d type:[IOSClass classWithClass:[%s class]]];\n", constants.size(), typeName);
    if (initializeMethod != null) {
      for (Statement s : ASTUtil.getStatements(initializeMethod.getBody())) {
        printf(
            "    %s",
            StatementGenerator.generate(s, fieldHiders, false, getBuilder().getSourcePosition()));
      }
    }
    println("  }\n}\n");

    // Print generated values and valueOf methods.
    println("+ (IOSObjectArray *)values {");
    printf("  return [IOSObjectArray arrayWithArray:%s_values];\n", typeName);
    println("}\n");
    printf("+ (%s *)valueOfWithNSString:(NSString *)name {\n", typeName);
    printf("  for (int i = 0; i < [%s_values count]; i++) {\n", typeName);
    printf("    %s *e = %s_values->buffer_[i];\n", typeName, typeName);
    printf("    if ([name isEqual:[e name]]) {\n");
    printf("      return e;\n");
    printf("    }\n");
    printf("  }\n");
    if (Options.useReferenceCounting()) {
      printf(
          "  @throw [[[JavaLangIllegalArgumentException alloc] initWithNSString:name]"
              + " autorelease];\n");
    } else {
      printf("  @throw [[JavaLangIllegalArgumentException alloc] initWithNSString:name];\n");
    }
    printf("  return nil;\n");
    println("}\n");

    if (!Options.stripReflection()) {
      printTypeAnnotationsMethod(node);
      printMetadata(node);
    }
    println("@end");
  }