Let's use org.openide.filesystems.annotations.LayerGeneratin gProcessor for the first time. We will use it to process an @Action annotation which we will be able to set on ActionListeners in NetBeans Platform applications, as shown yesterday.
Here's the definition of my @Action annotation, with Javadoc for "path()", so that you can see how, in yesterday's blog entry, the second screenshot was able to show Javadoc.package org.demo.action.annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Retention(RetentionPo licy.SOURCE)@Target(ElementType.TYPE)public @interface Action { int position() default Integer.MAX_VALUE; String displayName(); /** * The path to the folder where this action will be registered. * The menu item and/or toolbar button will be registered * in the same folder. For example, if "Edit" is returned, * the action will be registered in (at least) "Actions/Edit", * as well as, optionally, "Menu/Edit" and "Toolbars/Edit". * @return String (default is "File") */ String path() default "File"; String iconBase() default ""; boolean menuBar() default false; boolean toolBar() default false; }
Take note that the @Target is set to ElementType.TYPE. That means that the @Action annotation will be settable on the class level, i.e., as opposed to method level or field level or something else. Also note that @Retention is set to RetentionPolicy.SOURCE, which means that the the @Action annotation will be processed at compile time, i.e., as opposed to runtime.
So, now, I can annotate classes like this:package org.demo.action;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import org.demo.action.annotation.Action;@Action(position = 1, displayName = "#key",menuBar = true, toolBar = true, iconBase = "org/demo/action/icon.png")public class DemoActionListener implements ActionListener { public void actionPerformed(ActionEvent e) { System.out.println("hello world"); }}
When I build the module that contains the above ActionListener... guess what? In build/classes/META-INF, I see an XML file named "generated-layer.xml", which has all of the following content:
So... ALL of the XML above, i.e., the file as well as the content, was generated when I built the module containing the ActionListener shown earlier. I.e., the annotations on that specific ActionListener resulted in the above specific XML content in build/classes/META-INF. (And if I had 10 ActionListeners or 100 ActionListeners or any other number, they'd all be processed in the same way, if I add an @Action annotation to the class declaration, as shown above.)
All of this is made possible by... the annotation processor that processes the @Action annotation. This particular annotation processor uses several NetBeans API annotation processor classes, which enables the XML layer file to be created, with the content shown above.
And here it is:package org.demo.action.annotation.impl;import java.util.Set;import javax.annotation.processing.Processor;import javax.annotation.processing.RoundEnvironment;impor t javax.annotation.processing.SupportedAnnotationTyp es;import javax.annotation.processing.SupportedSourceVersion ;import javax.lang.model.SourceVersion;import javax.lang.model.element.Element;import javax.lang.model.element.TypeElement;import javax.lang.model.util.Elements;import org.demo.action.annotation.Action;import org.openide.filesystems.annotations.LayerBuilder.F ile;import org.openide.filesystems.annotations.LayerGeneratin gProcessor;import org.openide.filesystems.annotations.LayerGeneratio nException;import org.openide.util.lookup.ServiceProvider;@ServicePr ovider(service = Processor.class)@SupportedAnnotationTypes("org.demo.action.annotation.Action")@SupportedSourceVersion(SourceVersion.RELEASE_5)p ublic class ActionProcessor extends LayerGeneratingProcessor { @Override protected boolean handleProcess( Set