| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 flex.messaging.util; |
| |
| import java.io.InputStream; |
| import java.io.IOException; |
| import java.text.MessageFormat; |
| import java.util.Iterator; |
| import java.util.Locale; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.Set; |
| import java.util.TreeSet; |
| |
| import flex.messaging.log.Log; |
| import flex.messaging.log.Logger; |
| import flex.messaging.log.LogCategories; |
| |
| /** |
| * Implementation of <code>ResourceLoader</code> that loads string resources |
| * from property files. |
| * <p> |
| * This class uses <code>MessageFormat</code> to perform substitutions |
| * within parameterized strings. |
| * </p> |
| * |
| * @see MessageFormat |
| * |
| */ |
| public class PropertyStringResourceLoader implements ResourceLoader |
| { |
| // The property file bundle that contains localized error strings for BlazeDS. |
| public static final String PROPERTY_BUNDLE = "flex/messaging/errors"; |
| |
| // The property file bundle that contains localized error strings for BlazeDS |
| // code specific to vendors (eg. LoginCommands for specific application serves) |
| public static final String VENDORS_BUNDLE = "flex/messaging/vendors"; |
| |
| // The property file bundle that contains localized error strings for LCDS. |
| public static final String LCDS_PROPERTY_BUNDLE = "flex/data/errors"; |
| |
| // The category to write log entries under. |
| private static final String LOG_CATEGORY = LogCategories.RESOURCE; |
| |
| // The property bundle names to use in string lookups. |
| private String[] propertyBundles; |
| |
| // The default FDS locale. |
| private Locale defaultLocale; |
| |
| // The set of locales that have strings loaded. |
| private Set loadedLocales = new TreeSet(); |
| |
| // A map of all loaded strings. |
| private Map strings = new HashMap(); |
| |
| // The logger for this instance. |
| private Logger logger; |
| |
| /** |
| * Constructs a <code>PropertyStringResourceLoader</code> using the default |
| * property bundles specified by the <code>PROPERTY_BUNDLE</code> and |
| * <code>LCDS_PROPERTY_BUNDLE</code> fields. |
| */ |
| public PropertyStringResourceLoader() |
| { |
| this(new String[] {PROPERTY_BUNDLE, LCDS_PROPERTY_BUNDLE}); |
| } |
| |
| /** |
| * Constructs a <code>PropertyStringResourceLoader</code> that will use the |
| * specified property bundle to use for string lookups. |
| * |
| * @param propertyBundle The property bundles to use for lookups. |
| */ |
| public PropertyStringResourceLoader(String propertyBundle) |
| { |
| this(new String[] {propertyBundle}); |
| } |
| |
| /** |
| * Constructs a <code>PropertyStringResourceLoader</code> that will use the |
| * specified property bundles to use for string lookups. |
| * |
| * @param propertyBundles The list of the property bundles to use for lookups. |
| */ |
| public PropertyStringResourceLoader(String[] propertyBundles) |
| { |
| this.propertyBundles = propertyBundles; |
| logger = Log.getLogger(LOG_CATEGORY); |
| } |
| |
| // Implements flex.messaging.util.ResourceLoader.init; inherits javadoc specification. |
| public void init(Map properties) |
| {} |
| |
| // Implements flex.messaging.util.ResourceLoader.getString; inherits javadoc specification. |
| public String getString(String key) |
| { |
| return getString(key, null, null); |
| } |
| |
| // Implements flex.messaging.util.ResourceLoader.getString; inherits javadoc specification. |
| public String getString(String key, Object[] arguments) |
| { |
| return getString(key, null, arguments); |
| } |
| |
| // Implements flex.messaging.util.ResourceLoader.getString; inherits javadoc specification. |
| public String getString(String key, Locale locale) |
| { |
| return getString(key, locale, null); |
| } |
| |
| // Implements flex.messaging.util.ResourceLoader.getString; inherits javadoc specification. |
| public String getString(String key, Locale locale, Object[] arguments) |
| { |
| synchronized(strings) |
| { |
| if (defaultLocale == null) |
| { |
| defaultLocale = getDefaultLocale(); |
| } |
| } |
| String value = null; |
| String stringKey = null; |
| String localeKey = (locale != null) ? |
| generateLocaleKey(locale) : |
| generateLocaleKey(defaultLocale); |
| String originalStringKey = generateStringKey(key, localeKey); |
| int trimIndex = 0; |
| |
| /* |
| * Attempt to get a string for the target locale - fail back to less specific |
| * versions of the locale. |
| */ |
| while (true) |
| { |
| loadStrings(localeKey); |
| stringKey = generateStringKey(key, localeKey); |
| synchronized(strings) |
| { |
| value = (String) strings.get(stringKey); |
| if (value != null) |
| { |
| if (!stringKey.equals(originalStringKey)) |
| { |
| strings.put(originalStringKey, value); |
| } |
| return substituteArguments(value, arguments); |
| } |
| } |
| trimIndex = localeKey.lastIndexOf('_'); |
| if (trimIndex != -1) |
| { |
| localeKey = localeKey.substring(0, trimIndex); |
| } |
| else |
| { |
| break; |
| } |
| } |
| |
| /* |
| * Attempt to get the string in our default locale if it is |
| * different than the requested locale. |
| */ |
| if ((locale != null) && (!locale.equals(defaultLocale))) |
| { |
| localeKey = generateLocaleKey(defaultLocale); |
| stringKey = generateStringKey(key, localeKey); |
| synchronized(strings) |
| { |
| value = (String) strings.get(stringKey); |
| if (value != null) |
| { |
| strings.put(originalStringKey, value); |
| return substituteArguments(value, arguments); |
| } |
| } |
| } |
| |
| // As a last resort, try to get a non-locale-specific string. |
| loadStrings(""); |
| stringKey = generateStringKey(key, ""); |
| synchronized(strings) |
| { |
| value = (String) strings.get(stringKey); |
| if (value != null) |
| { |
| strings.put(originalStringKey, value); |
| return substituteArguments(value, arguments); |
| } |
| } |
| |
| // No string is available. Return a formatted missing string value. |
| return ("???" + key + "???"); |
| } |
| |
| /** |
| * Sets the default locale to be used when locating resources. The |
| * string will be converted into a Locale. |
| * |
| * @param locale The default locale to be used. |
| */ |
| public void setDefaultLocale(String locale) |
| { |
| defaultLocale = LocaleUtils.buildLocale(locale); |
| } |
| |
| /** |
| * Sets the default locale to be used when locating resources. |
| * |
| * @param locale The default locale to be used. |
| */ |
| public void setDefaultLocale(Locale locale) |
| { |
| defaultLocale = locale; |
| } |
| |
| /** |
| * The default locale to be used when locating resources. |
| * @return Locale the default Locale object |
| */ |
| public Locale getDefaultLocale() |
| { |
| if (defaultLocale == null) |
| defaultLocale = Locale.getDefault(); |
| |
| return defaultLocale; |
| } |
| |
| /** |
| * Loads localized strings for the specified locale from a property file. |
| * |
| * @param localeKey The locale to load strings for. |
| */ |
| protected synchronized void loadStrings(String localeKey) |
| { |
| if (loadedLocales.contains(localeKey)) |
| { |
| return; |
| } |
| |
| if (propertyBundles != null) |
| { |
| for (int i = 0; i < propertyBundles.length; i++) |
| { |
| String propertyBundle = propertyBundles[i]; |
| loadProperties(localeKey, propertyBundle); |
| } |
| } |
| } |
| |
| protected InputStream loadFile(String filename) |
| { |
| ClassLoader loader = Thread.currentThread().getContextClassLoader(); |
| InputStream stream = loader.getResourceAsStream(filename); |
| |
| // Try the properties file in our classloader too - just in case |
| if (stream == null) |
| { |
| stream = PropertyStringResourceLoader.class.getClassLoader().getResourceAsStream(filename); |
| } |
| |
| return stream; |
| } |
| |
| // Helper method for loadStrings. |
| protected void loadProperties(String localeKey, String propertyBundle) |
| { |
| // Build the path to the target property file. |
| String filename = propertyBundle; |
| if (localeKey.length() > 0) |
| { |
| filename += "_" + localeKey; |
| } |
| filename += ".properties"; |
| // Load the property file. |
| InputStream stream = loadFile(filename); |
| |
| Properties props = new Properties(); |
| if (stream != null) |
| { |
| try |
| { |
| props.load(stream); |
| } |
| catch (IOException ioe) |
| { |
| logger.warn("There was a problem reading the string resource property file '" + filename + "' stream.", ioe); |
| } |
| catch (IllegalArgumentException iae) |
| { |
| logger.warn("The string resource property file '" + filename + "' contains a malformed Unicode escape sequence.", iae); |
| } |
| finally |
| { |
| try |
| { |
| stream.close(); |
| } |
| catch (IOException ioe) |
| { |
| logger.warn("The string resource property file '" + filename + "' stream failed to close.", ioe); |
| } |
| } |
| } |
| else |
| { |
| logger.warn("The class loader could not locate the string resource property file '" + filename + "'. This may not be an issue if a property file is available for a less specific locale or the default locale."); |
| } |
| // Move strings into string cache. |
| if (props.size() > 0) |
| { |
| synchronized(strings) |
| { |
| Iterator iter = props.keySet().iterator(); |
| while (iter.hasNext()) |
| { |
| String key = (String) iter.next(); |
| strings.put(generateStringKey(key, localeKey), props.getProperty(key)); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Generates a locale cache key. |
| * |
| * @param locale The locale to generate a cache key for. |
| * @return The generated cache key. |
| */ |
| private String generateLocaleKey(Locale locale) |
| { |
| return (locale == null) ? "" : locale.toString(); |
| } |
| |
| /** |
| * Generates a cache key for a string resource. |
| * |
| * @param key The string to generate a cache key for. |
| * @param locale The locale to retrieve the string for. |
| * @return The generated cache key for the string resource. |
| */ |
| private String generateStringKey(String key, String locale) |
| { |
| return (key + "-" + locale); |
| } |
| |
| /** |
| * Substitutes the specified arguments into a parameterized string. |
| * |
| * @param parameterized The string containing parameter tokens for substitution. |
| * @param arguments The arguments to substitute into the parameterized string. |
| * @return The resulting substituted string. |
| */ |
| private String substituteArguments(String parameterized, Object[] arguments) |
| { |
| return MessageFormat.format(parameterized, arguments).trim(); |
| } |
| |
| } |