001package votorola.g.lang; // Copyright 2007-2008, 2011, 2013, Michael Allan.  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Votorola Software"), to deal in the Votorola Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Votorola Software, and to permit persons to whom the Votorola Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Votorola Software. THE VOTOROLA SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE VOTOROLA SOFTWARE OR THE USE OR OTHER DEALINGS IN THE VOTOROLA SOFTWARE.
002
003import java.util.*;
004
005
006/** Throwable utilities.
007  */
008public final @ThreadSafe class ThrowableX
009{
010
011
012    private ThrowableX() {}
013
014
015
016    /** An empty and immutable list of throwables for use with {@linkplain
017      * #listedThrowable(Throwable,List) listedThrowable}.
018      */
019    public static final List<Throwable> ENLIST_NONE = Collections.emptyList();
020
021
022
023    /** Returns true if both throwables are similar, or both null.  Compares class,
024      * message, nested causes and stack trace.  These comparisons are sufficient to
025      * discriminate among basic throwables, but not necessarily among their subclasses.
026      */
027    public static boolean isSimilar( final Throwable t1, final Throwable t2 )
028    {
029        if( t1 == null || t2 == null ) return t1 == t2;
030
031        return t1.getClass().equals( t2.getClass() )
032          && ObjectX.nullEquals( t1.getMessage(), t2.getMessage() )
033          && isSimilar( t1.getCause(), t2.getCause() )
034          && Arrays.equals( t1.getStackTrace(), t2.getStackTrace() );
035    }
036
037
038
039    /** Enlists the specified throwable and returns the list.
040      *
041      *     @param list the pre-constructed list to use.  {@linkplain #ENLIST_NONE
042      *       ENLIST_NONE} may be specified in order to defeat enlistment and instead
043      *       force immediate throwing, but the caller is responsible for detecting and
044      *       implementing that.  Passing ENLIST_NONE into this method will raise
045      *       UnsupportedOperationException.
046      *     @return the pre-constructed list, or if necessary a lazily constructed linked
047      *       list.
048      */
049    public static List<Throwable> listedThrowable( final Throwable x, List<Throwable> list )
050    {
051        if( list == null ) list = new LinkedList<Throwable>();
052
053        list.add( x );
054        return list;
055    }
056
057
058
059    /** Contructs an expanded <code>toString</code> that includes the throwable's own,
060      * plus that of all nested causes.
061      */
062    public static String toStringExpanded( final Throwable t )
063    {
064        StringBuilder b = new StringBuilder();
065        for( Throwable cause = t;; )
066        {
067            b.append( cause.toString() );
068            cause = cause.getCause();
069            if( cause == null ) break;
070
071            b.append( ": " );
072        }
073        return b.toString();
074    }
075
076
077}