001: /*
002: ** Authored by Timothy Gerard Endres
003: ** <mailto:time@ice.com> <http://www.ice.com>
004: **
005: ** This work has been placed into the public domain.
006: ** You may use this work in any way and for any purpose you wish.
007: **
008: ** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,
009: ** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR
010: ** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY
011: ** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR
012: ** REDISTRIBUTION OF THIS SOFTWARE.
013: **
014: */
015:
016: package com.ice.util;
017:
018: import java.text.MessageFormat;
019: import java.util.Locale;
020: import java.util.Hashtable;
021: import java.util.Enumeration;
022: import java.util.ResourceBundle;
023: import java.util.MissingResourceException;
024:
025: public class FormatResourceBundle extends ResourceBundle {
026: private static Hashtable alias = new Hashtable();
027: private static Hashtable cache = new Hashtable();
028:
029: /**
030: * Set this to true to get output whenever a resource is missing.
031: * Set to false for release!
032: */
033: private boolean debug = true;
034:
035: /**
036: * The actual resource bundle that we are wrapping.
037: */
038: private ResourceBundle bundle;
039:
040: /**
041: * Add a resource name alias. This allows you to use shorter
042: * names for your resource bundles. For example:
043: * <pre>
044: * FormatResourceBundle.addAlias( "ui", "com.ice.mail.resources.ui" );
045: * ...
046: * FormatResourceBundle fmtBndl =
047: * FormatResourceBundle.getFormatBundle( "ui" );
048: * ...
049: * FormatResourceBundle.getFormatBundle( "ui" ).getString( "menuName" );
050: * </pre>
051: *
052: * The alias will only work with the getFormatBundle() class methods
053: * in this class, and will not work with the other ResourceBundle
054: * getBundle() class methods.
055: *
056: * @param alias The alias by which you will refer to this resource bundle.
057: * @param baseName The actual resource name used to get the bundle.
058: *
059: */
060:
061: public static void addAlias(String alias, String baseName) {
062: FormatResourceBundle.alias.put(alias, baseName);
063: }
064:
065: /**
066: * This class method allows you to remove a bundle from the cache.
067: * This allows the bundle to be GC-ed.
068: *
069: * @param baseName The resource bundle's name or alias.
070: */
071:
072: public static void unCache(String alias, String baseName) {
073: String name = (String) FormatResourceBundle.alias.get(baseName);
074: if (name == null)
075: name = baseName;
076: FormatResourceBundle.cache.remove(name);
077: }
078:
079: /**
080: * Get a FormatResourceBundle. This method actually calls
081: * ResourceBundle.getBundle(). However, first, the method
082: * substitutes any aliases added by the programmer, and
083: * second, the method wraps the retrieved ResourceBundle
084: * within a FormatResourceBundle adding the getFormatString()
085: * method to those already provided by ResourceBundle().
086: *
087: * @param baseName The name, or alias, of the bundle to get.
088: * @return The cached or a new FormatResource.
089: */
090: public static FormatResourceBundle getFormatBundle(String baseName) {
091: String name = (String) FormatResourceBundle.alias.get(baseName);
092: if (name == null)
093: name = baseName;
094:
095: FormatResourceBundle result = (FormatResourceBundle) FormatResourceBundle.cache
096: .get(name);
097:
098: if (result == null) {
099: ResourceBundle bundle = ResourceBundle.getBundle(name);
100:
101: if (bundle != null) {
102: result = new FormatResourceBundle(bundle);
103: FormatResourceBundle.cache.put(name, result);
104: }
105: }
106:
107: return result;
108: }
109:
110: /**
111: * Get a FormatResourceBundle. This method is identical to
112: * getFormatBundle( String baseName ), except that it takes
113: * an additional locale parameter.
114: *
115: * @param baseName The name, or alias, of the bundle to get.
116: * @param locale The locale to use in locating the bundle.
117: * @return The cached or a new FormatResource.
118: */
119: public static FormatResourceBundle getFormatBundle(String baseName,
120: Locale locale) {
121: String name = (String) FormatResourceBundle.alias.get(baseName);
122: if (name == null)
123: name = baseName;
124:
125: FormatResourceBundle result = (FormatResourceBundle) FormatResourceBundle.cache
126: .get(name);
127:
128: if (result == null) {
129: ResourceBundle bundle = ResourceBundle.getBundle(name,
130: locale);
131:
132: if (bundle != null) {
133: result = new FormatResourceBundle(bundle);
134: FormatResourceBundle.cache.put(name, result);
135: }
136: }
137:
138: return result;
139: }
140:
141: /**
142: * Construct a new FormatResourceBundle by encapsulating another
143: * ResourceBundle object which is the actual bundle.
144: *
145: * @param bundle The resource bundle that we are encapsulating.
146: */
147:
148: public FormatResourceBundle(ResourceBundle bundle) {
149: super ();
150: this .bundle = bundle;
151: }
152:
153: /**
154: * This method simply extends the ResourceBundle's getString()
155: * with a method that allows a default value to be specified.
156: *
157: * @param key The key of the string resource to format.
158: * @param defValue The default value used if key not found.
159: * @return The resource string.
160: */
161:
162: public String getString(String key, String defValue) {
163: String rsrcStr = defValue;
164:
165: try {
166: rsrcStr = this .bundle.getString(key);
167: } catch (MissingResourceException ex) {
168: if (this .debug)
169: System.err.println("MISSING RESOURCE: '" + key + "'");
170: rsrcStr = defValue;
171: }
172:
173: return rsrcStr;
174: }
175:
176: /**
177: * Get a string from the resource bundle, and return the formatted
178: * version of the string using args. This method gets the resource
179: * string identified by key, then passes the string to the
180: * MessageFormat.format() as the format string, and passes args
181: * as the format arguments.
182: *
183: * @param key The key of the string resource to format.
184: * @param args The arguments to use to format the string.
185: * @return The formatted string.
186: */
187:
188: public String getFormatString(String key, Object[] args) {
189: String fmtStr = this .bundle.getString(key);
190:
191: return (fmtStr == null) ? null : this .format(fmtStr, args);
192: }
193:
194: /**
195: * Return an enumeration of the resource keys. This method simply
196: * calls the same method of the resource bundle we are encapsulating.
197: *
198: * @return An enumeration of the resource keys.
199: */
200:
201: public Enumeration getKeys() {
202: return this .bundle.getKeys();
203: }
204:
205: /**
206: * Return an object identified by its key. This method simply
207: * calls the same method of the resource bundle we are encapsulating.
208: *
209: * @param key The key of the object to return.
210: * @return The object identified by the key.
211: */
212:
213: protected Object handleGetObject(String key) {
214: return this .bundle.getObject(key);
215: }
216:
217: /**
218: * This is a HORRIBLE HACK that is necessitated because of the
219: * terribly lazing programming displayed by the author of the
220: * core Java class 'java.text.MessageFormat'. The sloppy coding
221: * hard codes a limit of ten items that may be replaced by the
222: * MessageFormat.format() method. Incredible.
223: *
224: * Thus, we need a method to allow more general formatting. The
225: * simplest thing I could come up with was to parse up the format
226: * text so that we MessageFormat.format() each item one at a time.
227: * I am not happy with this code, but am not up to a more general
228: * solution at this time. This is depressing...
229: *
230: */
231:
232: private static String filterQuotes(String str) {
233: StringBuffer buf = new StringBuffer();
234:
235: for (;;) {
236: int idx = str.indexOf("''");
237: if (idx == -1) {
238: buf.append(str);
239: break;
240: }
241:
242: buf.append(str.substring(0, idx));
243: buf.append("'");
244: str = str.substring(idx + 2);
245: }
246:
247: return buf.toString();
248: }
249:
250: private String format(String formatStr, Object[] fmtArgs) {
251: StringBuffer result = new StringBuffer(formatStr.length() + 256);
252:
253: String workStr = formatStr;
254:
255: for (;;) {
256: int lcbIdx = workStr.indexOf("{");
257: if (lcbIdx == -1) {
258: break;
259: }
260:
261: if (lcbIdx > 0) {
262: char lqt = workStr.charAt(lcbIdx - 1);
263: char num = workStr.charAt(lcbIdx + 1);
264: char rcb = workStr.charAt(lcbIdx + 2);
265:
266: String leftStr = workStr.substring(0, lcbIdx);
267:
268: if (lqt == '\'' && num == '\'') {
269: // This is a quoted brace, put it...
270: result.append(this .filterQuotes(workStr.substring(
271: 0, lcbIdx - 1)));
272:
273: result.append("{");
274:
275: workStr = workStr.substring(lcbIdx + 2);
276: } else if ((num >= '0' && num <= '9') && rcb == '}') {
277: // This is a valid format item, to be replaced...
278: result.append(this .filterQuotes(leftStr));
279: String fmtStr = "{" + num + "}";
280: result
281: .append(MessageFormat.format(fmtStr,
282: fmtArgs));
283: workStr = workStr.substring(lcbIdx + 3);
284: } else {
285: // This is an error, I believe!
286: result.append(this .filterQuotes(leftStr));
287: result.append("ERR{ERR");
288: workStr = workStr.substring(lcbIdx + 1);
289: }
290: } else {
291: char num = workStr.charAt(1);
292: char rcb = workStr.charAt(2);
293: if (rcb == '}' && num >= '0' && num <= '9') {
294: String fmtStr = "{" + num + "}";
295: result
296: .append(MessageFormat.format(fmtStr,
297: fmtArgs));
298: workStr = workStr.substring(3);
299: } else {
300: result.append("{");
301: workStr = workStr.substring(1);
302: }
303: }
304: }
305:
306: if (workStr.length() > 0) {
307: result.append(this.filterQuotes(workStr));
308: }
309:
310: return result.toString();
311: }
312:
313: }
|