FaqApt

(Difference between revisions)
Line 10: Line 10:
processors. The <tt>apt</tt> tool is no longer necessary as <tt>javac</tt> can automatically run processors.
processors. The <tt>apt</tt> tool is no longer necessary as <tt>javac</tt> can automatically run processors.
-
NetBeans currently does not directly support running annotation processors but you can set up your project to
+
NetBeans currently does not directly support running annotation processors (bug #111294 and others) 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
work fairly well with them. An example using JDK 6 follows. (In JDK 5 it is trickier since you must compile the
processor against <tt>tools.jar</tt> and you must run <tt>apt</tt> explicitly from the project using the annotations.)
processor against <tt>tools.jar</tt> and you must run <tt>apt</tt> explicitly from the project using the annotations.)
Line 82: Line 82:
     private static String capitalize(String name) {
     private static String capitalize(String name) {
         char[] c = name.toCharArray();
         char[] c = name.toCharArray();
-
         c[[0 | 0]] = Character.toUpperCase(c[0]);
+
         c[0] = Character.toUpperCase(c[0]);
         return new String(c);
         return new String(c);
     }
     }
Line 110: Line 110:
Initially you will see compilation errors in the editor for this file. Don't worry about that yet. In the
Initially you will see compilation errors in the editor for this file. Don't worry about that yet. In the
-
'''Files''' tab, open <tt>nbproject/project.properties</tt> and add/edit these lines:
+
'''Files''' tab, open <tt>build.xml</tt> and add these lines:
<pre>
<pre>
-
# edit to ensure path is absolute:
+
    <target name="-do-compile" depends="init,deps-jar,-pre-pre-compile,-pre-compile,-compile-depend" if="have.sources" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3">
-
build.dir=${basedir}/build
+
        <property name="ap.dir" location="${build.generated.sources.dir}/ap"/>
-
# add:
+
        <mkdir dir="${ap.dir}"/>
-
build.apt.dir=${build.dir}/apt
+
        <j2seproject3:javac gensrcdir="${build.generated.sources.dir}" sourcepath="${src.dir}">
-
# add ${build.apt.dir}:
+
            <customize>
-
javac.classpath=\
+
                <compilerarg line="-implicit:class -s ${ap.dir}"/>
-
    ${reference.lib.jar}:\
+
            </customize>
-
    ${build.apt.dir}
+
        </j2seproject3:javac>
-
# edit:
+
        <copy todir="${build.classes.dir}">
-
javac.compilerargs=-s ${build.apt.dir}
+
            <fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
 +
        </copy>
 +
    </target>
</pre>
</pre>
-
Now run the project. You should see
+
Now build & clean and then run the project. You should see
<pre>
<pre>
Line 132: Line 134:
</pre>
</pre>
-
If you look in the '''Files''' tab, you should see <tt>build/apt/demo/MainExtras.java</tt> with
+
The '''Projects''' tab should show a new '''Generated Sources (ap)''' node. Under it you should find:
<pre>
<pre>
Line 149: Line 151:
completion and the editor's background compiler will "know about" the generated sources.
completion and the editor's background compiler will "know about" the generated sources.
-
If you want to include <tt>MainExtras</tt> in Javadoc generation for the project, just open <tt>build.xml</tt> from '''Files'''
+
<tt>MainExtras</tt> will also be included in Javadoc generation for the project.
-
and add an override of a target from <tt>nbproject/build-impl.xml</tt> with one addition:
+
-
 
+
-
<pre>
+
-
    <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>
+
-
</pre>
+
Discussion on nbdev: "Requirements for Supporting Annotation Processors in Mustang" [http://www.netbeans.org/servlets/BrowseList?list=nbdev&by=thread&from=542406 #1] [http://www.netbeans.org/servlets/BrowseList?list=nbdev&by=thread&from=551162 #2]
Discussion on nbdev: "Requirements for Supporting Annotation Processors in Mustang" [http://www.netbeans.org/servlets/BrowseList?list=nbdev&by=thread&from=542406 #1] [http://www.netbeans.org/servlets/BrowseList?list=nbdev&by=thread&from=551162 #2]
Line 173: Line 157:
--------
--------
-
Applies to: NetBeans 5.0
+
Applies to: NetBeans 6.7+

Revision as of 18:46, 17 December 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 (bug #111294 and others) 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] = 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 build.xml and add these lines:

    <target name="-do-compile" depends="init,deps-jar,-pre-pre-compile,-pre-compile,-compile-depend" if="have.sources" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3">
        <property name="ap.dir" location="${build.generated.sources.dir}/ap"/>
        <mkdir dir="${ap.dir}"/>
        <j2seproject3:javac gensrcdir="${build.generated.sources.dir}" sourcepath="${src.dir}">
            <customize>
                <compilerarg line="-implicit:class -s ${ap.dir}"/>
            </customize>
        </j2seproject3:javac>
        <copy todir="${build.classes.dir}">
            <fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
        </copy>
    </target>

Now build & clean and then run the project. You should see

run:
hello

The Projects tab should show a new Generated Sources (ap) node. Under it you should find:

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.

MainExtras will also be included in Javadoc generation for the project.

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


Applies to: NetBeans 6.7+

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