package org.limewire.util;

/**
 * A Runnable that keeps the stack trace from when
 * it was created, so that an exception thrown while
 * running from another thread can keep the created stack trace.
 */
public class DebugRunnable implements Runnable {
    
    private final Exception creationTime;
    private final Runnable delegate;
    
    public DebugRunnable(Runnable runner) {
        this.creationTime = new Exception("Debug Exception Creation");
        this.delegate = runner;
        // Drop this line from the creationTime trace..
        StackTraceElement[] trace = creationTime.getStackTrace();
        StackTraceElement[] newTrace = new StackTraceElement[trace.length - 1];
        System.arraycopy(trace, 1, newTrace, 0, newTrace.length);
        creationTime.setStackTrace(newTrace);
    }
    
    public final void run() {
        boolean setStackTrace = true;
        try {
            delegate.run();
        } catch(Throwable t) {
            if(t.getCause() == null) {
                try { 
                    t.initCause(creationTime);
                    setStackTrace = false;
                } catch (IllegalStateException ise) {
                    // thrown if throwable was initialized with null cause for some reason
                }
            }
            if (setStackTrace) {
                // If it already had a cause, all we can do is manipulate the StackTraceElement[]
                StackTraceElement[] trace = t.getStackTrace();
                StackTraceElement[] createdTrace = creationTime.getStackTrace();
                StackTraceElement[] combinedTrace = new StackTraceElement[trace.length + createdTrace.length];
                System.arraycopy(trace, 0, combinedTrace, 0, trace.length);
                System.arraycopy(createdTrace, 0, combinedTrace, trace.length, createdTrace.length);
                t.setStackTrace(combinedTrace);
            }
            
            if(t instanceof Error)
                throw (Error)t;
            else if(t instanceof RuntimeException)
                throw (RuntimeException)t;
            else
                throw new RuntimeException(t);
        }
    }

}
