All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.concordion.internal.listener.ThrowableRenderer Maven / Gradle / Ivy

package org.concordion.internal.listener;

import java.util.HashSet;
import java.util.Set;

import org.concordion.api.Element;
import org.concordion.api.Source;
import org.concordion.api.listener.ThrowableCaughtEvent;
import org.concordion.api.listener.ThrowableCaughtListener;
import org.concordion.internal.FailFastException;
import org.concordion.internal.util.Check;

public class ThrowableRenderer implements ThrowableCaughtListener {

    private static final String TOGGLING_SCRIPT_RESOURCE_PATH = "/org/concordion/internal/resource/visibility-toggler.js";
    private long buttonId = 0;
    private Set rootElementsWithScript = new HashSet();
    private Source resourceSource;
    
    public ThrowableRenderer(Source resourceSource) {
        this.resourceSource = resourceSource;
    }
    
    public void throwableCaught(ThrowableCaughtEvent event) {
        Element element = event.getElement();
        if (!(event.getThrowable() instanceof FailFastException)) {
            buttonId++;
            
            element.appendChild(expectedSpan(element));
            
            // Special handling for  tags to avoid the stack-trace being inside the link text
            if (element.getLocalName().equals("a")) {
                Element div = new Element("div"); 
                element.appendSister(div);
                element = div;
            }
            element.appendChild(exceptionMessage(event.getThrowable().getMessage()));
            element.appendChild(stackTraceTogglingButton());
            element.appendChild(stackTrace(event.getThrowable(), event.getExpression()));
            
            ensureDocumentHasTogglingScript(element);
        } else {
            element.addStyleClass("failure");
        }
    }

    private void ensureDocumentHasTogglingScript(Element element) {
        Element rootElement = element.getRootElement();
        if (!rootElementsWithScript.contains(rootElement)) {
            rootElementsWithScript.add(rootElement);
            Element head = rootElement.getFirstDescendantNamed("head");
            if (head == null) {
                System.out.println(rootElement.toXML());
            }
            Check.notNull(head, "Document  section is missing");
            if (head != null) {
                Element script = new Element("script").addAttribute("type", "text/javascript");
                head.prependChild(script);
                script.appendText(resourceSource.readResourceAsString(TOGGLING_SCRIPT_RESOURCE_PATH));
            }
        }
    }

    private Element expectedSpan(Element element) {
        Element spanExpected = new Element("del").addStyleClass("expected");
        element.moveChildrenTo(spanExpected);
        spanExpected.appendNonBreakingSpaceIfBlank();
        Element spanFailure = new Element("span").addStyleClass("failure");
        spanFailure.appendChild(spanExpected);
        return spanFailure;
    }

    private Element exceptionMessage(String exceptionMessage) {
        return new Element("span")
                .addStyleClass("exceptionMessage")
                .appendText(exceptionMessage);
    }

    private Element stackTraceTogglingButton() {
        return new Element("input")
                .addStyleClass("stackTraceButton")
                .setId("stackTraceButton" + buttonId)
                .addAttribute("type", "button")
                .addAttribute("onclick", "javascript:toggleStackTrace('" + buttonId + "')")
                .addAttribute("value", "View Stack");
    }
    
    private Element stackTrace(Throwable t, String expression) {
        Element stackTrace = new Element("div").addStyleClass("stackTrace");
        stackTrace.setId("stackTrace" + buttonId);
        
        Element p = new Element("p")
                .appendText("While evaluating expression: ");
        p.appendChild(new Element("code").appendText(expression));
        stackTrace.appendChild(p);
        
        recursivelyAppendStackTrace(t, stackTrace);

        return stackTrace;
    }

    private void recursivelyAppendStackTrace(Throwable t, Element stackTrace) {
        Element stackTraceExceptionMessage = new Element("div")
                .addStyleClass("stackTraceExceptionMessage")
                .appendText(t.getClass().getName() + ": " + t.getMessage());
        stackTrace.appendChild(stackTraceExceptionMessage);

        for (StackTraceElement traceElement : t.getStackTrace()) {
            stackTrace.appendChild(stackTraceElement(traceElement));
        }

        if (t.getCause() != null) {
            recursivelyAppendStackTrace(t.getCause(), stackTrace);
        }
    }

    private Element stackTraceElement(StackTraceElement traceElement) {
        Element entry = new Element("div")
                .addStyleClass("stackTraceEntry")
                .appendText("at " + traceElement.getClassName())
                .appendText("." + traceElement.getMethodName());
        if (traceElement.getFileName() == null) {
            entry.appendText(" (Unknown Source)");
        } else {
            entry.appendText(" (" + traceElement.getFileName() + ":" + traceElement.getLineNumber() + ")");
        }
        return entry;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy