For my application, I wanted to use a central
properties file for the validation messages.  As
oppposed to overriding all the validators, I made
changes to AbstractValidator to allow the setting of a
global or instance based strategy object for creating
the messages.   As it might be useful or might
generate some discussion, I've attached it to this
message.  I thinks its backward compatible, but its
mostly untested (that will change over the next couple
of days).  I won't be offended if you decide its
worthless :) 

BTW, I was wondering about the dom4j dependancy.  The
only place its used is in
wicket.util.string.Strings.toString() as follows:

else if (object instanceof Node)
{
  return ((Node)object).getText();
}

Is this just an artifact?  I'm trying to keep my
application size to a minimum so I end up just
commenting it out so I can exclude the dom4j.jar.  Any
chance you might get rid of it or is there some future
plans for it?  I just can't be bothered have to
comment it out each time I grab a new release :)

-Brad
/*
 * $Id: AbstractValidator.java,v 1.28 2005/07/19 11:33:41 eelco12 Exp $
 * $Revision: 1.28 $ $Date: 2005/07/19 11:33:41 $
 * 
 * ==============================================================================
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package wicket.markup.html.form.validation;

import java.util.HashMap;
import java.util.Map;

import wicket.Localizer;
import wicket.markup.html.form.FormComponent;
import wicket.model.IModel;
import wicket.model.Model;
import wicket.util.lang.Classes;

/**
 * Base class for form component validators. This class is thread-safe and
 * therefore it is safe to share validators across sessions/threads.
 * <p>
 * Error messages can be registered on a component by calling one of the error()
 * overloads. The error message will be retrieved using the Localizer for the
 * form component. Normally, this localizer will find the error message in a
 * string resource bundle (properties file) associated with the page in which
 * this validator is contained. The resource key must be of the form:
 * [form-name].[component-name].[validator-class]. For example:
 * <p>
 * MyForm.name.RequiredValidator=A name is required.
 * <p>
 * Error message string resources can contain optional ognl variable
 * interpolations from the component, such as:
 * <p>
 * editBook.name.LengthValidator='${input}' is too short a name.
 * <p>
 * Available variables for interpolation are:
 * <ul>
 * <li>${input} - The user's input</li>
 * <li>${name} - The name of the component</li>
 * </ul>
 * but specific validator subclasses may add more values.
 * </p>
 * 
 * @author Jonathan Locke
 * @author Eelco Hillenius
 */
public abstract class AbstractValidator implements IValidator
{
	/** The form component being validated */
	private FormComponent formComponent;

	/**
	 * the validator's resource key. will be used when it was explicitly set by a user or
	 * extending validator.
	 */
	private String resourceKey = null;

        /** The global default strategy for determining messages. */
        private static IValidatorMessageStrategy defaultMessageStrategy = new DefaultMessageStrategy();
        
        /** The message strategy for determining messages. */
        private IValidatorMessageStrategy messageStrategy = defaultMessageStrategy;

        /** Default implementation of the strategy for determining messages. */
        private static final class DefaultMessageStrategy implements IValidatorMessageStrategy 
        {
            /* (non-Javadoc)
             * @see wicket.markup.html.form.validation.IValidatorMessageStrategy#getMessage(java.lang.String, wicket.markup.html.form.FormComponent, wicket.model.IModel)
             */
            public String getMessage(String resourceKey, FormComponent formComponent, IModel resourceModel)
            {
                Localizer localizer = formComponent.getLocalizer();
                return localizer.getString(resourceKey, formComponent, resourceModel);
            }

            /* (non-Javadoc)
             * @see wicket.markup.html.form.validation.IValidatorMessageStrategy#getDefaultResourceKey(wicket.markup.html.form.validation.IValidator, wicket.markup.html.form.FormComponent)
             */
            public String getDefaultResourceKey(IValidator validator, FormComponent formComponent)
            {
                // Resource key must be <form-id>.<component-name>.<validator-class>
                return formComponent.getForm().getId() + "." + formComponent.getId() + "."
                                + Classes.name(validator.getClass());
            }
        }
        
        /**
         * Globally set the message strategy for determining how messages are formatted.
         */
        public static void setDefaultMessageStrategy(IValidatorMessageStrategy messageStrategy) {
            defaultMessageStrategy = messageStrategy;
        }
        
        /**
         * Set the message strategy for determining how messages are formatted for this validator.
         */
        public AbstractValidator setMessageStrategy(IValidatorMessageStrategy messageStrategy)
        {
            this.messageStrategy = messageStrategy;
            return this;
        }

        /**
         * Get the message strategy for determining how messages are formatted for this validator.
         */
        public IValidatorMessageStrategy getMessageStrategy()
        {
            return this.messageStrategy;
        }
        
	/**
	 * Sets an error on the component being validated using the map returned by
	 * messageModel() for variable interpolations.
	 * <p>
	 * See class comments for details about how error messages are loaded and
	 * formatted.
	 */
	public void error()
	{
		error(messageModel());
	}

	/**
	 * Returns a formatted validation error message for a given component. The
	 * error message is retrieved from a message bundle associated with the page
	 * in which this validator is contained using the given resource key. The
	 * resourceModel is used for variable interpolation.
	 * 
	 * @param resourceKey
	 *            The resource key to use
	 * @param resourceModel
	 *            The model for variable interpolation
	 */
	public void error(final String resourceKey, final IModel resourceModel)
	{
		// Return formatted error message
		String message = messageStrategy.getMessage(resourceKey, formComponent, resourceModel);
		formComponent.error(message);
	}

	/**
	 * Sets an error on the component being validated using the given map for
	 * variable interpolations.
	 * 
	 * @param resourceKey
	 *            The resource key to use
	 * @param map
	 *            The model for variable interpolation
	 */
	public void error(final String resourceKey, final Map map)
	{
		error(resourceKey, Model.valueOf(map));
	}

	/**
	 * Sets an error on the component being validated using the given map for
	 * variable interpolations.
	 * 
	 * @param map
	 *            The model for variable interpolation
	 */
	public void error(final Map map)
	{
		error(resourceKey(), Model.valueOf(map));
	}

	/**
	 * @return Returns the component.
	 */
	public FormComponent getFormComponent()
	{
		return formComponent;
	}

	/**
	 * @return The string value being validated
	 */
	public String getInput()
	{
		return formComponent.getInput();
	}

	/**
	 * Explicitly set the resource key that should be used.
	 * WARNING: as resourceKey() is overrable, setting this parameter does not guarantee
	 * that extending validators honor using it.
	 * @param resourceKey the resource key
	 */
	public final void setResourceKey(String resourceKey)
	{
		this.resourceKey = resourceKey;
	}

	/**
	 * Returns the explicitly set resource key. When no resource key was explictly set
	 * (and the validator thus falls back on the <code>[form-name].[component-name].[validator-class]</code>)
	 * schema, this method returns null
	 * @return the explicitly set resource key or null
	 */
	protected final String getResourceKey()
	{
                if (resourceKey == null) {
                    resourceKey = messageStrategy.getDefaultResourceKey(this, formComponent);
                }
                return resourceKey;
	}

	/**
	 * Implemented by subclasses to validate component
	 */
	public abstract void onValidate();

	/**
	 * @see wicket.markup.html.form.validation.IValidator#validate(wicket.markup.html.form.FormComponent)
	 */
	public synchronized final void validate(final FormComponent formComponent)
	{
		// Save component
		this.formComponent = formComponent;

		// Cause validation to happen
		onValidate();
	}

	/**
	 * Gets the default variables for interpolation. These are:
	 * <ul>
	 * <li>${input}: the user's input</li>
	 * <li>${name}: the name of the component</li>
	 * </ul>
	 * 
	 * @return a map with the variables for interpolation
	 */
	protected Map messageModel()
	{
		final Map resourceModel = new HashMap(4);
		resourceModel.put("input", getInput());
		resourceModel.put("name", formComponent.getId());
		return resourceModel;
	}

	/**
	 * Gets the resource key based on the form component. It will have the form:
	 * <code>[form-name].[component-name].[validator-class]</code>
	 * 
	 * @return the resource key based on the form component
         * @deprecated use getResourceKey()
	 */
	protected String resourceKey()
	{
            return getResourceKey();
	}
}
package wicket.markup.html.form.validation;

import wicket.markup.html.form.FormComponent;
import wicket.model.IModel;

/**
 * Strategy pattern used by AbstractValidator for replacing the algorithm for 
 * determining a validation message.
 */
public interface IValidatorMessageStrategy {

    /**
     * Returns a formatted validation error message for a given component.
     * 
     * @param resourceKey
     *            The resource key to use
     * @param formComponent
     *            The component on which validation is being performed.
     * @param resourceModel
     *            The model for variable interpolation
     */
    public String getMessage(String resourceKey, FormComponent formComponent, IModel resourceModel);

    /**
     * Return the default resourceKey for this formComponent.
     * 
     * @param validator
     *            The validator instance that will use the resourceKey if its not explicitly set.
     * @param formComponent
     *            The component on which validation is being performed.
     */
    public String getDefaultResourceKey(IValidator validator, FormComponent formComponent);
}

Reply via email to