001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014package ch.qos.logback.classic.spi;
015
016import ch.qos.logback.core.CoreConstants;
017
018/**
019 * Convert a throwable into an array of ThrowableDataPoint objects.
020 *
021 *
022 * @author Ceki Gülcü
023 */
024public class ThrowableProxyUtil {
025
026    public static final int REGULAR_EXCEPTION_INDENT = 1;
027    public static final int SUPPRESSED_EXCEPTION_INDENT = 1;
028    private static final int BUILDER_CAPACITY = 2048;
029
030    public static void build(ThrowableProxy nestedTP, Throwable nestedThrowable, ThrowableProxy parentTP) {
031
032        StackTraceElement[] nestedSTE = nestedThrowable.getStackTrace();
033
034        int commonFramesCount = -1;
035        if (parentTP != null) {
036            commonFramesCount = findNumberOfCommonFrames(nestedSTE, parentTP.getStackTraceElementProxyArray());
037        }
038
039        nestedTP.commonFrames = commonFramesCount;
040        nestedTP.stackTraceElementProxyArray = steArrayToStepArray(nestedSTE);
041    }
042
043    static StackTraceElementProxy[] steArrayToStepArray(StackTraceElement[] stea) {
044        if (stea == null) {
045            return new StackTraceElementProxy[0];
046        }
047        StackTraceElementProxy[] stepa = new StackTraceElementProxy[stea.length];
048        for (int i = 0; i < stepa.length; i++) {
049            stepa[i] = new StackTraceElementProxy(stea[i]);
050        }
051        return stepa;
052    }
053
054    static int findNumberOfCommonFrames(StackTraceElement[] steArray, StackTraceElementProxy[] parentSTEPArray) {
055        if (parentSTEPArray == null || steArray == null) {
056            return 0;
057        }
058
059        int steIndex = steArray.length - 1;
060        int parentIndex = parentSTEPArray.length - 1;
061        int count = 0;
062        while (steIndex >= 0 && parentIndex >= 0) {
063            StackTraceElement ste = steArray[steIndex];
064            StackTraceElement otherSte = parentSTEPArray[parentIndex].ste;
065            if (ste.equals(otherSte)) {
066                count++;
067            } else {
068                break;
069            }
070            steIndex--;
071            parentIndex--;
072        }
073        return count;
074    }
075
076    public static String asString(IThrowableProxy tp) {
077        StringBuilder sb = new StringBuilder(BUILDER_CAPACITY);
078
079        recursiveAppend(sb, null, REGULAR_EXCEPTION_INDENT, tp);
080
081        return sb.toString();
082    }
083
084    private static void recursiveAppend(StringBuilder sb, String prefix, int indent, IThrowableProxy tp) {
085        if (tp == null)
086            return;
087        subjoinFirstLine(sb, prefix, indent, tp);
088        sb.append(CoreConstants.LINE_SEPARATOR);
089        subjoinSTEPArray(sb, indent, tp);
090        IThrowableProxy[] suppressed = tp.getSuppressed();
091        if (suppressed != null) {
092            for (IThrowableProxy current : suppressed) {
093                recursiveAppend(sb, CoreConstants.SUPPRESSED, indent + SUPPRESSED_EXCEPTION_INDENT, current);
094            }
095        }
096        recursiveAppend(sb, CoreConstants.CAUSED_BY, indent, tp.getCause());
097    }
098
099    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,
137     *             IThrowableProxy tp) instead.
138     */
139    public static void subjoinSTEPArray(StringBuilder sb, IThrowableProxy tp) {
140        // not called anymore - but it is public
141        subjoinSTEPArray(sb, REGULAR_EXCEPTION_INDENT, tp);
142    }
143
144    /**
145     * @param sb          The StringBuilder the STEPs are appended to.
146     * @param indentLevel indentation level used for the STEPs, usually
147     *                    REGULAR_EXCEPTION_INDENT.
148     * @param tp          the IThrowableProxy containing the STEPs.
149     */
150    public static void subjoinSTEPArray(StringBuilder sb, int indentLevel, IThrowableProxy tp) {
151        StackTraceElementProxy[] stepArray = tp.getStackTraceElementProxyArray();
152        int commonFrames = tp.getCommonFrames();
153
154        for (int i = 0; i < stepArray.length - commonFrames; i++) {
155            StackTraceElementProxy step = stepArray[i];
156            indent(sb, indentLevel);
157            subjoinSTEP(sb, step);
158            sb.append(CoreConstants.LINE_SEPARATOR);
159        }
160
161        if (commonFrames > 0) {
162            indent(sb, indentLevel);
163            sb.append("... ").append(commonFrames).append(" common frames omitted")
164                    .append(CoreConstants.LINE_SEPARATOR);
165        }
166
167    }
168
169    public static void subjoinFirstLine(StringBuilder buf, IThrowableProxy tp) {
170        int commonFrames = tp.getCommonFrames();
171        if (commonFrames > 0) {
172            buf.append(CoreConstants.CAUSED_BY);
173        }
174        subjoinExceptionMessage(buf, tp);
175    }
176
177    public static void subjoinFirstLineRootCauseFirst(StringBuilder buf, IThrowableProxy tp) {
178        if (tp.getCause() != null) {
179            buf.append(CoreConstants.WRAPPED_BY);
180        }
181        subjoinExceptionMessage(buf, tp);
182    }
183
184    private static void subjoinExceptionMessage(StringBuilder buf, IThrowableProxy tp) {
185        if (tp.isCyclic()) {
186            buf.append("[CIRCULAR REFERENCE: ").append(tp.getClassName()).append(": ").append(tp.getMessage())
187                    .append(']');
188        } else {
189            buf.append(tp.getClassName()).append(": ").append(tp.getMessage());
190        }
191    }
192}