/* Copyright 2005-2006 Tim Fennell
*
* 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 net.sourceforge.stripes.validation;
import net.sourceforge.stripes.action.ActionBean;
import net.sourceforge.stripes.localization.LocalizationUtility;
import java.util.Locale;
import java.util.MissingResourceException;
/**
* <p>Provides a mechanism for creating localizable error messages for presentation to the user.
* Uses ResourceBundles to provide the localization of the error message. Messages may contain one
* or more "replacement parameters ". Two replacement parameters are provided by default,
* they are the field name and field value, and are indices 0 and 1 respectively. To use
* replacement parameters a message must contain the replacement token {#} where # is the numeric
* index of the replacement parameter.</p>
*
* <p>For example, to construct an error message with one additional replacement parameter which is
* the action the user was trying to perform, you might have a properties file entry like:</p>
*
* <pre>/action/MyAction.myErrorMessage={1} is not a valid {0} when trying to {2}</pre>
*
* <p>At runtime this might get replaced out to result in an error message for the user that looks
* like "<em>Fixed</em> is not a valid <em>status</em> when trying to create a new
* <em>bug</em>".</p>
*
* <p>First looks for a resource with the action bean FQN prepended to the supplied message key. If
* If that cannot be found then looks with the action path as a prefix instead of the FQN. Failing
* that, the last attempt looks for a resource with the exact message key provided. This allows
* developers to segregate their error messages by action without having to repeat the action
* path in the ActionBean. For example a message constructed with
* {@code new LocalizableError("insufficientBalance")} might look for message resources with
* the following keys:</p>
*
* <ul>
* <li>{@code com.myco.TransferActionBean.insufficientBalance}</li>
* <li>{@code /account/Transfer.action.insufficientBalance}</li>
* <li>{@code insufficientBalance}</li>
* </ul>
*
* <p>One last point of interest is where the user friendly field name comes from. Firstly an
* attempt is made to look up the localized name in the applicable resource bundle using the
* String <em>beanClassFQN.fieldName</em> where beanClassFQN is the fully qualified name of the
* bean class, and fieldName is the name of the field on the form. The second attempt is made with
* String <em>actionPath.fieldName</em> where actionPath is the action of the form in the JSP
* (or equally, the path given in the @UrlBinding annotation in the ActionBean class). Finally,
* the last attempt uses fieldName by itself.</p>
*
* @see java.text.MessageFormat
* @see java.util.ResourceBundle
*/
public class LocalizableError extends SimpleError {
private static final long serialVersionUID = 1L;
private String messageKey;
/**
* Creates a new LocalizableError with the message key provided, and optionally zero or more
* replacement parameters to use in the message. It should be noted that the replacement
* parameters provided here can be referenced in the error message <b>starting with number
* 2</b>.
*
* @param messageKey a key to lookup a message in the resource bundle
* @param parameter one or more replacement parameters to insert into the message
*/
public LocalizableError(String messageKey, Object... parameter) {
super(null, parameter);
this.messageKey = messageKey;
}
/**
* Method responsible for using the information supplied to the error object to find a
* message template. In this class this is done simply by looking up the resource
* corresponding to the messageKey supplied in the constructor, first with the FQN
* prepended, then with the action path prepended and finally bare.
*/
@Override
protected String getMessageTemplate(Locale locale) {
String template = null;
Class<? extends ActionBean> beanclass = getBeanclass();
if (beanclass != null) {
template = LocalizationUtility.getErrorMessage(locale, beanclass.getName() + "." + messageKey);
}
if (template == null) {
String actionPath = getActionPath();
if (actionPath != null) {
template = LocalizationUtility.getErrorMessage(locale, actionPath + "." + messageKey);
}
}
if (template == null) {
template = LocalizationUtility.getErrorMessage(locale, messageKey);
}
if (template == null) {
throw new MissingResourceException(
"Could not find an error message with key: " + messageKey, null, null);
}
return template;
}
/** Generated equals method that compares each field and super.equals(). */
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
final LocalizableError that = (LocalizableError) o;
if (messageKey != null ? !messageKey.equals(that.messageKey) : that.messageKey != null) {
return false;
}
return true;
}
/** Generated hashCode method. */
@Override
public int hashCode() {
int result = super.hashCode();
result = 29 * result + (messageKey != null ? messageKey.hashCode() : 0);
return result;
}
public String getMessageKey() {
return messageKey;
}
}