/*
* Copyright (c) 2002-2012 Alibaba Group Holding Limited.
* All rights reserved.
*
* 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.alibaba.citrus.util.templatelite;
import static com.alibaba.citrus.test.TestUtil.*;
import static com.alibaba.citrus.util.StringUtil.*;
import static org.junit.Assert.*;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import org.hamcrest.Matchers;
import org.junit.Test;
public class TemplateVisitTests extends AbstractTemplateTests {
@Test
public void render_methodNotFound_forText() throws Exception {
String s;
s = "";
s += "test";
loadTemplate(s.getBytes(), "test.txt", 1, 0, 0);
// 无visitText(String)方法
class Visitor {
}
acceptFailure(new Visitor());
assertThat(runtimeError, exception(NoSuchMethodException.class, "Error rendering Text with 4 characters"));
assertEquals("Visitor.visitText(String)", runtimeError.getCause().getMessage());
// 有visitText()方法,但参数不匹配
@SuppressWarnings("unused")
class Visitor2 {
public void visitText() {
}
}
acceptFailure(new Visitor2());
assertThat(runtimeError, exception(NoSuchMethodException.class, "Error rendering Text with 4 characters"));
assertEquals("Visitor2.visitText(String)", runtimeError.getCause().getMessage());
// 有visitText()方法,但参数不匹配
@SuppressWarnings("unused")
class Visitor3 {
public void visitText(Object o) {
}
}
acceptFailure(new Visitor3());
assertThat(runtimeError, exception(NoSuchMethodException.class, "Error rendering Text with 4 characters"));
assertEquals("Visitor3.visitText(String)", runtimeError.getCause().getMessage());
}
@Test
public void render_methodNotFound_forPlaceholder_All_Strings() throws Exception {
@SuppressWarnings("unused")
class Visitor {
// 无方法
// public void visitTest1() {
// }
// 有1个参数,但参数类型非数组
public void visitTest2(Object obj) {
}
// 有1个参数,但参数类型非正确的数组
public void visitTest3(Template[] params) {
}
// 有多个参数,参数数量大于placeholder参数数量,多余的参数类型不正确
public void visitTest4(String a, String b, String c, Object d) {
}
// 有多个参数,但参数类型不匹配
public void visitTest5(String a, String b, Template c) {
}
}
loadTemplate("${test1: a, b, c}".getBytes(), "test.txt", 1, 0, 0);
acceptFailure(new Visitor());
assertThat(runtimeError,
exception(NoSuchMethodException.class, "Error rendering ${test1:a, b, c} at test.txt: Line 1 Column 1"));
assertEquals("One of the following method:\n" //
+ " 1. Visitor.visitTest1(String, String, String)\n" //
+ " 2. Visitor.visitTest1(String[])\n" //
+ " 3. Visitor.visitTest1()", runtimeError.getCause().getMessage());
loadTemplate("${test1}".getBytes(), "test.txt", 1, 0, 0);
acceptFailure(new Visitor());
assertThat(runtimeError,
exception(NoSuchMethodException.class, "Error rendering ${test1} at test.txt: Line 1 Column 1"));
assertEquals("One of the following method:\n" //
+ " 1. Visitor.visitTest1()\n" //
+ " 2. Visitor.visitTest1(String[])", runtimeError.getCause().getMessage());
loadTemplate("${test2: a, b, c}".getBytes(), "test.txt", 1, 0, 0);
acceptFailure(new Visitor());
assertThat(runtimeError,
exception(NoSuchMethodException.class, "Error rendering ${test2:a, b, c} at test.txt: Line 1 Column 1"));
assertEquals("One of the following method:\n" //
+ " 1. Visitor.visitTest2(String, String, String)\n" //
+ " 2. Visitor.visitTest2(String[])\n" //
+ " 3. Visitor.visitTest2()", runtimeError.getCause().getMessage());
loadTemplate("${test3: a, b, c}".getBytes(), "test.txt", 1, 0, 0);
acceptFailure(new Visitor());
assertThat(runtimeError,
exception(NoSuchMethodException.class, "Error rendering ${test3:a, b, c} at test.txt: Line 1 Column 1"));
assertEquals("One of the following method:\n" //
+ " 1. Visitor.visitTest3(String, String, String)\n" //
+ " 2. Visitor.visitTest3(String[])\n" //
+ " 3. Visitor.visitTest3()", runtimeError.getCause().getMessage());
loadTemplate("${test4: a, b, c}".getBytes(), "test.txt", 1, 0, 0);
acceptFailure(new Visitor());
assertThat(runtimeError,
exception(NoSuchMethodException.class, "Error rendering ${test4:a, b, c} at test.txt: Line 1 Column 1"));
assertEquals("One of the following method:\n" //
+ " 1. Visitor.visitTest4(String, String, String)\n" //
+ " 2. Visitor.visitTest4(String[])\n" //
+ " 3. Visitor.visitTest4()", runtimeError.getCause().getMessage());
loadTemplate("${test5: a, b, c}".getBytes(), "test.txt", 1, 0, 0);
acceptFailure(new Visitor());
assertThat(runtimeError,
exception(NoSuchMethodException.class, "Error rendering ${test5:a, b, c} at test.txt: Line 1 Column 1"));
assertEquals("One of the following method:\n" //
+ " 1. Visitor.visitTest5(String, String, String)\n" //
+ " 2. Visitor.visitTest5(String[])\n" //
+ " 3. Visitor.visitTest5()", runtimeError.getCause().getMessage());
}
@Test
public void render_methodNotFound_forPlaceholder_All_Template() throws Exception {
@SuppressWarnings("unused")
class Visitor {
// 无方法
// public void visitTest1() {
// }
// 有1个参数,但参数类型非数组
public void visitTest2(Object obj) {
}
// 有1个参数,但参数类型非正确的数组
public void visitTest3(String[] params) {
}
// 有多个参数,参数数量大于placeholder参数数量,多余的参数类型不正确
public void visitTest4(Template a, Template b, Template c, Object d) {
}
// 有多个参数,但参数类型不匹配
public void visitTest5(Template a, Template b, String c) {
}
}
String s = "\n#a\n#end\n#b\n#end\n#c\n#end\n";
loadTemplate(("${test1: #a, #b, #c}" + s).getBytes(), "test.txt", 1, 3, 0);
acceptFailure(new Visitor());
assertThat(
runtimeError,
exception(NoSuchMethodException.class,
"Error rendering ${test1:#a, #b, #c} at test.txt: Line 1 Column 1"));
assertEquals("One of the following method:\n" //
+ " 1. Visitor.visitTest1(Template, Template, Template)\n" //
+ " 2. Visitor.visitTest1(Template[])\n" //
+ " 3. Visitor.visitTest1()", runtimeError.getCause().getMessage());
loadTemplate(("${test2: #a, #b, #c}" + s).getBytes(), "test.txt", 1, 3, 0);
acceptFailure(new Visitor());
assertThat(
runtimeError,
exception(NoSuchMethodException.class,
"Error rendering ${test2:#a, #b, #c} at test.txt: Line 1 Column 1"));
assertEquals("One of the following method:\n" //
+ " 1. Visitor.visitTest2(Template, Template, Template)\n" //
+ " 2. Visitor.visitTest2(Template[])\n" //
+ " 3. Visitor.visitTest2()", runtimeError.getCause().getMessage());
loadTemplate(("${test3: #a, #b, #c}" + s).getBytes(), "test.txt", 1, 3, 0);
acceptFailure(new Visitor());
assertThat(
runtimeError,
exception(NoSuchMethodException.class,
"Error rendering ${test3:#a, #b, #c} at test.txt: Line 1 Column 1"));
assertEquals("One of the following method:\n" //
+ " 1. Visitor.visitTest3(Template, Template, Template)\n" //
+ " 2. Visitor.visitTest3(Template[])\n" //
+ " 3. Visitor.visitTest3()", runtimeError.getCause().getMessage());
loadTemplate(("${test4: #a, #b, #c}" + s).getBytes(), "test.txt", 1, 3, 0);
acceptFailure(new Visitor());
assertThat(
runtimeError,
exception(NoSuchMethodException.class,
"Error rendering ${test4:#a, #b, #c} at test.txt: Line 1 Column 1"));
assertEquals("One of the following method:\n" //
+ " 1. Visitor.visitTest4(Template, Template, Template)\n" //
+ " 2. Visitor.visitTest4(Template[])\n" //
+ " 3. Visitor.visitTest4()", runtimeError.getCause().getMessage());
loadTemplate(("${test5: #a, #b, #c}" + s).getBytes(), "test.txt", 1, 3, 0);
acceptFailure(new Visitor());
assertThat(
runtimeError,
exception(NoSuchMethodException.class,
"Error rendering ${test5:#a, #b, #c} at test.txt: Line 1 Column 1"));
assertEquals("One of the following method:\n" //
+ " 1. Visitor.visitTest5(Template, Template, Template)\n" //
+ " 2. Visitor.visitTest5(Template[])\n" //
+ " 3. Visitor.visitTest5()", runtimeError.getCause().getMessage());
}
@Test
public void render_methodNotFound_forPlaceholder_Hybrid_String_and_Template() throws Exception {
@SuppressWarnings("unused")
class Visitor {
// 无方法
// public void visitTest1() {
// }
// 有1个参数,但参数类型非数组
public void visitTest2(Object obj) {
}
// 有1个参数,但参数类型非正确的数组
public void visitTest3(String[] params) {
}
// 有1个参数,但参数类型非正确的数组
public void visitTest4(Template[] params) {
}
// 有多个参数,参数数量大于placeholder参数数量,多余的参数类型不正确
public void visitTest5(String a, Template b, Template c, Object d) {
}
// 有多个参数,但参数类型不匹配
public void visitTest6(String a, Template b, String c) {
}
}
String s = "\n#b\n#end\n#c\n#end\n";
loadTemplate(("${test1: a, #b, #c}" + s).getBytes(), "test.txt", 1, 2, 0);
acceptFailure(new Visitor());
assertThat(
runtimeError,
exception(NoSuchMethodException.class,
"Error rendering ${test1:a, #b, #c} at test.txt: Line 1 Column 1"));
assertEquals("One of the following method:\n" //
+ " 1. Visitor.visitTest1(String, Template, Template)\n" //
+ " 2. Visitor.visitTest1(Object[])\n" //
+ " 3. Visitor.visitTest1()", runtimeError.getCause().getMessage());
loadTemplate(("${test2: a, #b, #c}" + s).getBytes(), "test.txt", 1, 2, 0);
acceptFailure(new Visitor());
assertThat(
runtimeError,
exception(NoSuchMethodException.class,
"Error rendering ${test2:a, #b, #c} at test.txt: Line 1 Column 1"));
assertEquals("One of the following method:\n" //
+ " 1. Visitor.visitTest2(String, Template, Template)\n" //
+ " 2. Visitor.visitTest2(Object[])\n" //
+ " 3. Visitor.visitTest2()", runtimeError.getCause().getMessage());
loadTemplate(("${test3: a, #b, #c}" + s).getBytes(), "test.txt", 1, 2, 0);
acceptFailure(new Visitor());
assertThat(
runtimeError,
exception(NoSuchMethodException.class,
"Error rendering ${test3:a, #b, #c} at test.txt: Line 1 Column 1"));
assertEquals("One of the following method:\n" //
+ " 1. Visitor.visitTest3(String, Template, Template)\n" //
+ " 2. Visitor.visitTest3(Object[])\n" //
+ " 3. Visitor.visitTest3()", runtimeError.getCause().getMessage());
loadTemplate(("${test4: a, #b, #c}" + s).getBytes(), "test.txt", 1, 2, 0);
acceptFailure(new Visitor());
assertThat(
runtimeError,
exception(NoSuchMethodException.class,
"Error rendering ${test4:a, #b, #c} at test.txt: Line 1 Column 1"));
assertEquals("One of the following method:\n" //
+ " 1. Visitor.visitTest4(String, Template, Template)\n" //
+ " 2. Visitor.visitTest4(Object[])\n" //
+ " 3. Visitor.visitTest4()", runtimeError.getCause().getMessage());
loadTemplate(("${test5: a, #b, #c}" + s).getBytes(), "test.txt", 1, 2, 0);
acceptFailure(new Visitor());
assertThat(
runtimeError,
exception(NoSuchMethodException.class,
"Error rendering ${test5:a, #b, #c} at test.txt: Line 1 Column 1"));
assertEquals("One of the following method:\n" //
+ " 1. Visitor.visitTest5(String, Template, Template)\n" //
+ " 2. Visitor.visitTest5(Object[])\n" //
+ " 3. Visitor.visitTest5()", runtimeError.getCause().getMessage());
loadTemplate(("${test6: a, #b, #c}" + s).getBytes(), "test.txt", 1, 2, 0);
acceptFailure(new Visitor());
assertThat(
runtimeError,
exception(NoSuchMethodException.class,
"Error rendering ${test6:a, #b, #c} at test.txt: Line 1 Column 1"));
assertEquals("One of the following method:\n" //
+ " 1. Visitor.visitTest6(String, Template, Template)\n" //
+ " 2. Visitor.visitTest6(Object[])\n" //
+ " 3. Visitor.visitTest6()", runtimeError.getCause().getMessage());
}
@Test
public void render_forPlaceholder_All_Strings() throws Exception {
@SuppressWarnings("unused")
class Visitor extends TextWriter<StringBuilder> {
// 无参数
public void visitTest1() {
out().append("no_params");
}
// 1个参数:String
public void visitTest2(String a) {
out().append(a);
}
// 有1个参数:String[]
public void visitTest3(String[] params) {
out().append(join(params, ","));
}
// 有1个参数:Object[]
public void visitTest4(Object[] params) {
out().append(join(params, ","));
}
// 有多个参数,参数数量小于placeholder参数数量
public void visitTest5(String a, String b) {
out().append(a + "," + b);
}
// 有多个参数,参数数量完全匹配
public void visitTest6(String a, String b, String c) {
out().append(a + "," + b + "," + c);
}
}
loadTemplate("${test1: a, b, c}".getBytes(), "test.txt", 1, 0, 0);
assertEquals("no_params", template.renderToString(new Visitor()));
loadTemplate("${test2: a, b, c}".getBytes(), "test.txt", 1, 0, 0);
assertEquals("a", template.renderToString(new Visitor()));
loadTemplate("${test3: a, b, c}".getBytes(), "test.txt", 1, 0, 0);
assertEquals("a,b,c", template.renderToString(new Visitor()));
loadTemplate("${test4: a, b, c}".getBytes(), "test.txt", 1, 0, 0);
assertEquals("a,b,c", template.renderToString(new Visitor()));
loadTemplate("${test5: a, b, c}".getBytes(), "test.txt", 1, 0, 0);
assertEquals("a,b", template.renderToString(new Visitor()));
loadTemplate("${test6: a, b, c}".getBytes(), "test.txt", 1, 0, 0);
assertEquals("a,b,c", template.renderToString(new Visitor()));
}
@Test
public void render_forPlaceholder_All_Templates() throws Exception {
@SuppressWarnings("unused")
class Visitor extends TextWriter<StringBuilder> {
// 无参数
public void visitTest1() {
out().append("no_params");
}
// 1个参数:String
public void visitTest2(Template a) {
a.accept(this);
}
// 有1个参数:Template[]
public void visitTest3(Template[] params) {
for (Template template : params) {
template.accept(this);
}
}
// 有1个参数:Object[]
public void visitTest4(Object[] params) {
for (Object template : params) {
((Template) template).accept(this);
}
}
// 有多个参数,参数数量小于placeholder参数数量
public void visitTest5(Template a, Template b) {
a.accept(this);
b.accept(this);
}
// 有多个参数,参数数量完全匹配
public void visitTest6(Template a, Template b, Template c) {
a.accept(this);
b.accept(this);
c.accept(this);
}
}
String s = "\n#a\na\n#end\n#b\nb\n#end\n#c\nc\n#end\n";
loadTemplate(("${test1: #a, #b, #c}" + s).getBytes(), "test.txt", 1, 3, 0);
assertEquals("no_params", template.renderToString(new Visitor()));
loadTemplate(("${test2: #a, #b, #c}" + s).getBytes(), "test.txt", 1, 3, 0);
assertEquals("a", template.renderToString(new Visitor()));
loadTemplate(("${test3: #a, #b, #c}" + s).getBytes(), "test.txt", 1, 3, 0);
assertEquals("abc", template.renderToString(new Visitor()));
loadTemplate(("${test4: #a, #b, #c}" + s).getBytes(), "test.txt", 1, 3, 0);
assertEquals("abc", template.renderToString(new Visitor()));
loadTemplate(("${test5: #a, #b, #c}" + s).getBytes(), "test.txt", 1, 3, 0);
assertEquals("ab", template.renderToString(new Visitor()));
loadTemplate(("${test6: #a, #b, #c}" + s).getBytes(), "test.txt", 1, 3, 0);
assertEquals("abc", template.renderToString(new Visitor()));
}
@Test
public void render_forPlaceholder_Hybrid_String_and_Template() throws Exception {
@SuppressWarnings("unused")
class Visitor extends TextWriter<StringBuilder> {
// 无参数
public void visitTest1() {
out().append("no_params");
}
// 1个参数:String
public void visitTest2(String a) {
out().append(a);
}
// 有1个参数:Object[]
public void visitTest3(Object[] params) {
out().append(params[0]);
((Template) params[1]).accept(this);
((Template) params[2]).accept(this);
}
// 有多个参数,参数数量小于placeholder参数数量
public void visitTest4(String a, Template b) {
out().append(a);
b.accept(this);
}
// 有多个参数,参数数量完全匹配
public void visitTest5(String a, Template b, Template c) {
out().append(a);
b.accept(this);
c.accept(this);
}
}
String s = "\n";
s += "#a\n";
s += "a\n";
s += "#end\n";
s += "#b\n";
s += "b\n";
s += "#end\n";
s += "#c\n";
s += "c\n";
s += "#end\n";
loadTemplate(("${test1: a, #b, #c}" + s).getBytes(), "test.txt", 1, 3, 0);
assertEquals("no_params", template.renderToString(new Visitor()));
loadTemplate(("${test2: a, #b, #c}" + s).getBytes(), "test.txt", 1, 3, 0);
assertEquals("a", template.renderToString(new Visitor()));
loadTemplate(("${test3: a, #b, #c}" + s).getBytes(), "test.txt", 1, 3, 0);
assertEquals("abc", template.renderToString(new Visitor()));
loadTemplate(("${test4: a, #b, #c}" + s).getBytes(), "test.txt", 1, 3, 0);
assertEquals("ab", template.renderToString(new Visitor()));
loadTemplate(("${test5: a, #b, #c}" + s).getBytes(), "test.txt", 1, 3, 0);
assertEquals("abc", template.renderToString(new Visitor()));
}
@Test
public void render_forPlaceholder_TemplateRefs() throws Exception {
@SuppressWarnings("unused")
class Visitor extends TextWriter<StringBuilder> {
public void visitTest(Template[] params) {
for (Template template : params) {
template.accept(this);
}
}
public void visitA(Template a) {
}
/*
* // 缺失visitB public void visitB(Template b) { b.accept(this); }
*/
public void visitC(Template c) {
c.accept(this);
c.accept(this);
c.accept(this);
}
}
String s = "\n";
s += "#a\n";
s += "a\n";
s += "#end\n";
s += "#b\n";
s += "b\n";
s += "#end\n";
s += "#c\n";
s += "c\n";
s += "#end\n";
loadTemplate(("${test: #a, #b, #c}" + s).getBytes(), "test.txt", 1, 3, 0);
assertEquals("bccc", template.renderToString(new Visitor()));
}
@Test
public void render_include_template() throws Exception {
loadTemplate("$#{a}\n#a\naaa\n#end".getBytes(), "test.txt", 1, 1, 0);
assertEquals("aaa", template.renderToString(new FallbackTextWriter<StringBuilder>()));
}
@Test
public void render_include_templateRef() throws Exception {
@SuppressWarnings("unused")
class Visitor extends TextWriter<StringBuilder> {
public void visitA(Template a) {
a.accept(this);
a.accept(this);
a.accept(this);
}
}
loadTemplate("$#{a}\n#a\na\n#end".getBytes(), "test.txt", 1, 1, 0);
assertEquals("aaa", template.renderToString(new Visitor()));
}
@Test
public void render_include_redirect_templateRef() throws Exception {
@SuppressWarnings("unused")
class Visitor1 extends TextWriter<StringBuilder> {
public Visitor1(StringBuilder out) {
super(out);
}
public void visitA(Template a) {
a.accept(this);
a.accept(this);
a.accept(this);
}
}
@SuppressWarnings("unused")
class Visitor2 extends TextWriter<StringBuilder> {
public Visitor1 visitA(Template a) {
return new Visitor1(out());
}
}
loadTemplate("$#{a}\n#a\na\n#end".getBytes(), "test.txt", 1, 1, 0);
assertEquals("aaa", template.renderToString(new Visitor2()));
}
@Test
public void render_visitorThrowsException() throws Exception {
@SuppressWarnings("unused")
class Visitor {
public void visitTitle() throws IOException {
throw new IllegalArgumentException();
}
}
loadTemplate("${title}".getBytes(), "test.txt", 1, 0, 0);
acceptFailure(new Visitor());
assertThat(runtimeError.getCause(), Matchers.instanceOf(IllegalArgumentException.class));
assertThat(runtimeError,
exception(IllegalArgumentException.class, "Error rendering ${title} at test.txt: Line 1 Column 1"));
}
@Test
public void render_visitorThrowsIOException() throws Exception {
@SuppressWarnings("unused")
class Visitor {
public void visitTitle() throws IOException {
throw new IOException();
}
}
loadTemplate("${title}".getBytes(), "test.txt", 1, 0, 0);
acceptFailure(new Visitor());
assertThat(runtimeError.getCause(), Matchers.instanceOf(IOException.class));
assertThat(runtimeError, exception(IOException.class, "Error rendering ${title} at test.txt: Line 1 Column 1"));
}
@Test
public void render_visitorThrowsException_withInvocationHandler() throws Exception {
@SuppressWarnings("unused")
class Visitor implements VisitorInvocationErrorHandler {
public void visitTitle() throws IOException {
throw new IllegalArgumentException("haha");
}
public void handleInvocationError(String desc, Throwable e) {
assertThat(desc, containsAll("${title} at test.txt: Line 1 Column 1"));
runtimeError = new TemplateRuntimeException(e);
}
}
loadTemplate("${title}".getBytes(), "test.txt", 1, 0, 0);
template.accept(new Visitor());
assertThat(runtimeError.getCause(), exception(IllegalArgumentException.class, "haha"));
}
@Test
public void render_visitorThrowsException_withInvocationHandlerError() throws Exception {
@SuppressWarnings("unused")
class Visitor implements VisitorInvocationErrorHandler {
public void visitTitle() throws IOException {
throw new IllegalArgumentException("haha");
}
public void handleInvocationError(String desc, Throwable e) {
throw new IllegalArgumentException("handleInvocationError");
}
}
loadTemplate("${title}".getBytes(), "test.txt", 1, 0, 0);
try {
template.accept(new Visitor());
fail();
} catch (TemplateRuntimeException e) {
assertThat(
e,
exception(IllegalArgumentException.class, "handleInvocationError",
"${title} at test.txt: Line 1 Column 1"));
}
}
@Test
public void render_visitorThrowsException_textWriter() throws Exception {
@SuppressWarnings("unused")
class Visitor extends TextWriter<StringBuilder> {
public void visitTitle() throws IOException {
IOException e = new IOException();
e.initCause(new IllegalArgumentException("haha"));
throw e;
}
}
loadTemplate("${title}".getBytes(), "test.txt", 1, 0, 0);
// 打印root cause
assertThat(template.renderToString(new Visitor()),
containsAll("IllegalArgumentException - haha - ", Visitor.class.getName() + ".visitTitle("));
}
@Test
public void render_fallbackVisitor() throws Exception {
// no context
loadTemplate(("$#{a}\n" //
+ "#a\n" //
+ "${title:a,b}\n" //
+ "#end\n").getBytes(), "test.txt", 1, 1, 0);
assertEquals("${title}", template.renderToString(new FallbackTextWriter<StringBuilder>()));
// with context
FallbackTextWriter<StringBuilder> visitor = new FallbackTextWriter<StringBuilder>();
visitor.context().put("title", "hello, world");
assertEquals("hello, world", template.renderToString(visitor));
}
@Test
public void render_fallbackVisitor_failed() throws Exception {
loadTemplate("${title:a,b}".getBytes(), "test.txt", 1, 0, 0);
class Visitor implements FallbackVisitor {
public boolean visitPlaceholder(String name, Object[] params) throws Exception {
return false;
}
}
acceptFailure(new Visitor());
assertThat(runtimeError,
exception(NoSuchMethodException.class, "Error rendering ${title:a,b} at test.txt: Line 1 Column 1"));
assertEquals("One of the following method:\n" //
+ " 1. Visitor.visitTitle(String, String)\n" //
+ " 2. Visitor.visitTitle(String[])\n" //
+ " 3. Visitor.visitTitle()", runtimeError.getCause().getMessage());
}
@Test
public void render_placeholder_string_noparam() throws Exception {
@SuppressWarnings("unused")
class Visitor extends TextWriter<StringBuilder> {
private int count;
public void visitTitle() throws IOException {
out().append("myTitle");
}
public void visitItem(Template dateItem) throws IOException {
for (count = 1; count < 6; count++) {
dateItem.accept(this);
}
}
public void visitDate() throws IOException {
out().append("count " + count);
}
}
render(new Visitor(), "");
}
@Test
public void render_placeholder_string_array() throws Exception {
@SuppressWarnings("unused")
class Visitor extends TextWriter<StringBuilder> {
private int count;
public void visitTitle() throws IOException {
out().append("myTitle");
}
public void visitItem(Template dateItem) throws IOException {
for (count = 1; count < 6; count++) {
dateItem.accept(this);
}
}
public void visitDate(String[] params) throws IOException {
out().append("count " + count + " - ").append(formatGMT(params[0]));
if (params.length > 1) {
out().append(" ").append(formatGMT(params[1]));
}
}
}
render(new Visitor(), " - 1970-01-01");
}
@Test
public void render_placeholder_strings() throws Exception {
@SuppressWarnings("unused")
class Visitor extends TextWriter<StringBuilder> {
private int count;
public void visitTitle() throws IOException {
out().append("myTitle");
}
public void visitItem(Template dateItem, Template datetimeItem) throws IOException {
for (count = 1; count < 6; count++) {
dateItem.accept(this);
}
}
public void visitDate(String p1, String p2) throws IOException {
out().append("count " + count + " - ").append(formatGMT(p1));
if (p2 != null) {
out().append(" ").append(formatGMT(p2));
}
}
}
render(new Visitor(), " - 1970-01-01");
}
@Test
public void render_placeholder_template_array() throws Exception {
@SuppressWarnings("unused")
class Visitor extends TextWriter<StringBuilder> {
private int count;
public void visitTitle() throws IOException {
out().append("myTitle");
}
public void visitItem(Template[] tpls) throws IOException {
for (count = 1; count < 6; count++) {
tpls[0].accept(this);
}
}
public void visitDate(String p1, String p2) throws IOException {
out().append("count " + count + " - ").append(formatGMT(p1));
if (p2 != null) {
out().append(" ").append(formatGMT(p2));
}
}
}
render(new Visitor(), " - 1970-01-01");
}
@Test
public void render_placeholder_templates() throws Exception {
@SuppressWarnings("unused")
class Visitor extends TextWriter<StringBuilder> {
private int count;
public void visitTitle() throws IOException {
out().append("myTitle");
}
public void visitItem(Template dateItem, Template datetimeItem) throws IOException {
for (count = 1; count < 6; count++) {
datetimeItem.accept(this);
}
}
public void visitDate(String p1, String p2) throws IOException {
out().append("count " + count + " - ").append(formatGMT(p1));
if (p2 != null) {
out().append(" ").append(formatGMT(p2));
}
}
}
render(new Visitor(), " - 1970-01-01 00:00");
}
@Test
public void render_placeholder_method_overrides() throws Exception {
assertRenderOverride("${title}", "title()");
assertRenderOverride("${title:a}", "title(String)");
assertRenderOverride("${title:a,b}", "title(String, String)");
assertRenderOverride("${title:a,b,c}", "title(String[])");
assertRenderOverride("${title:a,b,c,d}", "title(String[])");
assertRenderOverride("${title:#a}", "title(Template)");
assertRenderOverride("${title:#a,#b}", "title(Template, Template)");
assertRenderOverride("${title:#a,#b,#c}", "title(Template[])");
assertRenderOverride("${title:#a,#b,#c,#d}", "title(Template[])");
assertRenderOverride("${title:#a,b}", "title(Template, String)");
assertRenderOverride("${title:#a,b,c}", "title(Object[])");
}
private void assertRenderOverride(String placeholder, String result) {
@SuppressWarnings("unused")
class Visitor extends TextWriter<StringBuilder> {
public void visitTitle() {
out().append("title()");
}
public void visitTitle(String[] s) {
out().append("title(String[])");
}
public void visitTitle(Template[] s) {
out().append("title(Template[])");
}
public void visitTitle(Object[] s) {
out().append("title(Object[])");
}
public void visitTitle(String s) {
out().append("title(String)");
}
public void visitTitle(String s, String s1) {
out().append("title(String, String)");
}
public void visitTitle(Template s) {
out().append("title(Template)");
}
public void visitTitle(Template s, Template s1) {
out().append("title(Template, Template)");
}
public void visitTitle(Template s, String s1) {
out().append("title(Template, String)");
}
}
String s = "\n";
s += "#a\n";
s += "#end\n";
s += "#b\n";
s += "#end\n";
s += "#c\n";
s += "#end\n";
s += "#d\n";
s += "#end\n";
loadTemplate((placeholder + s).getBytes(), "test.txt", 1, 4, 0);
assertEquals(result, template.renderToString(new Visitor()));
}
@Test
public void render_forPlaceholder_redirect() {
@SuppressWarnings("unused")
class Visitor1 extends TextWriter<StringBuilder> {
public Visitor1(StringBuilder out) {
super(out);
}
public void visitItems(Template t, String s) {
out().append(s);
t.accept(this);
}
}
@SuppressWarnings("unused")
class Visitor2 extends TextWriter<StringBuilder> {
public Visitor1 visitItems() {
return new Visitor1(out()); // redirect to visitor1
}
}
String s = "${items: #a, hello}\n";
s += "#a\n";
s += "world\n";
s += "#end\n";
loadTemplate(s.getBytes(), "test.txt", 1, 1, 0);
assertEquals("helloworld", template.renderToString(new Visitor2()));
}
@Test
public void render_forPlaceholder_redirectToSelf() {
@SuppressWarnings("unused")
class Visitor2 extends TextWriter<StringBuilder> {
public Visitor2 visitItems() {
return this; // redirect to self
}
}
String s = "${items: #a, hello}\n";
s += "#a\n";
s += "world\n";
s += "#end\n";
loadTemplate(s.getBytes(), "test.txt", 1, 1, 0);
assertEquals("", template.renderToString(new Visitor2()));
}
@Test
public void render_forPlaceholder_redirect_infinite() {
@SuppressWarnings("unused")
class Visitor2 extends TextWriter<StringBuilder> {
public Visitor2(StringBuilder out) {
super(out);
}
public Visitor2 visitItems() {
return new Visitor2(out()); // redirect to another visitor2
}
}
String s = "${items: #a, hello}\n";
s += "#a\n";
s += "world\n";
s += "#end\n";
loadTemplate(s.getBytes(), "test.txt", 1, 1, 0);
try {
template.renderToString(new Visitor2(null));
fail();
} catch (TemplateRuntimeException e) {
assertThat(e, exception("Redirection out of control (depth>10) in ", "Visitor2 ", "Visitor2.visitItems()"));
}
}
@Test
public void render_fallbackToVisitor() {
try {
new FallbackToVisitor(null);
fail();
} catch (IllegalArgumentException e) {
assertThat(e, exception("fallback to visitor"));
}
@SuppressWarnings("unused")
class Visitor1 extends TextWriter<StringBuilder> {
public Visitor1(StringBuilder out) {
super(out);
}
public void visitTitle() {
out().append("hello");
}
}
class Visitor2 extends TextWriter<StringBuilder> implements FallbackVisitor {
public boolean visitPlaceholder(String name, Object[] params) throws Exception {
FallbackToVisitor ftv = new FallbackToVisitor(new Visitor1(out()));
return ftv.visitPlaceholder(name, params);
}
}
String s = "${title}";
loadTemplate(s.getBytes(), "test.txt", 1, 0, 0);
assertEquals("hello", template.renderToString(new Visitor2()));
}
@Test
public void render_fallbackToFallbackVisitor() {
class Visitor1 extends FallbackTextWriter<StringBuilder> {
public Visitor1(StringBuilder out) {
super(out);
context().put("title", "hello");
}
}
class Visitor2 extends TextWriter<StringBuilder> implements FallbackVisitor {
public boolean visitPlaceholder(String name, Object[] params) throws Exception {
FallbackToVisitor ftv = new FallbackToVisitor(new Visitor1(out()));
return ftv.visitPlaceholder(name, params);
}
}
String s = "${title}";
loadTemplate(s.getBytes(), "test.txt", 1, 0, 0);
assertEquals("hello", template.renderToString(new Visitor2()));
}
@Test
public void render_fallbackToFallbackVisitor_failed() {
class Visitor1 implements FallbackVisitor {
public boolean visitPlaceholder(String name, Object[] params) throws Exception {
return false;
}
}
class Visitor2 extends TextWriter<StringBuilder> implements FallbackVisitor {
public boolean visitPlaceholder(String name, Object[] params) throws Exception {
FallbackToVisitor ftv = new FallbackToVisitor(new Visitor1());
return ftv.visitPlaceholder(name, params);
}
}
String s = "${title}";
loadTemplate(s.getBytes(), "test.txt", 1, 0, 0);
acceptFailure(new Visitor2());
assertThat(runtimeError,
exception(NoSuchMethodException.class, "Error rendering ${title} at test.txt: Line 1 Column 1"));
assertEquals("One of the following method:\n" //
+ " 1. Visitor2.visitTitle()\n" //
+ " 2. Visitor2.visitTitle(String[])", runtimeError.getCause().getMessage());
}
@Test
public void render_fallbackToVisitor_failed() {
class Visitor2 extends TextWriter<StringBuilder> implements FallbackVisitor {
public boolean visitPlaceholder(String name, Object[] params) throws Exception {
FallbackToVisitor ftv = new FallbackToVisitor(new Object());
return ftv.visitPlaceholder(name, params);
}
}
String s = "${title}";
loadTemplate(s.getBytes(), "test.txt", 1, 0, 0);
acceptFailure(new Visitor2());
assertThat(runtimeError,
exception(NoSuchMethodException.class, "Error rendering ${title} at test.txt: Line 1 Column 1"));
assertEquals("One of the following method:\n" //
+ " 1. Visitor2.visitTitle()\n" //
+ " 2. Visitor2.visitTitle(String[])", runtimeError.getCause().getMessage());
}
private String formatGMT(String format) {
DateFormat fmt = new SimpleDateFormat(format, Locale.US);
fmt.setTimeZone(TimeZone.getTimeZone("GMT"));
return fmt.format(new Date(0));
}
private void render(TextWriter<StringBuilder> visitor, String extra) throws Exception {
loadTemplate("test06_real_case_2.txt", 5, 1, 3);
String expected = "";
expected += "<html>\n";
expected += "<head>\n";
expected += "<title>myTitle</title>\n";
expected += "</head>\n";
expected += "<body>\n";
expected += "<ul>\n";
expected += "<li>count 1" + extra + "</li>";
expected += "<li>count 2" + extra + "</li>";
expected += "<li>count 3" + extra + "</li>";
expected += "<li>count 4" + extra + "</li>";
expected += "<li>count 5" + extra + "</li>\n";
expected += "</ul>\n";
expected += "</body>\n";
expected += "</html>";
assertEquals(expected, template.renderToString(visitor));
}
}