1 /* 2 * Copyright 2007-2013 smartics, Kronseder & Reiner GmbH 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package de.smartics.exceptions.i18n; 17 18 import java.util.Locale; 19 20 import org.apache.commons.lang.StringUtils; 21 22 import de.smartics.exceptions.AbstractCoreRuntimeException; 23 import de.smartics.exceptions.core.Code; 24 import de.smartics.exceptions.i18n.message.LocalizedInfo; 25 import de.smartics.exceptions.i18n.message.MessageType; 26 import de.smartics.exceptions.i18n.message.Messages; 27 28 /** 29 * The base implementation of exceptions that support internationalization for 30 * unchecked exceptions. 31 */ 32 public abstract class AbstractLocalizedRuntimeException extends 33 AbstractCoreRuntimeException implements LocalizedException 34 { 35 // ********************************* Fields ********************************* 36 37 // --- constants ------------------------------------------------------------ 38 39 /** 40 * The class version identifier. 41 * <p> 42 * The value of this constant is {@value}. 43 * </p> 44 */ 45 private static final long serialVersionUID = 1L; 46 47 // ... resource key ......................................................... 48 49 // --- members -------------------------------------------------------------- 50 51 /** 52 * The localized information controls the localization information. 53 * 54 * @serial 55 */ 56 protected final LocalizedInfo localizedInfo; 57 58 // ****************************** Initializer ******************************* 59 60 // ****************************** Constructors ****************************** 61 62 /** 63 * Constructor. 64 * 65 * @param code the error or exception code of the exception. 66 * @see #AbstractLocalizedRuntimeException(Throwable,Code) 67 */ 68 protected AbstractLocalizedRuntimeException(final Code code) 69 { 70 this(null, code); 71 } 72 73 /** 74 * Constructor. 75 * 76 * @param code the error or exception code of the exception. 77 * @param bundleBaseName the fully qualified name of the bundle to use. 78 * @see #AbstractLocalizedRuntimeException(Throwable,Code,String) 79 */ 80 protected AbstractLocalizedRuntimeException(final Code code, 81 final String bundleBaseName) 82 { 83 this(null, code, bundleBaseName); 84 } 85 86 /** 87 * Constructor. 88 * 89 * @param cause the cause (which is saved for later retrieval by the 90 * {@link #getCause()} method). (A <tt>null</tt> value is permitted, 91 * and indicates that the cause is nonexistent or unknown.) 92 * @param code the error or exception code of the exception. 93 * @see #AbstractLocalizedRuntimeException(Throwable,Code,String) 94 */ 95 protected AbstractLocalizedRuntimeException(final Throwable cause, 96 final Code code) 97 { 98 this(cause, code, null); 99 } 100 101 /** 102 * Constructor. 103 * 104 * @param cause the cause (which is saved for later retrieval by the 105 * {@link #getCause()} method). (A <tt>null</tt> value is permitted, 106 * and indicates that the cause is nonexistent or unknown.) 107 * @param code the error or exception code of the exception. 108 * @param bundleBaseName the fully qualified name of the bundle to use. 109 * @see #AbstractLocalizedRuntimeException(Throwable,Code,String,String) 110 */ 111 protected AbstractLocalizedRuntimeException(final Throwable cause, 112 final Code code, final String bundleBaseName) 113 { 114 this(cause, code, bundleBaseName, null); 115 } 116 117 /** 118 * Constructor. 119 * 120 * @param cause the cause (which is saved for later retrieval by the 121 * {@link #getCause()} method). (A <tt>null</tt> value is permitted, 122 * and indicates that the cause is nonexistent or unknown.) 123 * @param code the error or exception code of the exception. 124 * @param bundleBaseName the fully qualified name of the bundle to use. 125 * @param resourceKey the localization key to fetch messages from message 126 * bundles. 127 * @see AbstractCoreRuntimeException#AbstractCoreRuntimeException(String, 128 * Throwable, Code) 129 */ 130 protected AbstractLocalizedRuntimeException(final Throwable cause, 131 final Code code, final String bundleBaseName, final String resourceKey) 132 { 133 super(null, cause, code); 134 this.localizedInfo = 135 new LocalizedInfo(code, cause, bundleBaseName, resourceKey); 136 } 137 138 // ****************************** Inner Classes ***************************** 139 140 // ********************************* Methods ******************************** 141 142 // --- init ----------------------------------------------------------------- 143 144 // --- get&set -------------------------------------------------------------- 145 146 /** 147 * Returns the localization key to fetch messages from message bundles. If 148 * this value is not set, the {@link String} representation of the 149 * {@link #getCode()} instance is used. 150 * 151 * @return the localization key to fetch messages from message bundles. 152 */ 153 public final String getResourceKey() 154 { 155 return localizedInfo.getResourceKey(); 156 } 157 158 /** 159 * Returns the fully qualified base name of the bundle to use. 160 * 161 * @return the fully qualified base name of the bundle to use. 162 */ 163 public final String getBundleBaseName() 164 { 165 return localizedInfo.getBundleBaseName(); 166 } 167 168 // --- business ------------------------------------------------------------- 169 170 // ... get message .......................................................... 171 172 /** 173 * Returns the message for the given message type and the system's default 174 * locale. 175 * 176 * @param messageType the type if message to return. 177 * @return the message for the given message type. 178 */ 179 @Override 180 public final String getMessage(final MessageType messageType) 181 { 182 final Locale locale = Locale.getDefault(); 183 return getMessage(locale, messageType); 184 } 185 186 /** 187 * {@inheritDoc} 188 * <p> 189 * Returns the localized message for the default locale. 190 * 191 * @see java.lang.Throwable#getMessage() 192 */ 193 @Override 194 public final String getMessage() 195 { 196 return getLocalizedMessage(); 197 } 198 199 /** 200 * Returns the message for the given message type. 201 * 202 * @param locale the locale to select the localized message. 203 * @param messageType the type if message to return. 204 * @return the message for the given message type. 205 */ 206 public final String getMessage(final Locale locale, 207 final MessageType messageType) 208 { 209 final String resourceKey = localizedInfo.getResourceKey(); 210 return getLocalizedMessage(resourceKey, locale, messageType, null); // , 211 // null 212 // ); 213 } 214 215 // ... throwable standard message 216 217 /** 218 * Returns the localized message according to the system's default locale. 219 * <p> 220 * {@inheritDoc} 221 */ 222 @Override 223 public final String getLocalizedMessage() 224 { 225 return getLocalizedMessage(Locale.getDefault()); 226 } 227 228 /** 229 * Returns the localized message for the given locale. 230 * 231 * @param locale the locale for which the message is requested. 232 * @return returns the localized message of this exception. 233 */ 234 public final String getLocalizedMessage(final Locale locale) 235 { 236 return getLocalizedMessage(locale, null); // , null); 237 } 238 239 /** 240 * Returns the localized message for the given locale. 241 * 242 * @param locale the locale for which the message is requested. 243 * @param loader the loader to read the message bundle. 244 * @return returns the localized message of this exception. 245 */ 246 public final String getLocalizedMessage(final Locale locale, 247 final ClassLoader loader) 248 { 249 final String keyPrefix = getCode().getCode(); 250 return getLocalizedMessage(keyPrefix, locale, MessageType.SUMMARY, loader); 251 } 252 253 // /** 254 // * Returns the localized message for the given locale. 255 // * 256 // * @param locale the locale for which the message is requested. 257 // * @param control the controller to load the resource bundle. 258 // * @return returns the localized message of this exception. 259 // * @todo Should we really provide these methods with class loader and 260 // control? 261 // * What is the user scenario for this? 262 // */ 263 // public String getLocalizedMessage( 264 // final Locale locale, 265 // final ResourceBundle.Control control) 266 // { 267 // return getLocalizedMessage(locale, null, control); 268 // } 269 270 // /** 271 // * Returns the localized message for the given locale. 272 // * 273 // * @param locale the locale for which the message is requested. 274 // * @param loader the loader to read the message bundle. 275 // * @param control the controller to load the resource bundle. 276 // * @return returns the localized message of this exception. 277 // */ 278 // public String getLocalizedMessage( 279 // final Locale locale, 280 // final ClassLoader loader, 281 // final ResourceBundle.Control control) 282 // { 283 // final String message = messageBean.getLocalizedMessage(this, locale, 284 // loader, control); 285 // return message; 286 // } 287 288 // ... fetcher for all messages 289 290 /** 291 * Returns the localized message for the given locale. 292 * 293 * @param keyPrefix the prefix of the key to load from the bundle. 294 * @param locale the locale for which the message is requested. 295 * @param messageType the type of message to localize. 296 * @param loader the loader to read the message bundle. 297 * @return returns the localized message of this exception. 298 * @todo Should we really provide these methods with class loader and control? 299 * What is the user scenario for this? 300 */ 301 public final String getLocalizedMessage(final String keyPrefix, 302 final Locale locale, final MessageType messageType, 303 final ClassLoader loader) // , 304 // final ResourceBundle.Control control) 305 { 306 final String message = 307 localizedInfo.getLocalizedMessage(this, keyPrefix, locale, messageType, 308 loader); // , control); 309 return message; 310 } 311 312 @Override 313 public final String getMessages() 314 { 315 return getMessages(Locale.getDefault()); 316 } 317 318 @Override 319 public final String getMessages(final Locale locale) 320 { 321 final StringBuilder buffer = new StringBuilder(); 322 for (MessageType type : MessageType.values()) 323 { 324 buffer.append(type.name()).append('=').append(getMessage(locale, type)) 325 .append(' '); 326 } 327 return StringUtils.chop(buffer.toString()); 328 } 329 330 @Override 331 public final Messages createMessages() 332 { 333 return createMessages(Locale.getDefault()); 334 } 335 336 @Override 337 public final Messages createMessages(final Locale locale) 338 { 339 final Messages.Builder builder = new Messages.Builder(); 340 final String keyPrefix = getCode().getCode(); 341 for (MessageType type : MessageType.values()) 342 { 343 builder.put(type, localizedInfo.createLocalizedMessage(this, keyPrefix, 344 locale, type, null)); 345 } 346 347 return builder.build(); 348 } 349 350 @Override 351 public final CauseTrailMessages getCauseTrail() 352 { 353 return getCauseTrail(Locale.getDefault()); 354 } 355 356 @Override 357 public final CauseTrailMessages getCauseTrail(final Locale locale) 358 { 359 return localizedInfo.getCauseTrail(locale); 360 } 361 362 // --- object basics -------------------------------------------------------- 363 364 /** 365 * Returns the string representation of the exception. 366 * <p> 367 * May be overridden by subclasses in an application (not a library) to change 368 * the string representation. Usually an implementation of 369 * {@code I18nCodeMessageFormatter} should be set to the 370 * {@link de.smartics.exceptions.core.ExceptionContext}. 371 * </p> 372 * 373 * @return the string representation of the object. 374 */ 375 @Override 376 public String toString() 377 { 378 final I18nCodeMessageFormatter formatter = 379 I18nExceptionContextManager.getFormatter(Thread.currentThread() 380 .getContextClassLoader()); 381 final String string = formatter.format(this); 382 return string; 383 } 384 }