Tuesday, April 05, 2005

JFrame setMinimumSize() and setMaximumSize() issue

I have been playing around with Swing tonight using JDK 1.5.0_02. I have found an issue where the setMinimumSize and setMaximumSize dimensions are not enforced on a JFrame. I wanted to keep a dialog from being resized smaller than a minimum bounds or larger than a maximum bounds. To do this, I originally used the following code in my application:

frame.setMinimumSize( new Dimension( 250, 200 ) ); frame.setMaximumSize( new Dimension( 325, 275 ) );
Unfortunately, as I mentioned above, this does not prevent you from resizing the frame smaller or larger than the specified sizes. Of course, you could just decide to not allow any resizing of the frame. To do that, you could use this line of code in your application:

frame.setResizable( false );
However, if you want to allow the user to resize a dialog, then this is not a viable alternative. I decided to create my own class derived from JFrame. This class takes a title as well as minimum and maximum dimensions. My constructor for the class looks like this:

private Dimension minimumSize;
private Dimension maximumSize;

public MyJFrame( String title, Dimension minimumSize,
  Dimension maximumSize ) throws HeadlessException
{
  super( title );
  this.minimumSize = minimumSize;
  this.maximumSize = maximumSize;
  initialize();
}
The initialize(): call does nothing more than make a call to addComponentListener on the frame. In this call, I create an anonymous ComponentAdapter class which overrides the componentResized method to handle the constraints on the frame. The code looks like this:

private void initialize() {
    this.addComponentListener( new ComponentAdapter() {
        @Override
        public void componentResized( ComponentEvent e ) {
            MyJFrame frame = (MyJFrame) e.getSource();
            int width = frame.getWidth();
            int height = frame.getHeight();
            boolean resize = false;
            if ( width < minimumSize.getSize().width ) {
                width = minimumSize.getSize().width;
                resize = true;
            }
            else if ( width > maximumSize.getSize().width ) {
                width = maximumSize.getSize().width;
                resize = true;
            }
            if ( height < minimumSize.getSize().height ) {
                height = minimumSize.getSize().height;
                resize = true;
            }
            else if (height > maximumSize.getSize().height ) {
                height = maximumSize.getSize().height;
                resize = true;
            }
            if ( resize ) {
                frame.setSize( width, height );
            }
        }
    })
}
This code is pretty straight forward. It grabs the current size constraints for the frame that fired the event. It then checks these constraints against the specified minimum and maximum dimensions. If the constraints on the frame are not met, the size of the frame is set explicitly, otherwise, the flow falls through and the frame is resized according to the size that the user has resized it.

There may be better ways to handle this situation. This is what I came up with tonight and it works pretty well. Hopefully future versions of the JDK will enforce the setMinimumSize and setMaximumSize dimensions on the JFrame component.

Technorati Tags: , ,

Monday, April 04, 2005

Eclipse Milestone Build 3.1M6 released; SWT problem?

Eclipse Foundation has released the latest milestone build of Eclipse 3.1 for download. This version has quite a few bug fixes and some enhancements. One of the enhancements allows for plugins to be setup in JAR files instead of a directory structure.

I downloaded it today and set it up on my laptop. I then opened up the SWT application that I am currently working on at home and found that there appears to be an issue with SWT in this build. My classpath is setup correctly and the build works, but when the application launches, I get a NullReferenceException on a call to super.configureShell( shell ). This all works in Eclipse 3.1M5a. I am not sure what has changed, but I need to play around with it a little more to see what is going on.

One thing that I noticed with the new plugin setup is that the SWT DLL for Win32 is located in a totally different directory now. I think they have a little work to do on this new plugin strategy. Of course, that is why they call this beta software. It might not work for everything that you want to do.

Update: I worked with this some more tonight and it appears that there is definitely an issue with SWT in the latest milestone build. I would imagine that it might be in the swt-win32-3128.dll file, but of course, you should not bank on that information. I use milestone build M6 and pointed it ot the swt.jar and the swt-win32-3123.dll for milestone build M5a and my application works.

Technorati Tags: , ,

Friday, April 01, 2005

Regular Expressions in Java

I started playing around with regular expressions this past week. I am going to use them in the personal project that I am working on to validate different user input data strings. I recently purchased a very good book from Apress called Regular Expression Recipes: A Problem-Solution Approach by Nathan A. Good. They have another book specifically for Java called Java Regular Expressions: Taming the java.util.regex Engine, but I did not know about it before I purchased the first book. It may have helped me with this problem. :)

Anyway, the book that I purchased has examples for Perl, Python, and shell scripting. Since it was the first example for each recipe, I picked off the regular expressions from the Perl code for the particulat items that I wanted to validate and used that in Java. I was dumbfounded when it did not work. As an example, I used a regular expression to validate an email address. The regular expression looked like this from the Perl example:

/^[-\w.]+@([A-z0-9][-A-z0-9]+\.)+[A-z]{2,4}$/
I spent a couple of evenings after work at home on this problem before I finally figured it out. You should not use the starting and ending slash ("/") when using any regular expression in Java. Java's regular expression compiler apparently sees this as a literal character when it compiles the expression and the corresponding validation ends up failing. When I removed those slashes, the validation unit test past and things were grand!

One other thing to remember in Java with regular expressions is that the backslash ("\") is an escape character. Unfortunately, it is also an escape character in Java. Thusly, you have to use double backslashes for the escape character in your regular expressions in Java. So, in the example above, the first \w should be set in Java as \\w.

Technorati Tags: , , ,

SWT/JFace, MVC, JUnit

Whew! Did I get them all in there? I have been struggling lately with the Model-View-Controller pattern. Well, I haven't been struggling with the pattern itself as much as I had been struggling with how to implement it properly and create unit tests in an SWT/JFace application. Being new to MVC, one struggles with what should be in the controller and what should not. I was trying to put everything in the controller, but that is not always feasible.

In my case, I was unsure about the inherited methods from the JFace Window class, such as createContents(), initializeBounds(), etc. Well, those methods do not need to be in the controller, because there is no interaction with the model. These methods are strictly there to create and initialize the view. The methods that I ended up putting in the controller were only those that dealt with the view with respect to the model.

For instance, in my example application that I put together tonight to play around with this process, I have a Contact class (model), a setup dialog (view), and a controller class. The model contains nothing more than first, middle, and last name strings for this example. It is quite simple. The view is also simple in that it has three Label controls, three Text controls, and two Button controls (Print and Cancel).

My controller class ended up having the following methods:

public void onFirstNameChanged();
public void onMiddleNameChanged();
public void onLastNameChanged();
public void onPrintClick();
public void onCancelClick();
The first three methods are called in the focusLost method for the FocusListener on the respective edit control for each component of the contact name. The onPrintClick method is called when I click on the Print button. It fires the print method on the view and does nothing more than print the contents of the model out to the console in this simple example. The onCancelClick method fires the close method on the view.

I was able to create unit tests for my controller class and the model, but I did not really create anything for the view. The view does nothing more than initialize itself and wait for user input or receives events from the controller. Since all of the major work for updating the model and other user interaction is handled in the controller, then I am not even sure if it matters that there are no unit tests for the view.

I could have simplified this example a little more by combining the view and the model, but since I have most of the classes already created for an application that I am currently working on for myself, I figured I would see if I could get things working with a model that is separate from the view.

I came across an interesting discussion on Martin Fowler's web site tonight which talks about Model-View-Presenter.  I need to investigate this further.

Technorati Tags: , , , , , , , ,