/*
* Copyright 2012 Will Benedict, Felix Berger and Roger Kapsi
*
* 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.ardverk.gibson;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import org.mongodb.morphia.annotations.Embedded;
/**
* A {@link Condition} is simply a JSON serializable version of a {@link Throwable}.
*/
@Embedded
public class Condition {
private static final Console LOG = Console.getLogger(Condition.class);
private static final Method STACK_TRACE = getOurStackTrace();
public static Condition valueOf(Throwable throwable) {
return create(throwable);
}
private static Condition create(Throwable throwable) {
Condition condition = new Condition();
condition.setTypeName(throwable.getClass().getName());
condition.setMessage(throwable.getMessage());
condition.setStackTrace(getStackTrace(throwable));
Throwable cause = throwable.getCause();
if (cause != null && cause != throwable) {
condition.setCause(create(cause));
}
return condition;
}
private String typeName;
private String message;
private List<StackTraceElement> stackTrace;
private Condition cause;
public String getTypeName() {
return typeName;
}
public void setTypeName(String type) {
this.typeName = type;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public List<StackTraceElement> getStackTrace() {
return stackTrace;
}
public void setStackTrace(List<StackTraceElement> stackTrace) {
this.stackTrace = stackTrace;
}
public Condition getCause() {
return cause;
}
public void setCause(Condition cause) {
this.cause = cause;
}
public void printStackTrace() {
printStackTrace(System.err);
}
public void printStackTrace(PrintStream s) {
s.println(toStringValue());
}
public void printStackTrace(PrintWriter pw) {
pw.println(toStringValue());
}
public String toStringValue() {
StringBuilder sb = new StringBuilder();
sb.append(this).append("\n");
for (StackTraceElement element : stackTrace) {
sb.append("\tat ").append(element).append("\n");
}
if (cause != null) {
cause.printStackTraceAsCause(sb, stackTrace);
}
return sb.toString();
}
private void printStackTraceAsCause(StringBuilder sb, List<? extends StackTraceElement> causedTrace) {
// Compute number of frames in common between this and caused
int m = stackTrace.size() - 1, n = causedTrace.size() - 1;
while (m >= 0 && n >= 0 && stackTrace.get(m).equals(causedTrace.get(n))) {
m--;
n--;
}
int framesInCommon = stackTrace.size() - 1 - m;
sb.append("Caused by: ").append(this).append("\n");
for (int i = 0; i <= m; i++) {
sb.append("\tat ").append(stackTrace.get(i)).append("\n");
}
if (framesInCommon != 0) {
sb.append("\t... ").append(framesInCommon).append(" more\n");
}
// Recurse if we have a cause
if (cause != null) {
cause.printStackTraceAsCause(sb, stackTrace);
}
}
@Override
public String toString() {
return (message != null) ? (typeName + ": " + message) : typeName;
}
private static List<StackTraceElement> getStackTrace(Throwable throwable) {
StackTraceElement[] elements = null;
if (STACK_TRACE != null) {
try {
elements = (StackTraceElement[])STACK_TRACE.invoke(throwable);
} catch (IllegalAccessException err) {
LOG.error(Gibson.MARKER, "IllegalAccessException", err);
} catch (InvocationTargetException err) {
LOG.error(Gibson.MARKER, "InvocationTargetException", err);
}
}
if (elements == null) {
elements = throwable.getStackTrace();
}
if (elements != null) {
return Arrays.asList(elements);
}
return null;
}
/**
* {@link Throwable#getStackTrace()} returns a copy. We can try to use the getOurStackTrace()
* method instead but it's a private.
*/
private static Method getOurStackTrace() {
Method method = null;
try {
method = Throwable.class.getDeclaredMethod("getOurStackTrace");
method.setAccessible(true);
} catch (NoSuchMethodException err) {
LOG.error(Gibson.MARKER, "NoSuchMethodException", err);
}
return method;
}
}