jdk/src/share/classes/java/lang/Throwable.java

Print this page

        

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 1994, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
  * published by the Free Software Foundation.  Oracle designates this

@@ -187,22 +187,25 @@
      * @serial
      * @since 1.4
      */
     private StackTraceElement[] stackTrace;
     /*
-     * This field is lazily initialized on first use or serialization and
-     * nulled out when fillInStackTrace is called.
+     * This field above is lazily initialized on first use or
+     * serialization and nulled out when fillInStackTrace is called.
      */
 
+    private static final List<Throwable> suppressedSentinal =
+        Collections.unmodifiableList(new ArrayList<Throwable>(0));
+
     /**
      * The list of suppressed exceptions, as returned by
-     * {@link #getSuppressedExceptions()}.
+     * {@link #getSuppressed()}.
      *
      * @serial
      * @since 1.7
      */
-    private List<Throwable> suppressedExceptions = null;
+    private List<Throwable> suppressedExceptions = suppressedSentinal;
     /*
      * This field is lazily initialized when the first suppressed
      * exception is added.
      *
      * OutOfMemoryError is preallocated in the VM for better OOM

@@ -212,10 +215,13 @@
      */
 
     /** Message for trying to suppress a null exception. */
     private static final String NULL_CAUSE_MESSAGE = "Cannot suppress a null exception.";
 
+    /** Message for trying to suppress oneself. */
+    private static final String SELF_SUPPRESSION_MESSAGE = "Self-suppression not permitted";
+
     /** Caption  for labeling causative exception stack traces */
     private static final String CAUSE_CAPTION = "Caused by: ";
 
     /** Caption for labeling suppressed exception stack traces */
     private static final String SUPPRESSED_CAPTION = "Suppressed: ";

@@ -570,11 +576,11 @@
             StackTraceElement[] trace = getOurStackTrace();
             for (StackTraceElement traceElement : trace)
                 s.println("\tat " + traceElement);
 
             // Print suppressed exceptions, if any
-            for (Throwable se : getSuppressedExceptions())
+            for (Throwable se : getSuppressed())
                 se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, "\t", dejaVu);
 
             // Print cause, if any
             Throwable ourCause = getCause();
             if (ourCause != null)

@@ -611,11 +617,11 @@
                 s.println(prefix + "\tat " + trace[i]);
             if (framesInCommon != 0)
                 s.println(prefix + "\t... " + framesInCommon + " more");
 
             // Print suppressed exceptions, if any
-            for (Throwable se : getSuppressedExceptions())
+            for (Throwable se : getSuppressed())
                 se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION,
                                            prefix +"\t", dejaVu);
 
             // Print cause, if any
             Throwable ourCause = getCause();

@@ -784,17 +790,23 @@
         throws IOException, ClassNotFoundException {
         s.defaultReadObject();     // read in all fields
         List<Throwable> suppressed = null;
         if (suppressedExceptions != null &&
             !suppressedExceptions.isEmpty()) { // Copy Throwables to new list
-            suppressed = new ArrayList<Throwable>();
+            suppressed = new ArrayList<Throwable>(1);
             for (Throwable t : suppressedExceptions) {
                 if (t == null)
                     throw new NullPointerException(NULL_CAUSE_MESSAGE);
+                if (t == this)
+                    throw new IllegalArgumentException(SELF_SUPPRESSION_MESSAGE);
                 suppressed.add(t);
             }
         }
+
+        if (suppressed != null && suppressed.isEmpty())
+            suppressedExceptions = suppressedSentinal;
+        else
         suppressedExceptions = suppressed;
     }
 
     private synchronized void writeObject(ObjectOutputStream s)
         throws IOException

@@ -806,10 +818,18 @@
     /**
      * Adds the specified exception to the list of exceptions that
      * were suppressed, typically by the {@code try}-with-resources
      * statement, in order to deliver this exception.
      *
+     * If the first exception to be suppressed is {@code null}, that
+     * indicates suppressed exception information will <em>not</em> be
+     * recorded for this exception.  Subsequent calls to this method
+     * will not record any suppressed exceptions.  Otherwise,
+     * attempting to suppress {@code null} after an exception has
+     * already been successfully suppressed results in a {@code
+     * NullPointerException}.
+     *
      * <p>Note that when one exception {@linkplain
      * #initCause(Throwable) causes} another exception, the first
      * exception is usually caught and then the second exception is
      * thrown in response.  In contrast, when one exception suppresses
      * another, two exceptions are thrown in sibling code blocks, such

@@ -822,34 +842,52 @@
      * @throws NullPointerException if {@code exception} is null
      * @throws IllegalArgumentException if {@code exception} is this
      *         throwable; a throwable cannot suppress itself.
      * @since 1.7
      */
-    public synchronized void addSuppressedException(Throwable exception) {
-        if (exception == null)
-            throw new NullPointerException(NULL_CAUSE_MESSAGE);
+    public synchronized void addSuppressed(Throwable exception) {
         if (exception == this)
-            throw new IllegalArgumentException("Self-suppression not permitted");
+            throw new IllegalArgumentException(SELF_SUPPRESSION_MESSAGE);
+
+        if (exception == null) {
+            if (suppressedExceptions == suppressedSentinal) {
+                suppressedExceptions = null; // No suppression information recorded
+                return;
+            } else
+                throw new NullPointerException(NULL_CAUSE_MESSAGE);
+        } else {
+            assert exception != null && exception != this;
+
+            if (suppressedExceptions == null) // Suppressed exceptions not recorded
+                return;
+
+            if (suppressedExceptions == suppressedSentinal)
+                suppressedExceptions = new ArrayList<Throwable>(1);
+
+            assert suppressedExceptions != suppressedSentinal;
 
-        if (suppressedExceptions == null)
-            suppressedExceptions = new ArrayList<Throwable>();
         suppressedExceptions.add(exception);
+            return;
+        }
     }
 
     private static final Throwable[] EMPTY_THROWABLE_ARRAY = new Throwable[0];
 
     /**
      * Returns an array containing all of the exceptions that were
      * suppressed, typically by the {@code try}-with-resources
      * statement, in order to deliver this exception.
      *
+     * If no exceptions were suppressed, an empty array is returned.
+     *
      * @return an array containing all of the exceptions that were
      *         suppressed to deliver this exception.
      * @since 1.7
      */
-    public synchronized Throwable[] getSuppressedExceptions() {
-        if (suppressedExceptions == null)
+    public synchronized Throwable[] getSuppressed() {
+        if (suppressedExceptions == suppressedSentinal ||
+            suppressedExceptions == null)
             return EMPTY_THROWABLE_ARRAY;
         else
             return suppressedExceptions.toArray(EMPTY_THROWABLE_ARRAY);
     }
 }