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.

5 comments:

Unknown said...

Hello Jing

I like this version!

the only recommendation I have
is specifying the fact that this layerUI is not shareable between layers, and adding a checking in installUI() to prevent incorrect setting this ui to multiple layer,
just like is implemented in AbstractBufferedLayerUI

Thanks
alexp

Unknown said...

Hello Alexp

Thanks a lot. The check is added.

Regards!
Jing

Anonymous said...

You know ,I have some Anarchy credits, and my friend also has some
Anarchy Online credits, do you know they have the same meaning,Both of them can be called
Anarchy gold,I just want to buy AO credits, because there are many cheap Anarchy online gold.

Anonymous said...

I can get Megaten Gold cheaply.
Yesterday i bought Megaten online Gold for my brother.
i hope him like it. i will give Megaten money to him
as birthday present. i like the cheap Megaten Gold very much.
I usuallybuy Megaten Gold and keep it in my store.
I enjoy the Megaten online money.

I can getmabinogi goldcheaply,
Yesterday i boughtmabinogi online gold for my brother.
i hope him like it. i will give mabinogi money to him
as birthday present. i like the cheap mabinogi very much.
I usually buy mabinogi gold and keep it in my store.

Anonymous said...

Once I played flyff, I did not know how to get strong, someone told me that you must have flyff penya. He gave me some flyff money, he said that I could buy flyff penya, but I did not have money, then I played it all my spare time. From then on, I got some flyff gold, if I did not continue to play it, I can sell cheap penya to anyone who want.


Once I played flyff, I did not know how to get strong, someone told me that you must have flyff penya. He gave me some flyff money, he said that I could buy flyff penya, but I did not have money, then I played it all my spare time. From then on, I got some flyff gold, if I did not continue to play it, I can sell cheap penya to anyone who want.