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 runtime Exceptions with a given root cause.
020 */
021public abstract class WrappedRuntimeException extends RuntimeException {
022
023        private static final long serialVersionUID = 2370572498972367128L;
024
025        /**
026         * Construct a WrappedRuntimeException with the specified 
027         * message.
028         * @param message the specific message
029         */
030        public WrappedRuntimeException(String message) {
031                super(message);
032        }
033
034        /**
035         * Construct a WrappedRuntimeException with the specified 
036         * message and wrapped exception.
037         * @param message the specific message
038         * @param cause the wrapped exception
039         */
040        public WrappedRuntimeException(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         * @return The message
048         */
049        @Override
050        public String getMessage() {
051                return constructMessage(super.getMessage(), getCause());
052        }
053
054        /**
055         * Constructs an exception String with the given message and incorporating the
056         * causing exception
057         * @param message The message
058         * @param cause The causing exception
059         * @return The exception String
060         */
061        protected String constructMessage(String message, Throwable cause) {
062                
063                if (cause != null) {
064                
065                        StringBuilder strBuilder = new StringBuilder();
066                        
067                        if (message != null) {
068                                strBuilder.append(message).append(": ");
069                        }
070                        
071                        strBuilder.append("Wrapped exception is {").append(cause);
072                        strBuilder.append("}");
073                        
074                        return strBuilder.toString();
075                        
076                } else {
077                        return message;
078                }
079        }
080
081        /**
082         * Retrieves the ultimate root cause for this exception, or null
083         * @return The root cause
084         */
085        public Throwable getRootCause() {
086                
087                Throwable rootCause = null;
088                Throwable nextCause = getCause();
089                
090                while (nextCause != null && !nextCause.equals(rootCause)) {
091                        rootCause = nextCause;
092                        nextCause = nextCause.getCause();
093                }
094                
095                return rootCause;
096        }
097
098        /**
099         * Returns the next parent exception of the given type, or null
100         * @param exceptionType the exception type to match
101         * @param <E> The exception type
102         * @return The matched exception of the target type, or null
103         */
104        public <E extends Exception> E findWrapped(Class<E> exceptionType) {
105                
106                if (exceptionType == null) {
107                        return null;
108                }
109                
110                Throwable cause = getCause();
111                
112                while (true) {
113                        
114                        if (cause == null) {
115                                return null;
116                        }
117                        
118                        if (exceptionType.isInstance(cause)) {
119                                
120                                @SuppressWarnings("unchecked") E matchedCause = (E) cause;
121                                return matchedCause;
122                        }
123                        
124                        if (cause.getCause() == cause) {
125                                return null;
126                        }
127                        
128                        if (cause instanceof WrappedRuntimeException) {
129                                return ((WrappedRuntimeException) cause).findWrapped(exceptionType);
130                        }
131                        if (cause instanceof WrappedCheckedException) {
132                                return ((WrappedCheckedException) cause).findWrapped(exceptionType);
133                        }
134                        
135                        cause = cause.getCause();
136                }
137        }
138}