Hello everyone,

I'm relatively new to wicket, but I'm building a web application where several 
forms are backed by compound models with boolean and enum constituent models. 
The straight-forward implementation of this would display true/false values, 
and the names of the enum values in the code, which is generally not very 
user friendly (peopel say "yes" and "no" and they don't generally put choices 
in all upper case) and not internationalized, so Something Must Be Done.

I'd like to show my solution, with input from Igor Vaynberg, for comment and 
perhaps re-use. I've only got the boolean case implemented, but the enum case 
is basically more of the same with more localization resources needing to be 
loaded.

The simplest case (and one not worth all this effort) is when displaying a 
boolean value in a ListView:
  new Label("active");
which becomes:
  new Label("active", new I18nBooleanModelDecorator(item.getModel()));
where the (attached) I18nBooleanModelDecorator has been applied to the model. 
This will display "yes" or "no" for the boolean values unless those strings 
have been replaced by setting the I18nBooleanModelDecorator.TRUE and 
I18nBooleanModelDecorator.FALSE licalization strings to something else.

Usually on a form you'd use a check-box for setting boolean values, such as 
this:
  new CheckBox("active");
but for illustrative purposes suppose we have this text field instead:
  new TextField("active");
The following will work if the component's compound model has been set before 
the form is populated with fields and will not be changed later:
  new TextField("active", new I18nBooleanModelDecorator(getModel()));
but in reality getModel() is likely to return null or to return a model which 
is later replaced with another in response to user or application events, so 
the text field would now either have no underlying model or an out-dated one.

For this I wrote a ComponentModelDelegator which takes a Component and 
delegates all model operations to the current model of that component like 
so:
  IModel model = new ComponentModelDelegator(this)
  new TextField("active", new I18nBooleanModelDecorator(model));

This works like a charm and you can write "nei" in my Icelandic locale to set 
the value of the active property of my underlying model to false... before 
you then change the code back to using the much more reasonable CheckBox 
class.

Is there anything terribly wrong with this? Would this be useful for other 
wicket users, probably in combination with the similar decorator for 
enumerations?

PS The attached code is hereby published under the Apache license.
-- 
Logi Ragnarsson
package is.gig.wicket.models;

import org.apache.log4j.Logger;
import wicket.Component;
import wicket.model.IModel;

/** Model which delegates to the model of a given component. */
public class ComponentModelDelegator/*T*/
    implements IModel/*T*/
{
    private static final Logger LOG = Logger.getLogger(ComponentModelDelegator.class);

    private Component component;

    /** Create a new delegator to the model of the givne component. */
    public ComponentModelDelegator(Component component) {
        this.component = component;
    }

    public Component getComponent() {
        return component;
    }

    public void setComponent(Component component) {
        this.component = component;
    }

    public IModel /*T*/ getNestedModel() {
        return component.getModel();
    }

    public Object getObject(final Component component) {
        IModel nested = getNestedModel();
        if (nested != null) {
            return nested.getObject(component);
        } else {
            return null;
        }
    }

    public void setObject(final Component component, final Object object) {
        IModel nested = getNestedModel();
        if (nested != null) {
            nested.setObject(component, object);
        }
    }

    public void detach() {
        IModel nested = getNestedModel();
        if (nested != null) nested.detach();
    }
}
package is.gig.wicket.models.i18n;

import wicket.Component;
import wicket.model.IModel;

/**
 * Decorator for a boolean model which converts to and from localized and human-friendly display values, falling back to
 * "yes" and "no".
 *
 * <p/>The display values are taken from the localization strings with keys <code>I18nBooleanModelDecorator.TRUE</code>
 * and <code>I18nBooleanModelDecorator.FALSE</code> respectively.
 */
public class I18nBooleanModelDecorator
    implements IModel/*Boolean*/
{
    private static final String I18N_KEY_PREFIX = "I18nBooleanModelDecorator";

    private IModel /*Boolean*/  nestedModel;
    private Boolean defaultValue = Boolean.FALSE;

    // CONSTRUCTION AND CONFIGURATION

    /**
     * Create a new i18n decorator.
     *
     * @param nestedModel  the model to decorate
     * @param defaultValue the value to set in the nested model if the display string being set is not recognized.
     */
    public I18nBooleanModelDecorator(IModel /*Boolean*/ nestedModel, Boolean defaultValue) {
        this.defaultValue = defaultValue;
        this.nestedModel = nestedModel;
    }

    /**
     * Create a new i18n decorator.
     *
     * @param nestedModel the model to decorate
     */
    public I18nBooleanModelDecorator(IModel /*Boolean*/ nestedModel) {
        this(nestedModel, Boolean.FALSE);
    }

    public IModel /*Boolean*/ getNestedModel() {
        return nestedModel;
    }

    public void setNestedModel(IModel /*Boolean*/ nestedModel) {
        this.nestedModel = nestedModel;
    }

    /** et the value used in the nested model if the display string being set is not recognized. */
    public Boolean getDefaultValue() {
        return defaultValue;
    }

    /** Set the value to set in the nested model if the display string being set is not recognized. */
    public void setDefaultValue(Boolean defaultValue) {
        this.defaultValue = defaultValue;
    }

    // MODEL IMPLEMENTATION

    private String getHumanReadableValue(Component component, boolean b) {
        if (b) {
            return component.getString(I18N_KEY_PREFIX + ".TRUE", nestedModel, "yes");
        } else {
            return component.getString(I18N_KEY_PREFIX + ".FALSE", nestedModel, "no");
        }
    }

    public Object getObject(final Component component) {
        Boolean b = (Boolean) nestedModel.getObject(component);
        if (b == null) return null;
        return getHumanReadableValue(component, b.booleanValue());
    }

    public void setObject(final Component component, final Object object) {
        if (object == null) nestedModel.setObject(component, null);
        String str = object.toString();
        if (getHumanReadableValue(component, true).equalsIgnoreCase(str)) {
            nestedModel.setObject(component, Boolean.TRUE);
        } else if (getHumanReadableValue(component, false).equalsIgnoreCase(str)) {
            nestedModel.setObject(component, Boolean.FALSE);
        } else {
            nestedModel.setObject(component, defaultValue);
        }
    }

    public void detach() {
        nestedModel.detach();
    }
}
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys - and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Wicket-user mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/wicket-user

Reply via email to