All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.wycst.wast.common.template.StringTemplate Maven / Gradle / Ivy

Go to download

Wast is a high-performance Java toolset library package that includes JSON, YAML, CSV, HttpClient, JDBC and EL engines

The newest version!
package io.github.wycst.wast.common.template;

import io.github.wycst.wast.common.compiler.JDKCompiler;
import io.github.wycst.wast.common.compiler.JavaSourceObject;
import io.github.wycst.wast.common.exceptions.ParserException;
import io.github.wycst.wast.common.utils.RegexUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 字符串模板引擎
 * 参考jsp模板引擎
 * 

* <% java代码;%> * <%=java表达式%> * ${k} el表达式解析 * *

当前仅仅内部orm进行代码自动生成模块使用,性能未做优化 * * @Author: wangy * @Date: 2021/9/7 21:53 * @Description: */ public final class StringTemplate { private static final AtomicLong ATOMIC_LONG = new AtomicLong(0); private static final String PACKAGE_NAME = StringTemplate.class.getPackage().getName(); private static final String DEFINE_PREFIX = "$define "; private static final String FOR_LOOP_REGEX = "^[$]for[ ]*[(][ ]*(\\w+)(,[ ]*(\\w+))?[ ]*[)][ ]*in[ ]*(\\w+)$"; private static final String IF_REGEX = "^[$]if[ ]*[(](.*?)[)]$"; private static final String ELSE_IF_REGEX = "^[$]else[ ]+if[ ]*[(](.*?)[)]$"; private static final String ELSE_REGEX = "^[$]else$"; private static final String PLACEHOLDER_REGEX = ".*[^\\\\]?[$][{]([ ]*[0-9a-zA-Z_.$]+[ ]*)[}].*"; private static final Pattern FOR_LOOP_PATTERN = Pattern.compile(FOR_LOOP_REGEX); private static final Pattern IF_PATTERN = Pattern.compile(IF_REGEX); private static final Pattern ELSE_IF_PATTERN = Pattern.compile(ELSE_IF_REGEX); private static final Pattern ELSE_PATTERN = Pattern.compile(ELSE_REGEX); private final String templateId = UUID.randomUUID().toString(); private final byte[] templateJavaSource; private TemplateClass templateClass; // 静态字符串临时缓冲区(优化) private StringBuilder staticTempBuffer = new StringBuilder(); private boolean nextNewLine = false; public StringTemplate(String template) { StringBuilder importPackages = new StringBuilder(); importPackages.append("package " + PACKAGE_NAME + ";\r\n"); importPackages.append("import java.util.*;\r\n"); // importPackages.append("import io.github.wycst.wast.common.template.TemplateClass;\r\n"); // importPackages.append("import io.github.wycst.wast.common.utils.*;\r\n"); // 源代码 StringBuilder source = new StringBuilder(); String[] lines = template.split("\r\n", -1); boolean javaCodeBegin = false; boolean userLocalContext = false; boolean defineLocalContext = false; int scopeLevel = 0; int flag = 0; int index = 0, lineLength = lines.length; for (String line : lines) { index++; if (nextNewLine) { nextNewLine = false; // 如果以占位符号结尾,追加换行符 appendIndent(source, scopeLevel); source.append("\t\tprintln();\r\n"); } else { if (line.length() == 0) { boolean hasAppendNewLine = staticTempBuffer.length() > 0; this.persistBuffer(source); // 如果最后一个line为空忽略掉(分割虚拟出的一行) if (index < lineLength || !hasAppendNewLine) { appendIndent(source, scopeLevel); source.append("\t\tprintln();\r\n"); } continue; } } // 转义符替换 line = line.replace("\\", "\\\\"); line = line.replace("\r", "\\r"); line = line.replace("\n", "\\n"); String trimLine = line.trim(); if (trimLine.startsWith("<%")) { if (trimLine.endsWith("%>")) { // java代码同一行 String lineCode = trimLine.substring(2, trimLine.length() - 2); if (lineCode.startsWith("=")) { // 解析表达式直接作为java代码执行 source.append("\t\tprintln(\"" + lineCode.substring(1).replace("\"", "\\\"") + "\");\r\n"); } else { if (lineCode.startsWith("import ") && lineCode.endsWith(";")) { importPackages.append(lineCode).append("\r\n"); } else { // java代码 source.append("\t\t").append(lineCode).append("\r\n"); } } } else { // java代码开始 javaCodeBegin = true; } } else if (trimLine.endsWith("%>")) { // java代码结束 javaCodeBegin = false; } else { if (javaCodeBegin) { // import语句 if (trimLine.startsWith("import ") && trimLine.endsWith(";")) { importPackages.append(line); } else { source.append("\t\t").append(line).append("\r\n"); } } else { boolean isTextLine = true; if (trimLine.startsWith("$")) { isTextLine = false; if (trimLine.matches(FOR_LOOP_REGEX)) { persistBuffer(source); if (!defineLocalContext) { appendDefineSource(source, line, "Map localContext = null;"); appendDefineSource(source, line, "Iterable iterable = null;"); appendDefineSource(source, line, "int index = 0;"); defineLocalContext = true; } userLocalContext = appendForLoopSource(source, line, trimLine); flag += 1; scopeLevel++; } else if (trimLine.startsWith(DEFINE_PREFIX)) { this.persistBuffer(source); appendIndent(source, scopeLevel); appendDefineSource(source, line, trimLine); } else if (trimLine.matches(IF_REGEX)) { this.persistBuffer(source); appendIndent(source, scopeLevel); appendIfClauseSource(source, line, trimLine); flag += 1; scopeLevel++; } else if (trimLine.matches(ELSE_IF_REGEX)) { this.persistBuffer(source); appendElseIfClauseSource(source, line, trimLine); flag += 1; } else if (trimLine.matches(ELSE_REGEX)) { this.persistBuffer(source); appendElseClauseSource(source, line, trimLine); flag += 1; } else if (trimLine.equals("$end")) { this.persistBuffer(source); if (scopeLevel > 0) { scopeLevel--; } appendIndent(source, scopeLevel); source.append("\t\t}\r\n"); flag -= 1; } else { // println isTextLine = true; } if (flag == 0) { userLocalContext = false; if (scopeLevel > 0) { scopeLevel--; } } } if (isTextLine) { if (line.matches(PLACEHOLDER_REGEX)) { this.persistBuffer(source); line = line.replace("\"", "\\\""); appendIndent(source, scopeLevel); appendPlaceHolderLine(source, line, userLocalContext ? "localContext" : "context", scopeLevel); // if (userLocalContext) { // source.append("\t\tprintln(\"" + line + "\", localContext);\r\n"); // } else { // source.append("\t\tprintln(\"" + line + "\", context);\r\n"); // } } else { // appendIndent(source, scopeLevel); if (staticTempBuffer.length() == 0) { appendIndent(source, scopeLevel); } // 追加静态缓冲区 this.appendBuffer(line.replace("\"", "\\\"")); // source.append("\t\tprintln(\"" + line.replace("\"", "\\\"") + "\");\r\n"); } } } } } this.persistBuffer(source, false); String simpleClassName = genTemplateClassName(); final String javaCode = getJavaCode(simpleClassName, importPackages, source); this.templateJavaSource = javaCode.getBytes(); try { templateClass = (TemplateClass) JDKCompiler.compileJavaSource(new JavaSourceObject(PACKAGE_NAME, simpleClassName, javaCode)).newInstance(); } catch (Throwable e) { throw new ParserException(" parse exception :" + e.getMessage(), e); } } private void appendIndent(StringBuilder source, int scopeLevel) { while (scopeLevel-- > 0) { source.append("\t"); } } private String genTemplateClassName() { return String.format("StringTemplate$_%d", ATOMIC_LONG.getAndIncrement()); } private String getJavaCode(String className, StringBuilder importPackages, StringBuilder source) { StringBuilder javaCodeBuffer = new StringBuilder(); javaCodeBuffer.append(importPackages); javaCodeBuffer.append("public class ").append(className).append(" extends TemplateClass {\r\n"); javaCodeBuffer.append("\r\n"); javaCodeBuffer.append("\tprotected void renderTemplate(final Map context) {\r\n").append(source).append("\r\n").append("\t}\r\n"); javaCodeBuffer.append("}"); return javaCodeBuffer.toString(); } private void appendDefineSource(StringBuilder source, String line, String trimLine) { this.persistBuffer(source); if (trimLine.startsWith(DEFINE_PREFIX)) { source.append("\t\t").append(trimLine.substring(DEFINE_PREFIX.length())); } else { source.append("\t\t").append(trimLine); } if (!trimLine.endsWith(";")) { source.append(";"); } source.append("\r\n"); } /** * 将line追加到缓冲区 */ private void appendBuffer(String line) { // println函数会自动追加换行, 临时静态字符串追加行文本(占一行)的最后一行不需要换行标识 if (staticTempBuffer.length() > 0) { staticTempBuffer.append("\\r\\n"); } staticTempBuffer.append(line); } /** * 解析上下文,模板核心代码 */ private void appendPlaceHolderLine(StringBuilder source, String line, String contextName, int scopeLevel) { // 暂时只支持解析表达式,可拓展支持函数调用,后续支持 String reg = "[$][{]([ ]*[0-9a-zA-Z_.$]+[ ]*)[}]"; Pattern pattern = RegexUtils.getPattern(reg); Matcher matcher = pattern.matcher(line); int beginIndex = 0; StringBuilder buffer = staticTempBuffer; boolean hasNewlineFlag = false; while (matcher.find()) { // 判断group开始是否为转义标识符\\,如果是就原group跳过 int newBeginIndex = matcher.start(0); if (newBeginIndex > 1 && line.charAt(newBeginIndex - 1) == '\\') { if (!hasNewlineFlag) { if (buffer.length() > 0) { buffer.append("\\r\\n"); hasNewlineFlag = true; } } buffer.append(line, beginIndex, newBeginIndex - 2); buffer.append(matcher.group(0)); } else { String key = matcher.group(1).trim(); buffer.append(line, beginIndex, newBeginIndex); // 这里需要动态append值,先持久化 persistBuffer(source, false); appendIndent(source, scopeLevel); // 占位符实际替换代码 source.append("\t\tprint(getContextValue(" + contextName + ", \"" + key + "\", \"\"));\r\n"); appendIndent(source, scopeLevel); } beginIndex = matcher.end(0); } if (beginIndex < line.length()) { // appendIndent(source, scopeLevel); buffer.append(line, beginIndex, line.length()); } else { nextNewLine = true; } } /** * 缓冲区持久化到source */ private void persistBuffer(StringBuilder source) { persistBuffer(source, true); } /** * 缓冲区持久化到source */ private void persistBuffer(StringBuilder source, int level) { this.appendIndent(source, level); persistBuffer(source, true); } /** * 缓冲区持久化到source */ private void persistBuffer(StringBuilder source, boolean newLine) { if (staticTempBuffer.length() > 0) { if (newLine) { // 临时静态字符串持久化到source source.append("\t\tprintln(\"").append(staticTempBuffer).append("\");\r\n"); } else { source.append("\t\tprint(\"").append(staticTempBuffer).append("\");\r\n"); } // 重置 staticTempBuffer.setLength(0); } } private boolean appendForLoopSource(StringBuilder source, String line, String trimLine) { Matcher matcher = FOR_LOOP_PATTERN.matcher(trimLine); boolean matched = false; if (matcher.find()) { matched = true; String itemKey = matcher.group(1); String indexKey = matcher.group(3); String itemsKey = matcher.group(4); source.append("\t\titerable = getContextIterable(context, \"" + itemsKey + "\");\r\n"); source.append("\t\tlocalContext = new HashMap(context);\r\n"); if (indexKey != null) { source.append("\t\tindex = 0;\r\n"); } source.append("\t\tfor (Object " + itemKey + " : iterable) {\r\n"); source.append("\t\t\tlocalContext.put(\"" + itemKey + "\", " + itemKey + ");\r\n"); if (indexKey != null) { source.append("\t\t\tlocalContext.put(\"" + indexKey + "\", index++);\r\n"); } } else { // 理论上代码不可达,暂不处理 } return matched; } private boolean appendIfClauseSource(StringBuilder source, String line, String trimLine) { Matcher matcher = IF_PATTERN.matcher(trimLine); boolean matched = false; if (matcher.find()) { matched = true; String condition = matcher.group(1); source.append("\t\tif(").append(condition).append("){\r\n"); } else { // 理论上代码不可达,暂不处理 } return matched; } private boolean appendElseIfClauseSource(StringBuilder source, String line, String trimLine) { Matcher matcher = ELSE_IF_PATTERN.matcher(trimLine); boolean matched = false; if (matcher.find()) { matched = true; String condition = matcher.group(1); source.append("\t\t} else if(").append(condition).append("){\r\n\t"); } else { // 理论上代码不可达,暂不处理 } return matched; } private void appendElseClauseSource(StringBuilder source, String line, String trimLine) { source.append("\t\t} else {\r\n\t"); } private Map context = new HashMap(); public void binding(String key, Object value) { context.put(key, value); } public void binding(Map data) { context.putAll(data); } public void clearBinging() { context.clear(); } public void clearBinging(String key) { context.remove(key); } /*** * 生成模板串 * * @return */ public synchronized String render() { return render(context); } /** * 以指定上下文渲染数据模板 * * @param context * @return */ public synchronized String render(Map context) { return templateClass.render(context); } /*** * 返回实例id * * @return */ public final String getId() { return templateId; } /** * 获取模板源代码 */ byte[] getTemplateJavaSource() { return templateJavaSource; } }