Extreme Java When vanilla Java is not enough

11Oct/100

From Swing to NetBeans Platform (part 3)

In my previous posts, I put an old Swing application to run as a NetBeans Platform Application and start converting JDialogs to TopComponent sensitive to global selection. Now, it's time to remove a panel from the main editor.

The old MapEditor is a "demigod class" that is the centre of all logic of the editor (I guess that's why they abandoned the project). Everything is there: layer management, tileset choosing, among others. It's over 2000 lines!

The first feature I will refactor is the layer management. Just like the Tileset Manager, I created a TopComponent sensitive to MapEditor selection (available from the main editor). The difference is I'm using the NB's Outline view to reproduce the old table layer style, and the layer node implements CheckableNode's contract (with "checked" meaning "visible").

Another interesting behaviour is the toolbar below the layer table. I used a JToolBar with this recipe to add its buttons:

for (Object obj : Lookups.forPath("...")
    .lookupAll(Object.class)) {
  if (obj instanceof JSeparator) {
    tbrBottom.addSeparator();
  }
  if (obj instanceof Action) {
    tbrBottom.add((Action) obj);
  }
}

Using this snippet with NB's actions registered on layer.xml, it was easy to make an extensible toolbar with buttons synchronized to the Outline's selection.

As a last tip, I associated an InstanceContent to the layer TopComponent's lookup. This IC adds the global selection (i.e. the map shown in the Outline view) to the TC lookup. The resultChanged method is:

Collection<? extends TiledMap> c = result.allInstances();
if (!c.isEmpty()) {
  TiledMap map = c.iterator().next();
  ic.add(map);
  em.setRootContext(new RootNode(map));
} else {
  ic.set(Collections.EMPTY_LIST, null);
  em.setRootContext(Node.EMPTY);
}

Using this snippet retains the global selection when user put the focus on layer TC. Without this workaround, it loses the selection, like this example.

Filed under: NetBeans No Comments
27Sep/100

From Swing to NetBeans Platform (part 2)

In my previous post, I put an old Swing application to run as a NetBeans Platform Application. After some refactoring, I got it working, nicely integrated to NB's file editing support. Now, I'm converting their Tileset Manager from a JDialog to a TopComponent sensitive to global selection.

I started with a new "TileSet Manager" module, then moved the TSM dialog to that module and changed it to a JPanel. After adding a TC and putting the TSM Panel on it, half-work was done. Thanks to the Swing skills of Tiled team, the next step was a piece of cake. After tracking the global selection, I only made the right "setEditor" on their manager and voilá! The tileset table changes according to the selected file! God bless the NetBeans Team!

It's already very cool, but I want more. Their version uses JTables and lots of buttons on this dialog. What about using NBP's ExplorerManager? Easy as 1-2-3. I replaced the TSM Panel with a BeanTreeView plus some Nodes and the ExplorerManager (just like FeedReader tutorial). I could use OutlineView to keep the table look, but I plan to add some depth on this tree later. And, instead of a table with two columns, I use a nice tree view with HTML display name:

Migration of actions started by the most complex one: Delete. Adding the action to the ExplorerManager and their JButton's actionPerformed to Node.canDestroy and Node.destroy did the trick.

There were other actions, like "Save As" and "Embed". All of them were easy to convert. I cut the code on "actionPerformed", pasting it on new actions. I put them on NB layer and used this snippet to convert them to Node.getActions:

@Override
public Action[] getActions(boolean context) {
  List<Action> list = new ArrayList<Action>();
  for (Object obj : Lookups.forPath("...")
      .lookupAll(Object.class)) {
    if (obj instanceof JSeparator) {
      list.add(null);
    }
    if (obj instanceof Action) {
      list.add((Action) obj);
    }
  }
  return list.toArray(new Action[list.size()]);
}
23Sep/100

From Swing to NetBeans Platform (part 1)

I don't remember how, but I found a nice tile map editor named Tiled. It has two versions: QT and Java. The former is their "trunk", while the latter is their "legacy". Too bad, it was a nice piece of software.

Nonetheless, their screenshot made me think: "this MUST be refactored to be a NetBeans Platform App". There's a lot of dialogs, splitpanes, toolbars, etc. It is an waste of time doing that on Swing.

Let's see the situation of the source code:

  • Almost abandoned - only a couple of commits this year. Check.
  • No plans to keep it up-to-date - blame the QT version. Check.
  • GPL licensed. Check.

That's the recipe that allows me to play with this cool application, refactoring it from Swing to NBP.

I started by cloning their Mercurial repository (cool, I can keep their SCM history). Then, I run it with NetBeans. Now that I know it works, I deleted the project metadata (build.xml and nbproject) and created a new NBP-based Application. Their old source on "src" I moved to a new "src" inside a "Legacy Integration" module.

Now, the fun part: remove the junk. There's a lot of cool features on the original project, but almost all of them NBP does too:

  • Plugin support, with custom classloader and other stuff. Deleted
  • Multiple filetype support. Imploded to support only native formats
  • Command line parsing. Split in a new module, using NB own support

Up to this point I have: nothing! After all, that application was SDI with JFrame. To make the editor working again, I created a "TMX File Support". The first version of TMXDataObject contained only an OpenCookie that opens a TopComponent. In this TC, I embedded the Tiled JFrame components (editor, menubar, statusbar, etc). This ugly workaround worked pretty nice! And the Application is now MDI!

After some cleanup, it was pretty easy to migrate their NewMapAction to NB layer.xml, the SaveAction to a SaveCookie and SaveAsAction to SaveAsCapable. In a matter of minutes, their editor was fully integrated with NB file support, including the conditionally enabled "Save" button.

Next step is the Tileset Manager. Currently it is a JDialog, and I will migrate it to a TopComponent (like Navigator).

Stay tuned!

16Jul/100

NB-managed JDBC connections

If you are developing a NetBeans-based module or application and need a database connection, you can use "Database Explorer API" to manage them for you. Geertjan has the recipe, but there are two catchs:

  1. DatabaseConnection.getJDBCConnection(true) must NOT be called on EDT;
  2. ConnectionManager.showConnectionDialog MUST be called on EDT.

So, if you want to do something like this:

DatabaseConnection dbc = ConnectionManager
   .getDefault()
   .getConnection(databaseConnectionName);
ConnectionManager.getDefault()
   .showConnectionDialog(dbc);
Connection conn = dbc.getJDBCConnection(true);

You must call line 4 on EDT and line 6 on a RequestProcessor or similar. I had to deal with these scenarios:

  1. Populate a JComboBox on a button's click event: I created a RP with the sixth line plus statements and result sets - first and forth line are called by Swing on EDT;
  2. Populate an asynchronous ChildFactory: my code is called on CF's refresh thread. I had to encapsulate showConnectionFactory on EDT using SwingUtilities.invokeAndWait, without using RP for line 6.

It's strange at first, but works nice (way better than developing my own database connection management).

BTW, if you call DatabaseConnection.getJDBCConnection on EDT you receive an useful warning, but you will suffer if you forget to call ConnectionManager.showConnectionDialog on EDT (no cool warnings to help you). I filled a request for adding this warning in bug 188741.

Tagged as: , No Comments
30May/100

Using NetBeans Parser API with Navigator API

So, you created a new language on you NBP-based application and associated it with a new file type. Now, you need to show your parse result on your Navigator panel. I haven't found a standard way to do so, but the best shot so far was adding the parser result to the DataObject's CookieSet. This way you can lookup it in your NavigationPanel's panelActivated. Easy and fast.

7May/102

Using NewType on a NetBeans Node

Given any Node on a NetBeans Platform Application, you can create a "new action" by adding "SystemAction.get(NewAction.class)" to your node's actions (overriding Node.getAction). You must also override the Node.getNewTypes.

Everything is simple and obvious - except one detail: be careful when overriding the node's lookup. This works:

public class MyNode extends AbstractNode {
  public MyNode() {
    super(Children.LEAF);
  }
}

But this don't (you will only get a grayed "Add" menu):

public class MyNode extends AbstractNode {
  public MyNode(MyElement a) {
    super(Children.LEAF, Lookups.fixed(a));
  }
}

The answer is not obvious (I took hours to find out) - you must add "this" to the node lookup, using the old InstanceContent recipe:

public class MyNode extends AbstractNode {
  public MyNode(MyElement a) {
    this(a, new InstanceContent());
  }
  public MyNode(MyElement a, InstanceContent ic) {
    super(Children.LEAF, new AbstractLookup(ic));
    ic.add(this);
    ic.add(a);
  }
}
Tagged as: , , 2 Comments
15Dec/090

File attributes on layer.xml

When registering files on NBM's layer file, you can define attributes, too:

<file name="my-pkg-MyClass.instance">
  <attr name="mystr" stringvalue="value"/>
  <attr name="myint" intvalue="1"/>
  <attr name="mymethod"
    methodvalue="my.pkg.MyClass.myMethod"/>
</file>

They are avaliable as attributes of FileObject (through getAttribute). You can also use special attributes "instanceClass" to define a different class to instantiate or "instanceCreate" to define a factory method. This factory method can receive a map of attributes. I'm using it to customize a generic DataLoader:

public class MyLoader extends MultiDataLoader {
  private MyDataLoader(String secondaryExt) {
    super("MyObject");
    this.secondaryExt = secondaryExt;
  }
  public static build(Map<String, Object> attrs) {
    return new MyLoader(attrs.get("ext").toString());
  }
  ...
}
30Jun/090

NetBeans 6.7

Two days ago, NetBeans 6.7 was released. These are the new features I tested:

  • Maven integration was improved with autocomplete and dependency graph;
  • Profiler's HeapWalker supports OQL - very useful if you do memory tunning;
  • Hudson server management and a status bar icon with build status.

See more at release page!

27Mar/090

NetBeans Platform + JPA + Derby embedded

If you want to use NetBeans Platform and JPA together, there's a great tutorial on NB's site. Unfortunatelly, it explains how to do it with an external database and using an external JAR for your entities.

If you want to have a entity module with your classes (instead of an external JAR and a library wrapper), no big deal - it works! You can follow GJ's tutorial, but you will create a module project instead and you will not have NB's wizards to help creating entity classes.

I also created a module install on my Derby wrapper, with this line:

FileUtil configRoot = FileUtil.getConfigRoot();
System.setProperty("derby.system.home",
    FileUtil.toFile(configRoot).getCanonicalPath());

This defines where Derby will create database files (user's dir, in this case).

BTW, if a "no suitable driver found" is thrown, you forgot to add a dependency between JPA wrapper (EclipseLink, TopLink, OpenJPA or Hibernate) and JDBC wrapper.

19Nov/080

NetBeans 6.5

Finally, NB 6.5 was released! Grab your copy!

Unfortunatelly the download link is blocked here... I hate proxies...

Tagged as: No Comments