FileDocCategorySizeDatePackage
ExceptionCommon.javaAPI DocAndroid 1.5 API18212Wed May 06 22:41:16 BST 2009com.vladium.util.exception

ExceptionCommon

public abstract class ExceptionCommon extends Object implements com.vladium.util.IJREVersion
TODO: javadoc Based on code published by me in JavaPro, 2002.

This non-instantiable class provides static support functions common to {@link AbstractException} and {@link AbstractRuntimeException}.

author
Vlad Roubtsov, (C) 2002

Fields Summary
private static final boolean
EMBED_ERROR_CODE
private static final String
ROOT_RESOURCE_BUNDLE_NAME
private static final ResourceBundle
ROOT_RESOURCE_BUNDLE
private static final Map
s_exceptionCodeMap
private static final String
NESTED_THROWABLE_HEADER
private static final Class
THROWABLE
private static final Class
ABSTRACT_EXCEPTION
private static final Class
ABSTACT_RUNTIME_EXCEPTION
static final Enumeration
EMPTY_ENUMERATION
private static final ResourceBundle
EMPTY_RESOURCE_BUNDLE
private static final String
EOL
Constructors Summary
private ExceptionCommon()

Methods Summary
public static java.util.ResourceBundleaddExceptionResource(java.lang.Class namespace, java.lang.String messageResourceBundleName)
This method can be called by static initializers of {@link AbstractException} and {@link AbstractRuntimeException} subclasses in order to add another resource bundle to the set that is used to look up error codes. This makes it possible to extend the set of exception error codes across independently maintained and built projects.

Note that this introduces a possibility of error code name clashes. This is resolved in the following way:
  • when getMessage(namespace, code) is called, 'code' is attempted to be looked up in the resource bundle previously keyed under 'namespace';
  • if no such bundle it found or if it does not contain a value for key 'code', the same step is repeated for the superclass of 'namespace';
  • finally, if all of the above steps fail, the root resource bundle specified by {@link #ROOT_RESOURCE_BUNDLE_NAME} is searched.
This strategy ensures that error codes follow inheritance and hiding rules similar to Java static methods.

IMPORTANT: this method must be called from static class initializers only.

There is no visible state change if the indicated resource is not found or if it has been added already under the same key.

param
namespace the Class object acting as the namespace key for the resource bundle identified by 'messageResourceBundleName'. This should be the calling class. [the method is a no-op if this is null]
param
messageResourceBundleName name of a bundle (path relative to 'namespace' package) to add to the set from which error code mappings are retrieved [the method is a no-op if this is null or an empty string]
return
ResourceBundle that corresponds to 'namespace' key or null if no such bundle could be loaded
throws
Error if 'namespace' does not correspond to an exception class derived from {@link AbstractException} or {@link AbstractRuntimeException}.
see
#lookup

        if ((namespace != null) && (messageResourceBundleName != null)
            && (messageResourceBundleName.length () > 0))
        {
            // bail out if the some other exception hierarchy attempts
            // to use this functionality:
            if (! ABSTRACT_EXCEPTION.isAssignableFrom (namespace)
                && ! ABSTACT_RUNTIME_EXCEPTION.isAssignableFrom (namespace))
            {
                throw new Error ("addExceptionResource(): class [" + namespace +
                    "] is not a subclass of " + ABSTRACT_EXCEPTION.getName () +
                    " or " + ABSTACT_RUNTIME_EXCEPTION.getName ());
            }
            
            // try to load resource bundle
            
            ResourceBundle temprb = null;
            String nameInNamespace = null;
            try
            {
                nameInNamespace = getNameInNamespace (namespace, messageResourceBundleName);
                
                //temprb = ResourceBundle.getBundle (nameInNamespace);
                
                ClassLoader loader = namespace.getClassLoader ();
                if (loader == null) loader = ClassLoader.getSystemClassLoader ();
                
                temprb = ResourceBundle.getBundle (nameInNamespace, Locale.getDefault (), loader);
            }
            catch (Throwable ignore)
            {
               // ignored intentionally: if the exception codes rb is absent,
               // we are still in a supported configuration
               temprb = null;
            }
            
            if (temprb != null)
            {
                synchronized (s_exceptionCodeMap)
                {
                    final ResourceBundle currentrb =
                        (ResourceBundle) s_exceptionCodeMap.get (namespace); 
                    if (currentrb != null)
                        return currentrb;
                    else
                    {
                        s_exceptionCodeMap.put (namespace, temprb);
                        return temprb;
                    }
                }
            }
        }
        
        return null;
    
static java.lang.StringgetMessage(java.lang.Class namespace, java.lang.String code)
Provides support for lookup of exception error codes from {@link AbstractException} and {@link AbstractRuntimeException} and their subclasses.

param
namespace the Class object acting as the key to the namespace from which to retrieve the description for 'code' [can be null, in which case only the root namespace is used for lookup]
param
code the message string value that was passed into exception constructor [can be null, in which case null is returned].
return
looked-up error message or the error code if it could not be looked up [null is returned on null 'code' input only]. This method does not throw.
see
AbstractException#getMessage
see
AbstractRuntimeException#getMessage

        if (code == null) return null;
        
        try
        {                      
            if (code.length () > 0)
            {
                // look the code up in the resource bundle:
                final String msg = lookup (namespace, code);     
                if (msg == null)
                {
                    // if code lookup failed, return 'code' as is:
                    return code;
                }
                else
                {
                    return EMBED_ERROR_CODE ? "[" + code + "] " + msg : msg;
                }
           }
           else
           {
               return "";
           }
        }
        catch (Throwable t)
        {
            // this method must never fail: default to returning the input
            // verbatim on all unexpected problems
            return code;
        }
    
static java.lang.StringgetMessage(java.lang.Class namespace, java.lang.String code, java.lang.Object[] arguments)
Provides support for lookup of exception error codes from {@link AbstractException} and {@link AbstractRuntimeException} and their subclasses.

param
namespace the Class object acting as the key to the namespace from which to retrieve the description for 'code' [can be null, in which case only the root namespace is used for lookup]
param
code the message string value that was passed into exception constructor [can be null, in which case null is returned].
param
arguments java.text.MessageFormat-style parameters to be substituted into the error message once it is looked up.
return
looked-up error message or the error code if it could not be looked up [null is returned on null 'code' input only]. This method does not throw.
see
AbstractException#getMessage
see
AbstractRuntimeException#getMessage

        if (code == null) return null;
        final String pattern = getMessage (namespace, code);

        // assertion: pattern != null
        
        if ((arguments == null) || (arguments.length == 0))
        {
            return pattern;
        }
        else
        {
            try
            {
                return MessageFormat.format (pattern, arguments);
            }
            catch (Throwable t)
            {
                // this method cannot fail: default to returning the input
                // verbatim on all unexpected problems:
                
                final StringBuffer msg = new StringBuffer (code + EOL);
                
                for (int a = 0; a < arguments.length; a ++)
                {
                    msg.append ("\t{" + a + "} = [");
                    final Object arg = arguments [a];
                    try
                    {
                        msg.append (arg.toString ());
                    }
                    catch (Throwable e) // guard against bad toString() overrides
                    {
                        if (arg != null)
                            msg.append (arg.getClass ().getName ());
                        else
                            msg.append ("null");
                    }
                    msg.append ("]");
                    msg.append (EOL);
                }
                
                return msg.toString ();
            }
        }
    
private static java.lang.StringgetNameInNamespace(java.lang.Class namespace, java.lang.String name)

        if (namespace == null) return name;
        
        final String namespaceName = namespace.getName ();
        final int lastDot = namespaceName.lastIndexOf ('.");
        
        if (lastDot <= 0)
            return name;
        else
            return namespaceName.substring (0, lastDot + 1)  + name;
    
private static java.lang.Stringlookup(java.lang.Class namespace, java.lang.String propertyName)
Internal property lookup method. It implements the lookup scheme described in {@link #addExceptionResource}.

return
property value corresponding to 'propertyName' [null if lookup fails]

        if (propertyName == null) return null;
        
        // note: the following does not guard against exceptions that do not subclass
        // our base classes [done elsewhere], however it will not crash either
        
        // check extension bundles:
        if (namespace != null)
        {
            ResourceBundle rb;
            while (namespace != ABSTRACT_EXCEPTION && namespace != ABSTACT_RUNTIME_EXCEPTION
                   && namespace != THROWABLE && namespace != null)
            {
                synchronized (s_exceptionCodeMap)
                {
                    rb = (ResourceBundle) s_exceptionCodeMap.get (namespace);
                    if (rb == null)
                    {
                        // check if there is a default bundle to be loaded for this namespace: 
                        if ((rb = addExceptionResource (namespace, "exceptions")) == null)
                        {
                            // add an immutable empty bundle to avoid this check in the future:
                            s_exceptionCodeMap.put (namespace, EMPTY_RESOURCE_BUNDLE);
                        }
                    }
                }
                
                if (rb != null)
                {
                    String propertyValue = null;
                    try
                    {
                        propertyValue = rb.getString (propertyName);
                    }
                    catch (Throwable ignore) {}
                    if (propertyValue != null) return propertyValue;
                }
                
                // walk the inheritance chain for 'namespace':
                namespace = namespace.getSuperclass ();
            }
        }
        
        // if everything fails, check the root bundle:
        if (ROOT_RESOURCE_BUNDLE != null)
        {
            String propertyValue = null;
            try
            {
                propertyValue = ROOT_RESOURCE_BUNDLE.getString (propertyName);
            }
            catch (Throwable ignore) {}
            if (propertyValue != null) return propertyValue;
        }
        
        return null;
    
static voidprintStackTrace(java.lang.Throwable t, java.io.PrintWriter out)

        if (JRE_1_4_PLUS)
        {
            if (t instanceof IThrowableWrapper)
            {
                final IThrowableWrapper tw = (IThrowableWrapper) t;
                
                tw.__printStackTrace (out); 
            }
            else
            {
                t.printStackTrace (out);
            }
        }
        else
        {
            for (boolean first = true; t != null; )
            {
                if (first)
                    first = false;
                else
                {
                    out.println ();
                    out.println (NESTED_THROWABLE_HEADER);                
                }
                
                if (t instanceof IThrowableWrapper)
                {
                    final IThrowableWrapper tw = (IThrowableWrapper) t;
                    
                    tw.__printStackTrace (out);
                    t = tw.getCause (); 
                }
                else
                {
                    t.printStackTrace (out);
                    break;
                }
            }
        }
    
static voidprintStackTrace(java.lang.Throwable t, java.io.PrintStream out)

        if (JRE_1_4_PLUS)
        {
            if (t instanceof IThrowableWrapper)
            {
                final IThrowableWrapper tw = (IThrowableWrapper) t;
                
                tw.__printStackTrace (out); 
            }
            else
            {
                t.printStackTrace (out);
            }
        }
        else
        {
            for (boolean first = true; t != null; )
            {
                if (first)
                    first = false;
                else
                {
                    out.println ();
                    out.println (NESTED_THROWABLE_HEADER);                
                }
                
                if (t instanceof IThrowableWrapper)
                {
                    final IThrowableWrapper tw = (IThrowableWrapper) t;
                    
                    tw.__printStackTrace (out);
                    t = tw.getCause (); 
                }
                else
                {
                    t.printStackTrace (out);
                    break;
                }
            }
        }