/* * 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.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 nodes) { return new Cons(resourceName, lineNumber, nodes); } private static final class Cons extends Node { private final ImmutableList nodes; Cons(String resourceName, int lineNumber, ImmutableList 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(); } } }