You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2022/08/28 16:26:13 UTC
[groovy] 01/01: GROOVY-10731: run `MarkupTemplateEngine` type-checked not static compile
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit f743e3fd8c99f3c9948b91ea4a566ac2b49c4456
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun Aug 28 09:41:57 2022 -0500
GROOVY-10731: run `MarkupTemplateEngine` type-checked not static compile
---
.../classgen/asm/indy/IndyCallSiteWriter.java | 25 ++-
.../asm/sc/StaticTypesWriterController.java | 42 ++--
.../groovy/text/markup/MarkupTemplateEngine.java | 45 ++--
.../groovy/text/MarkupTemplateEngineTest.groovy | 248 ++++++++++-----------
4 files changed, 173 insertions(+), 187 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/indy/IndyCallSiteWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/indy/IndyCallSiteWriter.java
index 4ab2ca6d6a..786f016dad 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/indy/IndyCallSiteWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/indy/IndyCallSiteWriter.java
@@ -18,6 +18,7 @@
*/
package org.codehaus.groovy.classgen.asm.indy;
+import org.codehaus.groovy.GroovyBugError;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.classgen.asm.CallSiteWriter;
import org.codehaus.groovy.classgen.asm.WriterController;
@@ -29,27 +30,28 @@ import org.codehaus.groovy.classgen.asm.WriterController;
* call site caching is done by the jvm.
*/
public class IndyCallSiteWriter extends CallSiteWriter {
- private final WriterController controller;
- public IndyCallSiteWriter(WriterController controller) {
- super(controller);
- this.controller = controller;
+
+ public IndyCallSiteWriter(final WriterController controller) {
+ super(controller); this.controller = controller;
}
-
+ private final WriterController controller;
+
@Override
public void generateCallSiteArray() {}
@Override
- public void makeCallSite(Expression receiver, String message,
- Expression arguments, boolean safe, boolean implicitThis,
- boolean callCurrent, boolean callStatic) {}
+ public void makeCallSite(Expression receiver, String message, Expression arguments, boolean safe, boolean implicitThis, boolean callCurrent, boolean callStatic) {}
@Override
- public void makeSingleArgumentCall(Expression receiver, String message, Expression arguments, boolean safe) {}
+ public void makeSingleArgumentCall(Expression receiver, String message, Expression arguments, boolean safe) {
+ throw new GroovyBugError("At line " + receiver.getLineNumber() + " column " + receiver.getColumnNumber() + "\n" +
+ "On receiver: " + receiver.getText() + " with message: " + message + " and arguments: " + arguments.getText() + "\n" +
+ "This method should not have been called. Please try to create a simple example reproducing this error and file a bug report at https://issues.apache.org/jira/browse/GROOVY");
+ }
@Override
- public void prepareCallSite(String message) {}
+ public void prepareCallSite(String message) {}
@Override
public void makeSiteEntry() {}
@Override
public void makeCallSiteArrayInitializer() {}
-
@Override
public void makeGetPropertySite(Expression receiver, String name, boolean safe, boolean implicitThis) {
InvokeDynamicWriter idw = (InvokeDynamicWriter)controller.getInvocationWriter();
@@ -60,5 +62,4 @@ public class IndyCallSiteWriter extends CallSiteWriter {
InvokeDynamicWriter idw = (InvokeDynamicWriter)controller.getInvocationWriter();
idw.writeGetProperty(receiver, name, safe, implicitThis, true);
}
-
}
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesWriterController.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesWriterController.java
index 8aa352cef7..6cebe8970f 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesWriterController.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesWriterController.java
@@ -44,7 +44,6 @@ import org.codehaus.groovy.transform.sc.StaticCompilationVisitor;
import org.codehaus.groovy.transform.stc.StaticTypesMarker;
import org.objectweb.asm.ClassVisitor;
-
/**
* An alternative {@link org.codehaus.groovy.classgen.asm.WriterController} which handles static types and method
* dispatch. In case of a "mixed mode" where only some methods are annotated with {@link groovy.transform.TypeChecked}
@@ -53,19 +52,19 @@ import org.objectweb.asm.ClassVisitor;
public class StaticTypesWriterController extends DelegatingController {
protected boolean isInStaticallyCheckedMethod;
- private StaticTypesCallSiteWriter callSiteWriter;
- private StaticTypesStatementWriter statementWriter;
+
+ private LambdaWriter lambdaWriter;
+ private ClosureWriter closureWriter;
private StaticTypesTypeChooser typeChooser;
private StaticInvocationWriter invocationWriter;
- private BinaryExpressionMultiTypeDispatcher binaryExprHelper;
+ private StaticTypesCallSiteWriter callSiteWriter;
+ private StaticTypesStatementWriter statementWriter;
private UnaryExpressionHelper unaryExpressionHelper;
- private ClosureWriter closureWriter;
- private LambdaWriter lambdaWriter;
+ private BinaryExpressionMultiTypeDispatcher binaryExpressionHelper;
private MethodReferenceExpressionWriter methodReferenceExpressionWriter;
- public StaticTypesWriterController(WriterController normalController) {
- super(normalController);
- isInStaticallyCheckedMethod = false;
+ public StaticTypesWriterController(final WriterController controller) {
+ super(controller);
}
@Override
@@ -81,7 +80,7 @@ public class StaticTypesWriterController extends DelegatingController {
this.unaryExpressionHelper = new StaticTypesUnaryExpressionHelper(this);
CompilerConfiguration config = cn.getCompileUnit().getConfig();
- this.binaryExprHelper = config.isIndyEnabled()
+ this.binaryExpressionHelper = config.isIndyEnabled()
? new IndyStaticTypesMultiTypeDispatcher(this)
: new StaticTypesBinaryExpressionMultiTypeDispatcher(this);
}
@@ -93,15 +92,14 @@ public class StaticTypesWriterController extends DelegatingController {
}
private void updateStaticCompileFlag(final MethodNode mn) {
+ AnnotatedNode outer = mn;
ClassNode classNode = getClassNode();
- AnnotatedNode node = mn;
- boolean implementsGeneratedClosureOrGeneratedLambdaInterface = ClassHelper.isGeneratedFunction(classNode);
- if (implementsGeneratedClosureOrGeneratedLambdaInterface) {
- node = classNode.getOuterClass();
+ boolean inClosureOrLambda = ClassHelper.isGeneratedFunction(classNode);
+ if (inClosureOrLambda) {
+ outer = classNode.getOuterClass();
}
-
- isInStaticallyCheckedMethod = mn != null && (StaticCompilationVisitor.isStaticallyCompiled(node)
- || implementsGeneratedClosureOrGeneratedLambdaInterface && Boolean.TRUE.equals(classNode.getNodeMetaData(StaticCompilationMetadataKeys.STATIC_COMPILE_NODE)));
+ boolean isStaticCompileNode = Boolean.TRUE.equals(classNode.getNodeMetaData(StaticCompilationMetadataKeys.STATIC_COMPILE_NODE));
+ isInStaticallyCheckedMethod = mn != null && (StaticCompilationVisitor.isStaticallyCompiled(outer) || inClosureOrLambda && isStaticCompileNode);
}
@Override
@@ -119,10 +117,8 @@ public class StaticTypesWriterController extends DelegatingController {
@Override
public CallSiteWriter getCallSiteWriter() {
MethodNode methodNode = getMethodNode();
- if (methodNode != null && Boolean.TRUE.equals(methodNode.getNodeMetaData(StaticTypesMarker.DYNAMIC_RESOLUTION))) {
- return super.getCallSiteWriter();
- }
- if (isInStaticallyCheckedMethod) {
+ if (isInStaticallyCheckedMethod && (methodNode == null
+ || !Boolean.TRUE.equals(methodNode.getNodeMetaData(StaticTypesMarker.DYNAMIC_RESOLUTION)))) {
return callSiteWriter;
}
return super.getCallSiteWriter();
@@ -137,7 +133,7 @@ public class StaticTypesWriterController extends DelegatingController {
if (isInStaticallyCheckedMethod) {
return statementWriter;
} else {
- return super.getStatementWriter();
+ return super.getStatementWriter();
}
}
@@ -166,7 +162,7 @@ public class StaticTypesWriterController extends DelegatingController {
@Override
public BinaryExpressionHelper getBinaryExpressionHelper() {
if (isInStaticallyCheckedMethod) {
- return binaryExprHelper;
+ return binaryExpressionHelper;
} else {
return super.getBinaryExpressionHelper();
}
diff --git a/subprojects/groovy-templates/src/main/groovy/groovy/text/markup/MarkupTemplateEngine.java b/subprojects/groovy-templates/src/main/groovy/groovy/text/markup/MarkupTemplateEngine.java
index 7e9b220097..1c23437def 100644
--- a/subprojects/groovy-templates/src/main/groovy/groovy/text/markup/MarkupTemplateEngine.java
+++ b/subprojects/groovy-templates/src/main/groovy/groovy/text/markup/MarkupTemplateEngine.java
@@ -23,7 +23,7 @@ import groovy.lang.GroovyCodeSource;
import groovy.lang.Writable;
import groovy.text.Template;
import groovy.text.TemplateEngine;
-import groovy.transform.CompileStatic;
+import groovy.transform.TypeChecked;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.classgen.GeneratorContext;
@@ -47,6 +47,7 @@ import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collections;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
@@ -63,7 +64,7 @@ public class MarkupTemplateEngine extends TemplateEngine {
private static final Pattern LOCALIZED_RESOURCE_PATTERN = Pattern.compile("(.+?)(?:_([a-z]{2}(?:_[A-Z]{2,3})))?\\.([\\p{Alnum}.]+)$");
- private static final boolean DEBUG_BYTECODE = Boolean.valueOf(System.getProperty("markuptemplateengine.compiler.debug","false"));
+ private static final boolean DEBUG_BYTECODE = Boolean.getBoolean("markuptemplateengine.compiler.debug");
private static final AtomicLong counter = new AtomicLong();
@@ -77,35 +78,36 @@ public class MarkupTemplateEngine extends TemplateEngine {
this(new TemplateConfiguration());
}
- public MarkupTemplateEngine(final TemplateConfiguration tplConfig) {
- this(MarkupTemplateEngine.class.getClassLoader(), tplConfig);
+ public MarkupTemplateEngine(final TemplateConfiguration config) {
+ this(MarkupTemplateEngine.class.getClassLoader(), config, null);
}
- public MarkupTemplateEngine(final ClassLoader parentLoader, final TemplateConfiguration tplConfig) {
- this(parentLoader, tplConfig, null);
+ public MarkupTemplateEngine(final ClassLoader parentLoader, final TemplateConfiguration config) {
+ this(parentLoader, config, null);
}
- public MarkupTemplateEngine(final ClassLoader parentLoader, final TemplateConfiguration tplConfig, final TemplateResolver resolver) {
+ public MarkupTemplateEngine(final ClassLoader parentLoader, final TemplateConfiguration config, final TemplateResolver resolver) {
+ templateConfiguration = config;
compilerConfiguration = new CompilerConfiguration();
- templateConfiguration = tplConfig;
- compilerConfiguration.addCompilationCustomizers(new TemplateASTTransformer(tplConfig));
- compilerConfiguration.addCompilationCustomizers(
- new ASTTransformationCustomizer(Collections.singletonMap("extensions", "groovy.text.markup.MarkupTemplateTypeCheckingExtension"), CompileStatic.class));
+ List<CompilationCustomizer> customizers = compilerConfiguration.getCompilationCustomizers();
+ customizers.add(new TemplateASTTransformer(templateConfiguration));
+ customizers.add(new ASTTransformationCustomizer(
+ Collections.singletonMap("extensions", "groovy.text.markup.MarkupTemplateTypeCheckingExtension"), TypeChecked.class));
if (templateConfiguration.isAutoNewLine()) {
- compilerConfiguration.addCompilationCustomizers(
- new CompilationCustomizer(CompilePhase.CONVERSION) {
- @Override
- public void call(final SourceUnit source, final GeneratorContext context, final ClassNode classNode) throws CompilationFailedException {
- new AutoNewLineTransformer(source).visitClass(classNode);
- }
- }
- );
+ customizers.add(new CompilationCustomizer(CompilePhase.CONVERSION) {
+ @Override
+ public void call(final SourceUnit source, final GeneratorContext context, final ClassNode classNode) {
+ new AutoNewLineTransformer(source).visitClass(classNode);
+ }
+ });
}
- groovyClassLoader = AccessController.doPrivileged((PrivilegedAction<TemplateGroovyClassLoader>) () -> new TemplateGroovyClassLoader(parentLoader, compilerConfiguration));
if (DEBUG_BYTECODE) {
compilerConfiguration.setBytecodePostprocessor(BytecodeDumper.STANDARD_ERR);
}
- templateResolver = resolver == null ? new DefaultTemplateResolver() : resolver;
+
+ groovyClassLoader = AccessController.doPrivileged((PrivilegedAction<TemplateGroovyClassLoader>) () -> new TemplateGroovyClassLoader(parentLoader, compilerConfiguration));
+
+ templateResolver = resolver != null ? resolver : new DefaultTemplateResolver();
templateResolver.configure(groovyClassLoader, templateConfiguration);
}
@@ -371,5 +373,4 @@ public class MarkupTemplateEngine extends TemplateEngine {
return url;
}
}
-
}
diff --git a/subprojects/groovy-templates/src/test/groovy/groovy/text/MarkupTemplateEngineTest.groovy b/subprojects/groovy-templates/src/test/groovy/groovy/text/MarkupTemplateEngineTest.groovy
index 489c59195d..afc28b9221 100644
--- a/subprojects/groovy-templates/src/test/groovy/groovy/text/MarkupTemplateEngineTest.groovy
+++ b/subprojects/groovy-templates/src/test/groovy/groovy/text/MarkupTemplateEngineTest.groovy
@@ -18,7 +18,6 @@
*/
package groovy.text
-import groovy.test.NotYetImplemented
import groovy.text.markup.BaseTemplate
import groovy.text.markup.MarkupTemplateEngine
import groovy.text.markup.TagLibAdapter
@@ -47,93 +46,99 @@ final class MarkupTemplateEngineTest {
@Test
void testSimpleTemplate() {
- MarkupTemplateEngine engine = new MarkupTemplateEngine(new TemplateConfiguration())
+ def engine = new MarkupTemplateEngine(new TemplateConfiguration())
def template = engine.createTemplate '''
html {
body {
yield 'It works!'
}
}
- '''
+ '''
String rendered = template.make()
assert rendered == '<html><body>It works!</body></html>'
}
+ @Test // GROOVY-10731
+ void testSimpleTemplate2() {
+ def engine = new MarkupTemplateEngine(new TemplateConfiguration())
+ def template = engine.createTemplate '''
+ def map = [:]
+ String key = ""
+ def value = map[key] // ArrayIndexOutOfBoundsException
+ span(class: "label label-${value}")
+ '''
+ String rendered = template.make()
+ assert rendered == '<span class=\'label label-null\'/>'
+ }
+
@Test
void testSimpleTemplateWithModel() {
- MarkupTemplateEngine engine = new MarkupTemplateEngine(new TemplateConfiguration())
+ def engine = new MarkupTemplateEngine(new TemplateConfiguration())
def template = engine.createTemplate '''
html {
body {
yield message
}
}
- '''
- def model = [message: 'It works!']
- StringWriter rendered = new StringWriter()
- template.make(model).writeTo(rendered)
- assert rendered.toString() == '<html><body>It works!</body></html>'
+ '''
+ String rendered = template.make(message: 'It works!')
+ assert rendered == '<html><body>It works!</body></html>'
}
@Test
void testSimpleTemplateWithIncludeTemplate() {
- MarkupTemplateEngine engine = new MarkupTemplateEngine(new TemplateConfiguration())
+ def engine = new MarkupTemplateEngine(new TemplateConfiguration())
def template = engine.createTemplate '''
html {
body {
include template:'includes/hello.tpl'
}
}
- '''
- StringWriter rendered = new StringWriter()
- template.make().writeTo(rendered)
- assert rendered.toString() == '<html><body>Hello from include!</body></html>'
+ '''
+ String rendered = template.make()
+ assert rendered == '<html><body>Hello from include!</body></html>'
}
@Test
void testSimpleTemplateWithIncludeTemplateWithLocale() {
- def tplConfig = new TemplateConfiguration()
- tplConfig.locale = Locale.FRANCE
- MarkupTemplateEngine engine = new MarkupTemplateEngine(this.class.classLoader, tplConfig)
+ def config = new TemplateConfiguration(locale: Locale.FRANCE)
+ def engine = new MarkupTemplateEngine(config)
def template = engine.createTemplate '''
html {
body {
include template:'includes/hello.tpl'
}
}
- '''
- StringWriter rendered = new StringWriter()
- template.make().writeTo(rendered)
- assert rendered.toString() == '<html><body>Bonjour!</body></html>'
+ '''
+ String rendered = template.make()
+ assert rendered == '<html><body>Bonjour!</body></html>'
}
@Test
void testSimpleTemplateWithIncludeTemplateWithLocalePriority() {
- def tplConfig = new TemplateConfiguration()
- tplConfig.locale = Locale.FRANCE // set default locale
- MarkupTemplateEngine engine = new MarkupTemplateEngine(this.class.classLoader, tplConfig)
+ def config = new TemplateConfiguration(locale: Locale.FRANCE)
+ def engine = new MarkupTemplateEngine(config)
def template = engine.createTemplate '''
html {
body {
include template:'includes/hello_en_US.tpl' // if not found, will fall back to the default locale
}
}
- '''
- StringWriter rendered = new StringWriter()
- template.make().writeTo(rendered)
- assert rendered.toString() == '<html><body>Bonjour!</body></html>'
+ '''
+ String rendered = template.make()
+ assert rendered == '<html><body>Bonjour!</body></html>'
}
@Test
void testSimpleTemplateWithIncludeRaw() {
- MarkupTemplateEngine engine = new MarkupTemplateEngine(new TemplateConfiguration())
+ def engine = new MarkupTemplateEngine(new TemplateConfiguration())
def template = engine.createTemplate '''
html {
body {
include unescaped:'includes/hello.html'
}
}
- '''
+ '''
StringWriter rendered = new StringWriter()
template.make().writeTo(rendered)
assert rendered.toString() == '<html><body>Hello unescaped!</body></html>'
@@ -141,14 +146,14 @@ final class MarkupTemplateEngineTest {
@Test
void testSimpleTemplateWithIncludeEscaped() {
- MarkupTemplateEngine engine = new MarkupTemplateEngine(new TemplateConfiguration())
+ def engine = new MarkupTemplateEngine(new TemplateConfiguration())
def template = engine.createTemplate '''
html {
body {
include escaped:'includes/hello-escaped.txt'
}
}
- '''
+ '''
StringWriter rendered = new StringWriter()
template.make().writeTo(rendered)
assert rendered.toString() == '<html><body>Hello <escaped>!</body></html>'
@@ -156,13 +161,13 @@ final class MarkupTemplateEngineTest {
@Test
void testHTMLHeader() {
- MarkupTemplateEngine engine = new MarkupTemplateEngine(new TemplateConfiguration())
+ def engine = new MarkupTemplateEngine(new TemplateConfiguration())
def template = engine.createTemplate '''
yieldUnescaped '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
html {
body('Hello, XHTML!')
}
- '''
+ '''
StringWriter rendered = new StringWriter()
template.make().writeTo(rendered)
assert rendered.toString() == '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html><body>Hello, XHTML!</body></html>'
@@ -170,7 +175,7 @@ final class MarkupTemplateEngineTest {
@Test
void testTemplateWithHelperMethod() {
- MarkupTemplateEngine engine = new MarkupTemplateEngine(new TemplateConfiguration())
+ def engine = new MarkupTemplateEngine(new TemplateConfiguration())
def template = engine.createTemplate '''
def foo = {
body('Hello from foo!')
@@ -179,7 +184,7 @@ final class MarkupTemplateEngineTest {
html {
foo()
}
- '''
+ '''
StringWriter rendered = new StringWriter()
template.make().writeTo(rendered)
assert rendered.toString() == '<html><body>Hello from foo!</body></html>'
@@ -187,13 +192,13 @@ final class MarkupTemplateEngineTest {
@Test
void testCallPi() {
- MarkupTemplateEngine engine = new MarkupTemplateEngine(new TemplateConfiguration())
+ def engine = new MarkupTemplateEngine(new TemplateConfiguration())
def template = engine.createTemplate '''
pi("xml-stylesheet":[href:"mystyle.css", type:"text/css"])
html {
body('Hello, PI!')
}
- '''
+ '''
StringWriter rendered = new StringWriter()
template.make().writeTo(rendered)
assert rendered.toString().normalize() == '<?xml-stylesheet href=\'mystyle.css\' type=\'text/css\'?>\n<html><body>Hello, PI!</body></html>'
@@ -201,13 +206,13 @@ final class MarkupTemplateEngineTest {
@Test
void testXmlDeclaration() {
- MarkupTemplateEngine engine = new MarkupTemplateEngine(new TemplateConfiguration())
+ def engine = new MarkupTemplateEngine(new TemplateConfiguration())
def template = engine.createTemplate '''
xmlDeclaration()
html {
body('Hello, PI!')
}
- '''
+ '''
StringWriter rendered = new StringWriter()
template.make().writeTo(rendered)
assert rendered.toString().normalize() == '<?xml version=\'1.0\'?>\n<html><body>Hello, PI!</body></html>'
@@ -215,15 +220,14 @@ final class MarkupTemplateEngineTest {
@Test
void testXmlDeclarationWithEncoding() {
- def configuration = new TemplateConfiguration()
- configuration.declarationEncoding = 'UTF-8'
- MarkupTemplateEngine engine = new MarkupTemplateEngine(configuration)
+ def config = new TemplateConfiguration(declarationEncoding: 'UTF-8')
+ def engine = new MarkupTemplateEngine(config)
def template = engine.createTemplate '''
xmlDeclaration()
html {
body('Hello, PI!')
}
- '''
+ '''
StringWriter rendered = new StringWriter()
template.make().writeTo(rendered)
assert rendered.toString().normalize() == '<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n<html><body>Hello, PI!</body></html>'
@@ -231,14 +235,14 @@ final class MarkupTemplateEngineTest {
@Test
void testNewLine() {
- MarkupTemplateEngine engine = new MarkupTemplateEngine(new TemplateConfiguration())
+ def engine = new MarkupTemplateEngine(new TemplateConfiguration())
engine.templateConfiguration.newLineString = '||'
def template = engine.createTemplate '''
html {
newLine()
body('Hello, PI!')
}
- '''
+ '''
StringWriter rendered = new StringWriter()
template.make().writeTo(rendered)
assert rendered.toString() == '<html>||<body>Hello, PI!</body></html>'
@@ -246,10 +250,10 @@ final class MarkupTemplateEngineTest {
@Test
void testXMLWithYieldTag() {
- MarkupTemplateEngine engine = new MarkupTemplateEngine(new TemplateConfiguration())
+ def engine = new MarkupTemplateEngine(new TemplateConfiguration())
def template = engine.createTemplate '''
':yield'()
- '''
+ '''
StringWriter rendered = new StringWriter()
template.make().writeTo(rendered)
assert rendered.toString() == '<yield/>'
@@ -257,13 +261,13 @@ final class MarkupTemplateEngineTest {
@Test
void testTagsWithAttributes() {
- MarkupTemplateEngine engine = new MarkupTemplateEngine(new TemplateConfiguration())
+ def engine = new MarkupTemplateEngine(new TemplateConfiguration())
def template = engine.createTemplate '''
html {
a(href:'foo.html', 'Link text')
tagWithQuote(attr:"fo'o")
}
- '''
+ '''
StringWriter rendered = new StringWriter()
template.make().writeTo(rendered)
assert rendered.toString() == '<html><a href=\'foo.html\'>Link text</a><tagWithQuote attr=\'fo'o\'/></html>'
@@ -271,14 +275,14 @@ final class MarkupTemplateEngineTest {
@Test
void testTagsWithAttributesAndDoubleQuotes() {
- MarkupTemplateEngine engine = new MarkupTemplateEngine(new TemplateConfiguration())
+ def engine = new MarkupTemplateEngine(new TemplateConfiguration())
engine.templateConfiguration.useDoubleQuotes = true
def template = engine.createTemplate '''
html {
a(href:'foo.html', 'Link text')
tagWithQuote(attr:"fo'o")
}
- '''
+ '''
StringWriter rendered = new StringWriter()
template.make().writeTo(rendered)
assert rendered.toString() == '<html><a href="foo.html">Link text</a><tagWithQuote attr="fo\'o"/></html>'
@@ -286,7 +290,7 @@ final class MarkupTemplateEngineTest {
@Test
void testLoopInTemplate() {
- MarkupTemplateEngine engine = new MarkupTemplateEngine(new TemplateConfiguration())
+ def engine = new MarkupTemplateEngine(new TemplateConfiguration())
def model = [text: 'Hello', persons: ['Bob', 'Alice']]
def template = engine.createTemplate '''
html {
@@ -298,7 +302,7 @@ final class MarkupTemplateEngineTest {
}
}
}
- '''
+ '''
StringWriter rendered = new StringWriter()
template.make(model).writeTo(rendered)
assert rendered.toString() == '<html><body><ul><li>Hello Bob</li><li>Hello Alice</li></ul></body></html>'
@@ -306,7 +310,7 @@ final class MarkupTemplateEngineTest {
@Test
void testHelperFunctionInBinding() {
- MarkupTemplateEngine engine = new MarkupTemplateEngine(new TemplateConfiguration())
+ def engine = new MarkupTemplateEngine(new TemplateConfiguration())
def model = [text: { it.toUpperCase() }]
def template = engine.createTemplate '''
html {
@@ -314,7 +318,7 @@ final class MarkupTemplateEngineTest {
text('hello')
}
}
- '''
+ '''
StringWriter rendered = new StringWriter()
template.make(model).writeTo(rendered)
assert rendered.toString() == '<html><body>HELLO</body></html>'
@@ -322,13 +326,13 @@ final class MarkupTemplateEngineTest {
@Test
void testShouldNotEscapeUserInputAutomatically() {
- MarkupTemplateEngine engine = new MarkupTemplateEngine(new TemplateConfiguration())
+ def engine = new MarkupTemplateEngine(new TemplateConfiguration())
def model = [text: '<xml>']
def template = engine.createTemplate '''
html {
body(text)
}
- '''
+ '''
StringWriter rendered = new StringWriter()
template.make(model).writeTo(rendered)
assert rendered.toString() == '<html><body><xml></body></html>'
@@ -336,15 +340,14 @@ final class MarkupTemplateEngineTest {
@Test
void testShouldEscapeUserInputAutomatically() {
- TemplateConfiguration config = new TemplateConfiguration()
- config.autoEscape = true
- MarkupTemplateEngine engine = new MarkupTemplateEngine(this.class.classLoader, config)
+ def config = new TemplateConfiguration(autoEscape: true)
+ def engine = new MarkupTemplateEngine(this.class.classLoader, config)
def model = [text: '<xml>']
def template = engine.createTemplate '''
html {
body(text)
}
- '''
+ '''
StringWriter rendered = new StringWriter()
template.make(model).writeTo(rendered)
assert rendered.toString() == '<html><body><xml></body></html>'
@@ -352,15 +355,14 @@ final class MarkupTemplateEngineTest {
@Test
void testShouldNotEscapeUserInputAutomaticallyEvenIfFlagSet() {
- TemplateConfiguration config = new TemplateConfiguration()
- config.autoEscape = true
- MarkupTemplateEngine engine = new MarkupTemplateEngine(this.class.classLoader, config)
+ def config = new TemplateConfiguration(autoEscape: true)
+ def engine = new MarkupTemplateEngine(this.class.classLoader, config)
def model = [text: '<xml>']
def template = engine.createTemplate '''
html {
body(unescaped.text)
}
- '''
+ '''
StringWriter rendered = new StringWriter()
template.make(model).writeTo(rendered)
assert rendered.toString() == '<html><body><xml></body></html>'
@@ -368,12 +370,12 @@ final class MarkupTemplateEngineTest {
@Test
void testTypeCheckedModel() {
- MarkupTemplateEngine engine = new MarkupTemplateEngine(new TemplateConfiguration())
+ def engine = new MarkupTemplateEngine(new TemplateConfiguration())
def template = engine.createTypeCheckedModelTemplate '''
html {
body(text.toUpperCase())
}
- ''', [text: 'String']
+ ''', [text: 'String']
def model = [text: 'Type checked!']
StringWriter rendered = new StringWriter()
template.make(model).writeTo(rendered)
@@ -383,12 +385,12 @@ final class MarkupTemplateEngineTest {
@Test
void testTypeCheckedModelShouldFail() {
def err = shouldFail {
- MarkupTemplateEngine engine = new MarkupTemplateEngine(new TemplateConfiguration())
+ def engine = new MarkupTemplateEngine(new TemplateConfiguration())
def template = engine.createTypeCheckedModelTemplate '''
html {
body(text.toUpperCase())
}
- ''', [text: 'Integer']
+ ''', [text: 'Integer']
def model = [text: 'Type checked!']
StringWriter rendered = new StringWriter()
template.make(model).writeTo(rendered)
@@ -401,28 +403,27 @@ final class MarkupTemplateEngineTest {
@Test
void testTypeCheckedModelShouldFailWithoutModelDescription() {
def err = shouldFail {
- MarkupTemplateEngine engine = new MarkupTemplateEngine(new TemplateConfiguration())
+ def engine = new MarkupTemplateEngine(new TemplateConfiguration())
def template = engine.createTypeCheckedModelTemplate '''
html {
body(p.name.toUpperCase())
}
- ''', [:]
+ ''', [:]
def model = [p: new Person(name: 'Cédric')]
StringWriter rendered = new StringWriter()
template.make(model).writeTo(rendered)
}
-
assert err =~ /No such property: name/
}
@Test
void testTypeCheckedModelShouldSucceedWithModelDescription() {
- MarkupTemplateEngine engine = new MarkupTemplateEngine(new TemplateConfiguration())
+ def engine = new MarkupTemplateEngine(new TemplateConfiguration())
def template = engine.createTypeCheckedModelTemplate '''
html {
body(p.name.toUpperCase())
}
- ''', [p: 'groovy.text.MarkupTemplateEngineTest.Person']
+ ''', [p: 'groovy.text.MarkupTemplateEngineTest.Person']
def model = [p: new Person(name: 'Cedric')]
StringWriter rendered = new StringWriter()
template.make(model).writeTo(rendered)
@@ -431,7 +432,7 @@ final class MarkupTemplateEngineTest {
@Test
void testTypeCheckedModelShouldSucceedWithModelDescriptionUsingGenerics() {
- MarkupTemplateEngine engine = new MarkupTemplateEngine(new TemplateConfiguration())
+ def engine = new MarkupTemplateEngine(new TemplateConfiguration())
def template = engine.createTypeCheckedModelTemplate '''
html {
ul {
@@ -440,7 +441,7 @@ final class MarkupTemplateEngineTest {
}
}
}
- ''', [persons: 'List<groovy.text.MarkupTemplateEngineTest.Person>']
+ ''', [persons: 'List<groovy.text.MarkupTemplateEngineTest.Person>']
def model = [persons: [new Person(name: 'Cedric')]]
StringWriter rendered = new StringWriter()
template.make(model).writeTo(rendered)
@@ -450,28 +451,25 @@ final class MarkupTemplateEngineTest {
@Test
void testTypeCheckedTemplateShouldFailInInclude() {
def err = shouldFail {
- MarkupTemplateEngine engine = new MarkupTemplateEngine(new TemplateConfiguration())
+ def engine = new MarkupTemplateEngine(new TemplateConfiguration())
def template = engine.createTypeCheckedModelTemplate '''
html {
body {
include template:'includes/typecheckedinclude.tpl'
}
}
- ''', [text: 'Integer']
+ ''', [text: 'Integer']
def model = [text: 'Type checked!']
StringWriter rendered = new StringWriter()
template.make(model).writeTo(rendered)
}
-
assert err =~ /Cannot find matching method java.lang.Integer#toUpperCase\(\)/
}
@Test
void testSimpleAutoIndent() {
- TemplateConfiguration config = new TemplateConfiguration()
- config.autoIndent = true
- config.newLineString = '\n'
- MarkupTemplateEngine engine = new MarkupTemplateEngine(this.class.classLoader, config)
+ def config = new TemplateConfiguration(autoIndent: true, newLineString: '\n')
+ def engine = new MarkupTemplateEngine(this.class.classLoader, config)
def template = engine.createTemplate '''
html {
newLine()
@@ -495,10 +493,8 @@ html {
@Test
void testSimpleAutoIndentShouldAddNewLineInLoop() {
- TemplateConfiguration config = new TemplateConfiguration()
- config.autoIndent = true
- config.newLineString = '\n'
- MarkupTemplateEngine engine = new MarkupTemplateEngine(this.class.classLoader, config)
+ def config = new TemplateConfiguration(autoIndent: true, newLineString: '\n')
+ def engine = new MarkupTemplateEngine(this.class.classLoader, config)
def template = engine.createTemplate '''
html {
newLine()
@@ -533,11 +529,8 @@ html {
@Test
void testSimpleAutoIndentShouldAutoAddNewLineInLoop() {
- TemplateConfiguration config = new TemplateConfiguration()
- config.autoIndent = true
- config.autoNewLine = true
- config.newLineString = '\n'
- MarkupTemplateEngine engine = new MarkupTemplateEngine(this.class.classLoader, config)
+ def config = new TemplateConfiguration(autoIndent: true, autoNewLine: true, newLineString: '\n')
+ def engine = new MarkupTemplateEngine(this.class.classLoader, config)
def template = engine.createTemplate '''
html {
body {
@@ -567,11 +560,8 @@ html {
@Test
void testSimpleAutoIndentWithAutoNewLine() {
- TemplateConfiguration config = new TemplateConfiguration()
- config.autoIndent = true
- config.autoNewLine = true
- config.newLineString = '\n'
- MarkupTemplateEngine engine = new MarkupTemplateEngine(this.class.classLoader, config)
+ def config = new TemplateConfiguration(autoIndent: true, autoNewLine: true, newLineString: '\n')
+ def engine = new MarkupTemplateEngine(this.class.classLoader, config)
def template = engine.createTemplate '''
html {
body {
@@ -608,7 +598,7 @@ html {
MarkupTemplateEngine engine = new MarkupTemplateEngine(this.class.classLoader, config)
def template = engine.createTemplate '''int x = name.length()
yield "$name: $x"
- '''
+ '''
StringWriter rendered = new StringWriter()
def model = [name: 'Michel']
def tpl = template.make(model)
@@ -670,7 +660,7 @@ html {
include template:'hello-from-dir.tpl'
}
}
- '''
+ '''
StringWriter rendered = new StringWriter()
template.make().writeTo(rendered)
assert rendered.toString() == '<html><body>Hello from include!</body></html>'
@@ -707,16 +697,13 @@ html {
@Test
void testTypeCheckedModelShouldNotConflictWithAutoEscape() {
def model = [title: "This is my glorious title ${1 + 1}".toString()]
- def template = new MarkupTemplateEngine(
- getClass().getClassLoader(),
- new TemplateConfiguration(autoNewLine: true, autoEscape: true, newLineString: 'NL')).createTypeCheckedModelTemplate('''
- body {
- div(class: 'text') {
- yield title.toUpperCase()
- }
- }
-
- ''', [title: 'String'])
+ def template = new MarkupTemplateEngine(this.class.classLoader, new TemplateConfiguration(autoNewLine: true, autoEscape: true, newLineString: 'NL')).createTypeCheckedModelTemplate('''
+ body {
+ div(class: 'text') {
+ yield title.toUpperCase()
+ }
+ }
+ ''', [title: 'String'])
def stringWriter = new StringWriter()
template.make(model).writeTo(stringWriter)
assert stringWriter.toString() == '<body>NL<div class=\'text\'>NLTHIS IS MY GLORIOUS TITLE 2NL</div>NL</body>'
@@ -756,7 +743,7 @@ html {
}
}
}
- '''
+ '''
StringWriter rendered = new StringWriter()
def model = [persons: [[name: 'Cedric'], [name: 'Jochen']]]
template.make(model).writeTo(rendered)
@@ -780,7 +767,7 @@ html {
}
}
}
- '''
+ '''
StringWriter rendered = new StringWriter()
def model = [persons: [[name: 'Cedric'], [name: 'Jochen']]]
template.make(model).writeTo(rendered)
@@ -805,13 +792,12 @@ html {
}
}
}
- '''
+ '''
StringWriter rendered = new StringWriter()
def model = [persons: [[name: 'Cedric'], [name: 'Jochen']]]
template.make(model).writeTo(rendered)
assert rendered.toString() == '<html><body><ul><li>Cedric</li><li>Jochen</li></ul></body></html>'
}
-
assert err =~ /No such property: name for class: java.lang.String/
}
@@ -829,7 +815,7 @@ html {
}
}
}
- '''
+ '''
StringWriter rendered = new StringWriter()
template.make().writeTo(rendered)
assert rendered.toString() == "<html><body><a href='index.html'>Index</a><a href='page.html'>Page1</a><a href='page2.html'>Page2</a></body></html>"
@@ -845,7 +831,7 @@ html {
p('This is the body')
}
}, title: 'This is the title'
- '''
+ '''
StringWriter rendered = new StringWriter()
template.make().writeTo(rendered)
assert rendered.toString() == "<html><head><title>This is the title</title></head><body><div><p>This is the body</p></div></body></html>"
@@ -861,7 +847,7 @@ html {
p('This is the body')
}
}
- '''
+ '''
StringWriter rendered = new StringWriter()
template.make([title:'This is the title']).writeTo(rendered)
assert rendered.toString() == "<html><head><title/></head><body><div><p>This is the body</p></div></body></html>"
@@ -872,7 +858,7 @@ html {
p('This is the body')
}
}
- '''
+ '''
rendered = new StringWriter()
template.make([title:'This is the title']).writeTo(rendered)
assert rendered.toString() == "<html><head><title>This is the title</title></head><body><div><p>This is the body</p></div></body></html>"
@@ -883,7 +869,7 @@ html {
p('This is the body')
}
}, title: 'This is another title'
- '''
+ '''
rendered = new StringWriter()
template.make([title:'This is the title']).writeTo(rendered)
assert rendered.toString() == "<html><head><title>This is another title</title></head><body><div><p>This is the body</p></div></body></html>"
@@ -907,7 +893,7 @@ html {
def template = engine.createTemplate '''messages.each { message ->
yield message.summary
}
- '''
+ '''
StringWriter rendered = new StringWriter()
def model = [messages: [new Message(summary: 'summary')]]
template.make(model).writeTo(rendered)
@@ -940,7 +926,7 @@ html {
include template:'includes/hello.tpl'
}
}
- '''
+ '''
StringWriter rendered = new StringWriter()
template.make().writeTo(rendered)
assert rendered.toString() == '<html><body>Hello from include!Hello from include!Hello from include!</body></html>'
@@ -960,7 +946,7 @@ html {
p("This is a p with ${false?$a(href:'link.html','link'):x}")
}
}
- '''
+ '''
String rendered = template.make()
assert rendered == '<html><body><p>This is a p with <a href=\'link.html\'>link</a></p><p>This is a p with directly <p>hg</p></p></body></html>'
}
@@ -977,7 +963,7 @@ html {
p("This is a p with ${stringOf { false?a(href:'link.html','link'):x} }")
}
}
- '''
+ '''
String rendered = template.make()
assert rendered == '<html><body><p>This is a p with <a href=\'link.html\'>link</a></p><p>This is a p with directly <p>hg</p></p></body></html>'
}
@@ -987,7 +973,7 @@ html {
MarkupTemplateEngine engine = new MarkupTemplateEngine(new TemplateConfiguration())
def template = engine.createTemplate '''
p("This is an ${strong('error')}")
- '''
+ '''
String rendered = template.make().writeTo(new StringWriter())
assert rendered == '<strong>error</strong><p>This is an </p>'
}
@@ -1010,12 +996,12 @@ html {
assert rendered == '<a>link</a>'
}
- @Test @NotYetImplemented // GROOVY-6939
+ @Test // GROOVY-6939
void testShouldNotFailWithDoCallMethod() {
MarkupTemplateEngine engine = new MarkupTemplateEngine(new TemplateConfiguration())
def template = engine.createTemplate '''
groups.each { k, v -> li(k) }
- '''
+ '''
def model = [groups:[a:'Group a',b:'Group b']]
String rendered = template.make(model)
assert rendered == '<li>a</li><li>b</li>'
@@ -1026,7 +1012,7 @@ html {
MarkupTemplateEngine engine = new MarkupTemplateEngine(new TemplateConfiguration())
def template = engine.createTemplate '''
yield list[0]
- '''
+ '''
def model = [list:['Item 1']]
String rendered = template.make(model)
assert rendered == 'Item 1'
@@ -1034,7 +1020,7 @@ html {
template = engine.createTemplate '''
list[0] = 'Item 2'
yield list[0]
- '''
+ '''
model = [list:['Item 1']]
rendered = template.make(model)
assert model.list[0] == 'Item 2'
@@ -1044,7 +1030,7 @@ html {
def indirect = list
indirect[0] = 'Item 4'
yield list[0]
- '''
+ '''
model = [list:['Item 3']]
rendered = template.make(model)
assert model.list[0] == 'Item 4'
@@ -1058,12 +1044,14 @@ html {
div {
yield xml.file.name
}
- '''
+ '''
def model = [xml: [file:[name:'test']]]
String rendered = template.make(model)
assert rendered == '<div>test</div>'
}
+ //--------------------------------------------------------------------------
+
static class SimpleTagLib {
def emoticon = { attrs, body ->
out << body() << (attrs.happy == 'true' ? " :-)" : " :-(")