Home | History | Annotate | Download | only in testing
      1 /*
      2  * Copyright (C) 2009 The Guava Authors
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.common.testing;
     18 
     19 import com.google.common.annotations.GwtCompatible;
     20 
     21 import java.util.ArrayList;
     22 import java.util.Collection;
     23 import java.util.Collections;
     24 
     25 /**
     26  * An {@link ClusterException} is a data structure that allows for some code to
     27  * "throw multiple exceptions", or something close to it. The prototypical code
     28  * that calls for this class is presented below:
     29  *
     30  * <pre>
     31  * void runManyThings(List&lt;ThingToRun&gt; thingsToRun) {
     32  *   for (ThingToRun thingToRun : thingsToRun) {
     33  *     thingToRun.run(); // <-- say this may throw an exception, but you want to
     34  *                       // always run all thingsToRun
     35  *   }
     36  * }
     37  * </pre>
     38  *
     39  * <p>This is what the code would become:
     40  *
     41  * <pre>
     42  * void runManyThings(List&lt;ThingToRun&gt; thingsToRun) {
     43  *   List&lt;Exception&gt; exceptions = Lists.newArrayList();
     44  *   for (ThingToRun thingToRun : thingsToRun) {
     45  *     try {
     46  *       thingToRun.run();
     47  *     } catch (Exception e) {
     48  *       exceptions.add(e);
     49  *     }
     50  *   }
     51  *   if (exceptions.size() > 0) {
     52  *     throw ClusterException.create(exceptions);
     53  *   }
     54  * }
     55  * </pre>
     56  *
     57  * <p>See semantic details at {@link #create(Collection)}.
     58  *
     59  * @author Luiz-Otavio Zorzella
     60  */
     61 @GwtCompatible
     62 final class ClusterException extends RuntimeException {
     63 
     64   public final Collection<? extends Throwable> exceptions;
     65 
     66   private ClusterException(Collection<? extends Throwable> exceptions) {
     67     super(
     68         exceptions.size() + " exceptions were thrown. The first exception is listed as a cause.",
     69         exceptions.iterator().next());
     70     ArrayList<Throwable> temp = new ArrayList<Throwable>();
     71     temp.addAll(exceptions);
     72     this.exceptions = Collections.unmodifiableCollection(temp);
     73   }
     74 
     75   /**
     76    * @see #create(Collection)
     77    */
     78   public static RuntimeException create(Throwable... exceptions) {
     79     ArrayList<Throwable> temp = new ArrayList<Throwable>();
     80     for (Throwable exception : exceptions) {
     81       temp.add(exception);
     82     }
     83     return create(temp);
     84   }
     85 
     86   /**
     87    * Given a collection of exceptions, returns a {@link RuntimeException}, with
     88    * the following rules:
     89    *
     90    * <ul>
     91    *  <li>If {@code exceptions} has a single exception and that exception is a
     92    *    {@link RuntimeException}, return it
     93    *  <li>If {@code exceptions} has a single exceptions and that exceptions is
     94    *    <em>not</em> a {@link RuntimeException}, return a simple
     95    *    {@code RuntimeException} that wraps it
     96    *  <li>Otherwise, return an instance of {@link ClusterException} that wraps
     97    *    the first exception in the {@code exceptions} collection.
     98    * </ul>
     99    *
    100    * <p>Though this method takes any {@link Collection}, it often makes most
    101    * sense to pass a {@link java.util.List} or some other collection that
    102    * preserves the order in which the exceptions got added.
    103    *
    104    * @throws NullPointerException if {@code exceptions} is null
    105    * @throws IllegalArgumentException if {@code exceptions} is empty
    106    */
    107   public static RuntimeException create(Collection<? extends Throwable> exceptions) {
    108     if (exceptions.size() == 0) {
    109       throw new IllegalArgumentException(
    110           "Can't create an ExceptionCollection with no exceptions");
    111     }
    112     if (exceptions.size() == 1) {
    113       Throwable temp = exceptions.iterator().next();
    114       if (temp instanceof RuntimeException) {
    115         return (RuntimeException)temp;
    116       } else {
    117         return new RuntimeException(temp);
    118       }
    119     }
    120     return new ClusterException(exceptions);
    121   }
    122 }
    123