diff --git a/value/src/main/java/com/google/auto/value/processor/escapevelocity/ConstantExpressionNode.java b/value/src/main/java/com/google/auto/value/processor/escapevelocity/ConstantExpressionNode.java deleted file mode 100644 index e8df21bb69..0000000000 --- a/value/src/main/java/com/google/auto/value/processor/escapevelocity/ConstantExpressionNode.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2015 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. - */ - -/* - * 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.google.auto.value.processor.escapevelocity; - -/** - * A node in the parse tree representing a constant value. Evaluating the node yields the constant - * value. Instances of this class are used both in expressions, like the {@code 23} in - * {@code #set ($x = 23)}, and for literal text in templates. In the template... - *
{@code
- * abc#{if}($x == 5)def#{end}xyz
- * }
- * ...each of the strings {@code abc}, {@code def}, {@code xyz} is represented by an instance of - * this class that {@linkplain #evaluate evaluates} to that string, and the value {@code 5} is - * represented by an instance of this class that evaluates to the integer 5. - * - * @author emcmanus@google.com (Éamonn McManus) - */ -class ConstantExpressionNode extends ExpressionNode { - private final Object value; - - ConstantExpressionNode(String resourceName, int lineNumber, Object value) { - super(resourceName, lineNumber); - this.value = value; - } - - @Override - Object evaluate(EvaluationContext context) { - return value; - } -} diff --git a/value/src/main/java/com/google/auto/value/processor/escapevelocity/DirectiveNode.java b/value/src/main/java/com/google/auto/value/processor/escapevelocity/DirectiveNode.java deleted file mode 100644 index e390588064..0000000000 --- a/value/src/main/java/com/google/auto/value/processor/escapevelocity/DirectiveNode.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (C) 2015 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. - */ - -/* - * 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.google.auto.value.processor.escapevelocity; - -import com.google.common.base.Verify; -import com.google.common.collect.ImmutableList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.Map; - -/** - * A node in the parse tree that is a directive such as {@code #set ($x = $y)} - * or {@code #if ($x) y #end}. - * - * @author emcmanus@google.com (Éamonn McManus) - */ -abstract class DirectiveNode extends Node { - DirectiveNode(String resourceName, int lineNumber) { - super(resourceName, lineNumber); - } - - /** - * A node in the parse tree representing a {@code #set} construct. Evaluating - * {@code #set ($x = 23)} will set {@code $x} to the value 23. It does not in itself produce - * any text in the output. - * - *

Velocity supports setting values within arrays or collections, with for example - * {@code $set ($x[$i] = $y)}. That is not currently supported here. - */ - static class SetNode extends DirectiveNode { - private final String var; - private final Node expression; - - SetNode(String var, Node expression) { - super(expression.resourceName, expression.lineNumber); - this.var = var; - this.expression = expression; - } - - @Override - Object evaluate(EvaluationContext context) { - context.setVar(var, expression.evaluate(context)); - return ""; - } - } - - /** - * A node in the parse tree representing an {@code #if} construct. All instances of this class - * have a true subtree and a false subtree. For a plain {@code #if (cond) body - * #end}, the false subtree will be empty. For {@code #if (cond1) body1 #elseif (cond2) body2 - * #else body3 #end}, the false subtree will contain a nested {@code IfNode}, as if {@code #else - * #if} had been used instead of {@code #elseif}. - */ - static class IfNode extends DirectiveNode { - private final ExpressionNode condition; - private final Node truePart; - private final Node falsePart; - - IfNode( - String resourceName, - int lineNumber, - ExpressionNode condition, - Node trueNode, - Node falseNode) { - super(resourceName, lineNumber); - this.condition = condition; - this.truePart = trueNode; - this.falsePart = falseNode; - } - - @Override Object evaluate(EvaluationContext context) { - Node branch = condition.isDefinedAndTrue(context) ? truePart : falsePart; - return branch.evaluate(context); - } - } - - /** - * A node in the parse tree representing a {@code #foreach} construct. While evaluating - * {@code #foreach ($x in $things)}, {$code $x} will be set to each element of {@code $things} in - * turn. Once the loop completes, {@code $x} will go back to whatever value it had before, which - * might be undefined. During loop execution, the variable {@code $foreach} is also defined. - * Velocity defines a number of properties in this variable, but here we only support - * {@code $foreach.hasNext}. - */ - static class ForEachNode extends DirectiveNode { - private final String var; - private final ExpressionNode collection; - private final Node body; - - ForEachNode(String resourceName, int lineNumber, String var, ExpressionNode in, Node body) { - super(resourceName, lineNumber); - this.var = var; - this.collection = in; - this.body = body; - } - - @Override - Object evaluate(EvaluationContext context) { - Object collectionValue = collection.evaluate(context); - Iterable iterable; - if (collectionValue instanceof Iterable) { - iterable = (Iterable) collectionValue; - } else if (collectionValue instanceof Object[]) { - iterable = Arrays.asList((Object[]) collectionValue); - } else if (collectionValue instanceof Map) { - iterable = ((Map) collectionValue).values(); - } else { - throw evaluationException("Not iterable: " + collectionValue); - } - Runnable undo = context.setVar(var, null); - StringBuilder sb = new StringBuilder(); - Iterator it = iterable.iterator(); - Runnable undoForEach = context.setVar("foreach", new ForEachVar(it)); - while (it.hasNext()) { - context.setVar(var, it.next()); - sb.append(body.evaluate(context)); - } - undoForEach.run(); - undo.run(); - return sb.toString(); - } - - /** - * This class is the type of the variable {@code $foreach} that is defined within - * {@code #foreach} loops. Its {@link #getHasNext()} method means that we can write - * {@code #if ($foreach.hasNext)}. - */ - private static class ForEachVar { - private final Iterator iterator; - - ForEachVar(Iterator iterator) { - this.iterator = iterator; - } - - public boolean getHasNext() { - return iterator.hasNext(); - } - } - } - - /** - * A node in the parse tree representing a macro call. If the template contains a definition like - * {@code #macro (mymacro $x $y) ... #end}, then a call of that macro looks like - * {@code #mymacro (xvalue yvalue)}. The call is represented by an instance of this class. The - * definition itself does not appear in the parse tree. - * - *

Evaluating a macro involves temporarily setting the parameter variables ({@code $x $y} in - * the example) to thunks representing the argument expressions, evaluating the macro body, and - * restoring any previous values that the parameter variables had. - */ - static class MacroCallNode extends DirectiveNode { - private final String name; - private final ImmutableList thunks; - private Macro macro; - - MacroCallNode( - String resourceName, - int lineNumber, - String name, - ImmutableList argumentNodes) { - super(resourceName, lineNumber); - this.name = name; - this.thunks = argumentNodes; - } - - String name() { - return name; - } - - int argumentCount() { - return thunks.size(); - } - - void setMacro(Macro macro) { - this.macro = macro; - } - - @Override - Object evaluate(EvaluationContext context) { - Verify.verifyNotNull(macro, "Macro #%s should have been linked", name); - return macro.evaluate(context, thunks); - } - } -} diff --git a/value/src/main/java/com/google/auto/value/processor/escapevelocity/EvaluationContext.java b/value/src/main/java/com/google/auto/value/processor/escapevelocity/EvaluationContext.java deleted file mode 100644 index 94e80db925..0000000000 --- a/value/src/main/java/com/google/auto/value/processor/escapevelocity/EvaluationContext.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2015 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. - */ - -/* - * 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.google.auto.value.processor.escapevelocity; - -import java.util.Map; -import java.util.TreeMap; - -/** - * The context of a template evaluation. This consists of the template variables and the template - * macros. The template variables start with the values supplied by the evaluation call, and can - * be changed by {@code #set} directives and during the execution of {@code #foreach} and macro - * calls. The macros are extracted from the template during parsing and never change thereafter. - * - * @author emcmanus@google.com (Éamonn McManus) - */ -interface EvaluationContext { - Object getVar(String var); - - boolean varIsDefined(String var); - - /** - * Sets the given variable to the given value. - * - * @return a Runnable that will restore the variable to the value it had before. If the variable - * was undefined before this method was executed, the Runnable will make it undefined again. - * This allows us to restore the state of {@code $x} after {@code #foreach ($x in ...)}. - */ - Runnable setVar(final String var, Object value); - - class PlainEvaluationContext implements EvaluationContext { - private final Map vars; - - PlainEvaluationContext(Map vars) { - this.vars = new TreeMap(vars); - } - - @Override - public Object getVar(String var) { - return vars.get(var); - } - - @Override - public boolean varIsDefined(String var) { - return vars.containsKey(var); - } - - @Override - public Runnable setVar(final String var, Object value) { - Runnable undo; - if (vars.containsKey(var)) { - final Object oldValue = vars.get(var); - undo = () -> vars.put(var, oldValue); - } else { - undo = () -> vars.remove(var); - } - vars.put(var, value); - return undo; - } - } -} diff --git a/value/src/main/java/com/google/auto/value/processor/escapevelocity/EvaluationException.java b/value/src/main/java/com/google/auto/value/processor/escapevelocity/EvaluationException.java deleted file mode 100644 index 7edc67412d..0000000000 --- a/value/src/main/java/com/google/auto/value/processor/escapevelocity/EvaluationException.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2015 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. - */ - -/* - * 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.google.auto.value.processor.escapevelocity; - -/** - * An exception that occurred while evaluating a template, such as an undefined variable reference - * or a division by zero. - * - * @author emcmanus@google.com (Éamonn McManus) - */ -public class EvaluationException extends RuntimeException { - private static final long serialVersionUID = 1; - - EvaluationException(String message) { - super(message); - } - - EvaluationException(String message, Throwable cause) { - super(cause); - } -} diff --git a/value/src/main/java/com/google/auto/value/processor/escapevelocity/ExpressionNode.java b/value/src/main/java/com/google/auto/value/processor/escapevelocity/ExpressionNode.java deleted file mode 100644 index c1112cd5b1..0000000000 --- a/value/src/main/java/com/google/auto/value/processor/escapevelocity/ExpressionNode.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright (C) 2015 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. - */ - -/* - * 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.google.auto.value.processor.escapevelocity; - -import com.google.auto.value.processor.escapevelocity.Parser.Operator; - -/** - * A node in the parse tree representing an expression. Expressions appear inside directives, - * specifically {@code #set}, {@code #if}, {@code #foreach}, and macro calls. Expressions can - * also appear inside indices in references, like {@code $x[$i]}. - * - * @author emcmanus@google.com (Éamonn McManus) - */ -abstract class ExpressionNode extends Node { - ExpressionNode(String resourceName, int lineNumber) { - super(resourceName, lineNumber); - } - - /** - * True if evaluating this expression yields a value that is considered true by Velocity's - * - * rules. A value is false if it is null or equal to Boolean.FALSE. - * Every other value is true. - * - *

Note that the text at the similar link - * here - * states that empty collections and empty strings are also considered false, but that is not - * true. - */ - boolean isTrue(EvaluationContext context) { - Object value = evaluate(context); - if (value instanceof Boolean) { - return (Boolean) value; - } else { - return value != null; - } - } - - /** - * True if this is a defined value and it evaluates to true. This is the same as {@link #isTrue} - * except that it is allowed for this to be undefined variable, in which it evaluates to false. - * The method is overridden for plain references so that undefined is the same as false. - * The reason is to support Velocity's idiom {@code #if ($var)}, where it is not an error - * if {@code $var} is undefined. - */ - boolean isDefinedAndTrue(EvaluationContext context) { - return isTrue(context); - } - - /** - * The integer result of evaluating this expression. - * - * @throws EvaluationException if evaluating the expression produces an exception, or if it - * yields a value that is not an integer. - */ - int intValue(EvaluationContext context) { - Object value = evaluate(context); - if (!(value instanceof Integer)) { - throw evaluationException("Arithemtic is only available on integers, not " + show(value)); - } - return (Integer) value; - } - - /** - * Returns a string representing the given value, for use in error messages. The string - * includes both the value's {@code toString()} and its type. - */ - private static String show(Object value) { - if (value == null) { - return "null"; - } else { - return value + " (a " + value.getClass().getName() + ")"; - } - } - - /** - * Represents all binary expressions. In {@code #set ($a = $b + $c)}, this will be the type - * of the node representing {@code $b + $c}. - */ - static class BinaryExpressionNode extends ExpressionNode { - final ExpressionNode lhs; - final Operator op; - final ExpressionNode rhs; - - BinaryExpressionNode(ExpressionNode lhs, Operator op, ExpressionNode rhs) { - super(lhs.resourceName, lhs.lineNumber); - this.lhs = lhs; - this.op = op; - this.rhs = rhs; - } - - @Override Object evaluate(EvaluationContext context) { - switch (op) { - case OR: - return lhs.isTrue(context) || rhs.isTrue(context); - case AND: - return lhs.isTrue(context) && rhs.isTrue(context); - case EQUAL: - return equal(context); - case NOT_EQUAL: - return !equal(context); - default: // fall out - } - int lhsInt = lhs.intValue(context); - int rhsInt = rhs.intValue(context); - switch (op) { - case LESS: - return lhsInt < rhsInt; - case LESS_OR_EQUAL: - return lhsInt <= rhsInt; - case GREATER: - return lhsInt > rhsInt; - case GREATER_OR_EQUAL: - return lhsInt >= rhsInt; - case PLUS: - return lhsInt + rhsInt; - case MINUS: - return lhsInt - rhsInt; - case TIMES: - return lhsInt * rhsInt; - case DIVIDE: - return lhsInt / rhsInt; - case REMAINDER: - return lhsInt % rhsInt; - default: - throw new AssertionError(op); - } - } - - /** - * Returns true if {@code lhs} and {@code rhs} are equal according to Velocity. - * - *

Velocity's definition - * of equality differs depending on whether the objects being compared are of the same - * class. If so, equality comes from {@code Object.equals} as you would expect. But if they - * are not of the same class, they are considered equal if their {@code toString()} values are - * equal. This means that integer 123 equals long 123L and also string {@code "123"}. It also - * means that equality isn't always transitive. For example, two StringBuilder objects each - * containing {@code "123"} will not compare equal, even though the string {@code "123"} - * compares equal to each of them. - */ - private boolean equal(EvaluationContext context) { - Object lhsValue = lhs.evaluate(context); - Object rhsValue = rhs.evaluate(context); - if (lhsValue == rhsValue) { - return true; - } - if (lhsValue == null || rhsValue == null) { - return false; - } - if (lhsValue.getClass().equals(rhsValue.getClass())) { - return lhsValue.equals(rhsValue); - } - // Funky equals behaviour specified by Velocity. - return lhsValue.toString().equals(rhsValue.toString()); - } - } - - /** - * A node in the parse tree representing an expression like {@code !$a}. - */ - static class NotExpressionNode extends ExpressionNode { - private final ExpressionNode expr; - - NotExpressionNode(ExpressionNode expr) { - super(expr.resourceName, expr.lineNumber); - this.expr = expr; - } - - @Override Object evaluate(EvaluationContext context) { - return !expr.isTrue(context); - } - } -} diff --git a/value/src/main/java/com/google/auto/value/processor/escapevelocity/Macro.java b/value/src/main/java/com/google/auto/value/processor/escapevelocity/Macro.java deleted file mode 100644 index be65d028f0..0000000000 --- a/value/src/main/java/com/google/auto/value/processor/escapevelocity/Macro.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) 2015 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. - */ - -/* - * 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.google.auto.value.processor.escapevelocity; - -import com.google.common.base.Verify; -import com.google.common.collect.ImmutableList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -/** - * A macro definition. Macros appear in templates using the syntax {@code #macro (m $x $y) ... #end} - * and each one produces an instance of this class. Evaluating a macro involves setting the - * parameters (here {$x $y)} and evaluating the macro body. Macro arguments are call-by-name, which - * means that we need to set each parameter variable to the node in the parse tree that corresponds - * to it, and arrange for that node to be evaluated when the variable is actually referenced. - * - * @author emcmanus@google.com (Éamonn McManus) - */ -class Macro { - private final int definitionLineNumber; - private final String name; - private final ImmutableList parameterNames; - private final Node body; - - Macro(int definitionLineNumber, String name, List parameterNames, Node body) { - this.definitionLineNumber = definitionLineNumber; - this.name = name; - this.parameterNames = ImmutableList.copyOf(parameterNames); - this.body = body; - } - - String name() { - return name; - } - - int parameterCount() { - return parameterNames.size(); - } - - Object evaluate(EvaluationContext context, List thunks) { - try { - Verify.verify(thunks.size() == parameterNames.size(), "Argument mistmatch for %s", name); - Map parameterThunks = new LinkedHashMap<>(); - for (int i = 0; i < parameterNames.size(); i++) { - parameterThunks.put(parameterNames.get(i), thunks.get(i)); - } - EvaluationContext newContext = new MacroEvaluationContext(parameterThunks, context); - return body.evaluate(newContext); - } catch (EvaluationException e) { - EvaluationException newException = new EvaluationException( - "In macro #" + name + " defined on line " + definitionLineNumber + ": " + e.getMessage()); - newException.setStackTrace(e.getStackTrace()); - throw e; - } - } - - /** - * The context for evaluation within macros. This wraps an existing {@code EvaluationContext} - * but intercepts reads of the macro's parameters so that they result in a call-by-name evaluation - * of whatever was passed as the parameter. For example, if you write... - *

{@code
-   * #macro (mymacro $x)
-   * $x $x
-   * #end
-   * #mymacro($foo.bar(23))
-   * }
- * ...then the {@code #mymacro} call will result in {@code $foo.bar(23)} being evaluated twice, - * once for each time {@code $x} appears. The way this works is that {@code $x} is a thunk. - * Historically a thunk is a piece of code to evaluate an expression in the context where it - * occurs, for call-by-name procedures as in Algol 60. Here, it is not exactly a piece of code, - * but it has the same responsibility. - */ - static class MacroEvaluationContext implements EvaluationContext { - private final Map parameterThunks; - private final EvaluationContext originalEvaluationContext; - - MacroEvaluationContext( - Map parameterThunks, EvaluationContext originalEvaluationContext) { - this.parameterThunks = parameterThunks; - this.originalEvaluationContext = originalEvaluationContext; - } - - @Override - public Object getVar(String var) { - Node thunk = parameterThunks.get(var); - if (thunk == null) { - return originalEvaluationContext.getVar(var); - } else { - // Evaluate the thunk in the context where it appeared, not in this context. Otherwise - // if you pass $x to a parameter called $x you would get an infinite recursion. Likewise - // if you had #macro(mymacro $x $y) and a call #mymacro($y 23), you would expect that $x - // would expand to whatever $y meant at the call site, rather than to the value of the $y - // parameter. - return thunk.evaluate(originalEvaluationContext); - } - } - - @Override - public boolean varIsDefined(String var) { - return parameterThunks.containsKey(var) || originalEvaluationContext.varIsDefined(var); - } - - @Override - public Runnable setVar(final String var, Object value) { - // Copy the behaviour that #set will shadow a macro parameter, even though the Velocity peeps - // seem to agree that that is not good. - final Node thunk = parameterThunks.get(var); - if (thunk == null) { - return originalEvaluationContext.setVar(var, value); - } else { - parameterThunks.remove(var); - final Runnable originalUndo = originalEvaluationContext.setVar(var, value); - return () -> { - originalUndo.run(); - parameterThunks.put(var, thunk); - }; - } - } - } -} diff --git a/value/src/main/java/com/google/auto/value/processor/escapevelocity/NOTICE b/value/src/main/java/com/google/auto/value/processor/escapevelocity/NOTICE deleted file mode 100644 index 6d27f8f890..0000000000 --- a/value/src/main/java/com/google/auto/value/processor/escapevelocity/NOTICE +++ /dev/null @@ -1,6 +0,0 @@ -Apache Velocity - -Copyright (C) 2000-2007 The Apache Software Foundation - -This product includes software developed at -The Apache Software Foundation (http://www.apache.org/). \ No newline at end of file diff --git a/value/src/main/java/com/google/auto/value/processor/escapevelocity/Node.java b/value/src/main/java/com/google/auto/value/processor/escapevelocity/Node.java deleted file mode 100644 index 5136fbf6de..0000000000 --- a/value/src/main/java/com/google/auto/value/processor/escapevelocity/Node.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2015 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. - */ - -/* - * 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.google.auto.value.processor.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(); - } - } -} diff --git a/value/src/main/java/com/google/auto/value/processor/escapevelocity/ParseException.java b/value/src/main/java/com/google/auto/value/processor/escapevelocity/ParseException.java deleted file mode 100644 index 45507f05bf..0000000000 --- a/value/src/main/java/com/google/auto/value/processor/escapevelocity/ParseException.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2015 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. - */ - -/* - * 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.google.auto.value.processor.escapevelocity; - -/** - * An exception that occurred while parsing a template. - * - * @author emcmanus@google.com (Éamonn McManus) - */ -public class ParseException extends RuntimeException { - private static final long serialVersionUID = 1; - - ParseException(String message, String resourceName, int lineNumber) { - super(message + ", " + where(resourceName, lineNumber)); - } - - ParseException(String message, String resourceName, int lineNumber, String context) { - super(message + ", " + where(resourceName, lineNumber) + ", at text starting: " + context); - } - - private static String where(String resourceName, int lineNumber) { - if (resourceName == null) { - return "on line " + lineNumber; - } else { - return "on line " + lineNumber + " of " + resourceName; - } - } -} diff --git a/value/src/main/java/com/google/auto/value/processor/escapevelocity/Parser.java b/value/src/main/java/com/google/auto/value/processor/escapevelocity/Parser.java deleted file mode 100644 index 73e360b475..0000000000 --- a/value/src/main/java/com/google/auto/value/processor/escapevelocity/Parser.java +++ /dev/null @@ -1,1064 +0,0 @@ -/* - * Copyright (C) 2015 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. - */ - -/* - * 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.google.auto.value.processor.escapevelocity; - -import com.google.auto.value.processor.escapevelocity.DirectiveNode.SetNode; -import com.google.auto.value.processor.escapevelocity.ExpressionNode.BinaryExpressionNode; -import com.google.auto.value.processor.escapevelocity.ExpressionNode.NotExpressionNode; -import com.google.auto.value.processor.escapevelocity.ReferenceNode.IndexReferenceNode; -import com.google.auto.value.processor.escapevelocity.ReferenceNode.MemberReferenceNode; -import com.google.auto.value.processor.escapevelocity.ReferenceNode.MethodReferenceNode; -import com.google.auto.value.processor.escapevelocity.ReferenceNode.PlainReferenceNode; -import com.google.auto.value.processor.escapevelocity.TokenNode.CommentTokenNode; -import com.google.auto.value.processor.escapevelocity.TokenNode.ElseIfTokenNode; -import com.google.auto.value.processor.escapevelocity.TokenNode.ElseTokenNode; -import com.google.auto.value.processor.escapevelocity.TokenNode.EndTokenNode; -import com.google.auto.value.processor.escapevelocity.TokenNode.EofNode; -import com.google.auto.value.processor.escapevelocity.TokenNode.ForEachTokenNode; -import com.google.auto.value.processor.escapevelocity.TokenNode.IfTokenNode; -import com.google.auto.value.processor.escapevelocity.TokenNode.MacroDefinitionTokenNode; -import com.google.auto.value.processor.escapevelocity.TokenNode.NestedTokenNode; -import com.google.common.base.CharMatcher; -import com.google.common.base.Verify; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.Iterables; -import com.google.common.primitives.Chars; -import com.google.common.primitives.Ints; -import java.io.IOException; -import java.io.LineNumberReader; -import java.io.Reader; - -/** - * A parser that reads input from the given {@link Reader} and parses it to produce a - * {@link Template}. - * - * @author emcmanus@google.com (Éamonn McManus) - */ -class Parser { - private static final int EOF = -1; - - private final LineNumberReader reader; - private final String resourceName; - private final Template.ResourceOpener resourceOpener; - - /** - * The invariant of this parser is that {@code c} is always the next character of interest. - * This means that we almost never have to "unget" a character by reading too far. For example, - * after we parse an integer, {@code c} will be the first character after the integer, which is - * exactly the state we will be in when there are no more digits. - * - *

Sometimes we need to read two characters ahead, and in that case we use {@link #pushback}. - */ - private int c; - - /** - * A single character of pushback. If this is not negative, the {@link #next()} method will - * return it instead of reading a character. - */ - private int pushback = -1; - - Parser(Reader reader, String resourceName, Template.ResourceOpener resourceOpener) - throws IOException { - this.reader = new LineNumberReader(reader); - this.reader.setLineNumber(1); - next(); - this.resourceName = resourceName; - this.resourceOpener = resourceOpener; - } - - /** - * Parse the input completely to produce a {@link Template}. - * - *

Parsing happens in two phases. First, we parse a sequence of "tokens", where tokens include - * entire references such as

-   *    ${x.foo()[23]}
-   * 
or entire directives such as
-   *    #set ($x = $y + $z)
-   * 
But tokens do not span complex constructs. For example,
-   *    #if ($x == $y) something #end
-   * 
is three tokens:
-   *    #if ($x == $y)
-   *    (literal text " something ")
-   *   #end
-   * 
- * - *

The second phase then takes the sequence of tokens and constructs a parse tree out of it. - * Some nodes in the parse tree will be unchanged from the token sequence, such as the

-   *    ${x.foo()[23]}
-   *    #set ($x = $y + $z)
-   * 
examples above. But a construct such as the {@code #if ... #end} mentioned above will - * become a single IfNode in the parse tree in the second phase. - * - *

The main reason for this approach is that Velocity has two kinds of lexical contexts. At the - * top level, there can be arbitrary literal text; references like ${x.foo()}; and - * directives like {@code #if} or {@code #set}. Inside the parentheses of a directive, however, - * neither arbitrary text nor directives can appear, but expressions can, so we need to tokenize - * the inside of

-   *    #if ($x == $a + $b)
-   * 
as the five tokens "$x", "==", "$a", "+", "$b". Rather than having a classical - * parser/lexer combination, where the lexer would need to switch between these two modes, we - * replace the lexer with an ad-hoc parser that is the first phase described above, and we - * define a simple parser over the resultant tokens that is the second phase. - */ - Template parse() throws IOException { - ImmutableList tokens = parseTokens(); - return new Reparser(tokens).reparse(); - } - - private ImmutableList parseTokens() throws IOException { - ImmutableList.Builder tokens = ImmutableList.builder(); - Node token; - do { - token = parseNode(); - tokens.add(token); - } while (!(token instanceof EofNode)); - return tokens.build(); - } - - private int lineNumber() { - return reader.getLineNumber(); - } - - /** - * Gets the next character from the reader and assigns it to {@code c}. If there are no more - * characters, sets {@code c} to {@link #EOF} if it is not already. - */ - private void next() throws IOException { - if (c != EOF) { - if (pushback < 0) { - c = reader.read(); - } else { - c = pushback; - pushback = -1; - } - } - } - - /** - * Saves the current character {@code c} to be read again, and sets {@code c} to the given - * {@code c1}. Suppose the text contains {@code xy} and we have just read {@code y}. - * So {@code c == 'y'}. Now if we execute {@code pushback('x')}, we will have - * {@code c == 'x'} and the next call to {@link #next()} will set {@code c == 'y'}. Subsequent - * calls to {@code next()} will continue reading from {@link #reader}. So the pushback - * essentially puts us back in the state we were in before we read {@code y}. - */ - private void pushback(int c1) { - pushback = c; - c = c1; - } - - /** - * If {@code c} is a space character, keeps reading until {@code c} is a non-space character or - * there are no more characters. - */ - private void skipSpace() throws IOException { - while (Character.isWhitespace(c)) { - next(); - } - } - - /** - * Gets the next character from the reader, and if it is a space character, keeps reading until - * a non-space character is found. - */ - private void nextNonSpace() throws IOException { - next(); - skipSpace(); - } - - /** - * Skips any space in the reader, and then throws an exception if the first non-space character - * found is not the expected one. Sets {@code c} to the first character after that expected one. - */ - private void expect(char expected) throws IOException { - skipSpace(); - if (c == expected) { - next(); - } else { - throw parseException("Expected " + expected); - } - } - - /** - * Parses a single node from the reader, as part of the first parsing phase. - *
{@code
-   *