001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 /* 019 * $Id: Messages.java 468654 2006-10-28 07:09:23Z minchau $ 020 */ 021 package org.apache.xml.serializer.utils; 022 023 import java.util.ListResourceBundle; 024 import java.util.Locale; 025 import java.util.MissingResourceException; 026 import java.util.ResourceBundle; 027 028 /** 029 * A utility class for issuing error messages. 030 * 031 * A user of this class normally would create a singleton 032 * instance of this class, passing the name 033 * of the message class on the constructor. For example: 034 * <CODE> 035 * static Messages x = new Messages("org.package.MyMessages"); 036 * </CODE> 037 * Later the message is typically generated this way if there are no 038 * substitution arguments: 039 * <CODE> 040 * String msg = x.createMessage(org.package.MyMessages.KEY_ONE, null); 041 * </CODE> 042 * If there are arguments substitutions then something like this: 043 * <CODE> 044 * String filename = ...; 045 * String directory = ...; 046 * String msg = x.createMessage(org.package.MyMessages.KEY_TWO, 047 * new Object[] {filename, directory) ); 048 * </CODE> 049 * 050 * The constructor of an instance of this class must be given 051 * the class name of a class that extends java.util.ListResourceBundle 052 * ("org.package.MyMessages" in the example above). 053 * The name should not have any language suffix 054 * which will be added automatically by this utility class. 055 * 056 * The message class ("org.package.MyMessages") 057 * must define the abstract method getContents() that is 058 * declared in its base class, for example: 059 * <CODE> 060 * public Object[][] getContents() {return contents;} 061 * </CODE> 062 * 063 * It is suggested that the message class expose its 064 * message keys like this: 065 * <CODE> 066 * public static final String KEY_ONE = "KEY1"; 067 * public static final String KEY_TWO = "KEY2"; 068 * . . . 069 * </CODE> 070 * and used through their names (KEY_ONE ...) rather than 071 * their values ("KEY1" ...). 072 * 073 * The field contents (returned by getContents() 074 * should be initialized something like this: 075 * <CODE> 076 * public static final Object[][] contents = { 077 * { KEY_ONE, "Something has gone wrong!" }, 078 * { KEY_TWO, "The file ''{0}'' does not exist in directory ''{1}''." }, 079 * . . . 080 * { KEY_N, "Message N" } } 081 * </CODE> 082 * 083 * Where that section of code with the KEY to Message mappings 084 * (where the message classes 'contents' field is initialized) 085 * can have the Message strings translated in an alternate language 086 * in a errorResourceClass with a language suffix. 087 * 088 * More sophisticated use of this class would be to pass null 089 * when contructing it, but then call loadResourceBundle() 090 * before creating any messages. 091 * 092 * This class is not a public API, it is only public because it is 093 * used in org.apache.xml.serializer. 094 * 095 * @xsl.usage internal 096 */ 097 public final class Messages 098 { 099 /** 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 }