package org.rendersnake.error; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import org.rendersnake.HtmlCanvas; import org.rendersnake.Renderable; import static org.rendersnake.HtmlAttributesFactory.*; /** * RenderException is a RuntimeException that is thrown in the following cases * <ul> * <li>sending close() when there is no open tag * <li>sending close(someTag) where otherTag was expected * <li>general exception occurred when rendering a component * </ul> * * @author ernest */ public class RenderException extends RuntimeException implements Renderable { private static final long serialVersionUID = 5155772408981115672L; /** * */ public static final String KEY_PAGECONTEXT = "renderable.error"; /** * */ public boolean isNullTag = false; /** * */ public boolean isEmptyStack = false; /** * */ public String expectedTag = null; /** * */ public String closingTag = null; /** * */ public Exception exception = null; /** * Return a new RenderException for the situation that a different tag is being close than expected. * This indicates that a required close tag was not sent. * @return */ public static RenderException nullTag() { RenderException rex = new RenderException(); rex.isNullTag = true; return rex; } /** * Return a new RenderException for the situation that a different tag is being close than expected. * This indicates that a required close tag was not sent. * @param expected , what should have been the close tag * @param actual , what the program is trying to close * @return */ public static RenderException unexpectedTag(String expected, String actual) { RenderException rex = new RenderException(); rex.expectedTag = expected; rex.closingTag = actual; return rex; } /** * Return a new RenderException for the situation that a component is trying to close an open tag when there are no open tags to close. * @return */ public static RenderException emptyStack() { RenderException rex = new RenderException(); rex.isEmptyStack = true; return rex; } /** * Return a new RenderException for the situation that an unkown problem was detected when rendering a component. * @param ex * @return */ public static RenderException caught(Exception ex) { RenderException rex = new RenderException(); rex.exception = ex; return rex; } /** * Render the receiver on a html to provide debugging information about the exception. */ public void renderOn(HtmlCanvas html) throws IOException { html.div(style("color:red")); if (isEmptyStack) { html.write("[Render Error] unexpected close()"); return; } if (expectedTag != null) { // tag mismatch html.write(String.format("[Render Error] expected [%s] actual [%s]", expectedTag, closingTag)); } if (exception != null) { // general error html.write(String.format("[Render Error] general exception [%s]", exception.getMessage())); } // if debugging, the error component is available Renderable errorComponent = (Renderable)(html.getPageContext().getObject("renderable.error")); if (errorComponent != null) { html.write(" in ").write(errorComponent.getClass().getName()); } else { html.a(href("?inspect")).write(" Inspect details")._a(); this.renderStackTraceOn(html); } html._div(); } /** * Render information about the exception (if any) * @param html * @throws IOException */ public void renderStackTraceOn(HtmlCanvas html) throws IOException { // @formatter:off if (null == exception) return; StringWriter sw = new StringWriter(); PrintWriter buffer = new PrintWriter(sw); exception.printStackTrace(buffer); html.pre().write(sw.toString(), true)._pre(); html.hr(); } @Override public String toString() { return super.toString() + "{expected=" + expectedTag + ",actual=" + closingTag + ",exception=" + exception + ",empty=" + isEmptyStack + ",null=" + isNullTag + "}"; } }