Thursday, July 31, 2008

Refactoring to Patterns: Replace Conditional With Template

As many people already met, there are always many problems in a legacy system. One of the most famous problem is the “if else hell”. This article will try to tell you how to use the refactoring and unit test for getting ride of the “if else hell”.

Let’s see a legacy code example (note: even it is just an example, but it comes from a real project.


package de.jingge.refactoring;

public class SystemManager {

public static final int LOGGEDIN = 0;
public static final int LOGGEDOUT = 1;
public static final int IDLE = 2;
int state;

public void login() {
// call service#login()
updateState(LOGGEDIN);
}

public void logout() {
// call service#logout()
updateState(LOGGEDOUT);
}

public void idle() {
// call some other services
updateState(IDLE);
}

public void updateState(int state) {
if (state == LOGGEDIN) {
// do something after logging in is successful,
// for example: show welcome dialog, open the last edit document, etc.
} else if (state == LOGGEDOUT) {
// do something after logging out is successful,
// for example: free used resource, dispose GUI components, etc.
} else if (state == IDLE) {
// do something after the user is idle,
// for example: save the application state temporarily, lock the application, etc.
} else {
throw new IllegalArgumentException("unknown state");
}
this.state = state;
}
}


Here we get a SystemManager, which is responsible for managing the system state, i.e. logged in, logged out, idle. As you can see, this is legacy class, which use the “int” for presenting the system state and has an “If-else hell” in the method updateState(int state). With growing number of the states we will get more pains.

How can we refactoring this code and make it more object oriented?

First, we should create a unit test. Since the SystemManager class was created, we are not doing here 100% TDD. But we can still hold the TDD soul.

The first unit test looks like this:


package de.jingge.refactoring;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;

public class SystemManagerTest {

private static SystemManager manager;

@BeforeClass
public static void setUpClass() throws Exception {
manager = new SystemManager();
// add some service mock objects
}

@AfterClass
public static void tearDownClass() throws Exception {
}

@Test
public void login() {
manager.login();
assertEquals(manager.state, SystemManager.LOGGEDIN);
}

@Test
public void logout() {
manager.logout();
assertEquals(manager.state, SystemManager.LOGGEDOUT);
}

@Test
public void idle() {
manager.idle();
assertEquals(manager.state, SystemManager.IDLE);
}
}



Run the test, it passt:



Now let’s begin refactoring:


Replace the legacy integer constant with java Enum


This Step is relatively easy. Just create a new enum class:


package de.jingge.refactoring;

public enum SystemState {
LOGGEDIN,
LOGGEDOUT,
IDLE;
}



It is simple to replace all code calling the integer constant with calling the enum instance:

1. Add import static de.jingge.refactoring.SystemState.*;
2. Remove all integer constants.
3. Change the type of the state to the enum class SystemState.

After the refactoring, the code should look like this:


package de.jingge.refactoring;

import static de.jingge.refactoring.SystemState.*;

public class SystemManager {

SystemState state;

public void login() {
// call service#login()
updateState(LOGGEDIN);
}

public void logout() {
// call service#logout()
updateState(LOGGEDOUT);
}

public void idle() {
// call some other services
updateState(IDLE);
}

public void updateState(SystemState state) {
if (state == LOGGEDIN) {
// do something after logging in is successful,
// for example: show welcome dialog, open the last edit document, etc.
} else if (state == LOGGEDOUT) {
// do something after logging out is successful,
// for example: free used resource, dispose GUI components, etc.
} else if (state == IDLE) {
// do something after the user is idle,
// for example: save the application state temporarily, lock the application, etc.
} else {
throw new IllegalArgumentException("unknown state");
}
this.state = state;
}
}



And in the test class:

1. Add import static de.jingge.refactoring.SystemState.*;
2. Remove the SystemManager reference before all constants.


package de.jingge.refactoring;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
import static de.jingge.refactoring.SystemState.*;

/**
*
* @author JGe
*/
public class SystemManagerTest {

private static SystemManager manager;

@BeforeClass
public static void setUpClass() throws Exception {
manager = new SystemManager();
// add some service mock objects
}

@AfterClass
public static void tearDownClass() throws Exception {
}

@Test
public void login() {
manager.login();
assertEquals(manager.state, LOGGEDIN);
}

@Test
public void logout() {
manager.logout();
assertEquals(manager.state, LOGGEDOUT);
}

@Test
public void idle() {
manager.idle();
assertEquals(manager.state, IDLE);
}
}



Run the test again. It should be passed.

Now we should go to handle the “if-else hell”.

Get ride of the if-else hell

Take a look at the the updateState() method. The reason, why if-else is used, is that the SystemState is just an enum and does not has any activities. After caught this point, it should be clear how to refactoring this bad code: change the input the parameter of the updateState() method into a activity class and move the logic code into that class. By calling the appropriate method of such class in the updateState() method, the if-else will disappear.

We call these activity classes as System State Performer, the base class, which should be abstract, should look like this:


package de.jingge.refactoring;

import java.awt.Image;


public abstract class SystemStatePerformer {

private SystemState state;
private Image image;

public SystemStatePerformer(SystemState state, Image image) {
this.state = state;
this.image = image;
}

public SystemState getState() {
return state;
}

public Image getImage() {
return image;
}

public abstract void perform();
}


Like the code says, each performer has a SystemState and can contain any other object references. The Image showed here is such an example, which means each state has its own image shown to the user. The perform() method will do the real business logic for it’s state.

The next step is building concrete Performer for each state. The first idea came to my mind is building sub class for each state. You can do it, if you like this style. But, in my opinion, this way will make a lot of classes and we will easily lose the focus. I decide to use a Factory and create anonymous class for each state. Doing it this way can hold all classes for all state in a one location. Since adding support for new state will force changing at lease one class, why not choose the factory class?

The performer factory class should look like this:


package de.jingge.refactoring;

import static de.jingge.refactoring.SystemState.*;
import java.awt.Image;
import java.awt.image.BufferedImage;

public class SystemStatePerformerFactory {

private static SystemStatePerformerFactory INSTANCE = new SystemStatePerformerFactory();

private SystemStatePerformerFactory() {
}

public static SystemStatePerformer getSystemStatePerformer(SystemState state) {
switch (state) {
case LOGGEDIN:
return createLoggedInPerformer();
case IDLE:
return createIdlePerformer();
case LOGGEDOUT:
return createLoggedOutPerformer();
default:
throw new IllegalAccessError("Unkonw status");
}
}

private static SystemStatePerformer createLoggedInPerformer() {
return new SystemStatePerformer(LOGGEDIN, getImage("loggedin.gif")) {

@Override
public void perform() {
// do something after logging in is successful,
// for example: show welcome dialog, open the last edit document, etc.
}
};
}

private static SystemStatePerformer createLoggedOutPerformer() {
return new SystemStatePerformer(LOGGEDOUT, getImage("loggedout.gif")) {

@Override
public void perform() {
// do something after logging out is successful,
// for example: free used resource, dispose GUI components, etc. }
}
};
}

private static SystemStatePerformer createIdlePerformer() {
return new SystemStatePerformer(IDLE, getImage("idle.gif")) {

@Override
public void perform() {
// do something after the user is idle,
// for example: save the application state temporarily, lock the application, etc.
}
};
}

private static Image getImage(String string) {
return new BufferedImage(10, 10, BufferedImage.TYPE_4BYTE_ABGR);
}
}



In this factory, for each state, there is a performer create method, which creates the anonymous class. The different business logic code for different state are moved into the appropriate perform() method. There is only one public method getSystemStatePerformer(), which can be called by our SystemManager.

Note, since I want to concentrate on the if-else problem, there are some bad design in this class. First, the getImage() method is just used as example. In a real project, it should be more type save. Second, the static method will make the test painful. In a real project, you should avoid using it. Try DI framework, for example google guice for solving this problem.

Ok, now we have refactoring the conditional code out from the SystemManager into the SystemStatePerformer. The next step is refactoring the SystemManager, replace the SystemState with the performer:

1. Change the variable SystemState state to SystemStatePerformer statePerformer. Note: use the IDE refatoring, because the state is used in the test.
2. Call the SystemStatePerformerFactory in the updateState() method
3. Calling getState() after manager.statePerformer in the test, because we test the state not the statePerformer.

After the refactoring, the SystemManager looks like this:


package de.jingge.refactoring;

import static de.jingge.refactoring.SystemState.*;

public class SystemManager {

SystemStatePerformer statePerformer;

public void login() {
// call service#login()
updateState(LOGGEDIN);
}

public void logout() {
// call service#logout()
updateState(LOGGEDOUT);
}

public void idle() {
// call some other services
updateState(IDLE);
}

public void updateState(SystemState state) {
this.statePerformer = SystemStatePerformerFactory.getInstance()
getSystemStatePerformer(state);
statePerformer.perform();
}
}



Like the code tells us, the if-else is disappeared.

The test class looks like this:


package de.jingge.refactoring;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
import static de.jingge.refactoring.SystemState.*;


public class SystemManagerTest {

private static SystemManager manager;

@BeforeClass
public static void setUpClass() throws Exception {
manager = new SystemManager();
// add some service mock objects
}

@AfterClass
public static void tearDownClass() throws Exception {
}

@Test
public void login() {
manager.login();
assertEquals(manager.statePerformer.getState(), LOGGEDIN);
}

@Test
public void logout() {
manager.logout();
assertEquals(manager.statePerformer.getState(), LOGGEDOUT);
}

@Test
public void idle() {
manager.idle();
assertEquals(manager.statePerformer.getState(), IDLE);
}
}



Until now, the refactoring is almost done, the code looks more OO. But there is still one little problem: the if-else in the SystemManager disappeared, but we get a switch in the factory. It is, actually, nothing else as the if-else. It is not a pure solution.

Ok, let’s move further. For getting ride of this switch, we can instantiate all anonymous classes before the getSystemStatePerformer() is called and return the correct instance when the method is called. A Map is for this case a simple choice for holding all instances of the SystemStatePerformer.

After refactoring the SystemManager should look like this:


package de.jingge.refactoring;

import static de.jingge.refactoring.SystemState.*;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class SystemStatePerformerFactory {

private static SystemStatePerformerFactory INSTANCE = new SystemStatePerformerFactory();

private Map<SystemState, SystemStatePerformer> performers;

private SystemStatePerformerFactory() {
}

public static SystemStatePerformerFactory getInstance() {
return INSTANCE;
}

private synchronized Map<SystemState, SystemStatePerformer> getPerformers()
throws Exception {
if (performers == null) {
performers = new HashMap<SystemState, SystemStatePerformer>();
// call all @FactoryMethod using reflection
for (Method m : getClass().getDeclaredMethods()) {
if (m.getAnnotation(FactoryMethod.class) != null) {
SystemStatePerformer p = (SystemStatePerformer) m.invoke(
this, new Object[]{});
performers.put(p.getState(), p);
}
}
// make it readonly
performers = Collections.unmodifiableMap(performers);
}
return performers;
}

public SystemStatePerformer getSystemStatePerformer(SystemState state) throws Exception{
return getPerformers().get(state);
}

@FactoryMethod
private SystemStatePerformer createLoggedInPerformer() {
return new SystemStatePerformer(LOGGEDIN, getImage("loggedin.gif")) {

@Override
public void perform() {
// do something after logging in is successful,
// for example: show welcome dialog, open the last edit document, etc.
}
};
}

@FactoryMethod
private SystemStatePerformer createLoggedOutPerformer() {
return new SystemStatePerformer(LOGGEDOUT, getImage("loggedout.gif")) {

@Override
public void perform() {
// do something after logging out is successful,
// for example: free used resource, dispose GUI components, etc. }
}
};
}

@FactoryMethod
private SystemStatePerformer createIdlePerformer() {
return new SystemStatePerformer(IDLE, getImage("idle.gif")) {

@Override
public void perform() {
// do something after the user is idle,
// for example: save the application state temporarily, lock the application, etc.
}
};
}

private Image getImage(String string) {
return new BufferedImage(10, 10, BufferedImage.TYPE_4BYTE_ABGR);
}
}



The getPerformers() method will build all performer instances lazily and will be called when the getSystemStatePerformer() is called at the first time. I create and use the @FactoryMethod annotation, because I want to avoid changing the getSystemStatePerformer() method, each time when new create***Performer() method is added.

The annotation class looks like this:



package de.jingge.refactoring;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface FactoryMethod {

}



Well, all are done! There is no if-else, no switch. For further state, just add a new create***Performer() method in the factory, that’s all!

Friend, who read the “Refactoring to patterns”, may say that what we did here is same as what the author described in his book, in the last segment of the chapter 7: Replace Conditional Dispatcher with Command. Well, at first glance, they are, but after read my whole code and think about it, the answer is No.

The Book Refactoring to Patterns is great. I really like it. I have read it many times. All what I did here is, actually, based on the solution written in that book. But there are still some differents:

1. Factory + anonymous class instead of the concrete sub classes.
Doing it like this can hold all performer in one class and all needed object references are holt by the factory not the concrete sub classes. This can reduce the reference complex.
2. The performer is not just a command. It has a state and could have many more logic than a command.


Further reading:

Design patterns Elements of Reusable Object-Oriented

Refactoring to Patterns

Test Driven Practical TDD and Acceptance TDD for Java Developers

Wednesday, July 23, 2008

Update: Common Validation Module for Swing Application

In the last blog I have written how to build a common validation module by using the jxlayer, beansbinding, and hibernate validator. After discussing about it with the JXLayer Author Alexander Potochkin, I got a suggestion from him(thanks, Alexp :-)): we should not change the state of any component in the paint() method. This is a swing pattern.

The last version of the HibernateValidationUI contains Obviously an antipattern, where the tooltip is set in the paintLayer() method.

Today, I post here a updated version of the HibernateValidationUI and try to kill the antipattern.


/**
* @author Jing Ge
*/
public class HibernateValidationUI extends AbstractLayerUI<JTextComponent> {

private Object object;
private String propertyName;
private ClassValidator validator;
private ELProperty elProperty;
private PropertyStateListener propertyChangeHandler;
private JXLayer<JTextComponent> layer;

public HibernateValidationUI(Object obj, String propertyName) {
this.object = obj;
this.propertyName = propertyName;
propertyChangeHandler = new PropertyChangeHandler();
validator = new ClassValidator(obj.getClass());

elProperty = ELProperty.create("${" + propertyName + "}");
}

@Override
public void installUI(JComponent c) {
if (layer != null) {
throw new IllegalStateException(getClass().getSimpleName() +
" can't be shared between multiple layers");
}
super.installUI(c);
layer = (JXLayer<JTextComponent>) c;
elProperty.addPropertyStateListener(object, propertyChangeHandler);
validate();
}

@Override
public void uninstallUI(JComponent c) {
super.uninstallUI(c);
layer = null;
elProperty.removePropertyStateListener(object, propertyChangeHandler);
}

private void validate() {
InvalidValue[] validationMessages = validator.getInvalidValues(object,
propertyName);
if (validationMessages.length > 0) {
layer.getView().setToolTipText(validationMessages[0].getMessage());
return;
}
layer.getView().setToolTipText(null);
}

@Override
protected void paintLayer(Graphics2D g2, JXLayer<JTextComponent> l) {
super.paintLayer(g2, l);
if (!isValid()) {
BufferedImage image = Java2DIconFactory.createErrorIcon();
g2.drawImage(image, l.getWidth() - image.getWidth() - 1,
l.getHeight() - 8, null);
}
}

boolean isValid() {
return isBlank(layer.getView().getToolTipText());
}

/**
* a copy from the apache common lang.
* Just use the StringUtils if the common lang jar in your classpath.
*/
private boolean isBlank(String str) {
int strLen;
if (str == null || (strLen = str.length()) == 0) {
return true;
}
for (int i = 0; i < strLen; i++) {
if ((Character.isWhitespace(str.charAt(i)) == false)) {
return false;
}
}
return true;
}

class PropertyChangeHandler implements PropertyStateListener {

@Override
public void propertyStateChanged(PropertyStateEvent pse) {
validate();
setDirty(true);
}
}
}


There are some changes in the class:

1. The class holds a reference of the JXlayer instance, because it will be used while we validate the property. The reference will be injected when installUI() method is called.
2. A new method validate() is added, which will validate the property and set the tooltip appropriately.
3. The validate() method should be called in two places. One is just after the JXLayer instance is injected and another is when the property is changed, i.e. in the propertyStateChange(PropertyStateEvent) method.
4. Now we simply use the tooltip text of the managed text component for checking whether the value of the property is valid.
5. In the paintLayer() method we will only paint the error icon(when the the content of the property is invalid). No state of any component will be changed here.
6. like the comment says, the isBlank() method is a copy from the apache common lang. If the common lang jar is in your classpath, just call the StringUtils.isBlank() method.

Friday, July 18, 2008

Common Validation Module for Swing Application

I have been using swing for developing the rich client application for a long time. There are always so many things to do that I put all my time and soul on it. This year, with the release of Swing Application Framework and Beans binding, our life goes better. But you can not stop getting new issues. In this article I will try to use some frameworks/libraries and build them together to solve a very import issue which each swing application must face – the validation.

Validation plays a very important role in the modern application, because it can make your application more user friendly (always tells the user what he should do) and protect your system. In an n-tier system, the validation could be built on the server side or on the client side, while the second choice is always preferred. Today I will tell you how to use the beans binding, jxlayer, and hibernate validator to build validation module in a desktop application.

Since the hibernate validator does a great job for domain model validation and we should not reinvent the wheel. So, the goal of this module is trying to delegate all validation calls from the GUI to the hibernate validator. Each time the property of the bound bean is changed, the new value will be validated by the hibernate validator framework. Error icon will be shown when the validation failed.

Before going to the amazing validation code, I need some sample classes. For example a bean and some factories:

First, we need a bean. Open you IDE (I use here the Netbeans. actually I am a eclipse user, but for such a small project, Netbeans can really save much time), and create a Java application. Yes, “Java Application” not “Java Desktop Application”, because I want to keep it simple. The Swing Application Framework is great. I use it in my real project. But in this case, using it is not necessary, it will only make thing more complicated. So, just create a Java Application. In my example, the project calls “validation”.

Second, add the following jar files into the libraries:



All needed jar files can be got from the lib folder in the attached netbeans project.

Third, create a bean. I choose a sample bean for presenting a Country. The code looks like this:



package de.jingge.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import org.hibernate.validator.Length;
import org.hibernate.validator.NotEmpty;

/**
*
* @author Jing Ge
*/
@Entity
public class Country extends AbstractBean {

private static final long serialVersionUID = 5341382564159667599L;
public static final String PROPERTYNAME_NAME = "name";
public static final String PROPERTYNAME_CODE = "code";
private String name;
private String code;
private Long id;

public Country() {
}

public Country(String code, String name) {
super();
setCode(code);
setName(name);
}

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

@NotEmpty
public String getName() {
return name;
}

public void setName(String name) {
firePropertyChange(PROPERTYNAME_NAME, this.name, this.name = name);
}

@Length(min=2, max= 2, message="Code length must be 2")
@NotEmpty
public String getCode() {
return code;
}

public void setCode(String code) {
firePropertyChange(PROPERTYNAME_CODE, this.code, this.code = code);
}
}



Nothing difficult, The Country class is just a java bean class. It has two properties: code and name. Each property has its own validation annotation. The code should be not empty and its length should be 2 and the name should not be empty. Since most applications are working with database, a persistence bean is used here, but it is not mandatory. Hibernate validator works pretty well with normal pojo class.

The parent class AbstractBean comes from the framework Swingx. Extending the AbstractBean will let the Country support property change event.

Now we can begin writing the code for our validation module.

In my validation module, I will use the jxlayer to show the icon when the validation failed. For understanding the following description better, you should understand the jxlayer first. You can find all information about the jxlayer here: http://weblogs.java.net/blog/alexfromsun/

The JXlayer is an amazing new JComponent built by Alexander Potochkin. All painting events that the JXLayer gets will be delegated to the UI class. That means what we should do is just creating a new UI class: HibernateValidationUI.java and call the hibernate validation when the gui component is (re)painted. This UI class is based on the TextValidationDemo from the jxlayer's demo package. The code looks like this (I will explain it step by step):


package de.jingge.view;

import java.awt.Graphics2D;
import java.awt.image.BufferedImage;

import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.text.JTextComponent;

import org.hibernate.validator.ClassValidator;
import org.hibernate.validator.InvalidValue;
import org.jdesktop.beansbinding.ELProperty;
import org.jdesktop.beansbinding.PropertyStateEvent;
import org.jdesktop.beansbinding.PropertyStateListener;
import org.jdesktop.jxlayer.JXLayer;
import org.jdesktop.jxlayer.plaf.AbstractLayerUI;

/**
* Header:
* Description: A layerUI which will validate the referenced property value of
* the object each time when the paint(...) method is called.

* The value of the given object property will be observed.
* Note: This UI works only with {@link JXLayer}. Any change of the property
* will force repainting the UI. The work process looks like: property changed ->
* jxlayer will be repainted -> the paint(...) method of this UI will be called.
* The logic of validation will be handled by the Hibernate validator
* framework.
*
* @author Jing Ge
*/
public class HibernateValidationUI extends AbstractLayerUI<jTextComponent> {

private Object object;
private String propertyName;
private ClassValidator validator;
private ELProperty elProperty;
private PropertyStateListener propertyChangeHandler;

public HibernateValidationUI(Object obj, String propertyName) {
this.object = obj;
this.propertyName = propertyName;
propertyChangeHandler = new PropertyChangeHandler();
validator = new ClassValidator(obj.getClass());

elProperty = ELProperty.create("${" + propertyName + "}");
}

public void installUI(JComponent c) {
super.installUI(c);
elProperty.addPropertyStateListener(object, propertyChangeHandler);
}

public void uninstallUI(JComponent c) {
super.uninstallUI(c);
elProperty.removePropertyStateListener(object, propertyChangeHandler);
}

protected void paintLayer(Graphics2D g2, JXLayer<jTextComponent> l) {
super.paintLayer(g2, l);
InvalidValue[] validationMessages = validator.getInvalidValues(object,
propertyName);
if (validationMessages.length > 0) {
BufferedImage image = Java2DIconFactory.createErrorIcon();
g2.drawImage(image, l.getWidth() - image.getWidth() - 1,
l.getHeight() - 8, null);
l.getView().setToolTipText(validationMessages[0].getMessage());

return;
}
l.getView().setToolTipText(null);
}

boolean isValid() {
return validator.getInvalidValues(object, propertyName).length == 0;
}

class PropertyChangeHandler implements PropertyStateListener {

@Override
public void propertyStateChanged(PropertyStateEvent pse) {
setDirty(true);
}
}
}



The HibernateValidationUI have only one constructor, which need two parameters. The first parameter obj means the source object that will be edited, i.e. the java bean. The second parameter propertyName is the name of the property which is bound to the gui component which is managed by the jxlayer (if you don’t understand the relationship between the gui component and the jxlayer, please read the jxlayer source code).

The propertyChangeHandler will call setDirty(true) of the UI class each time the property of the given object is changed. Calling the setDirty(true) method will change the state of the jxlayer(if the jxlayer was not dirty) and this will force jxlayer to repaint itself, which means the UI class will be repainted, because the call will be delegated from the jxlayer to the UI class.

The validator is a new instance of the hibernate ClassValidator. It will be used later for doing the real validation logic.

The elProperty will be used with the propertyChangeHandler together for observing the change of the property.

The installUI(JComponent c) method will be called when layer.setUI(ui) is called. Here
We add the propertyChangeHandler with the given source object into the elProperty. This means the change of the property will be observed after the jxlayer is instantiated.

To avoid adding more than one propertyChangHandler, we will call removing the propertyChangeHandler in the uninstallUI(JComponent c) method.

The paintLayer(Graphics2D g2, JXLayer<jTextComponent> l) method contains the most import code for this validation module. We will first call the validation logic of the hibernate validator and get the validation result: the validationMessages. After that, we will check whether the value of the property is valid, if not (the validationMessages is not empty), an error icon will be shown and the first validation message will be shown as tooltips. (Of cause, you can show all validation messages, if want).

The used class Java2DIconFactory looks like this:



package de.jingge.view;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;

/**
*
* @author
*/
public class Java2DIconFactory {

public static BufferedImage createErrorIcon() {
return createErrorIcon(7, 8);
}

public static BufferedImage createErrorIcon(int width, int height) {
BufferedImage icon = new BufferedImage(width, height,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = (Graphics2D) icon.getGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
RenderingHints.VALUE_STROKE_PURE);
g2.setColor(Color.RED);
g2.fillRect(0, 0, width, height);
g2.setColor(Color.WHITE);
g2.drawLine(0, 0, width, height);
g2.drawLine(0, height, width, 0);
g2.dispose();
return icon;
}
}


Now we need a new Factory for creating (or maybe better call it building) a gui component, which can be used in the application for editing the object. The factory looks like this:


package de.jingge.view;

import javax.swing.JTextField;
import javax.swing.text.JTextComponent;
import org.jdesktop.beansbinding.AutoBinding;
import org.jdesktop.beansbinding.BeanProperty;
import org.jdesktop.beansbinding.BindingGroup;
import org.jdesktop.beansbinding.Bindings;
import org.jdesktop.beansbinding.ELProperty;
import org.jdesktop.jxlayer.JXLayer;
import static org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.*;

/**
*
* @author Jing Ge
*/

public class GuiComponentFactory {

public static JXLayer<jTextComponent> createTextField(
BindingGroup bindingGroup, Object sourceObject,
String sourceProperty) {
JTextField field = new JTextField();
AutoBinding binding = Bindings.createAutoBinding(READ_WRITE,
sourceObject, ELProperty.create("${" + sourceProperty + "}"),
field, BeanProperty.create("text"));
bindingGroup.addBinding(binding);
bindingGroup.bind();
return new JXLayer<jTextComponent>(field, new HibernateValidationUI(
sourceObject, sourceProperty));
}
}


In the craeteTextField() method, we create a JTextField, bind it with the property of the given object and return a jxlayer, which contains the HibernateValidationUI object and manage the created JTextField. You can find more information about beans binding here: http://weblogs.java.net/blog/shan_man/

Until now, we have finished all code what we need for a validation module. Now let’s build a demo application:



package de.jingge.main;

import de.jingge.domain.Country;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.text.JTextComponent;
import net.miginfocom.swing.MigLayout;
import org.jdesktop.beansbinding.BindingGroup;
import org.jdesktop.jxlayer.JXLayer;
import static de.jingge.view.GuiComponentFactory.*;

/**
*
* @author Jing Ge
*/
public class ValidationApplicaton {

private BindingGroup bg;
private Country country;
private JXLayer<jTextComponent> codeField;
private JXLayer<jTextComponent> nameField;

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(
"com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (UnsupportedLookAndFeelException ex) {
System.err.println(
"Nimbus L&F does not support. Default L&F will be used.");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ValidationApplicaton app = new ValidationApplicaton();
JFrame frame = new JFrame("Demo Validation Application");
frame.setPreferredSize(new Dimension(360, 150));
frame.getContentPane().add(app.buildPanel(), BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setCenter(frame);
frame.setVisible(true);
frame.pack();

}

private static void setCenter(JFrame frame) {
Toolkit toolkit = Toolkit.getDefaultToolkit();
Dimension screenSize = toolkit.getScreenSize();

// Calculate the frame location
int x = (screenSize.width - (int) frame.getPreferredSize().getWidth()) / 2;
int y = (screenSize.height - (int) frame.getPreferredSize().getHeight()) / 2;

// Set the new frame location
frame.setLocation(x, y);
}

public ValidationApplicaton() {
country = new Country();
bg = new BindingGroup();
}

private JPanel buildPanel() {

codeField = createTextField(bg, country, Country.PROPERTYNAME_CODE);
nameField = createTextField(bg, country, Country.PROPERTYNAME_NAME);
JPanel panel = new JPanel(new MigLayout("",
"[50px, right]10[200px:250px:300px]", "[center]"));
panel.add(new JLabel("Code:"), "cell 0 0");
panel.add(codeField, "cell 1 0, w 200px:250px:300px");
panel.add(new JLabel("Name:"), "cell 0 1");
panel.add(nameField, "cell 1 1, w 200px:250px:300px");
return panel;
}
}


In the main() method, we create a new JFrame and add the panel to it.

The setCenter(JFrame frame) method will set the frame on the center of the screen.

In the constructor new Country and BindingGroup instances will be created.

In the buildPanel() method we use the MigLayout for building a panel, which contains two text field for edit the code and name of the country. You can get more information about MigLayout here: http://www.miglayout.com/

Run the application, a window will be shown likes this:




Since the Country class is annotated by the hibernate validation annotation, the empty code and name are invalid. The error icons are shown. If you move your mouse over the text field, you will get the validation message, which will tell your how to make it valid.




After enter correct information, all error icons will disappear, like the following picture shows:



The benefit of using this validation module:

  1. No code anymore for normal validation. Add the validation annotation like @NotEmpty, @Email etc. to you domain models, value objects, or some other POJOs. The properties will be validated in the GUI automatically. Developer does not need to write any code.
  2. If you use Hibernate as ORM, this should be one of the best solutions. The cost of writing validation will be reduced extraordinarily.
  3. For more complex validation you can follow the hibernate validator extension. It is incredible easy thanks to the hibernate validator. What you need to do is just writing two classes: One is the annotation and the other is the concrete validator. You can find a introduction about the extension here: http://www.hibernate.org/hib_docs/validator/reference/en/html/validator-defineconstraints.html#validator-defineconstraints-own


Oh, I can not upload the project! I will try another way later. Guy, who wants to get the netbeans project, please contact me.