001/*
002 *  Copyright 2012 Christopher Pheby
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.jadira.cdt.exception;
017
018/**
019 * Class for constructing checked Exceptions with a given root cause.
020 */
021public abstract class WrappedCheckedException extends Exception {
022
023        private static final long serialVersionUID = 615855181745250021L;
024
025        /**
026         * Construct a WrappedCheckedException with the specified 
027         * message.
028         * @param message the specific message
029         */
030        public WrappedCheckedException(String message) {
031                super(message);
032        }
033
034        /**
035         * Construct a WrappedCheckedException with the specified 
036         * message and wrapped exception.
037         * @param message the specific message
038         * @param cause the wrapped exception
039         */
040        public WrappedCheckedException(String message, Throwable cause) {
041                super(message, cause);
042        }
043
044        /**
045         * Return the detail message, including the message from the Wrapped
046         * exception if there is one.
047         */
048        @Override
049        public String getMessage() {
050                return constructMessage(super.getMessage(), getCause());
051        }
052
053        /**
054         * Constructs an exception String with the given message and incorporating the
055         * causing exception
056         * @param message The specific message
057         * @param cause The causing exception
058         * @return The exception String
059         */
060        protected String constructMessage(String message, Throwable cause) {
061                
062                if (cause != null) {
063                
064                        StringBuilder strBuilder = new StringBuilder();
065                        
066                        if (message != null) {
067                                strBuilder.append(message).append(": ");
068                        }
069                        
070                        strBuilder.append("Wrapped exception is {").append(cause);
071                        strBuilder.append("}");
072                        
073                        return strBuilder.toString();
074                        
075                } else {
076                        return message;
077                }
078        }
079
080        /**
081         * Retrieves the ultimate root cause for this exception, or null
082         * @return The root cause
083         */
084        public Throwable getRootCause() {
085                
086                Throwable rootCause = null;
087                Throwable nextCause = getCause();
088                
089                while (nextCause != null && !nextCause.equals(rootCause)) {
090                        rootCause = nextCause;
091                        nextCause = nextCause.getCause();
092                }
093                
094                return rootCause;
095        }
096
097        /**
098         * Returns the next parent exception of the given type, or null
099         * @param exceptionType the exception type to match
100         * @param <E> The Exception type
101         * @return The matched exception of the target type, or null
102         */
103        public <E extends Exception> E findWrapped(Class<E> exceptionType) {
104                
105                if (exceptionType == null) {
106                        return null;
107                }
108                
109                Throwable cause = getCause();
110                
111                while (true) {
112                        
113                        if (cause == null) {
114                                return null;
115                        }
116                        
117                        if (exceptionType.isInstance(cause)) {
118                                
119                                @SuppressWarnings("unchecked") E matchedCause = (E) cause;
120                                return matchedCause;
121                        }
122
123                        if (cause.getCause() == cause) {
124                                return null;
125                        }
126                        
127                        if (cause instanceof WrappedRuntimeException) {
128                                return ((WrappedRuntimeException) cause).findWrapped(exceptionType);
129                        }
130                        if (cause instanceof WrappedCheckedException) {
131                                return ((WrappedCheckedException) cause).findWrapped(exceptionType);
132                        }                       
133                        
134                        cause = cause.getCause();
135                }
136        }
137}