diff --git a/docs/src/orchid/resources/wiki/guide/installation.md b/docs/src/orchid/resources/wiki/guide/installation.md index d922580c6..cee1b8133 100644 --- a/docs/src/orchid/resources/wiki/guide/installation.md +++ b/docs/src/orchid/resources/wiki/guide/installation.md @@ -86,4 +86,5 @@ All the settings are set during the construction of the `PebbleEngine` object. | `strictVariables` | If set to true, Pebble will throw an exception if you try to access a variable or attribute that does not exist (or an attribute of a null variable). If set to false, your template will treat non-existing variables/attributes as null without ever skipping a beat. | `false` | | `allowUnsafeMethods` | If set to false, Pebble will throw an exception if you try to access unsafe methods. Unsafe methods are defined [here](https://github.com/PebbleTemplates/pebble/tree/master/pebble/src/main/resources/unsafeMethods.properties) | `true` in 2.x, 'false' in v3.x | | `literalDecimalTreatedAsInteger` | option for toggling to enable/disable literal decimal treated as integer | `false` | +| `literalNumbersAsBigDecimals` | option for toggling to enable/disable literal numbers treated as BigDecimals | `false` | | `greedyMatchMethod` | option for toggling to enable/disable greedy matching mode for finding java method. Reduce the limit of the parameter type, try to find other method which has compatible parameter types. | `false` | diff --git a/pebble/src/main/java/com/mitchellbosecke/pebble/PebbleEngine.java b/pebble/src/main/java/com/mitchellbosecke/pebble/PebbleEngine.java index 276588c69..55b15c1f1 100644 --- a/pebble/src/main/java/com/mitchellbosecke/pebble/PebbleEngine.java +++ b/pebble/src/main/java/com/mitchellbosecke/pebble/PebbleEngine.java @@ -164,7 +164,7 @@ private PebbleTemplate getPebbleTemplate(String templateName, Loader loader, Obj Parser parser = new ParserImpl(this.extensionRegistry.getUnaryOperators(), this.extensionRegistry.getBinaryOperators(), this.extensionRegistry.getTokenParsers(), - this.parserOptions); + this.parserOptions); RootNode root = parser.parse(tokenStream); PebbleTemplateImpl instance = new PebbleTemplateImpl(this, root, templateName); @@ -291,6 +291,8 @@ public static class Builder { private boolean allowOverrideCoreOperators = false; + private boolean literalNumbersAsBigDecimals = false; + /** * Creates the builder. */ @@ -499,6 +501,18 @@ public Builder literalDecimalTreatedAsInteger(boolean literalDecimalTreatedAsInt return this; } + /** + * Enable/disable treat literal numbers as BigDecimals. Default is disabled, treated as Long/Double. + * + * @param literalNumbersAsBigDecimals toggle to enable/disable literal numbers treated as + * BigDecimals + * @return This builder object + */ + public Builder literalNumbersAsBigDecimals(boolean literalNumbersAsBigDecimals) { + this.literalNumbersAsBigDecimals = literalNumbersAsBigDecimals; + return this; + } + /** * Enable/disable greedy matching mode for finding java method. Default is disabled. If enabled, * when can not find perfect method (method name, parameter length and parameter type are all @@ -568,6 +582,7 @@ public PebbleEngine build() { ParserOptions parserOptions = new ParserOptions(); parserOptions.setLiteralDecimalTreatedAsInteger(this.literalDecimalTreatedAsInteger); + parserOptions.setLiteralNumbersAsBigDecimals(this.literalNumbersAsBigDecimals); EvaluationOptions evaluationOptions = new EvaluationOptions(); evaluationOptions.setAllowUnsafeMethods(this.allowUnsafeMethods); diff --git a/pebble/src/main/java/com/mitchellbosecke/pebble/node/expression/LiteralBigDecimalExpression.java b/pebble/src/main/java/com/mitchellbosecke/pebble/node/expression/LiteralBigDecimalExpression.java new file mode 100644 index 000000000..37a0a44bf --- /dev/null +++ b/pebble/src/main/java/com/mitchellbosecke/pebble/node/expression/LiteralBigDecimalExpression.java @@ -0,0 +1,47 @@ +/* + * This file is part of Pebble. + * + * Copyright (c) 2014 by Mitchell Bösecke + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +package com.mitchellbosecke.pebble.node.expression; + +import com.mitchellbosecke.pebble.extension.NodeVisitor; +import com.mitchellbosecke.pebble.template.EvaluationContextImpl; +import com.mitchellbosecke.pebble.template.PebbleTemplateImpl; + +import java.math.BigDecimal; + +public class LiteralBigDecimalExpression implements Expression { + + private final BigDecimal value; + private final int lineNumber; + + public LiteralBigDecimalExpression(BigDecimal value, int lineNumber) { + this.value = value; + this.lineNumber = lineNumber; + } + + @Override + public void accept(NodeVisitor visitor) { + visitor.visit(this); + } + + @Override + public BigDecimal evaluate(PebbleTemplateImpl self, EvaluationContextImpl context) { + return this.value; + } + + @Override + public int getLineNumber() { + return this.lineNumber; + } + + @Override + public String toString() { + return this.value.toString(); + } + +} diff --git a/pebble/src/main/java/com/mitchellbosecke/pebble/parser/ExpressionParser.java b/pebble/src/main/java/com/mitchellbosecke/pebble/parser/ExpressionParser.java index bcf8f9a09..5b36cc5e4 100644 --- a/pebble/src/main/java/com/mitchellbosecke/pebble/parser/ExpressionParser.java +++ b/pebble/src/main/java/com/mitchellbosecke/pebble/parser/ExpressionParser.java @@ -26,6 +26,7 @@ import com.mitchellbosecke.pebble.node.expression.FilterInvocationExpression; import com.mitchellbosecke.pebble.node.expression.FunctionOrMacroInvocationExpression; import com.mitchellbosecke.pebble.node.expression.GetAttributeExpression; +import com.mitchellbosecke.pebble.node.expression.LiteralBigDecimalExpression; import com.mitchellbosecke.pebble.node.expression.LiteralBooleanExpression; import com.mitchellbosecke.pebble.node.expression.LiteralDoubleExpression; import com.mitchellbosecke.pebble.node.expression.LiteralIntegerExpression; @@ -41,6 +42,8 @@ import com.mitchellbosecke.pebble.operator.Associativity; import com.mitchellbosecke.pebble.operator.BinaryOperator; import com.mitchellbosecke.pebble.operator.UnaryOperator; + +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -306,14 +309,18 @@ private Expression subparseExpression() { case NUMBER: final String numberValue = token.getValue(); - if (numberValue.contains(".")) { - node = new LiteralDoubleExpression(Double.valueOf(numberValue), token.getLineNumber()); + if (this.parserOptions.isLiteralNumbersAsBigDecimals()) { + node = new LiteralBigDecimalExpression(new BigDecimal(numberValue), token.getLineNumber()); } else { - if (this.parserOptions.isLiteralDecimalTreatedAsInteger()) { - node = new LiteralIntegerExpression(Integer.valueOf(numberValue), - token.getLineNumber()); + if (numberValue.contains(".")) { + node = new LiteralDoubleExpression(Double.valueOf(numberValue), token.getLineNumber()); } else { - node = new LiteralLongExpression(Long.valueOf(numberValue), token.getLineNumber()); + if (this.parserOptions.isLiteralDecimalTreatedAsInteger()) { + node = new LiteralIntegerExpression(Integer.valueOf(numberValue), + token.getLineNumber()); + } else { + node = new LiteralLongExpression(Long.valueOf(numberValue), token.getLineNumber()); + } } } this.stream.next(); diff --git a/pebble/src/main/java/com/mitchellbosecke/pebble/parser/ParserOptions.java b/pebble/src/main/java/com/mitchellbosecke/pebble/parser/ParserOptions.java index ce7d7ae58..ec0ca278e 100644 --- a/pebble/src/main/java/com/mitchellbosecke/pebble/parser/ParserOptions.java +++ b/pebble/src/main/java/com/mitchellbosecke/pebble/parser/ParserOptions.java @@ -9,6 +9,8 @@ public class ParserOptions { private boolean literalDecimalTreatedAsInteger; + private boolean literalNumbersAsBigDecimals; + public boolean isLiteralDecimalTreatedAsInteger() { return literalDecimalTreatedAsInteger; } @@ -17,4 +19,15 @@ public ParserOptions setLiteralDecimalTreatedAsInteger(boolean literalDecimalTre this.literalDecimalTreatedAsInteger = literalDecimalTreatedAsInteger; return this; } + + public boolean isLiteralNumbersAsBigDecimals() { + return literalNumbersAsBigDecimals; + } + + public ParserOptions setLiteralNumbersAsBigDecimals(boolean literalNumbersAsBigDecimals) { + this.literalNumbersAsBigDecimals = literalNumbersAsBigDecimals; + return this; + } + + } diff --git a/pebble/src/test/java/com/mitchellbosecke/pebble/GetAttributeTest.java b/pebble/src/test/java/com/mitchellbosecke/pebble/GetAttributeTest.java index ce2e338e9..61c4fab0e 100644 --- a/pebble/src/test/java/com/mitchellbosecke/pebble/GetAttributeTest.java +++ b/pebble/src/test/java/com/mitchellbosecke/pebble/GetAttributeTest.java @@ -568,6 +568,35 @@ void testBeanMethodWithGreedyMatchArgument() throws PebbleException, IOException assertEquals("hello 2 2", writer.toString()); } + @Test + void testBeanMethodWithNumberLiteralsAsBigDecimals() throws PebbleException, IOException { + PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader()) + .strictVariables(true).literalNumbersAsBigDecimals(true) + .build(); + + PebbleTemplate template = pebble.getTemplate("hello {{ 1234567890123456789012345678901234567890 }}"); + Map context = new HashMap<>(); + + Writer writer = new StringWriter(); + template.evaluate(writer, context); + assertEquals("hello 1234567890123456789012345678901234567890", writer.toString()); + } + + @Test + void testBeanMethodWithoutNumberLiteralsAsBigDecimals() throws PebbleException, IOException { + assertThrows(NumberFormatException.class, () -> { + PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader()) + .strictVariables(true).literalNumbersAsBigDecimals(false) + .build(); + + PebbleTemplate template = pebble.getTemplate("hello {{ 1234567890123456789012345678901234567890 }}"); + Map context = new HashMap<>(); + + Writer writer = new StringWriter(); + template.evaluate(writer, context); + }); + } + @Test void testBeanMethodWithOverloadedArgument() throws PebbleException, IOException { PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader())