/*
 * Jakelib2 - General purpose C++ library
 * Copyright (C) 2001 Florian Wolff  (florian@donuz.de)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * $Id: Throwable.h,v 1.8 2003/02/15 19:04:53 florian Exp $
 */

#ifndef INCLUDE_JAKELIB2_LANG_THROWABLE_H
# define INCLUDE_JAKELIB2_LANG_THROWABLE_H

namespace jakelib {
  
  namespace io {
    class PrintWriter;
  }

  namespace lang {
    class StackTraceElement;
    class StackTraceElements;

    /**
     * \c Throwable is the base class of all \c Exception and \c Error
     * classes throughout Jakelib.
     *
     * \c Throwables should always be thrown and caught 'by value' like this:
     * \code
     * try {
     *   ...
     *   throw Throwable();
     *   ...
     * }
     * catch (Throwable &e) {
     *   ...
     * }
     * \endcode
     *
     * @class Throwable
     * @version $Id: Throwable.h,v 1.8 2003/02/15 19:04:53 florian Exp $
     * @author Florian 'Overflo' Wolff (florian@donuz.de)
     */
    class Throwable : public Object
      {
      public:

        /**
         * Creates a new \c Throwable with empty message string
         * and cause set to <tt>this</tt>.
         */
        Throwable();

        /**
         * Creates a new \c Throwable with the given message string
         * and cause set to <tt>this</tt>.
         */
        Throwable(String *msg);
  
        /**
         * Instantiate this Throwable with the given message and cause. Note that
         * the message is unrelated to the message of the cause.
         * {@link #fillInStackTrace()} will be called to set up the stack trace.
         *
         * @param msg the message to associate with the Throwable
         * @param cause the cause, may be null
         */
        Throwable(String *msg, Throwable *cause);

        /**
         * Instantiate this Throwable with the given cause. The message is then
         * built as <code>cause == null ? null : cause.toString()</code>.
         * {@link #fillInStackTrace()} will be called to set up the stack trace.
         *
         * @param cause the cause, may be null
         */
        Throwable(Throwable *cause);

        /**
         * Returns a short string representation of this \c Throwable.
         */
        String* toString();
        
        /**
         * Print a stack trace to the standard error stream. This stream is the
         * current contents of <code>System.err</code>. The first line of output
         * is the result of \c toString(), and the remaining lines represent
         * the data created by \c fillInStackTrace(). While the format is
         * unspecified, this implementation uses the suggested format, demonstrated
         * by this example:<br>
         * <pre>
         * public class Junk
         * {
         *   public static void main(String args[])
         *   {
         *     try{
         *         a();
         *     }
         *     catch(HighLevelException e){
         *         e.printStackTrace();
         *     }
         *   }
         *   static void a() throws HighLevelException
         *   {
         *     try{
         *         b();
         *     }
         *     catch(MidLevelException e){
         *         throw new HighLevelException(e);
         *     }
         *   }
         *   static void b() throws MidLevelException
         *   {
         *     c();
         *   }
         *   static void c() throws MidLevelException
         *   {
         *     try{
         *         d();
         *     }
         *     catch(LowLevelException e){
         *         throw new MidLevelException(e);
         *       }
         *   }
         *   static void d() throws LowLevelException
         *   {
         *     e();
         *   }
         *   static void e() throws LowLevelException
         *   {
         *     throw new LowLevelException();
         *   }
         * }
         * class HighLevelException extends Exception
         * {
         *   HighLevelException(Throwable cause) { super(cause); }
         * }
         * class MidLevelException extends Exception
         * {
         *   MidLevelException(Throwable cause)  { super(cause); }
         * }
         * class LowLevelException extends Exception
         * {
         * }
         * </pre>
         * <p>
         * <pre>
         *  HighLevelException: MidLevelException: LowLevelException
         *          at Junk.a(Junk.java:13)
         *          at Junk.main(Junk.java:4)
         *  Caused by: MidLevelException: LowLevelException
         *          at Junk.c(Junk.java:23)
         *          at Junk.b(Junk.java:17)
         *          at Junk.a(Junk.java:11)
         *          ... 1 more
         *  Caused by: LowLevelException
         *          at Junk.e(Junk.java:30)
         *          at Junk.d(Junk.java:27)
         *          at Junk.c(Junk.java:21)
         *          ... 3 more
         * </pre>
         */
         void printStackTrace();

        /**
         * Print a stack trace to the specified PrintStream. See
         * {@link #printStackTrace()} for the sample format.
         *
         * @param s the PrintStream to write the trace to
         */
         /*XXX AlFa PrintStream is not implemented, so this function 
                    is temporarily commented*/
         //void printStackTrace(PrintStream* s);

        /**
         * Prints the exception, the detailed message and the stack trace
         * associated with this Throwable to the given <code>PrintWriter</code>.
         * The actual output written is implemention specific. Use the result of
         * <code>getStackTrace()</code> when more precise information is needed.
         *
         * <p>This implementation first prints a line with the result of this
         * object's <code>toString()</code> method.
         * <br>
         * Then for all elements given by <code>getStackTrace</code> it prints
         * a line containing three spaces, the string "at " and the result of calling
         * the <code>toString()</code> method on the <code>StackTraceElement</code>
         * object. If <code>getStackTrace()</code> returns an empty array it prints
         * a line containing three spaces and the string
         * "&lt;&lt;No stacktrace available&gt;&gt;".
         * <br>
         * Then if <code>getCause()</code> doesn't return null it adds a line
         * starting with "Caused by: " and the result of calling
         * <code>toString()</code> on the cause.
         * <br>
         * Then for every cause (of a cause, etc) the stacktrace is printed the
         * same as for the top level <code>Throwable</code> except that as soon
         * as all the remaining stack frames of the cause are the same as the
         * the last stack frames of the throwable that the cause is wrapped in
         * then a line starting with three spaces and the string "... X more" is
         * printed, where X is the number of remaining stackframes.
         *
         * @param pw the PrintWriter to write the trace to
         */
        void printStackTrace(jakelib::io::PrintWriter* pw);

       /**
        * Initialize the cause of this Throwable.  This may only be called once
        * during the object lifetime, including implicitly by chaining
        * constructors.
        *
        * @param cause the cause of this Throwable, may be null
        * @return this
        * @throws IllegalArgumentException if cause is this (a Throwable can't be
        *         its own cause!)
        * @throws IllegalStateException if the cause has already been set
        */
        Throwable* initCause(Throwable *cause);

       /**
        * Returns the cause of this exception, or null if the cause is not known
        * or non-existant. This cause is initialized by the new constructors,
        * or by calling initCause.
        *
        * @return the cause of this Throwable
        * @since 1.4
        */
        Throwable* getCause();
       
       /**
        * Fill in the stack trace with the current execution stack.
        *
        * @return this same throwable
        * @see #printStackTrace()
        */
        Throwable* fillInStackTrace();

       /**
        * Provides access to the information printed in {@link #printStackTrace()}.
        * The array is non-null, with no null entries, although the virtual
        * machine is allowed to skip stack frames.  If the array is not 0-length,
        * then slot 0 holds the information on the stack frame where the Throwable
        * was created (or at least where <code>fillInStackTrace()</code> was
        * called).
        *
        * @return pointer to array of stack trace information, as available from the VM
        */
        StackTraceElements* getStackTrace();

        /**
         * Change the stack trace manually. This method is designed for remote
         * procedure calls, which intend to alter the stack trace before or after
         * serialization according to the context of the remote call.
         * <p>
         * The contents of the given stacktrace is copied so changes to the
         * original array do not change the stack trace elements of this
         * throwable.
         *
         * @param stackTrace the new trace to use
         * @throw NullPointerException If \c stackTrace is \c null or has \c null elements
         */
        void setStackTrace(StackTraceElements* stackTrace);

        /**
         * Returns the error message of this \c Throwable.
         */
        virtual String* getMessage();

      private:

        /** The message string. */
        String *message;

        /**
         * The cause of the throwable, including null for an unknown or non-chained
         * cause. This may only be set once; so the field is set to
         * <code>this</code> until initialized.
         *
         * @serial the cause, or null if unknown, or this if not yet set
         */
         Throwable* cause;

         /**
          * Thread state when fillInStackTrace was called.
          * Used by getStackTrace() to get an array of StackTraceElements.
          * Cleared when no longer needed.
          */
         Throwable* state;
        
         /**
          * Create whole stack trace in a stringbuffer so we don't have to print
          * it line by line. This prevents printing multiple stack traces from
          * different threads to get mixed up when written to the same PrintWriter.
          */
          String* stackTraceString();

         /**
          * Adds to the given StringBuffer a line containing the name and
          * all stacktrace elements minus the last equal ones.
          */
          static void stackTraceStringBuffer(StringBuffer* sb, String* name,
                                             StackTraceElements* stack, int equal);

         /**
          * The stack trace, in a serialized form.
          *
          * @serial the elements of the stack trace; this is non-null, and has
          *         no null entries
          */
          StackTraceElements* stackTrace;

         /**
          * Initiate the message to 
          * <code>(msg == null)?message=String::emptyString:message=msg</code>
          *
          * @param msg the String, may be null
          */
          void initMsg(String *msg);
          
          JAKELIB_DECLARE_CLASS(Throwable);
      };
  }
}

#endif

