• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the  "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 /*
19  * $Id: Messages.java 468654 2006-10-28 07:09:23Z minchau $
20  */
21 package org.apache.xml.serializer.utils;
22 
23 import java.util.ListResourceBundle;
24 import java.util.Locale;
25 import java.util.MissingResourceException;
26 import java.util.ResourceBundle;
27 
28 /**
29  * A utility class for issuing error messages.
30  *
31  * A user of this class normally would create a singleton
32  * instance of this class, passing the name
33  * of the message class on the constructor. For example:
34  * <CODE>
35  * static Messages x = new Messages("org.package.MyMessages");
36  * </CODE>
37  * Later the message is typically generated this way if there are no
38  * substitution arguments:
39  * <CODE>
40  * String msg = x.createMessage(org.package.MyMessages.KEY_ONE, null);
41  * </CODE>
42  * If there are arguments substitutions then something like this:
43  * <CODE>
44  * String filename = ...;
45  * String directory = ...;
46  * String msg = x.createMessage(org.package.MyMessages.KEY_TWO,
47  *   new Object[] {filename, directory) );
48  * </CODE>
49  *
50  * The constructor of an instance of this class must be given
51  * the class name of a class that extends java.util.ListResourceBundle
52  * ("org.package.MyMessages" in the example above).
53  * The name should not have any language suffix
54  * which will be added automatically by this utility class.
55  *
56  * The message class ("org.package.MyMessages")
57  * must define the abstract method getContents() that is
58  * declared in its base class, for example:
59  * <CODE>
60  * public Object[][] getContents() {return contents;}
61  * </CODE>
62  *
63  * It is suggested that the message class expose its
64  * message keys like this:
65  * <CODE>
66  *   public static final String KEY_ONE = "KEY1";
67  *   public static final String KEY_TWO = "KEY2";
68  *   . . .
69  * </CODE>
70  * and used through their names (KEY_ONE ...) rather than
71  * their values ("KEY1" ...).
72  *
73  * The field contents (returned by getContents()
74  * should be initialized something like this:
75  * <CODE>
76  * public static final Object[][] contents = {
77  * { KEY_ONE, "Something has gone wrong!" },
78  * { KEY_TWO, "The file ''{0}'' does not exist in directory ''{1}''." },
79  * . . .
80  * { KEY_N, "Message N" }  }
81  * </CODE>
82  *
83  * Where that section of code with the KEY to Message mappings
84  * (where the message classes 'contents' field is initialized)
85  * can have the Message strings translated in an alternate language
86  * in a errorResourceClass with a language suffix.
87  *
88  * More sophisticated use of this class would be to pass null
89  * when contructing it, but then call loadResourceBundle()
90  * before creating any messages.
91  *
92  * This class is not a public API, it is only public because it is
93  * used in org.apache.xml.serializer.
94  *
95  *  @xsl.usage internal
96  */
97 public final class Messages
98 {
99     /** The local object to use.  */
100     private final Locale m_locale = Locale.getDefault();
101 
102     /** The language specific resource object for messages.  */
103     private ListResourceBundle m_resourceBundle;
104 
105     /** The class name of the error message string table with no language suffix. */
106     private String m_resourceBundleName;
107 
108 
109 
110     /**
111      * Constructor.
112      * @param resourceBundle the class name of the ListResourceBundle
113      * that the instance of this class is associated with and will use when
114      * creating messages.
115      * The class name is without a language suffix. If the value passed
116      * is null then loadResourceBundle(errorResourceClass) needs to be called
117      * explicitly before any messages are created.
118      *
119      * @xsl.usage internal
120      */
Messages(String resourceBundle)121     Messages(String resourceBundle)
122     {
123 
124         m_resourceBundleName = resourceBundle;
125     }
126 
127     /*
128      * Set the Locale object to use. If this method is not called the
129      * default locale is used. This method needs to be called before
130      * loadResourceBundle().
131      *
132      * @param locale non-null reference to Locale object.
133      * @xsl.usage internal
134      */
135 //    public void setLocale(Locale locale)
136 //    {
137 //        m_locale = locale;
138 //    }
139 
140     /**
141      * Get the Locale object that is being used.
142      *
143      * @return non-null reference to Locale object.
144      * @xsl.usage internal
145      */
getLocale()146     private Locale getLocale()
147     {
148         return m_locale;
149     }
150 
151     /**
152      * Get the ListResourceBundle being used by this Messages instance which was
153      * previously set by a call to loadResourceBundle(className)
154      * @xsl.usage internal
155      */
getResourceBundle()156     private ListResourceBundle getResourceBundle()
157     {
158         return m_resourceBundle;
159     }
160 
161     /**
162      * Creates a message from the specified key and replacement
163      * arguments, localized to the given locale.
164      *
165      * @param msgKey  The key for the message text.
166      * @param args    The arguments to be used as replacement text
167      * in the message created.
168      *
169      * @return The formatted message string.
170      * @xsl.usage internal
171      */
createMessage(String msgKey, Object args[])172     public final String createMessage(String msgKey, Object args[])
173     {
174         if (m_resourceBundle == null)
175             m_resourceBundle = loadResourceBundle(m_resourceBundleName);
176 
177         if (m_resourceBundle != null)
178         {
179             return createMsg(m_resourceBundle, msgKey, args);
180         }
181         else
182             return "Could not load the resource bundles: "+ m_resourceBundleName;
183     }
184 
185     /**
186      * Creates a message from the specified key and replacement
187      * arguments, localized to the given locale.
188      *
189      * @param errorCode The key for the message text.
190      *
191      * @param fResourceBundle The resource bundle to use.
192      * @param msgKey  The message key to use.
193      * @param args      The arguments to be used as replacement text
194      *                  in the message created.
195      *
196      * @return The formatted message string.
197      * @xsl.usage internal
198      */
createMsg( ListResourceBundle fResourceBundle, String msgKey, Object args[])199     private final String createMsg(
200         ListResourceBundle fResourceBundle,
201         String msgKey,
202         Object args[]) //throws Exception
203     {
204 
205         String fmsg = null;
206         boolean throwex = false;
207         String msg = null;
208 
209         if (msgKey != null)
210             msg = fResourceBundle.getString(msgKey);
211         else
212             msgKey = "";
213 
214         if (msg == null)
215         {
216             throwex = true;
217             /* The message is not in the bundle . . . this is bad,
218              * so try to get the message that the message is not in the bundle
219              */
220             try
221             {
222 
223                 msg =
224                     java.text.MessageFormat.format(
225                         MsgKey.BAD_MSGKEY,
226                         new Object[] { msgKey, m_resourceBundleName });
227             }
228             catch (Exception e)
229             {
230                 /* even the message that the message is not in the bundle is
231                  * not there ... this is really bad
232                  */
233                 msg =
234                     "The message key '"
235                         + msgKey
236                         + "' is not in the message class '"
237                         + m_resourceBundleName+"'";
238             }
239         }
240         else if (args != null)
241         {
242             try
243             {
244                 // Do this to keep format from crying.
245                 // This is better than making a bunch of conditional
246                 // code all over the place.
247                 int n = args.length;
248 
249                 for (int i = 0; i < n; i++)
250                 {
251                     if (null == args[i])
252                         args[i] = "";
253                 }
254 
255                 fmsg = java.text.MessageFormat.format(msg, args);
256                 // if we get past the line above we have create the message ... hurray!
257             }
258             catch (Exception e)
259             {
260                 throwex = true;
261                 try
262                 {
263                     // Get the message that the format failed.
264                     fmsg =
265                         java.text.MessageFormat.format(
266                             MsgKey.BAD_MSGFORMAT,
267                             new Object[] { msgKey, m_resourceBundleName });
268                     fmsg += " " + msg;
269                 }
270                 catch (Exception formatfailed)
271                 {
272                     // We couldn't even get the message that the format of
273                     // the message failed ... so fall back to English.
274                     fmsg =
275                         "The format of message '"
276                             + msgKey
277                             + "' in message class '"
278                             + m_resourceBundleName
279                             + "' failed.";
280                 }
281             }
282         }
283         else
284             fmsg = msg;
285 
286         if (throwex)
287         {
288             throw new RuntimeException(fmsg);
289         }
290 
291         return fmsg;
292     }
293 
294     /**
295      * Return a named ResourceBundle for a particular locale.  This method mimics the behavior
296      * of ResourceBundle.getBundle().
297      *
298      * @param className the name of the class that implements ListResourceBundle,
299      * without language suffix.
300      * @return the ResourceBundle
301      * @throws MissingResourceException
302      * @xsl.usage internal
303      */
loadResourceBundle(String resourceBundle)304     private ListResourceBundle loadResourceBundle(String resourceBundle)
305         throws MissingResourceException
306     {
307         m_resourceBundleName = resourceBundle;
308         Locale locale = getLocale();
309 
310         ListResourceBundle lrb;
311 
312         try
313         {
314 
315             ResourceBundle rb =
316                 ResourceBundle.getBundle(m_resourceBundleName, locale);
317             lrb = (ListResourceBundle) rb;
318         }
319         catch (MissingResourceException e)
320         {
321             try // try to fall back to en_US if we can't load
322                 {
323 
324                 // Since we can't find the localized property file,
325                 // fall back to en_US.
326                 lrb =
327                     (ListResourceBundle) ResourceBundle.getBundle(
328                         m_resourceBundleName,
329                         new Locale("en", "US"));
330             }
331             catch (MissingResourceException e2)
332             {
333 
334                 // Now we are really in trouble.
335                 // very bad, definitely very bad...not going to get very far
336                 throw new MissingResourceException(
337                     "Could not load any resource bundles." + m_resourceBundleName,
338                     m_resourceBundleName,
339                     "");
340             }
341         }
342         m_resourceBundle = lrb;
343         return lrb;
344     }
345 
346     /**
347      * Return the resource file suffic for the indicated locale
348      * For most locales, this will be based the language code.  However
349      * for Chinese, we do distinguish between Taiwan and PRC
350      *
351      * @param locale the locale
352      * @return an String suffix which can be appended to a resource name
353      * @xsl.usage internal
354      */
getResourceSuffix(Locale locale)355     private static String getResourceSuffix(Locale locale)
356     {
357 
358         String suffix = "_" + locale.getLanguage();
359         String country = locale.getCountry();
360 
361         if (country.equals("TW"))
362             suffix += "_" + country;
363 
364         return suffix;
365     }
366 }
367