View Javadoc

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 }