1 /**
2  * Logback: the reliable, generic, fast and flexible logging framework.
3  * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4  *
5  * This program and the accompanying materials are dual-licensed under
6  * either the terms of the Eclipse Public License v1.0 as published by
7  * the Eclipse Foundation
8  *
9  *   or (per the licensee's choosing)
10  *
11  * under the terms of the GNU Lesser General Public License version 2.1
12  * as published by the Free Software Foundation.
13  */

14 package ch.qos.logback.classic.spi;
15
16 import ch.qos.logback.core.CoreConstants;
17
18 /**
19  * Convert a throwable into an array of ThrowableDataPoint objects.
20  *
21  *
22  * @author Ceki Gülcü
23  */

24 public class ThrowableProxyUtil {
25
26     public static final int REGULAR_EXCEPTION_INDENT = 1;
27     public static final int SUPPRESSED_EXCEPTION_INDENT = 1;
28     private static final int BUILDER_CAPACITY = 2048;
29
30     public static void build(ThrowableProxy nestedTP, Throwable nestedThrowable, ThrowableProxy parentTP) {
31
32         StackTraceElement[] nestedSTE = nestedThrowable.getStackTrace();
33
34         int commonFramesCount = -1;
35         if (parentTP != null) {
36             commonFramesCount = findNumberOfCommonFrames(nestedSTE, parentTP.getStackTraceElementProxyArray());
37         }
38
39         nestedTP.commonFrames = commonFramesCount;
40         nestedTP.stackTraceElementProxyArray = steArrayToStepArray(nestedSTE);
41     }
42
43     static StackTraceElementProxy[] steArrayToStepArray(StackTraceElement[] stea) {
44         if (stea == null) {
45             return new StackTraceElementProxy[0];
46         }
47         StackTraceElementProxy[] stepa = new StackTraceElementProxy[stea.length];
48         for (int i = 0; i < stepa.length; i++) {
49             stepa[i] = new StackTraceElementProxy(stea[i]);
50         }
51         return stepa;
52     }
53
54     static int findNumberOfCommonFrames(StackTraceElement[] steArray, StackTraceElementProxy[] parentSTEPArray) {
55         if (parentSTEPArray == null || steArray == null) {
56             return 0;
57         }
58
59         int steIndex = steArray.length - 1;
60         int parentIndex = parentSTEPArray.length - 1;
61         int count = 0;
62         while (steIndex >= 0 && parentIndex >= 0) {
63             StackTraceElement ste = steArray[steIndex];
64             StackTraceElement otherSte = parentSTEPArray[parentIndex].ste;
65             if (ste.equals(otherSte)) {
66                 count++;
67             } else {
68                 break;
69             }
70             steIndex--;
71             parentIndex--;
72         }
73         return count;
74     }
75
76     public static String asString(IThrowableProxy tp) {
77         StringBuilder sb = new StringBuilder(BUILDER_CAPACITY);
78
79         recursiveAppend(sb, null, REGULAR_EXCEPTION_INDENT, tp);
80
81         return sb.toString();
82     }
83
84     private static void recursiveAppend(StringBuilder sb, String prefix, int indent, IThrowableProxy tp) {
85         if (tp == null)
86             return;
87         subjoinFirstLine(sb, prefix, indent, tp);
88         sb.append(CoreConstants.LINE_SEPARATOR);
89         subjoinSTEPArray(sb, indent, tp);
90         IThrowableProxy[] suppressed = tp.getSuppressed();
91         if (suppressed != null) {
92             for (IThrowableProxy current : suppressed) {
93                 recursiveAppend(sb, CoreConstants.SUPPRESSED, indent + SUPPRESSED_EXCEPTION_INDENT, current);
94             }
95         }
96         recursiveAppend(sb, CoreConstants.CAUSED_BY, indent, tp.getCause());
97     }
98
99     public static void indent(StringBuilder buf, int indent) {
100         for (int j = 0; j < indent; j++) {
101             buf.append(CoreConstants.TAB);
102         }
103     }
104
105     private static void subjoinFirstLine(StringBuilder buf, String prefix, int indent, IThrowableProxy tp) {
106         indent(buf, indent - 1);
107         if (prefix != null) {
108             buf.append(prefix);
109         }
110         subjoinExceptionMessage(buf, tp);
111     }
112
113     public static void subjoinPackagingData(StringBuilder builder, StackTraceElementProxy step) {
114         if (step != null) {
115             ClassPackagingData cpd = step.getClassPackagingData();
116             if (cpd != null) {
117                 if (!cpd.isExact()) {
118                     builder.append(" ~[");
119                 } else {
120                     builder.append(" [");
121                 }
122
123                 builder.append(cpd.getCodeLocation()).append(':').append(cpd.getVersion()).append(']');
124             }
125         }
126     }
127
128     public static void subjoinSTEP(StringBuilder sb, StackTraceElementProxy step) {
129         sb.append(step.toString());
130         subjoinPackagingData(sb, step);
131     }
132
133     /**
134      * @param sb The StringBuilder the STEPs are appended to.
135      * @param tp the IThrowableProxy containing the STEPs.
136      * @deprecated Use subjoinSTEPArray(StringBuilder sb, int indentLevel, IThrowableProxy tp) instead.
137      */

138     public static void subjoinSTEPArray(StringBuilder sb, IThrowableProxy tp) {
139         // not called anymore - but it is public
140         subjoinSTEPArray(sb, REGULAR_EXCEPTION_INDENT, tp);
141     }
142
143     /**
144      * @param sb The StringBuilder the STEPs are appended to.
145      * @param indentLevel indentation level used for the STEPs, usually REGULAR_EXCEPTION_INDENT.
146      * @param tp the IThrowableProxy containing the STEPs.
147      */

148     public static void subjoinSTEPArray(StringBuilder sb, int indentLevel, IThrowableProxy tp) {
149         StackTraceElementProxy[] stepArray = tp.getStackTraceElementProxyArray();
150         int commonFrames = tp.getCommonFrames();
151
152         for (int i = 0; i < stepArray.length - commonFrames; i++) {
153             StackTraceElementProxy step = stepArray[i];
154             indent(sb, indentLevel);
155             subjoinSTEP(sb, step);
156             sb.append(CoreConstants.LINE_SEPARATOR);
157         }
158
159         if (commonFrames > 0) {
160             indent(sb, indentLevel);
161             sb.append("... ").append(commonFrames).append(" common frames omitted").append(CoreConstants.LINE_SEPARATOR);
162         }
163
164     }
165
166     public static void subjoinFirstLine(StringBuilder buf, IThrowableProxy tp) {
167         int commonFrames = tp.getCommonFrames();
168         if (commonFrames > 0) {
169             buf.append(CoreConstants.CAUSED_BY);
170         }
171         subjoinExceptionMessage(buf, tp);
172     }
173
174     public static void subjoinFirstLineRootCauseFirst(StringBuilder buf, IThrowableProxy tp) {
175         if (tp.getCause() != null) {
176             buf.append(CoreConstants.WRAPPED_BY);
177         }
178         subjoinExceptionMessage(buf, tp);
179     }
180
181     private static void subjoinExceptionMessage(StringBuilder buf, IThrowableProxy tp) {
182         buf.append(tp.getClassName()).append(": ").append(tp.getMessage());
183     }
184 }
185