// 
// The Waveset SPML General License 
// 
// Version 0.1, April 2003
// Copyright (C) 2003 Waveset Technologies, Inc..
// 6034 West Courtyard Drive, Suite 210, Austin, Texas 78730
// All rights reserved.
// 
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
// 
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions, and the disclaimers in Sections 6
//    and 7 below.
// 
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the disclaimers in Sections 6
//    and 7 below in the documentation and/or other materials provided
//    with the distribution.
// 
// 3. The end-user documentation included with the redistribution, if
//    any, must include the following acknowledgment:
// 
//    "This product includes software developed by 
//     Waveset Technologies, Inc. (www.waveset.com)."
// 
//    Alternately, this acknowledgment may appear in the software itself, if
//    and wherever such third-party acknowledgments normally appear.
//  
// 4. The names "Waveset" and "Waveset Technologies, Inc." must not be
//    used to endorse or promote products derived from this software
//    without the prior written permission of Waveset. For written
//    permission, please contact www.waveset.com.
//  
// 5. Products derived from this software may not be called "Waveset",
//    nor may "Waveset" appear in their name, without the prior written
//    permission of Waveset.
//  
// 6. NO WARRANTY 
// 
//    THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED
//    OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
//    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
//    DISCLAIMED.
// 
// 7.  LIMITATION OF LIABILITY
// 
//    IN NO EVENT SHALL THE WAVESET OR ITS LICENSORS BE LIABLE FOR ANY
//    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
//    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
//    GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
//    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
//    IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
//    OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
//    ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 
// End of Terms and Conditions
// ====================================================================
//
// Component Name: org.openspml.util.ExceptionWrapper
//
// Author(s): Jeff Larson
//
// Description: 
//
// A exception class that can wrap other exceptions.
// Typically subclassed and used to add context to system exceptions,
// or to wrap exceptions thrown by third party tools in an application
// specific exception.
//
//

package org.openspml.util;

import java.util.*;

/**
 * An exception class that can wrap other exceptions.  Used so we can
 * consistently use <code>SpmlException</code> within this library, but
 * still convey information found in other exceptions.
 * @see SpmlException
 */
public class ExceptionWrapper extends Exception {

    private static final String code_id = "$Id: ExceptionWrapper.java,v 1.2 2003/04/21 19:28:16 jlarson Exp $";

    //////////////////////////////////////////////////////////////////////
    //
    // Fields
    //
    //////////////////////////////////////////////////////////////////////

    private List _exceptions = null;

    //////////////////////////////////////////////////////////////////////
    //
    // Constructors
    //
    //////////////////////////////////////////////////////////////////////

    /** 
     * Construct an empty exception wrapper.
     */
    public ExceptionWrapper() {
        super();
    }

    /** 
     * Construct an exception wrapper with a detail message.
     *
     * @param message detail message
     */
    public ExceptionWrapper(String message) {
        super(message);
    }

    /**
     * Construct an exception wrapper with an existing exception.
     * <p>
     * The detail message for the throwable will become the
     * default message for this wrapper.
     * 
     * @param t nested throwable
     */
    public ExceptionWrapper(Throwable t) {
        super();
        addException(t);
    }

   /**
    * Create an exception wrapper from an existing exception but
    * with a different detail message.
    *
    * @param message detail message
    * @param t nested throwable
    */
    public ExceptionWrapper(String message, Throwable t) {
        super(message);
        addException(t);
    }

    /** 
     * Adds another exception to the list of wrapped exceptions.
     */
    public synchronized void addException(Throwable t) {
	if (t != null) {
	    if (_exceptions == null) 
		_exceptions = new ArrayList();
	    _exceptions.add(t);
	}
    }

    /** 
     * Removes all wrapped exceptions.
     */
    public void clearExceptions() {
        _exceptions = null;
    }

    //////////////////////////////////////////////////////////////////////
    //
    // Accessors
    //
    //////////////////////////////////////////////////////////////////////

    /**
     * Return the list of nested exceptions.
     */
    public List getExceptions() {
	return _exceptions;
    }

    /** 
     * Returns the number of wrapped Exceptions.
     */
    public synchronized int getExceptionCount() {

	int size = 0;
        if (_exceptions != null)
	    size = _exceptions.size();

	return size;
    }

    /** 
     * Returns the first embedded exception, if any.
     */
    public synchronized Throwable getException() {
	return getException(0);
    }

    /** 
     * Returns the Nth embedded exception, if any.
     */
    public synchronized Throwable getException(int n) {

	Throwable e = null;
        if (_exceptions != null && _exceptions.size() > n)
            e = (Throwable)_exceptions.get(n);

        return e;
    }

    //////////////////////////////////////////////////////////////////////
    //
    // Rendering
    //
    //////////////////////////////////////////////////////////////////////

    /**
     * Return a detail message for this exception.
     * <p>
     * If there is a embedded exception, and if this exception
     * has no detail message of its own, getMessage method will
     * return the detail message from the first embedded exception.
     */
    public String getMessage() {

        String msg = super.getMessage();
        if (msg == null) {
	    Throwable e = getException();
	    if (e != null)
		msg = e.getMessage();
	}

        return msg;
    }

    /**
     * Return a string containing the exception class name and
     * messages for all wrapped exceptions.
     */
    public synchronized String getAllMessages() {

	StringBuffer b = new StringBuffer();

        b.append("Exception ");
	b.append(getClass().getName());

	String msg = super.getMessage();
	if (msg != null) {
	    b.append(": ");
	    b.append(msg);
	}

	int count = getExceptionCount();
	if (count > 0) {
	    if (count > 1)
		b.append("\nWrapped exceptions:");
	    else
		b.append("\nWrapped exception:");

	    for (int i = 0 ; i < count ; i++) {
		b.append("\n    ");
		Throwable t = getException(i);
		b.append(t.getClass().getName());
		msg = null;
		if (t instanceof ExceptionWrapper)
		    msg = ((ExceptionWrapper)t).getAllMessages();
		else
		    msg = t.getMessage();
		if (msg != null) {
		    b.append(": ");
		    b.append(msg);
		}
	    }
	}

	b.append("\n");
	return b.toString();
    }

    /**
     * Throwable overload to cause printing of the nested
     * exception stack traces as well as our own.
     */
    public void printStackTrace() {
	printStackTrace(System.err);
    }

    /**
     * Throwable overload to cause printing of the nested
     * exception stack traces as well as our own.
     */
    public void printStackTrace(java.io.PrintStream str) {
        printStackTrace(new java.io.PrintWriter(str));
    }

    /**
     * Throwable overload to cause printing of the nested
     * exception stack traces as well as our own.
     */
    public void printStackTrace(java.io.PrintWriter out) {

        super.printStackTrace(out);
	
	if (_exceptions != null) {
	    for (int i = 0 ; i < getExceptionCount() ; i++) {
		Throwable t = getException(i);
		out.println("Wrapped exception:");
		t.printStackTrace(out);
	    }
	}

	out.flush();
    }

}
