hutool-extra模板引擎增加pebble引擎支持。Pebble是一款受Twig启发的Java模板引擎。它具有模板继承和易于阅读的语法,内置有安全的autoescaping,并包括对国际化的综合支持。

This commit is contained in:
yudongbin 2023-03-31 11:10:33 +08:00
parent c9024ba8f9
commit 2674e5b13e
6 changed files with 219 additions and 0 deletions

View File

@ -151,6 +151,12 @@
<version>2.1.10</version> <version>2.1.10</version>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>io.pebbletemplates</groupId>
<artifactId>pebble</artifactId>
<version>3.2.0</version>
<optional>true</optional>
</dependency>
<!-- 邮件 --> <!-- 邮件 -->
<dependency> <dependency>

View File

@ -0,0 +1,65 @@
package cn.hutool.extra.template.engine.pebble;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.reflect.TypeReference;
import cn.hutool.extra.template.Template;
import cn.hutool.extra.template.TemplateException;
import cn.hutool.extra.template.engine.velocity.VelocityTemplate;
import java.io.*;
import java.util.Map;
/**
* @authorzooooooooy
*/
public class PebbleTemplate implements Template {
private final io.pebbletemplates.pebble.template.PebbleTemplate template;
public PebbleTemplate(io.pebbletemplates.pebble.template.PebbleTemplate template) {
this.template = template;
}
/**
* 包装pebbleTemplate模板
* @param template
* @return
*/
public static PebbleTemplate wrap(final io.pebbletemplates.pebble.template.PebbleTemplate template) {
return (null == template) ? null : new PebbleTemplate(template);
}
/**
* 渲染对象
* @param bindingMap 绑定的参数此Map中的参数会替换模板中的变量
* @param writer 输出
*/
@Override
public void render(Map<?, ?> bindingMap, Writer writer) {
final Map<String, Object> map = Convert.convert(new TypeReference<Map<String, Object>>() {}, bindingMap);
try {
this.template.evaluate(writer, map);
} catch (Exception e) {
throw new TemplateException("pebble template parse failed, cause by: ", e);
}
}
/**
* 渲染对象
* @param bindingMap 绑定的参数此Map中的参数会替换模板中的变量
* @param out 输出
*/
@Override
public void render(Map<?, ?> bindingMap, OutputStream out) {
final Map<String, Object> map = Convert.convert(new TypeReference<Map<String, Object>>() {}, bindingMap);
try {
this.template.evaluate(new OutputStreamWriter(out), map);
} catch (Exception e) {
throw new TemplateException("pebble template parse failed, cause by: ", e);
}
}
}

View File

@ -0,0 +1,122 @@
package cn.hutool.extra.template.engine.pebble;
import cn.hutool.core.io.file.FileUtil;
import cn.hutool.core.text.StrUtil;
import cn.hutool.extra.template.Template;
import cn.hutool.extra.template.TemplateConfig;
import cn.hutool.extra.template.TemplateEngine;
import cn.hutool.extra.template.TemplateException;
import io.pebbletemplates.pebble.PebbleEngine;
import io.pebbletemplates.pebble.error.PebbleException;
import io.pebbletemplates.pebble.loader.ClasspathLoader;
import io.pebbletemplates.pebble.loader.FileLoader;
import io.pebbletemplates.pebble.loader.StringLoader;
import org.beetl.core.GroupTemplate;
/**
* @authorzooooooooy
*/
public class PebbleTemplateEngine implements TemplateEngine {
private PebbleEngine engine;
public PebbleTemplateEngine() {
}
public PebbleTemplateEngine(TemplateConfig config) {
init(config);
}
@Override
public TemplateEngine init(TemplateConfig config) {
init(createEngine(config));
return this;
}
/**
* 初始化引擎
* @param engine 引擎
*/
private void init(PebbleEngine engine){
this.engine = engine;
}
/**
* 创建引擎
*
* @param config 模板配置
* @return {@link GroupTemplate}
*/
private static PebbleEngine createEngine(TemplateConfig config) {
if (null == config) {
config = TemplateConfig.DEFAULT;
}
PebbleEngine pebbleEngine;
switch (config.getResourceMode()) {
case CLASSPATH:
ClasspathLoader classpathLoader = new ClasspathLoader();
classpathLoader.setPrefix(StrUtil.addSuffixIfNot(config.getPath(), "/"));
pebbleEngine = new PebbleEngine.Builder()
.loader(classpathLoader)
.autoEscaping(false)
.build();
break;
case FILE:
FileLoader fileLoader = new FileLoader();
fileLoader.setPrefix(StrUtil.addSuffixIfNot(config.getPath(), "/"));
pebbleEngine = new PebbleEngine.Builder()
.loader(fileLoader)
.autoEscaping(false)
.build();
break;
case WEB_ROOT:
fileLoader = new FileLoader();
fileLoader.setPrefix(StrUtil.addSuffixIfNot(FileUtil.getAbsolutePath(FileUtil.file(FileUtil.getWebRoot(), config.getPath())), "/"));
pebbleEngine = new PebbleEngine.Builder()
.loader(fileLoader)
.autoEscaping(false)
.build();
break;
case STRING:
StringLoader stringLoader = new StringLoader();
stringLoader.setPrefix(StrUtil.addSuffixIfNot(config.getPath(), "/"));
pebbleEngine = new PebbleEngine.Builder()
.loader(stringLoader)
.autoEscaping(false)
.build();
break;
default:
classpathLoader = new ClasspathLoader();
classpathLoader.setPrefix(StrUtil.addSuffixIfNot(config.getPath(), "/"));
pebbleEngine = new PebbleEngine.Builder()
.loader(classpathLoader)
.autoEscaping(false)
.build();
break;
}
return pebbleEngine;
}
/**
* 通过路径获取对应模板操作类
* @param resource 资源根据实现不同此资源可以是模板本身也可以是模板的相对路径
* @return
*/
@Override
public Template getTemplate(String resource) {
if (null == this.engine) {
init(TemplateConfig.DEFAULT);
}
return PebbleTemplate.wrap(engine.getTemplate(resource));
}
@Override
public Object getRawEngine() {
return this.engine;
}
}

View File

@ -0,0 +1,6 @@
/**
* pebble template实现模板引擎介绍见https://github.com/PebbleTemplates/pebble
*
* @author zooooooooy
*/
package cn.hutool.extra.template.engine.pebble;

View File

@ -6,6 +6,7 @@ import cn.hutool.extra.template.TemplateConfig.ResourceMode;
import cn.hutool.extra.template.engine.beetl.BeetlEngine; import cn.hutool.extra.template.engine.beetl.BeetlEngine;
import cn.hutool.extra.template.engine.enjoy.EnjoyEngine; import cn.hutool.extra.template.engine.enjoy.EnjoyEngine;
import cn.hutool.extra.template.engine.freemarker.FreemarkerEngine; import cn.hutool.extra.template.engine.freemarker.FreemarkerEngine;
import cn.hutool.extra.template.engine.pebble.PebbleTemplateEngine;
import cn.hutool.extra.template.engine.rythm.RythmEngine; import cn.hutool.extra.template.engine.rythm.RythmEngine;
import cn.hutool.extra.template.engine.thymeleaf.ThymeleafEngine; import cn.hutool.extra.template.engine.thymeleaf.ThymeleafEngine;
import cn.hutool.extra.template.engine.velocity.VelocityEngine; import cn.hutool.extra.template.engine.velocity.VelocityEngine;
@ -143,6 +144,24 @@ public class TemplateUtilTest {
Assert.assertEquals("<h3>Hutool</h3>", result); Assert.assertEquals("<h3>Hutool</h3>", result);
} }
/**
* pebble template engine test
*/
@Test
public void pebbleEngineTest() {
// 字符串模板
TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("templates").setCustomEngine(PebbleTemplateEngine.class));
Template template = engine.getTemplate("<h3>{{ message }}</h3>");
String result = template.render(Dict.of().set("message", "Hutool"));
Assert.assertEquals("<h3>Hutool</h3>", result);
//ClassPath模板
engine = TemplateUtil.createEngine(new TemplateConfig("templates", ResourceMode.CLASSPATH).setCustomEngine(PebbleTemplateEngine.class));
template = engine.getTemplate("pebble_test.peb");
result = template.render(Dict.of().set("name", "Hutool"));
Assert.assertEquals("hello, Hutool", result);
}
@Test @Test
@Ignore @Ignore
public void renderToFileTest() { public void renderToFileTest() {

View File

@ -0,0 +1 @@
hello, {{name}}