KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > lateralnz > common > util > MVResourceBundle


1 /* ====================================================================
2  * The LateralNZ Software License, Version 1.0
3  *
4  * Copyright (c) 2003 LateralNZ. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in
15  * the documentation and/or other materials provided with the
16  * distribution.
17  *
18  * 3. The end-user documentation included with the redistribution,
19  * if any, must include the following acknowledgment:
20  * "This product includes software developed by
21  * LateralNZ (http://www.lateralnz.org/) and other third parties."
22  * Alternately, this acknowledgment may appear in the software itself,
23  * if and wherever such third-party acknowledgments normally appear.
24  *
25  * 4. The names "LateralNZ" must not be used to endorse or promote
26  * products derived from this software without prior written
27  * permission. For written permission, please
28  * contact oss@lateralnz.org.
29  *
30  * 5. Products derived from this software may not be called "Panther",
31  * or "Lateral" or "LateralNZ", nor may "PANTHER" or "LATERAL" or
32  * "LATERALNZ" appear in their name, without prior written
33  * permission of LateralNZ.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of LateralNZ. For more
51  * information on Lateral, please see http://www.lateralnz.com/ or
52  * http://www.lateralnz.org
53  *
54  */

55 package org.lateralnz.common.util;
56
57 import java.io.File JavaDoc;
58 import java.io.IOException JavaDoc;
59 import java.io.FilenameFilter JavaDoc;
60 import java.util.Arrays JavaDoc;
61 import java.util.Comparator JavaDoc;
62 import java.util.HashMap JavaDoc;
63 import java.util.Iterator JavaDoc;
64 import java.util.Locale JavaDoc;
65 import java.util.Map JavaDoc;
66 import java.util.MissingResourceException JavaDoc;
67 import java.util.regex.*;
68
69 import org.apache.log4j.Logger;
70 import org.w3c.dom.Document JavaDoc;
71 import org.w3c.dom.Node JavaDoc;
72 import org.w3c.dom.NodeList JavaDoc;
73
74 import org.lateralnz.common.util.StringUtils;
75 import org.lateralnz.common.util.XMLUtils;
76
77 /**
78  * an XML-based resource bundle that works similar to a normal java.util.ResourceBundle,
79  * however this handles multiple variants.
80  * For example, the locale CA_fr_var1_var2_var3 would be parsed into:<br />
81  * CA_fr_var1_var2_var3<br />
82  * CA_fr_var1_var2<br />
83  * CA_fr_var1<br />
84  * CA_fr<br />
85  * CA<br />
86  * followed by the default resourcebundle.
87  * Which hopefully should explain the name: Multi Variant Resource Bundle
88  */

89 public class MVResourceBundle implements Constants {
90   private static final Logger log = Logger.getLogger(MVResourceBundle.class.getName());
91   private static final String JavaDoc BRANCH = "branch";
92   private static final String JavaDoc CLASSNAME = MVResourceBundle.class.getName();
93   private static final String JavaDoc DOT_XML = ".xml";
94   private static final String JavaDoc NAME = "name";
95   private static final String JavaDoc PATTERN_SUFFIX = "_?.*\\.xml";
96   private static final String JavaDoc PROPERTY = "property";
97   private static final String JavaDoc PROPERTIES_NODE = "properties";
98   
99   private static final Pattern PROPS_PATTERN = Pattern.compile("(\\$\\{[^}]*\\})");
100   
101   private static HashMap JavaDoc resourceBundles = new HashMap JavaDoc();
102   static {
103     ObjRefUtils.getInstance().add(resourceBundles);
104   }
105   
106   private static final Comparator JavaDoc rbcomp = new Comparator JavaDoc() {
107     public int compare(Object JavaDoc o1, Object JavaDoc o2) {
108       String JavaDoc s1 = (String JavaDoc)o1;
109       String JavaDoc s2 = (String JavaDoc)o2;
110       
111       int tok1 = StringUtils.countOccurrences(s1, '_');
112       int tok2 = StringUtils.countOccurrences(s2, '_');
113       
114       if (tok1 == tok2) {
115         return 0;
116       }
117       else if (tok1 < tok2) {
118         return -1;
119       }
120       else {
121         return 1;
122       }
123     }
124     
125     public boolean equals(Object JavaDoc o1, Object JavaDoc o2) {
126       String JavaDoc s1 = (String JavaDoc)o1;
127       String JavaDoc s2 = (String JavaDoc)o2;
128       return s1.equals(s2);
129     }
130   };
131   
132   protected HashMap JavaDoc props = null;
133   protected Locale JavaDoc thislocale = null;
134   private String JavaDoc dir;
135   private String JavaDoc name;
136   private String JavaDoc thisname;
137   private HashMap JavaDoc bundles;
138   
139   private MVResourceBundle(HashMap JavaDoc bundles, String JavaDoc dir, String JavaDoc name, String JavaDoc thisname) throws MissingResourceException JavaDoc {
140     this.dir = dir;
141     this.name = name;
142     this.thisname = thisname;
143     // 'thisname' is the resource bundle name followed by the locale string
144
// therefore we need to remove the bundle name and underscore (e.g. "mymessages_")
145
// so we substring name.length()+1
146
int pos = name.length()+1;
147     if (pos >= thisname.length()) {
148       this.thislocale = Locale.getDefault();
149     }
150     else {
151       this.thislocale = LocaleUtils.getLocale(thisname.substring(pos));
152     }
153     this.bundles = bundles;
154     
155     init();
156   }
157   
158   private void init() throws MissingResourceException JavaDoc {
159     try {
160       if (log.isInfoEnabled()) {
161         log.info("initing " + StringUtils.toDirectory(dir) + thisname + DOT_XML);
162       }
163       HashMap JavaDoc newprops = getParentProps(bundles, thisname);
164       
165       String JavaDoc xml = StringUtils.readFromFile(StringUtils.toDirectory(dir) + thisname + DOT_XML);
166       Document JavaDoc doc = XMLUtils.parse(XMLUtils.preprocess(xml));
167       
168       Node JavaDoc n = doc.getFirstChild();
169       NodeList JavaDoc nl = doc.getChildNodes();
170       NodeList JavaDoc nl2 = null;
171       for (int i = 0; i < nl.getLength(); i++) {
172         Node JavaDoc n2 = nl.item(i);
173         if (n2.getNodeName().equals(PROPERTIES_NODE)) {
174           nl2 = n2.getChildNodes();
175           break;
176         }
177       }
178       if (nl2 == null) {
179         throw new Exception JavaDoc("invalid property file");
180       }
181       
182       parse(EMPTY, newprops, nl2);
183       
184       this.props = newprops;
185     }
186     catch (Exception JavaDoc e) {
187       e.printStackTrace();
188       throw new MissingResourceException JavaDoc(e.getMessage(), CLASSNAME, "");
189     }
190     
191   }
192   
193   /**
194    * parse a list of nodes, putting the values of tag 'property' into the map
195    * as strings, and the children of 'branch' as a hashmap (recursive call back to
196    * this method)
197    */

198   private void parse(String JavaDoc branch, HashMap JavaDoc hm, NodeList JavaDoc nl) throws Exception JavaDoc {
199     for (int i = 0; i < nl.getLength(); i++) {
200       Node JavaDoc n = nl.item(i);
201       String JavaDoc name = XMLUtils.getAttributeValue(n, NAME, EMPTY);
202       if (StringUtils.isEmpty(name)) {
203         continue;
204       }
205       else if (n.getNodeName().equals(PROPERTY)) {
206         String JavaDoc val;
207         if (n.getFirstChild() != null) {
208           val = n.getFirstChild().getNodeValue();
209         }
210         else {
211           val = n.getNodeValue();
212         }
213         
214         if (!StringUtils.isEmpty(val) && val.indexOf("${") >= 0) {
215           Matcher mat = PROPS_PATTERN.matcher(val);
216
217           StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
218           while (mat.find()) {
219             String JavaDoc match = mat.group(1).substring(2, mat.group(1).length()-1);
220             String JavaDoc prop = System.getProperty(match, EMPTY);
221             if (StringUtils.isEmpty(prop)) {
222               throw new Exception JavaDoc("missing system property " + match);
223             }
224             mat.appendReplacement(sb, prop);
225           }
226           mat.appendTail(sb);
227           val = sb.toString();
228         }
229         hm.put(branch + name, val);
230       }
231       else if (n.getNodeName().equals(BRANCH)) {
232         parse(branch + name + FORWARD_SLASH, hm, n.getChildNodes());
233       }
234     }
235   }
236   
237   /**
238    * get the default bundle
239    */

240   public static final MVResourceBundle getBundle(String JavaDoc dir, String JavaDoc name) throws MissingResourceException JavaDoc {
241     return getBundle(dir, name, null);
242   }
243   
244   public static final MVResourceBundle getBundle(String JavaDoc dir, String JavaDoc name, Locale JavaDoc locale) throws MissingResourceException JavaDoc {
245     return getBundle(dir, name, EMPTY, locale);
246   }
247   
248   /**
249    * get a bundle with the specified locale. This handles multiple variants. All variants
250    * of the resource bundle are loaded if they have not already been loaded.
251    */

252   public static final MVResourceBundle getBundle(String JavaDoc dir, String JavaDoc name, String JavaDoc prevariant, Locale JavaDoc locale) throws MissingResourceException JavaDoc {
253     if (log.isDebugEnabled()) {
254       log.debug("getBundle " + dir + ", " + name + ", " + prevariant + ", " + locale);
255     }
256     
257     File JavaDoc f = new File JavaDoc(dir);
258     try {
259       dir = f.getCanonicalPath();
260     }
261     catch (IOException JavaDoc ioe) {
262       log.error(ioe);
263     }
264     
265     String JavaDoc mainkey = dir + FORWARD_SLASH + name;
266     if (!resourceBundles.containsKey(mainkey)) {
267       reload(dir, name);
268     }
269     
270     HashMap JavaDoc hm = (HashMap JavaDoc)resourceBundles.get(mainkey);
271     if (hm != null) {
272       String JavaDoc key = name;
273       if (!StringUtils.isEmpty(prevariant)) {
274         key = key + UNDERSCORE + prevariant;
275       
276         if (locale != null) {
277           key = key + UNDERSCORE + locale.getLanguage() + UNDERSCORE + locale.getCountry() + UNDERSCORE + locale.getVariant();
278         }
279       }
280       
281       if (hm.containsKey(key)) {
282         return (MVResourceBundle)hm.get(key);
283       }
284       else {
285         String JavaDoc[] split = key.split(UNDERSCORE);
286         for (int i = 0, j = split.length-1; i < split.length; i++, j--) {
287           key = StringUtils.fromArray(split, UNDERSCORE, 0, j);
288           if (hm.containsKey(key)) {
289             if (log.isDebugEnabled()) {
290               log.debug("found resources for " + key);
291             }
292             return (MVResourceBundle)hm.get(key);
293           }
294         }
295         if (hm.containsKey(name)) {
296           if (log.isDebugEnabled()) {
297             log.debug("no localised bundle, returning default for " + name);
298           }
299           return (MVResourceBundle)hm.get(name);
300         }
301       }
302     }
303     throw new MissingResourceException JavaDoc("no such bundle " + dir + FORWARD_SLASH + name, CLASSNAME, EMPTY);
304   }
305   
306   /**
307    * load all the variants of a resource bundle
308    */

309   private static final void reload(String JavaDoc dir, String JavaDoc name) throws MissingResourceException JavaDoc {
310     final String JavaDoc pat = name + PATTERN_SUFFIX;
311     FilenameFilter JavaDoc filter = new FilenameFilter JavaDoc() {
312       public boolean accept(File JavaDoc f, String JavaDoc fname) {
313         if (StringUtils.matches(fname, pat)) {
314           return true;
315         }
316         else {
317           return false;
318         }
319       }
320     };
321     
322     File JavaDoc d = new File JavaDoc(dir);
323     if (d.exists() && d.isDirectory()) {
324       String JavaDoc[] list = d.list(filter);
325       
326       // sort the list of files so that we process in order
327
// (in this way we can pick up the parent values for any variant)
328
Arrays.sort(list, rbcomp);
329       
330       if (list != null && list.length > 0) {
331         String JavaDoc key = dir + FORWARD_SLASH + name;
332         HashMap JavaDoc bundles;
333         if (!resourceBundles.containsKey(key)) {
334           synchronized (resourceBundles) {
335             if (!resourceBundles.containsKey(key)) {
336               bundles = new HashMap JavaDoc();
337               resourceBundles.put(key, bundles);
338             }
339           }
340         }
341         bundles = (HashMap JavaDoc)resourceBundles.get(key);
342         
343         synchronized (bundles) {
344           for (int i = 0; i < list.length; i++) {
345             String JavaDoc bkey = list[i].substring(0, list[i].length()-4);
346             if (!bundles.containsKey(bkey)) {
347               MVResourceBundle mvrb = new MVResourceBundle(bundles, dir, name, bkey);
348               bundles.put(bkey, mvrb);
349             }
350             else {
351               MVResourceBundle mvrb = (MVResourceBundle)bundles.get(bkey);
352               mvrb.init();
353             }
354           }
355         }
356       }
357     }
358     else {
359       throw new MissingResourceException JavaDoc("invalid resource bundle " + dir + FORWARD_SLASH + name, CLASSNAME, EMPTY);
360     }
361   }
362   
363   private static final HashMap JavaDoc getParentProps(HashMap JavaDoc bundles, String JavaDoc thisname) {
364     if (StringUtils.countOccurrences(thisname, '_') < 1) {
365       return new HashMap JavaDoc();
366     }
367     else {
368       // work backwards through the name checking for existence of a resource bundle
369
// (i.e. first look for CA_fr_var1, then CA_fr, then CA, then the default
370
String JavaDoc[] split = thisname.split(UNDERSCORE);
371       for (int i = 0, j = split.length-1; i < split.length; i++, j--) {
372         String JavaDoc key = StringUtils.fromArray(split, UNDERSCORE, 0, j);
373         if (bundles.containsKey(key)) {
374           MVResourceBundle mvrb = (MVResourceBundle)bundles.get(key);
375           return (HashMap JavaDoc)mvrb.props.clone();
376         }
377       }
378       return new HashMap JavaDoc();
379     }
380   }
381   
382   /**
383    * reload property files (note: this reloads all variants)
384    */

385   public final void reload() throws Exception JavaDoc {
386     reload(dir, name);
387   }
388   
389   public final boolean contains(String JavaDoc name) {
390     return props.containsKey(name);
391   }
392   
393   public final String JavaDoc formatString(String JavaDoc name, Object JavaDoc[] args) {
394     String JavaDoc s = getString(name);
395     if (!StringUtils.isEmpty(s)) {
396       return StringUtils.format(s, args);
397     }
398     else {
399       return s;
400     }
401   }
402   
403   public final Locale JavaDoc getLocale() {
404     return thislocale;
405   }
406   
407   public final String JavaDoc getString(String JavaDoc name) {
408     return getString(name, true);
409   }
410   
411   public final String JavaDoc getString(String JavaDoc name, boolean throwerror) {
412     if (props.containsKey(name)) {
413       return (String JavaDoc)props.get(name);
414     }
415     else if (throwerror) {
416       throw new MissingResourceException JavaDoc("no such resource '" + name + "'", CLASSNAME, name);
417     }
418     else {
419       return EMPTY;
420     }
421   }
422   
423   /**
424    * NOTE: not a particularly fast method. Be careful how this is used
425    */

426   public final Map JavaDoc getBranchAsMap(String JavaDoc branch) {
427     if (!branch.endsWith(FORWARD_SLASH)) {
428       branch = branch + FORWARD_SLASH;
429     }
430     int len = branch.length();
431     HashMap JavaDoc copy = (HashMap JavaDoc)props.clone();
432     Iterator JavaDoc iter = copy.keySet().iterator();
433     HashMap JavaDoc rtn = new HashMap JavaDoc();
434     while (iter.hasNext()) {
435       String JavaDoc key = (String JavaDoc)iter.next();
436       if (key.startsWith(branch)) {
437         rtn.put(key.substring(len), copy.get(key));
438       }
439     }
440     return rtn;
441   }
442
443 }
444
Popular Tags