diff options
Diffstat (limited to 'src/main/java/com/google/escapevelocity/Node.java')
-rw-r--r-- | src/main/java/com/google/escapevelocity/Node.java | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/src/main/java/com/google/escapevelocity/Node.java b/src/main/java/com/google/escapevelocity/Node.java new file mode 100644 index 0000000..a017afa --- /dev/null +++ b/src/main/java/com/google/escapevelocity/Node.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2018 Google, Inc. + * + * 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 com.google.escapevelocity; + +import com.google.common.collect.ImmutableList; + +/** + * A node in the parse tree. + * + * @author emcmanus@google.com (Éamonn McManus) + */ +abstract class Node { + final String resourceName; + final int lineNumber; + + Node(String resourceName, int lineNumber) { + this.resourceName = resourceName; + this.lineNumber = lineNumber; + } + + /** + * Returns the result of evaluating this node in the given context. This result may be used as + * part of a further operation, for example evaluating {@code 2 + 3} to 5 in order to set + * {@code $x} to 5 in {@code #set ($x = 2 + 3)}. Or it may be used directly as part of the + * template output, for example evaluating replacing {@code name} by {@code Fred} in + * {@code My name is $name.}. + */ + abstract Object evaluate(EvaluationContext context); + + private String where() { + String where = "In expression on line " + lineNumber; + if (resourceName != null) { + where += " of " + resourceName; + } + return where; + } + + EvaluationException evaluationException(String message) { + return new EvaluationException(where() + ": " + message); + } + + EvaluationException evaluationException(Throwable cause) { + return new EvaluationException(where() + ": " + cause, cause); + } + + /** + * Returns an empty node in the parse tree. This is used for example to represent the trivial + * "else" part of an {@code #if} that does not have an explicit {@code #else}. + */ + static Node emptyNode(String resourceName, int lineNumber) { + return new Cons(resourceName, lineNumber, ImmutableList.<Node>of()); + } + + /** + * Create a new parse tree node that is the concatenation of the given ones. Evaluating the + * new node produces the same string as evaluating each of the given nodes and concatenating the + * result. + */ + static Node cons(String resourceName, int lineNumber, ImmutableList<Node> nodes) { + return new Cons(resourceName, lineNumber, nodes); + } + + private static final class Cons extends Node { + private final ImmutableList<Node> nodes; + + Cons(String resourceName, int lineNumber, ImmutableList<Node> nodes) { + super(resourceName, lineNumber); + this.nodes = nodes; + } + + @Override Object evaluate(EvaluationContext context) { + StringBuilder sb = new StringBuilder(); + for (Node node : nodes) { + sb.append(node.evaluate(context)); + } + return sb.toString(); + } + } +} |