001: /*
002: * The contents of this file are subject to the Mozilla Public License
003: * Version 1.1 (the "License"); you may not use this file except in
004: * compliance with the License. You may obtain a copy of the License at
005: * http://www.mozilla.org/MPL/
006: *
007: * Software distributed under the License is distributed on an "AS IS"
008: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
009: * License for the specific language governing rights and limitations
010: * under the License.
011: *
012: * The Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
013: *
014: * The Initial Developer of the Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
015: * Portions created by Mark A. Kobold are Copyright (C) 2000-2007. All Rights Reserved.
016: *
017: * Contributor(s):
018: * Mark A. Kobold [mkobold <at> isqlviewer <dot> com].
019: *
020: * If you didn't download this code from the following link, you should check
021: * if you aren't using an obsolete version: http://www.isqlviewer.com
022: */
023: package org.isqlviewer.util;
024:
025: import java.text.MessageFormat;
026: import java.util.Hashtable;
027: import java.util.Locale;
028: import java.util.MissingResourceException;
029: import java.util.ResourceBundle;
030:
031: /**
032: * Create localized formated/unformatted messages from ResourceBundles.
033: * <p>
034: * This class is mainly designed to take alot of the grunt work for making an application localized while still taking
035: * advantage of the MessageFormat object as much as possible.
036: * <p>
037: * This object implements a caching mechanisim at a global level as well as local level so that effeciency is maximized
038: * for resuing message formats whenever possible.
039: *
040: * @author Markus A. Kobold.
041: * @version 1.0
042: */
043: public final class LocalMessages {
044:
045: private static Hashtable<String, ResourceBundle> bundleCache = new Hashtable<String, ResourceBundle>();
046: private static Hashtable<String, Hashtable<String, MessageFormat>> formatCache = new Hashtable<String, Hashtable<String, MessageFormat>>();
047: private static Locale systemLocale;
048: private ResourceBundle bundle;
049: private Hashtable<String, MessageFormat> compiledFormats;
050: private Locale preferredLocale;
051: private String bundleIdentifier;
052: private ClassLoader classLoader;
053: static {
054: setSystemLocale(Locale.getDefault());
055: }
056:
057: public static void setSystemLocale(Locale locale) {
058:
059: synchronized (LocalMessages.class) {
060: systemLocale = locale;
061: if (bundleCache != null) {
062: bundleCache.clear();
063: }
064: if (formatCache != null) {
065: formatCache.clear();
066: }
067: bundleCache = new Hashtable<String, ResourceBundle>();
068: formatCache = new Hashtable<String, Hashtable<String, MessageFormat>>();
069: }
070: }
071:
072: public LocalMessages(String resourceName, Class clazz) {
073:
074: this (resourceName, systemLocale, clazz.getClassLoader());
075: }
076:
077: public LocalMessages(String resourceName) {
078:
079: this (resourceName, systemLocale, Thread.currentThread()
080: .getContextClassLoader());
081: }
082:
083: public LocalMessages(String resourceName, ClassLoader classLoader) {
084:
085: this (resourceName, systemLocale, classLoader);
086: }
087:
088: /**
089: * Creates a new Message object with a given resource bundle name.
090: * <p>
091: * This will initialize the local caches and detect if the ResourceBundle has already been loaded. If a cached
092: * ResourceBundle is detected all cached MessageFormats are used as well.
093: * <p>
094: * The resourceName parameters will have the text '.properties' appended to be used as a fulle bundle name.
095: * <p>
096: * For instance: <br/><code>
097: * new Message("com.solutionary.util.resources.LocalMessages"); *
098: * </code> There
099: * would have to be a resource bundle located in '/com/solutionary/util/resource/LocalMessages.properties'
100: *
101: * @param resourceName common name for the resource bundle.
102: * @param preferredLocale the default locale to use for this bundle.
103: */
104:
105: public LocalMessages(String resourceName, Locale preferredLocale) {
106:
107: this (resourceName, preferredLocale, Thread.currentThread()
108: .getContextClassLoader());
109:
110: }
111:
112: public LocalMessages(String resourceName, Locale preferredLocale,
113: ClassLoader classLoader) {
114:
115: synchronized (LocalMessages.class) {
116: this .preferredLocale = preferredLocale;
117: this .classLoader = classLoader;
118: bundleIdentifier = resourceName;
119: ResourceBundle rb = bundleCache
120: .get(getFullBundleIdentifier(bundleIdentifier));
121: if (rb == null) {
122: init();
123: } else {
124: bundle = rb;
125: compiledFormats = formatCache
126: .get(getFullBundleIdentifier(bundleIdentifier));
127: }
128: }
129: }
130:
131: /**
132: * Gets a localized message that does not require formatting.
133: * <p>
134: *
135: * @param message the bundle key to get the localized message.
136: * @return localized message.
137: */
138: public String format(String message) {
139:
140: return getMessage(message);
141: }
142:
143: /**
144: * Formats a message from the local bundle with variant number of arguments.
145: * <p>
146: * This is the heart of this object for formatting localized messages. The locales are first synchronized to ensure
147: * that there is no contention between differnt locales.
148: * <p>
149: * Afterwards the MessgeFormat objects that have been pre-compiled in a matter of speaking are queried based on the
150: * message key, if a cached format is found then that instance is used, otherwise a new one is created to format the
151: * local message and return it and then cache the format object.
152: *
153: * @param message resource bundle key to format.
154: * @param args list of object to be used in the formatting pattern.
155: * @return formatted localized string.
156: * @see MessageFormat#format(java.lang.String, java.lang.Object[])
157: */
158: public String format(String message, Object... args) {
159:
160: if (preferredLocale != systemLocale) {
161: synchronized (LocalMessages.class) {
162: init();
163: }
164: }
165: MessageFormat mf;
166: String msg;
167: try {
168: mf = compiledFormats.get(message);
169: if (mf == null) {
170: synchronized (compiledFormats) {
171: try {
172: msg = bundle.getString(message);
173: } catch (MissingResourceException except) {
174: return '!' + message + '!';
175: }
176: mf = new MessageFormat(msg);
177: compiledFormats.put(message, mf);
178: }
179: }
180: return mf.format(args);
181: } catch (Exception except) {
182: return "An internal error occured while processing message "
183: + message;
184: }
185: }
186:
187: /**
188: * Gets a localized message that does not require formatting.
189: * <p>
190: *
191: * @param message the bundle key to get the localized message.
192: * @return localized message.
193: */
194: public String getMessage(String message) {
195:
196: if (preferredLocale != systemLocale) {
197: synchronized (LocalMessages.class) {
198: init();
199: }
200: }
201:
202: try {
203: return bundle.getString(message);
204: } catch (MissingResourceException except) {
205: return message;
206: }
207: }
208:
209: /**
210: * This will initialize caches and actually attempt to load the bundle.
211: */
212: protected void init() {
213:
214: try {
215: if (preferredLocale == null) {
216: bundle = ResourceBundle.getBundle(bundleIdentifier,
217: systemLocale, classLoader);
218: } else {
219: bundle = ResourceBundle.getBundle(bundleIdentifier,
220: preferredLocale, classLoader);
221: }
222: } catch (Exception except) {
223: throw new MissingResourceException(bundleIdentifier,
224: bundleIdentifier + "_" + preferredLocale, "");
225: }
226: bundleIdentifier = getFullBundleIdentifier(bundleIdentifier);
227: compiledFormats = new Hashtable<String, MessageFormat>();
228: bundleCache.put(bundleIdentifier, bundle);
229: formatCache.put(bundleIdentifier, compiledFormats);
230: }
231:
232: // this is a copy from the java.util.ResourceBundle. //
233: private String getFullBundleIdentifier(final String baseName) {
234:
235: String localeSuffix = preferredLocale.toString();
236: String bundleId = baseName;
237: if (localeSuffix.length() > 0) {
238: bundleId += "_" + localeSuffix;
239: } else if (preferredLocale.getVariant().length() > 0) {
240: bundleId += "___" + preferredLocale.getVariant();
241: }
242: return bundleId;
243: }
244: }
|