The Art of the Visitor Pattern (3)

In the previous posts in this series I looked at the basic Visitor design pattern and a couple of variations, like eliminating recursion to make the visitor interruptable. As promised, this time I will make our visitor re-entrant.

Here’s what we have so far:

public class BaseFileSystemVisitor
    implements FileSystemVisitor {

  public FileSystemNode visit(final FileSystemNode node) {
    int numNodesVisited = 0;
    FileSystemNode current = node;

   while (current != null && numNodesVisited < maxNodes) {
      numNodesVisited++;
      stack.push(current);
      if (current.start(this)) {
        current = current.getNext();
      } else {
        current = current.getNextNoChild();
      }
      while (!stack.isEmpty()
          && !stack.peek().isAncestorOf(current)) {
        stack.pop().stop(this);
      }
    }
    return current;
  }

First of all, note there is a bug in here: when start returns false, the stop method is called anyway! This is because the folder is still on the stack and therefore will be stopped when it is popped from the stack. Given this analysis, the fix is easy:

  public FileSystemNode visit(final FileSystemNode node) {
    int numNodesVisited = 0;
    FileSystemNode current = node;

    while (current != null && numNodesVisited < maxNodes) {
      numNodesVisited++;
      stack.push(current);
      if (current.start(this)) {
        current = current.getNext();
      } else {
        current = current.getNextNoChild();
        stack.pop();
      }
      while (!stack.isEmpty()
          && !stack.peek().isAncestorOf(current)) {
        stack.pop().stop(this);
      }
    }
    return current;
  }

Now suppose we want to change the order in which children are displayed, e.g. first show all sub-folders, than the others. We could write that as follows:

public class XmlVisitor
    extends BaseFileSystemVisitor {

  public boolean start(Folder folder) {
    out.println("<folder name='" + folder.getName()
        + "'>");

    Iterator children = folder.getChildren();
    while (children.hasNext()) {
      FileSystemNode child = children.next();
      if (child instanceof Folder) {
        while ((child = visit(child)) != null) {
          // Repeat, since the visitor may be interrupted
        }
      }
    }

    children = folder.getChildren();
    while (children.hasNext()) {
      FileSystemNode child = children.next();
      if (!(child instanceof Folder)) {
        while ((child = visit(child)) != null) {
          // Repeat, since the visitor may be interrupted
        }
      }
    }

    stop(folder);
    return false;
  }

But this produces the wrong output, and an EmptyStackException on top of that. The problem is that the visit method just iterates until there are no more nodes left. This is wrong for the re-entrant case: it should iterate until the nodes are no longer descendants of the root that is passed into the method:

  public FileSystemNode visit(final FileSystemNode node) {
    final FileSystemNode root;
    FileSystemNode current;
    if (stack.isEmpty() || stack.peek() != node) {
      current = node;
      root = node;
    } else {
      current = (FileSystemNode) stack.pop();
      root = (FileSystemNode) stack.firstElement();
    }
    while (root.isAncestorOf(current)
        && numNodesVisited < maxNodes) {
      numNodesVisited++;
      stack.push(current);
      if (current.start(this)) {
        current = current.getNext();
      } else {
        current = current.getNextNoChild();
        stack.pop();
      }
      if (root.isAncestorOf(current)) {
        while (!stack.isEmpty()
            && !stack.peek().isAncestorOf(current)) {
          stack.pop().stop(this);
        }
      }
    }

    if (numNodesVisited >= maxNodes) {
      numNodesVisited = 0;
    }

    final FileSystemNode result;
    if (root.isAncestorOf(current)) {
      stack.push(current);
      result = current;
    } else {
      result = null;
    }
    return result;
  }

Note that numNodesVisited is now a field instead of local variable.

The code has become much more complex, but we are now successfully supporting both interruptability and re-entrancy. Next time, we will make things even more complex by adding support for modification of the data structure being visited.

Published in:  on 2008-12-13 at 16:38 Leave a Comment
Tags: ,

JavaFX plugin for Eclipse

Being an happy Eclipse user, I’ve always felt sort of a second-rate citizen in the JavaFX world. I get it that Sun wants to promote their own IDE. But let’s face it, there are a lot of us Eclipseans (is that even a word?) out there. Today, my luck has changed with the release of the JavaFX Plugin for Eclipse. Thanks to Jim Weaver for pointing that out to me. BTW, anybody interested in JavaFX should subscribe to his blog.

The first thing I notice, is that, again, there is no support for GNU/Linux. Well, I’m getting used to that by now. I downloaded the Mac version, and it seems to work on my Ubuntu box. So I naturally took it for a spin right away. Here are my first impressions.

Wizards

The plugin doesn’t provide a New Project wizard. Instead, you need to create a new Java project, and then add the JavaFX nature to it. A bit odd, but OK.

I follow the documentation and add a new script using the New Script wizard. Easy as pie. Then I’m supposed to drag a Stage onto the script. The Insert Template: Stage dialog doesn’t work: it just keeps popping back up over and over again. It doesn’t show any errors, but it also doesn’t insert any code. Maybe this is because I’m running on an unsupported platform, I don’t know.

Editor

Anyway, who needs these UI builders anyway ;) Let’s type in some code myself. The plugin provides basic syntax highlighting, which is good. Other than that, it doesn’t seem to provide much value. Error markers in the gutter don’t show the error messages when you hover over them, as the JDT does. The error messages in the Problems view take up three lines, so that I can see only a few of them at once.

Problems view

Speaking of errors, whenever I insert some non-ASCII symbol, like the é in my name, the plugin reports a mysterious error: Sorry, but the character ''. Right.

Also, there is no Code Completion (or I haven’t been able to find it). For someone just learning the language, as I am, this is a serious drawback. And some Quick Fixes are sorely missed as well.

Running a program

Running a program works using the expected Run As > JavaFX Application menu. The Run Configurations dialog looks like it is not a result of a collaboration between developers and designers:
Run Configurations

What is cool, is the Profile drop down list. It allows you to select the platform on which to run. You can emulate a mobile device, or run on the desktop as an application, as an applet, or with Web Start.

What is not so cool, is that you cannot select a working directory. This means you cannot test real-life deployments all that well. Nor can you specify Virtual Machine arguments. So for memory intensive applications, you’d have to revert to the command line or some build script.

Conclusion

I’m very happy that Sun is finally welcoming Eclipse users into the JavaFX world. But it seems that this plugin is indeed an early release, as there are many rough edges and missing features. Having said that, something is better than nothing. And the BSD license is certainly appreciated.

Published in:  on 2008-12-11 at 22:55 Comments (1)
Tags: ,

JavaFX 1.0 is out!

The 1.0 version of JavaFX is finally out. Unfortunately, there is no support for GNU/Linux and Solaris yet, but you can get it to work on GNU/Linux by downloading the Mac OSX version, and following these instructions. Have fun!

Update: There is now an unofficial GNU/Linux SDK download at Silveira Neto’s blog. BTW, this is a very nice blog in general if you’re interested in JavaFX.

Published in:  on 2008-12-05 at 16:00 Leave a Comment
Tags: ,