/* 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.localization; import net.sourceforge.stripes.controller.ParameterName; import net.sourceforge.stripes.controller.StripesFilter; import net.sourceforge.stripes.util.Log; import net.sourceforge.stripes.validation.ValidationMetadata; import net.sourceforge.stripes.config.Configuration; import net.sourceforge.stripes.action.ActionBean; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; /** * Provides simple localization utility methods that are used in multiple places in the Stripes * code base. * * @author Tim Fennell * @since Stripes 1.1 */ public class LocalizationUtility { private static final Log log = Log.getInstance(LocalizationUtility.class); /** * <p>Fetches the localized name for a form field if one exists in the form field resource bundle. * If for any reason a localized value cannot be found (e.g. the bundle cannot be found, or * does not contain the required properties) then null will be returned.</p> * * <p>Looks first for a property called {@code beanClassFQN.fieldName} in the resource bundle. * If that is undefined, it next looks for {@code actionPath.fieldName} and * if not defined, looks for a property called {@code fieldName}. Will strip any indexing * from the field name prior to using it to construct property names (e.g. foo[12] will become * simply foo).</p> * * @param fieldName The name of the field whose localized name to look up * @param actionPath The action path of the form in which the field is nested. If for some * reason this is not available, null may be passed without causing errors. * @param locale The desired locale of the looked up name. * @return a localized field name if one can be found, or null otherwise. */ public static String getLocalizedFieldName(String fieldName, String actionPath, Class<? extends ActionBean> beanclass, Locale locale) { ParameterName parameterName = new ParameterName(fieldName); String strippedName = parameterName.getStrippedName(); String localizedValue = null; ResourceBundle bundle; try { bundle = StripesFilter.getConfiguration().getLocalizationBundleFactory() .getFormFieldBundle(locale); } catch (MissingResourceException mre) { log.error(mre); return null; } // First with the bean class if (beanclass != null) { try { localizedValue = bundle.getString(beanclass.getName() + "." + strippedName); } catch (MissingResourceException mre) { /* do nothing */ } } // Then all by itself if (localizedValue == null) { try { localizedValue = bundle.getString(strippedName); } catch (MissingResourceException mre2) { /* do nothing */ } } // Lastly, check @Validate on the ActionBean property if (localizedValue == null && beanclass != null) { ValidationMetadata validate = StripesFilter.getConfiguration() .getValidationMetadataProvider() .getValidationMetadata(beanclass, parameterName); if (validate != null && validate.label() != null && !"".equals(validate.label())) { localizedValue = validate.label(); } } return localizedValue; } /** * Makes a half hearted attempt to convert the property name of a field into a human * friendly name by breaking it on periods and upper case letters and capitalizing each word. * This is only used when developers do not provide names for their fields. * * @param fieldNameKey the programmatic name of a form field * @return String a more user friendly name for the field in the absence of anything better */ public static String makePseudoFriendlyName(String fieldNameKey) { StringBuilder builder = new StringBuilder(fieldNameKey.length() + 10); char[] characters = fieldNameKey.toCharArray(); builder.append( Character.toUpperCase(characters[0]) ); boolean upcaseNextChar = false; for (int i=1; i<characters.length; ++i) { if (characters[i] == '.') { builder.append(' '); upcaseNextChar = true; } else if (Character.isUpperCase(characters[i])) { builder.append(' ').append(characters[i]); upcaseNextChar = false; } else if (upcaseNextChar) { builder.append( Character.toUpperCase(characters[i]) ); upcaseNextChar = false; } else { builder.append(characters[i]); upcaseNextChar = false; } } return builder.toString(); } /** * Looks up the specified key in the error message resource bundle. If the * bundle is missing or if the resource cannot be found, will return null * instead of throwing an exception. * * @param locale the locale in which to lookup the resource * @param key the exact resource key to lookup * @return the resource String or null */ public static String getErrorMessage(Locale locale, String key) { try { Configuration config = StripesFilter.getConfiguration(); ResourceBundle bundle = config.getLocalizationBundleFactory().getErrorMessageBundle(locale); return bundle.getString(key); } catch (MissingResourceException mre) { return null; } } /** * Gets the simple name of a class for use as a key to look up a resource. This is usually the * same as {@link Class#getSimpleName()}, but static inner classes are handled such that the * simple name is {@code OuterClass.InnerClass}. Multiple layers of nesting are supported. * * @param c The class whose simple name is requested. * @return The simple name of the class. */ public static String getSimpleName(Class<?> c) { if (c.getEnclosingClass() == null) return c.getSimpleName(); else return prefixSimpleName(new StringBuilder(), c).toString(); } /** A recursive method used by {@link #getSimpleName(Class)}. */ private static StringBuilder prefixSimpleName(StringBuilder s, Class<?> c) { if (c.getEnclosingClass() != null) prefixSimpleName(s, c.getEnclosingClass()).append('.'); return s.append(c.getSimpleName()); } }