FaqApt

(Difference between revisions)
Line 1: Line 1:
-
__NOTOC__
 
__NOTOC__
__NOTOC__
===Can I run an annotation processor from my project?===
===Can I run an annotation processor from my project?===

Revision as of 16:27, 6 November 2009

Can I run an annotation processor from my project?

Yes, with a little work.

JDK 5 introduced annotations and a tool apt which could scan for annotations in a project and perform various actions on them, including code generation.

JDK 6 improves on this support and, as part of JSR 269, provides an official API for writing annotation processors. The apt tool is no longer necessary as javac can automatically run processors.

NetBeans currently does not directly support running annotation processors but you can set up your project to work fairly well with them. An example using JDK 6 follows. (In JDK 5 it is trickier since you must compile the processor against tools.jar and you must run apt explicitly from the project using the annotations.)

Create a Java SE library project. Make sure it is using JDK 6 or higher (see Libraries in the project's Properties dialog). Define an annotation:

package ann;
public @interface Handlable {}

Create a processor for it:

package proc;
import ann.Handlable;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
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 javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
@SupportedAnnotationTypes("ann.Handlable")
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class HandlableProcessor extends AbstractProcessor {
    /** public for ServiceLoader */
    public HandlableProcessor() {}
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        for (Element e : roundEnv.getElementsAnnotatedWith(Handlable.class)) {
            if (e.getKind() != ElementKind.FIELD) {
                processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "Not a field", e);
                continue;
            }
            String name = capitalize(e.getSimpleName().toString());
            TypeElement clazz = (TypeElement) e.getEnclosingElement();
            try {
                JavaFileObject f = processingEnv.getFiler().createSourceFile(clazz.getQualifiedName() + "Extras");
                processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Creating " + f.toUri());
                Writer w = f.openWriter();
                try {
                    PrintWriter pw = new PrintWriter(w);
                    pw.println("package " + clazz.getEnclosingElement().getSimpleName() + ";");
                    pw.println("public abstract class " + clazz.getSimpleName() + "Extras {");
                    pw.println("    protected " + clazz.getSimpleName() + "Extras() {}");
                    TypeMirror type = e.asType();
                    pw.println("    /** Handle something. */");
                    pw.println("    protected final void handle" + name + "(" + type + " value) {");
                    pw.println("        System.out.println(value);");
                    pw.println("    }");
                    pw.println("}");
                    pw.flush();
                } finally {
                    w.close();
                }
            } catch (IOException x) {
                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, x.toString());
            }
        }
        return true;
    }
    private static String capitalize(String name) {
        char[] c = name.toCharArray();
        c[[0 | 0]] = Character.toUpperCase(c[0]);
        return new String(c);
    }
}

You must also register your processor. Create a file src/META-INF/services/javax.annotation.processing.Processor:

proc.HandlableProcessor

Now to use the annotation. Create a new Java SE main project (again, using JDK 6 or higher) and add the library project as a dependency (e.g. right-click the Libraries node and choose "Add Project"). Here is a usage of the annotation:

package demo;
import ann.Handlable;
public class Main extends MainExtras {
    @Handlable
    private String stuff;
    public static void main(String[] args) {
        new Main().handleStuff("hello");
    }
}

Initially you will see compilation errors in the editor for this file. Don't worry about that yet. In the Files tab, open nbproject/project.properties and add/edit these lines:

# edit to ensure path is absolute:
build.dir=${basedir}/build
# add:
build.apt.dir=${build.dir}/apt
# add ${build.apt.dir}:
javac.classpath=\
    ${reference.lib.jar}:\
    ${build.apt.dir}
# edit:
javac.compilerargs=-s ${build.apt.dir}

Now run the project. You should see

run:
hello

If you look in the Files tab, you should see build/apt/demo/MainExtras.java with

package demo;
public abstract class MainExtras {
    protected MainExtras() {}
    /** Handle something. */
    protected final void handleStuff(java.lang.String value) {
        System.out.println(value);
    }
}

Switch back to Main.java; the editor should show it as compilable (green box in upper right corner, and no error markings). Generally you will need to compile the project after changing annotated elements before code completion and the editor's background compiler will "know about" the generated sources.

If you want to include MainExtras in Javadoc generation for the project, just open build.xml from Files and add an override of a target from nbproject/build-impl.xml with one addition:

    <target name="-javadoc-build" depends="init">
        <mkdir dir="${dist.javadoc.dir}"/>
        <javadoc destdir="${dist.javadoc.dir}" source="${javac.source}" notree="${javadoc.notree}" use="${javadoc.use}" nonavbar="${javadoc.nonavbar}" noindex="${javadoc.noindex}" splitindex="${javadoc.splitindex}" author="${javadoc.author}" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}" private="${javadoc.private}" additionalparam="${javadoc.additionalparam}" failonerror="true" useexternalfile="true">
            <classpath>
                <path path="${javac.classpath}"/>
            </classpath>
            <sourcepath>
                <pathelement location="${src.dir}"/>
                <pathelement location="${build.apt.dir}"/> <!-- added -->
            </sourcepath>
            <packageset dir="${src.dir}" includes="*/**"/>
            <fileset dir="${src.dir}" includes="*.java"/>
        </javadoc>
    </target>

Discussion on nbdev: "Requirements for Supporting Annotation Processors in Mustang" #1 #2


Applies to: NetBeans 5.0

Not logged in. Log in, Register

By use of this website, you agree to the NetBeans Policies and Terms of Use. © 2012, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo