Hi,

The 1.4 spec finally defines a generic Exception chaining mechanism
so you don't have to implement this in an ad hoc way for every project.
It also defines a general StackTraceElement that can be used to inspect
the call stack of an exception. I have seen people do this by parsing the
output of printStackTrace which is clever, but not really portable or
reliably.

The attached patch implements the Java side of this. I am not going to
check this in because I have not yet looked at the serialization issues.
It is attached so that people know what I am working on.

I was hoping that we could use the java version of printStackTrace and
let the VM only handle the fillInStackTrace() <-> StackTraceElement side
of things. This might be doable for japhar as far as I can understand the
code. But for libgcj this might be a bit more difficuly since it seems
to use external programs (addr2line and c++filt) through pipes to get
and print this information.

Cheers,

Mark
-- 
Stuff to read:
    <http://www.toad.com/gnu/whatswrong.html>
  What's Wrong with Copy Protection, by John Gilmore
Index: java/lang/ClassNotFoundException.java
===================================================================
RCS file: /cvs/classpath/java/lang/ClassNotFoundException.java,v
retrieving revision 1.5
diff -u -u -r1.5 ClassNotFoundException.java
--- java/lang/ClassNotFoundException.java       2000/03/16 23:31:21     1.5
+++ java/lang/ClassNotFoundException.java       2001/07/10 00:45:59
@@ -31,10 +31,11 @@
 import java.io.ObjectOutputStream;
 import java.io.ObjectInputStream;
 import java.io.IOException;
-import java.io.PrintStream;
-import java.io.PrintWriter;
 
 /**
+ * Thrown when a class represented by a String could not be found by either
+ * <code>Class.forName()</code>, <code>ClassLoader.findSystemClass()</code>
+ * or <code>ClassLoader.loadClass()</code>.
  * Exceptions may be thrown by one part of a Java program and caught
  * by another in order to deal with exceptional conditions.  This 
  * exception can by thrown by specific methods of <code>ClassLoader</code>
@@ -93,50 +94,16 @@
     }
 
   /**
-   * Print a stack trace of the exception that occurred.
-   */
-  public void printStackTrace()
-    {
-      if (ex == null)
-        {
-          super.printStackTrace();
-        }
-      else
-        {
-          ex.printStackTrace();
-        }
-    }
-
-  /**
-   * Print a stack trace of the exception that occurred to 
-   * the specified <code>PrintStream</code>.
-   */
-  public void printStackTrace(PrintStream ps)
-    {
-      if (ex == null)
-        {
-          super.printStackTrace(ps);
-        }
-      else
-        {
-          ex.printStackTrace(ps);
-        }
-    }
-
-  /**
-   * Print a stack trace of the exception that occurred to 
-   * the specified <code>PrintWriter</code>.
+   * Returns the exception which occurred while loading the class, 
+   * otherwise returns null.
+   * Returns the same as <code>getException()</code> to support the general
+   * exception chaining mechanism of 1.4.
+   * 
+   * @since JDK 1.4
    */
-  public void printStackTrace(PrintWriter pw)
+  public Throwable getCause()
     {
-      if (ex == null)
-        {
-          super.printStackTrace(pw);
-        }
-      else
-        {
-          ex.printStackTrace(pw);
-        }
+      return ex;
     }
 
   /**
Index: java/lang/Error.java
===================================================================
RCS file: /cvs/classpath/java/lang/Error.java,v
retrieving revision 1.4
diff -u -u -r1.4 Error.java
--- java/lang/Error.java        2001/03/11 15:52:38     1.4
+++ java/lang/Error.java        2001/07/10 00:45:59
@@ -55,6 +55,8 @@
 
   /**
    * Create an error without a message.
+   * The cause will not be initialized which means it can be set later by
+   * calling <code>initCause()</code>.
    */
   public Error()
     {
@@ -63,9 +65,34 @@
 
   /**
    * Create an error with a message.
+   * The cause will not be initialized which means it can be set later by
+   * calling <code>initCause()</code>.
    */
   public Error(String s)
     {
       super(s);
+    }
+
+  /**
+   * Create an error with a cause.
+   * If the cause is not null the message will be set to the result of calling
+   * <code>toString()</code> on the cause otherwise the message will be null.
+   *
+   * @since 1.4
+   */
+  public Error(Throwable c)
+    {
+      super(c);
+    }
+
+  /**
+   * Create an error with a cause and a message.
+   * Both parameters may be null.
+   *
+   * @since 1.4
+   */
+  public Error(String s, Throwable c)
+    {
+      super(s, c);
     }
 }
Index: java/lang/Exception.java
===================================================================
RCS file: /cvs/classpath/java/lang/Exception.java,v
retrieving revision 1.6
diff -u -u -r1.6 Exception.java
--- java/lang/Exception.java    2001/03/11 15:52:38     1.6
+++ java/lang/Exception.java    2001/07/10 00:45:59
@@ -51,6 +51,8 @@
 
   /**
    * Create an exception without a message.
+   * The cause will not be initialized which means it can be set later by
+   * calling <code>initCause()</code>.
    */
   public Exception()
     {
@@ -59,9 +61,34 @@
 
   /**
    * Create an exception with a message.
+   * The cause will not be initialized which means it can be set later by
+   * calling <code>initCause()</code>.
    */
   public Exception(String s)
     {
       super(s);
+    }
+
+  /**
+   * Create an exception with a cause.
+   * If the cause is not null the message will be set to the result of calling
+   * <code>toString()</code> on the cause otherwise the message will be null.
+   *
+   * @since 1.4
+   */
+  public Exception(Throwable c)
+    {
+      super(c);
+    }
+
+  /**
+   * Create an exception with a cause and a message.
+   * Both parameters may be null.
+   *
+   * @since 1.4
+   */
+  public Exception(String s, Throwable c)
+    {
+      super(s, c);
     }
 }
Index: java/lang/ExceptionInInitializerError.java
===================================================================
RCS file: /cvs/classpath/java/lang/ExceptionInInitializerError.java,v
retrieving revision 1.6
diff -u -u -r1.6 ExceptionInInitializerError.java
--- java/lang/ExceptionInInitializerError.java  2001/03/19 22:15:47     1.6
+++ java/lang/ExceptionInInitializerError.java  2001/07/10 00:45:59
@@ -27,8 +27,6 @@
 
 package java.lang;
 
-import java.io.PrintStream;
-import java.io.PrintWriter;
 
 /* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3
  * "The Java Language Specification", ISBN 0-201-63451-1
@@ -92,53 +90,19 @@
       return exception;
     }
 
-  /**
-   * Print a stack trace of the exception that occurred.
-   */
-  public void printStackTrace()
-    {
-      if (exception == null)
-       {
-         super.printStackTrace();
-       }
-      else
-       {
-         System.err.print(this.getClass() + ": ");
-         exception.printStackTrace();
-       }
-    }
-
-  /**
-   * Print a stack trace of the exception that occurred to 
-   * the specified <code>PrintStream</code>.
-   */
-  public void printStackTrace(PrintStream ps)
-    {
-      if (exception == null)
-       {
-         super.printStackTrace(ps);
-       }
-      else
-       {
-         ps.print(this.getClass() + ": ");
-         exception.printStackTrace(ps);
-       }
-    }
-
-  /**
-   * Print a stack trace of the exception that occurred to 
-   * the specified <code>PrintWriter</code>.
+  /** 
+   * Return the exception that caused this error to be created.
+   * Returns the same exception as <code>getException</code> to support
+   * the general exception chaining mechanism of 1.4.
+   *
+   * @return the stored <code>Throwable</code> object or <code>null</code>
+   * if this <code>ExceptionInInitializerError</code> has no stored
+   * <code>Throwable</code> object.
+   *
+   * @since 1.4
    */
-  public void printStackTrace(PrintWriter pw)
+  public Throwable getCause()
     {
-      if (exception == null)
-       {
-         super.printStackTrace(pw);
-       }
-      else
-       {
-         pw.print(this.getClass() + ": ");
-         exception.printStackTrace(pw);
-       }
+      return exception;
     }
 }
Index: java/lang/RuntimeException.java
===================================================================
RCS file: /cvs/classpath/java/lang/RuntimeException.java,v
retrieving revision 1.6
diff -u -u -r1.6 RuntimeException.java
--- java/lang/RuntimeException.java     2001/03/11 15:52:38     1.6
+++ java/lang/RuntimeException.java     2001/07/10 00:45:59
@@ -54,6 +54,8 @@
 
   /**
    * Create an exception without a message.
+   * The cause will not be initialized which means it can be set later by
+   * calling <code>initCause()</code>.
    */
   public RuntimeException()
     {
@@ -62,9 +64,35 @@
 
   /**
    * Create an exception with a message.
+   * The cause will not be initialized which means it can be set later by
+   * calling <code>initCause()</code>.
    */
   public RuntimeException(String s)
     {
       super(s);
     }
+
+  /**
+   * Create an exception with a cause.
+   * If the cause is not null the message will be set to the result of calling
+   * <code>toString()</code> on the cause otherwise the message will be null.
+   *
+   * @since 1.4
+   */
+  public RuntimeException(Throwable c)
+    {
+      super(c);
+    }
+
+  /**
+   * Create an exception with a cause and a message.
+   * Both parameters may be null.
+   *
+   * @since 1.4
+   */
+  public RuntimeException(String s, Throwable c)
+    {
+      super(s, c);
+    }
+
 }
Index: java/lang/StackTraceElement.java
===================================================================
RCS file: StackTraceElement.java
diff -N StackTraceElement.java
--- /dev/null   Sat Apr 14 17:46:23 2001
+++ StackTraceElement.java      Mon Jul  9 17:45:59 2001
@@ -0,0 +1,208 @@
+/* java.lang.StackTraceElement - One function call or call stack element.
+   Copyright (C) 2001 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+ 
+GNU Classpath 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
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+As a special exception, if you link this library with other files to
+produce an executable, this library does not by itself cause the
+resulting executable to be covered by the GNU General Public License.
+This exception does not however invalidate any other reasons why the
+executable file might be covered by the GNU General Public License. */
+
+package java.lang;
+
+import java.io.Serializable;
+
+/**
+ * One function call or stack trace element. Gives information about
+ * the execution point such as the source file name, the line number,
+ * the fully qualified class name, the method name and wether this method
+ * is native if this information is known.
+ * <p>
+ * XXX - FIXME - Lookup serializable information.
+ *
+ * @author Mark Wielaard ([EMAIL PROTECTED])
+ *
+ * @since 1.4
+ */
+public class StackTraceElement implements Serializable {
+
+    // Variables
+
+    /** The name of the file or null if unknown */
+    final private String fileName;
+
+    /** The line number in the file or negative if unknown */
+    final private int lineNumber;
+
+    /** The fully qualified class name or null if unknown */
+    final private String className;
+
+    /** The method name in the class or null if unknown */
+    final private String methodName;
+
+    /** If the method is a native method true, otherwise false */
+    final private boolean isNative;
+
+    // Constructors
+
+    /** 
+     * A package local constructor for the StackTraceElement class.
+     * There are no public constructors defined for this class. Creation
+     * of new elements is implementation specific.
+     * 
+     * @param fileName The name of the file or null if unknown
+     * @param lineNumber The line in the file or negative if unknown
+     * @param className The fully qualified name of the class or null if unknown
+     * @param methodName The name of the method or null if unknown
+     * @param isNative true if native, false otherwise
+     */
+    StackTraceElement(String fileName, int lineNumber, String className,
+                         String methodName, boolean isNative)
+    {
+        this.fileName = fileName;
+        this.lineNumber = lineNumber;
+        this.className = className;
+        this.methodName = methodName;
+        this.isNative = isNative;
+    }
+
+    // Methods
+
+    /** 
+     * Returns the name of the file or null if unknown.
+     */
+    public String getFileName()
+    {
+        return fileName;
+    }
+
+    /** 
+     * Returns the line number in the file or a negative number if unknown.
+     */
+    public int getLineNumber()
+    {
+        return lineNumber;
+    }
+
+    /** 
+     * Returns the fully qualified class name or null if unknown.
+     */
+    public String getClassName()
+    {
+        return className;
+    }
+
+    /** 
+     * Returns the method name in the class or null if unknown.
+     */
+    public String getMethodName()
+    {
+        return methodName;
+    }
+
+    /** 
+     * Returns true if the method is native or false if it is not or unknown.
+     */
+    public boolean isNativeMethod()
+    {
+        return isNative;
+    }
+
+    /**
+     * Returns true if the given object is also a StackTraceElement
+     * and all attributes, except the native flag are equal.
+     */
+    public boolean equals(Object o)
+    {
+        if (o instanceof StackTraceElement) {
+          StackTraceElement e = (StackTraceElement)o;
+          boolean equal =
+            ((fileName == e.fileName)
+              || ((fileName != null) && fileName.equals(e.fileName)))
+            &&
+            ((lineNumber == e.lineNumber)
+              || ((lineNumber < 0) && (e.lineNumber < 0)))
+            &&
+            ((className == e.className)
+              || ((className != null) && className.equals(e.className)))
+            &&
+            ((methodName == e.methodName)
+              || ((methodName != null) && methodName.equals(e.methodName)));
+
+            return equal;
+        }
+        return false;
+    }
+
+    /** 
+     * Returns the hashCode of this StackTraceElement.
+     * <p>
+     * This implementation computes the hashcode by xor-ing the hashcode of all
+     * attributes except the native flag.
+     */
+    public int hashCode()
+    {
+        return fileName.hashCode() ^ lineNumber
+                      ^ className.hashCode() ^ methodName.hashCode();
+    }
+
+    /** 
+     * Returns a string representation of this stack trace element.
+     * <p>
+     * The returned String is implementation specific. This implementation
+     * returns the following String: "[class][.][method]([file][:line])".
+     * If the fully qualified class name or the method is unknown it is
+     * omitted including the point seperator. If the source file name is
+     * unknown it is replaced by "Unknown Source" if the method is not native
+     * or by "Native Method" if the method is native. If the line number
+     * is unknown it and the colon are omitted.
+     */
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+
+        if (className != null)
+            sb.append(className);
+
+        if (className != null && methodName != null)
+            sb.append('.');
+
+        if (methodName != null)
+            sb.append(methodName);
+
+        sb.append('(');
+
+        if (fileName != null)
+            sb.append(fileName);
+        else
+            if (!isNative)
+                sb.append("Unknown Source");
+            else
+                sb.append("Native Method");
+
+        if (lineNumber >= 0)
+        {
+            sb.append(':');
+            sb.append(lineNumber);
+        }
+
+        sb.append(')');
+
+        return sb.toString();
+    }
+}
Index: java/lang/reflect/InvocationTargetException.java
===================================================================
RCS file: /cvs/classpath/java/lang/reflect/InvocationTargetException.java,v
retrieving revision 1.6
diff -u -u -r1.6 InvocationTargetException.java
--- java/lang/reflect/InvocationTargetException.java    2001/03/19 22:15:47     1.6
+++ java/lang/reflect/InvocationTargetException.java    2001/07/10 00:45:59
@@ -27,9 +27,6 @@
 
 package java.lang.reflect;
 
-import java.io.PrintStream;
-import java.io.PrintWriter;
-
 /* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3
  * "The Java Language Specification", ISBN 0-201-63451-1
  * Status:  Believed complete and correct.
@@ -92,37 +89,18 @@
     {
       return target;
     }
-
-  public void printStackTrace()
-    {
-      if (target == null)
-       super.printStackTrace();
-      else
-      {
-       System.err.print(this.getClass() + ": ");
-       target.printStackTrace();
-      }
-    }
-
-  public void printStackTrace(PrintStream ps)
-    {
-      if (target == null)
-       super.printStackTrace(ps);
-      else
-      {
-       ps.print(this.getClass() + ": ");
-       target.printStackTrace(ps);
-      }
-    }
 
-  public void printStackTrace(PrintWriter pw)
+  /**
+   * Get the wrapped (targeted) exception.
+   * Returns the same exception as <code>getTargetException()</code> to
+   * support the general 1.4 exception chaining mechanism.
+   * 
+   * @return the targeted exception.
+   *
+   * @since 1.4
+   */
+  public Throwable getCause() 
     {
-      if (target == null)
-       super.printStackTrace(pw);
-      else
-      {
-       pw.print(this.getClass() + ": ");
-       target.printStackTrace(pw);
-      }
+      return target;
     }
 }
Index: java/security/PrivilegedActionException.java
===================================================================
RCS file: /cvs/classpath/java/security/PrivilegedActionException.java,v
retrieving revision 1.2
diff -u -u -r1.2 PrivilegedActionException.java
--- java/security/PrivilegedActionException.java        2000/03/16 20:18:21     1.2
+++ java/security/PrivilegedActionException.java        2001/07/10 00:45:59
@@ -1,6 +1,6 @@
 /* PrivilegedActionException.java -- An exception occurred in a 
    privileged action
-   Copyright (C) 1998 Free Software Foundation, Inc.
+   Copyright (C) 1998, 2001 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -28,9 +28,6 @@
 
 package java.security;
 
-import java.io.PrintStream;
-import java.io.PrintWriter;
-
 /**
   * This exception is thrown when an exception is thrown during a
   * privileged action being performed with the 
@@ -91,43 +88,20 @@
   return(e);
 }
 
-/*************************************************************************/
-
 /**
-  * This method prints the stack trace of the wrappered exception.
-  */
-public void
-printStackTrace()
-{
-  e.printStackTrace();
-}
-
-/*************************************************************************/
-
-/**
-  * This method prints the stack trace of the wrappered exception to the
-  * specified <code>PrintStream</code>.
+  * This method returns the underlying <code>Exception</code> that caused
+  * this exception to be raised.
+  * Returns the same exception as <code>getException()</code> to support
+  * the general exception chaining mechanism of 1.4.
   *
-  * @param ps The <code>PrintStream</code> to print the stack trace to.
-  */
-public void
-printStackTrace(PrintStream ps)
-{
-  e.printStackTrace(ps);
-}
-
-/*************************************************************************/
-
-/**
-  * This method prints the stack trace of the wrappered exception to the
-  * specified <code>PrintWriter</code>.
+  * @return The wrappered <code>Exception</code>.
   *
-  * @param pw The <code>PrintWriter</code> to print the stack trace to.
+  * @since 1.4
   */
-public void
-printStackTrace(PrintWriter pw)
+public Throwable
+getCause()
 {
-  e.printStackTrace(pw);
+  return(e);
 }
 
 } // class PrivilegedActionException
Index: vm/reference/java/lang/Throwable.java
===================================================================
RCS file: /cvs/classpath/vm/reference/java/lang/Throwable.java,v
retrieving revision 1.1
diff -u -u -r1.1 Throwable.java
--- vm/reference/java/lang/Throwable.java       2000/12/10 17:42:10     1.1
+++ vm/reference/java/lang/Throwable.java       2001/07/10 00:45:59
@@ -1,5 +1,5 @@
 /* java.lang.Throwable
-   Copyright (C) 1998 Free Software Foundation, Inc.
+   Copyright (C) 1998, 2001 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -37,9 +37,10 @@
 /**
  * Throwable is the superclass of all exceptions that can be raised.
  *
- * @version 1.1.0, Oct 5 1998
+ * @version 1.3.99, Jul 8 2001
  * @author Brian Jones
  * @author John Keiser
+ * @author Mark Wielaard
  * @since JDK1.0
  */
 public class Throwable extends Object implements Serializable 
@@ -48,28 +49,71 @@
 
   private String message = null;
 
+  /**
+   * The Throwable that caused this Throwable. This field is null if the
+   * cause is explicitly set to null, it points to 'this' if it has not yet
+   * been explicitly set and it points to another Throwable if it has been
+   * explicitly set by <code>initCause()</code>.
+   */
+  private Throwable cause;
+
   static 
   {
     System.loadLibrary ("runtime");
   }  
 
   /** 
-   * Instantiate this Throwable with an empty message. 
+   * Instantiate this Throwable with an empty message.
+   * Does not initialize the cause which means that <code>initCause</code>
+   * can be called later.
    */
   public Throwable() {
-    this(null);
+    fillInStackTrace();
+    this.message = null;
+    this.cause = this;
   }
   
   /**
    * Instantiate this Throwable with the given message.
+   * Does not initialize the cause which means that <code>initCause</code>
+   * can be called later.
    * @param message the message to associate with the Throwable.
    */
   public Throwable(String message) {
     fillInStackTrace();
     this.message = message;
+    this.cause = this;
   }
-  
+
   /**
+   * Instantiate this Throwable with the given cause.
+   * If the cause if not null the message will be set to the result of
+   * the <code>cause.toString()</call>, otherwise it will be set to null.
+   * @param cause the cause to associate with the Throwable
+   *
+   * @since 1.4
+   */
+  public Throwable(Throwable cause) {
+    fillInStackTrace();
+    this.message = (cause != null) ? cause.toString() : null;
+       this.cause = cause;
+  }
+
+  /**
+   * Instantiate this Throwable with the given message and cause.
+   * Both parameters may be null.
+   * @param message the message to associate with the Throwable
+   * @param cause the cause to associate with the Throwable
+   *
+   * @since 1.4
+   */
+  public Throwable(String message, Throwable cause) {
+    fillInStackTrace();
+    this.message = message;
+    this.cause = cause;
+  }
+
+  /**
    * Get the message associated with this Throwable.
    * @return the error message associated with this Throwable.
    */
@@ -88,14 +132,71 @@
   public String getLocalizedMessage() {
     return getMessage();
   }
-  
   /**
+   * Sets the cause of this Throwable if it has not already been set.
+   * This can be even be used when the Throwable subclass has no contructor
+   * that takes a cause. So if the only declared exception for an method
+   * or interface allows you to throw for example a <code>IOException</code>
+   * but the real cause is some <code>SQLException</code> then you can
+   * do the following:
+   * <pre>
+   *   try {
+   *       ...
+   *   } catch (SQLException sqle) {
+   *       throw new IOException(sqle.toString()).initCause(sqle);
+   *   }
+   * </pre>
+   * The cause cannot be the Throwable itself and the cause can be set
+   * only once. Note that this method returns the Throwable itself so
+   * it can be easily used as above in a return statement.
+   *
+   * @return this Throwable
+   * @exception IllegalArgumentException if <code>cause == this</code>
+   * @exception IllegalStateException if the cause was already initialized
+   *
+   * @since 1.4
+   */
+  public Throwable initCause(Throwable cause) {
+    if (cause == this)
+      throw new IllegalArgumentException("Throwable cannot be its own cause");
+
+    if (this.cause != this)
+      throw new IllegalStateException("cause already initialized");
+
+    this.cause = cause;
+
+       return this;
+  }
+
+  /**
+   * Returns the cause of this Throwable or null when it is unknown or
+   * has not yet been set.
+   *
+   * @since 1.4
+   */
+  public Throwable getCause() {
+    // Check that the cause has been initialized
+    if (cause == this)
+      return null;
+    else
+      return cause;
+  }
+
+  /**
    * Get a human-readable representation of this Throwable.
-   * @return a human-readable String represting this Throwable.
-   * @XXX find out what exactly this should look like.
+   * The string returned starts with the actual class name (as given by
+   * <code>getClass().getName()</code>), if <code>getMessage()</code>
+   * does not return null then the string ": " and the actual message
+   * is appended.
+   * @return a human-readable String representing this Throwable.
    */
   public String toString() {
-    return getClass().getName() + (message != null ? ": " + message : "");
+    String name = getClass().getName();
+    String message = getMessage();
+    if (message == null)
+      return name;
+    else
+      return name + ": " + message;
   }
   
   /** 
@@ -125,6 +226,89 @@
 
   private native void printStackTrace0 (Object stream);
 
+  /**
+   * Alternative printStackTrace that uses <code>getStrackTrace()</code>
+   * to print the exception, stack trace and cause (recursively). Not used
+   * since <code>getStackTrace()</code> has not yet been implemented - FIXME.
+   * <p>
+   * 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 objects
+   * <code>toString()</code> method.
+   * <br>
+   * Then for all elements given by <code>getStackTrace</code> it prints
+   * a line containing a tab, 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 a tab and the string "<<No stacktrace available>>".
+   * <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 a tab and the string "... X more" is printed,
+   * where X is the number of remaining stackframes.
+   */
+  private void printStackTrace1(PrintWriter pw) {
+    // First line
+    pw.println(toString());
+
+    // The stacktrace
+    StackTraceElement[] stack = getStackTrace();
+    for (int i = 0; i < stack.length; i++) {
+      pw.print("\tat ");
+      pw.println(stack[i].toString());
+    }
+
+    // The cause(s)
+    Throwable cause = getCause();
+    while (cause != null) {
+      // Cause first line
+      pw.print("Caused by: ");
+      pw.println(cause.toString());
+
+      // Cause stacktrace
+      StackTraceElement[] parentStack = stack;
+      stack = cause.getStackTrace();
+      boolean equal = false; // Is rest of the stack is equal to parent frame?
+      for (int i = 0; i < stack.length && !equal; i++) {
+        // Check if we already printed the rest of the stack
+        // since it was the tail of the parent stack
+        int remaining = stack.length - i;
+        int element = i;
+        int parentElement = parentStack.length - remaining;
+        equal = parentElement >= 0
+                && parentElement < parentStack.length; // be optimistic
+        while(equal && element < stack.length) {
+          if (stack[element].equals(parentStack[parentElement])) {
+            element++;
+            parentElement++;
+          } else {
+            equal = false;
+          }
+        }
+        // Print stacktrace element or indicate the rest is equal 
+        if (!equal) {
+          pw.print("\tat ");
+          pw.println(stack[i]);
+        } else {
+          pw.print("\t...");
+          pw.print(remaining);
+          pw.println(" more");
+          break; // from stack printing for loop
+        }
+      }
+      cause = cause.getCause();
+    }
+  }
+   
   /** 
    * Fill in the stack trace with the current execution stack.
    * Normally used when rethrowing an exception, to strip
@@ -132,6 +316,21 @@
    * @return this same throwable.
    */
   public native Throwable fillInStackTrace();
+
+  /**
+   * Returns an array of stack trace elements representing the execution
+   * points on the call stack when <code>fillInStackTrace()</code> was
+   * called. The first element represents the most recent execution point
+   * the last element is the oldest. Depending on the implementation all,
+   * some or none of the elements are included.
+   * <p>
+   * XXX - FIXME - Not implemented yet, always returns an empty array.
+   *
+   * @since 1.4
+   */
+  public StackTraceElement[] getStackTrace() {
+    return new StackTraceElement[0];
+  }
 
   /**
    * Serialize the object in a manner binary compatible with the JDK 1.2

Reply via email to