KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > vladium > util > exception > ExceptionCommon


1 /* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved.
2  *
3  * This program and the accompanying materials are made available under
4  * the terms of the Common Public License v1.0 which accompanies this distribution,
5  * and is available at http://www.eclipse.org/legal/cpl-v10.html
6  *
7  * $Id: ExceptionCommon.java,v 1.1.1.1.2.2 2004/07/10 03:34:52 vlad_r Exp $
8  */

9 package com.vladium.util.exception;
10
11 import java.io.PrintStream JavaDoc;
12 import java.io.PrintWriter JavaDoc;
13 import java.text.MessageFormat JavaDoc;
14 import java.util.Collections JavaDoc;
15 import java.util.Enumeration JavaDoc;
16 import java.util.HashMap JavaDoc;
17 import java.util.Locale JavaDoc;
18 import java.util.Map JavaDoc;
19 import java.util.ResourceBundle JavaDoc;
20
21 import com.vladium.util.IJREVersion;
22
23 // TODO: embed build # in error codes
24

25 // ----------------------------------------------------------------------------
26
/**
27  * TODO: javadoc
28  *
29  * Based on code <a HREF="http://www.fawcette.com/javapro/2002_12/online/exception_vroubtsov_12_16_02/default_pf.asp">published</a>
30  * by me in JavaPro, 2002.<P>
31  *
32  * This non-instantiable class provides static support functions common to
33  * {@link AbstractException} and {@link AbstractRuntimeException}.<P>
34  *
35  * @author Vlad Roubtsov, (C) 2002
36  */

37 abstract class ExceptionCommon implements IJREVersion
38 {
39     // public: ................................................................
40

41     /**
42      * This method can be called by static initializers of {@link AbstractException}
43      * and {@link AbstractRuntimeException} subclasses in order to add another
44      * resource bundle to the set that is used to look up error codes. This makes
45      * it possible to extend the set of exception error codes across independently
46      * maintained and built projects.<P>
47      *
48      * <BLOCKQUOTE>
49      * Note that this introduces a possibility of error code name clashes. This
50      * is resolved in the following way:
51      * <UL>
52      * <LI> when <CODE>getMessage(namespace, code)</CODE> is called, 'code'
53      * is attempted to be looked up in the resource bundle previously keyed
54      * under 'namespace';
55      *
56      * <LI> if no such bundle it found or if it does not contain a value for
57      * key 'code', the same step is repeated for the superclass of 'namespace';
58      *
59      * <LI> finally, if all of the above steps fail, the root resource bundle
60      * specified by {@link #ROOT_RESOURCE_BUNDLE_NAME} is searched.
61      * </UL>
62      *
63      * This strategy ensures that error codes follow inheritance and hiding rules
64      * similar to Java static methods.<P>
65      * </BLOCKQUOTE>
66      *
67      * <B>IMPORTANT:</B> this method must be called from static class initializers
68      * <I>only</I>.<P>
69      *
70      * There is no visible state change if the indicated resource is not found
71      * or if it has been added already under the same key.<P>
72      *
73      * @param namespace the Class object acting as the namespace key for the
74      * resource bundle identified by 'messageResourceBundleName'. <I>This should
75      * be the calling class.</I> [the method is a no-op if this is null]
76      *
77      * @param messageResourceBundleName name of a bundle (path relative to 'namespace'
78      * package) to add to the set from which error code mappings are retrieved
79      * [the method is a no-op if this is null or an empty string]
80      *
81      * @return ResourceBundle that corresponds to 'namespace' key or null if
82      * no such bundle could be loaded
83      *
84      * @throws Error if 'namespace' does not correspond to an exception class derived
85      * from {@link AbstractException} or {@link AbstractRuntimeException}.
86      *
87      * @see #lookup
88      */

89     public static ResourceBundle JavaDoc addExceptionResource (final Class JavaDoc namespace,
90                                                        final String JavaDoc messageResourceBundleName)
91     {
92         if ((namespace != null) && (messageResourceBundleName != null)
93             && (messageResourceBundleName.length () > 0))
94         {
95             // bail out if the some other exception hierarchy attempts
96
// to use this functionality:
97
if (! ABSTRACT_EXCEPTION.isAssignableFrom (namespace)
98                 && ! ABSTACT_RUNTIME_EXCEPTION.isAssignableFrom (namespace))
99             {
100                 throw new Error JavaDoc ("addExceptionResource(): class [" + namespace +
101                     "] is not a subclass of " + ABSTRACT_EXCEPTION.getName () +
102                     " or " + ABSTACT_RUNTIME_EXCEPTION.getName ());
103             }
104             
105             // try to load resource bundle
106

107             ResourceBundle JavaDoc temprb = null;
108             String JavaDoc nameInNamespace = null;
109             try
110             {
111                 nameInNamespace = getNameInNamespace (namespace, messageResourceBundleName);
112                 
113                 //temprb = ResourceBundle.getBundle (nameInNamespace);
114

115                 ClassLoader JavaDoc loader = namespace.getClassLoader ();
116                 if (loader == null) loader = ClassLoader.getSystemClassLoader ();
117                 
118                 temprb = ResourceBundle.getBundle (nameInNamespace, Locale.getDefault (), loader);
119             }
120             catch (Throwable JavaDoc ignore)
121             {
122                // ignored intentionally: if the exception codes rb is absent,
123
// we are still in a supported configuration
124
temprb = null;
125             }
126             
127             if (temprb != null)
128             {
129                 synchronized (s_exceptionCodeMap)
130                 {
131                     final ResourceBundle JavaDoc currentrb =
132                         (ResourceBundle JavaDoc) s_exceptionCodeMap.get (namespace);
133                     if (currentrb != null)
134                         return currentrb;
135                     else
136                     {
137                         s_exceptionCodeMap.put (namespace, temprb);
138                         return temprb;
139                     }
140                 }
141             }
142         }
143         
144         return null;
145     }
146     
147     // protected: .............................................................
148

149     // package: ...............................................................
150

151
152     static void printStackTrace (Throwable JavaDoc t, final PrintWriter JavaDoc out)
153     {
154         if (JRE_1_4_PLUS)
155         {
156             if (t instanceof IThrowableWrapper)
157             {
158                 final IThrowableWrapper tw = (IThrowableWrapper) t;
159                 
160                 tw.__printStackTrace (out);
161             }
162             else
163             {
164                 t.printStackTrace (out);
165             }
166         }
167         else
168         {
169             for (boolean first = true; t != null; )
170             {
171                 if (first)
172                     first = false;
173                 else
174                 {
175                     out.println ();
176                     out.println (NESTED_THROWABLE_HEADER);
177                 }
178                 
179                 if (t instanceof IThrowableWrapper)
180                 {
181                     final IThrowableWrapper tw = (IThrowableWrapper) t;
182                     
183                     tw.__printStackTrace (out);
184                     t = tw.getCause ();
185                 }
186                 else
187                 {
188                     t.printStackTrace (out);
189                     break;
190                 }
191             }
192         }
193     }
194     
195     
196     static void printStackTrace (Throwable JavaDoc t, final PrintStream JavaDoc out)
197     {
198         if (JRE_1_4_PLUS)
199         {
200             if (t instanceof IThrowableWrapper)
201             {
202                 final IThrowableWrapper tw = (IThrowableWrapper) t;
203                 
204                 tw.__printStackTrace (out);
205             }
206             else
207             {
208                 t.printStackTrace (out);
209             }
210         }
211         else
212         {
213             for (boolean first = true; t != null; )
214             {
215                 if (first)
216                     first = false;
217                 else
218                 {
219                     out.println ();
220                     out.println (NESTED_THROWABLE_HEADER);
221                 }
222                 
223                 if (t instanceof IThrowableWrapper)
224                 {
225                     final IThrowableWrapper tw = (IThrowableWrapper) t;
226                     
227                     tw.__printStackTrace (out);
228                     t = tw.getCause ();
229                 }
230                 else
231                 {
232                     t.printStackTrace (out);
233                     break;
234                 }
235             }
236         }
237     }
238     
239
240     /**
241      * Provides support for lookup of exception error codes from {@link AbstractException}
242      * and {@link AbstractRuntimeException} and their subclasses.
243      *
244      * @param namespace the Class object acting as the key to the namespace from
245      * which to retrieve the description for 'code' [can be null, in which case
246      * only the root namespace is used for lookup]
247      *
248      * @param code the message string value that was passed into exception
249      * constructor [can be null, in which case null is returned].
250      *
251      * @return looked-up error message or the error code if it could not be
252      * looked up [null is returned on null 'code' input only].
253      *
254      * This method does not throw.
255      *
256      * @see AbstractException#getMessage
257      * @see AbstractRuntimeException#getMessage
258      */

259     static String JavaDoc getMessage (final Class JavaDoc namespace, final String JavaDoc code)
260     {
261         if (code == null) return null;
262         
263         try
264         {
265             if (code.length () > 0)
266             {
267                 // look the code up in the resource bundle:
268
final String JavaDoc msg = lookup (namespace, code);
269                 if (msg == null)
270                 {
271                     // if code lookup failed, return 'code' as is:
272
return code;
273                 }
274                 else
275                 {
276                     return EMBED_ERROR_CODE ? "[" + code + "] " + msg : msg;
277                 }
278            }
279            else
280            {
281                return "";
282            }
283         }
284         catch (Throwable JavaDoc t)
285         {
286             // this method must never fail: default to returning the input
287
// verbatim on all unexpected problems
288
return code;
289         }
290     }
291     
292     /**
293      * Provides support for lookup of exception error codes from {@link AbstractException}
294      * and {@link AbstractRuntimeException} and their subclasses.
295      *
296      * @param namespace the Class object acting as the key to the namespace from
297      * which to retrieve the description for 'code' [can be null, in which case
298      * only the root namespace is used for lookup]
299      *
300      * @param code the message string value that was passed into exception
301      * constructor [can be null, in which case null is returned].
302      *
303      * @param arguments java.text.MessageFormat-style parameters to be substituted
304      * into the error message once it is looked up.
305      *
306      * @return looked-up error message or the error code if it could not be
307      * looked up [null is returned on null 'code' input only].
308      *
309      * This method does not throw.
310      *
311      * @see AbstractException#getMessage
312      * @see AbstractRuntimeException#getMessage
313      */

314     static String JavaDoc getMessage (final Class JavaDoc namespace, final String JavaDoc code, final Object JavaDoc [] arguments)
315     {
316         if (code == null) return null;
317         final String JavaDoc pattern = getMessage (namespace, code);
318
319         // assertion: pattern != null
320

321         if ((arguments == null) || (arguments.length == 0))
322         {
323             return pattern;
324         }
325         else
326         {
327             try
328             {
329                 return MessageFormat.format (pattern, arguments);
330             }
331             catch (Throwable JavaDoc t)
332             {
333                 // this method cannot fail: default to returning the input
334
// verbatim on all unexpected problems:
335

336                 final StringBuffer JavaDoc msg = new StringBuffer JavaDoc (code + EOL);
337                 
338                 for (int a = 0; a < arguments.length; a ++)
339                 {
340                     msg.append ("\t{" + a + "} = [");
341                     final Object JavaDoc arg = arguments [a];
342                     try
343                     {
344                         msg.append (arg.toString ());
345                     }
346                     catch (Throwable JavaDoc e) // guard against bad toString() overrides
347
{
348                         if (arg != null)
349                             msg.append (arg.getClass ().getName ());
350                         else
351                             msg.append ("null");
352                     }
353                     msg.append ("]");
354                     msg.append (EOL);
355                 }
356                 
357                 return msg.toString ();
358             }
359         }
360     }
361     
362     // private: ...............................................................
363

364
365     private ExceptionCommon () {} // prevent subclassing
366

367     /**
368      * Internal property lookup method. It implements the lookup scheme described
369      * in {@link #addExceptionResource}.
370      *
371      * @return property value corresponding to 'propertyName' [null if lookup fails]
372      */

373     private static String JavaDoc lookup (Class JavaDoc namespace, final String JavaDoc propertyName)
374     {
375         if (propertyName == null) return null;
376         
377         // note: the following does not guard against exceptions that do not subclass
378
// our base classes [done elsewhere], however it will not crash either
379

380         // check extension bundles:
381
if (namespace != null)
382         {
383             ResourceBundle JavaDoc rb;
384             while (namespace != ABSTRACT_EXCEPTION && namespace != ABSTACT_RUNTIME_EXCEPTION
385                    && namespace != THROWABLE && namespace != null)
386             {
387                 synchronized (s_exceptionCodeMap)
388                 {
389                     rb = (ResourceBundle JavaDoc) s_exceptionCodeMap.get (namespace);
390                     if (rb == null)
391                     {
392                         // check if there is a default bundle to be loaded for this namespace:
393
if ((rb = addExceptionResource (namespace, "exceptions")) == null)
394                         {
395                             // add an immutable empty bundle to avoid this check in the future:
396
s_exceptionCodeMap.put (namespace, EMPTY_RESOURCE_BUNDLE);
397                         }
398                     }
399                 }
400                 
401                 if (rb != null)
402                 {
403                     String JavaDoc propertyValue = null;
404                     try
405                     {
406                         propertyValue = rb.getString (propertyName);
407                     }
408                     catch (Throwable JavaDoc ignore) {}
409                     if (propertyValue != null) return propertyValue;
410                 }
411                 
412                 // walk the inheritance chain for 'namespace':
413
namespace = namespace.getSuperclass ();
414             }
415         }
416         
417         // if everything fails, check the root bundle:
418
if (ROOT_RESOURCE_BUNDLE != null)
419         {
420             String JavaDoc propertyValue = null;
421             try
422             {
423                 propertyValue = ROOT_RESOURCE_BUNDLE.getString (propertyName);
424             }
425             catch (Throwable JavaDoc ignore) {}
426             if (propertyValue != null) return propertyValue;
427         }
428         
429         return null;
430     }
431     
432     private static String JavaDoc getNameInNamespace (final Class JavaDoc namespace, final String JavaDoc name)
433     {
434         if (namespace == null) return name;
435         
436         final String JavaDoc namespaceName = namespace.getName ();
437         final int lastDot = namespaceName.lastIndexOf ('.');
438         
439         if (lastDot <= 0)
440             return name;
441         else
442             return namespaceName.substring (0, lastDot + 1) + name;
443     }
444     
445
446     // changes this to 'false' to eliminate repetition of error codes in
447
// the output of getMessage():
448
private static final boolean EMBED_ERROR_CODE = true;
449
450     // the name of the 'root' message resource bundle, derived as
451
// [this package name + ".exceptions"]:
452
private static final String JavaDoc ROOT_RESOURCE_BUNDLE_NAME; // set in <clinit>
453

454     // the root resource bundle; always checked if all other lookups fail:
455
private static final ResourceBundle JavaDoc ROOT_RESOURCE_BUNDLE; // set in <clinit>
456

457     // static cache of all loaded resource bundles, populated via addExceptionResource():
458
private static final Map JavaDoc /* Class -> ResourceBundle */ s_exceptionCodeMap = new HashMap JavaDoc ();
459     
460     // misc constants:
461

462     private static final String JavaDoc NESTED_THROWABLE_HEADER = "[NESTED EXCEPTION]:";
463     private static final Class JavaDoc THROWABLE = Throwable JavaDoc.class;
464     private static final Class JavaDoc ABSTRACT_EXCEPTION = AbstractException.class;
465     private static final Class JavaDoc ABSTACT_RUNTIME_EXCEPTION = AbstractRuntimeException.class;
466     /*private*/ static final Enumeration JavaDoc EMPTY_ENUMERATION = Collections.enumeration (Collections.EMPTY_SET);
467     private static final ResourceBundle JavaDoc EMPTY_RESOURCE_BUNDLE = new ResourceBundle JavaDoc ()
468     {
469         public Object JavaDoc handleGetObject (final String JavaDoc key)
470         {
471             return null;
472         }
473         
474         public Enumeration JavaDoc getKeys ()
475         {
476             return EMPTY_ENUMERATION;
477         }
478     };
479     
480     // end-of-line terminator for the current platform:
481
private static final String JavaDoc EOL = System.getProperty ("line.separator", "\n");
482     
483     
484     static
485     {
486         // set the name of ROOT_RESOURCE_BUNDLE_NAME:
487
ROOT_RESOURCE_BUNDLE_NAME = getNameInNamespace (ExceptionCommon.class, "exceptions");
488                 
489         // set the root resource bundle:
490
ResourceBundle JavaDoc temprb = null;
491         try
492         {
493             temprb = ResourceBundle.getBundle (ROOT_RESOURCE_BUNDLE_NAME);
494         }
495         catch (Throwable JavaDoc ignore)
496         {
497             // if the exception codes rb is absent, we are still in a supported configuration
498
}
499         ROOT_RESOURCE_BUNDLE = temprb;
500     }
501     
502 } // end of class
503
// ----------------------------------------------------------------------------
504
Popular Tags