com.greenlaw110.rythm.internal.parser.build_in.InvokeTemplateParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rythm-engine Show documentation
Show all versions of rythm-engine Show documentation
A strong typed high performance Java Template engine with .Net Razor like syntax
The newest version!
/*
* Copyright (C) 2013 The Rythm Engine project
* Gelin Luo
*
* 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 com.greenlaw110.rythm.internal.parser.build_in;
import com.greenlaw110.rythm.utils.Escape;
import com.greenlaw110.rythm.internal.*;
import com.greenlaw110.rythm.internal.parser.CodeToken;
import com.greenlaw110.rythm.internal.parser.ParserBase;
import com.greenlaw110.rythm.internal.parser.Patterns;
import com.greenlaw110.rythm.utils.S;
import com.greenlaw110.rythm.utils.TextBuilder;
import com.stevesoft.pat.Regex;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Parse tag invocation:
*
* @myApp.myTag(...) Note since this is also a pattern for expression parser, InvokeTagParser must
* be put in front of expression parser
*/
public class InvokeTemplateParser extends CaretParserFactoryBase {
public static class ParameterDeclaration {
public String nameDef;
public String valDef;
ParameterDeclaration(String name, String val, IContext ctx) {
if (null != name) {
if (name.startsWith("\"") || name.startsWith("'")) name = name.substring(1);
if (name.endsWith("\"") || name.endsWith("'")) name = name.substring(0, name.length() - 1);
}
nameDef = name;
valDef = Token.processRythmExpression(val, ctx);
//System.out.println(String.format("%s : %s", name, val));
}
@Override
public String toString() {
return String.format("%s:%s", nameDef, valDef);
}
}
public static class ParameterDeclarationList {
public List pl = new ArrayList();
void addParameterDeclaration(String nameDef, String valDef, IContext ctx) {
pl.add(new ParameterDeclaration(nameDef, valDef, ctx));
}
@Override
public String toString() {
return pl.toString();
}
}
static class InvokeTagToken extends CodeToken {
protected boolean isDynamic = false;
protected String tagName;
private boolean enableCallback = false;
ParameterDeclarationList params = new ParameterDeclarationList();
protected boolean enableCache = false;
protected String cacheDuration = null;
protected String cacheArgs = null;
protected Escape escape = null;
protected boolean ignoreNonExistsTag = false;
protected String assignTo = null;
protected boolean assignToFinal = false;
protected List argList = null;
static InvokeTagToken dynamicTagToken(String tagName, String paramLine, String extLine, IContext context) {
InvokeTagToken t = new InvokeTagToken(tagName, paramLine, extLine, context);
t.isDynamic = true;
return t;
}
static InvokeTagToken dynamicTagToken(String tagName, String paramLine, String extLine, IContext context, boolean enableCallback) {
InvokeTagToken t = new InvokeTagToken(tagName, paramLine, extLine, context, enableCallback);
t.isDynamic = true;
return t;
}
InvokeTagToken(String tagName, String paramLine, String extLine, IContext context) {
this(tagName, paramLine, extLine, context, false);
}
InvokeTagToken(String tagName, String paramLine, String extLine, IContext context, boolean enableCallback) {
super(null, context);
this.tagName = tagName;
this.enableCallback = enableCallback;
parseParams(paramLine);
parseExtension(extLine);
}
/*
* Parse line like (bar='c', foo=bar.length(), zee=component[foo], "hello")
*/
private void parseParams(String line) {
parseParams(line, params, ctx);
}
static void parseParams(String line, ParameterDeclarationList params, IContext ctx) {
if (S.isEmpty(line)) return;
// strip '(' and ')'
line = line.trim();
if (line.startsWith("(")) line = S.stripBrace(line);
Regex r = new Regex("\\G(\\s*,\\s*)?((([a-zA-Z_][\\w$_]*)\\s*[=:]\\s*)?((?@())|'.'|(?@\"\")|[0-9\\.]+[l]?|[a-zA-Z_][a-zA-Z0-9_\\.]*(?@())*(?@[])*(?@())*(\\.[a-zA-Z][a-zA-Z0-9_\\.]*(?@())*(?@[])*(?@())*)*)|[_a-zA-Z][a-z_A-Z0-9]*)");
line = line.replaceAll("^\\s+", ""); // allow line breaks in params
line = S.strip(line, "{", "}");
line = line.replaceAll("^\\s+", ""); // allow line breaks in params
while (r.search(line)) {
params.addParameterDeclaration(r.stringMatched(4), r.stringMatched(5), ctx);
}
}
private void parseExtension(String line) {
if (S.isEmpty(line)) {
return;
}
Regex r = new Regex("\\G(\\.)([_a-zA-Z][_a-zA-Z0-9]*)((?@()))");
while (r.search(line)) {
String group = r.stringMatched();
String extension = r.stringMatched(2);
String param = S.stripBrace(r.stringMatched(3));
if ("cache".equals(extension)) {
parseCache(param);
} else if ("escape".equals(extension)) {
parseEscape(param);
} else if ("raw".equals(extension)) {
escape = Escape.RAW;
} else if ("callback".equals(extension)) {
parseCallback(param);
} else if ("ignoreNonExistsTag".equals(extension)) {
ignoreNonExistsTag = true;
} else if ("assign".equals(extension)) {
parseAssign(param);
ctx.getCodeBuilder().removeNextLF = true;
} else {
raiseParseException(ctx, "Unknown tag invocation extension: %s. Currently supported extension: cache, escape, raw, callback, ignoreNonExistsTag", extension);
}
}
}
private void parseCache(String param) {
enableCache = true;
String[] sa = param.split(",");
if (sa.length > 0) {
String s = sa[0];
cacheDuration = S.isEmpty(s) ? "null" : s;
} else {
cacheDuration = "null";
}
// check if duration is valid
CacheParser.validateDurationStr(cacheDuration, ctx);
if (sa.length > 1) {
cacheArgs = param.replaceFirst(cacheDuration, "");
} else {
cacheArgs = ", _plUUID";
}
}
private void parseEscape(String param) {
if (S.isEmpty(param)) {
escape = Escape.HTML;
} else {
param = S.stripQuotation(param).trim();
try {
escape = Escape.valueOf(param.toUpperCase());
} catch (Exception e) {
raiseParseException(ctx, "Unknown escape type: %s. Supported escape: RAW, HTML(default), JAVA, JS, JSON, CSV, XML", param);
}
}
}
private void parseAssign(String param) {
String[] sa = param.split(",");
assignTo = S.stripQuotation(sa[0]);
if (S.isEmpty(assignTo)) {
raiseParseException(ctx, "assign extension needs a variable name");
}
if (Patterns.RESERVED.matches(assignTo)) {
raiseParseException(ctx, "assign variable name is reserved: %s", assignTo);
}
if (sa.length > 1) {
this.assignToFinal = Boolean.parseBoolean(sa[1].trim());
}
}
private void parseCallback(String param) {
if (!enableCallback) {
raiseParseException(ctx, "callback extension only apply to tag invocation with body");
}
argList = ArgsParser.parseArgDeclaration(ctx.currentLine(), param);
}
private String cacheKey = null;
protected String cacheKey() {
if (null == cacheKey) {
if (!isDynamic) {
cacheKey = "\"" + UUID.nameUUIDFromBytes(("_RYTHM_TAG_" + tagName + ctx.getTemplateClass().name()).getBytes()).toString() + "\"";
} else {
cacheKey = "\"_RYTHM_TAG_\" + " + tagName + " + \"" + ctx.getTemplateClass().name() + "\"";
}
}
return cacheKey;
}
protected boolean needsNewOut() {
return (assignTo != null) || (escape != null) || enableCache;
}
@Override
public void output() {
if (assignTo != null) {
if (assignToFinal) {
pt("Object ").p(assignTo).p("___ = null;");
} else {
pt("Object ").p(assignTo).p(" = null;");
}
pline();
}
pline("{");
ptline("com.greenlaw110.rythm.template.ITag.__ParameterList _pl = null; ");
if (params.pl.size() > 0) {
ptline("_pl = new com.greenlaw110.rythm.template.ITag.__ParameterList();");
for (int i = 0; i < params.pl.size(); ++i) {
ParameterDeclaration pd = params.pl.get(i);
//if (i == 0 && pd.nameDef == null) pd.nameDef = "arg";
pt("_pl.add(\"").p(pd.nameDef == null ? "" : pd.nameDef).p("\",").p(pd.valDef).p(");");
pline();
}
}
if (needsNewOut()) {
ptline("Object _r_s = null;");
if (enableCache) {
ptline("String _plUUID = null == _pl ? \"\" : _pl.toUUID();");
pt("_r_s = __engine().cached(").p(cacheKey()).p(cacheArgs).p(");");
pline();
}
ptline("if (null == _r_s) {");
p2tline("StringBuilder sbOld = __getBuffer();");
p2tline("StringBuilder sbNew = new StringBuilder();");
p2tline("setSelfOut(sbNew);");
if (ctx.peekInsideBody()) {
p2t("__invokeTag(").p(line).p(", ").p(tagName).p(", _pl, null, __self, ").p(ignoreNonExistsTag).p(");");
} else {
p2t("__invokeTag(").p(line).p(", ").p(tagName).p(", _pl, ").p(ignoreNonExistsTag).p(");");
}
pline();
p2tline("_r_s = sbNew.toString();");
p2tline("setSelfOut(sbOld);");
if (escape != null) {
p2tline(String.format("_r_s = com.greenlaw110.rythm.utils.Escape.%s.apply(_r_s);", escape.name()));
}
if (enableCache) {
p2t("__engine().cache(").p(cacheKey()).p(", _r_s, ").p(cacheDuration).p(cacheArgs).p(");");
pline();
}
ptline("}");
if (assignTo != null) {
if (assignToFinal) {
pt(assignTo).p("___ = _r_s;");
} else {
pt(assignTo).p(" = _r_s;");
}
pline();
} else {
ptline("p(_r_s);");
}
} else {
if (ctx.peekInsideBody()) {
p2t("__invokeTag(").p(line).p(", ").p(tagName).p(", _pl, null, __self, ").p(ignoreNonExistsTag).p(");");
} else {
p2t("__invokeTag(").p(line).p(", ").p(tagName).p(", _pl, ").p(ignoreNonExistsTag).p(");");
}
pline();
}
pline("}");
if (assignTo != null && assignToFinal) {
p("final Object ").p(assignTo).p(" = ").p(assignTo).p("___;");
pline();
}
}
}
static class InvokeTagWithBodyToken extends InvokeTagToken implements IBlockHandler {
private String textListenerKey = UUID.randomUUID().toString();
private StringBuilder tagBodyBuilder = new StringBuilder();
private int startIndex = 0;
private int endIndex = 0;
private String key = null;
InvokeTagWithBodyToken(String tagName, String paramLine, String extLine, IContext context) {
super(tagName, paramLine, extLine, context, true);
context.openBlock(this);
startIndex = ctx.cursor();
}
private String cacheKey = null;
@Override
protected String cacheKey() {
if (null == cacheKey) {
if (!isDynamic) {
cacheKey = "\"" + UUID.nameUUIDFromBytes(("_RYTHM_TAG_" + tagName + key + ctx.getTemplateClass().name()).getBytes()).toString() + "\"";
} else {
cacheKey = "\"_RYTHM_TAG_\" + " + tagName + " + \"" + key + "\"" + " + \"" + ctx.getTemplateClass().name() + "\"";
}
}
return cacheKey;
}
@Override
public void openBlock() {
ctx.pushInsideBody2(true);
CodeBuilder cb = ctx.getCodeBuilder();
cb.addBuilder(new Token("", ctx) {
@Override
protected void output() {
ctx.pushInsideBody(true);
super.output();
}
});
}
@Override
public void output() {
if (assignTo != null) {
ptline("Object ").p(assignTo).p(" = null;");
}
pline("{");
ptline("com.greenlaw110.rythm.template.ITag.__ParameterList _pl = null; ");
if (params.pl.size() > 0) {
ptline("_pl = new com.greenlaw110.rythm.template.ITag.__ParameterList();");
for (int i = 0; i < params.pl.size(); ++i) {
ParameterDeclaration pd = params.pl.get(i);
//if (i == 0 && pd.nameDef == null) pd.nameDef = "arg";
pt("_pl.add(\"").p(pd.nameDef == null ? "" : pd.nameDef).p("\",").p(pd.valDef).p(");");
pline();
}
}
String curClassName = ctx.getCodeBuilder().includingClassName();
if (needsNewOut()) {
pline("Object _r_s = null;");
if (enableCache) {
ptline("String _plUUID = null == _pl ? \"\" : _pl.toUUID();");
pt("_r_s = __engine().cached(").p(cacheKey()).p(cacheArgs).p(");");
pline();
}
ptline("if (null == _r_s) {");
p2tline("StringBuilder sbOld = __getBuffer();");
p2tline("StringBuilder sbNew = new StringBuilder();");
p2tline("setSelfOut(sbNew);");
}
p2t("__invokeTag(").p(line).p(", ").p(tagName).p(", _pl, new com.greenlaw110.rythm.template.ITag.__Body(").p(curClassName).p(".this) {");
pline();
if (null != argList && !argList.isEmpty()) {
buildBodyArgList(argList);
}
p3tline("@Override public void __setProperty(String name, Object val) {");
p4tline("__setRenderArg(name, val);");
p3tline("}");
p3tline("@Override public Object __getProperty(String name) {");
p4tline("return __getRenderArg(name); ");
p3tline("}");
p3tline("@Override protected void __setBodyArgByName(String name, Object val) {");
if (null != argList && !argList.isEmpty()) {
buildSetBodyArgByName(argList);
}
p3tline("}");
p3tline("@Override protected void __setBodyArgByPos(int pos, Object val) {");
if (null != argList && !argList.isEmpty()) {
buildSetBodyArgByPos(argList);
}
p3tline("}");
p3tline("@Override protected void __call() {");
}
private void buildBodyArgList(List al) {
for (CodeBuilder.RenderArgDeclaration arg : al) {
p3t("protected ").p(arg.type).p(" ").p(arg.name);
if (null != arg.defVal) {
p("=").p(arg.defVal).p(";");
} else {
p(";");
}
pline();
}
}
private void buildSetBodyArgByName(List al) {
for (CodeBuilder.RenderArgDeclaration arg : al) {
p4t("if (\"").p(arg.name).p("\".equals(name)) this.").p(arg.name).p("=(").p(arg.type).p(")val;");
pline();
}
}
private void buildSetBodyArgByPos(List al) {
p4tline("int p = 0;");
for (CodeBuilder.RenderArgDeclaration arg : al) {
p4t("if (p++ == pos) { Object v = val; boolean isString = (\"java.lang.String\".equals(\"")
.p(arg.type).p("\") || \"String\".equals(\"").p(arg.type).p("\")); ")
.p(arg.name).p(" = (").p(arg.type).p(")(isString ? (null == v ? \"\" : v.toString()) : v); }");
pline();
}
}
@Override
public String closeBlock() {
ctx.popInsideBody2();
ctx.getCodeBuilder().addBuilder(new Token("", ctx) {
@Override
protected void output() {
ctx.popInsideBody();
super.output();
}
});
if (!needsNewOut()) {
if (ctx.peekInsideBody2()) {
return "\n\t\t}\n\t}, __self);\n}";
} else {
return "\n\t\t}\n\t});\n}";
}
}
if (enableCache) {
endIndex = ctx.cursor();
String body = ctx.getTemplateSource(startIndex, endIndex);
key = UUID.nameUUIDFromBytes(body.getBytes()).toString();
}
StringBuilder sbOld = __getBuffer();
StringBuilder sbNew = new StringBuilder();
__setBuffer(sbNew);
p3tline("}");
if (ctx.peekInsideBody2()) {
p2t("}, __self, ").p(ignoreNonExistsTag).p(");");
} else {
p2t("}, ").p(ignoreNonExistsTag).p(");");
}
pline();
p2tline("_r_s = sbNew.toString();");
p2tline("setSelfOut(sbOld);");
if (escape != null) {
p2tline(String.format("_r_s = com.greenlaw110.rythm.utils.Escape.%s.apply(_r_s);", escape.name()));
}
if (enableCache) {
p2t("__engine().cache(").p(cacheKey()).p(", _r_s, ").p(cacheDuration).p(cacheArgs).p(");");
pline();
}
ptline("}");
if (assignTo != null) {
pt(assignTo).p(" = _r_s;");
pline();
} else {
ptline("p(_r_s);");
}
p2tline("}");
String s = sbNew.toString();
__setBuffer(sbOld);
return s;
}
}
static final Pattern P_HEREDOC_SIMBOL = Pattern.compile("(\\s*<<).*", Pattern.DOTALL);
static final Pattern P_STANDARD_BLOCK = Pattern.compile("(\\s*\\{).*", Pattern.DOTALL);
@Override
public IParser create(IContext ctx) {
return new ParserBase(ctx) {
String testTag(String name) {
return engine_.testTemplate(name, ctx().getTemplateClass());
}
@Override
public TextBuilder go() {
Regex r = new Regex(String.format(patternStr(), dialect().a()));
if (!r.search(remain())) return null;
String tagName = r.stringMatched(2);
try {
tagName = testTag(tagName);
} catch (NoClassDefFoundError e) {
raiseParseException("Error load tag class: " + e.getMessage() + "\nPossible cause: lower or upper case issue on windows platform");
}
if (null == tagName) return null;
else tagName = new StringBuilder("\"").append(tagName).append("\"").toString();
String s = r.stringMatched();
ctx().step(s.length());
s = remain();
Matcher m0 = P_HEREDOC_SIMBOL.matcher(s);
Matcher m1 = P_STANDARD_BLOCK.matcher(s);
if (m0.matches()) {
ctx().step(m0.group(1).length());
return new InvokeTagWithBodyToken(tagName, r.stringMatched(3), r.stringMatched(4), ctx());
} else if (m1.matches()) {
ctx().step(m1.group(1).length());
return new InvokeTagWithBodyToken(tagName, r.stringMatched(3), r.stringMatched(4), ctx());
} else {
return new InvokeTagToken(tagName, r.stringMatched(3), r.stringMatched(4), ctx());
}
}
};
}
private static String patternStr() {
return "^(%s([_a-zA-Z][a-zA-Z$_\\.0-9]*)\\s*((?@()))((\\.([_a-zA-Z][_a-zA-Z0-9]*)((?@())))*))";
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy