1818import java .io .IOException ;
1919import java .io .PrintStream ;
2020import java .io .PrintWriter ;
21-
21+ import java . io . StringWriter ;
2222import java .util .Collections ;
23+ import java .util .IdentityHashMap ;
2324import java .util .List ;
2425import java .util .Objects ;
2526import java .util .Optional ;
26-
27+ import java . util . Set ;
2728import java .util .function .BiConsumer ;
2829import java .util .function .Consumer ;
30+ import java .util .stream .Collectors ;
2931
3032public class MultitargetException extends Exception {
3133 private final static long serialVersionUID = 1L ;
@@ -37,6 +39,25 @@ public MultitargetException(List<Throwable> exceptions) {
3739 Collections .emptyList ()
3840 :
3941 Collections .unmodifiableList (exceptions );
42+ List <Throwable > causes = this .exceptions
43+ .stream ()
44+ .filter (Objects ::nonNull )
45+ .collect (Collectors .toList ());
46+
47+ // Need to report back internal details in some standard way
48+ // If there is a single error - then it's the cause
49+ // Otherwie no dedicated cause and a list of suppressed exceptions
50+ switch (causes .size ()) {
51+ case 0 :
52+ break ;
53+ case 1 :
54+ initCause (causes .get (0 ));
55+ break ;
56+ default :
57+ for (Throwable cause : causes ) {
58+ addSuppressed (cause );
59+ }
60+ }
4061 }
4162
4263 public List <Throwable > getExceptions () {
@@ -52,27 +73,109 @@ public static MultitargetException of(final Throwable exception) {
5273 }
5374
5475 @ Override
55- public void printStackTrace (PrintStream s ) {
76+ public String toString () {
77+ return getClass ().getName ();
78+ }
79+
80+ @ Override
81+ public String getMessage () {
82+ StringWriter w = new StringWriter ();
83+ printDetails (new PrintWriter (w ), newDejavueSet ());
84+ return w .toString ();
85+ }
86+
87+ void printDetails (PrintWriter w , Set <Throwable > visited ) {
88+ visited .add (this );
89+ w .println (toStringSafe ());
90+ printExceptions (w , (ex , padding ) -> {
91+ PrintWriter pw = new PrintWriter (new PaddedWriter (w , padding ), true );
92+ if (visited .contains (ex )) {
93+ String message = ex instanceof MultitargetException ?
94+ ((MultitargetException )ex ).toStringSafe ()
95+ :
96+ ex .toString ();
97+ pw .println ("\t [CIRCULAR REFERENCE:" + message + "]" );
98+ } else {
99+ if (ex instanceof MultitargetException ) {
100+ ((MultitargetException )ex ).printDetails (pw , visited );
101+ } else {
102+ pw .println (ex .toString ());
103+ }
104+ }
105+ });
106+
107+ }
108+
109+ public void printExceptions () {
110+ printExceptions (System .err );
111+ }
112+
113+ public void printExceptions (PrintStream s ) {
56114 synchronized (s ) {
57- super .printStackTrace (s );
58- printExceptions (s , (ex , padding ) -> {
59- PrintStream ps = new PrintStream (new PaddedOutputStream (s , padding ));
60- ex .printStackTrace (ps );
61- });
115+ printExceptions (s , newDejavueSet ());
62116 }
63117 }
64118
65- @ Override
66- public void printStackTrace (PrintWriter w ) {
119+ void printExceptions (PrintStream s , Set <Throwable > visited ) {
120+ visited .add (this );
121+
122+ //super.printStackTrace(s);
123+ // Print our stack trace
124+ s .println (toStringSafe ());
125+ for (StackTraceElement trace : getStackTrace ())
126+ s .println ("\t at " + trace );
127+
128+ printExceptions (s , (ex , padding ) -> {
129+ PrintStream ps = new PrintStream (new PaddedOutputStream (s , padding ));
130+ if (visited .contains (ex )) {
131+ String message = ex instanceof MultitargetException ?
132+ ((MultitargetException )ex ).toStringSafe ()
133+ :
134+ ex .toString ();
135+ ps .println ("\t [CIRCULAR REFERENCE:" + message + "]" );
136+ } else {
137+ if (ex instanceof MultitargetException ) {
138+ ((MultitargetException )ex ).printExceptions (ps , visited );
139+ } else {
140+ ex .printStackTrace (ps );
141+ }
142+ }
143+ });
144+ }
145+
146+ public void printExceptions (PrintWriter w ) {
67147 synchronized (w ) {
68- super .printStackTrace (w );
69- printExceptions (w , (ex , padding ) -> {
70- PrintWriter pw = new PrintWriter (new PaddedWriter (w , padding ), true );
71- ex .printStackTrace (pw );
72- });
148+ printExceptions (w , newDejavueSet ());
73149 }
74150 }
75151
152+ void printExceptions (PrintWriter w , Set <Throwable > visited ) {
153+ visited .add (this );
154+
155+ //super.printStackTrace(s);
156+ // Print our stack trace
157+ w .println (toStringSafe ());
158+ for (StackTraceElement trace : getStackTrace ())
159+ w .println ("\t at " + trace );
160+
161+ printExceptions (w , (ex , padding ) -> {
162+ PrintWriter pw = new PrintWriter (new PaddedWriter (w , padding ), true );
163+ if (visited .contains (ex )) {
164+ String message = ex instanceof MultitargetException ?
165+ ((MultitargetException )ex ).toStringSafe ()
166+ :
167+ ex .toString ();
168+ pw .println ("\t [CIRCULAR REFERENCE:" + message + "]" );
169+ } else {
170+ if (ex instanceof MultitargetException ) {
171+ ((MultitargetException )ex ).printExceptions (pw , visited );
172+ } else {
173+ ex .printStackTrace (pw );
174+ }
175+ }
176+ });
177+ }
178+
76179 private <O extends Appendable > void printExceptions (O out , BiConsumer <Throwable , String > nestedExceptionPrinter ) {
77180 int idx = 0 ;
78181 int n = ((int )Math .log10 (exceptions .size ()) + 1 );
@@ -104,5 +207,13 @@ private static <O extends Appendable> void printException(String idx, Throwable
104207 }
105208 }
106209
210+ String toStringSafe () {
211+ return getClass ().getName ();
212+ }
213+
214+ private static <T > Set <T > newDejavueSet () {
215+ return Collections .newSetFromMap (new IdentityHashMap <T , Boolean >());
216+ }
217+
107218 private static final String NEW_LINE = System .lineSeparator ();
108219}
0 commit comments