001: /* ====================================================================
002: * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
003: *
004: * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * 3. The end-user documentation included with the redistribution,
019: * if any, must include the following acknowledgment:
020: * "This product includes software developed by Jcorporate Ltd.
021: * (http://www.jcorporate.com/)."
022: * Alternately, this acknowledgment may appear in the software itself,
023: * if and wherever such third-party acknowledgments normally appear.
024: *
025: * 4. "Jcorporate" and product names such as "Expresso" must
026: * not be used to endorse or promote products derived from this
027: * software without prior written permission. For written permission,
028: * please contact info@jcorporate.com.
029: *
030: * 5. Products derived from this software may not be called "Expresso",
031: * or other Jcorporate product names; nor may "Expresso" or other
032: * Jcorporate product names appear in their name, without prior
033: * written permission of Jcorporate Ltd.
034: *
035: * 6. No product derived from this software may compete in the same
036: * market space, i.e. framework, without prior written permission
037: * of Jcorporate Ltd. For written permission, please contact
038: * partners@jcorporate.com.
039: *
040: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
041: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
042: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
043: * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
044: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
045: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
046: * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
047: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
048: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
049: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
050: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
051: * SUCH DAMAGE.
052: * ====================================================================
053: *
054: * This software consists of voluntary contributions made by many
055: * individuals on behalf of the Jcorporate Ltd. Contributions back
056: * to the project(s) are encouraged when you make modifications.
057: * Please send them to support@jcorporate.com. For more information
058: * on Jcorporate Ltd. and its products, please see
059: * <http://www.jcorporate.com/>.
060: *
061: * Portions of this software are based upon other open source
062: * products and are subject to their respective licenses.
063: */
064: package com.jcorporate.expresso.core.i18n;
065:
066: import com.jcorporate.expresso.core.dbobj.Schema;
067: import com.jcorporate.expresso.core.dbobj.SchemaFactory;
068: import com.jcorporate.expresso.core.misc.StringUtil;
069: import com.jcorporate.expresso.kernel.util.FastStringBuffer;
070: import org.apache.log4j.Logger;
071:
072: import java.text.MessageFormat;
073: import java.util.HashMap;
074: import java.util.Locale;
075: import java.util.Map;
076: import java.util.ResourceBundle;
077: import java.util.WeakHashMap;
078:
079: /**
080: * A message bundle handles the messages for a specific combination of
081: * language, country and schema Created on January 21, 2001, 8:43 PM
082: */
083: public class MessageBundle extends Object {
084: /**
085: * The default bundle name
086: */
087: public static final String DEFAULT_BUNDLE_NAME = "MessagesBundle";
088: public static final String DEFAULT_BUNDLE_PATH = "com.jcorporate.expresso.core";
089:
090: /**
091: * The log4j logger
092: */
093: private static Logger log = Logger.getLogger(MessageBundle.class);
094:
095: /**
096: * Default BLANK locale
097: */
098: private static final Locale BLANK_LOCALE = new Locale("en", "us",
099: "null");
100: private static Map schemas = new HashMap();
101:
102: /**
103: * Resounce bundle name
104: */
105: private String bundleName = DEFAULT_BUNDLE_NAME;
106: private String bundlePath = null;
107: private String myCountry = null;
108: private String myLanguage = null;
109: private String mySchema = null;
110:
111: /**
112: * Bundle Map
113: */
114: private WeakHashMap bundleCache = new WeakHashMap();
115:
116: /**
117: * Creates new Class
118: */
119: public MessageBundle() {
120: }
121:
122: /* MessageBundle() */
123:
124: /**
125: * Sets the resource bundle name
126: *
127: * @param bundleName The bundle name to use when retrieving messages
128: */
129: public synchronized void setBundleName(String bundleName) {
130: this .bundleName = bundleName;
131: }
132:
133: /**
134: * Gets the resource bundle name author: Peter Pilgrim, Sun Sep 07 12:16:56
135: * BST 2003
136: *
137: * @return java.lang.String the bundle name
138: */
139: public synchronized String getBundleName() {
140: return bundleName;
141: }
142:
143: /**
144: * Sets the path for the bundle for the specified schema
145: *
146: * @param newBundlePath the bundle path
147: */
148: public synchronized void setBundlePath(String newBundlePath) {
149: bundlePath = newBundlePath;
150: }
151:
152: /* setBundlePath(String) */
153:
154: /**
155: * Returns the location of the Messages bundle path as specified by your
156: * schema.
157: *
158: * @return java.lang.String
159: */
160: public synchronized String getBundlePath() {
161: return bundlePath;
162: }
163:
164: /* getBundlePath() */
165:
166: /**
167: * Sets the country for the bundle
168: *
169: * @param newCountry the new two digit country code
170: */
171: public synchronized void setCountry(String newCountry) {
172: myCountry = newCountry;
173: }
174:
175: /* setCountry(String) */
176:
177: /**
178: * Used to get the sub-language country code
179: *
180: * @return the ISO two letter country code.
181: */
182: public String getCountry() {
183: return myCountry;
184: }
185:
186: /* getCountry() */
187:
188: /**
189: * Sets the language to get the Messages for
190: *
191: * @param newLanguage the new language code
192: */
193: public synchronized void setLanguage(String newLanguage) {
194: myLanguage = newLanguage;
195: }
196:
197: /* setLanguage(String) */
198:
199: /**
200: * @return the language code as a java.lang.String
201: */
202: public String getLanguage() {
203: return myLanguage;
204: }
205:
206: /* getLanguage() */
207:
208: /**
209: * Sets the schema so you can get messages from your own apps messages
210: * bundle
211: *
212: * @param schemaName The name of the schema to get the messages from
213: */
214: public synchronized void setSchema(String schemaName) {
215: mySchema = schemaName;
216:
217: Schema theSchema = getSchema(mySchema);
218:
219: bundlePath = theSchema.getMessageBundlePath();
220: }
221:
222: /* setSchema(String) */
223:
224: /**
225: * Formats the string code with the appropriate argument array. Issues warning log if code is not found.
226: *
227: * @param stringCode The string code to retrieve
228: * @param args the formatting arguments for the String
229: * @return The properly formatted string as read from the messages bundle, or the stringCode if not found
230: * @see java.text.MessageFormat
231: */
232: public String getString(String stringCode, Object[] args) {
233: try {
234: Locale l = null;
235:
236: if (myLanguage != null) {
237: if (myCountry.equals("default")) {
238: l = new Locale(myLanguage, "");
239: } else {
240: l = new Locale(myLanguage, myCountry);
241: }
242: } else {
243: l = Locale.getDefault();
244: }
245:
246: String messageString = getStringFromBundle(l, stringCode);
247: MessageFormat formatter = new MessageFormat("");
248: formatter.setLocale(l);
249: formatter.applyPattern(messageString);
250:
251: return StringUtil.notNull(formatter.format(args));
252: } catch (Exception se) {
253:
254: String msg = "No such key '" + stringCode
255: + "' in bundle at '" + bundlePath + "'";
256: if (log.isDebugEnabled()) {
257: msg += " with language '" + getLanguage() + "', "
258: + "Country '" + getCountry() + "'";
259: }
260: log.warn(msg);
261: return stringCode;
262: }
263: }
264:
265: /* getString(String, Object[]) */
266:
267: /**
268: * Formats the string code with the appropriate argument array, throwing an exception if
269: * the key does not exist.
270: *
271: * @param stringCode The string code to retrieve
272: * @param args the formatting arguments for the String
273: * @return The properly formatted string as read from the messages bundle
274: * @throws IllegalArgumentException if the given string code doesn't exist
275: * in the given bundle.
276: * @see java.text.MessageFormat
277: */
278: public String getStringRequired(String stringCode, Object[] args)
279: throws IllegalArgumentException {
280: try {
281: Locale locale = null;
282: String result = "";
283:
284: if (myLanguage != null) {
285: if (myCountry.equals("default")) {
286: locale = new Locale(myLanguage, "");
287: } else {
288: locale = new Locale(myLanguage, myCountry);
289: }
290: } else {
291: locale = Locale.getDefault();
292: }
293:
294: String messageString = getStringFromBundle(locale,
295: stringCode);
296: if (args != null && args.length > 0) {
297: MessageFormat formatter = new MessageFormat(
298: messageString, locale);
299: result = StringUtil.notNull(formatter.format(args));
300: } else {
301: result = messageString;
302: }
303:
304: return result;
305: } catch (Exception se) {
306: String msg = "No such key '" + stringCode
307: + "' in bundle at '" + bundlePath + "'";
308: if (log.isDebugEnabled()) {
309: msg += " with language '" + getLanguage() + "', "
310: + "Country '" + getCountry() + "'";
311: }
312: log.warn(msg);
313: throw new IllegalArgumentException(msg);
314: }
315: }
316:
317: /* getStringRequired(String, Object[]) */
318:
319: /**
320: * Instantiate & return the schema class given in the current parameter
321: *
322: * @param schemaClass The instantiated schema factory
323: * @return A Schema object instantiated from the class named by the
324: * 'SchemaClass' parameter
325: * @throws IllegalArgumentException if schema class cannot be instantiated
326: */
327: protected Schema getSchema(String schemaClass) {
328: Schema oneSchema;
329:
330: synchronized (schemas) {
331: oneSchema = (Schema) schemas.get(schemaClass);
332: }
333:
334: if (oneSchema != null) {
335: return oneSchema;
336: } else {
337: oneSchema = SchemaFactory.getInstance().getSchema(
338: schemaClass);
339:
340: if (oneSchema == null) {
341: throw new IllegalArgumentException("Can't instantiate "
342: + "Schema class " + schemaClass);
343: }
344: }
345:
346: synchronized (schemas) {
347: schemas.put(schemaClass, oneSchema);
348: }
349:
350: return oneSchema;
351: }
352:
353: /* getSchema(String) */
354:
355: /**
356: * Return a string for a specific locale
357: *
358: * @param ourLocale The specified Locale Object
359: * @param stringCode The specified String code
360: * @return The properly formatted string as read from the messages bundle; never null--will throw runtime exception if not found
361: */
362: protected String getStringFromBundle(Locale ourLocale,
363: String stringCode) {
364: String result = null;
365: try {
366: ResourceBundle rb = getBundle(ourLocale);
367: result = rb.getString(stringCode);
368: } catch (Exception e) {
369: // try default bundle if we fail in app bundle
370: if (!DEFAULT_BUNDLE_PATH.equals(bundlePath)) {
371: result = getStringFromDefaultSchemaBundle(ourLocale,
372: stringCode);
373: }
374: if (result == null) {
375: throw new RuntimeException(
376: "cannot find translated string");
377: }
378: }
379: return result;
380: }
381:
382: /**
383: * Return a string for a specific locale, using the default (expresso) schema
384: *
385: * @param ourLocale The specified Locale Object
386: * @param stringCode The specified String code
387: * @return The properly formatted string as read from the messages bundle
388: */
389: protected String getStringFromDefaultSchemaBundle(Locale ourLocale,
390: String stringCode) {
391: ResourceBundle rb = null;
392: synchronized (bundleCache) {
393: if (ourLocale != null) {
394: rb = getBundleFromPath(ourLocale, DEFAULT_BUNDLE_PATH);
395: } else {
396: rb = getBundleFromPath(BLANK_LOCALE,
397: DEFAULT_BUNDLE_PATH);
398: }
399: }
400:
401: return rb.getString(stringCode);
402: }
403:
404: /* getStringFromBundle(Locale, String) */
405:
406: /**
407: * Get the resource bundle associated with the specified schema Needs to
408: * be synchronized because of the creation elements.
409: *
410: * @param ourLocale The specified Locale Object
411: * @return a loaded resource bundle
412: */
413: private synchronized ResourceBundle getBundle(Locale ourLocale) {
414:
415: ResourceBundle rb = null;
416: synchronized (bundleCache) {
417: if (ourLocale != null) {
418: rb = getBundleFromPath(ourLocale, bundlePath);
419: } else {
420: rb = getBundleFromPath(BLANK_LOCALE, bundlePath);
421: }
422: }
423: return rb;
424: }
425:
426: /**
427: * requires external sync
428: */
429: private ResourceBundle getBundleFromPath(Locale ourLocale,
430: String path) {
431: String key = ourLocale + path;
432: ResourceBundle rb = (ResourceBundle) bundleCache.get(key);
433:
434: if (rb == null) {
435: FastStringBuffer fsb = new FastStringBuffer(path);
436: fsb.append("/" + bundleName);
437: rb = ResourceBundle.getBundle(fsb.toString(), ourLocale);
438:
439: bundleCache.put(key, rb);
440: }
441:
442: return rb;
443: }
444:
445: /**
446: * Formats the string code with the appropriate argument array. Does NOT issue warning log if code is not found, but rather returns null
447: *
448: * @param stringCode The string code to retrieve
449: * @param args the formatting arguments for the String
450: * @return The properly formatted string as read from the messages bundle, or null if not found
451: * @see java.text.MessageFormat
452: */
453: public String getStringOrNull(String stringCode, Object[] args) {
454: try {
455: Locale l = null;
456:
457: if (myLanguage != null) {
458: if (myCountry.equals("default")) {
459: l = new Locale(myLanguage, "");
460: } else {
461: l = new Locale(myLanguage, myCountry);
462: }
463: } else {
464: l = Locale.getDefault();
465: }
466:
467: String messageString = getStringFromBundle(l, stringCode);
468: MessageFormat formatter = new MessageFormat("");
469: formatter.setLocale(l);
470: formatter.applyPattern(messageString);
471:
472: return StringUtil.notNull(formatter.format(args));
473: } catch (Exception se) {
474: return null;
475: }
476: }
477: }
478:
479: /* MessageBundle */
|