/* * Copyright 2011 Google 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 com.google.template.soy.sharedpasses.render; import com.google.template.soy.soytree.SoyNode; import com.google.template.soy.soytree.TemplateNode; import java.util.ArrayDeque; import java.util.Deque; import javax.annotation.Nullable; /** * Exception thrown when a rendering or evaluation attempt fails. * */ public final class RenderException extends RuntimeException { public static RenderException create(String message) { return create(message, (Throwable) null); } public static RenderException create(String message, Throwable cause) { return new RenderException(message, cause); } public static RenderException createWithSource(String message, SoyNode source) { return createWithSource(message, null, source); } public static RenderException createWithSource( String message, @Nullable Throwable cause, SoyNode source) { return new RenderException(message, cause).addStackTraceElement(source); } public static RenderException createFromRenderException( String message, RenderException cause, SoyNode node) { RenderException renderException = new RenderException(message, cause.getCause()); renderException.soyStackTrace.addAll(cause.soyStackTrace); renderException.addStackTraceElement(node); return renderException; } /** The list of all stack traces from the soy rendering. */ private final Deque<StackTraceElement> soyStackTrace = new ArrayDeque<>(); /** * @param message A detailed description of the error. * @param cause The underlying error. */ private RenderException(String message, Throwable cause) { super(message, cause); } @Override public Throwable fillInStackTrace() { // Remove java stack trace, we only care about the soy stack. return this; } /** Add a partial stack trace element by specifying the source location of the soy file. */ RenderException addStackTraceElement(SoyNode node) { // Typically, this is fast since templates aren't that deep and we only do this in error // situations so performance matters less. TemplateNode template = node.getNearestAncestor(TemplateNode.class); soyStackTrace.add(template.createStackTraceElement(node.getSourceLocation())); return this; } /** Finalize the stack trace by prepending the soy stack trace to the given Throwable. */ public void finalizeStackTrace(Throwable t) { t.setStackTrace(concatWithJavaStackTrace(t.getStackTrace())); } /** * Prepend the soy stack trace to the given standard java stack trace. * * @param javaStackTrace The java stack trace to prepend. This should come from * Throwable#getStackTrace() * @return The combined stack trace to use. Callers should call Throwable#setStackTrace() to * override another Throwable's stack trace. */ private StackTraceElement[] concatWithJavaStackTrace(StackTraceElement[] javaStackTrace) { if (soyStackTrace.isEmpty()) { return javaStackTrace; } StackTraceElement[] finalStackTrace = new StackTraceElement[soyStackTrace.size() + javaStackTrace.length]; soyStackTrace.toArray(finalStackTrace); System.arraycopy( javaStackTrace, 0, finalStackTrace, soyStackTrace.size(), javaStackTrace.length); return finalStackTrace; } }