private static String collapseText(
     Throwable t, boolean includeAllCausalMessages, Set<Throwable> visited) {
   if (t == null) return null;
   if (visited.contains(t)) {
     // IllegalStateException sometimes refers to itself; guard against stack overflows
     if (Strings.isNonBlank(t.getMessage())) return t.getMessage();
     else return "(" + t.getClass().getName() + ", recursive cause)";
   }
   Throwable t2 = collapse(t, true, includeAllCausalMessages, visited);
   visited = MutableSet.copyOf(visited);
   visited.add(t);
   visited.add(t2);
   if (t2 instanceof PropagatedRuntimeException) {
     if (((PropagatedRuntimeException) t2).isCauseEmbeddedInMessage())
       // normally
       return t2.getMessage();
     else if (t2.getCause() != null)
       return collapseText(t2.getCause(), includeAllCausalMessages, ImmutableSet.copyOf(visited));
     return "" + t2.getClass();
   }
   String result = t2.toString();
   if (!includeAllCausalMessages) {
     return result;
   }
   Throwable cause = t2.getCause();
   if (cause != null) {
     String causeResult = collapseText(new PropagatedRuntimeException(cause));
     if (result.indexOf(causeResult) >= 0) return result;
     return result + "; caused by " + causeResult;
   }
   return result;
 }
  private static Throwable collapse(
      Throwable source,
      boolean collapseCausalChain,
      boolean includeAllCausalMessages,
      Set<Throwable> visited) {
    visited = MutableSet.copyOf(visited);
    String message = "";
    Throwable collapsed = source;
    int collapseCount = 0;
    boolean messageIsFinal = false;
    // remove boring stack traces at the head
    while (isBoring(collapsed) && !messageIsFinal) {
      collapseCount++;
      Throwable cause = collapsed.getCause();
      if (cause == null) {
        // everything in the tree is boring...
        return source;
      }
      if (visited.add(collapsed)) {
        // there is a recursive loop
        break;
      }
      String collapsedS = collapsed.getMessage();
      if (collapsed instanceof PropagatedRuntimeException
          && ((PropagatedRuntimeException) collapsed).isCauseEmbeddedInMessage()) {
        message = collapsed.getMessage();
        messageIsFinal = true;
      } else if (Strings.isNonBlank(collapsedS)) {
        collapsedS =
            Strings.removeAllFromEnd(
                collapsedS,
                cause.toString(),
                stripBoringPrefixes(cause.toString()),
                cause.getMessage());
        collapsedS = stripBoringPrefixes(collapsedS);
        if (Strings.isNonBlank(collapsedS)) message = appendSeparator(message, collapsedS);
      }
      collapsed = cause;
    }
    // if no messages so far (ie we will be the toString) then remove boring prefixes from the
    // message
    Throwable messagesCause = collapsed;
    while (messagesCause != null && isPrefixBoring(messagesCause) && Strings.isBlank(message)) {
      collapseCount++;
      if (Strings.isNonBlank(messagesCause.getMessage())) {
        message = messagesCause.getMessage();
        messagesCause = messagesCause.getCause();
        break;
      }
      visited.add(messagesCause);
      messagesCause = messagesCause.getCause();
    }

    if (collapseCount == 0 && !includeAllCausalMessages) return source;

    if (collapseCount == 0 && messagesCause != null) {
      message = messagesCause.toString();
      messagesCause = messagesCause.getCause();
    }

    if (messagesCause != null && !messageIsFinal) {
      String extraMessage =
          collapseText(messagesCause, includeAllCausalMessages, ImmutableSet.copyOf(visited));
      message = appendSeparator(message, extraMessage);
    }
    if (message == null) message = "";
    return new PropagatedRuntimeException(message, collapseCausalChain ? collapsed : source, true);
  }
 private void assertEqualsIgnoringOrder(
     Iterable<? extends Object> col1, Iterable<? extends Object> col2) {
   assertEquals(Iterables.size(col1), Iterables.size(col2), "col2=" + col1 + "; col2=" + col2);
   assertEquals(
       MutableSet.copyOf(col1), MutableSet.copyOf(col2), "col1=" + col1 + "; col2=" + col2);
 }