001    package org.springframework.richclient.exceptionhandling.delegation;
002    
003    import java.util.Collections;
004    import java.util.List;
005    
006    /**
007     * A purger that looks through to a throwable chain and can select one to unwrap.
008     * 
009     * @author Geoffrey De Smet
010     * @since 0.3.0
011     */
012    public class DefaultExceptionPurger implements ExceptionPurger {
013    
014        protected List<Class> includeThrowableClassList = Collections.emptyList();
015        protected List<Class> excludeThrowableClassList = Collections.emptyList();
016        
017        public DefaultExceptionPurger() {}
018    
019        public DefaultExceptionPurger(Class includeThrowableClass, Class excludeThrowableClass) {
020            if (includeThrowableClass != null) {
021                this.includeThrowableClassList = Collections.singletonList(includeThrowableClass);
022            }
023            if (excludeThrowableClass != null) {
024                this.excludeThrowableClassList = Collections.singletonList(excludeThrowableClass);
025            }
026        }
027        
028        public DefaultExceptionPurger(List<Class> includeThrowableClassList, List<Class> excludeThrowableClassList) {
029            if (includeThrowableClassList != null) {
030                this.includeThrowableClassList = includeThrowableClassList;
031            }
032            if (excludeThrowableClassList != null) {
033                this.excludeThrowableClassList = excludeThrowableClassList;
034            }
035        }
036    
037        /**
038         * See @{link {@link #setIncludeThrowableClassList(List)}.
039         * @param includeThrowableClass used as a singleton list for includeThrowableClassList
040         */
041        public void setIncludeThrowableClass(Class includeThrowableClass) {
042            setIncludeThrowableClassList(Collections.singletonList(includeThrowableClass));
043        }
044        
045        /**
046         * Sets Throwables that if found, are unwrapped.
047         * These Throwables are ussually very specific exceptions, for example: LoginCredentialsExpiredException.
048         * The earliest throwable found is selected.
049         * </p>
050         * Given a chain A1->B1->C1->B2->D1:
051         * {A} returns A1;
052         * {B} returns B1;
053         * {D} returns D1;
054         * {Z) returns A1;
055         * {C, Z} returns C1;
056         * {B, D} returns B1;
057         * {D, B} return B1;
058         * </p>
059         * When combined, includeThrowableClassList takes priority over excludeThrowableClassList.
060         * @param includeThrowableClassList a list of classes
061         */
062        public void setIncludeThrowableClassList(List<Class> includeThrowableClassList) {
063            this.includeThrowableClassList = includeThrowableClassList;
064        }
065    
066        /**
067         * See @{link {@link #setExcludeThrowableClassList(List)}.
068         * @param excludeThrowableClass used as a singleton list for excludeThrowableClassList
069         */
070        public void setExcludeThrowableClass(Class excludeThrowableClass) {
071            setExcludeThrowableClassList(Collections.singletonList(excludeThrowableClass));
072        }
073        
074        /**
075         * Sets Throwables that if found, its cause is unwrapped.
076         * These Throwables are ussually very general wrapper exceptions, for example: WrapperException.
077         * The last throwable found's cause is selected.
078         * If the cause is null, itself is selected.
079         * </p>
080         * Given a chain A1->B1->C1->B2->D1:
081         * {A} returns B1;
082         * {B} returns D1;
083         * {D} returns D1;
084         * {Z) returns A1;
085         * {C, Z} returns B2;
086         * {C, D} returns D1;
087         * {D, C} return D1;
088         * </p>
089         * When combined, includeThrowableClassList takes priority over excludeThrowableClassList.
090         * @param excludeThrowableClassList a list of classes
091         */
092        public void setExcludeThrowableClassList(List<Class> excludeThrowableClassList) {
093            this.excludeThrowableClassList = excludeThrowableClassList;
094        }
095        
096        
097        public Throwable purge(Throwable root) {
098            Throwable excludedPurged = root;
099            Throwable e = root;
100            while (e != null) {
101                if (containedIn(e, includeThrowableClassList)) {
102                    return e;
103                }
104                boolean excludedContained = containedIn(e, excludeThrowableClassList);
105                if (excludedContained) {
106                    excludedPurged = e; // in case the cause is null
107                }
108                // get cause of e (and null at end of chain)
109                e = (e.getCause() == e) ? null : e.getCause();
110                if (excludedContained && e != null) {
111                    excludedPurged = e; // in case the cause is not null
112                }
113            }
114            return excludedPurged;
115        }
116    
117        
118    //    public Throwable purge(Throwable root) {
119    //        Throwable purged = root;
120    //        Throwable e = root;
121    //        while (containedIn(e, excludeThrowableClassList)) {
122    //            // get cause of e (and null at end of chain)
123    //            e = (e.getCause() == e) ? null : e.getCause();
124    //            if (e == null) {
125    //                break;
126    //            }
127    //            purged = e;
128    //        }
129    //        return purged;
130    //    }
131        
132        protected boolean containedIn(Throwable e, List<Class> throwableClassList) {
133            for (Class throwableClass : throwableClassList) {
134                if (throwableClass.isInstance(e)) {
135                    return true;
136                }
137            }
138            return false;
139        }
140        
141    }