jetbrick.template.parser.ast.AstStatementList Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jetbrick-template Show documentation
Show all versions of jetbrick-template Show documentation
Next generation template engine for Java
/**
* Copyright 2013-2016 Guoqiang Chen, Shanghai, China. All rights reserved.
*
* Author: Guoqiang Chen
* Email: [email protected]
* WebURL: https://github.com/subchen
*
* 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 jetbrick.template.parser.ast;
import java.util.List;
import jetbrick.template.parser.ParserContext;
import jetbrick.template.runtime.InterpretContext;
public final class AstStatementList extends AstStatement {
private static final AstStatement[] EMPTY_ARRAY = new AstStatement[0];
private final AstStatement[] statements;
public AstStatementList(List statements, int block, ParserContext ctx) {
if (statements == null || statements.isEmpty()) {
this.statements = EMPTY_ARRAY;
} else {
if (statements.size() > 0 && block != Tokens.AST_BLOCK_SET) {
// 注意: 这里直接修改的是 statements 本身
ListIterator it = new ListIterator(statements);
splitStatementList(it);
combinedContinuousTexts(it); // 合并由 escape 等产生的连续文本
trimDirectiveWhitespacesAndComments(it, block, ctx);
removeNoopDirective(it);
combinedContinuousTexts(it); // 合并由于 AstDirectiveNoop 等产生的连续文本
}
this.statements = statements.toArray(EMPTY_ARRAY);
}
}
// 优化 - 分解 AstStatementList 子节点 (主要是 #set 产生的)
private void splitStatementList(ListIterator it) {
it.reset();
while (it.has()) {
AstStatement statment = it.peek();
if (statment instanceof AstStatementList) {
it.remove();
it.addAll(((AstStatementList) statment).statements);
}
it.move();
}
}
// 优化 - 移除 AstDirectiveNoop 子节点 (主要是 #define, #options, #macro 产生的),
private void removeNoopDirective(ListIterator it) {
it.reset();
while (it.has()) {
if (it.peek() instanceof AstDirectiveNoop) {
it.remove();
} else {
it.move();
}
}
}
// 优化 - 合并连续的 AstText 节点
private void combinedContinuousTexts(ListIterator it) {
StringBuilder sb = null;
it.reset();
while (it.has()) {
AstStatement stmt = it.peek();
if (!(stmt instanceof AstText)) {
it.move();
continue;
}
if (it.hasNext()) {
AstStatement next = it.peek(1);
if (!(next instanceof AstText)) {
it.move(2);
continue;
}
if (sb == null) {
sb = new StringBuilder(256);
} else {
sb.setLength(0);
}
sb.append(((AstText) stmt).getText());
it.remove();
sb.append(((AstText) next).getText());
it.remove();
while (it.has()) {
next = it.peek();
if (next instanceof AstText) {
sb.append(((AstText) next).getText());
it.remove();
} else {
break;
}
}
it.add(new AstText(sb.toString(), ((AstText) stmt).getLine()));
}
it.move();
}
}
// trimDirectiveWhitespaces & trimDirectiveComments
private void trimDirectiveWhitespacesAndComments(ListIterator it, int block, ParserContext ctx) {
boolean trimDirectiveWhitespaces = ctx.isTrimDirectiveWhitespaces();
boolean trimDirectiveComments = ctx.isTrimDirectiveComments();
String trimDirectiveCommentsPrefix = ctx.getTrimDirectiveCommentsPrefix();
String trimDirectiveCommentsSuffix = ctx.getTrimDirectiveCommentsSuffix();
if (!trimDirectiveWhitespaces && !trimDirectiveComments) {
return;
}
it.reset();
while (it.has()) {
AstStatement stmt = it.peek();
if (!(stmt instanceof AstText)) {
it.move();
continue;
}
AstText text = (AstText) stmt;
boolean trimWhitespacesLeft, trimCommentsLeft;
boolean keepLeftNewLine = false;
if (it.hasPrevious()) {
trimWhitespacesLeft = isAstDirective(it.peek(-1), false);
trimCommentsLeft = isAstDirective(it.peek(-1), true);
if (trimWhitespacesLeft) {
// inline directive, 对于一个内联的 #if, #for 等指令,后面有要求保留一个 NewLine
// @see https://github.com/subchen/jetbrick-template-1x/issues/25
AstStatement prev = it.peek(-1);
if (prev != null) {
if (prev instanceof AstDirective) {
if (prev instanceof AstDirectiveTag) {
// #tag 调用后面要求保留一个 NewLine
keepLeftNewLine = true;
} else {
keepLeftNewLine = ((AstDirective) prev).getPosition().getLine() == text.getLine();
}
}
}
}
} else {
trimWhitespacesLeft = (block != Tokens.AST_BLOCK_TEMPLATE);
trimCommentsLeft = trimWhitespacesLeft;
}
boolean trimWhitespacesRight, trimCommentsRight;
if (it.hasNext()) {
trimWhitespacesRight = isAstDirective(it.peek(1), false);
trimCommentsRight = isAstDirective(it.peek(1), true);
} else {
trimWhitespacesRight = (block != Tokens.AST_BLOCK_TEMPLATE);
trimCommentsRight = trimWhitespacesRight;
}
// trim 指令两边的注释
if (trimDirectiveComments) {
text.trimDirectiveComments(trimCommentsLeft, trimCommentsRight, trimDirectiveCommentsPrefix, trimDirectiveCommentsSuffix);
}
// trim 指令两边的空白内容
if (trimDirectiveWhitespaces) {
text.trimDirectiveWhitespaces(trimWhitespacesLeft, trimWhitespacesRight, keepLeftNewLine);
}
// trim 掉 #tag 和 #macro 指令最后一个多余的 '\n'
if (!it.hasNext()) {
if (block == Tokens.AST_BLOCK_TAG || block == Tokens.AST_BLOCK_MACRO) {
text.trimLastNewLine();
}
}
if (text.isEmpty()) {
it.remove();
} else {
it.move();
}
}
}
private boolean isAstDirective(AstNode node, boolean includeInlineDirective) {
if (node instanceof AstDirective) {
if (includeInlineDirective) {
return true;
}
// 将 #include/#call 当做 ${value} 这样的 value 来对待
return !(node instanceof AstDirectiveInclude || node instanceof AstDirectiveCall);
}
return false;
}
@Override
public void execute(InterpretContext ctx) {
for (AstStatement stmt : statements) {
stmt.execute(ctx);
// 处理 break, continue, return, stop 语句
if (ctx.getSignal() != InterpretContext.SIGNAL_NONE) {
return;
}
}
}
// --------------------------------------------------------------------
static final class ListIterator {
private final List list;
private int index;
private int size;
public ListIterator(List list) {
this.list = list;
this.index = 0;
this.size = list.size();
}
// 当前位置是否存在对象
public boolean has() {
return index >= 0 && index < size;
}
public boolean hasNext() {
return index + 1 < size;
}
public boolean hasPrevious() {
return index > 0;
}
public boolean has(int offset) {
int p = index + offset;
return p >= 0 && p < size;
}
// 向下移动一个位置
public void move() {
index += 1;
}
public void move(int offset) {
index += offset;
}
public void reset() {
this.index = 0;
this.size = list.size();
}
// 获取当前位置对象
public T peek() {
return list.get(index);
}
public T peek(int offset) {
return list.get(index + offset);
}
// 删除当前位置,并 move 到下一个
public void remove() {
list.remove(index);
size = list.size();
}
// 在当前位置加入,同时指针还是指向原来的那个对象
public void add(T item) {
list.add(index++, item);
size = list.size();
}
public void addAll(T[] items) {
for (T item : items) {
list.add(index++, item);
}
size = list.size();
}
}
}