@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");
  }
  private void printMethodsAndOcni(
      AbstractTypeDeclaration typeNode,
      Iterable<MethodDeclaration> methods,
      Iterable<Comment> comments) {
    Set<String> methodsPrinted = Sets.newHashSet();
    Iterator<MethodDeclaration> methodsIter = methods.iterator();
    Iterator<Comment> commentsIter = comments.iterator();
    MethodDeclaration nextMethod = methodsIter.hasNext() ? methodsIter.next() : null;
    Comment nextComment = commentsIter.hasNext() ? commentsIter.next() : null;
    int minPos = 0;
    while (nextMethod != null || nextComment != null) {
      int methodStartPos = nextMethod != null ? nextMethod.getStartPosition() : Integer.MAX_VALUE;
      if (methodStartPos < 0) {
        methodStartPos = minPos;
      }
      int commentStartPos =
          nextComment != null ? nextComment.getStartPosition() : Integer.MAX_VALUE;
      if (methodStartPos < commentStartPos) {
        assert nextMethod != null;
        printMethod(nextMethod);
        minPos = methodStartPos + nextMethod.getLength();
        nextMethod = methodsIter.hasNext() ? methodsIter.next() : null;
      } else {
        assert nextComment != null;
        if (commentStartPos > minPos) {
          String nativeCode = extractNativeCode(commentStartPos, nextComment.getLength());
          if (nativeCode != null) {
            nativeCode = reindent(nativeCode.trim());
            findMethodSignatures(nativeCode, methodsPrinted);
            print(nativeCode + "\n\n");
          }
        }
        nextComment = commentsIter.hasNext() ? commentsIter.next() : null;
      }
    }

    // If the type implements Iterable and there's no existing implementation
    // for NSFastEnumeration's protocol method, then add the default
    // implementation.
    if (BindingUtil.findInterface(Types.getTypeBinding(typeNode), "java.lang.Iterable") != null
        && !methodsPrinted.contains("countByEnumeratingWithState:objects:count:")) {
      print(
          "- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state "
              + "objects:(__unsafe_unretained id *)stackbuf count:(NSUInteger)len {\n"
              + "  return JreDefaultFastEnumeration(self, state, stackbuf, len);\n}\n\n");
    }
  }
  private void printMethods(TypeDeclaration node) {
    printMethodsAndOcni(node, Arrays.asList(node.getMethods()), blockComments.get(node));

    // If node implements CharSequence, add forwarding method from the
    // sequenceDescription method to description (toString()).  See
    // JavaToIOSMethodTranslator.loadCharSequenceMethods() for details.
    ITypeBinding binding = Types.getTypeBinding(node);
    for (ITypeBinding interfaze : binding.getInterfaces()) {
      if (interfaze.getQualifiedName().equals("java.lang.CharSequence")) {
        println("- (NSString *)description {\n  return [self sequenceDescription];\n}\n");
      }
    }

    List<VariableDeclarationFragment> properties = getProperties(node.getFields());
    if (properties.size() > 0) {
      printStrongReferencesMethod(properties);
    }
  }
 private void printAnnotationAccessors(List<AnnotationTypeMemberDeclaration> members) {
   int nPrinted = 0;
   for (AnnotationTypeMemberDeclaration member : members) {
     Expression deflt = member.getDefault();
     if (deflt != null) {
       ITypeBinding type = Types.getTypeBinding(member.getType());
       String typeString = NameTable.getSpecificObjCType(type);
       String propertyName = NameTable.getName(member.getName());
       printf("+ (%s)%sDefault {\n", typeString, propertyName);
       printf("  return %s;\n", generateExpression(deflt));
       println("}\n");
       nPrinted++;
     }
   }
   if (nPrinted > 0) {
     newline();
   }
 }
 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]");
     }
   }
 }
 // Returns whether the property is a strong reference.
 private boolean isStrongReferenceProperty(VariableDeclarationFragment property) {
   IVariableBinding varBinding = Types.getVariableBinding(property);
   ITypeBinding type = Types.getTypeBinding(property);
   return !type.isPrimitive() && !BindingUtil.isWeakReference(varBinding);
 }