/*******************************************************************************
* $Id$
* $Author$
* $Date$
*
* Copyright 2002-2003 YAJUL Developers, Joshua Davis, Kent Vogel.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE 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 SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
******************************************************************************/
package org.yajul.util;
import org.yajul.collections.CollectionUtil;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;
/**
* A checked exception that contains a list of other exceptions.
* <br>
* User: josh
* Date: Oct 24, 2003
* Time: 7:51:20 AM
*/
public class ExceptionList extends Exception {
/**
* The list of throwables. *
*/
private final List<Throwable> list = CollectionUtil.newArrayList();
/**
* True if a message was explicitly specified. *
*/
private boolean messageSpecified = false;
/**
* Constructs a new exception with <code>null</code> as its detail message.
* The cause is not initialized, and may subsequently be initialized by a
* call to initCause.
*/
public ExceptionList() {
}
/**
* Constructs a new exception with the specified detail message. The
* cause is not initialized, and may subsequently be initialized by
* a call to initCause.
*
* @param message the detail message. The detail message is saved for
* later retrieval by the {@link #getMessage()} method.
*/
public ExceptionList(String message) {
super(message);
messageSpecified = true;
}
/**
* Constructs a new exception with the specified detail message and
* cause. <p>Note that the detail message associated with
* <code>cause</code> is <i>not</i> automatically incorporated in
* this exception's detail message.
*
* @param message the detail message (which is saved for later retrieval
* by the {@link #getMessage()} method).
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A <tt>null</tt> value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
*/
public ExceptionList(String message, Throwable cause) {
super(message);
messageSpecified = true;
add(cause);
}
/**
* Constructs a new exception with the specified cause and a detail
* message of <tt>(cause==null ? null : cause.toString())</tt> (which
* typically contains the class and detail message of <tt>cause</tt>).
* This constructor is useful for exceptions that are little more than
* wrappers for other throwables (for example, {@link
* java.security.PrivilegedActionException}).
*
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A <tt>null</tt> value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
*/
public ExceptionList(Throwable cause) {
add(cause);
}
/**
* Returns the cause of this throwable or <code>null</code> if the
* cause is nonexistent or unknown. (The cause is the throwable that
* caused this throwable to get thrown.)
* <p/>
* <p>This implementation returns the cause that was supplied via one of
* the constructors requiring a <tt>Throwable</tt>, or that was set after
* creation with the initCause(Throwable) method. While it is
* typically unnecessary to override this method, a subclass can override
* it to return a cause set by some other means. This is appropriate for
* a "legacy chained throwable" that predates the addition of chained
* exceptions to <tt>Throwable</tt>. Note that it is <i>not</i>
* necessary to override any of the <tt>PrintStackTrace</tt> methods,
* all of which invoke the <tt>getCause</tt> method to determine the
* cause of a throwable.
*
* @return the cause of this throwable or <code>null</code> if the
* cause is nonexistent or unknown.
*/
public Throwable getCause() {
if (list.size() > 0)
return list.get(0);
else
return null;
}
/**
* Converts everything to a string.
*
* @return a string.
*/
public String toString() {
if (list.size() == 0)
return super.toString();
else {
StringBuffer buf = new StringBuffer();
buf.append(super.toString());
int i = 0;
for (Iterator iterator = list.iterator(); iterator.hasNext(); i++) {
Throwable throwable = (Throwable) iterator.next();
buf.append("\nDetail [");
buf.append(Integer.toString(i));
buf.append("] = ");
buf.append(throwable.toString());
}
return buf.toString();
}
}
/**
* Returns the detail message string of this throwable.
*
* @return the detail message string of this <tt>Throwable</tt> instance
* (which may be <tt>null</tt>).
*/
public String getMessage() {
if ((!messageSpecified) && (list.size() > 0))
return getFirst().getMessage();
else
return super.getMessage();
}
/**
* Creates a localized description of this throwable.
* Subclasses may override this method in order to produce a
* locale-specific message. For subclasses that do not override this
* method, the default implementation returns the same result as
* <code>getMessage()</code>.
*
* @return The localized description of this throwable.
* @since JDK1.1
*/
public String getLocalizedMessage() {
if ((!messageSpecified) && (list.size() > 0))
return getFirst().getLocalizedMessage();
else
return super.getLocalizedMessage();
}
/**
* Prints a stack trace.
*/
public void printStackTrace() {
super.printStackTrace();
for (Throwable throwable : list) {
throwable.printStackTrace();
}
}
/**
* Prints a stack trace to the specified print stream.
*
* @param s the print stream.
*/
public void printStackTrace(PrintStream s) {
super.printStackTrace(s);
int i = 0;
for (Iterator<Throwable> iterator = list.iterator(); iterator.hasNext(); i++) {
@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
Throwable throwable = iterator.next();
s.println("Detail [" + i + "] :");
throwable.printStackTrace(s);
}
}
/**
* Prints this throwable and its backtrace to the specified print writer.
*
* @param w the print writer.s
*/
public void printStackTrace(PrintWriter w) {
super.printStackTrace(w);
int i = 0;
for (Iterator<Throwable> iterator = list.iterator(); iterator.hasNext(); i++) {
@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
Throwable throwable = iterator.next();
w.println("Detail [" + i + "] :");
throwable.printStackTrace(w);
}
}
/**
* Adds a new exception (Throwable) to the list.
*
* @param t The new exception (Throwable).
*/
public void add(Throwable t) {
list.add(t);
}
/**
* Returns the number of throwables in the list.
*
* @return int - The number of throwables in the list.
*/
public int size() {
return list.size();
}
/**
* Returns an iterator which will provide all of the Throwables.
*
* @return Iterator - An iterator of all the Throwables.
*/
public Iterator<Throwable> iterator() {
return list.iterator();
}
/**
* Throws this ExceptionList object if there are more than one
* throwables in the list. If there is only one throwable in the
* list, the throwable element will be thrown.
*
* @throws Throwable if the size of the list is not zero.
*/
public void throwIfThrowable() throws Throwable {
// If there is only one throwable in the list, just
// throw it.
if (list.size() == 1)
throw getFirst();
// Otherwise, throw the whole list.
else if (list.size() > 0)
throw this.fillInStackTrace();
}
/**
* Throws this ExceptionList object if there are more than one
* throwables in the list. If there is only one throwable in the
* list, *and* that throwable is acutally a checked exception, then
* the throwable element will be thrown.
*
* @throws Exception if the size of the list is not zero.
*/
public void throwIfException() throws Exception {
if (list.size() == 1) {
Throwable t = getFirst();
if (t instanceof Exception) {
throw (Exception) t;
}
fillInStackTrace();
throw this;
} else if (list.size() > 0) {
fillInStackTrace();
throw this;
}
}
private Throwable getFirst() {
return list.get(0);
}
}