2021SC@SDUSC
TemplateException
代码分析
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 freemarker.template;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import freemarker.core.Environment;
import freemarker.core.Expression;
import freemarker.core.InvalidReferenceException;
import freemarker.core.ParseException;
import freemarker.core.TemplateElement;
import freemarker.core.TemplateObject;
import freemarker.core._CoreAPI;
import freemarker.core._ErrorDescriptionBuilder;
import freemarker.template.utility.CollectionUtils;
/**
* Runtime exception in a template (as opposed to a parsing-time exception: {@link ParseException}).
* It prints a special stack trace that contains the template-language stack trace along the usual Java stack trace.
*/
public class TemplateException extends Exception {
private static final String FTL_INSTRUCTION_STACK_TRACE_TITLE
= "FTL stack trace (\"~\" means nesting-related):";
// Set in constructor:
private transient _ErrorDescriptionBuilder descriptionBuilder;
private final transient Environment env;
private final transient Expression blamedExpression;
private transient TemplateElement[] ftlInstructionStackSnapshot;
// Calculated on demand:
private String renderedFtlInstructionStackSnapshot; // clalc. from ftlInstructionStackSnapshot
private String renderedFtlInstructionStackSnapshotTop; // clalc. from ftlInstructionStackSnapshot
private String description; // calc. from descriptionBuilder, or set by the construcor
private transient String messageWithoutStackTop;
private transient String message;
private boolean blamedExpressionStringCalculated;
private String blamedExpressionString;
private boolean positionsCalculated;
private String templateName;
private String templateSourceName;
private Integer lineNumber;
private Integer columnNumber;
private Integer endLineNumber;
private Integer endColumnNumber;
// Concurrency:
private transient Object lock = new Object();
private transient ThreadLocal messageWasAlreadyPrintedForThisTrace;
/**
* Constructs a TemplateException with no specified detail message
* or underlying cause.
*/
public TemplateException(Environment env) {
this(null, null, env);
}
/**
* Constructs a TemplateException with the given detail message,
* but no underlying cause exception.
*
* @param description the description of the error that occurred
*/
public TemplateException(String description, Environment env) {
this(description, null, env);
}
//构造一个没有指定详细信息或底层原因的TemplateException。//
/**
* The same as {@link #TemplateException(Throwable, Environment)}; it's exists only for binary
* backward-compatibility.
*/
public TemplateException(Exception cause, Environment env) {
this(null, cause, env);
}
/**
* Constructs a TemplateException with the given underlying Exception,
* but no detail message.
*
* @param cause the underlying {@link Exception} that caused this
* exception to be raised
*
* @since 2.3.20
*/
public TemplateException(Throwable cause, Environment env) {
this(null, cause, env);
}
/**
* The same as {@link #TemplateException(String, Throwable, Environment)}; it's exists only for binary
* backward-compatibility.
*/
public TemplateException(String description, Exception cause, Environment env) {
this(description, cause, env, null, null);
}
/**
* Constructs a TemplateException with both a description of the error
* that occurred and the underlying Exception that caused this exception
* to be raised.
*
* @param description the description of the error that occurred
* @param cause the underlying {@link Exception} that caused this exception to be raised
*
* @since 2.3.20
*/
public TemplateException(String description, Throwable cause, Environment env) {
this(description, cause, env, null, null);
}
/**
* Don't use this; this is to be used internally by FreeMarker. No backward compatibility guarantees.
*
* @param blamedExpr Maybe {@code null}. The FTL stack in the {@link Environment} only specifies the error location
* with "template element" granularity, and this can be used to point to the expression inside the
* template element.
*/
protected TemplateException(Throwable cause, Environment env, Expression blamedExpr,
_ErrorDescriptionBuilder descriptionBuilder) {
this(null, cause, env, blamedExpr, descriptionBuilder);
}
private TemplateException(
String renderedDescription,
Throwable cause,
Environment env, Expression blamedExpression,
_ErrorDescriptionBuilder descriptionBuilder) {
// Note: Keep this constructor lightweight.
super(cause); // Message managed locally.
if (env == null) env = Environment.getCurrentEnvironment();
this.env = env;
this.blamedExpression = blamedExpression;
this.descriptionBuilder = descriptionBuilder;
description = renderedDescription;
if (env != null) ftlInstructionStackSnapshot = _CoreAPI.getInstructionStackSnapshot(env);
}
private void renderMessages() {
String description = getDescription();
if (description != null && description.length() != 0) {
messageWithoutStackTop = description;
} else if (getCause() != null) {
messageWithoutStackTop = "No error description was specified for this error; low-level message: "
+ getCause().getClass().getName() + ": " + getCause().getMessage();
} else {
messageWithoutStackTop = "[No error description was available.]";
}
String stackTopFew = getFTLInstructionStackTopFew();
if (stackTopFew != null) {
message = messageWithoutStackTop + "\n\n"
+ _CoreAPI.ERROR_MESSAGE_HR + "\n"
+ FTL_INSTRUCTION_STACK_TRACE_TITLE + "\n"
+ stackTopFew
+ _CoreAPI.ERROR_MESSAGE_HR;
messageWithoutStackTop = message.substring(0, messageWithoutStackTop.length()); // to reuse backing char[]
} else {
message = messageWithoutStackTop;
}
}
private void calculatePosition() {
synchronized (lock) {
if (!positionsCalculated) {
// The expressions is the argument of the template element, so we prefer it as it's more specific.
TemplateObject templateObject = blamedExpression != null
? blamedExpression
: (
ftlInstructionStackSnapshot != null && ftlInstructionStackSnapshot.length != 0
? ftlInstructionStackSnapshot[0] : null);
// Line number blow 0 means no info, negative means position in ?eval-ed value that we won't use here.
if (templateObject != null && templateObject.getBeginLine() > 0) {
final Template template = templateObject.getTemplate();
templateName = template != null ? template.getName() : null;
templateSourceName = template != null ? template.getSourceName() : null;
lineNumber = Integer.valueOf(templateObject.getBeginLine());
columnNumber = Integer.valueOf(templateObject.getBeginColumn());
endLineNumber = Integer.valueOf(templateObject.getEndLine());
endColumnNumber = Integer.valueOf(templateObject.getEndColumn());
}
positionsCalculated = true;
deleteFTLInstructionStackSnapshotIfNotNeeded();
}
}
}
/**
* @deprecated Java 1.4 has introduced {@link #getCause()} - use that instead, especially as this can't return
* runtime exceptions and errors as is.
*/
@Deprecated
//Java 1.4引入了Throwable.getCause()—使用它代替,特别是当它不能返回运行时异常和错误时。//
public Exception getCauseException() {
return getCause() instanceof Exception
? (Exception) getCause()
: new Exception("Wrapped to Exception: " + getCause(), getCause());
}
/**
* Returns the snapshot of the FTL stack trace at the time this exception was created.
*/
public String getFTLInstructionStack() {
synchronized (lock) {
if (ftlInstructionStackSnapshot != null || renderedFtlInstructionStackSnapshot != null) {
if (renderedFtlInstructionStackSnapshot == null) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
_CoreAPI.outputInstructionStack(ftlInstructionStackSnapshot, false, pw);
pw.close();
if (renderedFtlInstructionStackSnapshot == null) {
renderedFtlInstructionStackSnapshot = sw.toString();
deleteFTLInstructionStackSnapshotIfNotNeeded();
}
}
//返回创建此异常时FTL堆栈跟踪的快照。//
return renderedFtlInstructionStackSnapshot;
} else {
return null;
}
}
}
private String getFTLInstructionStackTopFew() {
synchronized (lock) {
if (ftlInstructionStackSnapshot != null || renderedFtlInstructionStackSnapshotTop != null) {
if (renderedFtlInstructionStackSnapshotTop == null) {
int stackSize = ftlInstructionStackSnapshot.length;
String s;
if (stackSize == 0) {
s = "";
} else {
StringWriter sw = new StringWriter();
_CoreAPI.outputInstructionStack(ftlInstructionStackSnapshot, true, sw);
s = sw.toString();
}
if (renderedFtlInstructionStackSnapshotTop == null) {
renderedFtlInstructionStackSnapshotTop = s;
deleteFTLInstructionStackSnapshotIfNotNeeded();
}
}
return renderedFtlInstructionStackSnapshotTop.length() != 0
? renderedFtlInstructionStackSnapshotTop : null;
} else {
return null;
}
}
}
private void deleteFTLInstructionStackSnapshotIfNotNeeded() {
if (renderedFtlInstructionStackSnapshot != null && renderedFtlInstructionStackSnapshotTop != null
&& (positionsCalculated || blamedExpression != null)) {
ftlInstructionStackSnapshot = null;
}
}
private String getDescription() {
synchronized (lock) {
if (description == null && descriptionBuilder != null) {
description = descriptionBuilder.toString(
getFailingInstruction(),
env != null ? env.getShowErrorTips() : true);
descriptionBuilder = null;
}
return description;
}
}
private TemplateElement getFailingInstruction() {
if (ftlInstructionStackSnapshot != null && ftlInstructionStackSnapshot.length > 0) {
return ftlInstructionStackSnapshot[0];
} else {
return null;
}
}
/**
* @return the execution environment in which the exception occurred.
* {@code null} if the exception was deserialized.
*/
public Environment getEnvironment() {
return env;
}
//异常发生的执行环境。如果异常被反序列化,则为Null。//
/**
* Overrides {@link Throwable#printStackTrace(PrintStream)} so that it will include the FTL stack trace.
*/
@Override
public void printStackTrace(PrintStream out) {
printStackTrace(out, true, true, true);
}
//重写Throwable.printStackTrace(PrintStream),以便它将包含FTL堆栈跟踪。//
//覆盖:java.lang.Throwable类中的printStackTrace//
@Override
public void printStackTrace(PrintWriter out) {
printStackTrace(out, true, true, true);
}
/**
* @param heading should the heading at the top be printed
* @param ftlStackTrace should the FTL stack trace be printed
* @param javaStackTrace should the Java stack trace be printed
*
* @since 2.3.20
*/
public void printStackTrace(PrintWriter out, boolean heading, boolean ftlStackTrace, boolean javaStackTrace) {
synchronized (out) {
printStackTrace(new PrintWriterStackTraceWriter(out), heading, ftlStackTrace, javaStackTrace);
}
}
/**
* @param heading should the heading at the top be printed
* @param ftlStackTrace should the FTL stack trace be printed
* @param javaStackTrace should the Java stack trace be printed
*
* @since 2.3.20
*/
public void printStackTrace(PrintStream out, boolean heading, boolean ftlStackTrace, boolean javaStackTrace) {
synchronized (out) {
printStackTrace(new PrintStreamStackTraceWriter(out), heading, ftlStackTrace, javaStackTrace);
}
}
/*参数:
hending-应该打印顶部的标题
ftlStackTrace——应该打印FTL堆栈跟踪
javaStackTrace——应该打印Java堆栈跟踪
*/
private void printStackTrace(StackTraceWriter out, boolean heading, boolean ftlStackTrace, boolean javaStackTrace) {
synchronized (out) {
if (heading) {
out.println("FreeMarker template error:");
}
if (ftlStackTrace) {
String stackTrace = getFTLInstructionStack();
if (stackTrace != null) {
out.println(getMessageWithoutStackTop()); // Not getMessage()!
out.println();
out.println(_CoreAPI.ERROR_MESSAGE_HR);
out.println(FTL_INSTRUCTION_STACK_TRACE_TITLE);
out.print(stackTrace);
out.println(_CoreAPI.ERROR_MESSAGE_HR);
} else {
ftlStackTrace = false;
javaStackTrace = true;
}
}
if (javaStackTrace) {
if (ftlStackTrace) { // We are after an FTL stack trace
out.println();
out.println("Java stack trace (for programmers):");
out.println(_CoreAPI.ERROR_MESSAGE_HR);
synchronized (lock) {
if (messageWasAlreadyPrintedForThisTrace == null) {
messageWasAlreadyPrintedForThisTrace = new ThreadLocal();
}
messageWasAlreadyPrintedForThisTrace.set(Boolean.TRUE);
}
try {
out.printStandardStackTrace(this);
} finally {
messageWasAlreadyPrintedForThisTrace.set(Boolean.FALSE);
}
} else { // javaStackTrace only
out.printStandardStackTrace(this);
}
if (getCause() != null) {
// Dirty hack to fight with ServletException class whose getCause() method doesn't work properly:
Throwable causeCause = getCause().getCause();
if (causeCause == null) {
try {
// Reflection is used to prevent dependency on Servlet classes.
Method m = getCause().getClass().getMethod("getRootCause", CollectionUtils.EMPTY_CLASS_ARRAY);
Throwable rootCause = (Throwable) m.invoke(getCause(), CollectionUtils.EMPTY_OBJECT_ARRAY);
if (rootCause != null) {
out.println("ServletException root cause: ");
out.printStandardStackTrace(rootCause);
}
} catch (Throwable exc) {
; // ignore
}
}
}
} // if (javaStackTrace)
}
}
/**
* Prints the stack trace as if wasn't overridden by {@link TemplateException}.
* @since 2.3.20
*/
public void printStandardStackTrace(PrintStream ps) {
super.printStackTrace(ps);
}
//
打印堆栈跟踪,就像TemplateException没有覆盖一样。//
/**
* Prints the stack trace as if wasn't overridden by {@link TemplateException}.
* @since 2.3.20
*/
public void printStandardStackTrace(PrintWriter pw) {
super.printStackTrace(pw);
}
@Override
public String getMessage() {
if (messageWasAlreadyPrintedForThisTrace != null
&& messageWasAlreadyPrintedForThisTrace.get() == Boolean.TRUE) {
return "[... Exception message was already printed; see it above ...]";
} else {
synchronized (lock) {
if (message == null) renderMessages();
return message;
}
}
}
/**
类似于getMessage(),但它不包含失败指令在文本末尾的位置。它可能包含失败表达式的位置作为表达式
引用的一部分,因为这是描述的一部分。
*/
public String getMessageWithoutStackTop() {
synchronized (lock) {
if (messageWithoutStackTop == null) renderMessages();
return messageWithoutStackTop;
}
}
/**
* 1-based line number of the failing section, or {@code null} if the information is not available.
*
* @since 2.3.21
*/
public Integer getLineNumber() {
synchronized (lock) {
if (!positionsCalculated) {
calculatePosition();
}
return lineNumber;
}
}
/**
* Returns the name ({@link Template#getName()}) of the template where the error has occurred, or {@code null} if
* the information isn't available. This shouldn't be used for showing the error position; use
* {@link #getTemplateSourceName()} instead.
*
* @deprecated Use {@link #getTemplateSourceName()} instead, unless you are really sure that this is what you want.
* This method isn't really deprecated, it's just marked so to warn users about this.
*
* @since 2.3.21
*/
@Deprecated
public String getTemplateName() {
synchronized (lock) {
if (!positionsCalculated) {
calculatePosition();
}
return templateName;
}
}
/**
* Returns the source name ({@link Template#getSourceName()}) of the template where the error has occurred, or
* {@code null} if the information isn't available. This is what should be used for showing the error position.
*
* @since 2.3.22
*/
public String getTemplateSourceName() {
synchronized (lock) {
if (!positionsCalculated) {
calculatePosition();
}
return templateSourceName;
}
}
/**
* 1-based column number of the failing section, or {@code null} if the information is not available.
*
* @since 2.3.21
*/
public Integer getColumnNumber() {
synchronized (lock) {
if (!positionsCalculated) {
calculatePosition();
}
return columnNumber;
}
}
/**
* 1-based line number of the last line that contains the failing section, or {@code null} if the information is not
* available.
*
* @since 2.3.21
*/
public Integer getEndLineNumber() {
synchronized (lock) {
if (!positionsCalculated) {
calculatePosition();
}
return endLineNumber;
}
}
/**
* 1-based column number of the last character of the failing template section, or {@code null} if the information
* is not available. Note that unlike with Java string API-s, this column number is inclusive.
*
* @since 2.3.21
*/
public Integer getEndColumnNumber() {
synchronized (lock) {
if (!positionsCalculated) {
calculatePosition();
}
return endColumnNumber;
}
}
/**
* If there was a blamed expression attached to this exception, it returns its canonical form, otherwise it returns
* {@code null}. This expression should always be inside the failing FTL instruction.
*
* <p>The typical application of this is getting the undefined expression from {@link InvalidReferenceException}-s.
*
* @since 2.3.21
*/
public String getBlamedExpressionString() {
synchronized (lock) {
if (!blamedExpressionStringCalculated) {
if (blamedExpression != null) {
blamedExpressionString = blamedExpression.getCanonicalForm();
}
blamedExpressionStringCalculated = true;
}
return blamedExpressionString;
}
}
Expression getBlamedExpression() {
return blamedExpression;
}
private void writeObject(ObjectOutputStream out) throws IOException, ClassNotFoundException {
// These are calculated from transient fields, so this is the last chance to calculate them:
getFTLInstructionStack();
getFTLInstructionStackTopFew();
getDescription();
calculatePosition();
getBlamedExpressionString();
out.defaultWriteObject();
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
lock = new Object();
in.defaultReadObject();
}
/** Delegate to a {@link PrintWriter} or to a {@link PrintStream}. */
private interface StackTraceWriter {
void print(Object obj);
void println(Object obj);
void println();
void printStandardStackTrace(Throwable exception);
}
private static class PrintStreamStackTraceWriter implements StackTraceWriter {
private final PrintStream out;
PrintStreamStackTraceWriter(PrintStream out) {
this.out = out;
}
@Override
public void print(Object obj) {
out.print(obj);
}
@Override
public void println(Object obj) {
out.println(obj);
}
@Override
public void println() {
out.println();
}
@Override
public void printStandardStackTrace(Throwable exception) {
if (exception instanceof TemplateException) {
((TemplateException) exception).printStandardStackTrace(out);
} else {
exception.printStackTrace(out);
}
}
}
private static class PrintWriterStackTraceWriter implements StackTraceWriter {
private final PrintWriter out;
PrintWriterStackTraceWriter(PrintWriter out) {
this.out = out;
}
@Override
public void print(Object obj) {
out.print(obj);
}
@Override
public void println(Object obj) {
out.println(obj);
}
@Override
public void println() {
out.println();
}
@Override
public void printStandardStackTrace(Throwable exception) {
if (exception instanceof TemplateException) {
((TemplateException) exception).printStandardStackTrace(out);
} else {
exception.printStackTrace(out);
}
}
}
}
TemplateExceptionHandler
代码分析
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 freemarker.template;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import freemarker.core.Configurable;
import freemarker.core.Environment;
import freemarker.core.StopException;
import freemarker.template.utility.StringUtil;
/**
* Used for the {@link Configurable#setTemplateExceptionHandler(TemplateExceptionHandler) template_exception_handler}
* configuration setting.
*/
public interface TemplateExceptionHandler {
/**
* Method called after a {@link TemplateException} was raised inside a template. The exception should be re-thrown
* unless you want to suppress the exception.
*
* <p>Note that you can check with {@link Environment#isInAttemptBlock()} if you are inside a {@code #attempt}
* block, which then will handle this exception and roll back the output generated inside it.
*
* <p>Note that {@link StopException}-s (raised by {@code #stop}) won't be captured here.
*
* <p>Note that you shouldn't log the exception in this method unless you suppress it. If there's a concern that the
* exception might won't be logged after it bubbles up from {@link Template#process(Object, Writer)}, simply
* ensure that {@link Configuration#getLogTemplateExceptions()} is {@code true}.
*
* @param te The exception that occurred; don't forget to re-throw it unless you want to suppress it
* @param env The runtime environment of the template
* @param out This is where the output of the template is written
*/
void handleTemplateException(TemplateException te, Environment env, Writer out) throws TemplateException;
/**
* {@link TemplateExceptionHandler} that simply skips the failing instructions, letting the template continue
* executing. It does nothing to handle the event. Note that the exception is still logged, as with all
* other {@link TemplateExceptionHandler}-s.
*/
TemplateExceptionHandler IGNORE_HANDLER = new TemplateExceptionHandler() {
@Override
public void handleTemplateException(TemplateException te, Environment env, Writer out) {
// Do nothing
}
};
/**
* {@link TemplateExceptionHandler} that simply re-throws the exception; this should be used
* in most production systems.
*/
//创建新的对象实例//
TemplateExceptionHandler RETHROW_HANDLER = new TemplateExceptionHandler() {
@Override
public void handleTemplateException(TemplateException te, Environment env, Writer out)
throws TemplateException {
throw te;
}
};
/**
* {@link TemplateExceptionHandler} useful when you are developing non-HTML templates. This
* handler outputs the stack trace information to the client and then re-throws the exception.
*/
TemplateExceptionHandler DEBUG_HANDLER = new TemplateExceptionHandler() {
@Override
public void handleTemplateException(TemplateException te, Environment env, Writer out)
throws TemplateException {
if (!env.isInAttemptBlock()) {
PrintWriter pw = (out instanceof PrintWriter) ? (PrintWriter) out : new PrintWriter(out);
pw.print("FreeMarker template error (DEBUG mode; use RETHROW in production!):\n");
te.printStackTrace(pw, false, true, true);
pw.flush(); // To commit the HTTP response
}
throw te;
}
};
/**
* {@link TemplateExceptionHandler} useful when you are developing HTML templates. This handler
* outputs the stack trace information to the client, formatting it so that it will be usually
* well readable in the browser, and then re-throws the exception.
*/
TemplateExceptionHandler HTML_DEBUG_HANDLER = new TemplateExceptionHandler() {
@Override
//handleTemplateException
public void handleTemplateException(TemplateException te, Environment env, Writer out)
//参数:Te—发生的异常。env—表示呈现上下文的环境对象.输出-要输出到的字符输出流。//
throws TemplateException {
//抛出:TemplateException//
if (!env.isInAttemptBlock()) {
boolean externalPw = out instanceof PrintWriter;
PrintWriter pw = externalPw ? (PrintWriter) out : new PrintWriter(out);
try {
pw.print("<!-- FREEMARKER ERROR MESSAGE STARTS HERE -->"
+ "<!-- ]]> -->"
+ "<script language=javascript>//\"></script>"
+ "<script language=javascript>//'></script>"
+ "<script language=javascript>//\"></script>"
+ "<script language=javascript>//'></script>"
+ "</title></xmp></script></noscript></style></object>"
+ "</head></pre></table>"
+ "</form></table></table></table></a></u></i></b>"
+ "<div align='left' "
+ "style='background-color:#FFFF7C; "
+ "display:block; border-top:double; padding:4px; margin:0; "
+ "font-family:Arial,sans-serif; ");
pw.print(FONT_RESET_CSS);
pw.print("'>"
+ "<b style='font-size:12px; font-style:normal; font-weight:bold; "
+ "text-decoration:none; text-transform: none;'>FreeMarker template error "
+ " (HTML_DEBUG mode; use RETHROW in production!)</b>"
+ "<pre style='display:block; background: none; border: 0; margin:0; padding: 0;"
+ "font-family:monospace; ");
pw.print(FONT_RESET_CSS);
pw.println("; white-space: pre-wrap; white-space: -moz-pre-wrap; white-space: -pre-wrap; "
+ "white-space: -o-pre-wrap; word-wrap: break-word;'>");
StringWriter stackTraceSW = new StringWriter();
PrintWriter stackPW = new PrintWriter(stackTraceSW);
te.printStackTrace(stackPW, false, true, true);
stackPW.close();
pw.println();
pw.println(StringUtil.XMLEncNQG(stackTraceSW.toString()));
pw.println("</pre></div></html>");
pw.flush(); // To commit the HTTP response
} finally {
if (!externalPw) pw.close();
}
} // if (!env.isInAttemptBlock())
throw te;
}
//确定输出的格式//
private static final String FONT_RESET_CSS =
"color:#A80000; font-size:12px; font-style:normal; font-variant:normal; "
+ "font-weight:normal; text-decoration:none; text-transform: none";
};
}
DefaultEnumerationAdapter
代码分析
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 freemarker.template;
import java.io.Serializable;
import java.util.Enumeration;
import java.util.Iterator;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import freemarker.ext.util.WrapperTemplateModel;
import freemarker.template.utility.ObjectWrapperWithAPISupport;
/**
* Adapts an {@link Enumeration} to the corresponding {@link TemplateModel} interface(s), most importantly to
* {@link TemplateCollectionModel}. Putting aside that it wraps an {@link Enumeration} instead of an {@link Iterator},
* this is identical to {@link DefaultIteratorAdapter}, so see further details there.
*
* @since 2.3.26
*/
@SuppressWarnings("serial")
public class DefaultEnumerationAdapter extends WrappingTemplateModel implements TemplateCollectionModel,
AdapterTemplateModel, WrapperTemplateModel, TemplateModelWithAPISupport, Serializable {
@SuppressFBWarnings(value="SE_BAD_FIELD", justification="We hope it's Seralizable")
private final Enumeration<?> enumeration;
private boolean enumerationOwnedBySomeone;
//参数:enumeration—适应的枚举;不能为空//
public static DefaultEnumerationAdapter adapt(Enumeration<?> enumeration, ObjectWrapper wrapper) {
return new DefaultEnumerationAdapter(enumeration, wrapper);
}
private DefaultEnumerationAdapter(Enumeration<?> enumeration, ObjectWrapper wrapper) {
super(wrapper);
this.enumeration = enumeration;
}
//规定:接口AdapterTemplateModel中的getAdaptedObject//
@Override
public Object getWrappedObject() {
return enumeration;
}
//返回:底层对象,或它的值容纳给提示类//
@Override
public Object getAdaptedObject(Class<?> hint) {
return getWrappedObject();
}
@Override
public TemplateModelIterator iterator() throws TemplateModelException {
return new SimpleTemplateModelIterator();
}
/*描述从接口复制:TemplateModelWithAPISupport
返回公开值的(Java) API的模型。这通常通过委托给objectwrapperwithapissupport . wrapasapi(对象)来实现。*/
//规定:在接口TemplateModelWithAPISupport中的getAPI//
@Override
public TemplateModel getAPI() throws TemplateModelException {
return ((ObjectWrapperWithAPISupport) getObjectWrapper()).wrapAsAPI(enumeration);
}
/**
* Not thread-safe.
*/
private class SimpleTemplateModelIterator implements TemplateModelIterator {
private boolean enumerationOwnedByMe;
@Override
public TemplateModel next() throws TemplateModelException {
if (!enumerationOwnedByMe) {
checkNotOwner();
enumerationOwnedBySomeone = true;
enumerationOwnedByMe = true;
}
if (!enumeration.hasMoreElements()) {
throw new TemplateModelException("The collection has no more items.");
}
Object value = enumeration.nextElement();
return value instanceof TemplateModel ? (TemplateModel) value : wrap(value);
}
@Override
public boolean hasNext() throws TemplateModelException {
// Calling hasNext may looks safe, but I have met sync. problems.
if (!enumerationOwnedByMe) {
checkNotOwner();
}
return enumeration.hasMoreElements();
}
private void checkNotOwner() throws TemplateModelException {
if (enumerationOwnedBySomeone) {
throw new TemplateModelException(
"This collection value wraps a java.util.Enumeration, thus it can be listed only once.");
}
}
}
}