Someone creating superclass sensitive actions should need to specify only the following things:
In other words, the code for the enablement (which, in this case, means the visibility of the popup menu item when you right-click on the Java class) should be handled generically, under the hood, and not every time all over again in each action that needs this special kind of enablement.
So, here's the usage of my newly created @SuperclassBasedActionAnnotation, where you should note that the DataObject must be in the Lookup, since the action will only be available to be invoked when you right-click on a Java source file (i.e., text/x-java) in an explorer view:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import org.netbeans.sbas.annotations.SuperclassBasedActionAnnotation;
import org.openide.awt.StatusDisplayer;
import org.openide.loaders.DataObject;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
@SuperclassBasedActionAnnotation(
position=30,
displayName="#CTL_BrandTopComponentAction",
path="File",
type="org.openide.windows.TopComponent")
@NbBundle.Messages("CTL_BrandTopComponentAction=Brand")
public class BrandTopComponentAction implements ActionListener {
private final DataObject context;
public BrandTopComponentAction() {
context = Utilities.actionsGlobalContext().lookup(DataObject.class);
}
@Override
public void actionPerformed(ActionEvent ev) {
String message = context.getPrimaryFile().getPath();
StatusDisplayer.getDefault().setStatusText(message);
}
}
That implies I've created (in a separate module to where it is used) a new annotation. Here's the definition:
package org.netbeans.sbas.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface SuperclassBasedActionAnnotation {
String type();
String path();
int position();
String displayName();
}
And here's the processor:
package org.netbeans.sbas.annotations;
import java.util.Set;
import javax.annotation.processing.Processor;
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.TypeElement;
import javax.lang.model.util.Elements;
import org.openide.filesystems.annotations.LayerBuilder.File;
import org.openide.filesystems.annotations.LayerGeneratingProcessor;
import org.openide.filesystems.annotations.LayerGenerationException;
import org.openide.util.lookup.ServiceProvider;
@ServiceProvider(service = Processor.class)
@SupportedAnnotationTypes("org.netbeans.sbas.annotations.SuperclassBasedActionAnnotation")
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class SuperclassBasedActionProcessor extends LayerGeneratingProcessor {
@Override
protected boolean handleProcess(Set annotations, RoundEnvironment roundEnv) throws LayerGenerationException {
Elements elements = processingEnv.getElementUtils();
for (Element e : roundEnv.getElementsAnnotatedWith(SuperclassBasedActionAnnotation.class)) {
TypeElement clazz = (TypeElement) e;
SuperclassBasedActionAnnotation mpm = clazz.getAnnotation(SuperclassBasedActionAnnotation.class);
String teName = elements.getBinaryName(clazz).toString();
String originalFile = "Actions/" + mpm.path() + "/" + teName.replace('.', '-') + ".instance";
File actionFile = layer(e).file(
originalFile).
bundlevalue("displayName", mpm.displayName()).
methodvalue("instanceCreate", "org.netbeans.sbas.annotations.SuperclassSensitiveAction", "create").
stringvalue("type", mpm.type()).
newvalue("delegate", teName);
actionFile.write();
File javaPopupFile = layer(e).file(
"Loaders/text/x-java/Actions/" + teName.replace('.', '-') + ".shadow").
stringvalue("originalFile", originalFile).
intvalue("position", mpm.position());
javaPopupFile.write();
}
return true;
}
}
The "SuperclassSensitiveAction" referred to in the code above is unchanged from how I had it in yesterday's blog entry.
When I build the module containing two action listeners that use my new annotation, the generated layer file looks as follows, which is identical to the layer file entries I hard coded yesterday:
<folder name="Actions">
<folder name="File">
<file name="org-netbeans-sbas-impl-ActionListenerSensitiveAction.instance">
<attr name="displayName" stringvalue="Process Action Listener"/>
<attr methodvalue="org.netbeans.sbas.annotations.SuperclassSensitiveAction.create" name="instanceCreate"/>
<attr name="type" stringvalue="java.awt.event.ActionListener"/>
<attr name="delegate" newvalue="org.netbeans.sbas.impl.ActionListenerSensitiveAction"/>
</file>
<file name="org-netbeans-sbas-impl-BrandTopComponentAction.instance">
<attr bundlevalue="org.netbeans.sbas.impl.Bundle#CTL_BrandTopComponentAction" name="displayName"/>
<attr methodvalue="org.netbeans.sbas.annotations.SuperclassSensitiveAction.create" name="instanceCreate"/>
<attr name="type" stringvalue="org.openide.windows.TopComponent"/>
<attr name="delegate" newvalue="org.netbeans.sbas.impl.BrandTopComponentAction"/>
</file>
</folder>
</folder>
<folder name="Loaders">
<folder name="text">
<folder name="x-java">
<folder name="Actions">
<file name="org-netbeans-sbas-impl-ActionListenerSensitiveAction.shadow">
<attr name="originalFile" stringvalue="Actions/File/org-netbeans-sbas-impl-ActionListenerSensitiveAction.instance"/>
<attr intvalue="10" name="position"/>
</file>
<file name="org-netbeans-sbas-impl-BrandTopComponentAction.shadow">
<attr name="originalFile" stringvalue="Actions/File/org-netbeans-sbas-impl-BrandTopComponentAction.instance"/>
<attr intvalue="30" name="position"/>
</file>
</folder>
</folder>
</folder>
</folder>
Hello,
I followed you example but I can't get ANY custom processor running.. Trying to put some "debug strings" in the code but nothing... I just see:
Note: Attempting to workaround javac bug #6512707
Note: eu.margiel.SuperclassBasedActionProcessor to be registered as a javax.annotation.processing.Processor
warning: No processor claimed any of these annotations: [javax.annotation.processing.SupportedSourceVersion, javax.annotation.processing.SupportedAnnotationTypes, eu.margiel.SuperclassBasedActionAnnotation]
is there any precondition that I am missing or anything like this?
The annotation needs to be defined in one module. In another module, use the annotation. Don't define and use within the same module.
now it worked. thanks, I think that this info should be somwhere inside this post (I couldn't find it anywhere).
anyway - I have one more question, I wanna use my custom LayerGeneratingProcessor to generate Top Menu (so layer.xml) accordingly to User roles - which can change while using our app on some actions.
Is it possible to regenerate layer.xml during runtime? Now we are doing that by loading multiple layer files - but I hate this solution since it is very messy. I would like to have kind of builder where in one place I would like to define which menus are available to which roles.
Something like...
@MenuBuilder
public class MyMenuBuilder{
public Menu create(){
return new Menu()
.item(new MenuItem("My Item", Action1.class).forRoles(ADMIN))
.item(new MenuItem("My Item 2", Action1.class).forRoles(CONTRIBUTOR));
}
}
Any idea how I can achieve that?
Don't think that's possible. I'd recommend writing to dev@platform.netbeans.org with this and other questions, sorry, don't know the answer to this.
I still confused about your opinion "When I build the module containing two action listeners that use my new annotation, the generated layer file looks as follows".
Can you describe what this exactly,because I have try your example but I can't get it running,this makes me wonder how it can works,since I have tried for 2 nights,
regard,
listyo
http://www.advantoday.com