/* 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.localization.LocalizationUtility; import java.util.Locale; import java.util.MissingResourceException; /** * <p>Provides a slightly more customizable approach to error messages. Where the LocalizedError * class looks for an error message in a single place based on the key provided, * ScopedLocalizableError performs a scoped search for an error message.</p> * * <p>As an example, let's say that the IntegerConverter raises an error message with the values * defaultScope=<em>converter.integer</em> and key=<em>outOfRange</em>, for a field called * <em>age</em> on an ActionBean bound to <em>/cats/KittenDetail.action</em>. Based on this information * an instance of ScopedLocalizableError would fetch the resource bundle and look for error message * templates in the following order:</p> * * <ul> * <li>com.myco.KittenDetailActionBean.age.outOfRange</li> * <li>com.myco.KittenDetailActionBean.age.errorMessage</li> * <li>age.outOfRange</li> * <li>age.errorMessage</li> * <li>com.myco.KittenDetailActionBean.outOfRange</li> * <li>com.myco.KittenDetailActionBean.errorMessage</li> * <li>converter.integer.outOfRange</li> * </ul> * * <p>Using ScopingLocalizedErrors provides application developers with the flexibility to provide * as much or as little specificity in error messages as desired. The scope and ordering of the * messages is designed to allow developers to specify default messages at several levels, and * then override those as needed for specific circumstances.</p> * * @author Tim Fennell */ public class ScopedLocalizableError extends LocalizableError { private static final long serialVersionUID = 1L; /** Default key that is used for looking up error messages. */ public static final String DEFAULT_NAME = "errorMessage"; private String defaultScope; private String key; /** * Constructs a ScopedLocalizableError with the supplied information. * * @param defaultScope the default scope under which to look for the error message should more * specifically scoped lookups fail * @param key the name of the message to lookup * @param parameters an optional number of replacement parameters to be used in the message */ public ScopedLocalizableError(String defaultScope, String key, Object... parameters) { super(defaultScope + "." + key, parameters); this.defaultScope = defaultScope; this.key = key; } /** * Get the default scope that was passed into the constructor. * * @see #ScopedLocalizableError(String, String, Object...) */ public String getDefaultScope() { return defaultScope; } /** * Get the key that was passed into the constructor. * * @see #ScopedLocalizableError(String, String, Object...) */ public String getKey() { return key; } /** * Overrides getMessageTemplate to perform a scoped search for a message template as defined in * the class level javadoc. */ @Override protected String getMessageTemplate(Locale locale) { String name1, name2=null, name3=null, name4=null, name5=null, name6=null, name7=null, name8=null, name9=null, name10=null, name11=null; final String actionPath = getActionPath(); final String fqn = getBeanclass().getName(); final String fieldName = getFieldName(); // 1. com.myco.KittenDetailActionBean.age.outOfRange name1 = fqn + "." + fieldName + "." + this.key; String template = LocalizationUtility.getErrorMessage(locale, name1); if (template == null) { // 2. com.myco.KittenDetailActionBean.age.errorMessage name2 = fqn + "." + fieldName + "." + DEFAULT_NAME; template = LocalizationUtility.getErrorMessage(locale, name2); } if (template == null) { // 3. /cats/KittenDetail.action.age.outOfRange name3 = actionPath + "." + fieldName + "." + this.key; template = LocalizationUtility.getErrorMessage(locale, name3); } if (template == null) { // 4. /cats/KittenDetail.action.age.errorMessage name4 = actionPath + "." + fieldName + "." + DEFAULT_NAME; template = LocalizationUtility.getErrorMessage(locale, name4); } if (template == null) { // 5. age.outOfRange name5 = fieldName + "." + this.key; template = LocalizationUtility.getErrorMessage(locale, name5); } if (template == null) { // 6. age.errorMessage name6 = fieldName + "." + DEFAULT_NAME; template = LocalizationUtility.getErrorMessage(locale, name6); } if (template == null) { // 7. com.myco.KittenDetailActionBean.outOfRange name7 = fqn + "." + this.key; template = LocalizationUtility.getErrorMessage(locale, name7); } if (template == null) { // 8. com.myco.KittenDetailActionBean.outOfRange name8 = fqn + "." + DEFAULT_NAME; template = LocalizationUtility.getErrorMessage(locale, name8); } if (template == null) { // 9. /cats/KittenDetail.action.outOfRange name9 = actionPath + "." + this.key; template = LocalizationUtility.getErrorMessage(locale, name9); } if (template == null) { // 10. /cats/KittenDetail.action.errorMessage name10 = actionPath + "." + DEFAULT_NAME; template = LocalizationUtility.getErrorMessage(locale, name10); } if (template == null) { // 11. converter.integer.outOfRange name11 = this.defaultScope + "." + this.key; template = LocalizationUtility.getErrorMessage(locale, name11); } if (template == null) { throw new MissingResourceException( "Could not find an error message with any of the following keys: " + "'" + name1 + "', '" + name2 + "', '" + name3 + "', '" + name4 + "', '" + name5 + "', '" + name6 + "', '" + name7 + "'." + name8 + "', '" + name9 + "', '" + name10 + "', '" + name11 + "'.", null, null ); } return template; } /** Generated equals method that checks all fields 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 ScopedLocalizableError that = (ScopedLocalizableError) o; if (defaultScope != null ? !defaultScope.equals(that.defaultScope) : that.defaultScope != null) { return false; } if (key != null ? !key.equals(that.key) : that.key != null) { return false; } return true; } /** Generated hashCode() method. */ @Override public int hashCode() { int result = super.hashCode(); result = 29 * result + (defaultScope != null ? defaultScope.hashCode() : 0); result = 29 * result + (key != null ? key.hashCode() : 0); return result; } }