Saturday May 31, 2008

Epictetus: Yet Another NetBeans Platform Application

A standalone NetBeans Platform application for database management. I came across it yesterday here on Javalobby. Pretty interesting. I downloaded the application, set up MySQL (which was a piece of cake on Ubuntu), and was then able to browse my database and so on:

The range of database servers supported is impressive:

I think the application used to be called "Chronos" (here's an announcement on Chronos 0.2.1 Beta and Chronos 0.2 Beta).

And here's a funny video of an even earlier version (funny because of the accompanying music, but slightly blurry):

I'm looking forward to seeing how this application develops. Antilogic Software, the company behind it, seems pretty interesting.

Friday May 30, 2008

Reshaping NetBeans

Reading How to Create Translucent and Shaped Windows, I discovered a link at the end to a NetBeans project. That's pretty cool. And I've been using JDK 6 Update 10 Beta for quite a while now, so there was nothing special I needed to do to get set up. So I narrowed the code down to its absolute basics. I started by creating a new JFrame using the JFrame Form template in the IDE. Importantly, to create a round JFrame, you need to set its "undecorated" property to false (in Matisse GUI Builder that means just a click in a checkbox in the Properties sheet). Then define the main method like this:

public static void main(String[] args) {
    java.awt.EventQueue.invokeLater(new Runnable() {
        ComponentListener shapeListener = null;
        @Override
        public void run() {
            final JFrame frame = new NewJFrame();
            frame.addComponentListener(shapeListener = new ComponentAdapter() {
                @Override
                public void componentResized(ComponentEvent evt) {
                    Shape shape = null;
                    shape = new Ellipse2D.Float(0, 0, frame.getWidth(), frame.getHeight());
                    AWTUtilities.setWindowShape(frame, shape);
                }
            });
            frame.setVisible(true);
        }
    });
}

Then run it and you have an ellipse instead of your boring old pointy cornered JFrame. Speaking of which, replace the line that defines the shape above with this one and you'll have a less radical result, i.e., not an ellipse, but a rounded rectangle:

shape = new RoundRectangle2D.Float(0, 0, frame.getWidth(), frame.getHeight(), 30, 30);

It's kind of funny when you're experimenting with this, because your JFrame is normal in design mode, while being transformed at deployment:

However, that's only the start. Since it is possible to grab the main window of the NetBeans Platform and cast it to a JFrame, you can do fun (though probably pointless) things like this:

The above is made possible via this Installer class in a NetBeans module which, when plugged into the NetBeans Platform, produces the results above:

public class Installer extends ModuleInstall {

    @Override
    public void restored() {
        SwingUtilities.invokeLater(new Runnable() {
            private ComponentListener shapeListener = null;
            public void run() {
                final JFrame frame = (JFrame) WindowManager.getDefault().getMainWindow();
                frame.setUndecorated(true);
                frame.addComponentListener(shapeListener = new ComponentAdapter() {
                    @Override
                    public void componentShown(ComponentEvent evt) {
                        Shape shape = null;
                        shape = new Ellipse2D.Float(0, 0, frame.getWidth(), frame.getHeight());
                        AWTUtilities.setWindowShape(frame, shape);
                    }
                });
            }
        });
    }
}

An error is thrown because the "setUndecorated" is called at the wrong time. Not sure when to call it to fix this. Still, pretty cool to see the above results.

Other great articles on this topic are this one and this one by Kirill.

Updated: VisualVM Blog Entries and Other Documentation

Today I did something that I've never done before—I updated some of my blog entries. There had been several (well, one or two, to be exact) requests for me to update my VisualVM series to use the latest VisualVM APIs, and now I have done so. In each case you'll find that the blog entry begins and ends with the assurance that, despite the date on the blog entry being sometime in the past, the code is completely current, at least to the specified date.

Also, where applicable, I've referenced the new set of samples and templates that are helpful in getting started as well as troubleshooting the scenarios described in the blog entries. In fact, each of the scenarios discussed is now checked in as a sample on visualvm.dev.java.net and available in binary form as part of the VisualVM Sample Collection.

So, here's the list of blog entries on VisualVM, all of them updated to the current APIs:

In addition, the VisualVM API Quick Start has been completely updated. Next, I will work on the FAQs at the end of that document. Some need to be updated and there are other questions to be added to that document.

In other news. In the process of some tweaking, I messed up the CSS of this blog so that the left sidebar doesn't work as it should, i.e., the width is too wide. If some CSS guru could volunteer to help me unravel my CSS and fix that, I would really appreciate it. I'd also like the whole sidebar to be on the right side of the blog.

Thursday May 29, 2008

The 10,000th Plugin in the NetBeans Plugin Portal?

Check it out... I just created a new entry in the Plugin Portal for a collection of VisualVM samples. And look at the number in the URL:

http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=10000

That seems to imply that I am number 10,000. I.e., there are 10,000 plugins in the Plugin Portal. That's assuming, of course, that counting begins with "1" and not some other number. Still, not bad to be number 10,000. When you install it, you get a bunch of VisualVM module samples in the New Project wizard:

Some of those samples are more than just samples—they provide some actual useful functionality. Read more about them here on Javalobby.

Today on NetBeans Zone. Fabrizio Giudici tells us about NetBeans & JPA with Multiple Persistence.xml Files.

Tuesday May 27, 2008

Tom Wheeler in Prague

Last week NetBeans guru Tom Wheeler and his wife Dawn were in Prague. A few NetBeans people hung out with them in a Czech restaurant one night. Dalibor Topic and David Herron, from the OpenJDK team, were there too—they were in Prague because of the Ubuntu fest that took place here last week.

At the end of the evening, i.e., early hours of the morning, Jarda took Tom and I on a tour of the significant places in the history of NetBeans. Being one of the founders of NetBeans, he could tell us all about it, as well as where all the early meetings and so on took place. It turned out that all the places were drinking establishments and most of them were closed. But eventually we found one (it was a very interesting pub, with taps on the table, one for each person, and a computerized scoreboard on the wall that kept a tally of how much each table had consumed, so a semi-competitive setting, in other words.)

Most pics taken were not of great quality, because it was dark, but here are a few to give an impression:

So, which NetBeans user will drop by next? We can promise a fun time.

Monday May 26, 2008

Advanced Java Swing Calendar: Perfect for NetBeans Users!

Many/most(?) applications need a calendar in one form or another. MiG Calendar, by Swedish fighter pilot / Swing developer Mikael Grev, is an out-of-the-box calendar component for Java Swing applications. But not for free. However, it seems cool enough (and well documented enough) to make it a very worthwhile choice.

On top of that, it seems particularly well suited for NetBeans users. The evaluation page shows the following:

So I installed that NetBeans plugin in NetBeans IDE 6.1 and got a whole bunch of new components in the palette:

Plus, when I downloaded the evaluation pack (i.e., not the plugin, but one of the other items in the first screenshot above), I found a bunch of NetBeans projects in the download:

Especially the "CategoriesDemo" is great:

And porting it to the NetBeans Platform was a purely point-and-click activity:

I reckon that, especially if you're a NetBeans Platform developer, i.e., especially if you're working on a LARGE application, the advanced functionality provided by the Mig Calendar component seems a perfect fit. Do you really want to mess around creating your own calendar? Or do you want to take something off the shelf that is very configurable (and well documented with great samples) and simply integrate it into your application?

By the way, this particular information would have been available to far more people and a lot sooner if it had been made available via the Plugin Portal. In other words, you don't need to make an actual plugin available there if you don't want to do that—you can simply create an entry that points to a page where your plugin is found, even if the functionality isn't free and even if it is for evaluation purposes only.

PS: Never mind. Here it is:

http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=4588

Saturday May 24, 2008

NetBeans.tv: Embedded Player Rocks!

I found out last week that NetBeans.tv now has a NetBeans branded player and also offers embedded code for its movies so that you can easily place them on your own site. I'm trying it out here for the first time. I simply went to the NetBeans.tv Community page, saw Fabrizio's cool movie about blueMarine (which he showed at his JavaOne Technical Session), clicked "Read more" under "Brief description" and then saw the tags that I needed to copy:

Then I pasted them into this blog entry. The result is very nice. Small space taken up, professional look and feel, smooth running of video, responsive forward/back buttons, etc.

Note: This particular video includes sound, so if that's a potential issue, make sure to plug your headset in before starting the movie:

<script type="text/javascript" src="http://www.netbeans.tv/js/swfobject.js"></script> <script type="text/javascript"> </script>

Be aware that... the NetBeans.tv embedded player offers a higher resolution and better quality videos than YouTube!

Also note another change, which is that it is now easier for you to submit a video, with a more streamlined form and the usage of keywords for search engine indexing. You can see the new form live at http://www.netbeans.tv/community/join-in/. You are recommended to upload a "flv" file format for video, although "mov" and others are also acceptable. Optimally, it should be under 100MB in size.

Learning from the Bible

Yet another application on the NetBeans Platform! Alkitab Bible Study, by the same developers in Indonesia who created (at least) two other applications on the NetBeans Platform—Sketsa SVG Editor and Citra FX Photo Effects.

It is also built on top of JSword, the free Bible study software, and looks like this (running on Ubuntu Linux, in this case):

Even better, the source code is available too, so there's a lot you could learn from it if you're developing on top of the NetBeans Platform yourself. Once downloaded, just open the suite and there it is, a fantastic sample application on top of the NetBeans Platform:

So... that's a free, mature sample of an application on top of the NetBeans Platform! Hurray for Tonny Kohar and everyone else at Kiyut. Especially if you're developing a similar application, these learning resources are worth their price in gold. (Oops, their price is zero, so... hmmm.) Of course, since the application is pluggable, you could extend it by creating new NetBeans modules for it. Finally, why is it called "Alkitab", you ask? If you'd studied (or if you are) Arabic a bit (as I have), you'd know that "Al Kitab" is the phonetic Arabic for... "the book"!

Friday May 23, 2008

10 Steps to Happiness with Maven and the NetBeans Platform

If you're interested in Maven and NetBeans IDE, you should definitely read Best Practices for Apache Maven in NetBeans 6.x, by Milos Kleint, the developer of Maven support in NetBeans IDE. There is an incredible amount of tips and tricks in there, from getting started, to deployment, to optimizing your editing experience. It is probably the most important document you will ever find on Maven and NetBeans IDE. Another crucial article is NetBeans Platform Development with Maven 2 by Emilian Bold, although the archetypes used in this blog entry did not exist at the time that Emilian wrote that article. However, everything in that article is of vital importance to you if you are using Maven to create your NetBeans Platform application/s.

Now let's create our first NetBeans Platform application via the tailor-made NetBeans Platform Maven archetype provided by the NetBeans Maven plugin for NetBeans IDE 6.1.

  1. Start NetBeans IDE 6.1 and install the Maven plugin (Tools | Plugins | Available Plugins).

  2. Go to the New Project wizard (Ctrl-Shift-N) and choose Maven | Maven Project and then click Next.

  3. Choose "Maven Netbeans Platform Application Archetype":

    Click Next.

  4. Very important: Don't forget to fill out the details at the bottom of the dialog:

    The 'brandingtoken' and the 'cluster' could be set to anything. The 'brandingtoken' is used during the customization phase of your application, i.e., to distinguish it from other applications. You need to set this here so that you're aware of what the token is later. However, this will not be very important to you (at all) in the greater part of your development process. The 'cluser' determines the name of the folder that will contain your custom modules, i.e., those that are in addition to the clusters of modules provided by the NetBeans Platform. Again, the cluster name is not very important and can be modified later anyway.

    The 'netbeans-install' field is very important. Later, when you run the application, the application will need to know the location of a NetBeans installation. Above, I have set "/home/geertjan/netbeans-6.1" because that is the top level folder of my NetBeans installation.

    Make sure that you press Enter for each of the fields, otherwise the entered text wont't "take" and the wizard will assume that the field is empty, causing problems later when you try to deploy it. There should be default values for all of the above fields; this is a known problem and will be fixed at some point.

  5. Click Finish and observe the two projects in the Projects window. The first is for the application and the second for a dummy module. You can use that module as your starting point and then add more as needed. Here's what you should see:

    There's one small yet very important thing that should be tweaked (again, a known problem). Open the profiles.xml file that you see above. In there, you'll see an element named "netbeans.installation.directory":

    Change that element name to "netbeans.installation". It is a known bug.

  6. Right-click the application and choose Build. It is very important that you do this, because the Run action won't do it for you, so that you'll have deployment problems in the next step if you haven't built the application first.

  7. Now right-click the application and choose Run. This will run the application and you'll see the splash screen of NetBeans IDE 6.1 and then your new application:

    Now, even though our first module doesn't do anything yet, we'll add a second module to our application. To do this, we will need to create our new module in the same folder as where the application is found. Look at step 4 above and notice that we will need to create our new module in "/home/geertjan/NetBeansProjects/DemoRCPApp". OK, let's do it via a new archetype in the next step.

  8. Press Ctrl-Shift-N again and choose Maven | Maven Project | Maven NetBeans Module Archetype:

    Click Next.

  9. Specify the location of the application, as pointed out above:

  10. Click Finish and now you can see that your application consists of two modules (in addition to the modules that the NetBeans Platform makes available):

Now refer to the two articles at the start of this blog entry which, together, should give you everything you need when working with Maven and the NetBeans Platform.

Wednesday May 21, 2008

Comment/Uncomment Lines in HTML/JSP/XML Files

I demoed a small plugin I was working on yesterday to Ludo—it lets you toggle the comment/uncomment tags in HTML, JSP and XML files, which is possible by default in Java source files (and in Properties files) but not in HTML files, JSP files, and XML files. At the time, the plugin only worked for single lines and only via a shortcut or a contextual menu item. However, Ludo told me that he uses the editor's toolbar and that he'd also like multiline selections to be possible. Well, Ludo, both items have been added and now the plugin is available in the Plugin Portal:

http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=9609

Make sure you read the open issues below the screenshots there, so that you're aware of them before you run into them by accident. In short, you're able to select multiple lines, as below, but if you don't, the plugin assumes that you're only interested in the current line, even if you haven't selected anything in the current line. Then you invoke the action, via Ctrl-Shift-C (just like in the Java editor and the Properties editor), or choose the contextual menu item, shown below, or Ludo's button in the editor's toolbar.

And then you find yourself with the comment tags around the selection (as illustrated below) which, when you select the selection again and then invoke the action again, are removed so that the selection is returned to its original state.

Note that the plugin installs this functionality for HTML files, XML files, and JSP files. I have tried it out on Ubuntu and a colleague tried it out on Windows XP. It worked as expected in both environments. Comments/uncomments on this plugin are more than welcome...

GlassFish v3 is Agnostic

Today Ludo showed me that GlassFish v3 is IDE-agnostic, since it uses Maven as its build system:

Above, you see ALL the sources that make up GlassFish v3. You can simply check out the Maven projects via Subversion and then open the main pom.xml into the IDE of your choice which, in the case of IntelliJ, would result in views like this, for one of the Maven projects:

Here's the link to the relevant SVN site for GlassFish v3 which, when you check them out will result in the Maven projects you see above:

https://glassfish-svn.dev.java.net/source/browse/glassfish-svn/trunk/v3/

Monday May 19, 2008

Maven: Opening New Doors for NetBeans IDE

Yesterday I started up NetBeans IDE 6.1, went to the Plugin Portal, and installed the Maven plugin:

I then went to the New Project wizard and selected Maven and then "Maven Project":

After clicking Next, I found the following project types:

Wow. I can create NetBeans modules in Maven? No set up or manual wackiness needed? Then I expanded "Archetypes from remote Maven Repositories". Wow, check it out:

...and check it out some more...

...and more...

...and more...

...and more...

...and more...

From some of the many cool-seeming archetypes, which I can now work with after simply clicking through this wizard, it would seem that one should be able to create OSGi bundles in NetBeans IDE. How interesting.

Sunday May 18, 2008

Customized Rendering of Class Members in the NetBeans Java Editor

Some small experiments with the Highlighting SPI, which I discussed yesterday, in response to Casper Bang's comments at the end of that blog entry:
I am curious Geertjan, would this be the API to use, if one wanted to implement a feature which could filter away the noise of non-essential code.

Say I wanted to be able to toggle a button up in the editor toolbar, which would cause all System.out.print/Logger statements as well as catch blocks to be rendered almost invisible (until my caret is on that line).

I often find myself using too few logging statements and uniting exception handling into one catch, just to avoid this "noise" which obscures what my code is actually trying to do.

In the first experiment, I have changed the background color of all methods in my class (but not the methods of inner classes), as can be seen here:

Notice that the constructor is not treated as a method. And here, the same class is shown, this time with the methods greyed out (i.e., I changed their foreground color, which is different to commenting out the code, because here we are only interested in rendering of the code, not in adding the // to the front of the lines):

Here is the AttributeSet for the above:

private static final AttributeSet defaultColors =
            AttributesUtilities.createImmutable(StyleConstants.Foreground,
            new Color(150, 150, 150));

The change (i.e., the new background color or foreground color) is performed when the caret changes (see the CaretListener in yesterday's blog entry), but could be done in other ways too, such as via the ModuleInstall class, for example, although that would affect performance at start up, so be careful.

The pieces that make all this possible are the aforementioned Highlighting SPI, together with the Java Sources API, and other supporting APIs which, in NetBeans terms, are collectively referred to as the "Retouche APIs".

Starting from the code presented in yesterday's blog entry, I only changed the scheduleUpdate() method, as follows:

public void scheduleUpdate() {
    if (task == null) {
        task = RequestProcessor.getDefault().create(new Runnable() {

            public void run() {
                Document doc = comp.getDocument();
                //Get the JavaSource object from the java.swing.text.Document,
                //which, in turn, is obtained from the JTextComponent that
                //represents the editor (see the constructor of
                //MarkOccurrencesHighlighter class in yesterday's blog entry):
                JavaSource javaSource = JavaSource.forDocument(doc);
                try {
                    javaSource.runUserActionTask(new Task<CompilationController>() {

                        public void run(CompilationController compilationController) throws Exception {
                            compilationController.toPhase(Phase.ELEMENTS_RESOLVED);
                            Document document = compilationController.getDocument();
                            if (document != null) {
                                new MemberVisitor(compilationController).scan(compilationController.getCompilationUnit(), null);
                            }
                        }
                    }, true);
                } catch (IOException ex) {
                    Exceptions.printStackTrace(ex);
                }
            }
        }, true);
        task.setPriority(Thread.MIN_PRIORITY);
    }
    task.cancel();
    task.schedule(DELAY);
}

Here's the MemberVisitor:

private static class MemberVisitor extends TreePathScanner {

    private CompilationInfo info;

    public MemberVisitor(CompilationInfo info) {
        this.info = info;
    }

    @Override
    public Void visitClass(ClassTree t, Void v) {
        Element el = info.getTrees().getElement(getCurrentPath());
        TypeElement te = (TypeElement) el;
        List enclosedElements = te.getEnclosedElements();
        for (int i = 0; i < enclosedElements.size(); i++) {
            Element encEl = (Element) enclosedElements.get(i);
            Trees trees = info.getTrees();
            CompilationUnitTree cUnit = info.getCompilationUnit();
            if (encEl.getKind() == ElementKind.METHOD) {
                //Get a new Tree, specifically for our found elements:
                Tree tree = trees.getTree(encEl);
                int startPosition = (int) info.getTrees().getSourcePositions().getStartPosition(cUnit, tree);
                int endPosition = (int) info.getTrees().getSourcePositions().getEndPosition(cUnit, tree);
                //Add the start position and end position,
                //together with colors, to the Highlighting SPI's OffsetsBag:
                bag.addHighlight(startPosition, endPosition, defaultColors);
            }
        }
        return null;
    }
}

So, those are my first small steps in using the Highlighting SPI to render the class members in the Java editor. Other experiments will follow, but this should give an idea of what is needed if you want to explore this part of the NetBeans APIs. "Simply" identify the elements you'd like to handle differently and then add them to the Highlighting SPI's highlighting bag.

Essential reading for programmatically customizing the Java editor in NetBeans IDE:

Saturday May 17, 2008

How Does "Mark Occurrences" Work?

Yesterday's code generator was made possible by Editor Library 2: "The NetBeans editor infrastructure provides an implementation of the Swing Text package APIs as well as some additional features such as syntax coloring, code folding, braces matching, etc."

The Code Generator SPI is in that module, but so is the Highlighting SPI. There's several usecases that relate to this SPI, as you can read in that link. Yesterday I came across a very cool tutorial that describes one of them: Extending the C/C++ Editor in NetBeans IDE 6.0 to Provide Mark Occurrences Highlighting. That's an excellent tutorial by Sergey Grinev from the NetBeans C++ team in St. Petersburg. Let's get to know this SPI while doing something pretty useful: we'll create a mark occurrences plugin for HTML files on the NetBeans Platform (i.e., typically, in NetBeans IDE, but also applicable to any other HTML file open in an application on the NetBeans Platform):

Above, I selected one instance of "you" and then all matching instances were highlighted. Try it. Doesn't work, does it? That's because this functionality is not part of the IDE, but part of my plugin. If you're interested in this functionality as a user, get it here from the Plugin Portal:

http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=9441

To create the above, I simply copied Sergey's code, except that I read the content of HTML files rather than C++ (or C) files and then changed the layer.xml accordingly. My MarkOccurrencesHighlightsFactory is identical to Sergey's:

package org.netbeans.modules.markoccurrences;

import javax.swing.text.Document;
import org.netbeans.spi.editor.highlighting.HighlightsLayer;
import org.netbeans.spi.editor.highlighting.HighlightsLayerFactory;
import org.netbeans.spi.editor.highlighting.ZOrder;

public class MarkOccurrencesHighlightsLayerFactory implements HighlightsLayerFactory {

    public static MarkOccurrencesHighlighter getMarkOccurrencesHighlighter(Document doc) {
        MarkOccurrencesHighlighter highlighter = (MarkOccurrencesHighlighter) doc.getProperty(MarkOccurrencesHighlighter.class);
        if (highlighter == null) {
            doc.putProperty(MarkOccurrencesHighlighter.class, highlighter = new MarkOccurrencesHighlighter(doc));
        }
        return highlighter;
    }

    @Override
    public HighlightsLayer[] createLayers(Context context) {
        return new HighlightsLayer[]{
                    HighlightsLayer.create(
                    MarkOccurrencesHighlighter.class.getName(),
                    ZOrder.CARET_RACK.forPosition(2000),
                    true,
                    getMarkOccurrencesHighlighter(context.getDocument()).getHighlightsBag())
                };
    }

}

Here's the layer.xml registration:

<folder name="Editors">
    <folder name="text">
        <folder name="html">
            <file name="org-netbeans-modules-markoccurrences-MarkOccurrencesHighlightsLayerFactory.instance"/>
        </folder>
    </folder>
</folder>

And, finally, the CaretListener, which picks up whatever is selected and then finds matching occurrences:

package org.netbeans.modules.markoccurrences;

import java.awt.Color;
import java.lang.ref.WeakReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JEditorPane;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.AttributeSet;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import javax.swing.text.StyleConstants;
import org.netbeans.api.editor.settings.AttributesUtilities;
import org.netbeans.modules.editor.NbEditorUtilities;
import org.netbeans.spi.editor.highlighting.support.OffsetsBag;
import org.openide.cookies.EditorCookie;
import org.openide.loaders.DataObject;
import org.openide.util.RequestProcessor;

public class MarkOccurrencesHighlighter implements CaretListener {

    private static final AttributeSet defaultColors =
            AttributesUtilities.createImmutable(StyleConstants.Background,
            new Color(236, 235, 163));
    private final OffsetsBag bag;
    private JTextComponent comp;
    private final WeakReference weakDoc;

    public MarkOccurrencesHighlighter(Document doc) {
        bag = new OffsetsBag(doc);
        weakDoc = new WeakReference((Document) doc);
        DataObject dobj = NbEditorUtilities.getDataObject(weakDoc.get());
        EditorCookie pane = dobj.getCookie(EditorCookie.class);
        JEditorPane[] panes = pane.getOpenedPanes();
        if (panes != null && panes.length > 0) {
            comp = panes[0];
            comp.addCaretListener(this);
        }
    }

    @Override
    public void caretUpdate(CaretEvent e) {
        bag.clear();
        scheduleUpdate();
    }
    private RequestProcessor.Task task = null;
    private final static int DELAY = 100;

    public void scheduleUpdate() {
        if (task == null) {
            task = RequestProcessor.getDefault().create(new Runnable() {

                public void run() {
                    String selection = comp.getSelectedText();
                    if (selection != null) {
                        Pattern p = Pattern.compile(selection);
                        Matcher m = p.matcher(comp.getText());
                        while (m.find() == true) {
                            int startOffset = m.start();
                            int endOffset = m.end();
                            bag.addHighlight(startOffset, endOffset, defaultColors);
                        }
                    }
                }
            }, true);
            task.setPriority(Thread.MIN_PRIORITY);
        }
        task.cancel();
        task.schedule(DELAY);
    }

    public OffsetsBag getHighlightsBag() {
        return bag;
    }
}

The only difference from Sergey's tutorial is the bit in bold above. Nothing magical, not using any NetBeans APIs, except that whenever a matching pattern is found, it is added to the "bag" (hello, lookup, I knew you well), and highlighted. The only difference between my implementation of "mark occurrences" and the typical implementation is that in my case one actually needs to select something, rather than simply putting the caret in the middle of some word. But that's fine, I think this does the job pretty well. Not only is this a nice introduction to this SPI, but now there is something I've been missing for a while—mark occurrences for HTML files. I've written to Vladimir Voskresensky to ask him what he did to make navigation between marked occurrences possible (as described here) because that would be very cool to add to this plugin. It works pretty well together with Ctrl-F (i.e., search), so that you can search for a word while using mark occurrences for another word (so, you have a double-edged search, as in Java files), as you can see here (orange is from Ctrl-F, while yellow is from mark occurrences):

I'm looking forward to exploring the Highlighting SPI in other contexts too, now that I have this example as an entry point.

Code Generator SPI Officially Available

Sometime ago this blog provided a preliminary example of a code generator. However, at the time, all that info wasn't official yet and you had to set an implementation dependency on one of the APIs. In the meantime, if you look at the NetBeans API Changes since Last Release document (which you should keep tabs on religiously, if you're a NetBeans Platform developer), you'll see this notification: "Code Generation SPI added":

The Code Generation SPI consists of two interfaces. The CodeGenerator implementations registered for various mime types serve for creating code snippets and inserting them into documents on the Insert Code editor action invocation. The CodeGeneratorContextProvider implementations registered for the mime types could provide the respective CodeGenerators with an additional context information.

Fine. What does all that mean? Firstly, interestingly, it means that the CodeGenerator class (and its supporting classes) are now officially supported and are exposed to the NetBeans API Javadoc. So, read the description to get an overview of it all.

Secondly, guess what? You can create code generators for any MIME type you want. So, you could add one/more to your HTML files... such as here:

So, the above appears when I press Alt-Insert in an HTML source file. What happens when I select the code generator item is up to me (i.e., the implementor of the API). In the case of Java source files, you can make use of the rather cool (though very cryptic) Retouche APIs. Let this document be your friend. I have found it pretty tough going, but gradually things become clear. Below, step by step, is my first implementation of a code generator. In the process, you'll see the Retouche APIs in action.

  1. Get a very recent post 6.1 development build and create a new NetBeans module.

  2. Set dependencies on Editor Library 2, Javac API Wrapper, Java Source, and Utilities API.

  3. Let's start very gently:

    import java.util.Collections;
    import java.util.List;
    import org.netbeans.spi.editor.codegen.CodeGenerator;
    import org.openide.util.Lookup;
    
    public class HelloGenerator implements CodeGenerator {
    
        public static class Factory implements CodeGenerator.Factory {
    
            @Override
            public List create(Lookup context) {
                return Collections.singletonList(new HelloGenerator());
            }
        }
    
        @Override
        public String getDisplayName() {
            return "Hello world!";
        }
    
        @Override
        public void invoke() {
        }
        
    }

  4. Register it in the layer.xml file like this, in other words, register the Factory that you see in the Java code above:

    <folder name="Editors">
        <folder name="text">
            <folder name="x-java">
                <folder name="CodeGenerators">
                    <file name="org-netbeans-modules-my-demo-HelloGenerator$Factory.instance"/>
                </folder>
            </folder>
        </folder>
    </folder>

  5. Now you're good to go. Just install the module and invoke the code generators as always, Alt-Insert, and then you'll see the new one added:

  6. OK, now we'll do something useful. We'll start by getting the JTextComponent from the Lookup. That JTextComponent is the Java editor and if we use getText, we can get the text of the Java editor. We can also get the Document object, which is all that we need in order to get the JavaSource object, via JavaSource javaSource = JavaSource.forDocument(doc);, which, in turn, is our entry point into the Retouche APIs. So, here we go:

    public class HelloGenerator implements CodeGenerator {
    
        private JTextComponent textComp;
    
        private HelloGenerator(JTextComponent textComp) {
            this.textComp = textComp;
        }
    
        public static class Factory implements CodeGenerator.Factory {
    
            public List<? extends CodeGenerator> create(Lookup context) {
                Item<JTextComponent> textCompItem = context.lookupItem(new Template(JTextComponent.class, null, null));
                JTextComponent textComp = textCompItem.getInstance();
                return Collections.singletonList(new HelloGenerator(textComp));
            }
        }
    
        @Override
        public String getDisplayName() {
            return "Hello world!";
        }
    
        @Override
        public void invoke() {
        }
        
    }

    I don't know whether the above is the optimal way of doing this, but at the end of the day we now have a JTextComponent. In the next part, I've simply taken code from the Java Developer Guide, referred to earlier, and implemented the invoke exactly as described there, so see that document in order to understand the code below:

  7. Define the invoke method like this, which as stated above is completely taken from the Java Developer Guide:

    @Override
    public void invoke() {
        try {
            Document doc = textComp.getDocument();
            JavaSource javaSource = JavaSource.forDocument(doc);
            CancellableTask task = new CancellableTask<WorkingCopy>() {
                @Override
                public void run(WorkingCopy workingCopy) throws IOException {
                    workingCopy.toPhase(Phase.RESOLVED);
                    CompilationUnitTree cut = workingCopy.getCompilationUnit();
                    TreeMaker make = workingCopy.getTreeMaker();
                    for (Tree typeDecl : cut.getTypeDecls()) {
                        if (Tree.Kind.CLASS == typeDecl.getKind()) {
                            ClassTree clazz = (ClassTree) typeDecl;
                            ModifiersTree methodModifiers = make.Modifiers(Collections.<Modifier>singleton(Modifier.PUBLIC), Collections.<AnnotationTree>emptyList());
                            VariableTree parameter = make.Variable(make.Modifiers(Collections.<Modifier>singleton(Modifier.FINAL), Collections.<AnnotationTree>emptyList()), "arg0", make.Identifier("Object"), null);
                            TypeElement element = workingCopy.getElements().getTypeElement("java.io.IOException");
                            ExpressionTree throwsClause = make.QualIdent(element);
                            MethodTree newMethod = make.Method(methodModifiers, "writeExternal", make.PrimitiveType(TypeKind.VOID), Collections.<TypeParameterTree>emptyList(), Collections.singletonList(parameter), Collections.<ExpressionTree>singletonList(throwsClause), "{ throw new UnsupportedOperationException(\\"Not supported yet.\\") }", null);
                            ClassTree modifiedClazz = make.addClassMember(clazz, newMethod);
                            workingCopy.rewrite(clazz, modifiedClazz);
                        }
                    }
                }
                @Override
                public void cancel() {}
            };
            ModificationResult result = javaSource.runModificationTask(task);
            result.commit();
        } catch (Exception ex) {
            Exceptions.printStackTrace(ex);
        }
    
    }

  8. Excellent. At this point, check that you have this impressive list of import statements:

    import com.sun.source.tree.AnnotationTree;
    import com.sun.source.tree.ClassTree;
    import com.sun.source.tree.CompilationUnitTree;
    import com.sun.source.tree.ExpressionTree;
    import com.sun.source.tree.MethodTree;
    import com.sun.source.tree.ModifiersTree;
    import com.sun.source.tree.Tree;
    import com.sun.source.tree.TypeParameterTree;
    import com.sun.source.tree.VariableTree;
    import java.io.IOException;
    import java.util.Collections;
    import java.util.List;
    import javax.lang.model.element.Modifier;
    import javax.lang.model.element.TypeElement;
    import javax.lang.model.type.TypeKind;
    import javax.swing.text.Document;
    import javax.swing.text.JTextComponent;
    import org.netbeans.api.java.source.CancellableTask;
    import org.netbeans.api.java.source.JavaSource;
    import org.netbeans.api.java.source.JavaSource.Phase;
    import org.netbeans.api.java.source.ModificationResult;
    import org.netbeans.api.java.source.TreeMaker;
    import org.netbeans.api.java.source.WorkingCopy;
    import org.netbeans.spi.editor.codegen.CodeGenerator;
    import org.openide.util.Exceptions;
    import org.openide.util.Lookup;
    import org.openide.util.Lookup.Item;
    import org.openide.util.Lookup.Template;

  9. Now install the module again. Then open a Java source file. Let's say it looks like this:

    package org.netbeans.modules.my.demo;
    
    public class NewClass {
    
    }

    Press Alt-Insert anywhere in the source file, choose "Hello world!", and now you will see this instead of the code above:

    package org.netbeans.modules.my.demo;
    
    import java.io.IOException;
    
    public class NewClass {
    
        public void writeExternal(final Object arg0) throws IOException {
            throw new UnsupportedOperationException("Not supported yet.");
        }
    
    }

Hurray. You have your first code generator. In an HTML source file, the above invoke method could be as simple as this, which would print <h2>hello</h2> at the caret:

@Override
public void invoke() {
    try {
        Caret caret = textComp.getCaret();
        int dot = caret.getDot();
        textComp.getDocument().insertString(dot, "<h2>hello</h2>", null);
    } catch (BadLocationException ex) {
        Exceptions.printStackTrace(ex);
    }

}

The very cool thing about these code generators is that you can keep typing, i.e., your fingers don't leave the keyboard in order to go to a menu item via the mouse, etc. You're typing as normal, then you press Alt-Insert, you select something, and then you go on typing. Being able to make your own contributions to the list of Java code generators (plus, being able to make them for other MIME types) is a really powerful enhancement to NetBeans IDE.

Thursday May 15, 2008

Drag/Drop Snippets into Palette

Changes to the NetBeans APIs since the release of 6.1 are continually updated here. So, if you're interested in the NetBeans APIs, bookmark that page and look at it every week or so to see the latest changes. (By the way, the list of NetBeans API changes in 6.1 are found here.)

One of the post-6.1 changes (available since about a week in development builds) is this one: "Allowing user dropping text into the palette to create new custom code clips." (Read about it here.) The author of the API, Stan Aubrecht, who also added this latest enhancement, described it in his blog yesterday.

In summary, this enhancement means two different things:

  • NetBeans users. If you are a user of NetBeans IDE, you are able, since last week's post-6.1 development builds, to drag and drop HTML snippets from the editor into the palette, to create a new custom code snippet. If you hold down the Ctrl key while you drag the snippet into the palette, the snippet will be copied from the editor to the palette; if you don't hold down the Ctrl key, the snippet will be cut from the editor and pasted into the palette. Click the image below to enlarge it, it shows you what you'll see when you drop the snippet into the palette, whether copied or cut:

    You need to drag the snippet onto an existing item or a category in the palette. Currently this works for HTML files, while other existing palettes will follow.

  • NetBeans developers. This is even more interesting (in my opinion). If you provide a palette (i.e., you are creating a module that adds a palette to NetBeans IDE or another NetBeans Platform application that has an editor and a palette), you can enable the palette to provide the same functionality as the above. Let's go through the steps that make this possible. Take the following steps:
    1. Download the Java Source File Palette Sample from the Plugin Portal.

    2. Install the module and open the sample from the New Project wizard's Samples | NetBeans Modules category.

    3. Tweak the class called JavaSourceFileLayerPaletteFactory. Everything that is in bold below is what you need to add, i.e., everything else is from the class you find in the sample:
      public class JavaSourceFileLayerPaletteFactory {
      
          public static final String JAVA_PALETTE_FOLDER = "JavaPalette";
          private static PaletteController palette = null;
      
          public JavaSourceFileLayerPaletteFactory() {
          }
      
          public static PaletteController createPalette() {
              try {
                  if (null == palette) {
                      //Add null for the filter, which is unused in this sample,
                      //but needs to be set if the DragAndDropHandler will be used:
                      palette = PaletteFactory.createPalette(JAVA_PALETTE_FOLDER, 
                         new MyActions(), null, new MyDragAndDropHandler());
                  }
                  return palette;
              } catch (IOException ex) {
                  Exceptions.printStackTrace(ex);
              }
              return null;
          }
      
          private static class MyDragAndDropHandler extends DragAndDropHandler {
      
              MyDragAndDropHandler() {
                  super(true);
              }
      
              //Maybe you don't like the default 'add to palette' implementation,
              //so you could create your own here:
              @Override
              public void customize(ExTransferable t, Lookup item) {
              }
      
          }
      
          private static class MyActions extends PaletteActions {
      
              //Add new buttons to the Palette Manager here:
              @Override
              public Action[] getImportActions() {
                  return null;
              }
      
              //Add new contextual menu items to the palette here:
              @Override
              public Action[] getCustomPaletteActions() {
                  return null;
              }
              
              //Add new contextual menu items to the categories here:
              @Override
              public Action[] getCustomCategoryActions(Lookup arg0) {
                  return null;
              }
              
              //Add new contextual menu items to the items here:
              @Override
              public Action[] getCustomItemActions(Lookup arg0) {
                  return null;
              }
              
              //Define the default action here:
              @Override
              public Action getPreferredAction(Lookup arg0) {
                  return null;
              }
      
          }
      
      }

      What this means is that you need to provide (at least) a default implementation of org.netbeans.spi.palette.DragAndDropHandler, as done above.

    4. Install the module. Now you can drag and drop code from the editor to which the palette applies (i.e., the Java editor, in the case of the sample above) and then automatically the "Add to Palette" dialog will appear, exactly as done for HTML files in the case of the NetBeans user scenario above.

I think this is an extremely cool enhancement and we'll see it implemented in the other palettes in the IDE too. But it's especially cool that it's possible (and so easy) to implement in your custom palettes too. Of all the NetBeans APIs, the Palette API is one of the ones that has seen the most cool enhancements over the past two years, in my opinion. Are there other things that could make it even better?

Zipping A Suite's NBM Files for Distribution

One of the many nice people I met at JavaOne was Mark Stephens, from IDR Solutions. Mark and a colleague did a very interesting BOF about how they created their JPedal viewer plugin for each of the main IDEs (i.e., IntelliJ, NetBeans IDE, and Eclipse). He highlighted various things that were cool and not so cool about creating plugins for each of the IDEs. One of the things he told me was that the "Build ZIP Distribution" menu item on the project node of module suite projects is confusing. He had expected to be able to choose that menu item and then get a ZIP file containing all the NBMs in the suite. Instead, the menu item resulted in an application being created, i.e., including a launcher and a bin folder and so on.

However, Mark's usecase was different: his suite contained a number of different modules which he wanted to upload to the Plugin Portal. However, one can only upload one file to a new Plugin Portal entry. Either one uploads one NBM file or one ZIP file. Mark, having multiple NBM files in his plugin, thus chose "Build ZIP Distribution" on the suite project and was disappointed/confused to discover that he was then prompted to make the suite an application after which he still didn't get his NBMs zipped up into a suite. As pointed out above, the purpose of that menu item is different to what Mark had expected. Therefore, I created a plugin today that provides this functionality:

Get it here:

http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=9296

I think everything is explained on that page. The menu item is only shown for module suite projects. The ZIP file is created via an Ant script that is created in the tmp directory and then removed after the target has completed. The Ant target simply runs the "nbms" target, which every module suite project has, thus creating all the NBM files into the build/updates folder and then picks up all the NBM files in that folder and ZIPs them up. (The only problem is that I can't put the menu item higher in the contextual menu, because ideally it would appear right beneath the existing "Build ZIP Distribution" item, but currently that's not possible.) If you are a NetBeans module developer and are interested in how all this is done, click here and you will get the Action's source file. (Click here to see the related layer file.) Comments/critiques/feedback on this functionality is/are welcome.

Today on NetBeans Zone. Two NetBeans Keyboard Shortcuts That Will Change Your Life (Forever)

Wednesday May 14, 2008

Book Signing at JavaOne

At JavaOne, the authors of "Rich Client Programming: Plugging into the NetBeans Platform" (Jaroslav Tulach, Tim Boudreau, and myself) did a book signing. Here we all are, being authors at the book signing:

The queue of people wanting their copy of the book signed seemed very long...

...until we realized that they were ALL waiting for the next author's book signing. They'd come early because they knew the next author's queue would be very long. The next author was Josh Bloch, to sign the latest "Effective Java". And he was delayed because he was being mobbed on his way to his book signing. By people wanting him to sign their copy of his book...

Tuesday May 13, 2008

Identifying the PNG Files in the JDK

Let's identify all the PNG files in the JDK's Boot ClassPath. Create a new NetBeans module with an action that has a performAction defined as folows:

OutputWriter writer;

@Override
public void performAction() {
    InputOutput io = IOProvider.getDefault().getIO("PNG Images", false);
    try {
        writer = io.getOut();
        io.select();
        io.getOut().reset();
        Set cps = GlobalPathRegistry.getDefault().getPaths(ClassPath.BOOT);
        for (ClassPath cp : cps) {
            List entries = cp.entries();
            for (ClassPath.Entry entry : entries) {
                ClassPath entryCp = entry.getDefiningClassPath();
                FileObject[] fos = entryCp.getRoots();
                for (FileObject fo : fos) {
                    cycleThroughKids(fo);
                }
            }
        }
    } catch (IOException ex) {
        Exceptions.printStackTrace(ex);
    }
}

private void cycleThroughKids(final FileObject fo) {
    final Thread thread = new Thread() {

        @Override
        public void run() {
            if (fo.getChildren().length > 0) {
                for (FileObject oneKid : fo.getChildren()) {
                    if (oneKid.getMIMEType().equals("image/png")) { {
                        try {
                            writer.println(oneKid.getPath(), new PngOutPutListener(oneKid));
                        } catch (IOException ex) {
                            Exceptions.printStackTrace(ex);
                        }
                    }
                    cycleThroughKids(oneKid);
                }
            }
        }
    };
    thread.start();
}

The line in bold above has as its final argument the creation of a NetBeans API OutputListener class, which magically returns a hyperlink for each line added to the Output window, as defined below:

class PngOutPutListener implements OutputListener {

    FileObject oneKid;

    PngOutPutListener(FileObject oneKid) {
        this.oneKid = oneKid;
    }

    @Override
    public void outputLineAction(OutputEvent arg0) {
        try {
            DataObject dObj = DataObject.find(oneKid);
            OpenCookie open = (OpenCookie) dObj.getCookie(OpenCookie.class);
            if (open != null) {
                open.open();
            }
        } catch (DataObjectNotFoundException ex) {
            Exceptions.printStackTrace(ex);
        }
    }

    @Override
    public void outputLineSelected(OutputEvent arg0) {}

    @Override
    public void outputLineCleared(OutputEvent arg0) {}
    
}

When a link is clicked, which is an event that a hyperlink implies should be performed, the file is opened in the editor. This is how the hyperlinks look in the Output window:

Upon being clicked, the related PNG file opens in the editor. Not useful in any way, though.

Monday May 12, 2008

JFrets/JFugue Mashup

A first draft of three JFrets modules installed into the JFugue Music Application Platform:

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
« May 2008 »
SunMonTueWedThuFriSat
    
2
5
7
16
20
22
25
28
       
Today