org.mvel2.templates.TemplateCompiler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of virtdata-lib-realer Show documentation
Show all versions of virtdata-lib-realer Show documentation
With inspiration from other libraries
/**
* MVEL 2.0
* Copyright (C) 2007 The Codehaus
* Mike Brock, Dhanji Prasanna, John Graham, Mark Proctor
*
* 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 org.mvel2.templates;
import org.mvel2.CompileException;
import org.mvel2.ParserContext;
import org.mvel2.templates.res.*;
import org.mvel2.templates.util.TemplateTools;
import org.mvel2.util.ExecutionStack;
import org.mvel2.util.ParseTools;
import java.io.File;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import static org.mvel2.util.ParseTools.balancedCaptureWithLineAccounting;
import static org.mvel2.util.ParseTools.subset;
/**
* The TemplateCompiler class is used for pre-compiling MVEL Templates. To execute a compiled template see
* {@link TemplateRuntime}
*
* @author Mike Brock
*/
@SuppressWarnings({"ManualArrayCopy"})
public class TemplateCompiler {
private char[] template;
private int length;
private int start;
private int cursor;
private int lastTextRangeEnding;
private int line;
private int colStart;
private boolean codeCache = false;
private Map> customNodes;
private ParserContext parserContext;
private static final Map OPCODES = new HashMap();
static {
OPCODES.put("if", Opcodes.IF);
OPCODES.put("else", Opcodes.ELSE);
OPCODES.put("elseif", Opcodes.ELSE);
OPCODES.put("end", Opcodes.END);
OPCODES.put("foreach", Opcodes.FOREACH);
OPCODES.put("includeNamed", Opcodes.INCLUDE_NAMED);
OPCODES.put("include", Opcodes.INCLUDE_FILE);
OPCODES.put("comment", Opcodes.COMMENT);
OPCODES.put("code", Opcodes.CODE);
OPCODES.put("eval", Opcodes.EVAL);
OPCODES.put("declare", Opcodes.DECLARE);
OPCODES.put("stop", Opcodes.STOP);
}
public CompiledTemplate compile() {
return new CompiledTemplate(template, compileFrom(null, new ExecutionStack()));
}
public Node compileFrom(Node root, ExecutionStack stack) {
line = 1;
Node n = root;
if (root == null) {
n = root = new TextNode(0, 0);
}
IfNode last;
Integer opcode;
String name;
int x;
try {
while (cursor < length) {
switch (template[cursor]) {
case '\n':
line++;
colStart = cursor + 1;
break;
case '@':
case '$':
if (isNext(template[cursor])) {
start = ++cursor;
(n = markTextNode(n)).setEnd(n.getEnd() + 1);
start = lastTextRangeEnding = ++cursor;
continue;
}
if ((x = captureOrbToken()) != -1) {
start = x;
switch ((opcode = OPCODES.get(name = new String(capture()))) == null ? 0 : opcode) {
case Opcodes.IF:
/**
* Capture any residual text node, and push the if statement on the nesting stack.
*/
stack.push(n = markTextNode(n).next =
codeCache ? new CompiledIfNode(start, name, template, captureOrbInternal(), start, parserContext)
: new IfNode(start, name, template, captureOrbInternal(), start));
n.setTerminus(new TerminalNode());
break;
case Opcodes.ELSE:
if (!stack.isEmpty() && stack.peek() instanceof IfNode) {
markTextNode(n).next = (last = (IfNode) stack.pop()).getTerminus();
last.demarcate(last.getTerminus(), template);
last.next = n = codeCache ? new CompiledIfNode(start, name, template, captureOrbInternal(), start, parserContext)
: new IfNode(start, name, template, captureOrbInternal(), start);
n.setTerminus(last.getTerminus());
stack.push(n);
}
break;
case Opcodes.FOREACH:
stack.push(
n = markTextNode(n).next = codeCache ? new CompiledForEachNode(start, name, template, captureOrbInternal(), start, parserContext)
: new ForEachNode(start, name, template, captureOrbInternal(), start)
);
n.setTerminus(new TerminalNode());
break;
case Opcodes.INCLUDE_FILE:
n = markTextNode(n).next =
codeCache ? new CompiledIncludeNode(start, name, template, captureOrbInternal(), start = cursor + 1, parserContext)
: new IncludeNode(start, name, template, captureOrbInternal(), start = cursor + 1);
break;
case Opcodes.INCLUDE_NAMED:
n = markTextNode(n).next =
codeCache ? new CompiledNamedIncludeNode(start, name, template, captureOrbInternal(), start = cursor + 1, parserContext)
: new NamedIncludeNode(start, name, template, captureOrbInternal(), start = cursor + 1);
break;
case Opcodes.CODE:
n = markTextNode(n)
.next = codeCache ? new CompiledCodeNode(start, name, template, captureOrbInternal(), start = cursor + 1, parserContext)
: new CodeNode(start, name, template, captureOrbInternal(), start = cursor + 1);
break;
case Opcodes.EVAL:
n = markTextNode(n).next =
codeCache ? new CompiledEvalNode(start, name, template, captureOrbInternal(), start = cursor + 1, parserContext)
: new EvalNode(start, name, template, captureOrbInternal(), start = cursor + 1);
break;
case Opcodes.COMMENT:
n = markTextNode(n)
.next = new CommentNode(start, name, template, captureOrbInternal(), start = cursor + 1);
break;
case Opcodes.DECLARE:
stack.push(n = markTextNode(n).next =
codeCache ?
new CompiledDeclareNode(start, name, template, captureOrbInternal(), start = cursor + 1, parserContext)
: new DeclareNode(start, name, template, captureOrbInternal(), start = cursor + 1));
n.setTerminus(new TerminalNode());
break;
case Opcodes.END:
n = markTextNode(n);
Node end = (Node) stack.pop();
Node terminal = end.getTerminus();
terminal.setCStart(captureOrbInternal());
terminal.setEnd((lastTextRangeEnding = start) - 1);
terminal.calculateContents(template);
if (end.demarcate(terminal, template)) n = n.next = terminal;
else n = terminal;
break;
default:
if (name.length() == 0) {
n = markTextNode(n).next =
codeCache ?
new CompiledExpressionNode(start, name, template, captureOrbInternal(), start = cursor + 1, parserContext)
: new ExpressionNode(start, name, template, captureOrbInternal(), start = cursor + 1);
}
else if (customNodes != null && customNodes.containsKey(name)) {
Class extends Node> customNode = customNodes.get(name);
try {
(n = markTextNode(n).next = (customNode.newInstance())).setBegin(start);
n.setName(name);
n.setCStart(captureOrbInternal());
n.setCEnd(start = cursor + 1);
n.setEnd(n.getCEnd());
n.setContents(subset(template, n.getCStart(), n.getCEnd() - n.getCStart() - 1));
if (n.isOpenNode()) {
stack.push(n);
}
}
catch (InstantiationException e) {
throw new RuntimeException("unable to instantiate custom node class: " + customNode.getName());
}
catch (IllegalAccessException e) {
throw new RuntimeException("unable to instantiate custom node class: " + customNode.getName());
}
}
else {
throw new RuntimeException("unknown token type: " + name);
}
}
}
break;
}
cursor++;
}
}
catch (RuntimeException e) {
CompileException ce = new CompileException(e.getMessage(), template, cursor, e);
ce.setExpr(template);
if (e instanceof CompileException) {
CompileException ce2 = (CompileException) e;
if (ce2.getCursor() != -1) {
ce.setCursor(ce2.getCursor());
if (ce2.getColumn() == -1) ce.setColumn(ce.getCursor() - colStart);
else ce.setColumn(ce2.getColumn());
}
}
ce.setLineNumber(line);
throw ce;
}
if (!stack.isEmpty()) {
CompileException ce = new CompileException("unclosed @" + ((Node) stack.peek()).getName() + "{} block. expected @end{}", template, cursor);
ce.setColumn(cursor - colStart);
ce.setLineNumber(line);
throw ce;
}
if (start < template.length) {
n = n.next = new TextNode(start, template.length);
}
n.next = new EndNode();
n = root;
do {
if (n.getLength() != 0) {
break;
}
}
while ((n = n.getNext()) != null);
if (n != null && n.getLength() == template.length - 1) {
if (n instanceof ExpressionNode) {
return codeCache ? new CompiledTerminalExpressionNode(n, parserContext) : new TerminalExpressionNode(n);
}
else {
return n;
}
}
return root;
}
// Parse Utilities Start Here
private boolean isNext(char c) {
return cursor != length && template[cursor + 1] == c;
}
private int captureOrbToken() {
int newStart = ++cursor;
while ((cursor != length) && ParseTools.isIdentifierPart(template[cursor])) cursor++;
if (cursor != length && template[cursor] == '{') return newStart;
return -1;
}
private int captureOrbInternal() {
try {
ParserContext pCtx = new ParserContext();
cursor = balancedCaptureWithLineAccounting(template, start = cursor, length, '{', pCtx);
line += pCtx.getLineCount();
int ret = start + 1;
start = cursor + 1;
return ret;
}
catch (CompileException e) {
e.setLineNumber(line);
e.setColumn((cursor - colStart) + 1);
throw e;
}
}
private char[] capture() {
char[] newChar = new char[cursor - start];
for (int i = 0; i < newChar.length; i++) {
newChar[i] = template[i + start];
}
return newChar;
}
private Node markTextNode(Node n) {
int s = (n.getEnd() > lastTextRangeEnding ? n.getEnd() : lastTextRangeEnding);
if (s < start) {
return n.next = new TextNode(s, lastTextRangeEnding = start - 1);
}
return n;
}
public ParserContext getParserContext() {
return parserContext;
}
public static CompiledTemplate compileTemplate(String template) {
return new TemplateCompiler(template, true, ParserContext.create()).compile();
}
public static CompiledTemplate compileTemplate(char[] template) {
return new TemplateCompiler(template, true, ParserContext.create()).compile();
}
public static CompiledTemplate compileTemplate(CharSequence template) {
return new TemplateCompiler(template, true, ParserContext.create()).compile();
}
public static CompiledTemplate compileTemplate(String template, ParserContext context) {
return new TemplateCompiler(template, true, context).compile();
}
public static CompiledTemplate compileTemplate(char[] template, ParserContext context) {
return new TemplateCompiler(template, true, context).compile();
}
public static CompiledTemplate compileTemplate(CharSequence template, ParserContext context) {
return new TemplateCompiler(template, true, context).compile();
}
public static CompiledTemplate compileTemplate(String template, Map> customNodes) {
return new TemplateCompiler(template, customNodes, true, ParserContext.create()).compile();
}
public static CompiledTemplate compileTemplate(char[] template, Map> customNodes) {
return new TemplateCompiler(template, customNodes, true, ParserContext.create()).compile();
}
public static CompiledTemplate compileTemplate(CharSequence template, Map> customNodes) {
return new TemplateCompiler(template, customNodes, true, ParserContext.create()).compile();
}
public static CompiledTemplate compileTemplate(String template, Map> customNodes,
ParserContext context) {
return new TemplateCompiler(template, customNodes, true, context).compile();
}
public static CompiledTemplate compileTemplate(char[] template, Map> customNodes,
ParserContext context) {
return new TemplateCompiler(template, customNodes, true, context).compile();
}
public static CompiledTemplate compileTemplate(CharSequence template, Map> customNodes,
ParserContext context) {
return new TemplateCompiler(template, customNodes, true, context).compile();
}
public static CompiledTemplate compileTemplate(InputStream stream) {
return compileTemplate(stream, ParserContext.create());
}
public static CompiledTemplate compileTemplate(InputStream stream, ParserContext context) {
return compileTemplate(stream, null, context);
}
public static CompiledTemplate compileTemplate(InputStream stream, Map> customNodes) {
return new TemplateCompiler(TemplateTools.readStream(stream), customNodes, true, ParserContext.create()).compile();
}
public static CompiledTemplate compileTemplate(InputStream stream, Map> customNodes,
ParserContext context) {
return new TemplateCompiler(TemplateTools.readStream(stream), customNodes, true, context).compile();
}
public static CompiledTemplate compileTemplate(File file) {
return compileTemplate(file, ParserContext.create());
}
public static CompiledTemplate compileTemplate(File file, ParserContext context) {
return compileTemplate(file, null, context);
}
public static CompiledTemplate compileTemplate(File file, Map> customNodes) {
return new TemplateCompiler(TemplateTools.readInFile(file), customNodes, true, ParserContext.create()).compile();
}
public static CompiledTemplate compileTemplate(File file, Map> customNodes,
ParserContext context) {
return new TemplateCompiler(TemplateTools.readInFile(file), customNodes, true, context).compile();
}
public TemplateCompiler(String template) {
this.length = (this.template = template.toCharArray()).length;
}
public TemplateCompiler(char[] template) {
this.length = (this.template = template).length;
}
public TemplateCompiler(String template, boolean codeCache) {
this.length = (this.template = template.toCharArray()).length;
this.codeCache = codeCache;
}
public TemplateCompiler(char[] template, boolean codeCache) {
this.length = (this.template = template).length;
this.codeCache = codeCache;
}
public TemplateCompiler(char[] template, boolean codeCache, ParserContext context) {
this.length = (this.template = template).length;
this.codeCache = codeCache;
this.parserContext = context;
}
public TemplateCompiler(CharSequence sequence) {
this.length = (this.template = sequence.toString().toCharArray()).length;
}
public TemplateCompiler(CharSequence sequence, boolean codeCache) {
this.length = (this.template = sequence.toString().toCharArray()).length;
this.codeCache = codeCache;
}
public TemplateCompiler(CharSequence sequence, boolean codeCache, ParserContext context) {
this.length = (this.template = sequence.toString().toCharArray()).length;
this.codeCache = codeCache;
this.parserContext = context;
}
public TemplateCompiler(String template, Map> customNodes) {
this.length = (this.template = template.toCharArray()).length;
this.customNodes = customNodes;
}
public TemplateCompiler(char[] template, Map> customNodes) {
this.length = (this.template = template).length;
this.customNodes = customNodes;
}
public TemplateCompiler(CharSequence sequence, Map> customNodes) {
this.length = (this.template = sequence.toString().toCharArray()).length;
this.customNodes = customNodes;
}
public TemplateCompiler(String template, Map> customNodes, boolean codeCache) {
this.length = (this.template = template.toCharArray()).length;
this.customNodes = customNodes;
this.codeCache = codeCache;
}
public TemplateCompiler(char[] template, Map> customNodes, boolean codeCache) {
this.length = (this.template = template).length;
this.customNodes = customNodes;
this.codeCache = codeCache;
}
public TemplateCompiler(CharSequence sequence, Map> customNodes, boolean codeCache) {
this.length = (this.template = sequence.toString().toCharArray()).length;
this.customNodes = customNodes;
this.codeCache = codeCache;
}
public TemplateCompiler(String template, Map> customNodes, boolean codeCache,
ParserContext context) {
this.length = (this.template = template.toCharArray()).length;
this.customNodes = customNodes;
this.codeCache = codeCache;
this.parserContext = context;
}
public TemplateCompiler(char[] template, Map> customNodes, boolean codeCache,
ParserContext context) {
this.length = (this.template = template).length;
this.customNodes = customNodes;
this.codeCache = codeCache;
this.parserContext = context;
}
public TemplateCompiler(CharSequence sequence, Map> customNodes, boolean codeCache,
ParserContext context) {
this.length = (this.template = sequence.toString().toCharArray()).length;
this.customNodes = customNodes;
this.codeCache = codeCache;
this.parserContext = context;
}
}