001package com.identityworksllc.iiq.common;
002
003import java.io.PrintStream;
004import java.io.PrintWriter;
005import java.util.Objects;
006import java.util.StringJoiner;
007
008/**
009 * This exception is to be used whenever an exception is caught inside of a catch
010 * block. It will display details of both exceptions on printStackTrace and other
011 * methods.
012 *
013 * The first exception will be treated like any other "cause" passed to an
014 * exception, so it will be returned by {@link Throwable#getCause()}, while
015 * the second exception will be stored separately as {@link #t2}.
016 */
017public final class PairedException extends Throwable {
018    /**
019     * The internal second exception
020     */
021    private final Throwable t2;
022
023    /**
024     * Constructs a new {@link PairedException} from the two throwables
025     * @param t1 The first throwable
026     * @param t2 The second throwable
027     */
028    public PairedException(Throwable t1, Throwable t2) {
029        super(t1);
030        Objects.requireNonNull(t2);
031        this.t2 = t2;
032    }
033
034    /**
035     * Constructs a new {@link PairedException} from the two throwables
036     * @param t1 The first throwable
037     * @param t2 The second throwable
038     */
039    public PairedException(String message, Throwable t1, Throwable t2) {
040        super(message, t1);
041        Objects.requireNonNull(t2);
042        this.t2 = t2;
043    }
044
045    @Override
046    public String getLocalizedMessage() {
047        return new StringJoiner(",", PairedException.class.getSimpleName() + "[", "]")
048                .add("m1=" + super.getLocalizedMessage())
049                .add("m2=" + t2.getLocalizedMessage())
050                .toString();
051    }
052
053    @Override
054    public String getMessage() {
055        return new StringJoiner(",", PairedException.class.getSimpleName() + "[", "]")
056                .add("m1=" + super.getMessage())
057                .add("m2=" + t2.getMessage())
058                .toString();
059    }
060
061    /**
062     * Gets the second cause in this paired exception
063     * @return The second cause
064     */
065    public Throwable getSecondCause() {
066        return t2;
067    }
068
069    /**
070     * Gets the second cause's stack trace in this paired exception
071     * @return The second cause's stack trace
072     */
073    public StackTraceElement[] getSecondStackTrace() {
074        Objects.requireNonNull(t2);
075        return t2.getStackTrace();
076    }
077
078    @Override
079    public void printStackTrace(PrintWriter s) {
080        super.printStackTrace(s);
081        if (t2 != null) {
082            t2.printStackTrace(s);
083        }
084    }
085
086    @Override
087    public void printStackTrace() {
088        super.printStackTrace();
089        if (t2 != null) {
090            t2.printStackTrace();
091        }
092    }
093
094    @Override
095    public void printStackTrace(PrintStream s) {
096        super.printStackTrace(s);
097        if (t2 != null) {
098            t2.printStackTrace(s);
099        }
100    }
101
102    @Override
103    public String toString() {
104        return new StringJoiner(", ", PairedGeneralException.class.getSimpleName() + "[", "]")
105                .add("t1=" + getCause().toString())
106                .add("t2=" + getSecondCause().toString())
107                .toString();
108    }
109}