import static com.sun.mirror.util.DeclarationVisitors.NO_OP;
import static com.sun.mirror.util.DeclarationVisitors.getDeclarationScanner;

import com.sun.mirror.apt.AnnotationProcessor;
import com.sun.mirror.apt.AnnotationProcessorEnvironment;
import com.sun.mirror.declaration.AnnotationMirror;
import com.sun.mirror.declaration.MethodDeclaration;
import com.sun.mirror.declaration.TypeDeclaration;
import com.sun.mirror.util.DeclarationVisitor;
import com.sun.mirror.util.SimpleDeclarationVisitor;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;

public class InternalApiAnnotationProcessor implements AnnotationProcessor {
  
  private static final String METHODS = "methods";
  
  private static final List<String> annotations = Arrays.asList(
    "org.apache.hadoop.classification.InterfaceAudience.Private",
    "org.apache.hadoop.classification.InterfaceAudience.LimitedPrivate",
    "org.apache.hadoop.classification.InterfaceStability.Unstable"
  );

  private final AnnotationProcessorEnvironment env;
  public InternalApiAnnotationProcessor(AnnotationProcessorEnvironment env) {
    this.env = env;
  }
  
  @Override
  public void process() {
    DeclarationVisitor visitor;
    if (env.getOptions().containsKey("-Aformat=" + METHODS)) {
      visitor = new MethodVisitor();
    } else {
      visitor = new TypeVisitor();
    }
    for (TypeDeclaration typeDecl : env.getSpecifiedTypeDeclarations()) {
      typeDecl.accept(getDeclarationScanner(visitor, NO_OP));
    }
  }
  

  private static class TypeVisitor extends SimpleDeclarationVisitor {
    public void visitTypeDeclaration(TypeDeclaration d) {
      process(d);
    }
    private void process(TypeDeclaration d) {
      Collection<AnnotationMirror> mirrors = d.getAnnotationMirrors();
      for (AnnotationMirror mirror : mirrors) {
        if (annotations.contains(getAnnotationName(mirror))) {
          System.out.println(d.getQualifiedName());
          printNestedTypes(d, d.getQualifiedName());
          return;
        }
      }
    }
  }
  
  private static void printNestedTypes(TypeDeclaration d, String className) {
    for (TypeDeclaration nested : d.getNestedTypes()) {
      String nestedClassName = className + "$" + nested.getSimpleName();
      System.out.println(nestedClassName);
      printNestedTypes(nested, nestedClassName);
    }
  }
  
  private static class MethodVisitor extends SimpleDeclarationVisitor {
    public void visitMethodDeclaration(MethodDeclaration d) {
      Collection<AnnotationMirror> mirrors = d.getAnnotationMirrors();
      for (AnnotationMirror mirror : mirrors) {
        if (annotations.contains(getAnnotationName(mirror))) {
          System.out.println("Method " +
              d.getDeclaringType().getQualifiedName() + "#" +
              d.getSimpleName());
          return;
        }
      }
    }
  }
  
  private static String getAnnotationName(AnnotationMirror mirror) {
    // Pull out the class name
    return mirror.toString().replaceFirst("^@([^\\(]*).*$", "$1");
  }

}

@@ -334,32 +334,76 @@
 hodDeclaration;%0A
+import com.sun.mirror.declaration.Modifier;%0A
 import com.sun.m
@@ -429,32 +429,32 @@
 ypeDeclaration;%0A
-
 import com.sun.m
@@ -618,30 +618,8 @@
 ist;
-%0Aimport java.util.Map;
 %0A%0Apu
@@ -747,16 +747,77 @@
 thods%22;%0A
+  private static final String PUBLIC_TYPES = %22public-types%22;%0A
   %0A  pri
@@ -1411,24 +1411,142 @@
 dVisitor();%0A
+    %7D else if (env.getOptions().containsKey(%22-Aformat=%22 + PUBLIC_TYPES)) %7B%0A        visitor = new PublicTypeVisitor();%0A
     %7D else %7B
@@ -1733,18 +1733,632 @@
   %7D%0A  %7D%0A
-
   
+%0A  private static class PublicTypeVisitor extends SimpleDeclarationVisitor %7B%0A    public void visitTypeDeclaration(TypeDeclaration d) %7B%0A      process(d);%0A    %7D%0A    private void process(TypeDeclaration d) %7B%0A      Collection%3CAnnotationMirror%3E mirrors = d.getAnnotationMirrors();%0A      for (AnnotationMirror mirror : mirrors) %7B%0A        if (!annotations.contains(getAnnotationName(mirror)) && isPublic(d)) %7B%0A          printNestedTypes(d, d.getQualifiedName());%0A          return;%0A        %7D%0A      %7D%0A    %7D%0A    private boolean isPublic(TypeDeclaration d) %7B%0A      return d.getModifiers().contains(Modifier.PUBLIC);%0A    %7D%0A  %7D
 %0A%0A  priv

Add option to show only public types.