/*
* Copyright 2008 Fedora Commons, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mulgara.util;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* Creates a stack trace for the current position at creation, or from a provided
* Throwable object.
*
* @created Jul 17, 2008
* @author Paula Gearon
* @copyright © 2008 <a href="http://www.fedora-commons.org/">Fedora Commons</a>
*/
public class StackTrace {
/** The string output for this stack. */
private String fullTrace;
/** The Throwable containing all the information for this stack. */
private final Throwable throwable;
/**
* The offset for accessing the stack array. Either 0 or 1.
* 1 is used to skip the frame for the constructor of this object.
*/
private final int offset;
/**
* Build a new stack around a provided Throwable.
* @param throwable The throwable object with the stack to encode.
*/
public StackTrace(Throwable throwable) {
this.throwable = throwable;
offset = 0;
fullTrace = getStringTrace();
}
/**
* Build a new stack based on the current position.
*/
public StackTrace() {
this.throwable = new Throwable();
offset = 1;
fullTrace = getStringTrace();
}
/**
* Output this stack as a string.
*/
public String toString() {
return fullTrace;
}
/**
* Get a stack frame for a given level from this stack.
* @param level The level of the frame to retrieve. 0 <= level < {@link #getStackDepth()}
* @return A StackTraceElement encoding the frame for the given level.
*/
public StackTraceElement getFrame(int level) {
return throwable.getStackTrace()[level + offset];
}
/**
* The maximum depth of this stack.
* @return The number of the deepest frame + 1.
*/
public int getStackDepth() {
return throwable.getStackTrace().length - offset;
}
/**
* Gets the string for this stack trace.
* @return A string encoding of the stack trace.
*/
private String getStringTrace() {
StringBuilder b = new StringBuilder("STACK TRACE:\n");
StackTraceElement[] stack = throwable.getStackTrace();
for (int level = offset; level < stack.length; level++) {
b.append(" ").append(stack[level].toString()).append("\n");
}
Throwable reason = throwable.getCause();
while (reason != null) {
b.append(" Caused by:");
b.append(reason.getClass().toString());
b.append(": ");
b.append(reason.getMessage());
stack = reason.getStackTrace();
for (int level = 0; level < stack.length; level++) {
b.append(" ").append(stack[level].toString()).append("\n");
}
reason = reason.getCause();
}
return b.toString();
}
/**
* Converts a throwable to a string. This appears an Exception/Error/etc
* instead of using the STACK TRACE label.
* @param t The Throwable to print.
* @return A String with the output from the Throwable.
*/
public static String throwableToString(Throwable t) {
StringWriter strWriter = new StringWriter();
t.printStackTrace(new PrintWriter(strWriter));
return strWriter.toString();
}
/**
* Gets the base of the cause chain.
* @param t The Throwable to get the cause from.
* @return The base of the cause chain for the throwable.
*/
public static Throwable getReason(Throwable t) {
while (t.getCause() != null) t = t.getCause();
return t;
}
/**
* Gets the message from the base cause for a Throwable.
* @param t The throwable to get the base of the cause chain for.
* @return The message from the base case.
*/
public static String getReasonMessage(Throwable t) {
String msg = getReason(t).getMessage();
return msg != null ? msg : t.getClass().getName();
}
}