Tuesday Apr 01, 2014

Dynamically Creating Menu Items (Part 2)

OK, following on from yesterday, here's the next part. What we want to do is click a button, such as shown below, and, when the button is clicked, cause a new menu item to be added to a predefined menu, in this case the "Shortcuts" menu.

The first step is to define the Shortcuts menu in a layer.xml file:

<folder name="Menu">
    <folder name="Shortcuts"/>
</folder>

Next, we want to populate that folder dynamically, whenever the user clicks the button above. To do that, we need to get hold of the Shortcuts folder within the Menu folder. Then we add in there a new file with ".instance" as the extension, assign our own Action to it, and (as described here, thanks Ernie and the vi/vim project), use the "instanceCreate" and "instanceClass" attributes.

public class SomeTopComponent extends TopComponent {
    private DateFormat formatter = new SimpleDateFormat("HH:mm:ss");
    public SomeTopComponent() {
        setDisplayName("Some");
        setLayout(new BorderLayout());
        add(new JButton(new AbstractAction("Add new menu item to shortcuts") {
            @Override
            public void actionPerformed(ActionEvent e) {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        final String formatted = formatter.format(System.currentTimeMillis());
                        FileObject menuFolder = FileUtil.getConfigFile("Menu/Shortcuts");
                        try {
                            FileObject newMenu = menuFolder.createData(formatted, "instance");
                            AbstractAction action = new AbstractAction(
                                    "Added at: " + formatted) {
                                @Override
                                public void actionPerformed(ActionEvent e) {
                                    JOptionPane.showMessageDialog(null, formatted);
                                }
                            };
                            newMenu.setAttribute("instanceCreate", action);
                            newMenu.setAttribute("instanceClass", action.getClass().getName());
                        } catch (IOException ex) {
                            Exceptions.printStackTrace(ex);
                        }
                    }
                });
            }
        }), BorderLayout.NORTH);
    }
}

OK. We now know how to dynamically add a new menu item within a top level menu. But can a top level menu be itself created dynamically too?

That's even simpler:

public class SomeTopComponent extends TopComponent {
    private DateFormat formatter = new SimpleDateFormat("HH:mm:ss");
    public SomeTopComponent() {
        setDisplayName("Some");
        setLayout(new BorderLayout());
        add(new JButton(new AbstractAction("Add new menu to main menubar") {
            @Override
            public void actionPerformed(ActionEvent e) {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        final String formatted = formatter.format(System.currentTimeMillis());
                        FileObject menuFolder = FileUtil.getConfigFile("Menu");
                        try {
                            menuFolder.createFolder("Added at: " + formatted);
                        } catch (IOException ex) {
                            Exceptions.printStackTrace(ex);
                        }

                    }
                });
            }
        }), BorderLayout.NORTH);
    }
}

The result:


Monday Mar 31, 2014

Dynamically Creating Menu Items (Part 1)

Let's set up an infrastructure for dynamically creating menu items in a NetBeans Platform application. We start with this, i.e., a simple skeleton. We have an Action presented as a JMenu over which we have control via Presenter.Menu, together with a LookupListener that we're going to use for listening to the Lookup for objects of interest:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.util.LookupEvent;
import org.openide.util.LookupListener;
import org.openide.util.actions.Presenter;
@ActionID(category = "Shortcuts", id = "org.bla.core.DynamicShortcutMenu")
@ActionReference(path = "Menu/Shortcuts")
public class DynamicShortcutMenu 
    implements ActionListener, Presenter.Menu, LookupListener {
    JMenu menu = new JMenu();
    @Override
    public JMenuItem getMenuPresenter() {
        return menu;
    }
    @Override
    public void resultChanged(LookupEvent le) {
    }
    @Override
    public void actionPerformed(ActionEvent e) {
        // not used
    }
}

Next, let's develop our LookupListener further so that we can listen for new objects of interest. What the "object of interest" is, is up to you. Let's create an interface named "Clickable" as the currency, i.e., the communication mechanism that will be used to communicate with the Action that we have created above, i.e., to tell it that new menu items are needed.

import javax.swing.AbstractAction;
public interface Clickable {
    AbstractAction getAbstractAction();
}

OK. We have an interface that is a wrapper around an AbstractAction. Later on, we'll publish implementations of this interface.

For the moment, let's focus on changing the earlier code so that we can consume the implementations, once those implementations are published.

@ActionID(category = "Shortcuts", id = "org.bla.core.DynamicShortcutMenu")
@ActionRegistration(displayName = "not-used", lazy = false)
@ActionReference(path = "Menu/Shortcuts")
public class DynamicShortcutMenu extends AbstractAction
        implements ContextAwareAction, Presenter.Menu, LookupListener {
    private final Lookup.Result<Clickable> clickables;
    private JMenu menu = new JMenu("Dynamic");
    public DynamicShortcutMenu() {
        this(Utilities.actionsGlobalContext());
    }
    public DynamicShortcutMenu(Lookup lkp) {
        clickables = lkp.lookupResult(Clickable.class);
        clickables.addLookupListener(
                WeakListeners.create(LookupListener.class, this, clickables));
    }
    @Override
    public JMenuItem getMenuPresenter() {
        return menu;
    }
    @Override
    public void resultChanged(LookupEvent le) {
        for (Clickable c : clickables.allInstances()){
            menu.add(c.getAbstractAction());
        }
    }
    @Override
    public void actionPerformed(ActionEvent e) {
        // not used
    }
    @Override
    public Action createContextAwareInstance(Lookup lkp) {
        return new DynamicShortcutMenu(lkp);
    }
}

Great. We're now listening to the global lookup for Clickables and then adding their AbstractActions to the menu we created above.

Now that we know how to consume Clickables, let's learn how to publish a Clickable. Below you see a TopComponent that contains a JButton. When clicked, the JButton publishes a Clickable into the Lookup of the TopComponent. At that stage, the Action code above causes a new JMenuItem to be added. Note that at no point is a JMenuItem removed, i.e., when selection changes to a different TopComponent, the JMenuItem added via the code below will still be in the dynamic menu, which is probably how you want things to be anyway.

@TopComponent.Description(
        preferredID = "SomeTopComponent",
        persistenceType = TopComponent.PERSISTENCE_ALWAYS
)
@TopComponent.Registration(
        mode = "explorer",
        openAtStartup = true)
public class SomeTopComponent extends TopComponent {
    private DateFormat formatter = new SimpleDateFormat("HH:mm:ss");
    private InstanceContent ic = new InstanceContent();
    public SomeTopComponent() {
        setDisplayName("Some");
        setLayout(new BorderLayout());
        add(new JButton(new AbstractAction("Add new menu item to shortcuts") {
            @Override
            public void actionPerformed(ActionEvent e) {
                ic.set(Collections.singletonList(new SomeClickable()), null);
            }
        }), BorderLayout.NORTH);
        associateLookup(new AbstractLookup(ic));
    }
    private class SomeClickable implements Clickable {
        @Override
        public AbstractAction getAbstractAction() {
            final String formatted = formatter.format(System.currentTimeMillis());
            return new AbstractAction("Added at: " + formatted) {
                @Override
                public void actionPerformed(ActionEvent e) {
                    JOptionPane.showMessageDialog(null, "hello from " + formatted);
                }
            };
        }
    }
}

Here's the result.

Of course, this throws up some new questions. Especially, how to achieve this result for a menu within the menubar, rather than a submenu within a menu within the menubar. I.e., as you can see above, this only solves the situation where a third-level menu needs to be dynamic. But what about if the menu "Dynamic" above needs to itself be dynamic? And, even more complexly, what if the "Shortcuts" menu should, itself, be dynamic? See part 2 for the solutions to these questions.

Sunday Mar 30, 2014

"Cannot load even default layout, using internally predefined configuration"

When I created the FeedReader application in NetBeans IDE 8 and then ran it, this is what I saw:

The above tells you something is messed up in the System FileSystem.

The log shows among other things:

Caused: java.io.FileNotFoundException: [WinSys] Missing Window Manager configuration file
    at org.netbeans.core.windows.persistence.WindowManagerParser$PropertyHandler.readData(WindowManagerParser.java:911)
    at org.netbeans.core.windows.persistence.WindowManagerParser.readProperties(WindowManagerParser.java:365)
    at org.netbeans.core.windows.persistence.WindowManagerParser.load(WindowManagerParser.java:110)
    at org.netbeans.core.windows.persistence.PersistenceManager.loadWindowSystem(PersistenceManager.java:1156)
[catch] at org.netbeans.core.windows.PersistenceHandler.load(PersistenceHandler.java:130)
    at org.netbeans.core.windows.WindowSystemImpl.load(WindowSystemImpl.java:81)
    at org.netbeans.core.GuiRunLevel$InitWinSys.run(GuiRunLevel.java:229)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:744)
    at java.awt.EventQueue.access$400(EventQueue.java:97)
    at java.awt.EventQueue$3.run(EventQueue.java:697)
    at java.awt.EventQueue$3.run(EventQueue.java:691)

After quite a bit of searching, I discovered this file in the "branding" folder of the application. The "branding" folder can only be seen when you're in the Files window.

I deleted the "resources" folder above, together with the "layer.xml" file that you see there. It contained only "_hidden" elements, i.e., this is an older way of hiding menus and toolbars, that is now obsolete; now you do so within a module instead of in a file within the "branding" folder like the above. (Even better than deleting it is to move its content into one of the layer.xml files of your own modules, e.g., the Feed Reader module.)

Then I cleaned the application. And then I ran the application and everything was OK. Seems like an old file format of the layer.xml file, or maybe something changed in that module, I don't know what the reason is.

Saturday Mar 29, 2014

Cause a Wizard to Appear Automatically

Sometimes you may want a wizard to automatically appear, as a result of logic, rather than a user action. Normally, the user goes to the New File dialog or the New Project dialog or clicks an Action, such as provided by a menu item or toolbar button, and then a wizard starts up.

However, here's something slightly different. The module depends on the Window System API and hence the @OnShowing annotation is available.

import java.io.File;
import org.openide.awt.Actions;
import org.openide.windows.OnShowing;
@OnShowing
public class Startable implements Runnable {
    @Override
    public void run() {
        if (!new File("/path/to/some/folder").exists()) {
            Actions.forID("File", "org.something.core.CreateFolderWizardAction").actionPerformed(null);
        }
    }   
}

In the "run" method, use "Actions.forID" and pass in the category and the ID of the Action that starts up the wizard. Since when you use the Wizard wizard to create a wizard skeleton, you also get an Action, you should have an Action somewhere in your module structure. Instead of registering the wizard in the Action such that it appears as a toolbar button or menu item, just remove those annotations. The only annotations your Action needs are @ActionID and @ActionRegistration, not @ActionReference.

And with that, the wizard starts up after the application is initialized, automatically.

Friday Mar 28, 2014

Nashorn and NetBeans: Not Your Average Joe's Rhino

What can be done with JavaScript and Java together in NetBeans IDE via Nashorn is kind of mind blowing. Watch Jim Laskey explain Nashorn within 10 minutes, including mixing JavaScript and Java in NetBeans 8.

Based on JSR 292 and available in JDK 8, Nashorn is a JavaScript engine implemented fully in Java on the JVM. In the screencast above, from the Java 8 launch, the Nashorn JavaScript Engine is described and a top-level view of how it all works is provided, with demos of JavaScript & Java working together.

And, over on DZone, Constantin Drabo today published another great article with code from NetBeans 8 on this topic: Nashorn Application with NetBeans 8.

Thursday Mar 27, 2014

Absolutely Awesome NetBeans Plugin: JPA Modeler

Possibly the most exciting new NetBeans plugin that's under development is the JPA Modeler, led by Gaurav Gupta, who also works on the Business Process Modeling plugin for NetBeans:

http://plugins.netbeans.org/plugin/53057/jpa-modeler

Watch this short movie to see it in action. In its latest incarnation, available from the link above, you can reverse engineer a diagram from existing JPA sources, which is also amazing, and let's you use the JPA modeler on old sources, i.e., no need to create a new project from scratch to use this plugin. And, whenever the diagram becomes out of date, you can simply regenerate it, from the current state of your sources. I've tried it and it delivers exactly what I had expected. Plus, it looks seriously professional and very polished, as this annotated YouTube clip shows:

If you watch the above, you'll see right near the end that you can change the diagram, then regenerate the tables in your database, and recreate the JPA classes. I.e., this lets you visually model your data and then generate all the database content and Java classes from the diagram. Really powerful.

Also, follow the JPA Modeler YouTube Channel, there's lots of content there, constantly being updated with new movies.

Wednesday Mar 26, 2014

Update 1 for "NetBeans Platform for Beginners"

Books on Leanpub.com are never done. The authors of "NetBeans Platform for Beginners" plan to release a new update each month, which everyone who buys the book will automatically receive. With permission, below is the update page for the first update of the book, pretty cool stuff there.

I think "NetBeans Platform for Beginners" is a fantastic resource for NetBeans Platform developers, all the more so in combination with Leanpub.com, which is a simply wonderful authoring/publishing platform.

Tuesday Mar 25, 2014

James Gosling Weighs In On NetBeans 8

James Gosling, founder of Java, has been a consistent and massive fan of NetBeans for, well, ever. (Here he is after 10 years of NetBeans and, last year, after 15 years of NetBeans.) And now, here's his thoughts on NetBeans 8.
NetBeans has always been the tool with the best support for the latest technologies. NetBeans 8 is no exception. I've been using the early access releases for months, and it's been lovely. There is a long list of great features, but the two that have made the biggest difference to me have been the support for lambda expressions and the support for embedded development. It's absolutely magical how the "Use Lambda Expression" refactoring makes my code so much clearer.

I've been giving "Internet of Things" talks for decades. I'm thrilled by the extent to which, for ordinary developers, this has been exploding beyond cell phones. NetBeans embedded support makes this development painless, fluid, and fast-paced. Being able to debug a running robot, at sea (or wherever your robot goes) from a thousand miles away, is truly life-altering.

NetBeans 8 is a landmark release.

Slightly mangled, you can find the above quote in the Oracle NetBeans 8 press release, too.

Included in the Java 8 Launch screencasts, released today, you'll find a cool one on NetBeans 8 as well.

Monday Mar 24, 2014

Onwards to NetBeans 9

Extract from a brand new interview with Chris Tonas, Vice President of Mobility and Application Development Tools at Oracle:

The whole thing is here:

https://blogs.oracle.com/java/entry/the_future_of_application_development

Sunday Mar 23, 2014

R Plugin for NetBeans IDE (Part 2)

When I wrote about R in NetBeans IDE some time ago, there were a lot of responses. Constantin Drabo from Burkina Faso, who is leading this project, has made the sources available and I have checked them in here:

https://github.com/geertjanw/nbr

I probably messed up the check in a bit, especially the Analyzer module, may need to check it in again in a fresh repository. But if everything is set up correctly, the sources look like this:

Right now it's a little bit early to make the binaries available in the NetBeans Plugin Portal, but hopefully we'll wrap up the open issues soon. (Anyone interested in being alpha testers, please say so in the comments to this blog.)

R projects can be created and one of the aims of the R support for NetBeans IDE is to provide a learning environment for R newbies (rewbies?), which will consists of, for example, all the examples found here, as file templates, as you can see here:

An R file open in the editor looks like this:

You can register R in the Options window, enabling you to right-click an R file and generate visualizations within PDF files, as you can see here, including the PDF viewer that the R plugin provides:

Interesting projects to incorporate somehow include Java ordinary R infrastructure support (Joris), Rserve, and Java GUI for R.

Lots of fun work ahead!

Saturday Mar 22, 2014

Entity Expander for NetBeans IDE 8 (Part 3)

I now have the Entity Expander working as I want. Right-click any POJO (i.e., class that extends nothing other than java.lang.Object, either implicitly or explicitly), and then you will see the "Entity Expanders" menu, with two templates registered by default, one for a plain Java Swing JPanel and the other for a Vaadin Form:

When you select either of these two templates, the fields are used from the POJO and inserted into a new class that extends either JPanel or VaadinForm.

However, the interesting menu item above is "Create". When you click that, a new template is created, after you type the template name in a dialog box, and it is opened into the editor so that you can tweak it however you like. And, automatically, the template is available next time you right-click on a POJO:

Above, you can see that I have added two new templates, one named "WicketComponent" and the other "PrimeFacesUI". These are templates that I have created and that are applied to the currently selected POJO (when you click the related menu item above) to create a new class based on the template.

Use the Template Manager (from the Tools menu) to edit any of the templates or to remove them. Removed templates in the Template Manager are automatically removed from the "Entity Expander" menu:


The variables shown yesterday are the only ones available, plus a new one, "fieldsAndModifiers", to get the modifier and type, together with the name, of a field.

Interested in this feature? Try it out in NetBeans IDE 8.0 after downloading it here:

http://plugins.netbeans.org/plugin/53874

Friday Mar 21, 2014

Entity Expander for NetBeans IDE 8 (Part 2)

Let's imagine you read my blog entry yesterday where I showed how to create a template that generates a Vaadin Form from a plain old Java object. However, while reading that blog entry you might have thought to yourself: "Hmmm, what a pity that the template is hardcoded within the module, i.e., the end user isn't going to be able to change the template and so this story is only relevant for Vaadin users."

Well, you couldn't be more wrong, if you thought the above. Install the module and then go to Tools | Templates, as shown below. 

The Template Manager shows "vaadinForm" in the "EntityTemplates" folder, as you can see here:

When you select "vaadinForm", the "Open in Editor" button, shown above, becomes enabled. Click it... and then the template opens into the editor, as you can see here: 

And now you can change the template! In fact, you can even completely rewrite the template, so long as you bear in mind the relevance of the FreeMarker variables you see above:

  • ${object} - the name of the POJO from which the new class will be generated
  • ${package} - the name of the package where the new class will be put
  • ${fields} - this gives you access to all the fields in the POJO, with which you can do whatever you need

As you can see below, I changed "TextField" to "Something":

As soon as you have saved the above template (i.e., just click Save), whenever you use "Generate Vaadin Form" the changed template will be used, i.e., whatever changes you make will be applied the very next time you invoke the Action shown below:

What this means is that the Vaadin Form template could be the basis of generating anything at all from your entity classes. I think that's pretty awesome.

Now, think about what all this means. The above is the basis of a far more generic solution. I'm going to work on a more general plugin that exposes not only the fields, but also the methods, and any other class members, to the template. Then the user, i.e., a developer that has a POJO that should be treated as the basis of something else, will be able to generate something else, anything they want, from the fields and methods (and constructor and anything else) in any POJO in their application.

Thursday Mar 20, 2014

Entity Expander for NetBeans IDE 8 (Part 1)

It's not uncommon to have a Java entity class, a.k.a. a POJO, a.k.a. a domain object, a.k.a. a business object, and to want to generate something else from that class. So too Matti Tahvonen from Vaadin, who I will be joining on 3 April for the big Vaadin/NetBeans Webinar (sign up today here). He wants to be able to create Vaadin Forms from Java entity classes.

Therefore, I created a small plugin that adds an Action to an Object, and not to any other Java type, which is based on an Action I created a while ago for TopComponents (go here for that).

The Action you see above, i.e., the popup menu item with the label "Generate Vaadin Form" takes the FreeMarker template below (in a file named "vaadinForm.template") as the starting point for the Vaadin form that it creates:

package ${package};

import com.vaadin.ui.Component;
import com.vaadin.ui.FormLayout;
import com.vaadin.ui.TextField;
import org.vaadin.maddon.fields.MTextField;
import org.vaadin.maddon.form.AbstractForm;
import org.vaadin.maddon.layouts.MVerticalLayout;

public class ${object}Form extends AbstractForm<${object}> {

  <#list fields as field>
    private TextField ${field} = new MTextField("${field}");
  </#list>

    @Override
    protected Component createContent() {
        return new MVerticalLayout(
                new FormLayout(
                  <#list fields as field>
                   ${field},
                  </#list>
                ),
                getToolbar()
        );
    }

}

The file above is registered in the System FileSystem like this, in a package-info.java class:

@TemplateRegistration(
        content = "vaadinForm.template",
        scriptEngine = "freemarker",
        folder = "EntityTemplates")
package org.netbeans.entity.expander;

import org.netbeans.api.templates.TemplateRegistration;

Finally, here's all the code of the Action, mostly for enabling/disabling (i.e., hiding) the Action appropriately, with a little bit of code for accessing the right bits of the entity class for passing into the FreeMarker template:

@ActionID(
        category = "Tools",
        id = "org.tc.customizer.ExpandEntityAction")
@ActionRegistration(
        displayName = "#CTL_ExpandEntityAction",
        lazy = false)
@ActionReferences({
    @ActionReference(path = "Loaders/text/x-java/Actions", position = 150)
})
@Messages("CTL_ExpandEntityAction=Generate Vaadin Form")
public final class ExpandEntityAction extends AbstractAction implements ContextAwareAction {
    private final DataObject dobj;
    private static Map args = new HashMap();
    public ExpandEntityAction() {
        this(Utilities.actionsGlobalContext());
    }
    public ExpandEntityAction(Lookup context) {
        super(Bundle.CTL_ExpandEntityAction());
        this.dobj = context.lookup(DataObject.class);
        JavaSource javaSource = JavaSource.forFileObject(dobj.getPrimaryFile());
        if (javaSource != null) {
            try {
                javaSource.runUserActionTask(new ScanForObjectTask(this), true);
            } catch (IOException ex) {
                Exceptions.printStackTrace(ex);
            }
        }
        //Hide the menu item if it isn't enabled:
        putValue(DynamicMenuContent.HIDE_WHEN_DISABLED, true);
    }
    @Override
    public void actionPerformed(ActionEvent ev) {
        FileObject vaadinFormFO = FileUtil.getConfigFile("Templates/EntityTemplates/vaadinForm");
        try {
            DataObject vaadinFormDobj = DataObject.find(vaadinFormFO);
            DataFolder df = DataFolder.findFolder(dobj.getPrimaryFile().getParent());
            String name = dobj.getPrimaryFile().getName();
            vaadinFormDobj.createFromTemplate(df, name + "Form.java", args);
        } catch (DataObjectNotFoundException ex) {
            Exceptions.printStackTrace(ex);
        } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
        }
    }
    @Override
    public Action createContextAwareInstance(Lookup actionContext) {
        return new ExpandEntityAction(actionContext);
    }
    private static class ScanForObjectTask implements Task {
        private final ExpandEntityAction action;
        private ScanForObjectTask(ExpandEntityAction action) {
            this.action = action;
        }
        @Override
        public void run(CompilationController compilationController) throws Exception {
            compilationController.toPhase(Phase.ELEMENTS_RESOLVED);
            new MemberVisitor(compilationController, action).scan(
                    compilationController.getCompilationUnit(), null);
        }
    }
    private static class MemberVisitor extends TreePathScanner {
        private CompilationInfo info;
        private final AbstractAction action;
        public MemberVisitor(CompilationInfo info, AbstractAction action) {
            this.info = info;
            this.action = action;
        }
        @Override
        public Void visitClass(ClassTree t, Void v) {
            Element el = info.getTrees().getElement(getCurrentPath());
            if (el != null) {
                TypeElement te = (TypeElement) el;
                if (te.getSuperclass().toString().equals("java.lang.Object")) {
                    action.setEnabled(true);
                    int index = te.getQualifiedName().toString().lastIndexOf(".");
                    args.put("package", te.getQualifiedName().toString().substring(0, index));
                    args.put("object", te.getQualifiedName().toString().substring(index + 1));
                    List fields = new ArrayList();
                    for (Element e : te.getEnclosedElements()) {
                        if (e.getKind() == ElementKind.FIELD) {
                            fields.add(e.getSimpleName().toString());
                        }
                    }
                    args.put("fields", fields);
                } else {
                    action.setEnabled(false);
                }
            }
            return null;
        }
    }
}

A key statement in the above is this one, i.e, the "args" below contains values built up from the visitor code above, which is magically passed into the FreeMarker template, where the FreeMarker variables are replaced by the passed in values:

vaadinFormDobj.createFromTemplate(df, name + "Form.java", args);

Import statements:

import com.sun.source.tree.ClassTree;
import com.sun.source.util.TreePathScanner;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.swing.AbstractAction;
import javax.swing.Action;
import org.netbeans.api.java.source.CompilationController;
import org.netbeans.api.java.source.CompilationInfo;
import org.netbeans.api.java.source.JavaSource;
import org.netbeans.api.java.source.JavaSource.Phase;
import org.netbeans.api.java.source.Task;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionReferences;
import org.openide.awt.ActionRegistration;
import org.openide.awt.DynamicMenuContent;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataFolder;
import org.openide.loaders.DataObject;
import org.openide.loaders.DataObjectNotFoundException;
import org.openide.util.ContextAwareAction;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.NbBundle.Messages;
import org.openide.util.Utilities;

The necessary dependencies are as follows:

  • Datasystems API
  • File System API
  • Javac API Wrapper
  • nb-javac-api.jar
  • Java Source
  • Lookup API
  • Nodes API
  • UI Utilities API
  • Utilities API

This is the basis of a more generic solution, where the user can choose one of multiple templates to be used by the generator to transform the Java entity class to something else, which could be anything, in this case a Vaadin form.

Here's the module structure:


Source code:

https://java.net/projects/nb-api-samples/sources/api-samples/show/versions/8.0/misc/EntityExpander

Wednesday Mar 19, 2014

One Click Open Sesame in NetBeans 8

And my first plugin for NetBeans IDE 8 is done and published!

@andypotter on Twitter, and on the NetBeans users mailing list, wants to be able to open files by selecting them, rather than double-clicking. And the topic of double-clicking is something he is quite passionate about: "As nice as NetBeans is I will not use it if I must double-click every time I want to open a file into the editor."

Well, then, here's a plugin I created today that fixes that:

http://plugins.netbeans.org/plugin/53723

The code is as simple as this:

import org.netbeans.api.actions.Openable;
import org.openide.modules.OnStart;
import org.openide.util.Lookup;
import org.openide.util.LookupEvent;
import org.openide.util.LookupListener;
import org.openide.util.Utilities;
import org.openide.util.WeakListeners;

@OnStart
public class OneClickOpenSesame implements Runnable, LookupListener {
    Lookup.Result openableResult = null;
    @Override
    public void run() {
        openableResult = Utilities.actionsGlobalContext().lookupResult(Openable.class);
        openableResult.addLookupListener(
                WeakListeners.create(LookupListener.class, this, openableResult));
    }
    @Override
    public void resultChanged(LookupEvent le) {
        for(Openable o : openableResult.allInstances()){
            o.open();
        }
    }
}

Someone reading the above code should now be thinking: "But wait a minute, doesn't this mean that someone who has selected a file, and thereby opened it, is never going to be able to close that file, unless selection changes to a different file?"

Indeed, yes, well done. The fatal flaw of this awesome plugin. But not a big deal. If you want to be able to close the file you opened, just select a folder (i.e., a folder doesn't have an Openable in its Lookup), after which you can close the file as usual.

Tuesday Mar 18, 2014

The Happy Couple: Java 8 & NetBeans 8

Don't they look sweet together?

Get them here:

www.oracle.com/technetwork/java/javase/downloads

NetBeans 8 info:

netbeans.org/community/releases/80/index.html

Hello Java 8 and NetBeans 8:

netbeans.dzone.com/articles/hello-java-8-and-netbeans-8-1

Monday Mar 17, 2014

YouTube: ANTLR v4 with Terence Parr

Highly awesome, educational, and entertaining:

Sunday Mar 16, 2014

org.netbeans.spi.java.queries.AccessibilityQueryImplementation

Let's say you want to create a plugin for NetBeans IDE that needs to do something special to Java classes in certain packages.

Here's an example.

Above is your application. As part of the build process, you want to let the user handle one of the packages, such as "org.demo.algorithms", differently to the other packages. You'd like to let the user right-click on the package and choose "Export". When they've done that, the package should be registered somewhere for special post-processing, while the user should be able to see a special icon indicating that the package has been exported, with the possibility of reverting the export.

How to do this?

The starting point is to read org.netbeans.spi.java.queries.AccessibilityQueryImplementation. As a simple demo, let's implement it as follows, which requires dependencies on File System API, Java Support APIs, Lookup API, and Project API:

import org.netbeans.spi.java.queries.AccessibilityQueryImplementation;
import org.netbeans.spi.project.ProjectServiceProvider;
import org.openide.filesystems.FileObject;
@ProjectServiceProvider(
        projectType = "org-netbeans-modules-java-j2seproject",
        service = AccessibilityQueryImplementation.class)
public class DemoAccessibilityImpl implements AccessibilityQueryImplementation {
    @Override
    public Boolean isPubliclyAccessible(FileObject pkg) {
        return pkg.getName().equals("algorithms");
    }
}

Now, when you install the above, you see this in our sample Java SE application:


Look at the unlock/lock icons that  have been merged into the package icons, based on the condition we set in our AccessibilityQueryImplementation.

Let's now make the unlock/lock icon appear based on whether a property "enableForJavaCard" has been set in the project.properties file of the project and, if so, whether the package has been registered in that property.

@ProjectServiceProvider(
        projectType = "org-netbeans-modules-java-j2seproject",
        service = AccessibilityQueryImplementation.class)
public class DemoAccessibilityImpl implements AccessibilityQueryImplementation {
    private final Project project;
    public DemoAccessibilityImpl(Project project) {
        this.project = project;
    }
    @Override
    public Boolean isPubliclyAccessible(FileObject pkg) {
        String packageName = pkg.getName();
        FileObject projectprop =
           project.getProjectDirectory().getFileObject(AntProjectHelper.PROJECT_PROPERTIES_PATH);
        EditableProperties ep;
        try {
            ep = loadProperties(projectprop);
            if (ep.containsKey("enableforJavaCard")) {
                return ep.get("enableforJavaCard").contains(packageName);
            }
        } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
        }
        return false;
    }
    private static EditableProperties loadProperties(FileObject propsFO) throws IOException {
        InputStream propsIS = propsFO.getInputStream();
        EditableProperties props = new EditableProperties(true);
        try {
            props.load(propsIS);
        } finally {
            propsIS.close();
        }
        return props;
    }
}

Next, we need to let the user switch between unlock/lock icons, i.e., export/unexport icons. That's done via an Action registered in "Projects/package/Actions".

There are at least two ugly solutions that are part of the code below; firstly, how the package name is constructed, secondly, how the icons are refreshed (via introspection).

@ActionID(
        category = "Edit",
        id = "org.netbeans.access.PublishPackageAction"
)
@ActionRegistration(
        lazy = false,
        displayName = "#CTL_PublishPackageAction"
)
@ActionReference(
        path = "Projects/package/Actions",
        position = 0)
@Messages("CTL_PublishPackageAction=Publish")
public final class PublishPackageAction extends AbstractAction implements LookupListener {
    private DataObject context;
    private Lookup.Result<DataObject> dataObjects;
    private boolean publishable = true;
    public PublishPackageAction() {
        dataObjects = Utilities.actionsGlobalContext().lookupResult(DataObject.class);
        dataObjects.addLookupListener(
                WeakListeners.create(LookupListener.class, this, dataObjects));
    }
    @Override
    public void actionPerformed(ActionEvent ev) {
        Project project = FileOwnerQuery.getOwner(context.getPrimaryFile());
        FileObject projectprop =
            project.getProjectDirectory().getFileObject(AntProjectHelper.PROJECT_PROPERTIES_PATH);
        EditableProperties ep;
        try {
            ep = loadProperties(projectprop);
            FileObject pkg = context.getPrimaryFile();
            String path = pkg.getPath();
            String packageName =
                   path.substring(path.lastIndexOf("src/")).replace("src/", "").replace("/", ".");
            if (ep.containsKey("enableforJavaCard")) {
                if (publishable &&
                  !ep.get("enableforJavaCard").contains(packageName)) {
                    ep.setProperty("enableforJavaCard",
                       ep.get("enableforJavaCard") + packageName + ",");
                } else if (!publishable &&
                   ep.get("enableforJavaCard").contains(packageName)) {
                    ep.setProperty("enableforJavaCard",
                       ep.get("enableforJavaCard").replace(packageName + ",", ""));
                }
            } else if (publishable) {
                ep.setProperty("enableforJavaCard", packageName + ",");
            }
            storeProperties(projectprop, ep);
            Node node = context.getNodeDelegate();
            uglyNodeIconRefresh(node);
        } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
        }
    }
    private void uglyNodeIconRefresh(Node found) {
        try {
            Method m1 = Node.class.getDeclaredMethod("fireIconChange");
            m1.setAccessible(true);
            Method m2 = Node.class.getDeclaredMethod("fireOpenedIconChange");
            m2.setAccessible(true);
            m2.invoke(found);
            m1.invoke(found);
        } catch (NoSuchMethodException ex) {
            Exceptions.printStackTrace(ex);
        } catch (SecurityException ex) {
            Exceptions.printStackTrace(ex);
        } catch (IllegalAccessException ex) {
            Exceptions.printStackTrace(ex);
        } catch (IllegalArgumentException ex) {
            Exceptions.printStackTrace(ex);
        } catch (InvocationTargetException ex) {
            Exceptions.printStackTrace(ex);
        }
    }
    private static EditableProperties loadProperties(FileObject propsFO) throws IOException {
        InputStream propsIS = propsFO.getInputStream();
        EditableProperties props = new EditableProperties(true);
        try {
            props.load(propsIS);
        } finally {
            propsIS.close();
        }
        return props;
    }
    public static void storeProperties(FileObject propsFO, EditableProperties props) throws IOException {
        FileLock lock = propsFO.lock();
        try {
            OutputStream os = propsFO.getOutputStream(lock);
            try {
                props.store(os);
            } finally {
                os.close();
            }
        } finally {
            lock.releaseLock();
        }
    }
    @Override
    public void resultChanged(LookupEvent ev) {
        Collection<? extends DataObject> dos = dataObjects.allInstances();
        if (dos.size() == 1) {
            DataObject currentDataObject = dos.iterator().next();
            FileObject selectedPkgIter = currentDataObject.getPrimaryFile();
            if (selectedPkgIter.isFolder()) {
                Boolean isPublic = AccessibilityQuery.isPubliclyAccessible(selectedPkgIter);
                if (isPublic) {
                    putValue("popupText", "Unpublish " + currentDataObject.getPrimaryFile().getName());
                    publishable = false;
                } else {
                    putValue("popupText", "Publish " + currentDataObject.getPrimaryFile().getName());
                    publishable = true;
                }
                context = currentDataObject;
            }
        }
    }
}

And now you have an Action on packages for publishing/unpublishing a package so that it is added/removed from a property in the project.properties file.

Below, the user right-clicks on a package named "org.demo.actions", which has not been registered in the "enableforJavaCard" property, hence the "Publish" text is shown, together with the package name:

Here, the "enableforJavaCard" property includes the "org.demo.ui" package, hence the "Unpublish" text is shown, together with the package name:

What's the point of all this? Well, it's now possible to register packages for post-processing for Java Card application development. This provides a much simpler alternative to jCardSim, via the Femto project which is what Eduard Karel de Jong is working on. With the above solution, he now has a hook to separating packages for Java Card processing.

YouTube soundless demo showing the workflow and result:

If you can't see the above for some reason, go here: https://www.youtube.com/watch?v=DHSVxJA9abI

Friday Mar 14, 2014

Top 5 Benefits of "NetBeans Platform for Beginners"

I've now read through most of the recently released "NetBeans Platform for Beginners" (subtitled "Modular Application Development for the Java Desktop"), parts of it several times, and here are what I believe are its greatest benefits:

  1. It provides a very comprehensive overview. Of all the key NetBeans APIs, all in one place, as opposed to scattered all over the Web. The benefit is that it gives you everything you need, all in one place.

  2. It combines theory and practice. Each chapter is split between a theoretical understanding of a particular topic and a series of step by step instructions for applying the concepts. The benefit is that you're given the opportunity of learning about the "why", followed and supported by the "how". By the end of each chapter, you have a thorough understanding of the concept, together with a lot of code samples that you can apply to your own scenarios.

  3. It provides many samples. There isn't a single sample that is built throughout the book, but many smaller samples built within each chapter. The benefit is that you can jump into a particular chapter, such as on "Lookup" or "Nodes" and then learn everything you need to learn on that topic, with several samples built from scratch within that chapter.

  4. It is a "living book". That means that the authors will provide updates over time. I.e., several comments have already been received on the Feedback Page for the book and these will be incorporated into revisions of the book which will be made available for free to everyone who has bought it. If you have feedback on the book, make sure to provide it on the Feedback Page or in e-mails to the authors.

  5. It is not for beginners only. Many obscure corners of the NetBeans APIs are covered, as well as many topics that anyone with an advanced background will find useful. E.g., do you know how to do reordering of Nodes? How to create layer generating annotations? How to make context-sensitive Actions based on property values? And how to put all these pieces together into a single application? Many of the chapters are divided into "Basic" and "Advanced", which helps in understanding which pieces you will need a lot and which parts are corner cases.

Moreover, it is hopefully the start of many more books on NetBeans by these authors! Therefore, whether the book applies to you directly or not, take this opportunity to support the authors while learning about modular application development... and buy NetBeans Platform for Beginners today!

Thursday Mar 13, 2014

New NetBeans, Java EE, and HTML5 Book

Adding to several new books related to NetBeans, here's another one, which I received in a box of 10 yesterday, as one of its co-authors, together with JB Brock and Arun Gupta.

Build and distribute business web applications that target both desktop and mobile devices. Cowritten by Java EE and NetBeans IDE experts, Java EE and HTML5 Enterprise Application Development fully explains cutting-edge, highly responsive design tools and strategies. Find out how to navigate NetBeans IDE, construct HTML5 programs, employ JavaScript APIs, integrate CSS and WebSockets, and handle security. This Oracle Press guide also offers practical coverage of SaaS, MVVM, and RESTful concepts.

Get it here on Amazon.com.

Wednesday Mar 12, 2014

EditorConfig and NetBeans IDE (Part 3)

Ultimately, org.netbeans.spi.project.ui.ProjectOpenedHook should be used. When a project opens, a FileChangeListener should start listening recursively from the project root downwards for the presence of .editorconfig files. No explicit Action should be needed, i.e., automatically the listening should begin and, if an .editorconfig file is found, it should be applied to the level where it applies.

The relevant properties are not many.

  • indent_style
  • indent_size
  • tab_width
  • end_of_line
  • charset
  • trim_trailing_whitespace
  • insert_final_newline
  • max_line_length

Just the above 8, apparently nothing more than that.

About

Geertjan Wielenga (@geertjanw) is a Principal Product Manager in the Oracle Developer Tools group living & working in Amsterdam. He is a Java technology enthusiast, evangelist, trainer, speaker, and writer. He blogs here daily.

The focus of this blog is mostly on NetBeans (a development tool primarily for Java programmers), with an occasional reference to NetBeans, and sometimes diverging to topics relating to NetBeans. And then there are days when NetBeans is mentioned, just for a change.

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
12
13
14
24
25
26
27
28
29
30
   
       
Today