Skip to content

Commit

Permalink
Allow treating all number literals as BigDecimals to avoid NumberForm…
Browse files Browse the repository at this point in the history
…atExceptions (#503)

* enhance Parser Options: add possibility to treat all number literals as BigDecimals

* code style fix

* update docs - add literalNumbersAsBigDecimals
  • Loading branch information
eXsio authored May 1, 2020
1 parent 7e536a8 commit c906e51
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 7 deletions.
1 change: 1 addition & 0 deletions docs/src/orchid/resources/wiki/guide/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` |
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -291,6 +291,8 @@ public static class Builder {

private boolean allowOverrideCoreOperators = false;

private boolean literalNumbersAsBigDecimals = false;

/**
* Creates the builder.
*/
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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<BigDecimal> {

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();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ public class ParserOptions {

private boolean literalDecimalTreatedAsInteger;

private boolean literalNumbersAsBigDecimals;

public boolean isLiteralDecimalTreatedAsInteger() {
return literalDecimalTreatedAsInteger;
}
Expand All @@ -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;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, Object> 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<String, Object> 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())
Expand Down

0 comments on commit c906e51

Please sign in to comment.