diff --git a/src/main/java/freemarker/core/Environment.java b/src/main/java/freemarker/core/Environment.java index bb435714f..6cbfe28bf 100644 --- a/src/main/java/freemarker/core/Environment.java +++ b/src/main/java/freemarker/core/Environment.java @@ -188,6 +188,8 @@ public final class Environment extends Configurable { private boolean fastInvalidReferenceExceptions; + private TemplateProcessingTracer currentTracer; + /** * Retrieves the environment object associated with the current thread, or {@code null} if there's no template * processing going on in this thread. Data model implementations that need access to the environment can call this @@ -2879,6 +2881,13 @@ public TemplateModel get(String key) throws TemplateModelException { }; } + /** + * Sets the tracer to use for this environment. + */ + public void setTracer(TemplateProcessingTracer tracer) { + currentTracer = tracer; + } + private void pushElement(TemplateElement element) { final int newSize = ++instructionStackSize; TemplateElement[] instructionStack = this.instructionStack; @@ -2891,9 +2900,20 @@ private void pushElement(TemplateElement element) { this.instructionStack = instructionStack; } instructionStack[newSize - 1] = element; + if (currentTracer != null) { + currentTracer.enterElement(element.getTemplate(), + element.getBeginColumn(), element.getBeginLine(), + element.getEndColumn(), element.getEndLine(), element.isLeaf()); + } } private void popElement() { + if (currentTracer != null) { + TemplateElement element = instructionStack[instructionStackSize - 1]; + currentTracer.exitElement(element.getTemplate(), + element.getBeginColumn(), element.getBeginLine(), + element.getEndColumn(), element.getEndLine()); + } instructionStackSize--; } diff --git a/src/main/java/freemarker/core/ListElseContainer.java b/src/main/java/freemarker/core/ListElseContainer.java index 53aeee5fa..856e5b02b 100644 --- a/src/main/java/freemarker/core/ListElseContainer.java +++ b/src/main/java/freemarker/core/ListElseContainer.java @@ -37,10 +37,10 @@ public ListElseContainer(IteratorBlock listPart, ElseOfList elsePart) { @Override TemplateElement[] accept(Environment env) throws TemplateException, IOException { - if (!listPart.acceptWithResult(env)) { - return elsePart.accept(env); + if (listPart.acceptWithResult(env)) { + return null; } - return null; + return new TemplateElement[] { elsePart }; } @Override diff --git a/src/main/java/freemarker/core/TemplateProcessingTracer.java b/src/main/java/freemarker/core/TemplateProcessingTracer.java new file mode 100644 index 000000000..434891e24 --- /dev/null +++ b/src/main/java/freemarker/core/TemplateProcessingTracer.java @@ -0,0 +1,56 @@ +/* + * 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.core; + +import freemarker.ext.util.IdentityHashMap; +import freemarker.template.Configuration; +import freemarker.template.Template; +import freemarker.template.TemplateDirectiveModel; +import freemarker.template.TemplateTransformModel; +import freemarker.template.utility.ObjectFactory; + +/** + * Run-time tracer plug-in. This may be * used to implement profiling, coverage analytis, execution tracing, + * and other on-the-fly debugging mechanisms. + *
+ * Use {@link Environment#setTracer(TemplateProcessingTracer)} to configure a tracer for the current environment.
+ *
+ * @since 2.3.33
+ */
+public interface TemplateProcessingTracer {
+
+ /**
+ * Invoked by {@link Environment} whenever it starts processing a new template element. {@code
+ * isLeafElement} indicates whether this element is a leaf, or whether the tracer should expect
+ * to receive lower-level elements within the context of this one.
+ *
+ * @since 2.3.23
+ */
+ void enterElement(Template template, int beginColumn, int beginLine, int endColumn, int endLine,
+ boolean isLeafElement);
+
+ /**
+ * Invoked by {@link Environment} whenever it completes processing a new template element.
+ *
+ * @since 2.3.23
+ */
+ void exitElement(Template template, int beginColumn, int beginLine, int endColumn, int endLine);
+
+}
diff --git a/src/test/java/freemarker/core/TemplateProcessingTracerTest.java b/src/test/java/freemarker/core/TemplateProcessingTracerTest.java
new file mode 100644
index 000000000..f069dc1b8
--- /dev/null
+++ b/src/test/java/freemarker/core/TemplateProcessingTracerTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.core;
+
+import static org.junit.Assert.*;
+
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+
+public class TemplateProcessingTracerTest {
+
+ private static final String TEMPLATE_TEXT =
+ "<#if 0 == 1>Nope.\n#if>" +
+ "<#if 1 == 1>Yup.\n#if>" +
+ "Always.\n" +
+ "<#list [1, 2, 3] as item>\n" +
+ "${item}<#else>\n" +
+ "Nope.\n" +
+ "#list>\n" +
+ "<#list [] as item>\n" +
+ "${item}<#else>" +
+ "Yup.\n" +
+ "#list>\n";
+
+ @Test
+ public void test() throws Exception {
+ Configuration cfg = new Configuration(Configuration.VERSION_2_3_32);
+ Template t = new Template("test.ftl", TEMPLATE_TEXT, cfg);
+ StringWriter sw = new StringWriter();
+ Tracer tracer = new Tracer(TEMPLATE_TEXT);
+ Environment env = t.createProcessingEnvironment(null, sw);
+ env.setTracer(tracer);
+ env.process();
+
+ List