private void printAnnotationValue(AST ast, Object value) {
   if (value == null) {
     print("nil");
   } else if (value instanceof IVariableBinding) {
     IVariableBinding var = (IVariableBinding) value;
     ITypeBinding declaringClass = var.getDeclaringClass();
     printf("[%s %s]", NameTable.getFullName(declaringClass), var.getName());
   } else if (value instanceof ITypeBinding) {
     ITypeBinding type = (ITypeBinding) value;
     printf("[[%s class] getClass]", NameTable.getFullName(type));
   } else if (value instanceof String) {
     StringLiteral node = ast.newStringLiteral();
     node.setLiteralValue((String) value);
     print(StatementGenerator.generateStringLiteral(node));
   } else if (value instanceof Number || value instanceof Character || value instanceof Boolean) {
     print(value.toString());
   } else if (value.getClass().isArray()) {
     print("[IOSObjectArray arrayWithObjects:(id[]) { ");
     Object[] array = (Object[]) value;
     for (int i = 0; i < array.length; i++) {
       if (i > 0) {
         print(", ");
       }
       printAnnotationValue(ast, array[i]);
     }
     printf(" } count:%d type:[[NSObject class] getClass]]", array.length);
   } else {
     assert false : "unknown annotation value type";
   }
 }
  @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");
    }
  }
  public void testMethodVarInSwitch() throws IOException {
    String source =
        "class Test { "
            + "  enum E { ONE, TWO };"
            + "  void foo(E e) { "
            + "    switch (e) {"
            + "      case ONE: {"
            + "        final Integer i = 1;"
            + "        Runnable r = new Runnable() { "
            + "          public void run() { int j = i + 1; } }; }}}}";

    // Verify method var in r1.run() isn't mistakenly made a field in r1.
    CompilationUnit unit = translateType("Test", source);
    List<TypeDeclaration> types = unit.types();
    TypeDeclaration r1 = types.get(2);
    assertEquals("Test_$1", NameTable.getFullName(r1));
    boolean found = false;
    for (FieldDeclaration field : r1.getFields()) {
      List<VariableDeclarationFragment> vars = field.fragments();
      for (VariableDeclaration var : vars) {
        if (var.getName().getIdentifier().equals("val$i")) {
          found = true;
        }
      }
    }
    assertTrue("required field not found", found);

    // Verify constructor takes both outer field and var.
    ObjectiveCImplementationGenerator.generate("Test.java", Language.OBJECTIVE_C, unit, source);
    String translation = getTranslatedFile("Test.m");
    assertTranslation(
        translation,
        "r = [[[Test_$1 alloc] " + "initWithTest:self withJavaLangInteger:i] autorelease]");
  }
 private String methodKey(IMethodBinding method) {
   StringBuilder sb = new StringBuilder();
   if (method.isConstructor()) {
     sb.append(NameTable.getFullName(method.getDeclaringClass()));
   } else {
     sb.append(NameTable.getName(method));
   }
   sb.append(parameterKey(method));
   return sb.toString();
 }
 @Override
 protected void printStaticConstructorDeclaration(MethodDeclaration m) {
   String className = NameTable.getFullName(Types.getMethodBinding(m).getDeclaringClass());
   StringBuffer sb = new StringBuffer();
   sb.append("{\nif (self == [" + className + " class]) {\n");
   for (Statement statement : ASTUtil.getStatements(m.getBody())) {
     sb.append(generateStatement(statement, false));
   }
   sb.append("}\n}");
   print("+ (void)initialize " + reindent(sb.toString()) + "\n\n");
 }
 private void printAnnotations(List<Annotation> runtimeAnnotations) {
   boolean first = true;
   for (Annotation annotation : runtimeAnnotations) {
     if (first) {
       first = false;
     } else {
       print(", ");
     }
     if (Options.useReferenceCounting()) {
       print('[');
     }
     printf("[[%s alloc] init", NameTable.getFullName(Types.getTypeBinding(annotation)));
     printAnnotationParameters(annotation);
     print(']');
     if (Options.useReferenceCounting()) {
       print(" autorelease]");
     }
   }
 }
 public void generate(CompilationUnit unit) {
   println(J2ObjC.getFileHeader(getSourceFileName()));
   List<AbstractTypeDeclaration> typesToGenerate = collectTypes(unit);
   if (!typesToGenerate.isEmpty()) {
     findBlockComments(unit, typesToGenerate);
     findInvokedConstructors(unit);
     printStart(getSourceFileName());
     printImports(unit);
     pushIgnoreDeprecatedDeclarationsPragma();
     for (AbstractTypeDeclaration type : typesToGenerate) {
       generate(type);
     }
     popIgnoreDeprecatedDeclarationsPragma();
   } else {
     // Print a dummy C function so compiled object file is valid.
     List<AbstractTypeDeclaration> types = ASTUtil.getTypes(unit);
     if (!types.isEmpty()) {
       printf("void %s_unused() {}\n", NameTable.getFullName(types.get(0)));
     }
   }
   save(unit);
 }
  @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");
  }