import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;

import com.sun.source.util.Trees;
import com.sun.tools.javac.model.JavacElements;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.Context;

@SupportedAnnotationTypes("*")
@SupportedOptions("replaces")
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class FullQualifier extends AbstractProcessor {
	private Trees trees;
	@Override
	public synchronized void init(ProcessingEnvironment env) {
		super.init(env);
		trees = Trees.instance(env);
		Context context = ((JavacProcessingEnvironment) env).getContext();
		TreeMaker.instance(context);
		JavacElements.instance(context);
	}

	@Override
	public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
		if (!roundEnv.processingOver()) {
			Set<? extends Element> elements = roundEnv.getRootElements();
			for (Element each : elements) {
				if (each.getKind() == ElementKind.CLASS) {
					JCTree tree = (JCTree) trees.getTree(each);

					StringWriter s = new StringWriter();
					try {
						new MyPretty(s, false).printExpr(tree);
					} catch (IOException e) {
						throw new AssertionError(e);
					}
					System.out.println(s.toString());
				}
			}
			// even with -proc:only the javac does some semantic checking
			// to avoid that (we compile the generated files anyway in the next step), exit in a clean way
			System.exit(0);
		}
		return false;
	}
}

class MyPretty extends com.sun.tools.javac.tree.Pretty {
	public MyPretty(Writer out, boolean sourceOutput) {
		super(out, sourceOutput);
	}

	public  void visitAnnotation(JCAnnotation tree) {
		try {
			print("@");
			String before = tree.annotationType.toString();
			switch (before) {
				case "After":
				case "Before":
				case "Ignore":
				case "FixMethodOrder":
				case "Rule":
				case "ClassRule":
				case "Test":
					print("org.junit.");
					break;
				case "RunWith":
					print("org.junit.runner.");
					break;
				case "Parameters":
					print("org.junit.runner.Parameterized.");
					break;
				case "Bonus":
				case "Malus":
				case "SecretCase":
				case "Ex":
				case "Exercises":
				case "Forbidden":
				case "NotForbidden":
					print("tester.annotations.");
					break;
				case "Replace": // FIXME: this is in the wrong package
					print("tester.");
					break;
			}
			printExpr(tree.annotationType);
			print("(");
			printExprs(tree.args);
			print(")");
		} catch (IOException e) {
			throw new Error("something failed while pretty printing annotations: " + e);
		}
	}
}

@@ -635,24 +635,61 @@
 util.Trees;%0A
+import com.sun.source.util.TreePath;%0A
 import com.s
@@ -724,16 +724,16 @@
 ements;%0A
-
 import c
@@ -1155,16 +1155,52 @@
  trees;%0A
+%09private boolean imported = false;%0A%0A
 %09@Overri
@@ -1734,24 +1734,24 @@
 nd.CLASS) %7B%0A
-
 %09%09%09%09%09JCTree 
@@ -1786,16 +1786,254 @@
 e(each);
+%0A%09%09%09%09%09if (!imported) %7B %0A%09%09%09%09%09%09imported = true;%0A%09%09%09%09%09%09TreePath path = trees.getPath(each);%0A%09%09%09%09%09%09java.util.List imports = path.getCompilationUnit().getImports();%0A%09%09%09%09%09%09for (Object o : imports) %7B%0A%09%09%09%09%09%09%09System.out.println(o);%0A%09%09%09%09%09%09%7D%0A%09%09%09%09%09%7D
 %0A%0A%09%09%09%09%09S

Include imports while full qualifying annotations, #18.