From c040e6f21fd6d11837a7f0a7d6a2ce27f26daca4 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 31 Aug 2015 13:59:20 -0400 Subject: [PATCH 0001/2465] bump jinjava version in benchmark --- benchmark/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/pom.xml b/benchmark/pom.xml index 70d78cc99..8caae1441 100644 --- a/benchmark/pom.xml +++ b/benchmark/pom.xml @@ -41,7 +41,7 @@ com.hubspot.jinjava jinjava - 2.0.8-SNAPSHOT + 2.0.10-SNAPSHOT commons-io From a8b558b797e06947f91f04ea26c9480b0c7cca2e Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 31 Aug 2015 15:55:59 -0400 Subject: [PATCH 0002/2465] minor code cleanup, more final fields, 1 fewer string copy --- .../java/com/hubspot/jinjava/tree/Node.java | 31 ++++++++-------- .../jinjava/tree/parse/TokenScanner.java | 9 +++-- .../hubspot/jinjava/util/CharArrayUtils.java | 22 +++++++++++ .../jinjava/util/HelperStringTokenizer.java | 37 ++++++++++--------- .../jinjava/util/CharArrayUtilsTest.java | 20 ++++++++++ 5 files changed, 82 insertions(+), 37 deletions(-) create mode 100644 src/main/java/com/hubspot/jinjava/util/CharArrayUtils.java create mode 100644 src/test/java/com/hubspot/jinjava/util/CharArrayUtilsTest.java diff --git a/src/main/java/com/hubspot/jinjava/tree/Node.java b/src/main/java/com/hubspot/jinjava/tree/Node.java index 0a8132afe..f2062c53c 100644 --- a/src/main/java/com/hubspot/jinjava/tree/Node.java +++ b/src/main/java/com/hubspot/jinjava/tree/Node.java @@ -1,17 +1,17 @@ /********************************************************************** -Copyright (c) 2014 HubSpot 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. + * Copyright (c) 2014 HubSpot 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.hubspot.jinjava.tree; @@ -27,14 +27,13 @@ public abstract class Node implements Serializable { private static final long serialVersionUID = 7323842986596895498L; + private final Token master; + private final int lineNumber; private int level; - private int lineNumber; private Node parent = null; private LinkedList children = new LinkedList(); - private Token master; - public Node(Token master, int lineNumber) { this.master = master; this.lineNumber = lineNumber; diff --git a/src/main/java/com/hubspot/jinjava/tree/parse/TokenScanner.java b/src/main/java/com/hubspot/jinjava/tree/parse/TokenScanner.java index 015b88fd3..bacc5b236 100644 --- a/src/main/java/com/hubspot/jinjava/tree/parse/TokenScanner.java +++ b/src/main/java/com/hubspot/jinjava/tree/parse/TokenScanner.java @@ -23,6 +23,7 @@ import static com.hubspot.jinjava.tree.parse.TokenScannerSymbols.TOKEN_POSTFIX; import static com.hubspot.jinjava.tree.parse.TokenScannerSymbols.TOKEN_PREFIX; import static com.hubspot.jinjava.tree.parse.TokenScannerSymbols.TOKEN_TAG; +import static com.hubspot.jinjava.util.CharArrayUtils.charArrayRegionMatches; import com.google.common.collect.AbstractIterator; import com.hubspot.jinjava.JinjavaConfig; @@ -32,11 +33,12 @@ public class TokenScanner extends AbstractIterator { private final JinjavaConfig config; private final char[] is; + private final int length; + private int currPost = 0; private int tokenStart = 0; private int tokenLength = 0; private int tokenKind = -1; - private int length = 0; private int lastStart = 0; private int inComment = 0; private int inRaw = 0; @@ -48,7 +50,8 @@ public TokenScanner(String input, JinjavaConfig config) { this.config = config; is = input.toCharArray(); - length = input.length(); + length = is.length; + currPost = 0; tokenStart = 0; tokenKind = -1; @@ -225,7 +228,7 @@ private boolean isEndRaw() { return false; } - return "endraw".equals(String.valueOf(is, pos - 1, 6)); + return charArrayRegionMatches(is, pos - 1, "endraw"); } private Token getEndToken() { diff --git a/src/main/java/com/hubspot/jinjava/util/CharArrayUtils.java b/src/main/java/com/hubspot/jinjava/util/CharArrayUtils.java new file mode 100644 index 000000000..2c24dd939 --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/util/CharArrayUtils.java @@ -0,0 +1,22 @@ +package com.hubspot.jinjava.util; + +public class CharArrayUtils { + + public static boolean charArrayRegionMatches(char[] value, int startPos, CharSequence toMatch) { + int matchLen = toMatch.length(), + endPos = startPos + matchLen; + + if (endPos > value.length) { + return false; + } + + for (int matchIndex = 0, i = startPos; i < endPos; i++, matchIndex++) { + if (value[i] != toMatch.charAt(matchIndex)) { + return false; + } + } + + return true; + } + +} diff --git a/src/main/java/com/hubspot/jinjava/util/HelperStringTokenizer.java b/src/main/java/com/hubspot/jinjava/util/HelperStringTokenizer.java index f4c26cdb3..4b0da1cf3 100644 --- a/src/main/java/com/hubspot/jinjava/util/HelperStringTokenizer.java +++ b/src/main/java/com/hubspot/jinjava/util/HelperStringTokenizer.java @@ -1,17 +1,17 @@ /********************************************************************** -Copyright (c) 2014 HubSpot 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. + * Copyright (c) 2014 HubSpot 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.hubspot.jinjava.util; @@ -28,18 +28,19 @@ */ public class HelperStringTokenizer extends AbstractIterator { - private char[] helpers; + private final char[] helpers; + private final int length; + private int currPost = 0; private int tokenStart = 0; - private int length = 0; private char lastChar = ' '; private boolean useComma = false; private char quoteChar = 0; private boolean inQuote = false; - public HelperStringTokenizer(String tobeToken) { - helpers = tobeToken.toCharArray(); - length = tobeToken.length(); + public HelperStringTokenizer(String s) { + helpers = s.toCharArray(); + length = s.length(); } /** diff --git a/src/test/java/com/hubspot/jinjava/util/CharArrayUtilsTest.java b/src/test/java/com/hubspot/jinjava/util/CharArrayUtilsTest.java new file mode 100644 index 000000000..1c3e441a3 --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/util/CharArrayUtilsTest.java @@ -0,0 +1,20 @@ +package com.hubspot.jinjava.util; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; + +public class CharArrayUtilsTest { + + @Test + public void testCharArrayRegionMatches() { + char[] s = "hello {% raw %} world {% endraw %}".toCharArray(); + + assertThat(CharArrayUtils.charArrayRegionMatches(s, 0, "raw")).isFalse(); + assertThat(CharArrayUtils.charArrayRegionMatches(s, 9, "raw")).isTrue(); + + assertThat(CharArrayUtils.charArrayRegionMatches(s, 25, "endraw")).isTrue(); + assertThat(CharArrayUtils.charArrayRegionMatches(s, 29, "endraw")).isFalse(); + } + +} From f10c127223255ceaea5b9b645ace3f3be276ad7c Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 31 Aug 2015 17:21:00 -0400 Subject: [PATCH 0003/2465] faster transform from snake to camel case --- .../jinjava/el/ext/JinjavaBeanELResolver.java | 108 ++++++++---------- 1 file changed, 48 insertions(+), 60 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java b/src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java index 7f91621ed..5fabbfd41 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java @@ -1,72 +1,60 @@ package com.hubspot.jinjava.el.ext; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - import javax.el.BeanELResolver; import javax.el.ELContext; +import com.google.common.base.CaseFormat; + /** * {@link BeanELResolver} supporting snake case property names. */ public class JinjavaBeanELResolver extends BeanELResolver { - /** - * Pattern to convert snake case to property names. - */ - private static final Pattern SNAKE_CASE = Pattern.compile("_([^_]?)"); - - /** - * Creates a new read/write {@link JinjavaBeanELResolver}. - */ - public JinjavaBeanELResolver() { - } - - /** - * Creates a new {@link JinjavaBeanELResolver} whose read-only status is determined by the given parameter. - */ - public JinjavaBeanELResolver(boolean readOnly) { - super(readOnly); - } - - @Override - public Class getType(ELContext context, Object base, Object property) { - return super.getType(context, base, transformPropertyName(property)); - } - - @Override - public Object getValue(ELContext context, Object base, Object property) { - return super.getValue(context, base, transformPropertyName(property)); - } - - @Override - public boolean isReadOnly(ELContext context, Object base, Object property) { - return super.isReadOnly(context, base, transformPropertyName(property)); - } - - @Override - public void setValue(ELContext context, Object base, Object property, Object value) { - super.setValue(context, base, transformPropertyName(property), value); - } - - /** - * Transform snake case to property name. - */ - private String transformPropertyName(Object property) { - if (property == null) { - return null; - } - - String name = property.toString(); - Matcher m = SNAKE_CASE.matcher(name); - - StringBuffer result = new StringBuffer(name.length()); - while (m.find()) { - String replacement = m.group(1).toUpperCase(); - m.appendReplacement(result, replacement); - } - m.appendTail(result); - return result.toString(); - } + /** + * Creates a new read/write {@link JinjavaBeanELResolver}. + */ + public JinjavaBeanELResolver() {} + + /** + * Creates a new {@link JinjavaBeanELResolver} whose read-only status is determined by the given parameter. + */ + public JinjavaBeanELResolver(boolean readOnly) { + super(readOnly); + } + + @Override + public Class getType(ELContext context, Object base, Object property) { + return super.getType(context, base, transformPropertyName(property)); + } + + @Override + public Object getValue(ELContext context, Object base, Object property) { + return super.getValue(context, base, transformPropertyName(property)); + } + + @Override + public boolean isReadOnly(ELContext context, Object base, Object property) { + return super.isReadOnly(context, base, transformPropertyName(property)); + } + + @Override + public void setValue(ELContext context, Object base, Object property, Object value) { + super.setValue(context, base, transformPropertyName(property), value); + } + + /** + * Transform snake case to property name. + */ + private String transformPropertyName(Object property) { + if (property == null) { + return null; + } + + String propertyStr = property.toString(); + if (propertyStr.indexOf('_') == -1) { + return propertyStr; + } + return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, propertyStr); + } } From 27380290fd03147579fa44cf1179c98b9ef4ac61 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 31 Aug 2015 17:21:07 -0400 Subject: [PATCH 0004/2465] fix trailing spaces --- src/main/java/com/hubspot/jinjava/tree/Node.java | 6 +++--- .../com/hubspot/jinjava/util/HelperStringTokenizer.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/tree/Node.java b/src/main/java/com/hubspot/jinjava/tree/Node.java index f2062c53c..834c509a8 100644 --- a/src/main/java/com/hubspot/jinjava/tree/Node.java +++ b/src/main/java/com/hubspot/jinjava/tree/Node.java @@ -1,12 +1,12 @@ /********************************************************************** * Copyright (c) 2014 HubSpot 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. diff --git a/src/main/java/com/hubspot/jinjava/util/HelperStringTokenizer.java b/src/main/java/com/hubspot/jinjava/util/HelperStringTokenizer.java index 4b0da1cf3..c260699d2 100644 --- a/src/main/java/com/hubspot/jinjava/util/HelperStringTokenizer.java +++ b/src/main/java/com/hubspot/jinjava/util/HelperStringTokenizer.java @@ -1,12 +1,12 @@ /********************************************************************** * Copyright (c) 2014 HubSpot 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. From 942a8941ddee0b06e5fbdf4cdfd37c59d0ff4e28 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 31 Aug 2015 17:59:42 -0400 Subject: [PATCH 0005/2465] more code cleanup; remove ObjectValue class in favor of simple java.util.Objects.toString() --- .../jinjava/interpret/JinjavaInterpreter.java | 54 ++++++++++--------- .../hubspot/jinjava/lib/filter/CutFilter.java | 31 +++++------ .../jinjava/lib/filter/EscapeFilter.java | 31 +++++------ .../com/hubspot/jinjava/lib/tag/BlockTag.java | 34 ++++++------ .../hubspot/jinjava/lib/tag/IncludeTag.java | 27 +++++----- .../hubspot/jinjava/tree/ExpressionNode.java | 31 +++++------ .../jinjava/util/HelperStringTokenizer.java | 14 ++--- .../com/hubspot/jinjava/util/ObjectValue.java | 29 ---------- 8 files changed, 114 insertions(+), 137 deletions(-) delete mode 100644 src/main/java/com/hubspot/jinjava/util/ObjectValue.java diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index 739b8e250..85b4b4a22 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -1,17 +1,17 @@ /********************************************************************** -Copyright (c) 2014 HubSpot 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. + * Copyright (c) 2014 HubSpot 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.hubspot.jinjava.interpret; @@ -22,6 +22,7 @@ import java.util.Collections; import java.util.LinkedList; import java.util.List; +import java.util.Objects; import java.util.Stack; import org.apache.commons.lang3.StringUtils; @@ -156,7 +157,7 @@ String resolveBlockStubs(CharSequence content) { if (block != null) { List superBlock = Iterables.get(blockChain, 1, null); - context.put("__superbl0ck__", superBlock); + context.put(BLOCK_SUPER_REF, superBlock); StringBuilder blockValueBuilder = new StringBuilder(); @@ -166,7 +167,7 @@ String resolveBlockStubs(CharSequence content) { blockValue = resolveBlockStubs(blockValueBuilder); - context.remove("__superbl0ck__"); + context.remove(BLOCK_SUPER_REF); } result.append(content.subSequence(pos, start)); @@ -235,7 +236,7 @@ public Object resolveObject(String variable, int lineNumber) { * @return resolved value for variable */ public String resolveString(String variable, int lineNumber) { - return java.util.Objects.toString(resolveObject(variable, lineNumber), ""); + return Objects.toString(resolveObject(variable, lineNumber), ""); } public Context getContext() { @@ -253,8 +254,10 @@ public JinjavaConfig getConfig() { /** * Resolve expression against current context. * - * @param expression Jinja expression. - * @param lineNumber Line number of expression. + * @param expression + * Jinja expression. + * @param lineNumber + * Line number of expression. * @return Value of expression. */ public Object resolveELExpression(String expression, int lineNumber) { @@ -266,8 +269,10 @@ public Object resolveELExpression(String expression, int lineNumber) { /** * Resolve property of bean. * - * @param object Bean. - * @param propertyName Name of property to resolve. + * @param object + * Bean. + * @param propertyName + * Name of property to resolve. * @return Value of property. */ public Object resolveProperty(Object object, String propertyName) { @@ -277,8 +282,10 @@ public Object resolveProperty(Object object, String propertyName) { /** * Resolve property of bean. * - * @param object Bean. - * @param propertyNames Names of properties to resolve recursively. + * @param object + * Bean. + * @param propertyNames + * Names of properties to resolve recursively. * @return Value of property. */ public Object resolveProperty(Object object, List propertyNames) { @@ -322,9 +329,8 @@ public static void popCurrent() { } } - public static final String INSERT_FLAG = "'IS\"INSERT"; - public static final String BLOCK_STUB_START = "___bl0ck___~"; public static final String BLOCK_STUB_END = "~"; + public static final String BLOCK_SUPER_REF = "__superbl0ck__"; } diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/CutFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/CutFilter.java index 5a96ce5af..109ccefa9 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/CutFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/CutFilter.java @@ -1,20 +1,22 @@ /********************************************************************** -Copyright (c) 2014 HubSpot 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. + * Copyright (c) 2014 HubSpot 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.hubspot.jinjava.lib.filter; +import java.util.Objects; + import org.apache.commons.lang3.StringUtils; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; @@ -22,7 +24,6 @@ import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; -import com.hubspot.jinjava.util.ObjectValue; @JinjavaDoc( value = "Removes a string from the value from another string", @@ -43,7 +44,7 @@ public Object filter(Object object, JinjavaInterpreter interpreter, String... ar throw new InterpretException("filter cut expects 1 arg >>> " + arg.length); } String cutee = arg[0]; - String origin = ObjectValue.printable(object); + String origin = Objects.toString(object, ""); return StringUtils.replace(origin, cutee, ""); } diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/EscapeFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/EscapeFilter.java index 58a296819..fb33641e8 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/EscapeFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/EscapeFilter.java @@ -1,21 +1,22 @@ /********************************************************************** -Copyright (c) 2014 HubSpot 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. + * Copyright (c) 2014 HubSpot 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.hubspot.jinjava.lib.filter; -import com.hubspot.jinjava.util.ObjectValue; +import java.util.Objects; + import org.apache.commons.lang3.StringUtils; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; @@ -59,7 +60,7 @@ public static String escapeHtmlEntities(String input) { @Override public Object filter(Object object, JinjavaInterpreter interpreter, String... arg) { - return escapeHtmlEntities(ObjectValue.printable(object)); + return escapeHtmlEntities(Objects.toString(object, "")); } @Override diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/BlockTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/BlockTag.java index 2c4dce2f7..1d060df97 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/BlockTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/BlockTag.java @@ -1,22 +1,20 @@ /********************************************************************** -Copyright (c) 2014 HubSpot 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. + * Copyright (c) 2014 HubSpot 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.hubspot.jinjava.lib.tag; -import java.util.List; - import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaParam; import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; @@ -50,12 +48,12 @@ public class BlockTag implements Tag { @Override public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { - List helper = new HelperStringTokenizer(tagNode.getHelpers()).allTokens(); - if (helper.isEmpty()) { + HelperStringTokenizer tagData = new HelperStringTokenizer(tagNode.getHelpers()); + if (!tagData.hasNext()) { throw new InterpretException("Tag 'block' expects an identifier", tagNode.getLineNumber()); } - String blockName = WhitespaceUtils.unquote(helper.get(0)); + String blockName = WhitespaceUtils.unquote(tagData.next()); interpreter.addBlock(blockName, tagNode.getChildren()); return JinjavaInterpreter.BLOCK_STUB_START + blockName + JinjavaInterpreter.BLOCK_STUB_END; diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java index b2e911bd6..c54021435 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java @@ -1,17 +1,17 @@ /********************************************************************** -Copyright (c) 2014 HubSpot 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. + * Copyright (c) 2014 HubSpot 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.hubspot.jinjava.lib.tag; @@ -66,7 +66,6 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { JinjavaInterpreter child = new JinjavaInterpreter(interpreter); child.getContext().addDependency("coded_files", templateFile); child.getContext().put(INCLUDE_PATH_PROPERTY, path); - interpreter.getContext().put(JinjavaInterpreter.INSERT_FLAG, true); String result = child.render(node); interpreter.getErrors().addAll(child.getErrors()); diff --git a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java index d2f7a67cc..b30c57a3e 100644 --- a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java @@ -1,20 +1,22 @@ /********************************************************************** -Copyright (c) 2014 HubSpot 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. + * Copyright (c) 2014 HubSpot 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.hubspot.jinjava.tree; +import java.util.Objects; + import org.apache.commons.lang3.StringUtils; import com.hubspot.jinjava.interpret.JinjavaInterpreter; @@ -22,7 +24,6 @@ import com.hubspot.jinjava.lib.tag.AutoEscapeTag; import com.hubspot.jinjava.tree.parse.ExpressionToken; import com.hubspot.jinjava.util.Logging; -import com.hubspot.jinjava.util.ObjectValue; public class ExpressionNode extends Node { private static final long serialVersionUID = 341642231109911346L; @@ -38,7 +39,7 @@ public ExpressionNode(ExpressionToken token) { public String render(JinjavaInterpreter interpreter) { Object var = interpreter.resolveELExpression(master.getExpr(), getLineNumber()); - String result = ObjectValue.printable(var); + String result = Objects.toString(var, ""); if (!StringUtils.equals(result, master.getImage()) && StringUtils.contains(result, "{{")) { try { diff --git a/src/main/java/com/hubspot/jinjava/util/HelperStringTokenizer.java b/src/main/java/com/hubspot/jinjava/util/HelperStringTokenizer.java index c260699d2..b13f15820 100644 --- a/src/main/java/com/hubspot/jinjava/util/HelperStringTokenizer.java +++ b/src/main/java/com/hubspot/jinjava/util/HelperStringTokenizer.java @@ -28,7 +28,7 @@ */ public class HelperStringTokenizer extends AbstractIterator { - private final char[] helpers; + private final char[] value; private final int length; private int currPost = 0; @@ -39,8 +39,8 @@ public class HelperStringTokenizer extends AbstractIterator { private boolean inQuote = false; public HelperStringTokenizer(String s) { - helpers = s.toCharArray(); - length = s.length(); + value = s.toCharArray(); + length = value.length; } /** @@ -60,7 +60,7 @@ protected String computeNext() { String token; while (currPost < length) { token = makeToken(); - lastChar = helpers[currPost - 1]; + lastChar = value[currPost - 1]; if (token != null) { return token; } @@ -71,7 +71,7 @@ protected String computeNext() { } private String makeToken() { - char c = helpers[currPost++]; + char c = value[currPost++]; if (c == '"' || c == '\'') { if (inQuote) { if (quoteChar == c) { @@ -92,7 +92,7 @@ private String makeToken() { } private String getEndToken() { - return String.copyValueOf(helpers, tokenStart, currPost - tokenStart); + return String.copyValueOf(value, tokenStart, currPost - tokenStart); } private String newToken() { @@ -102,7 +102,7 @@ private String newToken() { return null; } // startChar = -1;//change to save quote in helper - return String.copyValueOf(helpers, lastStart, currPost - lastStart - 1); + return String.copyValueOf(value, lastStart, currPost - lastStart - 1); } public List allTokens() { diff --git a/src/main/java/com/hubspot/jinjava/util/ObjectValue.java b/src/main/java/com/hubspot/jinjava/util/ObjectValue.java deleted file mode 100644 index 241228075..000000000 --- a/src/main/java/com/hubspot/jinjava/util/ObjectValue.java +++ /dev/null @@ -1,29 +0,0 @@ -/********************************************************************** -Copyright (c) 2014 HubSpot 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.hubspot.jinjava.util; - -import java.util.Objects; - -public final class ObjectValue { - - private ObjectValue() { - } - - public static String printable(Object variable) { - return Objects.toString(variable, ""); - } - -} From 4f057875dcdde87b241223d9aacf30347e3445b2 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 31 Aug 2015 18:00:54 -0400 Subject: [PATCH 0006/2465] fix newlines again --- src/main/java/com/hubspot/jinjava/lib/tag/BlockTag.java | 6 +++--- src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/BlockTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/BlockTag.java index 1d060df97..7ab6513dc 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/BlockTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/BlockTag.java @@ -1,12 +1,12 @@ /********************************************************************** * Copyright (c) 2014 HubSpot 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. diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java index c54021435..a5fd0ca2a 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java @@ -1,12 +1,12 @@ /********************************************************************** * Copyright (c) 2014 HubSpot 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. From ee2a3b041b32e19fab233233c19f3676584f88bc Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 31 Aug 2015 18:15:41 -0400 Subject: [PATCH 0007/2465] avoid string copy if no block stubs present --- .../jinjava/interpret/JinjavaInterpreter.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index 85b4b4a22..89e596b42 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -142,10 +142,14 @@ public String render(Node root, boolean processExtendRoots) { } String resolveBlockStubs(CharSequence content) { - StringBuilder result = new StringBuilder(content.length() + 256); + StringBuilder result = null; int pos = 0, start, end, stubStartLen = BLOCK_STUB_START.length(); while ((start = StringUtils.indexOf(content, BLOCK_STUB_START, pos)) != -1) { + if (result == null) { + result = new StringBuilder(content.length() + 256); + } + end = StringUtils.indexOf(content, BLOCK_STUB_END, start + stubStartLen); String blockName = content.subSequence(start + stubStartLen, end).toString(); @@ -175,9 +179,11 @@ String resolveBlockStubs(CharSequence content) { pos = end + 1; } - result.append(content.subSequence(pos, content.length())); + if (result != null) { + return result.append(content.subSequence(pos, content.length())).toString(); + } - return result.toString(); + return content.toString(); } /** From 9e4a65578e2afc49889d0e87829bfc67a78bf1cc Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 31 Aug 2015 18:16:07 -0400 Subject: [PATCH 0008/2465] code cleanup; make fields final --- .../com/hubspot/jinjava/util/Variable.java | 46 +++++++++---------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/util/Variable.java b/src/main/java/com/hubspot/jinjava/util/Variable.java index ccdf04cc5..75f4274bf 100644 --- a/src/main/java/com/hubspot/jinjava/util/Variable.java +++ b/src/main/java/com/hubspot/jinjava/util/Variable.java @@ -1,17 +1,17 @@ /********************************************************************** -Copyright (c) 2014 HubSpot 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. + * Copyright (c) 2014 HubSpot 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.hubspot.jinjava.util; @@ -25,26 +25,22 @@ public class Variable { private static final Splitter DOT_SPLITTER = Splitter.on('.'); - private JinjavaInterpreter interpreter; + private final JinjavaInterpreter interpreter; - private String name; - private List chainList; + private final String name; + private final List chainList; public Variable(JinjavaInterpreter interpreter, String variable) { this.interpreter = interpreter; - split(variable); - } - private void split(String variable) { - if (!variable.contains(".")) { + if (variable.indexOf('.') == -1) { name = variable; chainList = Collections.emptyList(); - return; + } else { + List parts = Lists.newArrayList(DOT_SPLITTER.split(variable)); + name = parts.get(0); + chainList = parts.subList(1, parts.size()); } - - List parts = Lists.newArrayList(DOT_SPLITTER.split(variable)); - name = parts.get(0); - chainList = parts.subList(1, parts.size()); } public String getName() { From 17cf783c44de8b44f7ed4d65f5cc0ca23bd08db2 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 31 Aug 2015 18:34:06 -0400 Subject: [PATCH 0009/2465] avoid boxing/unboxing when converting character codes for strftimeformatter --- .../objects/date/StrftimeFormatter.java | 77 +++++++++---------- 1 file changed, 36 insertions(+), 41 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/objects/date/StrftimeFormatter.java b/src/main/java/com/hubspot/jinjava/objects/date/StrftimeFormatter.java index 664320edb..4daf27f4b 100644 --- a/src/main/java/com/hubspot/jinjava/objects/date/StrftimeFormatter.java +++ b/src/main/java/com/hubspot/jinjava/objects/date/StrftimeFormatter.java @@ -3,8 +3,6 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; -import java.util.HashMap; -import java.util.Map; import org.apache.commons.lang3.StringUtils; @@ -19,36 +17,37 @@ public class StrftimeFormatter { /* * Mapped from http://strftime.org/, http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html */ - private static final Map CONVERSIONS = new HashMap(); + private static final String[] CONVERSIONS = new String[255]; + static { - CONVERSIONS.put('a', "EEE"); - CONVERSIONS.put('A', "EEEE"); - CONVERSIONS.put('b', "MMM"); - CONVERSIONS.put('B', "MMMM"); - CONVERSIONS.put('c', "EEE MMM dd HH:mm:ss yyyy"); - CONVERSIONS.put('d', "dd"); - CONVERSIONS.put('e', "d"); // The day of the month like with %d, but padded with blank (range 1 through 31). - CONVERSIONS.put('f', "SSSS"); - CONVERSIONS.put('H', "HH"); - CONVERSIONS.put('h', "hh"); - CONVERSIONS.put('I', "hh"); - CONVERSIONS.put('j', "DDD"); - CONVERSIONS.put('k', "H"); // The hour as a decimal number, using a 24-hour clock like %H, but padded with blank (range 0 through 23). - CONVERSIONS.put('l', "h"); // The hour as a decimal number, using a 12-hour clock like %I, but padded with blank (range 1 through 12). - CONVERSIONS.put('m', "MM"); - CONVERSIONS.put('M', "mm"); - CONVERSIONS.put('p', "a"); - CONVERSIONS.put('S', "ss"); - CONVERSIONS.put('U', "ww"); - CONVERSIONS.put('w', "uu"); - CONVERSIONS.put('W', "ww"); - CONVERSIONS.put('x', "MM/dd/yy"); - CONVERSIONS.put('X', "HH:mm:ss"); - CONVERSIONS.put('y', "yy"); - CONVERSIONS.put('Y', "yyyy"); - CONVERSIONS.put('z', "Z"); - CONVERSIONS.put('Z', "ZZZ"); - CONVERSIONS.put('%', "%"); + CONVERSIONS['a'] = "EEE"; + CONVERSIONS['A'] = "EEEE"; + CONVERSIONS['b'] = "MMM"; + CONVERSIONS['B'] = "MMMM"; + CONVERSIONS['c'] = "EEE MMM dd HH:mm:ss yyyy"; + CONVERSIONS['d'] = "dd"; + CONVERSIONS['e'] = "d"; // The day of the month like with %d, but padded with blank (range 1 through 31). + CONVERSIONS['f'] = "SSSS"; + CONVERSIONS['H'] = "HH"; + CONVERSIONS['h'] = "hh"; + CONVERSIONS['I'] = "hh"; + CONVERSIONS['j'] = "DDD"; + CONVERSIONS['k'] = "H"; // The hour as a decimal number, using a 24-hour clock like %H, but padded with blank (range 0 through 23). + CONVERSIONS['l'] = "h"; // The hour as a decimal number, using a 12-hour clock like %I, but padded with blank (range 1 through 12). + CONVERSIONS['m'] = "MM"; + CONVERSIONS['M'] = "mm"; + CONVERSIONS['p'] = "a"; + CONVERSIONS['S'] = "ss"; + CONVERSIONS['U'] = "ww"; + CONVERSIONS['w'] = "uu"; + CONVERSIONS['W'] = "ww"; + CONVERSIONS['x'] = "MM/dd/yy"; + CONVERSIONS['X'] = "HH:mm:ss"; + CONVERSIONS['y'] = "yy"; + CONVERSIONS['Y'] = "yyyy"; + CONVERSIONS['z'] = "Z"; + CONVERSIONS['Z'] = "ZZZ"; + CONVERSIONS['%'] = "%"; } /** @@ -76,27 +75,23 @@ private static String toJavaDateTimeFormat(String strftime) { } if (stripLeadingZero) { - result.append(CONVERSIONS.get(c).substring(1)); - } - else { - result.append(CONVERSIONS.get(c)); + result.append(CONVERSIONS[c].substring(1)); + } else { + result.append(CONVERSIONS[c]); } - } - else if (Character.isLetter(c)) { + } else if (Character.isLetter(c)) { result.append("'"); while (Character.isLetter(c)) { result.append(c); if (++i < strftime.length()) { c = strftime.charAt(i); - } - else { + } else { c = 0; } } result.append("'"); --i; // re-consume last char - } - else { + } else { result.append(c); } } From dc7a4277cb640253d793e558639d728b5f5a51a3 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 31 Aug 2015 23:00:50 -0400 Subject: [PATCH 0010/2465] remove redundant null check --- .../java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java index 63eb81f88..348838b92 100644 --- a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java @@ -66,7 +66,7 @@ public Object invoke(ELContext context, Object base, Object method, try { Object methodProperty = getValue(context, base, method, false); - if (methodProperty != null && methodProperty instanceof AbstractCallableMethod) { + if (methodProperty instanceof AbstractCallableMethod) { context.setPropertyResolved(true); return ((AbstractCallableMethod) methodProperty).evaluate(params); } From b3b72f519b383c91d6caebf415765c02df258f36 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 31 Aug 2015 23:22:32 -0400 Subject: [PATCH 0011/2465] making more fields final, removing unused code from Node --- .../el/JinjavaInterpreterResolver.java | 2 +- .../el/ext/AbstractCallableMethod.java | 14 +++++------ .../com/hubspot/jinjava/el/ext/AstDict.java | 8 +++---- .../com/hubspot/jinjava/el/ext/AstList.java | 2 +- .../jinjava/el/ext/AstNamedParameter.java | 4 ++-- .../java/com/hubspot/jinjava/tree/Node.java | 23 ------------------- 6 files changed, 13 insertions(+), 40 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java index 348838b92..b52d0b5bc 100644 --- a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java @@ -53,7 +53,7 @@ public class JinjavaInterpreterResolver extends SimpleResolver { } }; - private JinjavaInterpreter interpreter; + private final JinjavaInterpreter interpreter; public JinjavaInterpreterResolver(JinjavaInterpreter interpreter) { super(DEFAULT_RESOLVER_READ_WRITE); diff --git a/src/main/java/com/hubspot/jinjava/el/ext/AbstractCallableMethod.java b/src/main/java/com/hubspot/jinjava/el/ext/AbstractCallableMethod.java index 2ef139c2e..b1f3bf7dd 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/AbstractCallableMethod.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/AbstractCallableMethod.java @@ -17,6 +17,7 @@ public abstract class AbstractCallableMethod { public static final Method EVAL_METHOD; + static { try { EVAL_METHOD = AbstractCallableMethod.class.getMethod("evaluate", Object[].class); @@ -25,8 +26,8 @@ public abstract class AbstractCallableMethod { } } - private String name; - private LinkedHashMap argNamesWithDefaults; + private final String name; + private final LinkedHashMap argNamesWithDefaults; public AbstractCallableMethod(String name, LinkedHashMap argNamesWithDefaults) { this.name = name; @@ -48,8 +49,7 @@ public Object evaluate(Object... args) { break; } argEntry.setValue(arg); - } - else { + } else { break; } } @@ -61,12 +61,10 @@ public Object evaluate(Object... args) { NamedParameter param = (NamedParameter) arg; if (argMap.containsKey(param.getName())) { argMap.put(param.getName(), param.getValue()); - } - else { + } else { kwargMap.put(param.getName(), param.getValue()); } - } - else { + } else { varArgs.add(arg); } } diff --git a/src/main/java/com/hubspot/jinjava/el/ext/AstDict.java b/src/main/java/com/hubspot/jinjava/el/ext/AstDict.java index 70aa1eb0c..46db39c98 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/AstDict.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/AstDict.java @@ -16,7 +16,7 @@ public class AstDict extends AstLiteral { - private Map dict; + private final Map dict; public AstDict(Map dict) { this.dict = dict; @@ -31,11 +31,9 @@ public Object eval(Bindings bindings, ELContext context) { if (entry.getKey() instanceof AstString) { key = Objects.toString(entry.getKey().eval(bindings, context)); - } - else if (entry.getKey() instanceof AstIdentifier) { + } else if (entry.getKey() instanceof AstIdentifier) { key = ((AstIdentifier) entry.getKey()).getName(); - } - else { + } else { throw new IllegalArgumentException("Dict key must be a string or identifier, was: " + entry.getKey()); } diff --git a/src/main/java/com/hubspot/jinjava/el/ext/AstList.java b/src/main/java/com/hubspot/jinjava/el/ext/AstList.java index f18d8f131..40b551128 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/AstList.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/AstList.java @@ -15,7 +15,7 @@ public class AstList extends AstLiteral { - private AstParameters elements; + private final AstParameters elements; public AstList(AstParameters elements) { this.elements = elements; diff --git a/src/main/java/com/hubspot/jinjava/el/ext/AstNamedParameter.java b/src/main/java/com/hubspot/jinjava/el/ext/AstNamedParameter.java index f70036003..e4a6d971c 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/AstNamedParameter.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/AstNamedParameter.java @@ -9,8 +9,8 @@ public class AstNamedParameter extends AstLiteral { - private AstIdentifier name; - private AstNode value; + private final AstIdentifier name; + private final AstNode value; public AstNamedParameter(AstIdentifier name, AstNode value) { this.name = name; diff --git a/src/main/java/com/hubspot/jinjava/tree/Node.java b/src/main/java/com/hubspot/jinjava/tree/Node.java index 834c509a8..c6b9cbecb 100644 --- a/src/main/java/com/hubspot/jinjava/tree/Node.java +++ b/src/main/java/com/hubspot/jinjava/tree/Node.java @@ -29,7 +29,6 @@ public abstract class Node implements Serializable { private final Token master; private final int lineNumber; - private int level; private Node parent = null; private LinkedList children = new LinkedList(); @@ -39,10 +38,6 @@ public Node(Token master, int lineNumber) { this.lineNumber = lineNumber; } - public int getLevel() { - return level; - } - public Node getParent() { return parent; } @@ -67,24 +62,6 @@ public void setChildren(LinkedList children) { this.children = children; } - /** - * trusty call by TreeParser - * - * @param node - */ - void add(Node node) { - node.level = level + 1; - node.parent = this; - children.add(node); - } - - void computeLevel(int baseLevel) { - level = baseLevel; - for (Node child : children) { - child.computeLevel(level + 1); - } - } - public abstract String render(JinjavaInterpreter interpreter); public abstract String getName(); From 7079aaa3c9586133ae9b0782a1484febdaacdb3e Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 31 Aug 2015 23:26:07 -0400 Subject: [PATCH 0012/2465] migrate to new travis architecture --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 6efea5798..66172a5ae 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ +sudo: false language: java jdk: - oraclejdk8 From fffa2350d28a65a8238505e39cae227a4dcc448d Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 31 Aug 2015 23:34:26 -0400 Subject: [PATCH 0013/2465] another crack at solving for travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 66172a5ae..f3075d65c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,5 +2,5 @@ sudo: false language: java jdk: - oraclejdk8 -install: mvn install -DskipTests=false -Dgpg.skip=true +install: mvn clean install -DskipTests=false -Dgpg.skip=true From 6a063d5a639dd410bed07fd2b15c5a180e24d789 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 31 Aug 2015 23:45:26 -0400 Subject: [PATCH 0014/2465] skip git-commit plugn for travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f3075d65c..b409da7de 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,5 +2,5 @@ sudo: false language: java jdk: - oraclejdk8 -install: mvn clean install -DskipTests=false -Dgpg.skip=true +install: mvn clean install -DskipTests=false -Dgpg.skip=true -Dbasepom.git-id.skip=true From 13639b50df7372b9b30f14d11aa3252b76353dee Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Tue, 1 Sep 2015 10:08:31 -0400 Subject: [PATCH 0015/2465] fix travis?? --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b409da7de..280d733d4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,5 +2,5 @@ sudo: false language: java jdk: - oraclejdk8 -install: mvn clean install -DskipTests=false -Dgpg.skip=true -Dbasepom.git-id.skip=true +install: mvn test -B From 263a1338ca7dc0b09f1eeddf99723c095e0eaeae Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Tue, 1 Sep 2015 10:11:17 -0400 Subject: [PATCH 0016/2465] more travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 280d733d4..f41591c3f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,5 +2,5 @@ sudo: false language: java jdk: - oraclejdk8 -install: mvn test -B +install: mvn test -B -Dbasepom.git-id.skip=true From 711ef8730b76e58f519c77fe330f7f21625277e7 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Tue, 1 Sep 2015 10:19:26 -0400 Subject: [PATCH 0017/2465] trying stock java builder --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f41591c3f..aa5d3da00 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,5 +2,4 @@ sudo: false language: java jdk: - oraclejdk8 -install: mvn test -B -Dbasepom.git-id.skip=true From 17c26a5a525dd5cf6486a8b0c83378d3e986c0eb Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Tue, 1 Sep 2015 10:26:48 -0400 Subject: [PATCH 0018/2465] use latest hubspot basepom --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f703d473e..9676d8a9b 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.hubspot basepom - 10.4 + 12.4 com.hubspot.jinjava From 8b8b22deada792c77a2d0874aa20e192ecd184ee Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Tue, 1 Sep 2015 10:39:49 -0400 Subject: [PATCH 0019/2465] cache m2 artifacts --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index aa5d3da00..1087c421f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,3 +3,6 @@ language: java jdk: - oraclejdk8 +cache: + directories: + - $HOME/.m2 From 9ee9321685d98512ee7fc52a0134fe7bc93d7092 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Tue, 1 Sep 2015 10:46:58 -0400 Subject: [PATCH 0020/2465] disable debug mode for test logging --- src/test/resources/logback.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/resources/logback.xml b/src/test/resources/logback.xml index 859cb540e..5a6a22834 100644 --- a/src/test/resources/logback.xml +++ b/src/test/resources/logback.xml @@ -1,4 +1,4 @@ - + From d7ed4ba251f79b33631e89765f521dbb3c806c55 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Tue, 1 Sep 2015 11:11:19 -0400 Subject: [PATCH 0021/2465] updating changelog [ci skip] --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 9ee50d5b3..35d917b53 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Jinjava Releases # +### Version 2.0.10 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.0.10%22)) ### + +* minor performance enhancements + ### Version 2.0.9 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.0.9%22)) ### * update truncate_html filter to support preserving words by default, with an additional parameter to chop words at length From affa7fcfbff8f8d1a15d78eebd5b6ff8035491d1 Mon Sep 17 00:00:00 2001 From: The Gitter Badger Date: Fri, 4 Sep 2015 21:07:56 +0000 Subject: [PATCH 0022/2465] Added Gitter badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index cc9310ea9..62528a038 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ jinjava [![Build Status](https://travis-ci.org/HubSpot/jinjava.svg?branch=master)](https://travis-ci.org/HubSpot/jinjava) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.hubspot.jinjava/jinjava/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.hubspot.jinjava/jinjava) ======= +[![Join the chat at https://gitter.im/HubSpot/jinjava](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/HubSpot/jinjava?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + jinjava Java-based template engine based on django template syntax, adapted to render jinja templates (at least the subset of jinja in use in HubSpot content). Currently used in production to render thousands of websites with hundreds of millions of page views per month on the [HubSpot COS](http://www.hubspot.com/products/sites). From 6cb95a03cdc3ff9d53923f1eeeee2958ae9b98f2 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Fri, 4 Sep 2015 17:10:07 -0400 Subject: [PATCH 0023/2465] adding notification web hooks [ci skip] --- .travis.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.travis.yml b/.travis.yml index 1087c421f..017cf90da 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,3 +6,12 @@ jdk: cache: directories: - $HOME/.m2 + +notifications: + webhooks: + urls: + - https://webhooks.gitter.im/e/13ca3dd0f0bbc821ff55 + on_success: change # options: [always|never|change] default: always + on_failure: always # options: [always|never|change] default: always + on_start: never # options: [always|never|change] default: always + From 0677c6e46d403ee1f2e0541c7dc4bbb7062805c4 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Fri, 4 Sep 2015 17:11:42 -0400 Subject: [PATCH 0024/2465] adding gitter --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cc9310ea9..cdda6b6f5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ -jinjava [![Build Status](https://travis-ci.org/HubSpot/jinjava.svg?branch=master)](https://travis-ci.org/HubSpot/jinjava) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.hubspot.jinjava/jinjava/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.hubspot.jinjava/jinjava) +jinjava +[![Build Status](https://travis-ci.org/HubSpot/jinjava.svg?branch=master)](https://travis-ci.org/HubSpot/jinjava) +[![Gitter](https://img.shields.io/badge/gitter-join%20chat-green.svg)](https://gitter.im/HubSpot/jinjava) +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.hubspot.jinjava/jinjava/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.hubspot.jinjava/jinjava) ======= jinjava From 7a1a2547b05eb62430cbab662263a888ba02ccf0 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Fri, 4 Sep 2015 18:03:12 -0400 Subject: [PATCH 0025/2465] codecov --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 017cf90da..5a8c4e501 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,3 +15,8 @@ notifications: on_failure: always # options: [always|never|change] default: always on_start: never # options: [always|never|change] default: always +before_install: + - pip install codecov +after_success: + - codecov + From 8a5eb143ee82ac073db8c1ff0eadc3e1399bfbab Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Fri, 4 Sep 2015 18:03:53 -0400 Subject: [PATCH 0026/2465] update readme --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 12c51d583..8aac54479 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,10 @@ jinjava + [![Build Status](https://travis-ci.org/HubSpot/jinjava.svg?branch=master)](https://travis-ci.org/HubSpot/jinjava) -[![Gitter](https://img.shields.io/badge/gitter-join%20chat-green.svg)](https://gitter.im/HubSpot/jinjava) +[![Join the chat at https://gitter.im/HubSpot/jinjava](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/HubSpot/jinjava?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.hubspot.jinjava/jinjava/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.hubspot.jinjava/jinjava) ======= -[![Join the chat at https://gitter.im/HubSpot/jinjava](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/HubSpot/jinjava?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - jinjava Java-based template engine based on django template syntax, adapted to render jinja templates (at least the subset of jinja in use in HubSpot content). Currently used in production to render thousands of websites with hundreds of millions of page views per month on the [HubSpot COS](http://www.hubspot.com/products/sites). From aa9fec6c54b8e67fb5dbe9b0419e201c9b5752b5 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Fri, 4 Sep 2015 18:04:36 -0400 Subject: [PATCH 0027/2465] gitter wants to be last --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8aac54479..ccfa25d99 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ jinjava [![Build Status](https://travis-ci.org/HubSpot/jinjava.svg?branch=master)](https://travis-ci.org/HubSpot/jinjava) -[![Join the chat at https://gitter.im/HubSpot/jinjava](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/HubSpot/jinjava?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.hubspot.jinjava/jinjava/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.hubspot.jinjava/jinjava) +[![Join the chat at https://gitter.im/HubSpot/jinjava](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/HubSpot/jinjava?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ======= jinjava From 324fcfda4a586bfe8ad0425a1de9b68cb0c5795b Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Sat, 5 Sep 2015 10:48:15 -0400 Subject: [PATCH 0028/2465] install pip as user --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5a8c4e501..a719bce10 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ notifications: on_start: never # options: [always|never|change] default: always before_install: - - pip install codecov + - pip install --user codecov after_success: - codecov From ec95f3302173c8fd6db82a8e952d9684abdea70b Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Sat, 5 Sep 2015 11:06:54 -0400 Subject: [PATCH 0029/2465] adding jacoco plugin for code coverage reporting --- pom.xml | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9676d8a9b..39947ef00 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 @@ -102,5 +103,28 @@ test + + + + + org.jacoco + jacoco-maven-plugin + + + + prepare-agent + + + + report + test + + report + + + + + + From eef2a120e7b033d949079d6b0f2274603241590f Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Sat, 5 Sep 2015 11:16:27 -0400 Subject: [PATCH 0030/2465] add coverage badge --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ccfa25d99..2eb3fad98 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -jinjava +# jinjava [![Build Status](https://travis-ci.org/HubSpot/jinjava.svg?branch=master)](https://travis-ci.org/HubSpot/jinjava) +[![Coverage status](https://img.shields.io/codecov/c/github/HubSpot/jinjava/master.svg)](https://codecov.io/github/HubSpot/jinjava) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.hubspot.jinjava/jinjava/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.hubspot.jinjava/jinjava) [![Join the chat at https://gitter.im/HubSpot/jinjava](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/HubSpot/jinjava?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -======= jinjava From ef484457c09a23fa86a6758ef13f0fb2dbc53572 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Tue, 15 Sep 2015 14:20:40 -0400 Subject: [PATCH 0031/2465] clarify pywrapper use --- src/main/java/com/hubspot/jinjava/objects/PyWrapper.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/objects/PyWrapper.java b/src/main/java/com/hubspot/jinjava/objects/PyWrapper.java index cd4f8fe06..2dab75956 100644 --- a/src/main/java/com/hubspot/jinjava/objects/PyWrapper.java +++ b/src/main/java/com/hubspot/jinjava/objects/PyWrapper.java @@ -1,7 +1,9 @@ package com.hubspot.jinjava.objects; /** - * Marker indicating an object is a python wrapper interface + * Marker indicating an object is a python wrapper interface. + * + * Use this on classes which you don't want automatically wrapped into Py* forms (i.e. List types into PyList) * * @author jstehler */ From bb0869e9d162c26806f33711f26c39dda14b29d8 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Tue, 15 Sep 2015 14:27:28 -0400 Subject: [PATCH 0032/2465] trailing whitespace [ci skip] --- src/main/java/com/hubspot/jinjava/objects/PyWrapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/objects/PyWrapper.java b/src/main/java/com/hubspot/jinjava/objects/PyWrapper.java index 2dab75956..761f30d79 100644 --- a/src/main/java/com/hubspot/jinjava/objects/PyWrapper.java +++ b/src/main/java/com/hubspot/jinjava/objects/PyWrapper.java @@ -2,7 +2,7 @@ /** * Marker indicating an object is a python wrapper interface. - * + * * Use this on classes which you don't want automatically wrapped into Py* forms (i.e. List types into PyList) * * @author jstehler From 4d743d275103a5ed30e9cf610cac30ce91fd1431 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Sun, 20 Sep 2015 16:31:42 -0400 Subject: [PATCH 0033/2465] Update README.md --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2eb3fad98..0a2fc6849 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,20 @@ Get it: com.hubspot.jinjava jinjava - 2.0.7 + 2.0.9 ``` +or if you're stuck on java 7: +```xml + + com.hubspot.jinjava + jinjava + 2.0.11-java7 + +``` + + Example usage: -------------- From cd93d4e065d0dfe8731daf0efb7acbb07de0cae8 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 21 Sep 2015 15:15:08 -0400 Subject: [PATCH 0034/2465] [maven-release-plugin] prepare release jinjava-2.0.10 --- pom.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 39947ef00..ec8552e35 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,4 @@ - + 4.0.0 @@ -10,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.0.10-SNAPSHOT + 2.0.10 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -18,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.0.10 From 7546f3cf4eb38e46dafc1397619c15c8d53ed42c Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 21 Sep 2015 15:15:09 -0400 Subject: [PATCH 0035/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ec8552e35..dd3614c1b 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.0.10 + 2.0.12-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.0.10 + HEAD From 7259a373bbf5dfc59e81909cbf98419871402cb4 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 21 Sep 2015 15:19:18 -0400 Subject: [PATCH 0036/2465] prepare for next dev iteration --- CHANGES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 35d917b53..dfa36b56b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,8 @@ # Jinjava Releases # +### Version 2.0.11 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.0.11%22)) ### + + ### Version 2.0.10 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.0.10%22)) ### * minor performance enhancements From 6c3b59f48b20cf5d71f229582c488989a3f11e63 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 21 Sep 2015 15:19:48 -0400 Subject: [PATCH 0037/2465] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0a2fc6849..54c5d3452 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Get it: com.hubspot.jinjava jinjava - 2.0.9 + 2.0.10 ``` From 88243311f19226e6850a238ec0c2be0bebc7324d Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 5 Oct 2015 10:47:43 -0400 Subject: [PATCH 0038/2465] add clarifying javadoc for interpreter.render* methods, autocloseable for enter/leave scope --- .../jinjava/interpret/JinjavaInterpreter.java | 63 +++++++++++++++++-- .../interpret/JinjavaInterpreterTest.java | 33 +++++++++- 2 files changed, 89 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index 89e596b42..02d01fdcc 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -76,8 +76,22 @@ public void addBlock(String name, LinkedList value) { blocks.put(name, value); } - public void enterScope() { + /** + * Creates a new variable scope, extending from the current scope. Allows you to create a nested + * contextual scope which can override variables from higher levels. + * + * Should be used in a try/finally context, similar to lock-use patterns: + * + * + * interpreter.enterScope(); + * try (interpreter.enterScope()) { + * // ... + * } + * + */ + public InterpreterScopeClosable enterScope() { context = new Context(context); + return new InterpreterScopeClosable(); } public void leaveScope() { @@ -87,10 +101,26 @@ public void leaveScope() { } } + class InterpreterScopeClosable implements AutoCloseable { + + @Override + public void close() { + leaveScope(); + } + + } + public Node parse(String template) { return new TreeParser(this, template).buildTree(); } + /** + * Parse the given string into a root Node, and then render it without processing any extend parents. + * + * @param template + * string to parse + * @return rendered result + */ public String renderString(String template) { Integer depth = (Integer) context.get("hs_render_depth", 0); if (depth == null) { @@ -110,15 +140,38 @@ public String renderString(String template) { } } - public String render(Node root) { - return render(root, true); - } - + /** + * Parse the given string into a root Node, and then renders it processing extend parents. + * + * @param template + * string to parse + * @return rendered result + */ public String render(String template) { ENGINE_LOG.debug(template); return render(parse(template), true); } + /** + * Render the given root node, processing extend parents. Equivalent to render(root, true) + * + * @param root + * node to render + * @return rendered result + */ + public String render(Node root) { + return render(root, true); + } + + /** + * Render the given root node using this interpreter's current context + * + * @param root + * node to render + * @param processExtendRoots + * if true, also render all extend parents + * @return rendered result + */ public String render(Node root, boolean processExtendRoots) { StringBuilder buff = new StringBuilder(); diff --git a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java index 063b37b6e..01a05a16b 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java @@ -2,16 +2,17 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.time.ZonedDateTime; + import org.junit.Before; import org.junit.Test; import com.google.common.collect.Lists; import com.hubspot.jinjava.Jinjava; +import com.hubspot.jinjava.interpret.JinjavaInterpreter.InterpreterScopeClosable; import com.hubspot.jinjava.tree.TextNode; import com.hubspot.jinjava.tree.parse.TextToken; -import java.time.ZonedDateTime; - public class JinjavaInterpreterTest { Jinjava jinjava; @@ -95,4 +96,32 @@ public void triesBeanMethodFirst() { assertThat(interpreter.resolveProperty(ZonedDateTime.parse("2013-09-19T12:12:12+00:00"), "year").toString()).isEqualTo("2013"); } + @Test + public void enterScopeTryFinally() { + interpreter.getContext().put("foo", "parent"); + + interpreter.enterScope(); + try { + interpreter.getContext().put("foo", "child"); + assertThat(interpreter.resolveELExpression("foo", 1)).isEqualTo("child"); + } finally { + interpreter.leaveScope(); + } + + assertThat(interpreter.resolveELExpression("foo", 1)).isEqualTo("parent"); + + } + + @Test + public void enterScopeTryWithResources() { + interpreter.getContext().put("foo", "parent"); + + try (InterpreterScopeClosable c = interpreter.enterScope()) { + interpreter.getContext().put("foo", "child"); + assertThat(interpreter.resolveELExpression("foo", 1)).isEqualTo("child"); + } + + assertThat(interpreter.resolveELExpression("foo", 1)).isEqualTo("parent"); + } + } From 1e5f17b6747745e77811596ff6f2fc39c54b82de Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 5 Oct 2015 10:50:39 -0400 Subject: [PATCH 0039/2465] use try-with-resources pattern for interpreter.enterScope --- .../jinjava/interpret/JinjavaInterpreter.java | 2 +- .../hubspot/jinjava/lib/fn/MacroFunction.java | 19 ++++---- .../jinjava/lib/tag/AutoEscapeTag.java | 9 ++-- .../com/hubspot/jinjava/lib/tag/CallTag.java | 10 ++--- .../com/hubspot/jinjava/lib/tag/ForTag.java | 44 ++++++++----------- 5 files changed, 37 insertions(+), 47 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index 02d01fdcc..07c3560a8 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -101,7 +101,7 @@ public void leaveScope() { } } - class InterpreterScopeClosable implements AutoCloseable { + public class InterpreterScopeClosable implements AutoCloseable { @Override public void close() { diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/MacroFunction.java b/src/main/java/com/hubspot/jinjava/lib/fn/MacroFunction.java index 95c910881..d9bd8a615 100644 --- a/src/main/java/com/hubspot/jinjava/lib/fn/MacroFunction.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/MacroFunction.java @@ -7,6 +7,7 @@ import com.hubspot.jinjava.el.ext.AbstractCallableMethod; import com.hubspot.jinjava.interpret.Context; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.interpret.JinjavaInterpreter.InterpreterScopeClosable; import com.hubspot.jinjava.tree.Node; /** @@ -25,8 +26,13 @@ public class MacroFunction extends AbstractCallableMethod { private final Context localContextScope; - public MacroFunction(List content, String name, LinkedHashMap argNamesWithDefaults, - boolean catchKwargs, boolean catchVarargs, boolean caller, Context localContextScope) { + public MacroFunction(List content, + String name, + LinkedHashMap argNamesWithDefaults, + boolean catchKwargs, + boolean catchVarargs, + boolean caller, + Context localContextScope) { super(name, argNamesWithDefaults); this.content = content; this.catchKwargs = catchKwargs; @@ -39,14 +45,11 @@ public MacroFunction(List content, String name, LinkedHashMap argMap, Map kwargMap, List varArgs) { JinjavaInterpreter interpreter = JinjavaInterpreter.getCurrent(); - interpreter.enterScope(); - - try { + try (InterpreterScopeClosable c = interpreter.enterScope()) { for (Map.Entry scopeEntry : localContextScope.getScope().entrySet()) { if (scopeEntry.getValue() instanceof MacroFunction) { interpreter.getContext().addGlobalMacro((MacroFunction) scopeEntry.getValue()); - } - else { + } else { interpreter.getContext().put(scopeEntry.getKey(), scopeEntry.getValue()); } } @@ -67,8 +70,6 @@ public Object doEvaluate(Map argMap, Map kwargMa } return result.toString(); - } finally { - interpreter.leaveScope(); } } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/AutoEscapeTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/AutoEscapeTag.java index 2c0c37c86..c7b32cdb1 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/AutoEscapeTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/AutoEscapeTag.java @@ -6,6 +6,7 @@ import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.interpret.JinjavaInterpreter.InterpreterScopeClosable; import com.hubspot.jinjava.tree.Node; import com.hubspot.jinjava.tree.TagNode; @@ -16,8 +17,7 @@ @JinjavaSnippet( code = "{% autoescape %}\n" + "
Code to escape
\n" + - "{% endautoescape %}" - ) + "{% endautoescape %}") }) public class AutoEscapeTag implements Tag { public static final String AUTOESCAPE_CONTEXT_VAR = "__auto3sc@pe__"; @@ -35,8 +35,7 @@ public String getEndTagName() { @Override public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { - interpreter.enterScope(); - try { + try (InterpreterScopeClosable c = interpreter.enterScope()) { String boolFlagStr = StringUtils.trim(tagNode.getHelpers()); boolean escapeFlag = BooleanUtils.toBoolean(StringUtils.isNotBlank(boolFlagStr) ? boolFlagStr : "true"); interpreter.getContext().put(AUTOESCAPE_CONTEXT_VAR, escapeFlag); @@ -48,8 +47,6 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { } return result.toString(); - } finally { - interpreter.leaveScope(); } } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/CallTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/CallTag.java index 6f3bfd74a..d10755403 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/CallTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/CallTag.java @@ -5,6 +5,7 @@ import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.interpret.JinjavaInterpreter.InterpreterScopeClosable; import com.hubspot.jinjava.lib.fn.MacroFunction; import com.hubspot.jinjava.tree.TagNode; @@ -22,7 +23,7 @@ " \n" + " {% endmacro %}\n\n" + - " {% call render_dialog('Hello World') %}\n" + + " {% call render_dialog('Hello World') %}\n" + " This is a simple dialog rendered by using a macro and\n" + " a call block.\n" + " {% endcall %}"), @@ -38,7 +39,7 @@ " \n" + " {% endmacro %}\n\n" + - " {% call(user) dump_users(list_of_user) %}\n" + + " {% call(user) dump_users(list_of_user) %}\n" + "
\n" + "
Realname
\n" + "
{{ user.realname|e }}
\n" + @@ -65,15 +66,12 @@ public String getEndTagName() { public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { String macroExpr = "{{" + tagNode.getHelpers().trim() + "}}"; - interpreter.enterScope(); - try { + try (InterpreterScopeClosable c = interpreter.enterScope()) { LinkedHashMap args = new LinkedHashMap<>(); MacroFunction caller = new MacroFunction(tagNode.getChildren(), "caller", args, false, false, true, interpreter.getContext()); interpreter.getContext().addGlobalMacro(caller); return interpreter.render(macroExpr); - } finally { - interpreter.leaveScope(); } } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java index 6ea3dd4eb..a94512be4 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java @@ -1,17 +1,17 @@ /********************************************************************** -Copyright (c) 2014 HubSpot 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. + * Copyright (c) 2014 HubSpot 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.hubspot.jinjava.lib.tag; @@ -29,6 +29,7 @@ import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.interpret.JinjavaInterpreter.InterpreterScopeClosable; import com.hubspot.jinjava.tree.Node; import com.hubspot.jinjava.tree.TagNode; import com.hubspot.jinjava.util.ForLoop; @@ -78,8 +79,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { if ("in".equals(val)) { break; - } - else { + } else { loopVars.add(val); inPos++; } @@ -93,8 +93,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { Object collection = interpreter.resolveELExpression(loopExpr, tagNode.getLineNumber()); ForLoop loop = ObjectIterator.getLoop(collection); - interpreter.enterScope(); - try { + try (InterpreterScopeClosable c = interpreter.enterScope()) { interpreter.getContext().put(LOOP, loop); StringBuilder buff = new StringBuilder(); @@ -104,8 +103,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { // set item variables if (loopVars.size() == 1) { interpreter.getContext().put(loopVars.get(0), val); - } - else { + } else { for (String loopVar : loopVars) { if (Map.Entry.class.isAssignableFrom(val.getClass())) { Map.Entry entry = (Entry) val; @@ -113,14 +111,12 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { if ("key".equals(loopVar)) { entryVal = entry.getKey(); - } - else if ("value".equals(loopVar)) { + } else if ("value".equals(loopVar)) { entryVal = entry.getValue(); } interpreter.getContext().put(loopVar, entryVal); - } - else { + } else { try { PropertyDescriptor[] valProps = Introspector.getBeanInfo(val.getClass()).getPropertyDescriptors(); for (PropertyDescriptor valProp : valProps) { @@ -142,8 +138,6 @@ else if ("value".equals(loopVar)) { } return buff.toString(); - } finally { - interpreter.leaveScope(); } } From 317570d4bd8d6ecfc8d19592eccd4c88a690209d Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 5 Oct 2015 10:55:10 -0400 Subject: [PATCH 0040/2465] renaming renderString to renderFlat to better signify its purpose --- .../hubspot/jinjava/interpret/JinjavaInterpreter.java | 11 ++++++++++- .../hubspot/jinjava/lib/filter/StripTagsFilter.java | 2 +- .../java/com/hubspot/jinjava/tree/ExpressionNode.java | 2 +- .../jinjava/lib/filter/DateTimeFormatFilterTest.java | 4 ++-- .../jinjava/lib/filter/StripTagsFilterTest.java | 2 +- .../java/com/hubspot/jinjava/lib/tag/FromTagTest.java | 2 +- .../com/hubspot/jinjava/lib/tag/ImportTagTest.java | 2 +- 7 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index 07c3560a8..2ad5ea065 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -114,14 +114,23 @@ public Node parse(String template) { return new TreeParser(this, template).buildTree(); } + /** + * @deprecated see renderFlat(String template) + */ + @Deprecated + public String renderString(String template) { + return renderFlat(template); + } + /** * Parse the given string into a root Node, and then render it without processing any extend parents. + * This method should be used when the template is known to not have any extends or block tags. * * @param template * string to parse * @return rendered result */ - public String renderString(String template) { + public String renderFlat(String template) { Integer depth = (Integer) context.get("hs_render_depth", 0); if (depth == null) { depth = 0; diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/StripTagsFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/StripTagsFilter.java index dfe9f2543..d4ba4fc45 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/StripTagsFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/StripTagsFilter.java @@ -28,7 +28,7 @@ public Object filter(Object object, JinjavaInterpreter interpreter, String... ar return object; } - String val = interpreter.renderString((String) object); + String val = interpreter.renderFlat((String) object); String strippedVal = Jsoup.parseBodyFragment(val).text(); String normalizedVal = WHITESPACE.matcher(strippedVal).replaceAll(" "); diff --git a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java index b30c57a3e..2d19fbe73 100644 --- a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java @@ -43,7 +43,7 @@ public String render(JinjavaInterpreter interpreter) { if (!StringUtils.equals(result, master.getImage()) && StringUtils.contains(result, "{{")) { try { - result = interpreter.renderString(result); + result = interpreter.renderFlat(result); } catch (Exception e) { Logging.ENGINE_LOG.warn("Error rendering variable node result", e); } diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilterTest.java index 4a8919076..6a5f71e8f 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilterTest.java @@ -53,8 +53,8 @@ public void itHandlesVarsAndLiterals() throws Exception { interpreter.getContext().put("d", d); interpreter.getContext().put("foo", "%Y-%m"); - assertThat(interpreter.renderString("{{ d|datetimeformat(foo) }}")).isEqualTo("2013-11"); - assertThat(interpreter.renderString("{{ d|datetimeformat(\"%Y-%m-%d\") }}")).isEqualTo("2013-11-06"); + assertThat(interpreter.renderFlat("{{ d|datetimeformat(foo) }}")).isEqualTo("2013-11"); + assertThat(interpreter.renderFlat("{{ d|datetimeformat(\"%Y-%m-%d\") }}")).isEqualTo("2013-11-06"); assertThat(interpreter.getErrors()).isEmpty(); } diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/StripTagsFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/StripTagsFilterTest.java index 1f601b90e..a3209b0f1 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/StripTagsFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/StripTagsFilterTest.java @@ -25,7 +25,7 @@ public class StripTagsFilterTest { @Before public void setup() { - when(interpreter.renderString(anyString())).thenAnswer(new ReturnsArgumentAt(0)); + when(interpreter.renderFlat(anyString())).thenAnswer(new ReturnsArgumentAt(0)); } @Test diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/FromTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/FromTagTest.java index f5b92b8d1..1de50f41e 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/FromTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/FromTagTest.java @@ -56,7 +56,7 @@ public void importedContextExposesVars() { private String fixture(String name) { try { - return interpreter.renderString(Resources.toString( + return interpreter.renderFlat(Resources.toString( Resources.getResource(String.format("tags/macrotag/%s.jinja", name)), StandardCharsets.UTF_8)); } catch (IOException e) { throw Throwables.propagate(e); diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/ImportTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/ImportTagTest.java index 91039c0ff..5c5d3bb83 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/ImportTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/ImportTagTest.java @@ -100,7 +100,7 @@ public void importedContextFnsProperlyResolveScopedVars() { private String fixture(String name) { try { - return interpreter.renderString(Resources.toString( + return interpreter.renderFlat(Resources.toString( Resources.getResource(String.format("tags/macrotag/%s.jinja", name)), StandardCharsets.UTF_8)); } catch (IOException e) { throw Throwables.propagate(e); From 296181634531e5080849fbcba29a3aa9313f752b Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 5 Oct 2015 10:57:07 -0400 Subject: [PATCH 0041/2465] updating changes --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index dfa36b56b..aa6283ff8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,7 @@ ### Version 2.0.11 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.0.11%22)) ### +* Renaming JinjavaInterpreter.renderString() to renderFlat() to better signify its purpose ### Version 2.0.10 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.0.10%22)) ### From 0bec2f4410ab49c90ea948b985803974036f03fc Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 5 Oct 2015 11:04:29 -0400 Subject: [PATCH 0042/2465] fix trailing whitespace --- .../com/hubspot/jinjava/interpret/JinjavaInterpreter.java | 2 +- src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index 2ad5ea065..e3958c00f 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -163,7 +163,7 @@ public String render(String template) { /** * Render the given root node, processing extend parents. Equivalent to render(root, true) - * + * * @param root * node to render * @return rendered result diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java index a94512be4..3c9f4acff 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java @@ -1,12 +1,12 @@ /********************************************************************** * Copyright (c) 2014 HubSpot 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. From 60c544ea09f49aecf6b5a09a70b71720d743948a Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 5 Oct 2015 11:38:41 -0400 Subject: [PATCH 0043/2465] Update README.md --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 54c5d3452..b3f0b96ff 100644 --- a/README.md +++ b/README.md @@ -83,12 +83,15 @@ You can provide custom jinja tags, filters, and static functions to the template ```java // define a custom tag implementing com.hubspot.jinjava.lib.Tag -jinjava.getTagLibrary().addTag(new MyCustomTag()); +jinjava.getGlobalContext().registerTag(new MyCustomTag()); // define a custom filter implementing com.hubspot.jinjava.lib.Filter -jinjava.getFilterLibrary().addFilter(new MyAwesomeFilter()); +jinjava.getGlobalContext().registerFilter(new MyAwesomeFilter()); // define a custom public static function (this one will bind to myfn.my_func('foo', 42)) -jinjava.getFunctionLibrary().addFunction(new ELFunctionDefinition("myfn", "my_func", +jinjava.getGlobalContext().registerFunction(new ELFunctionDefinition("myfn", "my_func", MyFuncsClass.class, "myFunc", String.class, Integer.class); + +// define any number of classes which extend Importable +jinjava.getGlobalContext().registerClasses(Class... classes); ``` ### See also From 7b973a00ec3913da5c7deab389fb2155fec86acb Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Tue, 13 Oct 2015 11:28:26 -0400 Subject: [PATCH 0044/2465] properly detect and disallow cycles in extends tag --- CHANGES.md | 1 + .../hubspot/jinjava/interpret/Context.java | 22 ++++++++++ .../interpret/ExtendsTagCycleException.java | 17 +++++++ .../hubspot/jinjava/lib/tag/ExtendsTag.java | 44 ++++++++++--------- .../jinjava/lib/tag/ExtendsTagTest.java | 27 ++++++++++++ .../tags/extendstag/a-extends-b.jinja | 2 + .../tags/extendstag/b-extends-a.jinja | 2 + .../tags/extendstag/extends-self.jinja | 4 ++ 8 files changed, 98 insertions(+), 21 deletions(-) create mode 100644 src/main/java/com/hubspot/jinjava/interpret/ExtendsTagCycleException.java create mode 100644 src/test/resources/tags/extendstag/a-extends-b.jinja create mode 100644 src/test/resources/tags/extendstag/b-extends-a.jinja create mode 100644 src/test/resources/tags/extendstag/extends-self.jinja diff --git a/CHANGES.md b/CHANGES.md index aa6283ff8..722e4a6e7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ ### Version 2.0.11 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.0.11%22)) ### * Renaming JinjavaInterpreter.renderString() to renderFlat() to better signify its purpose +* Fix bug to properly detect cycles in extends tag ### Version 2.0.10 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.0.10%22)) ### diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index 0af515e0d..f7e5e2953 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -39,6 +40,7 @@ public class Context extends ScopeMap { public static final String GLOBAL_MACROS_SCOPE_KEY = "__macros__"; private final SetMultimap dependencies = HashMultimap.create(); + private final LinkedHashSet extendPaths = new LinkedHashSet<>(); private final ExpTestLibrary expTestLibrary; private final FilterLibrary filterLibrary; @@ -219,6 +221,26 @@ public void registerTag(Tag t) { tagLibrary.addTag(t); } + public void addExtendPath(String path, int lineNumber) { + if (containsExtendsPath(path)) { + throw new ExtendsTagCycleException(path, lineNumber); + } + + extendPaths.add(path); + } + + public boolean containsExtendsPath(String path) { + if (extendPaths.contains(path)) { + return true; + } + + if (parent != null) { + return parent.containsExtendsPath(path); + } + + return false; + } + public void addDependency(String type, String identification) { Context highestParentContext = getHighestParentContext(); highestParentContext.dependencies.get(type).add(identification); diff --git a/src/main/java/com/hubspot/jinjava/interpret/ExtendsTagCycleException.java b/src/main/java/com/hubspot/jinjava/interpret/ExtendsTagCycleException.java new file mode 100644 index 000000000..0157fec42 --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/interpret/ExtendsTagCycleException.java @@ -0,0 +1,17 @@ +package com.hubspot.jinjava.interpret; + +public class ExtendsTagCycleException extends InterpretException { + private static final long serialVersionUID = -3058494056577268723L; + + private final String path; + + public ExtendsTagCycleException(String path, int lineNumber) { + super("Extends tag cycle for path '" + path + "'", lineNumber); + this.path = path; + } + + public String getPath() { + return path; + } + +} diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java index 3eb52669f..932556ae2 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java @@ -1,17 +1,17 @@ /********************************************************************** -Copyright (c) 2014 HubSpot 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. + * Copyright (c) 2014 HubSpot 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.hubspot.jinjava.lib.tag; @@ -26,8 +26,9 @@ import com.hubspot.jinjava.tree.TagNode; import com.hubspot.jinjava.util.HelperStringTokenizer; -@JinjavaDoc(value = "Template inheritance allows you to build a base “skeleton” template that contains all the " - + "common elements of your site and defines blocks that child templates can override.", +@JinjavaDoc( + value = "Template inheritance allows you to build a base “skeleton” template that contains all the " + + "common elements of your site and defines blocks that child templates can override.", params = { @JinjavaParam(value = "path", desc = "Design Manager file path to parent template") }, @@ -74,9 +75,7 @@ "{% endblock %}") }) public class ExtendsTag implements Tag { - private static final long serialVersionUID = 4692863362280761393L; - private static final String TAGNAME = "extends"; @Override public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { @@ -84,12 +83,15 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { if (!tokenizer.hasNext()) { throw new InterpretException("Tag 'extends' expects template path", tagNode.getLineNumber()); } - String templateFile = interpreter.resolveString(tokenizer.next(), tagNode.getLineNumber()); + + String path = interpreter.resolveString(tokenizer.next(), tagNode.getLineNumber()); + interpreter.getContext().addExtendPath(path, tagNode.getLineNumber()); + try { - String template = interpreter.getResource(templateFile); + String template = interpreter.getResource(path); Node node = interpreter.parse(template); JinjavaInterpreter child = new JinjavaInterpreter(interpreter); - child.getContext().addDependency("coded_files", templateFile); + child.getContext().addDependency("coded_files", path); interpreter.addExtendParentRoot(node); return ""; } catch (IOException e) { @@ -104,7 +106,7 @@ public String getEndTagName() { @Override public String getName() { - return TAGNAME; + return "extends"; } } diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/ExtendsTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/ExtendsTagTest.java index c52b75516..2acaf03bb 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/ExtendsTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/ExtendsTagTest.java @@ -1,6 +1,7 @@ package com.hubspot.jinjava.lib.tag; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; import java.io.IOException; import java.nio.charset.Charset; @@ -16,6 +17,8 @@ import com.google.common.io.Resources; import com.hubspot.jinjava.Jinjava; import com.hubspot.jinjava.JinjavaConfig; +import com.hubspot.jinjava.interpret.ExtendsTagCycleException; +import com.hubspot.jinjava.interpret.FatalTemplateErrorsException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.RenderResult; import com.hubspot.jinjava.loader.ResourceLocator; @@ -75,6 +78,30 @@ public void itHasExtendsReferenceInContext() throws Exception { assertThat(dependencies.get("coded_files").contains("super-base.html")); } + @Test + public void itAvoidsSimpleExtendsCycles() throws IOException { + try { + jinjava.render(locator.fixture("extends-self.jinja"), + new HashMap()); + fail("expected extends tag cycle exception"); + } catch (FatalTemplateErrorsException e) { + ExtendsTagCycleException cycleException = (ExtendsTagCycleException) e.getErrors().iterator().next().getException(); + assertThat(cycleException.getPath()).isEqualTo("extends-self.jinja"); + } + } + + @Test + public void itAvoidsNestedExtendsCycles() throws IOException { + try { + jinjava.render(locator.fixture("a-extends-b.jinja"), + new HashMap()); + fail("expected extends tag cycle exception"); + } catch (FatalTemplateErrorsException e) { + ExtendsTagCycleException cycleException = (ExtendsTagCycleException) e.getErrors().iterator().next().getException(); + assertThat(cycleException.getPath()).isEqualTo("b-extends-a.jinja"); + } + } + private static class ExtendsTagTestResourceLocator implements ResourceLocator { @Override public String getString(String fullName, Charset encoding, JinjavaInterpreter interpreter) throws IOException { diff --git a/src/test/resources/tags/extendstag/a-extends-b.jinja b/src/test/resources/tags/extendstag/a-extends-b.jinja new file mode 100644 index 000000000..e14bed598 --- /dev/null +++ b/src/test/resources/tags/extendstag/a-extends-b.jinja @@ -0,0 +1,2 @@ +{% extends "b-extends-a.jinja" %} +A diff --git a/src/test/resources/tags/extendstag/b-extends-a.jinja b/src/test/resources/tags/extendstag/b-extends-a.jinja new file mode 100644 index 000000000..424005e58 --- /dev/null +++ b/src/test/resources/tags/extendstag/b-extends-a.jinja @@ -0,0 +1,2 @@ +{% extends "a-extends-b.jinja" %} +B diff --git a/src/test/resources/tags/extendstag/extends-self.jinja b/src/test/resources/tags/extendstag/extends-self.jinja new file mode 100644 index 000000000..9aa729054 --- /dev/null +++ b/src/test/resources/tags/extendstag/extends-self.jinja @@ -0,0 +1,4 @@ +{% extends "extends-self.jinja" %} +{% block header %} +hello world +{% endblock %} From a60a4d3b96cf1af461ee88f4f80288b1f42d8073 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Tue, 13 Oct 2015 11:30:02 -0400 Subject: [PATCH 0045/2465] correct changelog --- CHANGES.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 722e4a6e7..1f67c429e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,9 +1,13 @@ # Jinjava Releases # +### Version 2.0.12 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.0.11%22)) ### + +* Fix bug to properly detect cycles in extends tag + ### Version 2.0.11 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.0.11%22)) ### * Renaming JinjavaInterpreter.renderString() to renderFlat() to better signify its purpose -* Fix bug to properly detect cycles in extends tag +* Released build for jdk7 as version 2.0.11-java7 ### Version 2.0.10 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.0.10%22)) ### From deab4c36e4a4fc5e5019d1ace1da868bcd5e3e42 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Wed, 14 Oct 2015 14:59:27 -0400 Subject: [PATCH 0046/2465] Update pom.xml --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dd3614c1b..75189f9d0 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.hubspot basepom - 12.4 + 12.5 com.hubspot.jinjava From 86cc8f412a422cfb0afd54f85532c7e4bb02d282 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Wed, 14 Oct 2015 15:09:32 -0400 Subject: [PATCH 0047/2465] fix dep mgmt --- pom.xml | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 75189f9d0..8b95055cd 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 @@ -49,22 +50,18 @@ org.jsoup jsoup - 1.8.1 de.odysseus.juel juel-api - 2.2.7 de.odysseus.juel juel-impl - 2.2.7 de.odysseus.juel juel-spi - 2.2.7 runtime @@ -103,6 +100,31 @@ + + + + org.jsoup + jsoup + 1.8.1 + + + de.odysseus.juel + juel-api + 2.2.7 + + + de.odysseus.juel + juel-impl + 2.2.7 + + + de.odysseus.juel + juel-spi + 2.2.7 + + + + From ebab6e3ab283cff99f8172e908501c868520a94a Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Thu, 15 Oct 2015 17:08:18 -0400 Subject: [PATCH 0048/2465] prevent infinite recursion in resolveBlockStubs when a block contains a self-ref --- .../jinjava/interpret/JinjavaInterpreter.java | 30 ++++++++++++------- .../interpret/JinjavaInterpreterTest.java | 6 ++++ 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index e3958c00f..4dde1fc8c 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -204,6 +204,10 @@ public String render(Node root, boolean processExtendRoots) { } String resolveBlockStubs(CharSequence content) { + return resolveBlockStubs(content, new Stack<>()); + } + + String resolveBlockStubs(CharSequence content, Stack blockNames) { StringBuilder result = null; int pos = 0, start, end, stubStartLen = BLOCK_STUB_START.length(); @@ -218,22 +222,26 @@ String resolveBlockStubs(CharSequence content) { String blockValue = ""; - Collection> blockChain = blocks.get(blockName); - List block = Iterables.getFirst(blockChain, null); + if (!blockNames.contains(blockName)) { + Collection> blockChain = blocks.get(blockName); + List block = Iterables.getFirst(blockChain, null); - if (block != null) { - List superBlock = Iterables.get(blockChain, 1, null); - context.put(BLOCK_SUPER_REF, superBlock); + if (block != null) { + List superBlock = Iterables.get(blockChain, 1, null); + context.put(BLOCK_SUPER_REF, superBlock); - StringBuilder blockValueBuilder = new StringBuilder(); + StringBuilder blockValueBuilder = new StringBuilder(); - for (Node child : block) { - blockValueBuilder.append(child.render(this)); - } + for (Node child : block) { + blockValueBuilder.append(child.render(this)); + } - blockValue = resolveBlockStubs(blockValueBuilder); + blockNames.push(blockName); + blockValue = resolveBlockStubs(blockValueBuilder, blockNames); + blockNames.pop(); - context.remove(BLOCK_SUPER_REF); + context.remove(BLOCK_SUPER_REF); + } } result.append(content.subSequence(pos, start)); diff --git a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java index 01a05a16b..543cdabdc 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java @@ -49,6 +49,12 @@ public void resolveBlockStubsWithSpecialChars() throws Exception { assertThat(interpreter.resolveBlockStubs(content)).isEqualTo("this is $150.00!"); } + @Test + public void resolveBlockStubsWithCycle() throws Exception { + String content = interpreter.render("{% block foo %}{% block foo %}{% endblock %}{% endblock %}"); + System.out.println(content); + } + // Ex VariableChain stuff static class Foo { From b7069a689487c0d087588afec5deed4873423d9b Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Thu, 15 Oct 2015 17:09:11 -0400 Subject: [PATCH 0049/2465] updating changelog --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 1f67c429e..18a9d73dc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ ### Version 2.0.12 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.0.11%22)) ### * Fix bug to properly detect cycles in extends tag +* Fix infinite recursion bug in resolveBlockStubs when block contains self-reference ### Version 2.0.11 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.0.11%22)) ### From 141efded29412b1211340bbbe39050d668b01670 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Thu, 15 Oct 2015 22:38:22 -0400 Subject: [PATCH 0050/2465] refactor how block stubs are placed/resolved in render output --- .../jinjava/interpret/JinjavaInterpreter.java | 62 +++++++------------ .../com/hubspot/jinjava/lib/tag/BlockTag.java | 19 +++--- .../java/com/hubspot/jinjava/lib/tag/Tag.java | 32 ++++++---- .../hubspot/jinjava/tree/ExpressionNode.java | 6 +- .../java/com/hubspot/jinjava/tree/Node.java | 3 +- .../com/hubspot/jinjava/tree/RootNode.java | 29 ++++----- .../com/hubspot/jinjava/tree/TagNode.java | 31 +++++----- .../com/hubspot/jinjava/tree/TextNode.java | 32 +++++----- .../output/BlockPlaceholderOutputNode.java | 38 ++++++++++++ .../jinjava/tree/output/OutputList.java | 38 ++++++++++++ .../jinjava/tree/output/OutputNode.java | 7 +++ .../tree/output/RenderedOutputNode.java | 21 +++++++ .../interpret/JinjavaInterpreterTest.java | 16 ++--- .../hubspot/jinjava/lib/tag/MacroTagTest.java | 19 +++--- .../jinjava/tree/ExpressionNodeTest.java | 6 +- 15 files changed, 233 insertions(+), 126 deletions(-) create mode 100644 src/main/java/com/hubspot/jinjava/tree/output/BlockPlaceholderOutputNode.java create mode 100644 src/main/java/com/hubspot/jinjava/tree/output/OutputList.java create mode 100644 src/main/java/com/hubspot/jinjava/tree/output/OutputNode.java create mode 100644 src/main/java/com/hubspot/jinjava/tree/output/RenderedOutputNode.java diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index 4dde1fc8c..8af625802 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -35,6 +35,8 @@ import com.hubspot.jinjava.el.ExpressionResolver; import com.hubspot.jinjava.tree.Node; import com.hubspot.jinjava.tree.TreeParser; +import com.hubspot.jinjava.tree.output.BlockPlaceholderOutputNode; +import com.hubspot.jinjava.tree.output.OutputList; import com.hubspot.jinjava.util.Variable; import com.hubspot.jinjava.util.WhitespaceUtils; @@ -182,78 +184,64 @@ public String render(Node root) { * @return rendered result */ public String render(Node root, boolean processExtendRoots) { - StringBuilder buff = new StringBuilder(); + OutputList output = new OutputList(); for (Node node : root.getChildren()) { - buff.append(node.render(this)); + output.addNode(node.render(this)); } // render all extend parents, keeping the last as the root output if (processExtendRoots) { while (!extendParentRoots.isEmpty()) { Node parentRoot = extendParentRoots.removeFirst(); - buff = new StringBuilder(); + output = new OutputList(); for (Node node : parentRoot.getChildren()) { - buff.append(node.render(this)); + output.addNode(node.render(this)); } } } - return resolveBlockStubs(buff); - } + resolveBlockStubs(output); - String resolveBlockStubs(CharSequence content) { - return resolveBlockStubs(content, new Stack<>()); + return output.getValue(); } - String resolveBlockStubs(CharSequence content, Stack blockNames) { - StringBuilder result = null; - int pos = 0, start, end, stubStartLen = BLOCK_STUB_START.length(); - - while ((start = StringUtils.indexOf(content, BLOCK_STUB_START, pos)) != -1) { - if (result == null) { - result = new StringBuilder(content.length() + 256); - } - - end = StringUtils.indexOf(content, BLOCK_STUB_END, start + stubStartLen); - - String blockName = content.subSequence(start + stubStartLen, end).toString(); + private void resolveBlockStubs(OutputList output) { + resolveBlockStubs(output, new Stack<>()); + } - String blockValue = ""; + private void resolveBlockStubs(OutputList output, Stack blockNames) { + for (BlockPlaceholderOutputNode blockPlaceholder : output.getBlocks()) { - if (!blockNames.contains(blockName)) { - Collection> blockChain = blocks.get(blockName); + if (!blockNames.contains(blockPlaceholder.getBlockName())) { + Collection> blockChain = blocks.get(blockPlaceholder.getBlockName()); List block = Iterables.getFirst(blockChain, null); if (block != null) { List superBlock = Iterables.get(blockChain, 1, null); context.put(BLOCK_SUPER_REF, superBlock); - StringBuilder blockValueBuilder = new StringBuilder(); + OutputList blockValueBuilder = new OutputList(); for (Node child : block) { - blockValueBuilder.append(child.render(this)); + blockValueBuilder.addNode(child.render(this)); } - blockNames.push(blockName); - blockValue = resolveBlockStubs(blockValueBuilder, blockNames); + blockNames.push(blockPlaceholder.getBlockName()); + resolveBlockStubs(blockValueBuilder, blockNames); blockNames.pop(); context.remove(BLOCK_SUPER_REF); + + blockPlaceholder.resolve(blockValueBuilder.getValue()); } } - result.append(content.subSequence(pos, start)); - result.append(blockValue); - pos = end + 1; - } - - if (result != null) { - return result.append(content.subSequence(pos, content.length())).toString(); + if (!blockPlaceholder.isResolved()) { + blockPlaceholder.resolve(""); + } } - - return content.toString(); } /** @@ -405,8 +393,6 @@ public static void popCurrent() { } } - public static final String BLOCK_STUB_START = "___bl0ck___~"; - public static final String BLOCK_STUB_END = "~"; public static final String BLOCK_SUPER_REF = "__superbl0ck__"; } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/BlockTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/BlockTag.java index 7ab6513dc..7b749b19f 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/BlockTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/BlockTag.java @@ -21,6 +21,8 @@ import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.tree.TagNode; +import com.hubspot.jinjava.tree.output.BlockPlaceholderOutputNode; +import com.hubspot.jinjava.tree.output.OutputNode; import com.hubspot.jinjava.util.HelperStringTokenizer; import com.hubspot.jinjava.util.WhitespaceUtils; @@ -41,13 +43,10 @@ "{% endblock %}"), }) public class BlockTag implements Tag { - private static final long serialVersionUID = -2362317415797088108L; - private static final String TAGNAME = "block"; - private static final String ENDTAGNAME = "endblock"; @Override - public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { + public OutputNode interpretOutput(TagNode tagNode, JinjavaInterpreter interpreter) { HelperStringTokenizer tagData = new HelperStringTokenizer(tagNode.getHelpers()); if (!tagData.hasNext()) { throw new InterpretException("Tag 'block' expects an identifier", tagNode.getLineNumber()); @@ -56,17 +55,23 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { String blockName = WhitespaceUtils.unquote(tagData.next()); interpreter.addBlock(blockName, tagNode.getChildren()); - return JinjavaInterpreter.BLOCK_STUB_START + blockName + JinjavaInterpreter.BLOCK_STUB_END; + + return new BlockPlaceholderOutputNode(blockName); + } + + @Override + public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { + throw new UnsupportedOperationException("BlockTag must be rendered directly via interpretOutput() method"); } @Override public String getEndTagName() { - return ENDTAGNAME; + return "endblock"; } @Override public String getName() { - return TAGNAME; + return "block"; } } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/Tag.java b/src/main/java/com/hubspot/jinjava/lib/tag/Tag.java index 8c36b30e1..1e6be6cd4 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/Tag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/Tag.java @@ -1,17 +1,17 @@ /********************************************************************** -Copyright (c) 2014 HubSpot 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. + * Copyright (c) 2014 HubSpot 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.hubspot.jinjava.lib.tag; @@ -20,9 +20,15 @@ import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.lib.Importable; import com.hubspot.jinjava.tree.TagNode; +import com.hubspot.jinjava.tree.output.OutputNode; +import com.hubspot.jinjava.tree.output.RenderedOutputNode; public interface Tag extends Importable, Serializable { + default OutputNode interpretOutput(TagNode tagNode, JinjavaInterpreter interpreter) { + return new RenderedOutputNode(interpret(tagNode, interpreter)); + } + String interpret(TagNode tagNode, JinjavaInterpreter interpreter); /** diff --git a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java index 2d19fbe73..315e8ba30 100644 --- a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java @@ -22,6 +22,8 @@ import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.lib.filter.EscapeFilter; import com.hubspot.jinjava.lib.tag.AutoEscapeTag; +import com.hubspot.jinjava.tree.output.OutputNode; +import com.hubspot.jinjava.tree.output.RenderedOutputNode; import com.hubspot.jinjava.tree.parse.ExpressionToken; import com.hubspot.jinjava.util.Logging; @@ -36,7 +38,7 @@ public ExpressionNode(ExpressionToken token) { } @Override - public String render(JinjavaInterpreter interpreter) { + public OutputNode render(JinjavaInterpreter interpreter) { Object var = interpreter.resolveELExpression(master.getExpr(), getLineNumber()); String result = Objects.toString(var, ""); @@ -53,7 +55,7 @@ public String render(JinjavaInterpreter interpreter) { result = EscapeFilter.escapeHtmlEntities(result); } - return result; + return new RenderedOutputNode(result); } @Override diff --git a/src/main/java/com/hubspot/jinjava/tree/Node.java b/src/main/java/com/hubspot/jinjava/tree/Node.java index c6b9cbecb..edeaa96a1 100644 --- a/src/main/java/com/hubspot/jinjava/tree/Node.java +++ b/src/main/java/com/hubspot/jinjava/tree/Node.java @@ -21,6 +21,7 @@ import org.apache.commons.lang3.StringUtils; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.tree.output.OutputNode; import com.hubspot.jinjava.tree.parse.Token; public abstract class Node implements Serializable { @@ -62,7 +63,7 @@ public void setChildren(LinkedList children) { this.children = children; } - public abstract String render(JinjavaInterpreter interpreter); + public abstract OutputNode render(JinjavaInterpreter interpreter); public abstract String getName(); diff --git a/src/main/java/com/hubspot/jinjava/tree/RootNode.java b/src/main/java/com/hubspot/jinjava/tree/RootNode.java index 4d8c80bd1..93412ab18 100644 --- a/src/main/java/com/hubspot/jinjava/tree/RootNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/RootNode.java @@ -1,21 +1,22 @@ /********************************************************************** -Copyright (c) 2014 HubSpot 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. + * Copyright (c) 2014 HubSpot 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.hubspot.jinjava.tree; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.tree.output.OutputNode; public class RootNode extends Node { private static final long serialVersionUID = 97675838726004658L; @@ -25,7 +26,7 @@ public class RootNode extends Node { } @Override - public String render(JinjavaInterpreter interpreter) { + public OutputNode render(JinjavaInterpreter interpreter) { throw new UnsupportedOperationException("Please render RootNode by interpreter"); } diff --git a/src/main/java/com/hubspot/jinjava/tree/TagNode.java b/src/main/java/com/hubspot/jinjava/tree/TagNode.java index 7d797e42d..f33fedc01 100644 --- a/src/main/java/com/hubspot/jinjava/tree/TagNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/TagNode.java @@ -1,23 +1,24 @@ /********************************************************************** -Copyright (c) 2014 HubSpot 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. + * Copyright (c) 2014 HubSpot 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.hubspot.jinjava.tree; import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.lib.tag.Tag; +import com.hubspot.jinjava.tree.output.OutputNode; import com.hubspot.jinjava.tree.parse.TagToken; public class TagNode extends Node { @@ -44,9 +45,9 @@ private TagNode(TagNode n) { } @Override - public String render(JinjavaInterpreter interpreter) { + public OutputNode render(JinjavaInterpreter interpreter) { try { - return tag.interpret(this, interpreter); + return tag.interpretOutput(this, interpreter); } catch (InterpretException e) { throw e; } catch (Exception e) { diff --git a/src/main/java/com/hubspot/jinjava/tree/TextNode.java b/src/main/java/com/hubspot/jinjava/tree/TextNode.java index 4bc0e3f35..6af66bddc 100644 --- a/src/main/java/com/hubspot/jinjava/tree/TextNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/TextNode.java @@ -1,21 +1,23 @@ /********************************************************************** -Copyright (c) 2014 HubSpot 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. + * Copyright (c) 2014 HubSpot 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.hubspot.jinjava.tree; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.tree.output.OutputNode; +import com.hubspot.jinjava.tree.output.RenderedOutputNode; import com.hubspot.jinjava.tree.parse.TextToken; public class TextNode extends Node { @@ -29,8 +31,8 @@ public TextNode(TextToken token) { } @Override - public String render(JinjavaInterpreter interpreter) { - return master.output(); + public OutputNode render(JinjavaInterpreter interpreter) { + return new RenderedOutputNode(master.output()); } @Override diff --git a/src/main/java/com/hubspot/jinjava/tree/output/BlockPlaceholderOutputNode.java b/src/main/java/com/hubspot/jinjava/tree/output/BlockPlaceholderOutputNode.java new file mode 100644 index 000000000..524b1e919 --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/tree/output/BlockPlaceholderOutputNode.java @@ -0,0 +1,38 @@ +package com.hubspot.jinjava.tree.output; + +public class BlockPlaceholderOutputNode implements OutputNode { + + private final String blockName; + private String output; + + public BlockPlaceholderOutputNode(String blockName) { + this.blockName = blockName; + } + + public String getBlockName() { + return blockName; + } + + public boolean isResolved() { + return output != null; + } + + public void resolve(String output) { + this.output = output; + } + + @Override + public String getValue() { + if (output == null) { + throw new IllegalStateException("Block placeholder not resolved: " + blockName); + } + + return output; + } + + @Override + public String toString() { + return getValue(); + } + +} diff --git a/src/main/java/com/hubspot/jinjava/tree/output/OutputList.java b/src/main/java/com/hubspot/jinjava/tree/output/OutputList.java new file mode 100644 index 000000000..313fcb18a --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/tree/output/OutputList.java @@ -0,0 +1,38 @@ +package com.hubspot.jinjava.tree.output; + +import java.util.LinkedList; +import java.util.List; + +public class OutputList { + + private final List nodes = new LinkedList<>(); + private final List blocks = new LinkedList<>(); + + public void addNode(OutputNode node) { + nodes.add(node); + + if (node instanceof BlockPlaceholderOutputNode) { + blocks.add((BlockPlaceholderOutputNode) node); + } + } + + public List getBlocks() { + return blocks; + } + + public String getValue() { + StringBuilder val = new StringBuilder(); + + for (OutputNode node : nodes) { + val.append(node.getValue()); + } + + return val.toString(); + } + + @Override + public String toString() { + return getValue(); + } + +} diff --git a/src/main/java/com/hubspot/jinjava/tree/output/OutputNode.java b/src/main/java/com/hubspot/jinjava/tree/output/OutputNode.java new file mode 100644 index 000000000..a02e07abd --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/tree/output/OutputNode.java @@ -0,0 +1,7 @@ +package com.hubspot.jinjava.tree.output; + +public interface OutputNode { + + String getValue(); + +} diff --git a/src/main/java/com/hubspot/jinjava/tree/output/RenderedOutputNode.java b/src/main/java/com/hubspot/jinjava/tree/output/RenderedOutputNode.java new file mode 100644 index 000000000..efb9938ce --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/tree/output/RenderedOutputNode.java @@ -0,0 +1,21 @@ +package com.hubspot.jinjava.tree.output; + +public class RenderedOutputNode implements OutputNode { + + private final String output; + + public RenderedOutputNode(String output) { + this.output = output; + } + + @Override + public String getValue() { + return output; + } + + @Override + public String toString() { + return getValue(); + } + +} diff --git a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java index 543cdabdc..84d481413 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java @@ -26,33 +26,33 @@ public void setup() { @Test public void resolveBlockStubsWithNoStubs() { - assertThat(interpreter.resolveBlockStubs("foo")).isEqualTo("foo"); + assertThat(interpreter.render("foo")).isEqualTo("foo"); } @Test public void resolveBlockStubsWithMissingNamedBlock() { - String content = String.format("this is %sfoobar%s!", JinjavaInterpreter.BLOCK_STUB_START, JinjavaInterpreter.BLOCK_STUB_END); - assertThat(interpreter.resolveBlockStubs(content)).isEqualTo("this is !"); + String content = "this is {% block foobar %}{% endblock %}!"; + assertThat(interpreter.render(content)).isEqualTo("this is !"); } @Test public void resolveBlockStubs() throws Exception { interpreter.addBlock("foobar", Lists.newLinkedList(Lists.newArrayList((new TextNode(new TextToken("sparta", -1)))))); - String content = String.format("this is %sfoobar%s!", JinjavaInterpreter.BLOCK_STUB_START, JinjavaInterpreter.BLOCK_STUB_END); - assertThat(interpreter.resolveBlockStubs(content)).isEqualTo("this is sparta!"); + String content = "this is {% block foobar %}foobar{% endblock %}!"; + assertThat(interpreter.render(content)).isEqualTo("this is sparta!"); } @Test public void resolveBlockStubsWithSpecialChars() throws Exception { interpreter.addBlock("foobar", Lists.newLinkedList(Lists.newArrayList(new TextNode(new TextToken("$150.00", -1))))); - String content = String.format("this is %sfoobar%s!", JinjavaInterpreter.BLOCK_STUB_START, JinjavaInterpreter.BLOCK_STUB_END); - assertThat(interpreter.resolveBlockStubs(content)).isEqualTo("this is $150.00!"); + String content = "this is {% block foobar %}foobar{% endblock %}!"; + assertThat(interpreter.render(content)).isEqualTo("this is $150.00!"); } @Test public void resolveBlockStubsWithCycle() throws Exception { String content = interpreter.render("{% block foo %}{% block foo %}{% endblock %}{% endblock %}"); - System.out.println(content); + assertThat(content).isEmpty(); } // Ex VariableChain stuff diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java index 80cf2a952..71ee12101 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java @@ -48,7 +48,7 @@ public void cleanup() { @Test public void testSimpleFn() { TagNode t = fixture("simple"); - assertThat(t.render(interpreter)).isEmpty(); + assertThat(t.render(interpreter).getValue()).isEmpty(); MacroFunction fn = (MacroFunction) interpreter.resolveObject("__macros__.getPath", -1); assertThat(fn.getName()).isEqualTo("getPath"); @@ -58,40 +58,40 @@ public void testSimpleFn() { assertThat(fn.isCatchVarargs()).isFalse(); context.put("myname", "jared"); - assertThat(snippet("{{ getPath() }}").render(interpreter).trim()).isEqualTo("Hello jared"); + assertThat(snippet("{{ getPath() }}").render(interpreter).getValue().trim()).isEqualTo("Hello jared"); } @Test public void testFnWithArgs() { TagNode t = fixture("with-args"); - assertThat(t.render(interpreter)).isEmpty(); + assertThat(t.render(interpreter).getValue()).isEmpty(); MacroFunction fn = (MacroFunction) interpreter.resolveObject("__macros__.section_link", -1); assertThat(fn.getName()).isEqualTo("section_link"); assertThat(fn.getArguments()).containsExactly("link", "text"); - assertThat(snippet("{{section_link('mylink', 'mytext')}}").render(interpreter).trim()).isEqualTo("link: mylink, text: mytext"); + assertThat(snippet("{{section_link('mylink', 'mytext')}}").render(interpreter).getValue().trim()).isEqualTo("link: mylink, text: mytext"); } @Test public void testFnWithArgsWithDefVals() { TagNode t = fixture("def-vals"); - assertThat(t.render(interpreter)).isEmpty(); + assertThat(t.render(interpreter).getValue()).isEmpty(); MacroFunction fn = (MacroFunction) interpreter.resolveObject("__macros__.article", -1); assertThat(fn.getArguments()).containsExactly("title", "thumb", "link", "summary", "last"); assertThat(fn.getDefaults()).contains(entry("last", false)); - assertThat(snippet("{{ article('mytitle','mythumb','mylink','mysummary') }}").render(interpreter).trim()) + assertThat(snippet("{{ article('mytitle','mythumb','mylink','mysummary') }}").render(interpreter).getValue().trim()) .isEqualTo("title: mytitle, thumb: mythumb, link: mylink, summary: mysummary, last: false"); - assertThat(snippet("{{ article('mytitle','mythumb','mylink','mysummary', last=true) }}").render(interpreter).trim()) + assertThat(snippet("{{ article('mytitle','mythumb','mylink','mysummary', last=true) }}").render(interpreter).getValue().trim()) .isEqualTo("title: mytitle, thumb: mythumb, link: mylink, summary: mysummary, last: true"); } @Test public void testFnWithArrayDefVal() { TagNode t = fixture("array-def-val"); - assertThat(t.render(interpreter)).isEmpty(); + assertThat(t.render(interpreter).getValue()).isEmpty(); MacroFunction fn = (MacroFunction) interpreter.resolveObject("__macros__.prefix", -1); assertThat(fn.getArguments()).containsExactly("property", "value", "prefixes", "prefixval"); @@ -105,8 +105,7 @@ public void testMacroUsedInForLoop() throws Exception { "tools_body_1", ImmutableMap.of("html", "body1"), "tools_body_2", ImmutableMap.of("html", "body2"), "tools_body_3", ImmutableMap.of("html", "body3"), - "tools_body_4", ImmutableMap.of("html", "body4") - )); + "tools_body_4", ImmutableMap.of("html", "body4"))); Document dom = Jsoup.parseBodyFragment(new Jinjava().render(Resources.toString(Resources.getResource(String.format("tags/macrotag/%s.jinja", "macro-used-in-forloop")), StandardCharsets.UTF_8), bindings)); Element tabs = dom.select(".tabs").get(0); diff --git a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java index a7e40950a..164080264 100644 --- a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java @@ -31,7 +31,7 @@ public void itRendersResultAsTemplateWhenContainingVarBlocks() throws Exception context.put("place", "world"); ExpressionNode node = fixture("simplevar"); - assertThat(node.render(interpreter)).isEqualTo("hello world"); + assertThat(node.render(interpreter).toString()).isEqualTo("hello world"); } @Test @@ -40,7 +40,7 @@ public void itAvoidsInfiniteRecursionWhenVarsContainBraceBlocks() throws Excepti context.put("place", "{{ place }}"); ExpressionNode node = fixture("simplevar"); - assertThat(node.render(interpreter)).isEqualTo("hello {{ place }}"); + assertThat(node.render(interpreter).toString()).isEqualTo("hello {{ place }}"); } @Test @@ -65,7 +65,7 @@ public void itEscapesValueWhenContextSet() throws Exception { } private String val(String jinja) { - return parse(jinja).render(interpreter); + return parse(jinja).render(interpreter).getValue(); } private ExpressionNode parse(String jinja) { From 9283ce6cc0f15360adf2174f346b873bcdf15ff5 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Fri, 16 Oct 2015 07:48:31 -0400 Subject: [PATCH 0051/2465] fix trailing whitespace --- .../java/com/hubspot/jinjava/lib/tag/Tag.java | 6 ++-- .../com/hubspot/jinjava/tree/RootNode.java | 6 ++-- .../com/hubspot/jinjava/tree/TagNode.java | 6 ++-- .../com/hubspot/jinjava/tree/TextNode.java | 6 ++-- .../tree/output/RenderedOutputNode.java | 2 +- .../hubspot/jinjava/util/ObjectIterator.java | 36 +++++++++---------- 6 files changed, 29 insertions(+), 33 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/Tag.java b/src/main/java/com/hubspot/jinjava/lib/tag/Tag.java index 1e6be6cd4..a2c87960f 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/Tag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/Tag.java @@ -1,12 +1,12 @@ /********************************************************************** * Copyright (c) 2014 HubSpot 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. diff --git a/src/main/java/com/hubspot/jinjava/tree/RootNode.java b/src/main/java/com/hubspot/jinjava/tree/RootNode.java index 93412ab18..bf9512a0d 100644 --- a/src/main/java/com/hubspot/jinjava/tree/RootNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/RootNode.java @@ -1,12 +1,12 @@ /********************************************************************** * Copyright (c) 2014 HubSpot 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. diff --git a/src/main/java/com/hubspot/jinjava/tree/TagNode.java b/src/main/java/com/hubspot/jinjava/tree/TagNode.java index f33fedc01..c79cd3fab 100644 --- a/src/main/java/com/hubspot/jinjava/tree/TagNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/TagNode.java @@ -1,12 +1,12 @@ /********************************************************************** * Copyright (c) 2014 HubSpot 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. diff --git a/src/main/java/com/hubspot/jinjava/tree/TextNode.java b/src/main/java/com/hubspot/jinjava/tree/TextNode.java index 6af66bddc..e9b5fccb1 100644 --- a/src/main/java/com/hubspot/jinjava/tree/TextNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/TextNode.java @@ -1,12 +1,12 @@ /********************************************************************** * Copyright (c) 2014 HubSpot 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. diff --git a/src/main/java/com/hubspot/jinjava/tree/output/RenderedOutputNode.java b/src/main/java/com/hubspot/jinjava/tree/output/RenderedOutputNode.java index efb9938ce..4b4eddffc 100644 --- a/src/main/java/com/hubspot/jinjava/tree/output/RenderedOutputNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/output/RenderedOutputNode.java @@ -17,5 +17,5 @@ public String getValue() { public String toString() { return getValue(); } - + } diff --git a/src/main/java/com/hubspot/jinjava/util/ObjectIterator.java b/src/main/java/com/hubspot/jinjava/util/ObjectIterator.java index 7ba55c04a..94d6c1d1a 100644 --- a/src/main/java/com/hubspot/jinjava/util/ObjectIterator.java +++ b/src/main/java/com/hubspot/jinjava/util/ObjectIterator.java @@ -1,21 +1,20 @@ /********************************************************************** -Copyright (c) 2014 HubSpot 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. + * Copyright (c) 2014 HubSpot 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.hubspot.jinjava.util; -import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.Map; @@ -24,13 +23,12 @@ public final class ObjectIterator { - private ObjectIterator() { - } + private ObjectIterator() {} @SuppressWarnings("unchecked") public static ForLoop getLoop(Object obj) { if (obj == null) { - return new ForLoop(new ArrayList().iterator(), 0); + return new ForLoop(Iterators.emptyIterator(), 0); } // collection if (obj instanceof Collection) { @@ -55,9 +53,7 @@ public static ForLoop getLoop(Object obj) { return new ForLoop((Iterator) obj); } // others - ArrayList res = new ArrayList(); - res.add(obj); - return new ForLoop(res.iterator(), 1); + return new ForLoop(Iterators.singletonIterator(obj), 1); } } From 0d40d80bde7cfbb8771f7c596ceef5910d243d04 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Fri, 16 Oct 2015 10:44:57 -0400 Subject: [PATCH 0052/2465] refactor cycle detection for include, import tags to not use special named vars --- CHANGES.md | 4 +- benchmark/pom.xml | 2 +- .../benchmarks/jinja2/Jinja2Benchmark.java | 11 +-- pom.xml | 2 +- .../hubspot/jinjava/interpret/Context.java | 93 +++++++++++++++++-- .../interpret/ExtendsTagCycleException.java | 13 +-- .../interpret/ImportTagCycleException.java | 10 ++ .../interpret/IncludeTagCycleException.java | 10 ++ .../jinjava/interpret/JinjavaInterpreter.java | 10 +- .../jinjava/interpret/TagCycleException.java | 24 +++++ .../hubspot/jinjava/lib/tag/ExtendsTag.java | 2 +- .../hubspot/jinjava/lib/tag/ImportTag.java | 50 +++------- .../hubspot/jinjava/lib/tag/IncludeTag.java | 34 +++---- .../jinjava/lib/tag/ExtendsTagTest.java | 6 +- .../jinjava/lib/tag/ImportTagTest.java | 4 + 15 files changed, 176 insertions(+), 99 deletions(-) create mode 100644 src/main/java/com/hubspot/jinjava/interpret/ImportTagCycleException.java create mode 100644 src/main/java/com/hubspot/jinjava/interpret/IncludeTagCycleException.java create mode 100644 src/main/java/com/hubspot/jinjava/interpret/TagCycleException.java diff --git a/CHANGES.md b/CHANGES.md index 18a9d73dc..87e7b2675 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,7 +1,9 @@ # Jinjava Releases # -### Version 2.0.12 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.0.11%22)) ### +### Version 2.1.0 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.0%22)) ### +* Refactored node render logic to return richer OutputNode instances, removing a need for a special intermediate string value in text output +* Refactored cycle detection in import and include tags to remove use of special named vars in context * Fix bug to properly detect cycles in extends tag * Fix infinite recursion bug in resolveBlockStubs when block contains self-reference diff --git a/benchmark/pom.xml b/benchmark/pom.xml index 8caae1441..26360cd3c 100644 --- a/benchmark/pom.xml +++ b/benchmark/pom.xml @@ -41,7 +41,7 @@ com.hubspot.jinjava jinjava - 2.0.10-SNAPSHOT + 2.1.0-SNAPSHOT commons-io diff --git a/benchmark/src/main/java/com/hubspot/jinjava/benchmarks/jinja2/Jinja2Benchmark.java b/benchmark/src/main/java/com/hubspot/jinjava/benchmarks/jinja2/Jinja2Benchmark.java index 236d37ad3..acadd8c37 100644 --- a/benchmark/src/main/java/com/hubspot/jinjava/benchmarks/jinja2/Jinja2Benchmark.java +++ b/benchmark/src/main/java/com/hubspot/jinjava/benchmarks/jinja2/Jinja2Benchmark.java @@ -16,8 +16,6 @@ import org.openjdk.jmh.annotations.State; import org.slf4j.LoggerFactory; -import ch.qos.logback.classic.Level; - import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.hubspot.jinjava.Jinjava; @@ -26,6 +24,7 @@ import com.hubspot.jinjava.loader.ResourceLocator; import com.hubspot.jinjava.tree.Node; +import ch.qos.logback.classic.Level; @State(Scope.Benchmark) public class Jinja2Benchmark { @@ -55,7 +54,7 @@ public void setup() throws IOException, NoSuchAlgorithmException { jinjava.setResourceLocator(new ResourceLocator() { @Override public String getString(String fullName, Charset encoding, JinjavaInterpreter interpreter) throws IOException { - switch(fullName) { + switch (fullName) { case "helpers.html": return helpersTemplate; case "layout.html": @@ -74,7 +73,7 @@ public String getString(String fullName, Charset encoding, JinjavaInterpreter in List users = Lists.newArrayList(new User("John Doe"), new User("Jane Doe"), new User("Peter Somewhat")); SecureRandom rnd = SecureRandom.getInstanceStrong(); List
articles = new ArrayList<>(); - for(int i = 0; i < 20; i++) { + for (int i = 0; i < 20; i++) { articles.add(new Article(i, users.get(rnd.nextInt(users.size())))); } List> navigation = Lists.newArrayList( @@ -83,8 +82,7 @@ public String getString(String fullName, Charset encoding, JinjavaInterpreter in Lists.newArrayList("foo?bar=1", "Foo with Bar"), Lists.newArrayList("foo?bar=2&s=x", "Foo with X"), Lists.newArrayList("blah", "Blub Blah"), - Lists.newArrayList("hehe", "Haha") - ); + Lists.newArrayList("hehe", "Haha")); complexBindings = ImmutableMap.of("users", users, "articles", articles, "navigation", navigation); @@ -106,6 +104,7 @@ public static void main(String[] args) throws Exception { b.setup(); System.out.println(b.realWorldishBenchmark()); System.out.println(b.precompiledBenchmark()); + System.out.println(b.precompiledBenchmark()); } } diff --git a/pom.xml b/pom.xml index 8b95055cd..4ac27d185 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ com.hubspot.jinjava jinjava - 2.0.12-SNAPSHOT + 2.1.0-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index f7e5e2953..8b5e7c43f 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -18,9 +18,10 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.Stack; import com.google.common.collect.HashMultimap; import com.google.common.collect.SetMultimap; @@ -40,7 +41,10 @@ public class Context extends ScopeMap { public static final String GLOBAL_MACROS_SCOPE_KEY = "__macros__"; private final SetMultimap dependencies = HashMultimap.create(); - private final LinkedHashSet extendPaths = new LinkedHashSet<>(); + + private final Stack extendPathStack = new Stack<>(); + private final Stack importPathStack = new Stack<>(); + private final Stack includePathStack = new Stack<>(); private final ExpTestLibrary expTestLibrary; private final FilterLibrary filterLibrary; @@ -221,26 +225,99 @@ public void registerTag(Tag t) { tagLibrary.addTag(t); } - public void addExtendPath(String path, int lineNumber) { - if (containsExtendsPath(path)) { + public void pushExtendPath(String path, int lineNumber) { + if (extendPathStackContains(path)) { throw new ExtendsTagCycleException(path, lineNumber); } - extendPaths.add(path); + extendPathStack.push(path); + } + + public boolean extendPathStackContains(String path) { + if (extendPathStack.contains(path)) { + return true; + } + + if (parent != null) { + return parent.extendPathStackContains(path); + } + + return false; + } + + public Optional popExtendPath() { + if (extendPathStack.isEmpty()) { + if (parent != null) { + return parent.popExtendPath(); + } + return Optional.empty(); + } + + return Optional.of(extendPathStack.pop()); + } + + public void pushImportPath(String path, int lineNumber) { + if (importPathStackContains(path)) { + throw new ImportTagCycleException(path, lineNumber); + } + + importPathStack.push(path); + } + + public boolean importPathStackContains(String path) { + if (importPathStack.contains(path)) { + return true; + } + + if (parent != null) { + return parent.importPathStackContains(path); + } + + return false; } - public boolean containsExtendsPath(String path) { - if (extendPaths.contains(path)) { + public Optional popImportPath() { + if (importPathStack.isEmpty()) { + if (parent != null) { + return parent.popImportPath(); + } + return Optional.empty(); + } + + return Optional.of(importPathStack.pop()); + } + + public void pushIncludePath(String path, int lineNumber) { + if (includePathStackContains(path)) { + throw new IncludeTagCycleException(path, lineNumber); + } + + includePathStack.push(path); + } + + public boolean includePathStackContains(String path) { + if (includePathStack.contains(path)) { return true; } if (parent != null) { - return parent.containsExtendsPath(path); + return parent.includePathStackContains(path); } return false; } + public Optional popIncludePath() { + if (includePathStack.isEmpty()) { + if (parent != null) { + return parent.popIncludePath(); + } + return Optional.empty(); + } + + return Optional.of(includePathStack.pop()); + } + public void addDependency(String type, String identification) { Context highestParentContext = getHighestParentContext(); highestParentContext.dependencies.get(type).add(identification); diff --git a/src/main/java/com/hubspot/jinjava/interpret/ExtendsTagCycleException.java b/src/main/java/com/hubspot/jinjava/interpret/ExtendsTagCycleException.java index 0157fec42..003fe9aad 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/ExtendsTagCycleException.java +++ b/src/main/java/com/hubspot/jinjava/interpret/ExtendsTagCycleException.java @@ -1,17 +1,10 @@ package com.hubspot.jinjava.interpret; -public class ExtendsTagCycleException extends InterpretException { - private static final long serialVersionUID = -3058494056577268723L; - - private final String path; +public class ExtendsTagCycleException extends TagCycleException { + private static final long serialVersionUID = 3183769038400532542L; public ExtendsTagCycleException(String path, int lineNumber) { - super("Extends tag cycle for path '" + path + "'", lineNumber); - this.path = path; - } - - public String getPath() { - return path; + super("Extends", path, lineNumber); } } diff --git a/src/main/java/com/hubspot/jinjava/interpret/ImportTagCycleException.java b/src/main/java/com/hubspot/jinjava/interpret/ImportTagCycleException.java new file mode 100644 index 000000000..7d5933d3a --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/interpret/ImportTagCycleException.java @@ -0,0 +1,10 @@ +package com.hubspot.jinjava.interpret; + +public class ImportTagCycleException extends TagCycleException { + private static final long serialVersionUID = 1092085697026161185L; + + public ImportTagCycleException(String path, int lineNumber) { + super("Import", path, lineNumber); + } + +} diff --git a/src/main/java/com/hubspot/jinjava/interpret/IncludeTagCycleException.java b/src/main/java/com/hubspot/jinjava/interpret/IncludeTagCycleException.java new file mode 100644 index 000000000..e14efa869 --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/interpret/IncludeTagCycleException.java @@ -0,0 +1,10 @@ +package com.hubspot.jinjava.interpret; + +public class IncludeTagCycleException extends TagCycleException { + private static final long serialVersionUID = -5487642459443650227L; + + public IncludeTagCycleException(String path, int lineNumber) { + super("Include", path, lineNumber); + } + +} diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index 8af625802..7ab8b0b1f 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -116,14 +116,6 @@ public Node parse(String template) { return new TreeParser(this, template).buildTree(); } - /** - * @deprecated see renderFlat(String template) - */ - @Deprecated - public String renderString(String template) { - return renderFlat(template); - } - /** * Parse the given string into a root Node, and then render it without processing any extend parents. * This method should be used when the template is known to not have any extends or block tags. @@ -199,6 +191,8 @@ public String render(Node root, boolean processExtendRoots) { for (Node node : parentRoot.getChildren()) { output.addNode(node.render(this)); } + + context.popExtendPath(); } } diff --git a/src/main/java/com/hubspot/jinjava/interpret/TagCycleException.java b/src/main/java/com/hubspot/jinjava/interpret/TagCycleException.java new file mode 100644 index 000000000..207e3d577 --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/interpret/TagCycleException.java @@ -0,0 +1,24 @@ +package com.hubspot.jinjava.interpret; + +public class TagCycleException extends InterpretException { + private static final long serialVersionUID = -3058494056577268723L; + + private final String path; + private final String tagName; + + public TagCycleException(String tagName, String path, int lineNumber) { + super(tagName + " tag cycle for path '" + path + "'", lineNumber); + + this.path = path; + this.tagName = tagName; + } + + public String getPath() { + return path; + } + + public String getTagName() { + return tagName; + } + +} diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java index 932556ae2..2b4634f1a 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java @@ -85,7 +85,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { } String path = interpreter.resolveString(tokenizer.next(), tagNode.getLineNumber()); - interpreter.getContext().addExtendPath(path, tagNode.getLineNumber()); + interpreter.getContext().pushExtendPath(path, tagNode.getLineNumber()); try { String template = interpreter.getResource(path); diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java index 545570a39..b56ab59ea 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java @@ -1,12 +1,8 @@ package com.hubspot.jinjava.lib.tag; -import static com.hubspot.jinjava.util.Logging.ENGINE_LOG; - import java.io.IOException; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import org.apache.commons.lang3.StringUtils; @@ -14,8 +10,12 @@ import com.hubspot.jinjava.doc.annotations.JinjavaParam; import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; import com.hubspot.jinjava.interpret.Context; +import com.hubspot.jinjava.interpret.ImportTagCycleException; import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.interpret.TemplateError; +import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; +import com.hubspot.jinjava.interpret.TemplateError.ErrorType; import com.hubspot.jinjava.lib.fn.MacroFunction; import com.hubspot.jinjava.tree.Node; import com.hubspot.jinjava.tree.TagNode; @@ -42,19 +42,15 @@ "{% endmacro %}\n" + "{% macro footer(tag, footer_text) %}\n" + "
<{{ tag }}>{{ footer_text }}
\n" + - "{% endmacro %}" - ), + "{% endmacro %}"), @JinjavaSnippet( desc = "The macro html file is imported from a different template. Macros are then accessed from the name given to the import.", code = "{% import 'custom/page/web_page_basic/my_macros.html' as header_footer %}\n" + "{{ header_footer.header('h1', 'My page title') }}\n" + - "{{ header_footer.footer('h3', 'Company footer info') }}" - ) + "{{ header_footer.footer('h3', 'Company footer info') }}") }) -@SuppressWarnings("unchecked") public class ImportTag implements Tag { private static final long serialVersionUID = 8433638845398005260L; - private static final String IMPORT_PATH_PROPERTY = "__importP@th__"; @Override public String getName() { @@ -75,17 +71,14 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { } String path = StringUtils.trimToEmpty(helper.get(0)); - if (isPathInRenderStack(interpreter.getContext(), path)) { - ENGINE_LOG.debug("Path {} is already in include stack", path); - return ""; - } - Set importedPaths = (Set) interpreter.getContext().get(IMPORT_PATH_PROPERTY); - if (importedPaths == null) { - importedPaths = new HashSet(); - interpreter.getContext().put(IMPORT_PATH_PROPERTY, importedPaths); + try { + interpreter.getContext().pushImportPath(path, tagNode.getLineNumber()); + } catch (ImportTagCycleException e) { + interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.EXCEPTION, + "Import cycle detected for path: '" + path + "'", null, tagNode.getLineNumber(), e)); + return ""; } - importedPaths.add(path); String templateFile = interpreter.resolveString(path, tagNode.getLineNumber()); try { @@ -94,8 +87,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { if (StringUtils.isBlank(contextVar)) { interpreter.render(node); - } - else { + } else { JinjavaInterpreter child = new JinjavaInterpreter(interpreter); child.render(node); @@ -116,22 +108,6 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { } } - private boolean isPathInRenderStack(Context context, String path) { - Context current = context; - do { - Set importedPaths = (Set) current.get(IMPORT_PATH_PROPERTY, new HashSet()); - - if (importedPaths.contains(path)) { - return true; - } - - current = current.getParent(); - - } while (current != null); - - return false; - } - @Override public String getEndTagName() { return null; diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java index a5fd0ca2a..29cb0d897 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java @@ -15,8 +15,6 @@ **********************************************************************/ package com.hubspot.jinjava.lib.tag; -import static com.hubspot.jinjava.util.Logging.ENGINE_LOG; - import java.io.IOException; import org.apache.commons.lang3.StringUtils; @@ -24,9 +22,12 @@ import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaParam; import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; -import com.hubspot.jinjava.interpret.Context; +import com.hubspot.jinjava.interpret.IncludeTagCycleException; import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.interpret.TemplateError; +import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; +import com.hubspot.jinjava.interpret.TemplateError.ErrorType; import com.hubspot.jinjava.tree.Node; import com.hubspot.jinjava.tree.TagNode; import com.hubspot.jinjava.util.HelperStringTokenizer; @@ -43,7 +44,6 @@ }) public class IncludeTag implements Tag { private static final long serialVersionUID = -8391753639874726854L; - private static final String INCLUDE_PATH_PROPERTY = "__includeP@th__"; @Override public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { @@ -54,8 +54,11 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { String path = StringUtils.trimToEmpty(helper.next()); - if (isPathInRenderStack(interpreter.getContext(), path)) { - ENGINE_LOG.debug("Path {} is already in include stack", path); + try { + interpreter.getContext().pushIncludePath(path, tagNode.getLineNumber()); + } catch (IncludeTagCycleException e) { + interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.EXCEPTION, + "Include cycle detected for path: '" + path + "'", null, tagNode.getLineNumber(), e)); return ""; } @@ -65,7 +68,6 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { Node node = interpreter.parse(template); JinjavaInterpreter child = new JinjavaInterpreter(interpreter); child.getContext().addDependency("coded_files", templateFile); - child.getContext().put(INCLUDE_PATH_PROPERTY, path); String result = child.render(node); interpreter.getErrors().addAll(child.getErrors()); @@ -74,25 +76,11 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { } catch (IOException e) { throw new InterpretException(e.getMessage(), e, tagNode.getLineNumber()); + } finally { + interpreter.getContext().popIncludePath(); } } - private boolean isPathInRenderStack(Context context, String path) { - Context current = context; - do { - String includePath = (String) current.get(INCLUDE_PATH_PROPERTY); - - if (StringUtils.equals(path, includePath)) { - return true; - } - - current = current.getParent(); - - } while (current != null); - - return false; - } - @Override public String getEndTagName() { return null; diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/ExtendsTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/ExtendsTagTest.java index 2acaf03bb..9e993d8bf 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/ExtendsTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/ExtendsTagTest.java @@ -17,7 +17,7 @@ import com.google.common.io.Resources; import com.hubspot.jinjava.Jinjava; import com.hubspot.jinjava.JinjavaConfig; -import com.hubspot.jinjava.interpret.ExtendsTagCycleException; +import com.hubspot.jinjava.interpret.TagCycleException; import com.hubspot.jinjava.interpret.FatalTemplateErrorsException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.RenderResult; @@ -85,7 +85,7 @@ public void itAvoidsSimpleExtendsCycles() throws IOException { new HashMap()); fail("expected extends tag cycle exception"); } catch (FatalTemplateErrorsException e) { - ExtendsTagCycleException cycleException = (ExtendsTagCycleException) e.getErrors().iterator().next().getException(); + TagCycleException cycleException = (TagCycleException) e.getErrors().iterator().next().getException(); assertThat(cycleException.getPath()).isEqualTo("extends-self.jinja"); } } @@ -97,7 +97,7 @@ public void itAvoidsNestedExtendsCycles() throws IOException { new HashMap()); fail("expected extends tag cycle exception"); } catch (FatalTemplateErrorsException e) { - ExtendsTagCycleException cycleException = (ExtendsTagCycleException) e.getErrors().iterator().next().getException(); + TagCycleException cycleException = (TagCycleException) e.getErrors().iterator().next().getException(); assertThat(cycleException.getPath()).isEqualTo("b-extends-a.jinja"); } } diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/ImportTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/ImportTagTest.java index 5c5d3bb83..6cc845a7a 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/ImportTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/ImportTagTest.java @@ -55,6 +55,8 @@ public void itAvoidsSimpleImportCycle() throws IOException { interpreter.render(Resources.toString(Resources.getResource("tags/importtag/imports-self.jinja"), StandardCharsets.UTF_8)); assertThat(context.get("c")).isEqualTo("hello"); + + assertThat(interpreter.getErrors().get(0).getMessage()).contains("Import cycle detected for path:", "imports-self.jinja"); } @Test @@ -65,6 +67,8 @@ public void itAvoidsNestedImportCycle() throws IOException { interpreter.render(Resources.toString(Resources.getResource("tags/importtag/a-imports-b.jinja"), StandardCharsets.UTF_8)); assertThat(context.get("a")).isEqualTo("foo"); assertThat(context.get("b")).isEqualTo("bar"); + + assertThat(interpreter.getErrors().get(0).getMessage()).contains("Import cycle detected for path:", "b-imports-a.jinja"); } @Test From e8498d8aada02991841830ecd49fd3757d2bc4a4 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Tue, 20 Oct 2015 14:27:43 -0400 Subject: [PATCH 0053/2465] [maven-release-plugin] prepare release jinjava-2.1.0 --- pom.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 4ac27d185..6cf89b415 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,4 @@ - + 4.0.0 @@ -10,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.0-SNAPSHOT + 2.1.0 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -18,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.1.0 From 461d7a25905bcc556d9b4b568609c0d7b4f89c5a Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Tue, 20 Oct 2015 14:27:43 -0400 Subject: [PATCH 0054/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6cf89b415..9bbadd09f 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.0 + 2.1.1-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.1.0 + HEAD From cda9c710f385f0792f9088f22f97bb5236e9ecdd Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Tue, 20 Oct 2015 14:29:39 -0400 Subject: [PATCH 0055/2465] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b3f0b96ff..a2bf32258 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Get it: com.hubspot.jinjava jinjava - 2.0.10 + 2.1.0 ``` From 609c8beee715f6386116059ee5d0dc2db3aec928 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Tue, 20 Oct 2015 14:30:10 -0400 Subject: [PATCH 0056/2465] Update CHANGES.md --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 87e7b2675..7a6a92811 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Jinjava Releases # +### Version 2.1.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.1%22)) ### + +* + ### Version 2.1.0 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.0%22)) ### * Refactored node render logic to return richer OutputNode instances, removing a need for a special intermediate string value in text output From d5703f807a036c576603d7abe3f5309d197ace67 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Wed, 21 Oct 2015 11:38:36 -0400 Subject: [PATCH 0057/2465] better template error messages for invalid assignments in expressions --- CHANGES.md | 2 +- .../jinjava/el/ExpressionResolver.java | 23 +++++++++++++++---- .../jinjava/el/ext/ExtendedParser.java | 15 ++++-------- .../el/ext/NamedParameterOperator.java | 5 ++++ .../jinjava/el/ExtendedSyntaxBuilderTest.java | 14 +++++++++++ 5 files changed, 44 insertions(+), 15 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 7a6a92811..8d355c412 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,7 +2,7 @@ ### Version 2.1.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.1%22)) ### -* +* Better error messages for invalid assignment in expression ### Version 2.1.0 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.0%22)) ### diff --git a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java index bd711bc64..b62e3597f 100644 --- a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java @@ -11,6 +11,7 @@ import org.apache.commons.lang3.StringUtils; +import com.hubspot.jinjava.el.ext.NamedParameter; import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.TemplateError; @@ -18,6 +19,7 @@ import com.hubspot.jinjava.interpret.TemplateError.ErrorType; import com.hubspot.jinjava.interpret.TemplateSyntaxException; import com.hubspot.jinjava.lib.fn.ELFunctionDefinition; + import de.odysseus.el.tree.TreeBuilderException; /** @@ -44,7 +46,8 @@ public ExpressionResolver(JinjavaInterpreter interpreter, ExpressionFactory expr /** * Resolve expression against current context. * - * @param expression Jinja expression. + * @param expression + * Jinja expression. * @return Value of expression. */ public Object resolveExpression(String expression) { @@ -55,7 +58,11 @@ public Object resolveExpression(String expression) { try { String elExpression = "#{" + expression.trim() + "}"; ValueExpression valueExp = expressionFactory.createValueExpression(elContext, elExpression, Object.class); - return valueExp.getValue(elContext); + Object result = valueExp.getValue(elContext); + + validateResult(result); + + return result; } catch (PropertyNotFoundException e) { interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.UNKNOWN, e.getMessage(), "", interpreter.getLineNumber(), e)); @@ -72,11 +79,19 @@ public Object resolveExpression(String expression) { return ""; } + private void validateResult(Object result) { + if (result instanceof NamedParameter) { + throw new ELException("Unexpected '=' operator (use {% set %} tag for variable assignment)"); + } + } + /** * Resolve property of bean. * - * @param object Bean. - * @param propertyNames Names of properties to resolve recursively. + * @param object + * Bean. + * @param propertyNames + * Names of properties to resolve recursively. * @return Value of property. */ public Object resolveProperty(Object object, List propertyNames) { diff --git a/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java b/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java index 38a941130..5df1a11ec 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java @@ -93,8 +93,7 @@ protected AstNode expr(boolean required) throws ScanException, ParseException { consumeToken(COLON); AstNode b = expr(true); v = createAstChoice(v, a, b); - } - else { + } else { consumeToken(); AstNode cond = expr(true); AstNode elseNode = new AstNull(); @@ -270,8 +269,7 @@ protected AstNode literal() throws ScanException, ParseException { case EXTENSION: if (getToken() == LITERAL_DICT_START) { v = dict(); - } - else if (getToken() == LITERAL_DICT_END) { + } else if (getToken() == LITERAL_DICT_END) { return null; } break; @@ -324,16 +322,14 @@ protected AstNode value() throws ScanException, ParseException { AstNode rangeMax = expr(true); consumeToken(RBRACK); v = createAstRangeBracket(v, property, rangeMax, lvalue, strict); - } - else if (nextToken.getSymbol() == RBRACK) { + } else if (nextToken.getSymbol() == RBRACK) { AstBracket bracket = createAstBracket(v, property, lvalue, strict); if (getToken().getSymbol() == LPAREN && context.isEnabled(METHOD_INVOCATIONS)) { v = createAstMethod(bracket, params()); } else { v = bracket; } - } - else { + } else { fail(RBRACK); } @@ -357,8 +353,7 @@ else if (nextToken.getSymbol() == RBRACK) { v = createAstMethod(filterProperty, new AstParameters(filterParams)); // function("filter:" + filterName, new AstParameters(filterParams)); } while ("|".equals(getToken().getImage())); - } - else if ("is".equals(getToken().getImage()) && lookahead(0).getSymbol() == IDENTIFIER) { + } else if ("is".equals(getToken().getImage()) && lookahead(0).getSymbol() == IDENTIFIER) { consumeToken(); // 'is' String exptestName = consumeToken().getImage(); List exptestParams = Lists.newArrayList(v, interpreter()); diff --git a/src/main/java/com/hubspot/jinjava/el/ext/NamedParameterOperator.java b/src/main/java/com/hubspot/jinjava/el/ext/NamedParameterOperator.java index 04f7cc9a5..bf3ceba2f 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/NamedParameterOperator.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/NamedParameterOperator.java @@ -1,5 +1,7 @@ package com.hubspot.jinjava.el.ext; +import com.hubspot.jinjava.interpret.InterpretException; + import de.odysseus.el.tree.impl.Parser.ExtensionHandler; import de.odysseus.el.tree.impl.Parser.ExtensionPoint; import de.odysseus.el.tree.impl.Scanner; @@ -13,6 +15,9 @@ public class NamedParameterOperator { public static final ExtensionHandler HANDLER = new ExtensionHandler(ExtensionPoint.ADD) { @Override public AstNode createAstNode(AstNode... children) { + if (!(children[0] instanceof AstIdentifier)) { + throw new InterpretException("Expected IDENTIFIER, found " + children[0].toString()); + } AstIdentifier name = (AstIdentifier) children[0]; return new AstNamedParameter(name, children[1]); } diff --git a/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java b/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java index cc9d404de..0c7999fdc 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java @@ -212,6 +212,20 @@ public void listRangeSyntax() throws Exception { assertThat(val("mylist[2]")).isEqualTo(3); } + @Test + public void invalidNestedAssignmentExpr() throws Exception { + assertThat(val("content.template_path = 'Custom/Email/Responsive/testing.html'")).isEqualTo(""); + assertThat(interpreter.getErrors()).isNotEmpty(); + assertThat(interpreter.getErrors().get(0).getMessage()).containsIgnoringCase("identifier"); + } + + @Test + public void invalidIdentifierAssignmentExpr() throws Exception { + assertThat(val("content = 'Custom/Email/Responsive/testing.html'")).isEqualTo(""); + assertThat(interpreter.getErrors()).isNotEmpty(); + assertThat(interpreter.getErrors().get(0).getMessage()).containsIgnoringCase("'='"); + } + private Object val(String expr) { return interpreter.resolveELExpression(expr, -1); } From 595d58a822482b831398fe6677d5e459c73270a1 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Wed, 21 Oct 2015 13:10:55 -0400 Subject: [PATCH 0058/2465] invalid assignment operator in expr should be SYNTAX_ERROR --- .../com/hubspot/jinjava/el/ext/NamedParameterOperator.java | 4 ++-- .../com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/NamedParameterOperator.java b/src/main/java/com/hubspot/jinjava/el/ext/NamedParameterOperator.java index bf3ceba2f..2d2aebca9 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/NamedParameterOperator.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/NamedParameterOperator.java @@ -1,6 +1,6 @@ package com.hubspot.jinjava.el.ext; -import com.hubspot.jinjava.interpret.InterpretException; +import javax.el.ELException; import de.odysseus.el.tree.impl.Parser.ExtensionHandler; import de.odysseus.el.tree.impl.Parser.ExtensionPoint; @@ -16,7 +16,7 @@ public class NamedParameterOperator { @Override public AstNode createAstNode(AstNode... children) { if (!(children[0] instanceof AstIdentifier)) { - throw new InterpretException("Expected IDENTIFIER, found " + children[0].toString()); + throw new ELException("Expected IDENTIFIER, found " + children[0].toString()); } AstIdentifier name = (AstIdentifier) children[0]; return new AstNamedParameter(name, children[1]); diff --git a/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java b/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java index 0c7999fdc..2457ae956 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java @@ -19,6 +19,7 @@ import com.hubspot.jinjava.Jinjava; import com.hubspot.jinjava.interpret.Context; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; @SuppressWarnings("unchecked") public class ExtendedSyntaxBuilderTest { @@ -216,6 +217,7 @@ public void listRangeSyntax() throws Exception { public void invalidNestedAssignmentExpr() throws Exception { assertThat(val("content.template_path = 'Custom/Email/Responsive/testing.html'")).isEqualTo(""); assertThat(interpreter.getErrors()).isNotEmpty(); + assertThat(interpreter.getErrors().get(0).getReason()).isEqualTo(ErrorReason.SYNTAX_ERROR); assertThat(interpreter.getErrors().get(0).getMessage()).containsIgnoringCase("identifier"); } @@ -223,6 +225,7 @@ public void invalidNestedAssignmentExpr() throws Exception { public void invalidIdentifierAssignmentExpr() throws Exception { assertThat(val("content = 'Custom/Email/Responsive/testing.html'")).isEqualTo(""); assertThat(interpreter.getErrors()).isNotEmpty(); + assertThat(interpreter.getErrors().get(0).getReason()).isEqualTo(ErrorReason.SYNTAX_ERROR); assertThat(interpreter.getErrors().get(0).getMessage()).containsIgnoringCase("'='"); } From e12ef642ae1531395b3f13aa252fa0ebe6c853ef Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Wed, 21 Oct 2015 13:29:11 -0400 Subject: [PATCH 0059/2465] missing expression for if tag should be syntax error --- .../com/hubspot/jinjava/lib/tag/IfTag.java | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/IfTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/IfTag.java index cb712afb3..e6a6eb0eb 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/IfTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/IfTag.java @@ -1,17 +1,17 @@ /********************************************************************** -Copyright (c) 2014 HubSpot 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. + * Copyright (c) 2014 HubSpot 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.hubspot.jinjava.lib.tag; @@ -21,8 +21,8 @@ import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; -import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.interpret.TemplateSyntaxException; import com.hubspot.jinjava.tree.Node; import com.hubspot.jinjava.tree.TagNode; import com.hubspot.jinjava.util.ObjectTruthValue; @@ -33,8 +33,7 @@ @JinjavaSnippet( code = "{% if condition %}\n" + "If the condition is true print this to template.\n" + - "{% endif %}" - ), + "{% endif %}"), @JinjavaSnippet( code = "{% if number <= 2 %}\n" + "Varible named number is less than or equal to 2.\n" + @@ -55,7 +54,7 @@ public class IfTag implements Tag { @Override public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { if (StringUtils.isBlank(tagNode.getHelpers())) { - throw new InterpretException("Tag 'if' expects expression", tagNode.getLineNumber()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'if' expects expression", tagNode.getLineNumber()); } Iterator nodeIterator = tagNode.getChildren().iterator(); From a203c5f8d74e1fd0048f76484e85c5357074e2d9 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Wed, 21 Oct 2015 13:36:46 -0400 Subject: [PATCH 0060/2465] change invalid tag use errors to syntax errors --- .../hubspot/jinjava/lib/filter/AbsFilter.java | 28 +++++++------- .../com/hubspot/jinjava/lib/tag/BlockTag.java | 4 +- .../com/hubspot/jinjava/lib/tag/CycleTag.java | 30 +++++++-------- .../hubspot/jinjava/lib/tag/ExtendsTag.java | 3 +- .../com/hubspot/jinjava/lib/tag/ForTag.java | 3 +- .../com/hubspot/jinjava/lib/tag/FromTag.java | 12 +++--- .../hubspot/jinjava/lib/tag/IfchangedTag.java | 30 +++++++-------- .../hubspot/jinjava/lib/tag/ImportTag.java | 3 +- .../hubspot/jinjava/lib/tag/IncludeTag.java | 3 +- .../com/hubspot/jinjava/lib/tag/MacroTag.java | 13 +++---- .../com/hubspot/jinjava/lib/tag/SetTag.java | 38 +++++++++---------- 11 files changed, 83 insertions(+), 84 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/AbsFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/AbsFilter.java index 076578e1c..85ebcb358 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/AbsFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/AbsFilter.java @@ -1,17 +1,17 @@ /********************************************************************** -Copyright (c) 2014 HubSpot 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. + * Copyright (c) 2014 HubSpot 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.hubspot.jinjava.lib.filter; @@ -66,7 +66,7 @@ public Object filter(Object object, JinjavaInterpreter interpreter, String... ar try { return new BigDecimal((String) object).abs(); } catch (Exception e) { - throw new InterpretException(object + " can't be dealed with abs filter", e); + throw new InterpretException(object + " can't be handled by abs filter", e); } } return object; diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/BlockTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/BlockTag.java index 7b749b19f..4db41ddcf 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/BlockTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/BlockTag.java @@ -18,8 +18,8 @@ import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaParam; import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; -import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.interpret.TemplateSyntaxException; import com.hubspot.jinjava.tree.TagNode; import com.hubspot.jinjava.tree.output.BlockPlaceholderOutputNode; import com.hubspot.jinjava.tree.output.OutputNode; @@ -49,7 +49,7 @@ public class BlockTag implements Tag { public OutputNode interpretOutput(TagNode tagNode, JinjavaInterpreter interpreter) { HelperStringTokenizer tagData = new HelperStringTokenizer(tagNode.getHelpers()); if (!tagData.hasNext()) { - throw new InterpretException("Tag 'block' expects an identifier", tagNode.getLineNumber()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'block' expects an identifier", tagNode.getLineNumber()); } String blockName = WhitespaceUtils.unquote(tagData.next()); diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/CycleTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/CycleTag.java index 3516a4a14..687fca9b5 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/CycleTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/CycleTag.java @@ -1,17 +1,17 @@ /********************************************************************** -Copyright (c) 2014 HubSpot 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. + * Copyright (c) 2014 HubSpot 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.hubspot.jinjava.lib.tag; @@ -20,8 +20,8 @@ import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaParam; import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; -import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.interpret.TemplateSyntaxException; import com.hubspot.jinjava.tree.TagNode; import com.hubspot.jinjava.util.HelperStringTokenizer; @@ -89,7 +89,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { interpreter.getContext().put(var, values); return ""; } else { - throw new InterpretException("Tag 'cycle' expects 1 or 3 helper(s) >>> " + helper.size(), tagNode.getLineNumber()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'cycle' expects 1 or 3 helper(s), was: " + helper.size(), tagNode.getLineNumber()); } } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java index 2b4634f1a..45b433b4f 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java @@ -22,6 +22,7 @@ import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.interpret.TemplateSyntaxException; import com.hubspot.jinjava.tree.Node; import com.hubspot.jinjava.tree.TagNode; import com.hubspot.jinjava.util.HelperStringTokenizer; @@ -81,7 +82,7 @@ public class ExtendsTag implements Tag { public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { HelperStringTokenizer tokenizer = new HelperStringTokenizer(tagNode.getHelpers()); if (!tokenizer.hasNext()) { - throw new InterpretException("Tag 'extends' expects template path", tagNode.getLineNumber()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'extends' expects template path", tagNode.getLineNumber()); } String path = interpreter.resolveString(tokenizer.next(), tagNode.getLineNumber()); diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java index 3c9f4acff..7d84c585a 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java @@ -30,6 +30,7 @@ import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.JinjavaInterpreter.InterpreterScopeClosable; +import com.hubspot.jinjava.interpret.TemplateSyntaxException; import com.hubspot.jinjava.tree.Node; import com.hubspot.jinjava.tree.TagNode; import com.hubspot.jinjava.util.ForLoop; @@ -86,7 +87,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { } if (inPos >= helper.size()) { - throw new InterpretException("Tag 'for' expects valid 'in' clause, got: " + tagNode.getHelpers(), tagNode.getLineNumber()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'for' expects valid 'in' clause, got: " + tagNode.getHelpers(), tagNode.getLineNumber()); } String loopExpr = StringUtils.join(helper.subList(inPos + 1, helper.size()), ","); diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/FromTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/FromTag.java index af10c8c1d..a1c63d090 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/FromTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/FromTag.java @@ -12,6 +12,7 @@ import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.interpret.TemplateSyntaxException; import com.hubspot.jinjava.lib.fn.MacroFunction; import com.hubspot.jinjava.tree.Node; import com.hubspot.jinjava.tree.TagNode; @@ -31,13 +32,11 @@ "{% endmacro %}\n" + "{% macro footer(tag, footer_text) %}\n" + "
<{{ tag }}>{{ footer_text }}
\n" + - "{% endmacro %}" - ), + "{% endmacro %}"), @JinjavaSnippet( desc = "The macro html file is accessed from a different template, but only the footer macro is imported and executed", code = "{% from 'custom/page/web_page_basic/my_macros.html' import footer %}\n" + - "{{ footer('h2', 'My footer info') }}" - ), + "{{ footer('h2', 'My footer info') }}"), }) public class FromTag implements Tag { @@ -52,7 +51,7 @@ public String getName() { public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { List helper = new HelperStringTokenizer(tagNode.getHelpers()).splitComma(true).allTokens(); if (helper.size() < 3 || !helper.get(1).equals("import")) { - throw new InterpretException("Tag 'from' expects import list: " + helper, tagNode.getLineNumber()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'from' expects import list: " + helper, tagNode.getLineNumber()); } String templateFile = interpreter.resolveString(helper.get(0), tagNode.getLineNumber()); @@ -86,8 +85,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { if (val != null) { interpreter.getContext().addGlobalMacro((MacroFunction) val); - } - else { + } else { val = child.getContext().get(importMapping.getKey()); if (val != null) { diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/IfchangedTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/IfchangedTag.java index 79547ea7f..af1a30130 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/IfchangedTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/IfchangedTag.java @@ -1,17 +1,17 @@ /********************************************************************** -Copyright (c) 2014 HubSpot 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. + * Copyright (c) 2014 HubSpot 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.hubspot.jinjava.lib.tag; @@ -19,8 +19,8 @@ import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; -import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.interpret.TemplateSyntaxException; import com.hubspot.jinjava.tree.Node; import com.hubspot.jinjava.tree.TagNode; @@ -43,7 +43,7 @@ public class IfchangedTag implements Tag { @Override public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { if (StringUtils.isBlank(tagNode.getHelpers())) { - throw new InterpretException("Tag 'ifchanged' expects a variable parameter", tagNode.getLineNumber()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'ifchanged' expects a variable parameter", tagNode.getLineNumber()); } boolean isChanged = true; String var = tagNode.getHelpers().trim(); diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java index b56ab59ea..13981f521 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java @@ -16,6 +16,7 @@ import com.hubspot.jinjava.interpret.TemplateError; import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; import com.hubspot.jinjava.interpret.TemplateError.ErrorType; +import com.hubspot.jinjava.interpret.TemplateSyntaxException; import com.hubspot.jinjava.lib.fn.MacroFunction; import com.hubspot.jinjava.tree.Node; import com.hubspot.jinjava.tree.TagNode; @@ -61,7 +62,7 @@ public String getName() { public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { List helper = new HelperStringTokenizer(tagNode.getHelpers()).allTokens(); if (helper.isEmpty()) { - throw new InterpretException("Tag 'import' expects 1 helper >>> " + helper.size(), tagNode.getLineNumber()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'import' expects 1 helper, was: " + helper.size(), tagNode.getLineNumber()); } String contextVar = ""; diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java index 29cb0d897..3fcb70b75 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java @@ -28,6 +28,7 @@ import com.hubspot.jinjava.interpret.TemplateError; import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; import com.hubspot.jinjava.interpret.TemplateError.ErrorType; +import com.hubspot.jinjava.interpret.TemplateSyntaxException; import com.hubspot.jinjava.tree.Node; import com.hubspot.jinjava.tree.TagNode; import com.hubspot.jinjava.util.HelperStringTokenizer; @@ -49,7 +50,7 @@ public class IncludeTag implements Tag { public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { HelperStringTokenizer helper = new HelperStringTokenizer(tagNode.getHelpers()); if (!helper.hasNext()) { - throw new InterpretException("Tag 'include' expects template path", tagNode.getLineNumber()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'include' expects template path", tagNode.getLineNumber()); } String path = StringUtils.trimToEmpty(helper.next()); diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/MacroTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/MacroTag.java index 2d3cee771..49e2b4d72 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/MacroTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/MacroTag.java @@ -13,8 +13,8 @@ import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaParam; import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; -import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.interpret.TemplateSyntaxException; import com.hubspot.jinjava.lib.fn.MacroFunction; import com.hubspot.jinjava.tree.TagNode; @@ -31,8 +31,7 @@ " {{ argument_name }}\n" + " {{ argument_name2 }}\n" + "{% endmacro %}\n" + - "{{ name_of_macro(\"value to pass to argument 1\", \"value to pass to argument 2\") }}" - ), + "{{ name_of_macro(\"value to pass to argument 1\", \"value to pass to argument 2\") }}"), @JinjavaSnippet( desc = "Example of a macro used to print CSS3 properties with the various vendor prefixes", code = "{% macro trans(value) %}\n" + @@ -41,8 +40,7 @@ " -o-transition: {{value}};\n" + " -ms-transition: {{value}};\n" + " transition: {{value}};\n" + - "{% endmacro %}" - ), + "{% endmacro %}"), @JinjavaSnippet( desc = "The macro can then be called like a function. The macro is printed for anchor tags in CSS.", code = "a { {{ trans(\"all .2s ease-in-out\") }} }"), @@ -65,7 +63,7 @@ public String getEndTagName() { public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { Matcher matcher = MACRO_PATTERN.matcher(tagNode.getHelpers()); if (!matcher.find()) { - throw new InterpretException("Unable to parse macro definition: " + tagNode.getHelpers()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Unable to parse macro definition: " + tagNode.getHelpers(), tagNode.getLineNumber()); } String name = matcher.group(1); @@ -90,8 +88,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { Object argVal = interpreter.resolveELExpression(argValStr.toString(), tagNode.getLineNumber()); argNamesWithDefaults.put(argName, argVal); - } - else { + } else { argNamesWithDefaults.put(arg, null); } } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/SetTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/SetTag.java index 1daa0cd98..87cb413e1 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/SetTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/SetTag.java @@ -1,17 +1,17 @@ /********************************************************************** -Copyright (c) 2014 HubSpot 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. + * Copyright (c) 2014 HubSpot 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.hubspot.jinjava.lib.tag; @@ -22,6 +22,7 @@ import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.interpret.TemplateSyntaxException; import com.hubspot.jinjava.tree.TagNode; /** @@ -32,7 +33,8 @@ * @author anysome * */ -@JinjavaDoc(value = "Assigns the value or result of a statement to a variable", +@JinjavaDoc( + value = "Assigns the value or result of a statement to a variable", params = { @JinjavaParam(value = "var", type = "variable identifier", desc = "The name of the variable"), @JinjavaParam(value = "expr", type = "expression", desc = "The value stored in the variable (string, number, boolean, or sequence") @@ -41,14 +43,12 @@ @JinjavaSnippet( desc = "Set a variable in with a set statement and print the variable in a expression", code = "{% set primaryColor = \"#F7761F\" %}\n" + - "{{ primaryColor }}\n" - ), + "{{ primaryColor }}\n"), @JinjavaSnippet( desc = "You can combine multiple values or variables into a sequence variable", code = "{% set var_one = \"String 1\" %}\n" + "{% set var_two = \"String 2\" %}\n" + - "{% set sequence = [var_one, var_two] %}" - ), + "{% set sequence = [var_one, var_two] %}"), }) public class SetTag implements Tag { @@ -63,7 +63,7 @@ public String getName() { @Override public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { if (!tagNode.getHelpers().contains("=")) { - throw new InterpretException("Tag 'set' expects an assignment expression with '=', but was: " + tagNode.getHelpers(), tagNode.getLineNumber()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'set' expects an assignment expression with '=', but was: " + tagNode.getHelpers(), tagNode.getLineNumber()); } int eqPos = tagNode.getHelpers().indexOf('='); From 16b7b269e1a44a75d22a82ec6c986712b96dc9a4 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Wed, 21 Oct 2015 17:03:57 -0400 Subject: [PATCH 0061/2465] better error message for syntax error with pipe op in expr --- .../java/com/hubspot/jinjava/el/ext/ExtendedParser.java | 4 +++- .../com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java b/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java index 5df1a11ec..2454e47d9 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java @@ -17,6 +17,8 @@ import java.util.List; import java.util.Map; +import javax.el.ELException; + import com.google.common.collect.Lists; import de.odysseus.el.tree.impl.Builder; @@ -68,7 +70,7 @@ public ExtendedParser(Builder context, String input) { putExtensionHandler(PIPE, new ExtensionHandler(ExtensionPoint.AND) { @Override public AstNode createAstNode(AstNode... children) { - throw new IllegalStateException("Pipe operator reached from AST parse"); + throw new ELException("Illegal use of '|' operator"); } }); diff --git a/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java b/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java index 2457ae956..2ec59db88 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java @@ -229,6 +229,13 @@ public void invalidIdentifierAssignmentExpr() throws Exception { assertThat(interpreter.getErrors().get(0).getMessage()).containsIgnoringCase("'='"); } + @Test + public void invalidPipeOperatorExpr() throws Exception { + assertThat(val("topics|1")).isEqualTo(""); + assertThat(interpreter.getErrors()).isNotEmpty(); + assertThat(interpreter.getErrors().get(0).getReason()).isEqualTo(ErrorReason.SYNTAX_ERROR); + } + private Object val(String expr) { return interpreter.resolveELExpression(expr, -1); } From 7276a5f2856276ea746d75fb8fa397728898d22b Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 26 Oct 2015 11:05:46 -0400 Subject: [PATCH 0062/2465] create new class of interpret exception to signify programmatic state issues --- .../jinjava/interpret/TagCycleException.java | 2 +- .../interpret/TemplateStateException.java | 22 +++++++++++++++++++ .../interpret/JinjavaInterpreterTest.java | 9 ++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/hubspot/jinjava/interpret/TemplateStateException.java diff --git a/src/main/java/com/hubspot/jinjava/interpret/TagCycleException.java b/src/main/java/com/hubspot/jinjava/interpret/TagCycleException.java index 207e3d577..77471507b 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TagCycleException.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TagCycleException.java @@ -1,6 +1,6 @@ package com.hubspot.jinjava.interpret; -public class TagCycleException extends InterpretException { +public class TagCycleException extends TemplateStateException { private static final long serialVersionUID = -3058494056577268723L; private final String path; diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateStateException.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateStateException.java new file mode 100644 index 000000000..87c8f0308 --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateStateException.java @@ -0,0 +1,22 @@ +package com.hubspot.jinjava.interpret; + +public class TemplateStateException extends InterpretException { + private static final long serialVersionUID = 426925445445430522L; + + public TemplateStateException(String msg) { + super(msg); + } + + public TemplateStateException(String msg, Throwable e) { + super(msg, e); + } + + public TemplateStateException(String msg, int lineNumber) { + super(msg, lineNumber); + } + + public TemplateStateException(String msg, Throwable e, int lineNumber) { + super(msg, e, lineNumber); + } + +} diff --git a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java index 84d481413..3ae7c5e87 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import java.time.ZonedDateTime; +import java.util.HashMap; import org.junit.Before; import org.junit.Test; @@ -10,6 +11,7 @@ import com.google.common.collect.Lists; import com.hubspot.jinjava.Jinjava; import com.hubspot.jinjava.interpret.JinjavaInterpreter.InterpreterScopeClosable; +import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; import com.hubspot.jinjava.tree.TextNode; import com.hubspot.jinjava.tree.parse.TextToken; @@ -130,4 +132,11 @@ public void enterScopeTryWithResources() { assertThat(interpreter.resolveELExpression("foo", 1)).isEqualTo("parent"); } + @Test + public void parseWithSyntaxError() { + RenderResult result = new Jinjava().renderForResult("{%}", new HashMap<>()); + assertThat(result.getErrors()).isNotEmpty(); + assertThat(result.getErrors().get(0).getReason()).isEqualTo(ErrorReason.SYNTAX_ERROR); + } + } From b7a49e5d97a943159e1f8b28885c03806a28c239 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 26 Oct 2015 13:33:53 -0400 Subject: [PATCH 0063/2465] throw template state exception for invalid dict key --- src/main/java/com/hubspot/jinjava/el/ext/AstDict.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/AstDict.java b/src/main/java/com/hubspot/jinjava/el/ext/AstDict.java index 46db39c98..0e9b891ba 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/AstDict.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/AstDict.java @@ -6,6 +6,7 @@ import javax.el.ELContext; +import com.hubspot.jinjava.interpret.TemplateStateException; import com.hubspot.jinjava.objects.collections.PyMap; import de.odysseus.el.tree.Bindings; @@ -34,7 +35,7 @@ public Object eval(Bindings bindings, ELContext context) { } else if (entry.getKey() instanceof AstIdentifier) { key = ((AstIdentifier) entry.getKey()).getName(); } else { - throw new IllegalArgumentException("Dict key must be a string or identifier, was: " + entry.getKey()); + throw new TemplateStateException("Dict key must be a string or identifier, was: " + entry.getKey()); } resolved.put(key, entry.getValue().eval(bindings, context)); From c82aca00c8ddc7e66360a29e2fbb5a75872ec93e Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Fri, 6 Nov 2015 12:13:24 -0500 Subject: [PATCH 0064/2465] Stop iterating too high up the Context --- .../hubspot/jinjava/interpret/Context.java | 23 ++----------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index 8b5e7c43f..4e6ec48ae 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -319,29 +319,10 @@ public Optional popIncludePath() { } public void addDependency(String type, String identification) { - Context highestParentContext = getHighestParentContext(); - highestParentContext.dependencies.get(type).add(identification); + this.dependencies.get(type).add(identification); } public SetMultimap getDependencies() { - Context highestParentContext = getHighestParentContext(); - return highestParentContext.dependencies; + return this.dependencies; } - - private Context getHighestParentContext() { - Context highestParentContext = parent != null ? parent : this; - Context currentParentContext = highestParentContext.getParent(); - - while (currentParentContext != null) { - if (currentParentContext.equals(currentParentContext.getParent())) { - return highestParentContext; - } - - highestParentContext = highestParentContext.getParent(); - currentParentContext = highestParentContext.getParent(); - - } - return highestParentContext; - } - } From 6384f0c2ed36dba139633d841be601249f94b051 Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Fri, 6 Nov 2015 13:38:23 -0500 Subject: [PATCH 0065/2465] look one level up --- src/main/java/com/hubspot/jinjava/interpret/Context.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index 4e6ec48ae..1fa40cf3f 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -319,7 +319,7 @@ public Optional popIncludePath() { } public void addDependency(String type, String identification) { - this.dependencies.get(type).add(identification); + this.getParent().dependencies.get(type).add(identification); } public SetMultimap getDependencies() { From aeba10e180552ba79a26109e77a67f47b2c3721b Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Mon, 9 Nov 2015 11:17:00 -0500 Subject: [PATCH 0066/2465] update to use all the same context level --- .../hubspot/jinjava/interpret/Context.java | 22 ++----------------- .../hubspot/jinjava/lib/tag/ExtendsTag.java | 4 ++-- .../hubspot/jinjava/lib/tag/IncludeTag.java | 7 ++++-- 3 files changed, 9 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index 8b5e7c43f..a0300b3d4 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -319,29 +319,11 @@ public Optional popIncludePath() { } public void addDependency(String type, String identification) { - Context highestParentContext = getHighestParentContext(); - highestParentContext.dependencies.get(type).add(identification); + this.dependencies.get(type).add(identification); } public SetMultimap getDependencies() { - Context highestParentContext = getHighestParentContext(); - return highestParentContext.dependencies; - } - - private Context getHighestParentContext() { - Context highestParentContext = parent != null ? parent : this; - Context currentParentContext = highestParentContext.getParent(); - - while (currentParentContext != null) { - if (currentParentContext.equals(currentParentContext.getParent())) { - return highestParentContext; - } - - highestParentContext = highestParentContext.getParent(); - currentParentContext = highestParentContext.getParent(); - - } - return highestParentContext; + return this.dependencies; } } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java index 45b433b4f..98760260b 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java @@ -91,8 +91,8 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { try { String template = interpreter.getResource(path); Node node = interpreter.parse(template); - JinjavaInterpreter child = new JinjavaInterpreter(interpreter); - child.getContext().addDependency("coded_files", path); + JinjavaInterpreter currentInterpreter = JinjavaInterpreter.getCurrent(); + currentInterpreter.getContext().addDependency("coded_files", path); interpreter.addExtendParentRoot(node); return ""; } catch (IOException e) { diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java index 3fcb70b75..71553bc0e 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java @@ -67,8 +67,11 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { try { String template = interpreter.getResource(templateFile); Node node = interpreter.parse(template); - JinjavaInterpreter child = new JinjavaInterpreter(interpreter); - child.getContext().addDependency("coded_files", templateFile); + + JinjavaInterpreter currentInterpreter = JinjavaInterpreter.getCurrent(); + JinjavaInterpreter child = new JinjavaInterpreter(currentInterpreter); + + currentInterpreter.getContext().addDependency("coded_files", templateFile); String result = child.render(node); interpreter.getErrors().addAll(child.getErrors()); From e7f2c39602e936a781d13a359be3f7762a8cc7ed Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Tue, 10 Nov 2015 12:15:50 -0500 Subject: [PATCH 0067/2465] throw more specific exception when date format is invalid --- .../date/InvalidDateFormatException.java | 17 +++++++++++++++++ .../jinjava/objects/date/StrftimeFormatter.java | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/hubspot/jinjava/objects/date/InvalidDateFormatException.java diff --git a/src/main/java/com/hubspot/jinjava/objects/date/InvalidDateFormatException.java b/src/main/java/com/hubspot/jinjava/objects/date/InvalidDateFormatException.java new file mode 100644 index 000000000..34be3158e --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/objects/date/InvalidDateFormatException.java @@ -0,0 +1,17 @@ +package com.hubspot.jinjava.objects.date; + +public class InvalidDateFormatException extends IllegalArgumentException { + private static final long serialVersionUID = -1577669116818659228L; + + private final String format; + + public InvalidDateFormatException(String format, Throwable t) { + super("Invalid date format: [" + format + "]", t); + this.format = format; + } + + public String getFormat() { + return format; + } + +} diff --git a/src/main/java/com/hubspot/jinjava/objects/date/StrftimeFormatter.java b/src/main/java/com/hubspot/jinjava/objects/date/StrftimeFormatter.java index 4daf27f4b..feb919329 100644 --- a/src/main/java/com/hubspot/jinjava/objects/date/StrftimeFormatter.java +++ b/src/main/java/com/hubspot/jinjava/objects/date/StrftimeFormatter.java @@ -117,7 +117,7 @@ public static DateTimeFormatter formatter(String strftime) { try { return DateTimeFormatter.ofPattern(toJavaDateTimeFormat(strftime)); } catch (IllegalArgumentException e) { - throw new IllegalArgumentException("Invalid date format [" + strftime + "]: " + e.getMessage(), e); + throw new InvalidDateFormatException(strftime, e); } } } From d0775259858e69cc4cd23bd3eb730494f5ddf84f Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 30 Nov 2015 23:22:22 -0500 Subject: [PATCH 0068/2465] Allow for locale-based date formatting in StrftimeFormatter --- CHANGES.md | 2 ++ .../el/JinjavaInterpreterResolver.java | 2 +- .../jinjava/interpret/JinjavaInterpreter.java | 5 +++ .../com/hubspot/jinjava/lib/fn/Functions.java | 11 ++++-- .../objects/date/StrftimeFormatter.java | 34 +++++++++++++++---- 5 files changed, 45 insertions(+), 9 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 8d355c412..7c101b7a4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,8 @@ ### Version 2.1.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.1%22)) ### * Better error messages for invalid assignment in expression +* Allow for locale-based date formatting in StrftimeFormatter +* Use configured locale for Functions.datetimeformat ### Version 2.1.0 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.0%22)) ### diff --git a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java index b52d0b5bc..d19bc4dab 100644 --- a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java @@ -162,7 +162,7 @@ private static String formattedDateToString(JinjavaInterpreter interpreter, Form private static DateTimeFormatter getFormatter(JinjavaInterpreter interpreter, FormattedDate d) { if (!StringUtils.isBlank(d.getFormat())) { try { - return StrftimeFormatter.formatter(d.getFormat()); + return StrftimeFormatter.formatter(d.getFormat(), interpreter.getConfig().getLocale()); } catch (IllegalArgumentException e) { interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.SYNTAX_ERROR, e.getMessage(), null, interpreter.getLineNumber(), null)); } diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index 7ab8b0b1f..f84b4523b 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -23,6 +23,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.Stack; import org.apache.commons.lang3.StringUtils; @@ -377,6 +378,10 @@ public static JinjavaInterpreter getCurrent() { return CURRENT_INTERPRETER.get().peek(); } + public static Optional getCurrentMaybe() { + return Optional.ofNullable(getCurrent()); + } + public static void pushCurrent(JinjavaInterpreter interpreter) { CURRENT_INTERPRETER.get().push(interpreter); } diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java index 28f0f7682..294c3abc8 100644 --- a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java @@ -7,11 +7,13 @@ import java.time.ZonedDateTime; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Objects; import org.apache.commons.lang3.BooleanUtils; import com.google.common.collect.Lists; +import com.hubspot.jinjava.JinjavaConfig; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaParam; import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; @@ -72,10 +74,15 @@ public static String dateTimeFormat(Object var, String... format) { return ""; } + Locale locale = JinjavaInterpreter.getCurrentMaybe() + .map(JinjavaInterpreter::getConfig) + .map(JinjavaConfig::getLocale) + .orElse(Locale.ENGLISH); + if (format.length > 0) { - return StrftimeFormatter.format(d, format[0]); + return StrftimeFormatter.format(d, format[0], locale); } else { - return StrftimeFormatter.format(d); + return StrftimeFormatter.format(d, locale); } } diff --git a/src/main/java/com/hubspot/jinjava/objects/date/StrftimeFormatter.java b/src/main/java/com/hubspot/jinjava/objects/date/StrftimeFormatter.java index feb919329..916c13f48 100644 --- a/src/main/java/com/hubspot/jinjava/objects/date/StrftimeFormatter.java +++ b/src/main/java/com/hubspot/jinjava/objects/date/StrftimeFormatter.java @@ -3,6 +3,7 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; +import java.util.Locale; import org.apache.commons.lang3.StringUtils; @@ -104,30 +105,51 @@ private static String replaceL(String s) { } public static DateTimeFormatter formatter(String strftime) { + return formatter(strftime, Locale.ENGLISH); + } + + public static DateTimeFormatter formatter(String strftime, Locale locale) { + DateTimeFormatter fmt; + switch (strftime.toLowerCase()) { case "short": - return DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT); + fmt = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT); + break; case "medium": - return DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM); + fmt = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM); + break; case "long": - return DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG); + fmt = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG); + break; case "full": - return DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL); + fmt = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL); + break; default: try { - return DateTimeFormatter.ofPattern(toJavaDateTimeFormat(strftime)); + fmt = DateTimeFormatter.ofPattern(toJavaDateTimeFormat(strftime)); + break; } catch (IllegalArgumentException e) { throw new InvalidDateFormatException(strftime, e); } } + + return fmt.withLocale(locale); } public static String format(ZonedDateTime d) { return format(d, DEFAULT_DATE_FORMAT); } + public static String format(ZonedDateTime d, Locale locale) { + return format(d, DEFAULT_DATE_FORMAT, locale); + } + public static String format(ZonedDateTime d, String strftime) { - return formatter(strftime).format(d); + return format(d, strftime, Locale.ENGLISH); + } + + public static String format(ZonedDateTime d, String strftime, Locale locale) { + return formatter(strftime, locale).format(d); } } From f27f725be7bfe3a0d15868124609db4e70968ba6 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Tue, 1 Dec 2015 10:42:17 -0500 Subject: [PATCH 0069/2465] [maven-release-plugin] prepare release jinjava-2.1.1 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9bbadd09f..f947cd503 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.1-SNAPSHOT + 2.1.1 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.1.1 From ff38cfd9f5037a1b23be4bf5f1a37ed84cf1dc8e Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Tue, 1 Dec 2015 10:42:17 -0500 Subject: [PATCH 0070/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f947cd503..2e207a476 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.1 + 2.1.2-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.1.1 + HEAD From 86f30da6b4ae57c948f97a044f639f4a024f7842 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Tue, 1 Dec 2015 10:44:25 -0500 Subject: [PATCH 0071/2465] prepare for next dev iteration --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 7c101b7a4..c368ac752 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Jinjava Releases # +### Version 2.1.2 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.2%22)) ### + +* + ### Version 2.1.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.1%22)) ### * Better error messages for invalid assignment in expression From 22f028dd8a89e79a6326393f2a2f34f528017a28 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Thu, 3 Dec 2015 11:54:47 -0500 Subject: [PATCH 0072/2465] use resolved path value for include tag cycle detection --- CHANGES.md | 2 +- src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index c368ac752..1721ad0fd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,7 +2,7 @@ ### Version 2.1.2 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.2%22)) ### -* +* Use resolved path value in include tag cycle detection ### Version 2.1.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.1%22)) ### diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java index 71553bc0e..b227f4135 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java @@ -54,16 +54,16 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { } String path = StringUtils.trimToEmpty(helper.next()); + String templateFile = interpreter.resolveString(path, tagNode.getLineNumber()); try { - interpreter.getContext().pushIncludePath(path, tagNode.getLineNumber()); + interpreter.getContext().pushIncludePath(templateFile, tagNode.getLineNumber()); } catch (IncludeTagCycleException e) { interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.EXCEPTION, - "Include cycle detected for path: '" + path + "'", null, tagNode.getLineNumber(), e)); + "Include cycle detected for path: '" + templateFile + "'", null, tagNode.getLineNumber(), e)); return ""; } - String templateFile = interpreter.resolveString(path, tagNode.getLineNumber()); try { String template = interpreter.getResource(templateFile); Node node = interpreter.parse(template); From ff2bea9ba88272ba560c8a98a6fe903489b304a7 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Fri, 4 Dec 2015 14:12:07 -0500 Subject: [PATCH 0073/2465] store autoEscape flag in context outside of user-editable properties --- CHANGES.md | 1 + .../com/hubspot/jinjava/interpret/Context.java | 18 ++++++++++++++++++ .../hubspot/jinjava/lib/tag/AutoEscapeTag.java | 3 +-- .../hubspot/jinjava/tree/ExpressionNode.java | 3 +-- .../jinjava/tree/ExpressionNodeTest.java | 3 +-- 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 1721ad0fd..cadc467bf 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ ### Version 2.1.2 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.2%22)) ### * Use resolved path value in include tag cycle detection +* Store autoEscape flag in context outside of user-scoped properties ### Version 2.1.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.1%22)) ### diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index a0300b3d4..4e7ee0f42 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -53,6 +53,8 @@ public class Context extends ScopeMap { private final Context parent; + private Boolean autoEscape; + public Context() { this(null); } @@ -111,6 +113,22 @@ public boolean isGlobalMacro(String identifier) { return getGlobalMacro(identifier) != null; } + public boolean isAutoEscape() { + if (autoEscape != null) { + return autoEscape; + } + + if (parent != null) { + return parent.isAutoEscape(); + } + + return false; + } + + public void setAutoEscape(Boolean autoEscape) { + this.autoEscape = autoEscape; + } + @SafeVarargs @SuppressWarnings("unchecked") public final void registerClasses(Class... classes) { diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/AutoEscapeTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/AutoEscapeTag.java index c7b32cdb1..957da78c4 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/AutoEscapeTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/AutoEscapeTag.java @@ -20,7 +20,6 @@ "{% endautoescape %}") }) public class AutoEscapeTag implements Tag { - public static final String AUTOESCAPE_CONTEXT_VAR = "__auto3sc@pe__"; private static final long serialVersionUID = 786006577642541285L; @Override @@ -38,7 +37,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { try (InterpreterScopeClosable c = interpreter.enterScope()) { String boolFlagStr = StringUtils.trim(tagNode.getHelpers()); boolean escapeFlag = BooleanUtils.toBoolean(StringUtils.isNotBlank(boolFlagStr) ? boolFlagStr : "true"); - interpreter.getContext().put(AUTOESCAPE_CONTEXT_VAR, escapeFlag); + interpreter.getContext().setAutoEscape(escapeFlag); StringBuilder result = new StringBuilder(); diff --git a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java index 315e8ba30..a4f8f3c36 100644 --- a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java @@ -21,7 +21,6 @@ import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.lib.filter.EscapeFilter; -import com.hubspot.jinjava.lib.tag.AutoEscapeTag; import com.hubspot.jinjava.tree.output.OutputNode; import com.hubspot.jinjava.tree.output.RenderedOutputNode; import com.hubspot.jinjava.tree.parse.ExpressionToken; @@ -51,7 +50,7 @@ public OutputNode render(JinjavaInterpreter interpreter) { } } - if (interpreter.getContext().get(AutoEscapeTag.AUTOESCAPE_CONTEXT_VAR, Boolean.FALSE).equals(Boolean.TRUE)) { + if (interpreter.getContext().isAutoEscape()) { result = EscapeFilter.escapeHtmlEntities(result); } diff --git a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java index 164080264..c1d11f9e0 100644 --- a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java @@ -12,7 +12,6 @@ import com.hubspot.jinjava.Jinjava; import com.hubspot.jinjava.interpret.Context; import com.hubspot.jinjava.interpret.JinjavaInterpreter; -import com.hubspot.jinjava.lib.tag.AutoEscapeTag; public class ExpressionNodeTest { @@ -60,7 +59,7 @@ public void itEscapesValueWhenContextSet() throws Exception { context.put("a", "foo < bar"); assertThat(val("{{ a }}")).isEqualTo("foo < bar"); - context.put(AutoEscapeTag.AUTOESCAPE_CONTEXT_VAR, Boolean.TRUE); + context.setAutoEscape(true); assertThat(val("{{ a }}")).isEqualTo("foo < bar"); } From 1c990b4589951b23520615ea9d8e843376043799 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Fri, 4 Dec 2015 14:18:47 -0500 Subject: [PATCH 0074/2465] store superBlock reference in context outside of user-editable properties --- CHANGES.md | 3 ++- .../hubspot/jinjava/interpret/Context.java | 22 +++++++++++++++++++ .../jinjava/interpret/JinjavaInterpreter.java | 6 ++--- .../com/hubspot/jinjava/lib/fn/Functions.java | 3 +-- 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index cadc467bf..70c6b34a0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,7 +3,8 @@ ### Version 2.1.2 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.2%22)) ### * Use resolved path value in include tag cycle detection -* Store autoEscape flag in context outside of user-scoped properties +* Store autoEscape flag in context outside of user-editable properties +* Store superBlock reference in context outside of user-editable properties ### Version 2.1.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.1%22)) ### diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index 4e7ee0f42..7f60812ea 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -35,6 +35,7 @@ import com.hubspot.jinjava.lib.fn.MacroFunction; import com.hubspot.jinjava.lib.tag.Tag; import com.hubspot.jinjava.lib.tag.TagLibrary; +import com.hubspot.jinjava.tree.Node; import com.hubspot.jinjava.util.ScopeMap; public class Context extends ScopeMap { @@ -54,6 +55,7 @@ public class Context extends ScopeMap { private final Context parent; private Boolean autoEscape; + private List superBlock; public Context() { this(null); @@ -129,6 +131,26 @@ public void setAutoEscape(Boolean autoEscape) { this.autoEscape = autoEscape; } + public List getSuperBlock() { + if (superBlock != null) { + return superBlock; + } + + if (parent != null) { + return parent.getSuperBlock(); + } + + return null; + } + + public void setSuperBlock(List superBlock) { + this.superBlock = superBlock; + } + + public void removeSuperBlock() { + this.superBlock = null; + } + @SafeVarargs @SuppressWarnings("unchecked") public final void registerClasses(Class... classes) { diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index f84b4523b..bb2c1f8cd 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -215,7 +215,7 @@ private void resolveBlockStubs(OutputList output, Stack blockNames) { if (block != null) { List superBlock = Iterables.get(blockChain, 1, null); - context.put(BLOCK_SUPER_REF, superBlock); + context.setSuperBlock(superBlock); OutputList blockValueBuilder = new OutputList(); @@ -227,7 +227,7 @@ private void resolveBlockStubs(OutputList output, Stack blockNames) { resolveBlockStubs(blockValueBuilder, blockNames); blockNames.pop(); - context.remove(BLOCK_SUPER_REF); + context.removeSuperBlock(); blockPlaceholder.resolve(blockValueBuilder.getValue()); } @@ -392,6 +392,4 @@ public static void popCurrent() { } } - public static final String BLOCK_SUPER_REF = "__superbl0ck__"; - } diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java index 294c3abc8..4aacbec99 100644 --- a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java @@ -36,8 +36,7 @@ public static String renderSuperBlock() { JinjavaInterpreter interpreter = JinjavaInterpreter.getCurrent(); StringBuilder result = new StringBuilder(); - @SuppressWarnings("unchecked") - List superBlock = (List) interpreter.getContext().get("__superbl0ck__"); + List superBlock = interpreter.getContext().getSuperBlock(); if (superBlock != null) { for (Node n : superBlock) { result.append(n.render(interpreter)); From 03541f61cede1e5db0191c1b334c8c9a9956c302 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Wed, 16 Dec 2015 15:13:20 -0500 Subject: [PATCH 0075/2465] nicer threadlocal declaration --- .../com/hubspot/jinjava/interpret/JinjavaInterpreter.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index bb2c1f8cd..d4241fda2 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -363,12 +363,7 @@ public List getErrors() { return errors; } - private static final ThreadLocal> CURRENT_INTERPRETER = new ThreadLocal>() { - @Override - protected Stack initialValue() { - return new Stack<>(); - } - }; + private static final ThreadLocal> CURRENT_INTERPRETER = ThreadLocal.withInitial(Stack::new); public static JinjavaInterpreter getCurrent() { if (CURRENT_INTERPRETER.get().isEmpty()) { From bb1cc826f65c9fd1c201cd6961c7672605babd3d Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Fri, 18 Dec 2015 11:40:44 -0500 Subject: [PATCH 0076/2465] make EL resolver read-only by default, expose as config parameter --- CHANGES.md | 1 + .../com/hubspot/jinjava/JinjavaConfig.java | 31 ++++++++++++++----- .../el/JinjavaInterpreterResolver.java | 12 ++++++- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 70c6b34a0..8ce5e88d1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,7 @@ * Use resolved path value in include tag cycle detection * Store autoEscape flag in context outside of user-editable properties * Store superBlock reference in context outside of user-editable properties +* make EL resolver read-only by default, expose as config parameter ### Version 2.1.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.1%22)) ### diff --git a/src/main/java/com/hubspot/jinjava/JinjavaConfig.java b/src/main/java/com/hubspot/jinjava/JinjavaConfig.java index a6d96a9ea..78264d731 100644 --- a/src/main/java/com/hubspot/jinjava/JinjavaConfig.java +++ b/src/main/java/com/hubspot/jinjava/JinjavaConfig.java @@ -31,30 +31,34 @@ public class JinjavaConfig { private final boolean trimBlocks; private final boolean lstripBlocks; + private final boolean readOnlyResolver; + public static Builder newBuilder() { return new Builder(); } public JinjavaConfig() { - this(StandardCharsets.UTF_8, Locale.ENGLISH, ZoneOffset.UTC, 10, false, false); + this(StandardCharsets.UTF_8, Locale.ENGLISH, ZoneOffset.UTC, 10, false, false, true); } public JinjavaConfig(Charset charset, Locale locale, ZoneId timeZone, int maxRenderDepth) { - this(charset, locale, timeZone, maxRenderDepth, false, false); + this(charset, locale, timeZone, maxRenderDepth, false, false, true); } private JinjavaConfig(Charset charset, - Locale locale, - ZoneId timeZone, - int maxRenderDepth, - boolean trimBlocks, - boolean lstripBlocks) { + Locale locale, + ZoneId timeZone, + int maxRenderDepth, + boolean trimBlocks, + boolean lstripBlocks, + boolean readOnlyResolver) { this.charset = charset; this.locale = locale; this.timeZone = timeZone; this.maxRenderDepth = maxRenderDepth; this.trimBlocks = trimBlocks; this.lstripBlocks = lstripBlocks; + this.readOnlyResolver = readOnlyResolver; } public Charset getCharset() { @@ -81,6 +85,10 @@ public boolean isLstripBlocks() { return lstripBlocks; } + public boolean isReadOnlyResolver() { + return readOnlyResolver; + } + public static class Builder { private Charset charset = StandardCharsets.UTF_8; private Locale locale = Locale.ENGLISH; @@ -90,6 +98,8 @@ public static class Builder { private boolean trimBlocks; private boolean lstripBlocks; + private boolean readOnlyResolver = true; + private Builder() {} public Builder withCharset(Charset charset) { @@ -122,8 +132,13 @@ public Builder withLstripBlocks(boolean lstripBlocks) { return this; } + public Builder withReadOnlyResolver(boolean readOnlyResolver) { + this.readOnlyResolver = readOnlyResolver; + return this; + } + public JinjavaConfig build() { - return new JinjavaConfig(charset, locale, timeZone, maxRenderDepth, trimBlocks, lstripBlocks); + return new JinjavaConfig(charset, locale, timeZone, maxRenderDepth, trimBlocks, lstripBlocks, readOnlyResolver); } } diff --git a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java index d19bc4dab..5c4341f30 100644 --- a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java @@ -43,6 +43,16 @@ public class JinjavaInterpreterResolver extends SimpleResolver { + private static final ELResolver DEFAULT_RESOLVER_READ_ONLY = new CompositeELResolver() { + { + add(new ArrayELResolver(true)); + add(new JinjavaListELResolver(true)); + add(new MapELResolver(true)); + add(new ResourceBundleELResolver()); + add(new JinjavaBeanELResolver(true)); + } + }; + private static final ELResolver DEFAULT_RESOLVER_READ_WRITE = new CompositeELResolver() { { add(new ArrayELResolver(false)); @@ -56,7 +66,7 @@ public class JinjavaInterpreterResolver extends SimpleResolver { private final JinjavaInterpreter interpreter; public JinjavaInterpreterResolver(JinjavaInterpreter interpreter) { - super(DEFAULT_RESOLVER_READ_WRITE); + super(interpreter.getConfig().isReadOnlyResolver() ? DEFAULT_RESOLVER_READ_ONLY : DEFAULT_RESOLVER_READ_WRITE); this.interpreter = interpreter; } From 746961a44f1611dd93a19952234ea42979571ff3 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Fri, 18 Dec 2015 12:04:53 -0500 Subject: [PATCH 0077/2465] restrict certain methods/properties in object expressions --- CHANGES.md | 1 + .../jinjava/el/ext/JinjavaBeanELResolver.java | 41 +++++++++++++++++-- .../jinjava/el/ExpressionResolverTest.java | 24 ++++++++++- 3 files changed, 61 insertions(+), 5 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 8ce5e88d1..b73092e8b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,7 @@ * Store autoEscape flag in context outside of user-editable properties * Store superBlock reference in context outside of user-editable properties * make EL resolver read-only by default, expose as config parameter +* restrict certain methods/properties in object expressions ### Version 2.1.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.1%22)) ### diff --git a/src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java b/src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java index 5fabbfd41..1e7162f6b 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java @@ -1,14 +1,29 @@ package com.hubspot.jinjava.el.ext; +import java.util.Set; + import javax.el.BeanELResolver; import javax.el.ELContext; +import javax.el.MethodNotFoundException; import com.google.common.base.CaseFormat; +import com.google.common.collect.ImmutableSet; /** * {@link BeanELResolver} supporting snake case property names. */ public class JinjavaBeanELResolver extends BeanELResolver { + private static final Set RESTRICTED_PROPERTIES = ImmutableSet. builder() + .add("class") + .build(); + + private static final Set RESTRICTED_METHODS = ImmutableSet. builder() + .add("clone") + .add("hashCode") + .add("notify") + .add("notifyAll") + .add("wait") + .build(); /** * Creates a new read/write {@link JinjavaBeanELResolver}. @@ -24,22 +39,40 @@ public JinjavaBeanELResolver(boolean readOnly) { @Override public Class getType(ELContext context, Object base, Object property) { - return super.getType(context, base, transformPropertyName(property)); + return super.getType(context, base, validatePropertyName(property)); } @Override public Object getValue(ELContext context, Object base, Object property) { - return super.getValue(context, base, transformPropertyName(property)); + return super.getValue(context, base, validatePropertyName(property)); } @Override public boolean isReadOnly(ELContext context, Object base, Object property) { - return super.isReadOnly(context, base, transformPropertyName(property)); + return super.isReadOnly(context, base, validatePropertyName(property)); } @Override public void setValue(ELContext context, Object base, Object property, Object value) { - super.setValue(context, base, transformPropertyName(property), value); + super.setValue(context, base, validatePropertyName(property), value); + } + + @Override + public Object invoke(ELContext context, Object base, Object method, Class[] paramTypes, Object[] params) { + if (method == null || RESTRICTED_METHODS.contains(method.toString())) { + throw new MethodNotFoundException("Cannot find method '" + method + "' in " + base.getClass()); + } + return super.invoke(context, base, method, paramTypes, params); + } + + private String validatePropertyName(Object property) { + String propertyName = transformPropertyName(property); + + if (RESTRICTED_PROPERTIES.contains(propertyName)) { + return null; + } + + return propertyName; } /** diff --git a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java index c87eeb9eb..952f687fd 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java @@ -124,7 +124,7 @@ public void itResolvesInnerListVal() throws Exception { assertThat(val).isEqualTo("val"); } - public static class MyCustomList extends ForwardingListimplements PyWrapper { + public static class MyCustomList extends ForwardingList implements PyWrapper { private final List list; public MyCustomList(List list) { @@ -196,6 +196,28 @@ public void itWrapsDates() throws Exception { assertThat(result.toString()).isEqualTo("1970-01-01 00:00:00"); } + @Test + public void blackListedProperties() throws Exception { + context.put("myobj", new MyClass(new Date(0))); + interpreter.resolveELExpression("myobj.class.methods[0]", -1); + + assertThat(interpreter.getErrors()).isNotEmpty(); + TemplateError e = interpreter.getErrors().get(0); + assertThat(e.getReason()).isEqualTo(ErrorReason.UNKNOWN); + assertThat(e.getFieldName()).isEqualTo("class"); + assertThat(e.getMessage()).contains("Cannot resolve property 'class'"); + } + + @Test + public void blackListedMethods() throws Exception { + context.put("myobj", new MyClass(new Date(0))); + interpreter.resolveELExpression("myobj.wait()", -1); + + assertThat(interpreter.getErrors()).isNotEmpty(); + TemplateError e = interpreter.getErrors().get(0); + assertThat(e.getMessage()).contains("Cannot find method 'wait'"); + } + public static final class MyClass { private Date date; From 395aed8b7abae2477a4dd294aa3d82345939dac6 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Fri, 18 Dec 2015 12:29:03 -0500 Subject: [PATCH 0078/2465] [maven-release-plugin] prepare release jinjava-2.1.2 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 2e207a476..884801904 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.2-SNAPSHOT + 2.1.2 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.1.2 From f52ec0292744d1b36ca5fb327db31de25b86db7f Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Fri, 18 Dec 2015 12:29:03 -0500 Subject: [PATCH 0079/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 884801904..2912e648a 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.2 + 2.1.3-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.1.2 + HEAD From 4f1669fd24f159e4abbb3434a79ab4667d31e952 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Fri, 18 Dec 2015 12:42:01 -0500 Subject: [PATCH 0080/2465] prepare for next dev iteration --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index b73092e8b..3d9111688 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Jinjava Releases # +### Version 2.1.3 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.3%22)) ### + +* + ### Version 2.1.2 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.2%22)) ### * Use resolved path value in include tag cycle detection From ab6bb438968b8ebbdc151bec2ddc62a1ce028fba Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Sun, 20 Dec 2015 17:45:34 -0500 Subject: [PATCH 0081/2465] preserve order in dicts --- src/main/java/com/hubspot/jinjava/el/ext/AstDict.java | 4 ++-- src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/AstDict.java b/src/main/java/com/hubspot/jinjava/el/ext/AstDict.java index 0e9b891ba..0d1041229 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/AstDict.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/AstDict.java @@ -1,6 +1,6 @@ package com.hubspot.jinjava.el.ext; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; @@ -25,7 +25,7 @@ public AstDict(Map dict) { @Override public Object eval(Bindings bindings, ELContext context) { - Map resolved = new HashMap<>(); + Map resolved = new LinkedHashMap<>(); for (Map.Entry entry : dict.entrySet()) { String key; diff --git a/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java b/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java index 2454e47d9..bcec87b7e 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java @@ -13,7 +13,7 @@ import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -185,7 +185,7 @@ protected AstParameters params(Symbol left, Symbol right) throws ScanException, protected AstDict dict() throws ScanException, ParseException { consumeToken(); - Map dict = new HashMap<>(); + Map dict = new LinkedHashMap<>(); AstNode k = expr(false); if (k != null) { From 3f8bb142700c19862637fb35bad3968f74b55a38 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Wed, 6 Jan 2016 13:14:48 -0500 Subject: [PATCH 0082/2465] throw template syntax exception for set tag issues --- src/main/java/com/hubspot/jinjava/lib/tag/SetTag.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/SetTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/SetTag.java index 87cb413e1..39fe9d2c4 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/SetTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/SetTag.java @@ -20,7 +20,6 @@ import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaParam; import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; -import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.TemplateSyntaxException; import com.hubspot.jinjava.tree.TagNode; @@ -71,10 +70,10 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { String expr = tagNode.getHelpers().substring(eqPos + 1, tagNode.getHelpers().length()); if (var.length() == 0) { - throw new InterpretException("Tag 'set' requires a var name to assign to", tagNode.getLineNumber()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'set' requires a var name to assign to", tagNode.getLineNumber()); } if (StringUtils.isBlank(expr)) { - throw new InterpretException("Tag 'set' requires an expression to assign to a var", tagNode.getLineNumber()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'set' requires an expression to assign to a var", tagNode.getLineNumber()); } Object val = interpreter.resolveELExpression(expr, tagNode.getLineNumber()); From a47d18025e9751ac2e36a8d7e606cfa6d70b2991 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Wed, 6 Jan 2016 22:53:38 -0500 Subject: [PATCH 0083/2465] Fixing documentation for setResourceLocator --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a2bf32258..709a498b1 100644 --- a/README.md +++ b/README.md @@ -72,9 +72,9 @@ By default, it will load a ```FileLocator```; you will likely want to provide yo ```java JinjavaConfig config = new JinjavaConfig(); -config.setResourceLocator(new MyCustomResourceLocator()); Jinjava jinjava = new Jinjava(config); +jinjava.setResourceLocator(new MyCustomResourceLocator()); ``` ### Custom tags, filters and functions From 04fe6a4240ebda286ca240952e22530cf42ae009 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Fri, 15 Jan 2016 14:42:55 -0500 Subject: [PATCH 0084/2465] adding expression tests string_containing and string_startingwith --- CHANGES.md | 2 +- .../jinjava/lib/exptest/ExpTestLibrary.java | 2 ++ .../exptest/IsStringContainingExpTest.java | 36 +++++++++++++++++++ .../exptest/IsStringStartingWithExpTest.java | 36 +++++++++++++++++++ .../jinjava/el/ExtendedSyntaxBuilderTest.java | 11 +++++- 5 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/hubspot/jinjava/lib/exptest/IsStringContainingExpTest.java create mode 100644 src/main/java/com/hubspot/jinjava/lib/exptest/IsStringStartingWithExpTest.java diff --git a/CHANGES.md b/CHANGES.md index 3d9111688..3044f261e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,7 +2,7 @@ ### Version 2.1.3 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.3%22)) ### -* +* Added two new expression tests for strings: "is string_startingwith" and "is string_containing" ### Version 2.1.2 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.2%22)) ### diff --git a/src/main/java/com/hubspot/jinjava/lib/exptest/ExpTestLibrary.java b/src/main/java/com/hubspot/jinjava/lib/exptest/ExpTestLibrary.java index 848320606..9591bd1c3 100644 --- a/src/main/java/com/hubspot/jinjava/lib/exptest/ExpTestLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/exptest/ExpTestLibrary.java @@ -24,6 +24,8 @@ protected void registerDefaults() { IsSameAsExpTest.class, IsSequenceExpTest.class, IsStringExpTest.class, + IsStringContainingExpTest.class, + IsStringStartingWithExpTest.class, IsTruthyExpTest.class, IsUndefinedExpTest.class, IsUpperExpTest.class); diff --git a/src/main/java/com/hubspot/jinjava/lib/exptest/IsStringContainingExpTest.java b/src/main/java/com/hubspot/jinjava/lib/exptest/IsStringContainingExpTest.java new file mode 100644 index 000000000..655ee23eb --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/lib/exptest/IsStringContainingExpTest.java @@ -0,0 +1,36 @@ +package com.hubspot.jinjava.lib.exptest; + +import com.hubspot.jinjava.doc.annotations.JinjavaDoc; +import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; +import com.hubspot.jinjava.interpret.InterpretException; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; + +@JinjavaDoc( + value = "Return true if object is a string which contains a specified other string", + snippets = { + @JinjavaSnippet( + code = "{% if variable is string_containing 'foo' %}\n" + + " \n" + + "{% endif %}") + }) +public class IsStringContainingExpTest extends IsStringExpTest { + + @Override + public String getName() { + return super.getName() + "_containing"; + } + + @Override + public boolean evaluate(Object var, JinjavaInterpreter interpreter, Object... args) { + if (!super.evaluate(var, interpreter, args)) { + return false; + } + + if (args.length == 0 || args[0] == null) { + throw new InterpretException(getName() + " test requires an argument"); + } + + return ((String) var).contains(args[0].toString()); + } + +} diff --git a/src/main/java/com/hubspot/jinjava/lib/exptest/IsStringStartingWithExpTest.java b/src/main/java/com/hubspot/jinjava/lib/exptest/IsStringStartingWithExpTest.java new file mode 100644 index 000000000..7d698de2f --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/lib/exptest/IsStringStartingWithExpTest.java @@ -0,0 +1,36 @@ +package com.hubspot.jinjava.lib.exptest; + +import com.hubspot.jinjava.doc.annotations.JinjavaDoc; +import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; +import com.hubspot.jinjava.interpret.InterpretException; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; + +@JinjavaDoc( + value = "Return true if object is a string which starts with a specified other string", + snippets = { + @JinjavaSnippet( + code = "{% if variable is string_startingwith 'foo' %}\n" + + " \n" + + "{% endif %}") + }) +public class IsStringStartingWithExpTest extends IsStringExpTest { + + @Override + public String getName() { + return super.getName() + "_startingwith"; + } + + @Override + public boolean evaluate(Object var, JinjavaInterpreter interpreter, Object... args) { + if (!super.evaluate(var, interpreter, args)) { + return false; + } + + if (args.length == 0 || args[0] == null) { + throw new InterpretException(getName() + " test requires an argument"); + } + + return ((String) var).startsWith(args[0].toString()); + } + +} diff --git a/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java b/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java index 2ec59db88..1cae557ef 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java @@ -68,6 +68,15 @@ public void expTestOp() { assertThat(val("49 is odd")).isEqualTo(true); } + @Test + public void stringExpTestOps() { + assertThat(val("'football' is string_startingwith 'foot'")).isEqualTo(true); + assertThat(val("'football' is string_startingwith 'ball'")).isEqualTo(false); + + assertThat(val("'football' is string_containing 'tb'")).isEqualTo(true); + assertThat(val("'football' is string_containing 'golf'")).isEqualTo(false); + } + @Test public void namedFnArgs() { context.put("path", "/page"); @@ -235,7 +244,7 @@ public void invalidPipeOperatorExpr() throws Exception { assertThat(interpreter.getErrors()).isNotEmpty(); assertThat(interpreter.getErrors().get(0).getReason()).isEqualTo(ErrorReason.SYNTAX_ERROR); } - + private Object val(String expr) { return interpreter.resolveELExpression(expr, -1); } From 6b57c36b48cf4423629615ee3709044049715411 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 25 Jan 2016 12:42:25 -0500 Subject: [PATCH 0085/2465] cleaning up unnecessary autoboxing --- benchmark/pom.xml | 2 +- .../jinjava/el/TruthyTypeConverter.java | 2 +- .../el/ext/CollectionMembershipOperator.java | 8 +-- .../jinjava/el/ext/ExtendedScanner.java | 62 +++++++++---------- .../hubspot/jinjava/el/ext/OrOperator.java | 2 +- .../hubspot/jinjava/interpret/Context.java | 19 +++++- .../jinjava/interpret/JinjavaInterpreter.java | 11 ++-- .../jinjava/interpret/TemplateError.java | 22 +++++-- .../jinjava/lib/filter/WordCountFilter.java | 7 +-- .../hubspot/jinjava/lib/tag/ExtendsTag.java | 5 +- .../hubspot/jinjava/lib/tag/IncludeTag.java | 7 +-- 11 files changed, 85 insertions(+), 62 deletions(-) diff --git a/benchmark/pom.xml b/benchmark/pom.xml index 26360cd3c..3d9acf8d6 100644 --- a/benchmark/pom.xml +++ b/benchmark/pom.xml @@ -41,7 +41,7 @@ com.hubspot.jinjava jinjava - 2.1.0-SNAPSHOT + 2.1.3-SNAPSHOT commons-io diff --git a/src/main/java/com/hubspot/jinjava/el/TruthyTypeConverter.java b/src/main/java/com/hubspot/jinjava/el/TruthyTypeConverter.java index cc0046130..d7e20dbff 100644 --- a/src/main/java/com/hubspot/jinjava/el/TruthyTypeConverter.java +++ b/src/main/java/com/hubspot/jinjava/el/TruthyTypeConverter.java @@ -9,7 +9,7 @@ public class TruthyTypeConverter extends TypeConverterImpl { @Override protected Boolean coerceToBoolean(Object value) { - return ObjectTruthValue.evaluate(value); + return Boolean.valueOf(ObjectTruthValue.evaluate(value)); } } diff --git a/src/main/java/com/hubspot/jinjava/el/ext/CollectionMembershipOperator.java b/src/main/java/com/hubspot/jinjava/el/ext/CollectionMembershipOperator.java index 6d2644793..b3916f792 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/CollectionMembershipOperator.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/CollectionMembershipOperator.java @@ -18,18 +18,18 @@ public class CollectionMembershipOperator extends SimpleOperator { @Override protected Object apply(TypeConverter converter, Object o1, Object o2) { if (o2 == null) { - return false; + return Boolean.FALSE; } if (CharSequence.class.isAssignableFrom(o2.getClass())) { - return StringUtils.contains((CharSequence) o2, Objects.toString(o1, "")); + return Boolean.valueOf(StringUtils.contains((CharSequence) o2, Objects.toString(o1, ""))); } if (Collection.class.isAssignableFrom(o2.getClass())) { - return ((Collection) o2).contains(o1); + return Boolean.valueOf(((Collection) o2).contains(o1)); } - return false; + return Boolean.FALSE; } public static final CollectionMembershipOperator OP = new CollectionMembershipOperator(); diff --git a/src/main/java/com/hubspot/jinjava/el/ext/ExtendedScanner.java b/src/main/java/com/hubspot/jinjava/el/ext/ExtendedScanner.java index e4af8df57..01c29bdc7 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/ExtendedScanner.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/ExtendedScanner.java @@ -32,8 +32,7 @@ public Token next() throws ScanException { if (getPosition() == length) { token = fixed(Symbol.EOF); - } - else { + } else { token = nextToken(); } @@ -48,6 +47,7 @@ protected boolean isWhitespace(char c) { private static final Method ADD_KEY_TOKEN_METHOD; private static final Field TOKEN_FIELD; private static final Field POSITION_FIELD; + static { try { ADD_KEY_TOKEN_METHOD = Scanner.class.getDeclaredMethod("addKeyToken", Token.class); @@ -77,6 +77,7 @@ protected void setToken(Token token) { } } + @SuppressWarnings("boxing") protected void incrPosition(int n) { try { POSITION_FIELD.set(this, getPosition() + n); @@ -91,8 +92,7 @@ protected Token nextToken() throws ScanException { if (getInput().charAt(getPosition()) == '}') { if (getPosition() < getInput().length() - 1) { return ExtendedParser.LITERAL_DICT_END; - } - else { + } else { return fixed(Symbol.END_EVAL); } } @@ -100,10 +100,10 @@ protected Token nextToken() throws ScanException { } else { if (getPosition() + 1 < getInput().length() && getInput().charAt(getPosition() + 1) == '{') { switch (getInput().charAt(getPosition())) { - case '#': - return fixed(Symbol.START_EVAL_DEFERRED); - case '$': - return fixed(Symbol.START_EVAL_DYNAMIC); + case '#': + return fixed(Symbol.START_EVAL_DEFERRED); + case '$': + return fixed(Symbol.START_EVAL_DYNAMIC); } } return nextText(); @@ -151,29 +151,29 @@ protected Token nextString() throws ScanException { } else { c = getInput().charAt(i++); switch (c) { - case '\\': - case '\'': - case '"': - builder.append(c); - break; - - case 'n': - builder.append('\n'); - break; - case 't': - builder.append('\t'); - break; - case 'b': - builder.append('\b'); - break; - case 'f': - builder.append('\f'); - break; - case 'r': - builder.append('\r'); - break; - default: - throw new ScanException(getPosition(), "invalid escape sequence \\" + c, "\\" + quote + " or \\\\"); + case '\\': + case '\'': + case '"': + builder.append(c); + break; + + case 'n': + builder.append('\n'); + break; + case 't': + builder.append('\t'); + break; + case 'b': + builder.append('\b'); + break; + case 'f': + builder.append('\f'); + break; + case 'r': + builder.append('\r'); + break; + default: + throw new ScanException(getPosition(), "invalid escape sequence \\" + c, "\\" + quote + " or \\\\"); } } } else if (c == quote) { diff --git a/src/main/java/com/hubspot/jinjava/el/ext/OrOperator.java b/src/main/java/com/hubspot/jinjava/el/ext/OrOperator.java index a32f95c8b..aa80ac635 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/OrOperator.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/OrOperator.java @@ -11,7 +11,7 @@ public class OrOperator implements Operator { @Override public Object eval(Bindings bindings, ELContext context, AstNode left, AstNode right) { Object leftResult = left.eval(bindings, context); - if (bindings.convert(leftResult, Boolean.class)) { + if (bindings.convert(leftResult, Boolean.class).booleanValue()) { return leftResult; } diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index 7f60812ea..362244b9a 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -54,6 +54,7 @@ public class Context extends ScopeMap { private final Context parent; + private int renderDepth = -1; private Boolean autoEscape; private List superBlock; @@ -117,7 +118,7 @@ public boolean isGlobalMacro(String identifier) { public boolean isAutoEscape() { if (autoEscape != null) { - return autoEscape; + return autoEscape.booleanValue(); } if (parent != null) { @@ -358,6 +359,22 @@ public Optional popIncludePath() { return Optional.of(includePathStack.pop()); } + public int getRenderDepth() { + if (renderDepth != -1) { + return renderDepth; + } + + if (parent != null) { + return parent.getRenderDepth(); + } + + return 0; + } + + public void setRenderDepth(int renderDepth) { + this.renderDepth = renderDepth; + } + public void addDependency(String type, String identification) { this.dependencies.get(type).add(identification); } diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index d4241fda2..5135138c1 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -126,21 +126,18 @@ public Node parse(String template) { * @return rendered result */ public String renderFlat(String template) { - Integer depth = (Integer) context.get("hs_render_depth", 0); - if (depth == null) { - depth = 0; - } + int depth = context.getRenderDepth(); try { if (depth > config.getMaxRenderDepth()) { - ENGINE_LOG.warn("Max render depth exceeded: {}", depth); + ENGINE_LOG.warn("Max render depth exceeded: {}", Integer.toString(depth)); return template; } else { - context.put("hs_render_depth", depth + 1); + context.setRenderDepth(depth + 1); return render(parse(template), false); } } finally { - context.put("hs_render_depth", depth); + context.setRenderDepth(depth); } } diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java index f10474cd7..663837a50 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java @@ -8,18 +8,24 @@ public class TemplateError { public enum ErrorType { - FATAL, WARNING + FATAL, + WARNING } public enum ErrorReason { - SYNTAX_ERROR, UNKNOWN, BAD_URL, EXCEPTION, MISSING, OTHER + SYNTAX_ERROR, + UNKNOWN, + BAD_URL, + EXCEPTION, + MISSING, + OTHER } private final ErrorType severity; private final ErrorReason reason; private final String message; private final String fieldName; - private final Integer lineno; + private final int lineno; private final Exception exception; @@ -68,8 +74,12 @@ private static String friendlyObjectToString(Object o) { // java.lang.Object@7852e922 private static final Pattern GENERIC_TOSTRING_PATTERN = Pattern.compile("@[0-9a-z]{4,}$"); - public TemplateError(ErrorType severity, ErrorReason reason, String message, - String fieldName, Integer lineno, Exception exception) { + public TemplateError(ErrorType severity, + ErrorReason reason, + String message, + String fieldName, + int lineno, + Exception exception) { this.severity = severity; this.reason = reason; this.message = message; @@ -94,7 +104,7 @@ public String getFieldName() { return fieldName; } - public Integer getLineno() { + public int getLineno() { return lineno; } diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/WordCountFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/WordCountFilter.java index 35a725458..c01e4a40d 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/WordCountFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/WordCountFilter.java @@ -13,10 +13,9 @@ snippets = { @JinjavaSnippet( code = "{% set count_words = \"Count the number of words in this variable\" %}\n" + - "{{ count_words|wordcount }}" - ) + "{{ count_words|wordcount }}") - }) +}) public class WordCountFilter implements Filter { @Override @@ -34,7 +33,7 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args) count++; } - return count; + return Integer.valueOf(count); } private static final Pattern WORD_RE = Pattern.compile("\\w+", Pattern.UNICODE_CHARACTER_CLASS | Pattern.MULTILINE); diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java index 98760260b..255cb85b3 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java @@ -91,9 +91,10 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { try { String template = interpreter.getResource(path); Node node = interpreter.parse(template); - JinjavaInterpreter currentInterpreter = JinjavaInterpreter.getCurrent(); - currentInterpreter.getContext().addDependency("coded_files", path); + + interpreter.getContext().addDependency("coded_files", path); interpreter.addExtendParentRoot(node); + return ""; } catch (IOException e) { throw new InterpretException(e.getMessage(), e, tagNode.getLineNumber()); diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java index b227f4135..611581602 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java @@ -68,12 +68,11 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { String template = interpreter.getResource(templateFile); Node node = interpreter.parse(template); - JinjavaInterpreter currentInterpreter = JinjavaInterpreter.getCurrent(); - JinjavaInterpreter child = new JinjavaInterpreter(currentInterpreter); - - currentInterpreter.getContext().addDependency("coded_files", templateFile); + interpreter.getContext().addDependency("coded_files", templateFile); + JinjavaInterpreter child = new JinjavaInterpreter(interpreter); String result = child.render(node); + interpreter.getErrors().addAll(child.getErrors()); return result; From cf772cba5394e355ad763ebd960e34fbe5b89591 Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Wed, 27 Jan 2016 14:21:58 -0500 Subject: [PATCH 0086/2465] add error item --- .../jinjava/el/ExpressionResolver.java | 3 +- .../el/JinjavaInterpreterResolver.java | 5 ++-- .../jinjava/interpret/TemplateError.java | 29 ++++++++++++++----- .../hubspot/jinjava/lib/tag/ImportTag.java | 3 +- .../hubspot/jinjava/lib/tag/IncludeTag.java | 3 +- 5 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java index b62e3597f..05f126fe1 100644 --- a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java @@ -9,6 +9,7 @@ import javax.el.PropertyNotFoundException; import javax.el.ValueExpression; +import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; import org.apache.commons.lang3.StringUtils; import com.hubspot.jinjava.el.ext.NamedParameter; @@ -65,7 +66,7 @@ public Object resolveExpression(String expression) { return result; } catch (PropertyNotFoundException e) { - interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.UNKNOWN, e.getMessage(), "", interpreter.getLineNumber(), e)); + interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.UNKNOWN, ErrorItem.OTHER, e.getMessage(), "", interpreter.getLineNumber(), e)); } catch (TreeBuilderException e) { interpreter.addError(TemplateError.fromException(new TemplateSyntaxException(expression, "Error parsing '" + expression + "': " + StringUtils.substringAfter(e.getMessage(), "': "), interpreter.getLineNumber(), e))); diff --git a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java index 5c4341f30..c7f26f2da 100644 --- a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java @@ -21,6 +21,7 @@ import javax.el.PropertyNotFoundException; import javax.el.ResourceBundleELResolver; +import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; import org.apache.commons.lang3.LocaleUtils; import org.apache.commons.lang3.StringUtils; @@ -174,7 +175,7 @@ private static DateTimeFormatter getFormatter(JinjavaInterpreter interpreter, Fo try { return StrftimeFormatter.formatter(d.getFormat(), interpreter.getConfig().getLocale()); } catch (IllegalArgumentException e) { - interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.SYNTAX_ERROR, e.getMessage(), null, interpreter.getLineNumber(), null)); + interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.SYNTAX_ERROR, ErrorItem.OTHER, e.getMessage(), null, interpreter.getLineNumber(), null)); } } @@ -186,7 +187,7 @@ private static Locale getLocale(JinjavaInterpreter interpreter, FormattedDate d) try { return LocaleUtils.toLocale(d.getLanguage()); } catch (IllegalArgumentException e) { - interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.SYNTAX_ERROR, e.getMessage(), null, interpreter.getLineNumber(), null)); + interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.SYNTAX_ERROR, ErrorItem.OTHER, e.getMessage(), null, interpreter.getLineNumber(), null)); } } diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java index 663837a50..c52385bd0 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java @@ -18,11 +18,20 @@ public enum ErrorReason { BAD_URL, EXCEPTION, MISSING, + MISSING_TEMPLATE, OTHER } + public enum ErrorItem { + TEMPLATE, + TOKEN, + FUNCTION, + OTHER; + } + private final ErrorType severity; private final ErrorReason reason; + private final ErrorItem item; private final String message; private final String fieldName; private final int lineno; @@ -30,11 +39,11 @@ public enum ErrorReason { private final Exception exception; public static TemplateError fromSyntaxError(InterpretException ex) { - return new TemplateError(ErrorType.FATAL, ErrorReason.SYNTAX_ERROR, ExceptionUtils.getMessage(ex), null, ex.getLineNumber(), ex); + return new TemplateError(ErrorType.FATAL, ErrorReason.SYNTAX_ERROR, ErrorItem.OTHER, ExceptionUtils.getMessage(ex), null, ex.getLineNumber(), ex); } public static TemplateError fromException(TemplateSyntaxException ex) { - return new TemplateError(ErrorType.FATAL, ErrorReason.SYNTAX_ERROR, ExceptionUtils.getMessage(ex), null, ex.getLineNumber(), ex); + return new TemplateError(ErrorType.FATAL, ErrorReason.SYNTAX_ERROR, ErrorItem.OTHER, ExceptionUtils.getMessage(ex), null, ex.getLineNumber(), ex); } public static TemplateError fromException(Exception ex) { @@ -44,15 +53,15 @@ public static TemplateError fromException(Exception ex) { lineNumber = ((InterpretException) ex).getLineNumber(); } - return new TemplateError(ErrorType.FATAL, ErrorReason.EXCEPTION, ExceptionUtils.getMessage(ex), null, lineNumber, ex); + return new TemplateError(ErrorType.FATAL, ErrorReason.EXCEPTION, ErrorItem.OTHER, ExceptionUtils.getMessage(ex), null, lineNumber, ex); } public static TemplateError fromException(Exception ex, int lineNumber) { - return new TemplateError(ErrorType.FATAL, ErrorReason.EXCEPTION, ExceptionUtils.getMessage(ex), null, lineNumber, ex); + return new TemplateError(ErrorType.FATAL, ErrorReason.EXCEPTION, ErrorItem.OTHER, ExceptionUtils.getMessage(ex), null, lineNumber, ex); } public static TemplateError fromUnknownProperty(Object base, String variable, int lineNumber) { - return new TemplateError(ErrorType.WARNING, ErrorReason.UNKNOWN, String.format("Cannot resolve property '%s' in '%s'", variable, friendlyObjectToString(base)), + return new TemplateError(ErrorType.WARNING, ErrorReason.UNKNOWN, ErrorItem.OTHER, String.format("Cannot resolve property '%s' in '%s'", variable, friendlyObjectToString(base)), variable, lineNumber, null); } @@ -71,17 +80,18 @@ private static String friendlyObjectToString(Object o) { return c.getSimpleName(); } - // java.lang.Object@7852e922 private static final Pattern GENERIC_TOSTRING_PATTERN = Pattern.compile("@[0-9a-z]{4,}$"); public TemplateError(ErrorType severity, ErrorReason reason, + ErrorItem item, String message, String fieldName, int lineno, Exception exception) { this.severity = severity; this.reason = reason; + this.item = item; this.message = message; this.fieldName = fieldName; this.lineno = lineno; @@ -96,6 +106,10 @@ public ErrorReason getReason() { return reason; } + public ErrorItem getItem() { + return item; + } + public String getMessage() { return message; } @@ -113,7 +127,7 @@ public Exception getException() { } public TemplateError serializable() { - return new TemplateError(severity, reason, message, fieldName, lineno, null); + return new TemplateError(severity, reason, item, message, fieldName, lineno, null); } @Override @@ -124,6 +138,7 @@ public String toString() { .add("message", message) .add("fieldName", fieldName) .add("lineno", lineno) + .add("item", item) .toString(); } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java index 13981f521..6379c5c4e 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.Map; +import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; import org.apache.commons.lang3.StringUtils; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; @@ -76,7 +77,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { try { interpreter.getContext().pushImportPath(path, tagNode.getLineNumber()); } catch (ImportTagCycleException e) { - interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.EXCEPTION, + interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.EXCEPTION, ErrorItem.TOKEN, "Import cycle detected for path: '" + path + "'", null, tagNode.getLineNumber(), e)); return ""; } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java index 611581602..1b11bae2e 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java @@ -17,6 +17,7 @@ import java.io.IOException; +import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; import org.apache.commons.lang3.StringUtils; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; @@ -59,7 +60,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { try { interpreter.getContext().pushIncludePath(templateFile, tagNode.getLineNumber()); } catch (IncludeTagCycleException e) { - interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.EXCEPTION, + interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.EXCEPTION, ErrorItem.TOKEN, "Include cycle detected for path: '" + templateFile + "'", null, tagNode.getLineNumber(), e)); return ""; } From 7e977e42becb9757baaf3983305d24cf1db5aea8 Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Wed, 27 Jan 2016 16:39:25 -0500 Subject: [PATCH 0087/2465] Add ErrorItem.TAG --- src/main/java/com/hubspot/jinjava/interpret/TemplateError.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java index c52385bd0..e9180103f 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java @@ -25,6 +25,7 @@ public enum ErrorReason { public enum ErrorItem { TEMPLATE, TOKEN, + TAG, FUNCTION, OTHER; } From 1f5401d3f0f63599b883c257effebc1d2173050d Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Wed, 27 Jan 2016 16:43:05 -0500 Subject: [PATCH 0088/2465] remove MISSING_TEMPLATE --- src/main/java/com/hubspot/jinjava/interpret/TemplateError.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java index e9180103f..99d4050d5 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java @@ -18,7 +18,6 @@ public enum ErrorReason { BAD_URL, EXCEPTION, MISSING, - MISSING_TEMPLATE, OTHER } From a7053974fdff157832c3a886092ff60d6859373d Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Wed, 27 Jan 2016 16:43:41 -0500 Subject: [PATCH 0089/2465] remove semicolon --- src/main/java/com/hubspot/jinjava/interpret/TemplateError.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java index 99d4050d5..9f67387c7 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java @@ -26,7 +26,7 @@ public enum ErrorItem { TOKEN, TAG, FUNCTION, - OTHER; + OTHER } private final ErrorType severity; From 50d9c557914a9d0eba450cd5e34cf464a92e456e Mon Sep 17 00:00:00 2001 From: Amann Malik Date: Thu, 28 Jan 2016 10:23:14 -0600 Subject: [PATCH 0090/2465] allow multi-variable set assignments --- .../com/hubspot/jinjava/lib/tag/SetTag.java | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/SetTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/SetTag.java index 39fe9d2c4..452d35bb8 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/SetTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/SetTag.java @@ -24,6 +24,14 @@ import com.hubspot.jinjava.interpret.TemplateSyntaxException; import com.hubspot.jinjava.tree.TagNode; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + /** * {% set primary_line_height = primary_font_size_num*1.5 %} * @@ -76,8 +84,33 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'set' requires an expression to assign to a var", tagNode.getLineNumber()); } - Object val = interpreter.resolveELExpression(expr, tagNode.getLineNumber()); - interpreter.getContext().put(var, val); + String[] varTokens = var.split(","); + + if(varTokens.length > 1) { + + //handle multi-variable assignment + + List exprTokens = Pattern.compile("(,)(?=(?:[^']|'[^']*')*$)").splitAsStream(expr).collect(Collectors.toList()); + + if (varTokens.length != exprTokens.size()) { + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'set' declares an uneven number of variables and assigned values", tagNode.getLineNumber()); + } + + for (int i = 0; i < varTokens.length; i++) { + String varItem = varTokens[i].trim(); + String exprItem = exprTokens.get(i).trim(); + Object val = interpreter.resolveELExpression(exprItem, tagNode.getLineNumber()); + interpreter.getContext().put(varItem, val); + } + + } else { + + //handle single variable assignment + + Object val = interpreter.resolveELExpression(expr, tagNode.getLineNumber()); + interpreter.getContext().put(var, val); + + } return ""; } From 94bc7a65a0e5bc4b1bf1c7e0e89289f05f394c49 Mon Sep 17 00:00:00 2001 From: Amann Malik Date: Thu, 28 Jan 2016 10:26:09 -0600 Subject: [PATCH 0091/2465] remove unused imports --- src/main/java/com/hubspot/jinjava/lib/tag/SetTag.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/SetTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/SetTag.java index 452d35bb8..a53d9ff34 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/SetTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/SetTag.java @@ -24,11 +24,7 @@ import com.hubspot.jinjava.interpret.TemplateSyntaxException; import com.hubspot.jinjava.tree.TagNode; -import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; From c80306caad292ae06dc281fa7647cade4faa14a6 Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Thu, 28 Jan 2016 12:58:32 -0500 Subject: [PATCH 0092/2465] update tag --- src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java | 2 +- src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java index 6379c5c4e..228de894f 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java @@ -77,7 +77,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { try { interpreter.getContext().pushImportPath(path, tagNode.getLineNumber()); } catch (ImportTagCycleException e) { - interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.EXCEPTION, ErrorItem.TOKEN, + interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.EXCEPTION, ErrorItem.TAG, "Import cycle detected for path: '" + path + "'", null, tagNode.getLineNumber(), e)); return ""; } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java index 1b11bae2e..43fcb382a 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java @@ -60,7 +60,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { try { interpreter.getContext().pushIncludePath(templateFile, tagNode.getLineNumber()); } catch (IncludeTagCycleException e) { - interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.EXCEPTION, ErrorItem.TOKEN, + interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.EXCEPTION, ErrorItem.TAG, "Include cycle detected for path: '" + templateFile + "'", null, tagNode.getLineNumber(), e)); return ""; } From 54982d6213a987ca8fde6c17f6de48144742280d Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Thu, 28 Jan 2016 14:05:22 -0500 Subject: [PATCH 0093/2465] use expression engine to support arbitrary object values in multivar set @amannm --- .../com/hubspot/jinjava/lib/tag/SetTag.java | 24 +++++++------------ .../hubspot/jinjava/lib/tag/SetTagTest.java | 16 ++++++++++++- .../resources/tags/settag/set-multivar.jinja | 6 +++++ 3 files changed, 30 insertions(+), 16 deletions(-) create mode 100644 src/test/resources/tags/settag/set-multivar.jinja diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/SetTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/SetTag.java index a53d9ff34..52bf691f1 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/SetTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/SetTag.java @@ -15,6 +15,8 @@ **********************************************************************/ package com.hubspot.jinjava.lib.tag; +import java.util.List; + import org.apache.commons.lang3.StringUtils; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; @@ -24,10 +26,6 @@ import com.hubspot.jinjava.interpret.TemplateSyntaxException; import com.hubspot.jinjava.tree.TagNode; -import java.util.List; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - /** * {% set primary_line_height = primary_font_size_num*1.5 %} * @@ -82,27 +80,23 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { String[] varTokens = var.split(","); - if(varTokens.length > 1) { + if (varTokens.length > 1) { + // handle multi-variable assignment + @SuppressWarnings("unchecked") + List exprVals = (List) interpreter.resolveELExpression("[" + expr + "]", tagNode.getLineNumber()); - //handle multi-variable assignment - - List exprTokens = Pattern.compile("(,)(?=(?:[^']|'[^']*')*$)").splitAsStream(expr).collect(Collectors.toList()); - - if (varTokens.length != exprTokens.size()) { + if (varTokens.length != exprVals.size()) { throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'set' declares an uneven number of variables and assigned values", tagNode.getLineNumber()); } for (int i = 0; i < varTokens.length; i++) { String varItem = varTokens[i].trim(); - String exprItem = exprTokens.get(i).trim(); - Object val = interpreter.resolveELExpression(exprItem, tagNode.getLineNumber()); + Object val = exprVals.get(i); interpreter.getContext().put(varItem, val); } } else { - - //handle single variable assignment - + // handle single variable assignment Object val = interpreter.resolveELExpression(expr, tagNode.getLineNumber()); interpreter.getContext().put(var, val); diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/SetTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/SetTagTest.java index df4c356d3..f3dcca89d 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/SetTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/SetTagTest.java @@ -140,11 +140,25 @@ public void itSupportsListAppendFunc() throws Exception { assertThat(thelist).containsExactly("foo", "bar"); } + @Test + public void itSupportsMultiVar() throws Exception { + context.put("bar", "mybar"); + + TagNode tagNode = (TagNode) fixture("set-multivar"); + tag.interpret(tagNode, interpreter); + + assertThat(context).contains( + entry("myvar1", "foo"), + entry("myvar2", "mybar"), + entry("myvar3", Lists.newArrayList(1L, 2L, 3L, 4L)), + entry("myvar4", "yoooooo")); + } + private Node fixture(String name) { try { return new TreeParser(interpreter, Resources.toString( Resources.getResource(String.format("tags/settag/%s.jinja", name)), StandardCharsets.UTF_8)) - .buildTree().getChildren().getFirst(); + .buildTree().getChildren().getFirst(); } catch (IOException e) { throw Throwables.propagate(e); } diff --git a/src/test/resources/tags/settag/set-multivar.jinja b/src/test/resources/tags/settag/set-multivar.jinja new file mode 100644 index 000000000..2c3156505 --- /dev/null +++ b/src/test/resources/tags/settag/set-multivar.jinja @@ -0,0 +1,6 @@ +{% set myvar1, myvar2, myvar3, myvar4 = + 'foo', + bar, + (1, 2, 3, 4), + "yoooooo" +%} From 97a5cded0d7cbac5306d2efb95990b5d66e0a8d3 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Thu, 28 Jan 2016 14:09:55 -0500 Subject: [PATCH 0094/2465] add tests for unbalanced multivar set --- .../hubspot/jinjava/lib/tag/SetTagTest.java | 29 +++++++++++++++++++ .../settag/set-multivar-unbalanced-vals.jinja | 5 ++++ .../settag/set-multivar-unbalanced-vars.jinja | 6 ++++ 3 files changed, 40 insertions(+) create mode 100644 src/test/resources/tags/settag/set-multivar-unbalanced-vals.jinja create mode 100644 src/test/resources/tags/settag/set-multivar-unbalanced-vars.jinja diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/SetTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/SetTagTest.java index f3dcca89d..bb2bcf21f 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/SetTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/SetTagTest.java @@ -21,6 +21,7 @@ import com.hubspot.jinjava.Jinjava; import com.hubspot.jinjava.interpret.Context; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.interpret.TemplateSyntaxException; import com.hubspot.jinjava.tree.Node; import com.hubspot.jinjava.tree.TagNode; import com.hubspot.jinjava.tree.TreeParser; @@ -154,6 +155,34 @@ public void itSupportsMultiVar() throws Exception { entry("myvar4", "yoooooo")); } + @Test(expected = TemplateSyntaxException.class) + public void itThrowsErrorWhenMultiVarIsUnbalancedForVars() throws Exception { + context.put("bar", "mybar"); + + TagNode tagNode = (TagNode) fixture("set-multivar-unbalanced-vars"); + tag.interpret(tagNode, interpreter); + + assertThat(context).contains( + entry("myvar1", "foo"), + entry("myvar2", "mybar"), + entry("myvar3", Lists.newArrayList(1L, 2L, 3L, 4L)), + entry("myvar4", "yoooooo")); + } + + @Test(expected = TemplateSyntaxException.class) + public void itThrowsErrorWhenMultiVarIsUnbalancedForVals() throws Exception { + context.put("bar", "mybar"); + + TagNode tagNode = (TagNode) fixture("set-multivar-unbalanced-vals"); + tag.interpret(tagNode, interpreter); + + assertThat(context).contains( + entry("myvar1", "foo"), + entry("myvar2", "mybar"), + entry("myvar3", Lists.newArrayList(1L, 2L, 3L, 4L)), + entry("myvar4", "yoooooo")); + } + private Node fixture(String name) { try { return new TreeParser(interpreter, Resources.toString( diff --git a/src/test/resources/tags/settag/set-multivar-unbalanced-vals.jinja b/src/test/resources/tags/settag/set-multivar-unbalanced-vals.jinja new file mode 100644 index 000000000..4963a7f35 --- /dev/null +++ b/src/test/resources/tags/settag/set-multivar-unbalanced-vals.jinja @@ -0,0 +1,5 @@ +{% set myvar1, myvar2, myvar3, myvar4 = + 'foo', + bar, + "yoooooo" +%} diff --git a/src/test/resources/tags/settag/set-multivar-unbalanced-vars.jinja b/src/test/resources/tags/settag/set-multivar-unbalanced-vars.jinja new file mode 100644 index 000000000..d494641c4 --- /dev/null +++ b/src/test/resources/tags/settag/set-multivar-unbalanced-vars.jinja @@ -0,0 +1,6 @@ +{% set myvar1, myvar2, myvar3 = + 'foo', + bar, + (1, 2, 3, 4), + "yoooooo" +%} From 57490c036f795b47ee2b7ff73bd02474bd309232 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Thu, 28 Jan 2016 14:10:51 -0500 Subject: [PATCH 0095/2465] update changelog --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 3044f261e..03b068a21 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ ### Version 2.1.3 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.3%22)) ### * Added two new expression tests for strings: "is string_startingwith" and "is string_containing" +* Added support for multi-variable set in set tag (@amannm) ### Version 2.1.2 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.2%22)) ### From c054daee2e7d04221db2fec2ef705810e5d870dd Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Thu, 28 Jan 2016 14:35:45 -0500 Subject: [PATCH 0096/2465] add property ErrorItem --- src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java | 2 +- src/main/java/com/hubspot/jinjava/interpret/TemplateError.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java index 05f126fe1..0501a39fe 100644 --- a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java @@ -66,7 +66,7 @@ public Object resolveExpression(String expression) { return result; } catch (PropertyNotFoundException e) { - interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.UNKNOWN, ErrorItem.OTHER, e.getMessage(), "", interpreter.getLineNumber(), e)); + interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.UNKNOWN, ErrorItem.PROPERTY, e.getMessage(), "", interpreter.getLineNumber(), e)); } catch (TreeBuilderException e) { interpreter.addError(TemplateError.fromException(new TemplateSyntaxException(expression, "Error parsing '" + expression + "': " + StringUtils.substringAfter(e.getMessage(), "': "), interpreter.getLineNumber(), e))); diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java index 9f67387c7..27933a1d9 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java @@ -26,6 +26,7 @@ public enum ErrorItem { TOKEN, TAG, FUNCTION, + PROPERTY, OTHER } @@ -61,7 +62,7 @@ public static TemplateError fromException(Exception ex, int lineNumber) { } public static TemplateError fromUnknownProperty(Object base, String variable, int lineNumber) { - return new TemplateError(ErrorType.WARNING, ErrorReason.UNKNOWN, ErrorItem.OTHER, String.format("Cannot resolve property '%s' in '%s'", variable, friendlyObjectToString(base)), + return new TemplateError(ErrorType.WARNING, ErrorReason.UNKNOWN, ErrorItem.PROPERTY, String.format("Cannot resolve property '%s' in '%s'", variable, friendlyObjectToString(base)), variable, lineNumber, null); } From a1df4734fe3c787673f4d5613c136c49b7a84c31 Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Fri, 29 Jan 2016 09:05:21 -0500 Subject: [PATCH 0097/2465] add second constructor ot default to ErrorItem.OTHER --- .../hubspot/jinjava/interpret/TemplateError.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java index 27933a1d9..7de273546 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java @@ -99,6 +99,21 @@ public TemplateError(ErrorType severity, this.exception = exception; } + public TemplateError(ErrorType severity, + ErrorReason reason, + String message, + String fieldName, + int lineno, + Exception exception) { + this.severity = severity; + this.reason = reason; + this.item = ErrorItem.OTHER; + this.message = message; + this.fieldName = fieldName; + this.lineno = lineno; + this.exception = exception; + } + public ErrorType getSeverity() { return severity; } From a6aa05282ce09ff8c7930bbfebfa8c8a001c46c4 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Fri, 29 Jan 2016 10:14:00 -0500 Subject: [PATCH 0098/2465] update changelog --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 03b068a21..5c75324c5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,7 @@ * Added two new expression tests for strings: "is string_startingwith" and "is string_containing" * Added support for multi-variable set in set tag (@amannm) +* Added new detail dimension to TemplateError: ErrorItem ### Version 2.1.2 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.2%22)) ### From 786efb9ccab1c57fee680c006847800249aedc65 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Fri, 29 Jan 2016 16:56:28 +0000 Subject: [PATCH 0099/2465] [maven-release-plugin] prepare release jinjava-2.1.3 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 2912e648a..4dbf72b73 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.3-SNAPSHOT + 2.1.3 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.1.3 From 236197145cafd19220431cf56eeca52c42fae41e Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Fri, 29 Jan 2016 16:56:28 +0000 Subject: [PATCH 0100/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 4dbf72b73..8f18f7a0a 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.3 + 2.1.4-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.1.3 + HEAD From 366fed7d8d674727aee91849bc51b6f11306dbef Mon Sep 17 00:00:00 2001 From: Dan Kulla Date: Thu, 4 Feb 2016 09:50:55 -0500 Subject: [PATCH 0101/2465] add a pyishdate constructor that takes an Instant --- .../com/hubspot/jinjava/objects/date/PyishDate.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/objects/date/PyishDate.java b/src/main/java/com/hubspot/jinjava/objects/date/PyishDate.java index 718420308..0abb867eb 100644 --- a/src/main/java/com/hubspot/jinjava/objects/date/PyishDate.java +++ b/src/main/java/com/hubspot/jinjava/objects/date/PyishDate.java @@ -1,5 +1,8 @@ package com.hubspot.jinjava.objects.date; +import com.hubspot.jinjava.objects.PyWrapper; +import org.apache.commons.lang3.math.NumberUtils; + import java.io.Serializable; import java.time.Instant; import java.time.ZoneOffset; @@ -9,10 +12,6 @@ import java.util.Objects; import java.util.Optional; -import org.apache.commons.lang3.math.NumberUtils; - -import com.hubspot.jinjava.objects.PyWrapper; - /** * an object which quacks like a python date * @@ -42,6 +41,10 @@ public PyishDate(Long epochMillis) { Optional.ofNullable(epochMillis).orElseGet(System::currentTimeMillis)), ZoneOffset.UTC)); } + public PyishDate(Instant instant) { + this(ZonedDateTime.ofInstant(instant, ZoneOffset.UTC)); + } + public String isoformat() { return strftime("yyyy-MM-dd"); } From 966a13fa2feb98d3fc40b984233f0be91b2abfcf Mon Sep 17 00:00:00 2001 From: Zachary Webert Date: Tue, 9 Feb 2016 15:34:21 -0500 Subject: [PATCH 0102/2465] update line number on node render --- .../java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index 5135138c1..de9ae19b6 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -177,6 +177,7 @@ public String render(Node root, boolean processExtendRoots) { OutputList output = new OutputList(); for (Node node : root.getChildren()) { + lineNumber = node.getLineNumber(); output.addNode(node.render(this)); } From 51a0853bd6912137b09683ec2216a07b7f6e1135 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Thu, 11 Feb 2016 12:05:14 -0500 Subject: [PATCH 0103/2465] allow for null input bindings as empty --- src/main/java/com/hubspot/jinjava/interpret/Context.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index 362244b9a..080ccb942 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -74,7 +74,9 @@ public Context(Context parent) { public Context(Context parent, Map bindings) { this(parent); - this.putAll(bindings); + if (bindings != null) { + this.putAll(bindings); + } } @Override From da01d87a7f645aacaac8a65e9bf244ff9a781ca5 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Thu, 11 Feb 2016 12:06:06 -0500 Subject: [PATCH 0104/2465] fix outofbounds error when trimblocks and no trailing newline in template #34 --- .../jinjava/tree/parse/TokenScanner.java | 2 +- .../jinjava/tree/parse/TokenScannerTest.java | 19 ++++++++++++------- .../parse/tokenizer/tag-with-trim-chars.jinja | 3 +++ 3 files changed, 16 insertions(+), 8 deletions(-) create mode 100644 src/test/resources/parse/tokenizer/tag-with-trim-chars.jinja diff --git a/src/main/java/com/hubspot/jinjava/tree/parse/TokenScanner.java b/src/main/java/com/hubspot/jinjava/tree/parse/TokenScanner.java index bacc5b236..81cdb2226 100644 --- a/src/main/java/com/hubspot/jinjava/tree/parse/TokenScanner.java +++ b/src/main/java/com/hubspot/jinjava/tree/parse/TokenScanner.java @@ -244,7 +244,7 @@ private Token newToken(int kind) { Token t = Token.newToken(kind, String.valueOf(is, lastStart, tokenLength), currLine); if (t instanceof TagToken) { - if (config.isTrimBlocks() && is[currPost] == '\n') { + if (config.isTrimBlocks() && currPost < length && is[currPost] == '\n') { ++currPost; ++tokenStart; } diff --git a/src/test/java/com/hubspot/jinjava/tree/parse/TokenScannerTest.java b/src/test/java/com/hubspot/jinjava/tree/parse/TokenScannerTest.java index e79fbf4a6..16d9a8db2 100644 --- a/src/test/java/com/hubspot/jinjava/tree/parse/TokenScannerTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/parse/TokenScannerTest.java @@ -254,15 +254,20 @@ public void testEscapedBackslashWithinAttrValue() { "module_143819781983527999"); } + @Test + public void testLstripBlocks() { + config = JinjavaConfig.newBuilder() + .withLstripBlocks(true) + .withTrimBlocks(true) + .build(); + + List tokens = tokens("tag-with-trim-chars"); + assertThat(tokens).isNotEmpty(); + } + private List tokens(String fixture) { TokenScanner t = fixture(fixture); - - List tokens = Lists.newArrayList(); - while (t.hasNext()) { - tokens.add(t.next()); - } - - return tokens; + return Lists.newArrayList(t); } private TokenScanner fixture(String fixture) { diff --git a/src/test/resources/parse/tokenizer/tag-with-trim-chars.jinja b/src/test/resources/parse/tokenizer/tag-with-trim-chars.jinja new file mode 100644 index 000000000..bb1ab0390 --- /dev/null +++ b/src/test/resources/parse/tokenizer/tag-with-trim-chars.jinja @@ -0,0 +1,3 @@ +{% macro echo(what) -%} +echo {{ what }} +{%- endmacro %} \ No newline at end of file From f4daabf885bf83b2e8f3db65486d1a24063c7ead Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Thu, 11 Feb 2016 12:06:37 -0500 Subject: [PATCH 0105/2465] include exception stack trace in FatalTemplateErrorsException message --- .../jinjava/interpret/FatalTemplateErrorsException.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/com/hubspot/jinjava/interpret/FatalTemplateErrorsException.java b/src/main/java/com/hubspot/jinjava/interpret/FatalTemplateErrorsException.java index 4662158fb..c222752ea 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/FatalTemplateErrorsException.java +++ b/src/main/java/com/hubspot/jinjava/interpret/FatalTemplateErrorsException.java @@ -1,5 +1,7 @@ package com.hubspot.jinjava.interpret; +import org.apache.commons.lang3.exception.ExceptionUtils; + /** * Container exception thrown when fatal errors are encountered while rendering a template. * @@ -22,6 +24,10 @@ private static String generateMessage(Iterable errors) { for (TemplateError error : errors) { msg.append(error.toString()).append('\n'); + + if (error.getException() != null) { + msg.append(ExceptionUtils.getStackTrace(error.getException())).append('\n'); + } } return msg.toString(); From d16d2dc40e8508e74731ede62d9316690b2738d3 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Thu, 11 Feb 2016 12:08:16 -0500 Subject: [PATCH 0106/2465] update collection filtering to use j8 lambda --- src/main/java/com/hubspot/jinjava/Jinjava.java | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/Jinjava.java b/src/main/java/com/hubspot/jinjava/Jinjava.java index e0e7b731f..43279e62d 100644 --- a/src/main/java/com/hubspot/jinjava/Jinjava.java +++ b/src/main/java/com/hubspot/jinjava/Jinjava.java @@ -15,14 +15,13 @@ **********************************************************************/ package com.hubspot.jinjava; -import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.stream.Collectors; import javax.el.ExpressionFactory; -import com.google.common.base.Predicate; -import com.google.common.collect.Collections2; import com.hubspot.jinjava.doc.JinjavaDoc; import com.hubspot.jinjava.doc.JinjavaDocFactory; import com.hubspot.jinjava.el.ExtendedSyntaxBuilder; @@ -149,12 +148,9 @@ public JinjavaDoc getJinjavaDoc() { public String render(String template, Map bindings) { RenderResult result = renderForResult(template, bindings); - Collection fatalErrors = Collections2.filter(result.getErrors(), new Predicate() { - @Override - public boolean apply(TemplateError input) { - return input.getSeverity() == ErrorType.FATAL; - } - }); + List fatalErrors = result.getErrors().stream() + .filter(error -> error.getSeverity() == ErrorType.FATAL) + .collect(Collectors.toList()); if (!fatalErrors.isEmpty()) { throw new FatalTemplateErrorsException(template, fatalErrors); From efbf0d264f645af41ea90aa1c9e2e1ef3418aeab Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Thu, 11 Feb 2016 12:13:45 -0500 Subject: [PATCH 0107/2465] update changes --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 5c75324c5..44b3025b1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Jinjava Releases # +### Version 2.1.4 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.4%22)) ### + +* fixed ArrayIndexOutOfBoundsException when importing template with no trailing newline + ### Version 2.1.3 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.3%22)) ### * Added two new expression tests for strings: "is string_startingwith" and "is string_containing" From 96af0968aa3932aff0badcf51d8d89f7058d4775 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Thu, 11 Feb 2016 17:24:09 +0000 Subject: [PATCH 0108/2465] [maven-release-plugin] prepare release jinjava-2.1.4 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 8f18f7a0a..d3c48aac6 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.4-SNAPSHOT + 2.1.4 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.1.4 From dadf95babf61f994c6bebeca6b88ece8ef0ef615 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Thu, 11 Feb 2016 17:24:09 +0000 Subject: [PATCH 0109/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d3c48aac6..2b007b9fc 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.4 + 2.1.5-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.1.4 + HEAD From 94c323b4660449f12cd93dbd15e18d787a55922f Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Fri, 12 Feb 2016 15:29:47 -0500 Subject: [PATCH 0110/2465] add EL expression support for Optional properties --- CHANGES.md | 4 ++ .../el/JinjavaInterpreterResolver.java | 21 +++++- .../jinjava/el/ExpressionResolverTest.java | 67 +++++++++++++++++++ 3 files changed, 91 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 44b3025b1..d9a37c6be 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Jinjava Releases # +### Version 2.1.5 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.5%22)) ### + +* Add support for java.util.Optional properties, nested properties in EL expressions + ### Version 2.1.4 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.4%22)) ### * fixed ArrayIndexOutOfBoundsException when importing template with no trailing newline diff --git a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java index c7f26f2da..4e9dfa580 100644 --- a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java @@ -12,6 +12,7 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; +import java.util.Optional; import javax.el.ArrayELResolver; import javax.el.CompositeELResolver; @@ -21,7 +22,6 @@ import javax.el.PropertyNotFoundException; import javax.el.ResourceBundleELResolver; -import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; import org.apache.commons.lang3.LocaleUtils; import org.apache.commons.lang3.StringUtils; @@ -31,6 +31,7 @@ import com.hubspot.jinjava.el.ext.JinjavaListELResolver; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.TemplateError; +import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; import com.hubspot.jinjava.interpret.TemplateError.ErrorType; import com.hubspot.jinjava.objects.PyWrapper; @@ -116,7 +117,25 @@ private Object getValue(ELContext context, Object base, Object property, boolean } else { // Get property of base object. try { + if (base instanceof Optional) { + Optional optBase = (Optional) base; + if (!optBase.isPresent()) { + return null; + } + + base = optBase.get(); + } + value = super.getValue(context, base, propertyName); + + if (value instanceof Optional) { + Optional optValue = (Optional) value; + if (!optValue.isPresent()) { + return null; + } + + value = optValue.get(); + } } catch (PropertyNotFoundException e) { if (errOnUnknownProp) { interpreter.addError(TemplateError.fromUnknownProperty(base, propertyName, interpreter.getLineNumber())); diff --git a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java index 952f687fd..2fa861ce9 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java @@ -5,6 +5,8 @@ import java.util.Date; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.Optional; import org.junit.Before; import org.junit.Test; @@ -218,6 +220,41 @@ public void blackListedMethods() throws Exception { assertThat(e.getMessage()).contains("Cannot find method 'wait'"); } + @Test + public void presentOptionalProperty() { + context.put("myobj", new OptionalProperty(null, "foo")); + assertThat(interpreter.resolveELExpression("myobj.val", -1)).isEqualTo("foo"); + assertThat(interpreter.getErrors()).isEmpty(); + } + + @Test + public void emptyOptionalProperty() { + context.put("myobj", new OptionalProperty(null, null)); + assertThat(interpreter.resolveELExpression("myobj.val", -1)).isNull(); + assertThat(interpreter.getErrors()).isEmpty(); + } + + @Test + public void presentNestedOptionalProperty() { + context.put("myobj", new OptionalProperty(new MyClass(new Date(0)), "foo")); + assertThat(Objects.toString(interpreter.resolveELExpression("myobj.nested.date", -1))).isEqualTo("1970-01-01 00:00:00"); + assertThat(interpreter.getErrors()).isEmpty(); + } + + @Test + public void emptyNestedOptionalProperty() { + context.put("myobj", new OptionalProperty(null, null)); + assertThat(interpreter.resolveELExpression("myobj.nested.date", -1)).isNull(); + assertThat(interpreter.getErrors()).isEmpty(); + } + + @Test + public void presentNestedNestedOptionalProperty() { + context.put("myobj", new NestedOptionalProperty(new OptionalProperty(new MyClass(new Date(0)), "foo"))); + assertThat(Objects.toString(interpreter.resolveELExpression("myobj.nested.nested.date", -1))).isEqualTo("1970-01-01 00:00:00"); + assertThat(interpreter.getErrors()).isEmpty(); + } + public static final class MyClass { private Date date; @@ -229,4 +266,34 @@ public Date getDate() { return date; } } + + public static final class OptionalProperty { + private MyClass nested; + private String val; + + OptionalProperty(MyClass nested, String val) { + this.nested = nested; + this.val = val; + } + + public Optional getNested() { + return Optional.ofNullable(nested); + } + + public Optional getVal() { + return Optional.ofNullable(val); + } + } + + public static final class NestedOptionalProperty { + private OptionalProperty nested; + + public NestedOptionalProperty(OptionalProperty nested) { + this.nested = nested; + } + + public Optional getNested() { + return Optional.ofNullable(nested); + } + } } From 895026b5f43e00c6a88ce25c16b8222a06bd65a9 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Wed, 17 Feb 2016 16:15:22 +0000 Subject: [PATCH 0111/2465] [maven-release-plugin] prepare release jinjava-2.1.5 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 2b007b9fc..61e36a48a 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.5-SNAPSHOT + 2.1.5 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.1.5 From 97fea5c503a27ff05de7dfc85327e2ad9c150802 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Wed, 17 Feb 2016 16:15:22 +0000 Subject: [PATCH 0112/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 61e36a48a..d73e805a6 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.5 + 2.1.6-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.1.5 + HEAD From a7759439c1b315b182b9225427c4049fcbb448ec Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 22 Feb 2016 11:41:20 -0500 Subject: [PATCH 0113/2465] remove todo markers --- .../java/com/hubspot/jinjava/benchmarks/jinja2/Article.java | 2 +- .../main/java/com/hubspot/jinjava/benchmarks/liquid/Tags.java | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/benchmark/src/main/java/com/hubspot/jinjava/benchmarks/jinja2/Article.java b/benchmark/src/main/java/com/hubspot/jinjava/benchmarks/jinja2/Article.java index aacdab60b..acd85f369 100644 --- a/benchmark/src/main/java/com/hubspot/jinjava/benchmarks/jinja2/Article.java +++ b/benchmark/src/main/java/com/hubspot/jinjava/benchmarks/jinja2/Article.java @@ -28,7 +28,7 @@ public Article(int id, User user) throws NoSuchAlgorithmException { this.title = ipsum.getWords(10); this.user = user; this.body = ipsum.getParagraphs(); - this.pubDate = Date.from(LocalDateTime.now().minusHours(rnd.nextInt(128)).toInstant(ZoneOffset.UTC)); // TODO randomize + this.pubDate = Date.from(LocalDateTime.now().minusHours(rnd.nextInt(128)).toInstant(ZoneOffset.UTC)); this.published = true; } diff --git a/benchmark/src/main/java/com/hubspot/jinjava/benchmarks/liquid/Tags.java b/benchmark/src/main/java/com/hubspot/jinjava/benchmarks/liquid/Tags.java index a27474bb4..088c455f2 100644 --- a/benchmark/src/main/java/com/hubspot/jinjava/benchmarks/liquid/Tags.java +++ b/benchmark/src/main/java/com/hubspot/jinjava/benchmarks/liquid/Tags.java @@ -67,8 +67,7 @@ public static class TableRowTag implements Tag { @Override public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { - // TODO Auto-generated method stub - return null; + return ""; } @Override From 252dc3ab3a3620c110962dbe976b022cf9ebe816 Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 22 Feb 2016 11:44:51 -0500 Subject: [PATCH 0114/2465] removing TODO markers --- src/main/java/com/hubspot/jinjava/el/ext/AstDict.java | 2 +- src/main/java/com/hubspot/jinjava/el/ext/AstList.java | 2 +- .../java/com/hubspot/jinjava/el/ext/AstNamedParameter.java | 2 +- src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java | 7 +++---- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/AstDict.java b/src/main/java/com/hubspot/jinjava/el/ext/AstDict.java index 0d1041229..2a6f62611 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/AstDict.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/AstDict.java @@ -46,7 +46,7 @@ public Object eval(Bindings bindings, ELContext context) { @Override public void appendStructure(StringBuilder builder, Bindings bindings) { - throw new UnsupportedOperationException("TODO"); + throw new UnsupportedOperationException("appendStructure not implemented in " + getClass().getSimpleName()); } @Override diff --git a/src/main/java/com/hubspot/jinjava/el/ext/AstList.java b/src/main/java/com/hubspot/jinjava/el/ext/AstList.java index 40b551128..cf7b8c42e 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/AstList.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/AstList.java @@ -34,7 +34,7 @@ public Object eval(Bindings bindings, ELContext context) { @Override public void appendStructure(StringBuilder builder, Bindings bindings) { - throw new UnsupportedOperationException("TODO"); + throw new UnsupportedOperationException("appendStructure not implemented in " + getClass().getSimpleName()); } protected String elementsToString() { diff --git a/src/main/java/com/hubspot/jinjava/el/ext/AstNamedParameter.java b/src/main/java/com/hubspot/jinjava/el/ext/AstNamedParameter.java index e4a6d971c..cd3ed73f4 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/AstNamedParameter.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/AstNamedParameter.java @@ -24,7 +24,7 @@ public Object eval(Bindings bindings, ELContext context) { @Override public void appendStructure(StringBuilder builder, Bindings bindings) { - throw new UnsupportedOperationException("TODO"); + throw new UnsupportedOperationException("appendStructure not implemented in " + getClass().getSimpleName()); } } diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java index 9370101bc..245a59626 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java @@ -54,7 +54,7 @@ public void forLoopNestedFor() throws Exception { TagNode tagNode = (TagNode) fixture("nested-fors"); assertThat(Splitter.on("\n").trimResults().omitEmptyStrings().split( tag.interpret(tagNode, interpreter))) - .contains("02", "03", "12", "13"); + .contains("02", "03", "12", "13"); } @Test @@ -110,15 +110,14 @@ public void forLoopSupportsAllLoopVarsInHublDocs() throws Exception { assertThat(dom.select(".item-0 .depth").text()).isEqualTo("1"); assertThat(dom.select(".item-0 .depth0").text()).isEqualTo("0"); - // TODO for loop depth not implemented - // assertThat(dom.select(".item-0 .subnum").text()).isEqualTo("6 1"); + assertThat(dom.select(".item-0 .subnum").text()).isEqualTo("6 0"); } private Node fixture(String name) { try { return new TreeParser(interpreter, Resources.toString( Resources.getResource(String.format("tags/fortag/%s.jinja", name)), StandardCharsets.UTF_8)) - .buildTree().getChildren().getFirst(); + .buildTree().getChildren().getFirst(); } catch (IOException e) { throw Throwables.propagate(e); } From a64d70a3746af23db03af525c472783c6bb5c80c Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Mon, 22 Feb 2016 11:47:00 -0500 Subject: [PATCH 0115/2465] update with github issue link --- .../java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java index 4e9dfa580..346d8af67 100644 --- a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java @@ -87,6 +87,7 @@ public Object invoke(ELContext context, Object base, Object method, } // TODO map named params to special arg in fn to invoke + // https://github.com/HubSpot/jinjava/issues/11 return super.invoke(context, base, method, paramTypes, params); } From 201b457d4dca50e526ed7e01acde32c82475fb5a Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Wed, 24 Feb 2016 13:48:22 +0100 Subject: [PATCH 0116/2465] Add bool filter The bool filter will convert a string/int value to a boolean "true"|bool = true "false"|bool = false 0|bool = false 1|bool = true true|bool = true false|bool = false --- .../jinjava/lib/filter/BoolFilter.java | 41 ++++++++++++++++ .../jinjava/lib/filter/FilterLibrary.java | 1 + .../jinjava/lib/filter/BoolFilterTest.java | 49 +++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 src/main/java/com/hubspot/jinjava/lib/filter/BoolFilter.java create mode 100644 src/test/java/com/hubspot/jinjava/lib/filter/BoolFilterTest.java diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/BoolFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/BoolFilter.java new file mode 100644 index 000000000..19814bd28 --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/lib/filter/BoolFilter.java @@ -0,0 +1,41 @@ +package com.hubspot.jinjava.lib.filter; + +import com.hubspot.jinjava.doc.annotations.JinjavaDoc; +import com.hubspot.jinjava.doc.annotations.JinjavaParam; +import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import org.apache.commons.lang3.BooleanUtils; + +/** + * bool(value) Convert value to boolean. + */ +@JinjavaDoc( + value = "Convert value into a boolean.", + params = { + @JinjavaParam(value = "value", desc = "The value to convert to a boolean"), + }, + snippets = { + @JinjavaSnippet( + desc = "This example converts a text string value to a boolean", + code = "{% if \"true\"|bool == true %}hello world{% endif %}") + }) +public class BoolFilter implements Filter { + @Override + public String getName() { + return "bool"; + } + + @Override + public Object filter(Object var, JinjavaInterpreter interpreter, + String... args) { + if (var == null) { + return false; + } + + final String str = var.toString(); + + return str.equals("1") ? Boolean.TRUE : BooleanUtils.toBoolean(str); + } + +} + diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java b/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java index 1729f9a53..5592ae5d1 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java @@ -59,6 +59,7 @@ protected void registerDefaults() { AbsFilter.class, AddFilter.class, + BoolFilter.class, CutFilter.class, DivideFilter.class, DivisibleFilter.class, diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/BoolFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/BoolFilterTest.java new file mode 100644 index 000000000..e653a61c2 --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/lib/filter/BoolFilterTest.java @@ -0,0 +1,49 @@ +package com.hubspot.jinjava.lib.filter; + +import com.hubspot.jinjava.Jinjava; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import org.junit.Before; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class BoolFilterTest { + BoolFilter filter; + JinjavaInterpreter interpreter; + + @Before + public void setup() { + interpreter = new Jinjava().newInterpreter(); + filter = new BoolFilter(); + } + + @Test + public void itReturnsStringAsBool() { + assertThat(filter.filter("true", interpreter)).isEqualTo(true); + assertThat(filter.filter("false", interpreter)).isEqualTo(false); + } + + @Test + public void itReturnsIntAsBool() { + assertThat(filter.filter(Integer.valueOf(0), interpreter)).isEqualTo(false); + assertThat(filter.filter(Integer.valueOf(1), interpreter)).isEqualTo(true); + assertThat(filter.filter(Integer.valueOf(2), interpreter)).isEqualTo(false); + } + + @Test + public void itReturnsFalseWhenVarIsNull() { + assertThat(filter.filter(null, interpreter)).isEqualTo(false); + } + + @Test + public void itReturnsFalseWhenVarIsString() { + assertThat(filter.filter("foobar", interpreter)).isEqualTo(false); + } + + @Test + public void itReturnsSameWhenVarIsBool() { + assertThat(filter.filter(false, interpreter)).isEqualTo(false); + assertThat(filter.filter(true, interpreter)).isEqualTo(true); + } + +} From 1d7315a779574ca89ad85df6beb0a354edc04acd Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Wed, 24 Feb 2016 13:56:25 +0100 Subject: [PATCH 0117/2465] Add license header --- .../com/hubspot/jinjava/lib/filter/BoolFilter.java | 13 +++++++++++++ .../hubspot/jinjava/lib/filter/BoolFilterTest.java | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/BoolFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/BoolFilter.java index 19814bd28..14a3adbdb 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/BoolFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/BoolFilter.java @@ -1,3 +1,16 @@ +/** + * 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.hubspot.jinjava.lib.filter; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/BoolFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/BoolFilterTest.java index e653a61c2..31349e796 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/BoolFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/BoolFilterTest.java @@ -1,3 +1,16 @@ +/** + * 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.hubspot.jinjava.lib.filter; import com.hubspot.jinjava.Jinjava; From fb79232da693e706e394642cb83332058bc989cf Mon Sep 17 00:00:00 2001 From: Jared Stehler Date: Wed, 24 Feb 2016 10:01:44 -0500 Subject: [PATCH 0118/2465] update changelog --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index d9a37c6be..709301fea 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Jinjava Releases # +### Version 2.1.6 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.6%22)) ### + +* Added new bool filter to return boolean value from string + ### Version 2.1.5 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.5%22)) ### * Add support for java.util.Optional properties, nested properties in EL expressions From cc558203c8605f62b47e4df90357d2dac14c8979 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Wed, 24 Feb 2016 16:36:57 -0500 Subject: [PATCH 0119/2465] track resolved expressions --- .../hubspot/jinjava/el/ExpressionResolver.java | 4 +++- .../com/hubspot/jinjava/interpret/Context.java | 17 +++++++++++++++++ .../jinjava/el/ExpressionResolverTest.java | 3 +++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java index 0501a39fe..059adcdd4 100644 --- a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java @@ -9,13 +9,13 @@ import javax.el.PropertyNotFoundException; import javax.el.ValueExpression; -import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; import org.apache.commons.lang3.StringUtils; import com.hubspot.jinjava.el.ext.NamedParameter; import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.TemplateError; +import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; import com.hubspot.jinjava.interpret.TemplateError.ErrorType; import com.hubspot.jinjava.interpret.TemplateSyntaxException; @@ -56,6 +56,8 @@ public Object resolveExpression(String expression) { return ""; } + interpreter.getContext().addResolvedExpression(expression.trim()); + try { String elExpression = "#{" + expression.trim() + "}"; ValueExpression valueExp = expressionFactory.createValueExpression(elContext, elExpression, Object.class); diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index 080ccb942..47448128c 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -18,12 +18,15 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.Stack; import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.SetMultimap; import com.hubspot.jinjava.lib.Importable; import com.hubspot.jinjava.lib.exptest.ExpTest; @@ -47,6 +50,8 @@ public class Context extends ScopeMap { private final Stack importPathStack = new Stack<>(); private final Stack includePathStack = new Stack<>(); + private final Set resolvedExpressions = new HashSet<>(); + private final ExpTestLibrary expTestLibrary; private final FilterLibrary filterLibrary; private final FunctionLibrary functionLibrary; @@ -134,6 +139,18 @@ public void setAutoEscape(Boolean autoEscape) { this.autoEscape = autoEscape; } + public void addResolvedExpression(String expression) { + resolvedExpressions.add(expression); + } + + public Set getResolvedExpressions() { + return ImmutableSet.copyOf(resolvedExpressions); + } + + public boolean wasExpressionResolved(String expression) { + return resolvedExpressions.contains(expression); + } + public List getSuperBlock() { if (superBlock != null) { return superBlock; diff --git a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java index 2fa861ce9..759db95b8 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java @@ -67,6 +67,7 @@ public void itResolvesUntrimmedExprs() throws Exception { context.put("foo", "bar"); Object val = interpreter.resolveELExpression(" foo ", -1); assertThat(val).isEqualTo("bar"); + assertThat(interpreter.getContext().wasExpressionResolved("foo")).isTrue(); } @Test @@ -91,6 +92,7 @@ public void itResolvesDictValWithBracket() throws Exception { Object val = interpreter.resolveELExpression("thedict['foo']", -1); assertThat(val).isEqualTo("bar"); + assertThat(interpreter.getContext().wasExpressionResolved("thedict['foo']")).isTrue(); } @Test @@ -101,6 +103,7 @@ public void itResolvesDictValWithDotParam() throws Exception { Object val = interpreter.resolveELExpression("thedict.foo", -1); assertThat(val).isEqualTo("bar"); + assertThat(interpreter.getContext().wasExpressionResolved("thedict.foo")).isTrue(); } @Test From 5dd45de6dec6540434031e186e005d531db135c6 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Wed, 24 Feb 2016 21:16:40 -0500 Subject: [PATCH 0120/2465] record values resolved --- .../jinjava/el/JinjavaInterpreterResolver.java | 2 ++ .../java/com/hubspot/jinjava/interpret/Context.java | 13 +++++++++++++ .../hubspot/jinjava/el/ExpressionResolverTest.java | 9 +++++++++ 3 files changed, 24 insertions(+) diff --git a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java index 346d8af67..c1f1b7e10 100644 --- a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java @@ -105,6 +105,8 @@ private Object getValue(ELContext context, Object base, Object property, boolean String propertyName = Objects.toString(property, ""); Object value = null; + interpreter.getContext().addResolvedValue(propertyName); + if (ExtendedParser.INTERPRETER.equals(property)) { value = interpreter; } else if (propertyName.startsWith(ExtendedParser.FILTER_PREFIX)) { diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index 47448128c..248c69a6b 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -51,6 +51,7 @@ public class Context extends ScopeMap { private final Stack includePathStack = new Stack<>(); private final Set resolvedExpressions = new HashSet<>(); + private final Set resolvedValues = new HashSet<>(); private final ExpTestLibrary expTestLibrary; private final FilterLibrary filterLibrary; @@ -151,6 +152,18 @@ public boolean wasExpressionResolved(String expression) { return resolvedExpressions.contains(expression); } + public void addResolvedValue(String value) { + resolvedValues.add(value); + } + + public Set getResolvedValues() { + return ImmutableSet.copyOf(resolvedValues); + } + + public boolean wasValueResolved(String value) { + return resolvedValues.contains(value); + } + public List getSuperBlock() { if (superBlock != null) { return superBlock; diff --git a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java index 759db95b8..6c12ada6b 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.math.BigDecimal; import java.util.Date; import java.util.List; import java.util.Map; @@ -75,6 +76,7 @@ public void itResolvesMathVals() throws Exception { context.put("i_am_seven", 7L); Object val = interpreter.resolveELExpression("(i_am_seven * 2 + 1)/3", -1); assertThat(val).isEqualTo(5.0); + assertThat(interpreter.getContext().wasValueResolved("i_am_seven")).isTrue(); } @Test @@ -146,6 +148,13 @@ public int getTotalCount() { } } + @Test + public void itRecordsFilterNames() throws Exception { + Object val = interpreter.resolveELExpression("2.3 | round", -1); + assertThat(val).isEqualTo(new BigDecimal(2)); + assertThat(interpreter.getContext().wasValueResolved("filter:round")).isTrue(); + } + @Test public void callCustomListProperty() throws Exception { List myList = new MyCustomList<>(Lists.newArrayList(1, 2, 3, 4)); From d725f8a1d872128cf75367d1d488f66d8f0f1dea Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Thu, 25 Feb 2016 10:16:56 -0500 Subject: [PATCH 0121/2465] Added record of expressions and values evalulated --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 709301fea..2d82f079c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ ### Version 2.1.6 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.6%22)) ### * Added new bool filter to return boolean value from string +* Added record of expressions and values evalulated ### Version 2.1.5 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.5%22)) ### From 9b1b60ab1f67bccb8c2e334d164249d110dbe748 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Thu, 25 Feb 2016 15:22:13 +0000 Subject: [PATCH 0122/2465] [maven-release-plugin] prepare release jinjava-2.1.6 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d73e805a6..278471afa 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.6-SNAPSHOT + 2.1.6 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.1.6 From 1590d2e990f2677ca4a685c115b89865a4b3ea50 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Thu, 25 Feb 2016 15:22:13 +0000 Subject: [PATCH 0123/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 278471afa..3e2fd3411 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.6 + 2.1.7-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.1.6 + HEAD From 37724710b0990eb7eef9bb0d68cf91021a2c141c Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Wed, 16 Mar 2016 16:06:55 -0400 Subject: [PATCH 0124/2465] Update tag node to support correct raw tag in widgets --- src/main/java/com/hubspot/jinjava/lib/tag/RawTag.java | 2 +- src/main/java/com/hubspot/jinjava/tree/TagNode.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/RawTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/RawTag.java index af5d25f1d..c644d738b 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/RawTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/RawTag.java @@ -41,7 +41,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { return result.toString(); } - private String renderNodeRaw(Node n) { + public String renderNodeRaw(Node n) { StringBuilder result = new StringBuilder(n.getMaster().getImage()); for (Node child : n.getChildren()) { diff --git a/src/main/java/com/hubspot/jinjava/tree/TagNode.java b/src/main/java/com/hubspot/jinjava/tree/TagNode.java index c79cd3fab..92fd19716 100644 --- a/src/main/java/com/hubspot/jinjava/tree/TagNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/TagNode.java @@ -73,4 +73,6 @@ public String getHelpers() { return master.getHelpers(); } + public Tag getTag() { return tag; } + } From c894ce0d4d1e002e729863980442ad502e023bb6 Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Wed, 16 Mar 2016 17:12:01 -0400 Subject: [PATCH 0125/2465] Add space --- src/main/java/com/hubspot/jinjava/tree/TagNode.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/tree/TagNode.java b/src/main/java/com/hubspot/jinjava/tree/TagNode.java index 92fd19716..ee71bb8dd 100644 --- a/src/main/java/com/hubspot/jinjava/tree/TagNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/TagNode.java @@ -73,6 +73,8 @@ public String getHelpers() { return master.getHelpers(); } - public Tag getTag() { return tag; } + public Tag getTag() { + return tag; + } } From 6abda4949e5802f477e65bbefea3d984fa56284f Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Mon, 21 Mar 2016 20:52:26 +0000 Subject: [PATCH 0126/2465] [maven-release-plugin] prepare release jinjava-2.1.7 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3e2fd3411..363298587 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.7-SNAPSHOT + 2.1.7 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.1.7 From e329ccacc5b67c4165862a186ab4a7a045bd5aff Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Mon, 21 Mar 2016 20:52:27 +0000 Subject: [PATCH 0127/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 363298587..370a71415 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.7 + 2.1.8-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.1.7 + HEAD From 47de73afb93462bbd03a345befc955d1ffd8632c Mon Sep 17 00:00:00 2001 From: Andy Aylward Date: Thu, 31 Mar 2016 14:08:48 -0400 Subject: [PATCH 0128/2465] list filter should work on strings --- .../jinjava/lib/filter/ListFilter.java | 3 +- .../jinjava/lib/filter/ListFilterTest.java | 42 +++++++++++++++++++ .../com/hubspot/jinjava/lib/tag/TagTest.java | 7 ++++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/test/java/com/hubspot/jinjava/lib/filter/ListFilterTest.java diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/ListFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/ListFilter.java index 9b90b2e54..ff37642a5 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/ListFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/ListFilter.java @@ -4,6 +4,7 @@ import java.util.List; import com.google.common.collect.Lists; +import com.google.common.primitives.Chars; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaParam; import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; @@ -32,7 +33,7 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args) List result; if (var instanceof String) { - result = Lists.newArrayList(((String) var).toCharArray()); + result = Chars.asList(((String) var).toCharArray()); } else if (Collection.class.isAssignableFrom(var.getClass())) { diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/ListFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/ListFilterTest.java new file mode 100644 index 000000000..3d6c8c83a --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/lib/filter/ListFilterTest.java @@ -0,0 +1,42 @@ +package com.hubspot.jinjava.lib.filter; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Set; + +import org.junit.Before; +import org.junit.Test; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +public class ListFilterTest { + + ListFilter filter; + + @Before + public void setup() { + filter = new ListFilter(); + } + + @Test + public void itConvertsStringToListOfChars() { + List o = (List)filter.filter("hello", null); + assertThat(o).isEqualTo(Lists.newArrayList('h', 'e', 'l', 'l', 'o')); + } + + @Test + public void itConvertsSetsToLists() { + Set ints = Sets.newHashSet(1,2,3); + List o = (List)filter.filter(ints, null); + assertThat(o).isEqualTo(Lists.newArrayList(1,2,3)); + } + + @Test + public void itWrapsNonCollectionNonStringsInLists() { + List o = (List)filter.filter(BigDecimal.ONE, null); + assertThat(o).isEqualTo(Lists.newArrayList(BigDecimal.ONE)); + } +} diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/TagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/TagTest.java index 62999c18a..c6b128f9f 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/TagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/TagTest.java @@ -98,6 +98,13 @@ public void forLoop4() { assertEquals("3-232-451-450-689", res); } + @Test + public void forLoop5() { + String s = "{% for item in var2|list %}
  • {{ item }}
  • {% endfor %}"; + res = jinjava.render(s, bindings); + assertEquals("
  • 4
  • 5
  • ", res); + } + @Test public void reverseFor() { script = "{% for item in var1|reverse %}{{item}}{% endfor%}"; From 2ac2d081a321bf38e076abf85ece64488374d250 Mon Sep 17 00:00:00 2001 From: Andy Aylward Date: Thu, 31 Mar 2016 14:15:44 -0400 Subject: [PATCH 0129/2465] add license --- .../jinjava/lib/filter/ListFilterTest.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/ListFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/ListFilterTest.java index 3d6c8c83a..f1181a316 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/ListFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/ListFilterTest.java @@ -1,3 +1,18 @@ +/********************************************************************** +Copyright (c) 2016 HubSpot 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.hubspot.jinjava.lib.filter; import static org.assertj.core.api.Assertions.assertThat; From 9b333933613a41fb6bc7ed4af1319b84e27f9067 Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Mon, 25 Apr 2016 11:23:11 -0400 Subject: [PATCH 0130/2465] start updating errors that use TemplateError to have i18n --- .../jinjava/interpret/TemplateError.java | 40 ++++++++++++++++++- .../interpret/TemplateErrorCategory.java | 12 ++++++ .../hubspot/jinjava/lib/tag/ImportTag.java | 5 ++- 3 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/hubspot/jinjava/interpret/TemplateErrorCategory.java diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java index 7de273546..21bd1e437 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java @@ -1,5 +1,7 @@ package com.hubspot.jinjava.interpret; +import java.util.HashMap; +import java.util.Map; import java.util.regex.Pattern; import org.apache.commons.lang3.exception.ExceptionUtils; @@ -36,6 +38,8 @@ public enum ErrorItem { private final String message; private final String fieldName; private final int lineno; + private final TemplateErrorCategory category; + private final Map categoryErrors; private final Exception exception; @@ -97,6 +101,28 @@ public TemplateError(ErrorType severity, this.fieldName = fieldName; this.lineno = lineno; this.exception = exception; + this.category = TemplateErrorCategory.UNKNOWN; + this.categoryErrors = new HashMap<>(); + } + + public TemplateError(ErrorType severity, + ErrorReason reason, + ErrorItem item, + String message, + String fieldName, + int lineno, + Exception exception, + TemplateErrorCategory category, + Map categoryErrors) { + this.severity = severity; + this.reason = reason; + this.item = item; + this.message = message; + this.fieldName = fieldName; + this.lineno = lineno; + this.exception = exception; + this.category = category; + this.categoryErrors = categoryErrors; } public TemplateError(ErrorType severity, @@ -112,6 +138,8 @@ public TemplateError(ErrorType severity, this.fieldName = fieldName; this.lineno = lineno; this.exception = exception; + this.category = TemplateErrorCategory.UNKNOWN; + this.categoryErrors = new HashMap<>(); } public ErrorType getSeverity() { @@ -142,8 +170,16 @@ public Exception getException() { return exception; } + public TemplateErrorCategory getCategory() { + return category; + } + + public Map getCategoryErrors() { + return categoryErrors; + } + public TemplateError serializable() { - return new TemplateError(severity, reason, item, message, fieldName, lineno, null); + return new TemplateError(severity, reason, item, message, fieldName, lineno, null, category, categoryErrors); } @Override @@ -155,6 +191,8 @@ public String toString() { .add("fieldName", fieldName) .add("lineno", lineno) .add("item", item) + .add("category", category) + .add("categoryErrors", categoryErrors) .toString(); } diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateErrorCategory.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateErrorCategory.java new file mode 100644 index 000000000..d2379f6a1 --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateErrorCategory.java @@ -0,0 +1,12 @@ +package com.hubspot.jinjava.interpret; + +import org.omg.CORBA.UNKNOWN; + +public enum TemplateErrorCategory { + IMPORT_CYCLE_DETECTED, + INVALID_CONVERT_RGB_COLOR, + INVALID_COLOR_VARIANT_RGB_COLOR, + MISSING_RESOURCE, + MISSING_TEMPLATE, + UNKNOWN +} diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java index 228de894f..3f2e85398 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java @@ -4,7 +4,9 @@ import java.util.List; import java.util.Map; +import com.google.common.collect.ImmutableMap; import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; +import com.hubspot.jinjava.interpret.TemplateErrorCategory; import org.apache.commons.lang3.StringUtils; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; @@ -78,7 +80,8 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { interpreter.getContext().pushImportPath(path, tagNode.getLineNumber()); } catch (ImportTagCycleException e) { interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.EXCEPTION, ErrorItem.TAG, - "Import cycle detected for path: '" + path + "'", null, tagNode.getLineNumber(), e)); + "Import cycle detected for path: '" + path + "'", null, tagNode.getLineNumber(), + e, TemplateErrorCategory.IMPORT_CYCLE_DETECTED, ImmutableMap.of("path", path))); return ""; } From 97d74da058e4271bdd04800eb89fc9e761e1c806 Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Mon, 25 Apr 2016 12:23:48 -0400 Subject: [PATCH 0131/2465] add more enums for blogs --- .../com/hubspot/jinjava/interpret/TemplateErrorCategory.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateErrorCategory.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateErrorCategory.java index d2379f6a1..b48deb59d 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateErrorCategory.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateErrorCategory.java @@ -1,12 +1,14 @@ package com.hubspot.jinjava.interpret; -import org.omg.CORBA.UNKNOWN; public enum TemplateErrorCategory { + BLOG_NOT_FOUND_BY_ID, + BLOG_INVALID_PARAMETERS, IMPORT_CYCLE_DETECTED, INVALID_CONVERT_RGB_COLOR, INVALID_COLOR_VARIANT_RGB_COLOR, MISSING_RESOURCE, MISSING_TEMPLATE, + UNKNOWN_EMBED_TYPE, UNKNOWN } From 7083d4c5f5d33f2e7787aa1f6d41ed9a5a6a3da9 Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Mon, 25 Apr 2016 13:53:17 -0400 Subject: [PATCH 0132/2465] Update errors --- .../hubspot/jinjava/interpret/TemplateError.java | 6 ++++-- .../jinjava/interpret/TemplateErrorCategory.java | 14 -------------- .../errorcategory/BasicTemplateErrorCategory.java | 6 ++++++ .../errorcategory/TemplateErrorCategory.java | 4 ++++ .../com/hubspot/jinjava/lib/tag/ImportTag.java | 2 +- 5 files changed, 15 insertions(+), 17 deletions(-) delete mode 100644 src/main/java/com/hubspot/jinjava/interpret/TemplateErrorCategory.java create mode 100644 src/main/java/com/hubspot/jinjava/interpret/errorcategory/BasicTemplateErrorCategory.java create mode 100644 src/main/java/com/hubspot/jinjava/interpret/errorcategory/TemplateErrorCategory.java diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java index 21bd1e437..a8d8ff167 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java @@ -4,6 +4,8 @@ import java.util.Map; import java.util.regex.Pattern; +import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory; +import com.hubspot.jinjava.interpret.errorcategory.TemplateErrorCategory; import org.apache.commons.lang3.exception.ExceptionUtils; import com.google.common.base.Objects; @@ -101,7 +103,7 @@ public TemplateError(ErrorType severity, this.fieldName = fieldName; this.lineno = lineno; this.exception = exception; - this.category = TemplateErrorCategory.UNKNOWN; + this.category = BasicTemplateErrorCategory.UNKNOWN; this.categoryErrors = new HashMap<>(); } @@ -138,7 +140,7 @@ public TemplateError(ErrorType severity, this.fieldName = fieldName; this.lineno = lineno; this.exception = exception; - this.category = TemplateErrorCategory.UNKNOWN; + this.category = BasicTemplateErrorCategory.UNKNOWN; this.categoryErrors = new HashMap<>(); } diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateErrorCategory.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateErrorCategory.java deleted file mode 100644 index b48deb59d..000000000 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateErrorCategory.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.hubspot.jinjava.interpret; - - -public enum TemplateErrorCategory { - BLOG_NOT_FOUND_BY_ID, - BLOG_INVALID_PARAMETERS, - IMPORT_CYCLE_DETECTED, - INVALID_CONVERT_RGB_COLOR, - INVALID_COLOR_VARIANT_RGB_COLOR, - MISSING_RESOURCE, - MISSING_TEMPLATE, - UNKNOWN_EMBED_TYPE, - UNKNOWN -} diff --git a/src/main/java/com/hubspot/jinjava/interpret/errorcategory/BasicTemplateErrorCategory.java b/src/main/java/com/hubspot/jinjava/interpret/errorcategory/BasicTemplateErrorCategory.java new file mode 100644 index 000000000..3eceb2e9f --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/interpret/errorcategory/BasicTemplateErrorCategory.java @@ -0,0 +1,6 @@ +package com.hubspot.jinjava.interpret.errorcategory; + +public enum BasicTemplateErrorCategory implements TemplateErrorCategory { + IMPORT_CYCLE_DETECTED, + UNKNOWN +} diff --git a/src/main/java/com/hubspot/jinjava/interpret/errorcategory/TemplateErrorCategory.java b/src/main/java/com/hubspot/jinjava/interpret/errorcategory/TemplateErrorCategory.java new file mode 100644 index 000000000..7deb276c6 --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/interpret/errorcategory/TemplateErrorCategory.java @@ -0,0 +1,4 @@ +package com.hubspot.jinjava.interpret.errorcategory; + +public interface TemplateErrorCategory { +} diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java index 3f2e85398..dc6ee0cb8 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java @@ -6,7 +6,7 @@ import com.google.common.collect.ImmutableMap; import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; -import com.hubspot.jinjava.interpret.TemplateErrorCategory; +import com.hubspot.jinjava.interpret.errorcategory.TemplateErrorCategory; import org.apache.commons.lang3.StringUtils; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; From 8ea1df710278aedaa431ead91717011791e0b4dd Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Mon, 25 Apr 2016 16:05:25 -0400 Subject: [PATCH 0133/2465] update errors in tags --- .../java/com/hubspot/jinjava/el/ExpressionResolver.java | 5 ++++- .../hubspot/jinjava/el/JinjavaInterpreterResolver.java | 8 ++++++-- .../java/com/hubspot/jinjava/interpret/TemplateError.java | 3 ++- .../errorcategory/BasicTemplateErrorCategory.java | 1 + src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java | 3 ++- src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java | 5 ++++- 6 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java index 059adcdd4..18d8d4fac 100644 --- a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java @@ -9,6 +9,8 @@ import javax.el.PropertyNotFoundException; import javax.el.ValueExpression; +import com.google.common.collect.ImmutableMap; +import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory; import org.apache.commons.lang3.StringUtils; import com.hubspot.jinjava.el.ext.NamedParameter; @@ -68,7 +70,8 @@ public Object resolveExpression(String expression) { return result; } catch (PropertyNotFoundException e) { - interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.UNKNOWN, ErrorItem.PROPERTY, e.getMessage(), "", interpreter.getLineNumber(), e)); + interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.UNKNOWN, ErrorItem.PROPERTY, e.getMessage(), "", interpreter.getLineNumber(), e, + BasicTemplateErrorCategory.UNKNOWN, ImmutableMap.of("exception", e.getMessage()))); } catch (TreeBuilderException e) { interpreter.addError(TemplateError.fromException(new TemplateSyntaxException(expression, "Error parsing '" + expression + "': " + StringUtils.substringAfter(e.getMessage(), "': "), interpreter.getLineNumber(), e))); diff --git a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java index c1f1b7e10..3854d52c1 100644 --- a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java @@ -22,6 +22,8 @@ import javax.el.PropertyNotFoundException; import javax.el.ResourceBundleELResolver; +import com.google.common.collect.ImmutableMap; +import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory; import org.apache.commons.lang3.LocaleUtils; import org.apache.commons.lang3.StringUtils; @@ -197,7 +199,8 @@ private static DateTimeFormatter getFormatter(JinjavaInterpreter interpreter, Fo try { return StrftimeFormatter.formatter(d.getFormat(), interpreter.getConfig().getLocale()); } catch (IllegalArgumentException e) { - interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.SYNTAX_ERROR, ErrorItem.OTHER, e.getMessage(), null, interpreter.getLineNumber(), null)); + interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.SYNTAX_ERROR, ErrorItem.OTHER, e.getMessage(), null, interpreter.getLineNumber(), null, + BasicTemplateErrorCategory.UNKNOWN, ImmutableMap.of("exception", e.getMessage()))); } } @@ -209,7 +212,8 @@ private static Locale getLocale(JinjavaInterpreter interpreter, FormattedDate d) try { return LocaleUtils.toLocale(d.getLanguage()); } catch (IllegalArgumentException e) { - interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.SYNTAX_ERROR, ErrorItem.OTHER, e.getMessage(), null, interpreter.getLineNumber(), null)); + interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.SYNTAX_ERROR, ErrorItem.OTHER, e.getMessage(), null, interpreter.getLineNumber(), null, + BasicTemplateErrorCategory.UNKNOWN, ImmutableMap.of("exception", e.getMessage()))); } } diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java index a8d8ff167..5de6c5700 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java @@ -4,6 +4,7 @@ import java.util.Map; import java.util.regex.Pattern; +import com.google.common.collect.ImmutableMap; import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory; import com.hubspot.jinjava.interpret.errorcategory.TemplateErrorCategory; import org.apache.commons.lang3.exception.ExceptionUtils; @@ -60,7 +61,7 @@ public static TemplateError fromException(Exception ex) { lineNumber = ((InterpretException) ex).getLineNumber(); } - return new TemplateError(ErrorType.FATAL, ErrorReason.EXCEPTION, ErrorItem.OTHER, ExceptionUtils.getMessage(ex), null, lineNumber, ex); + return new TemplateError(ErrorType.FATAL, ErrorReason.EXCEPTION, ErrorItem.OTHER, ExceptionUtils.getMessage(ex), null, lineNumber, ex, BasicTemplateErrorCategory.UNKNOWN, ImmutableMap.of()); } public static TemplateError fromException(Exception ex, int lineNumber) { diff --git a/src/main/java/com/hubspot/jinjava/interpret/errorcategory/BasicTemplateErrorCategory.java b/src/main/java/com/hubspot/jinjava/interpret/errorcategory/BasicTemplateErrorCategory.java index 3eceb2e9f..dc3d361ea 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/errorcategory/BasicTemplateErrorCategory.java +++ b/src/main/java/com/hubspot/jinjava/interpret/errorcategory/BasicTemplateErrorCategory.java @@ -2,5 +2,6 @@ public enum BasicTemplateErrorCategory implements TemplateErrorCategory { IMPORT_CYCLE_DETECTED, + INCLUDE_CYCLE_DETECTED, UNKNOWN } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java index dc6ee0cb8..1f6a3fd00 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java @@ -6,6 +6,7 @@ import com.google.common.collect.ImmutableMap; import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; +import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory; import com.hubspot.jinjava.interpret.errorcategory.TemplateErrorCategory; import org.apache.commons.lang3.StringUtils; @@ -81,7 +82,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { } catch (ImportTagCycleException e) { interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.EXCEPTION, ErrorItem.TAG, "Import cycle detected for path: '" + path + "'", null, tagNode.getLineNumber(), - e, TemplateErrorCategory.IMPORT_CYCLE_DETECTED, ImmutableMap.of("path", path))); + e, BasicTemplateErrorCategory.IMPORT_CYCLE_DETECTED, ImmutableMap.of("path", path))); return ""; } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java index 43fcb382a..9acbf176d 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java @@ -17,7 +17,9 @@ import java.io.IOException; +import com.google.common.collect.ImmutableMap; import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; +import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory; import org.apache.commons.lang3.StringUtils; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; @@ -61,7 +63,8 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { interpreter.getContext().pushIncludePath(templateFile, tagNode.getLineNumber()); } catch (IncludeTagCycleException e) { interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.EXCEPTION, ErrorItem.TAG, - "Include cycle detected for path: '" + templateFile + "'", null, tagNode.getLineNumber(), e)); + "Include cycle detected for path: '" + templateFile + "'", null, tagNode.getLineNumber(), e, + BasicTemplateErrorCategory.INCLUDE_CYCLE_DETECTED, ImmutableMap.of("path", templateFile))); return ""; } From 69aa7850977f99c15ebb3a7efa04f1f2d27fbb1b Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 25 Apr 2016 17:28:19 -0400 Subject: [PATCH 0134/2465] add range function --- .../jinjava/lib/fn/FunctionLibrary.java | 1 + .../com/hubspot/jinjava/lib/fn/Functions.java | 58 +++++++++++++++++++ .../jinjava/lib/filter/ListFilterTest.java | 1 + .../jinjava/lib/fn/RangeFunctionTest.java | 37 ++++++++++++ 4 files changed, 97 insertions(+) create mode 100644 src/test/java/com/hubspot/jinjava/lib/fn/RangeFunctionTest.java diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/FunctionLibrary.java b/src/main/java/com/hubspot/jinjava/lib/fn/FunctionLibrary.java index 80ccae6b6..10fa480ae 100644 --- a/src/main/java/com/hubspot/jinjava/lib/fn/FunctionLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/FunctionLibrary.java @@ -13,6 +13,7 @@ public FunctionLibrary(boolean registerDefaults) { protected void registerDefaults() { register(new ELFunctionDefinition("", "datetimeformat", Functions.class, "dateTimeFormat", Object.class, String[].class)); register(new ELFunctionDefinition("", "truncate", Functions.class, "truncate", Object.class, Object[].class)); + register(new ELFunctionDefinition("", "range", Functions.class, "range", Object.class, Object[].class)); register(new ELFunctionDefinition("", "super", Functions.class, "renderSuperBlock")); diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java index 4aacbec99..8c00ab9de 100644 --- a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java @@ -5,6 +5,7 @@ import java.time.Instant; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; @@ -25,6 +26,8 @@ public class Functions { + public static final int RANGE_LIMIT = 1000; + @JinjavaDoc(value = "Only usable within blocks, will render the contents of the parent block by calling super.", snippets = { @JinjavaSnippet(desc = "This gives back the results of the parent block", code = "{% block sidebar %}\n" + "

    Table Of Contents

    \n\n" + @@ -145,4 +148,59 @@ public static int movePointerToJustBeforeLastWord(int pointer, String s) { return pointer + 1; } + @JinjavaDoc(value = "

    Return a list containing an arithmetic progression of integers. " + + " With one parameter, range will return a list from 0 up to or down to the value. " + + " With two parameters, the range will start at the first value and end at the second value. " + + " The third parameter specifies the step increment.

    All values can be negative.

    " + + "

    Ranges can generate a maximum of " + RANGE_LIMIT + " values.

    ", + params = { + @JinjavaParam(value = "start", type = "number", defaultValue = "0"), + @JinjavaParam(value = "end", type = "number"), + @JinjavaParam(value = "step", type = "number", defaultValue = "1")}) + public static List range(Object arg1, Object... args) { + + List result = new ArrayList<>(); + + int start = 0; + int end; + int step = 1; + + switch (args.length) { + case 0: + end = Integer.parseInt(arg1.toString()); + break; + case 1: + start = Integer.parseInt(arg1.toString()); + end = Integer.parseInt(args[0].toString()); + break; + default: + start = Integer.parseInt(arg1.toString()); + end = Integer.parseInt(args[0].toString()); + step = Integer.parseInt(args[1].toString()); + } + + if (start < end) { + for (int i = start; i < end; i += step) { + if (result.size() >= RANGE_LIMIT) { + break; + } + result.add(i); + } + } else { + + // make sure we're decrementing + if (step > 0) { + step = step * -1; + } + + for (int i = start; i > end; i += step) { + if (result.size() >= RANGE_LIMIT) { + break; + } + result.add(i); + } + } + + return result; + } } diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/ListFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/ListFilterTest.java index f1181a316..649075e37 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/ListFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/ListFilterTest.java @@ -40,6 +40,7 @@ public void setup() { public void itConvertsStringToListOfChars() { List o = (List)filter.filter("hello", null); assertThat(o).isEqualTo(Lists.newArrayList('h', 'e', 'l', 'l', 'o')); + assertThat(o.get(0)).isEqualTo('h'); } @Test diff --git a/src/test/java/com/hubspot/jinjava/lib/fn/RangeFunctionTest.java b/src/test/java/com/hubspot/jinjava/lib/fn/RangeFunctionTest.java new file mode 100644 index 000000000..028d83453 --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/lib/fn/RangeFunctionTest.java @@ -0,0 +1,37 @@ +package com.hubspot.jinjava.lib.fn; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Arrays; + +import org.junit.Test; + +public class RangeFunctionTest { + + @Test + public void itGeneratesSimpleRanges() { + assertThat(Functions.range(1)).isEqualTo(Arrays.asList(0)); + assertThat(Functions.range(2)).isEqualTo(Arrays.asList(0, 1)); + assertThat(Functions.range(2, 4)).isEqualTo(Arrays.asList(2, 3)); + assertThat(Functions.range(2, 8, 2)).isEqualTo(Arrays.asList(2, 4, 6)); + } + + @Test + public void itGeneratesBackwardsRanges() { + assertThat(Functions.range(-2)).isEqualTo(Arrays.asList(0, -1)); + assertThat(Functions.range(2, -1)).isEqualTo(Arrays.asList(2, 1, 0)); + assertThat(Functions.range(8, 2, -2)).isEqualTo(Arrays.asList(8, 6, 4)); + } + + @Test + public void itHandlesBadRanges() { + assertThat(Functions.range(2, 2)).isEqualTo(Arrays.asList()); + assertThat(Functions.range(2, 2000, -5).size()).isEqualTo(Functions.RANGE_LIMIT); + } + + @Test + public void itTruncatesHugeRanges() { + assertThat(Functions.range(2, 200000000).size()).isEqualTo(Functions.RANGE_LIMIT); + } + +} From 997909894f7a5b649802cc2de2cf374497a4d1dc Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 25 Apr 2016 17:33:14 -0400 Subject: [PATCH 0135/2465] doc formatting --- src/main/java/com/hubspot/jinjava/lib/fn/Functions.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java index 8c00ab9de..5b631b5ac 100644 --- a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java @@ -148,8 +148,8 @@ public static int movePointerToJustBeforeLastWord(int pointer, String s) { return pointer + 1; } - @JinjavaDoc(value = "

    Return a list containing an arithmetic progression of integers. " + - " With one parameter, range will return a list from 0 up to or down to the value. " + + @JinjavaDoc(value = "

    Return a list containing an arithmetic progression of integers.

    " + + "

    With one parameter, range will return a list from 0 up to or down to the value. " + " With two parameters, the range will start at the first value and end at the second value. " + " The third parameter specifies the step increment.

    All values can be negative.

    " + "

    Ranges can generate a maximum of " + RANGE_LIMIT + " values.

    ", From f3a75bfb7f6b1a9145a7ab044f5139be5c279d31 Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Tue, 26 Apr 2016 12:27:23 -0400 Subject: [PATCH 0136/2465] set categoryError to null when not in use --- .../java/com/hubspot/jinjava/interpret/TemplateError.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java index 5de6c5700..8d9506e11 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java @@ -1,6 +1,5 @@ package com.hubspot.jinjava.interpret; -import java.util.HashMap; import java.util.Map; import java.util.regex.Pattern; @@ -105,7 +104,7 @@ public TemplateError(ErrorType severity, this.lineno = lineno; this.exception = exception; this.category = BasicTemplateErrorCategory.UNKNOWN; - this.categoryErrors = new HashMap<>(); + this.categoryErrors = null; } public TemplateError(ErrorType severity, @@ -142,7 +141,7 @@ public TemplateError(ErrorType severity, this.lineno = lineno; this.exception = exception; this.category = BasicTemplateErrorCategory.UNKNOWN; - this.categoryErrors = new HashMap<>(); + this.categoryErrors = null; } public ErrorType getSeverity() { From d0a10de3d356128d51fcd9197b42f0b9746e9284 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 26 Apr 2016 13:40:10 -0400 Subject: [PATCH 0137/2465] detect macro cycles --- .../jinjava/el/ext/AstMacroFunction.java | 18 +++ .../hubspot/jinjava/interpret/CallStack.java | 47 ++++++++ .../hubspot/jinjava/interpret/Context.java | 106 +++--------------- .../jinjava/interpret/JinjavaInterpreter.java | 2 +- .../interpret/MacroTagCycleException.java | 11 ++ .../jinjava/interpret/TagCycleException.java | 23 +++- .../hubspot/jinjava/lib/tag/ExtendsTag.java | 2 +- .../hubspot/jinjava/lib/tag/ImportTag.java | 4 +- .../hubspot/jinjava/lib/tag/IncludeTag.java | 6 +- .../com/hubspot/jinjava/lib/tag/MacroTag.java | 7 +- .../hubspot/jinjava/lib/tag/MacroTagTest.java | 15 +++ .../resources/tags/macrotag/recursion.jinja | 5 + .../tags/macrotag/recursion_indirect.jinja | 9 ++ 13 files changed, 153 insertions(+), 102 deletions(-) create mode 100644 src/main/java/com/hubspot/jinjava/interpret/CallStack.java create mode 100644 src/main/java/com/hubspot/jinjava/interpret/MacroTagCycleException.java create mode 100644 src/test/resources/tags/macrotag/recursion.jinja create mode 100644 src/test/resources/tags/macrotag/recursion_indirect.jinja diff --git a/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java b/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java index b55de47c7..468231052 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java @@ -6,6 +6,8 @@ import javax.el.ELException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.interpret.MacroTagCycleException; +import com.hubspot.jinjava.interpret.TemplateError; import com.hubspot.jinjava.lib.fn.MacroFunction; import de.odysseus.el.misc.LocalMessages; @@ -25,12 +27,28 @@ public Object eval(Bindings bindings, ELContext context) { MacroFunction macroFunction = interpreter.getContext().getGlobalMacro(getName()); if (macroFunction != null) { + + try { + interpreter.getContext().getMacroStack().push(getName(), -1); + } catch (MacroTagCycleException e) { + interpreter.addError(new TemplateError(TemplateError.ErrorType.WARNING, + TemplateError.ErrorReason.EXCEPTION, + TemplateError.ErrorItem.TAG, + "Cycle detected for macro '" + getName() + "'", + null, + -1, + e)); + return ""; + } + try { return super.invoke(bindings, context, macroFunction, AbstractCallableMethod.EVAL_METHOD); } catch (IllegalAccessException e) { throw new ELException(LocalMessages.get("error.function.access", getName()), e); } catch (InvocationTargetException e) { throw new ELException(LocalMessages.get("error.function.invocation", getName()), e.getCause()); + } finally { + interpreter.getContext().getMacroStack().pop(); } } diff --git a/src/main/java/com/hubspot/jinjava/interpret/CallStack.java b/src/main/java/com/hubspot/jinjava/interpret/CallStack.java new file mode 100644 index 000000000..a3b713044 --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/interpret/CallStack.java @@ -0,0 +1,47 @@ +package com.hubspot.jinjava.interpret; + +import java.util.Optional; +import java.util.Stack; + +public class CallStack { + + private final CallStack parent; + private final Class exceptionClass; + private final Stack stack = new Stack<>(); + + public CallStack(CallStack parent, Class exceptionClass) { + this.parent = parent; + this.exceptionClass = exceptionClass; + } + + public boolean contains(String path) { + if (stack.contains(path)) { + return true; + } + + if (parent != null) { + return parent.contains(path); + } + + return false; + } + + public void push(String path, int lineNumber) { + if (contains(path)) { + throw TagCycleException.create(exceptionClass, path, Optional.of(lineNumber)); + } + + stack.push(path); + } + + public Optional pop() { + if (stack.isEmpty()) { + if (parent != null) { + return parent.pop(); + } + return Optional.empty(); + } + + return Optional.of(stack.pop()); + } +} diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index 248c69a6b..4611cf00f 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -21,9 +21,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; -import java.util.Stack; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableSet; @@ -46,9 +44,10 @@ public class Context extends ScopeMap { private final SetMultimap dependencies = HashMultimap.create(); - private final Stack extendPathStack = new Stack<>(); - private final Stack importPathStack = new Stack<>(); - private final Stack includePathStack = new Stack<>(); + private final CallStack extendPathStack; + private final CallStack importPathStack; + private final CallStack includePathStack; + private final CallStack macroStack; private final Set resolvedExpressions = new HashSet<>(); private final Set resolvedValues = new HashSet<>(); @@ -76,6 +75,10 @@ public Context(Context parent) { this.filterLibrary = new FilterLibrary(parent == null); this.functionLibrary = new FunctionLibrary(parent == null); this.tagLibrary = new TagLibrary(parent == null); + this.extendPathStack = new CallStack(parent == null ? null : parent.getExtendPathStack(), ExtendsTagCycleException.class); + this.importPathStack = new CallStack(parent == null ? null : parent.getImportPathStack(), ImportTagCycleException.class); + this.includePathStack = new CallStack(parent == null ? null : parent.getIncludePathStack(), IncludeTagCycleException.class); + this.macroStack = new CallStack(parent == null ? null : parent.getMacroStack(), MacroTagCycleException.class); } public Context(Context parent, Map bindings) { @@ -298,97 +301,20 @@ public void registerTag(Tag t) { tagLibrary.addTag(t); } - public void pushExtendPath(String path, int lineNumber) { - if (extendPathStackContains(path)) { - throw new ExtendsTagCycleException(path, lineNumber); - } - - extendPathStack.push(path); + public CallStack getExtendPathStack() { + return extendPathStack; } - public boolean extendPathStackContains(String path) { - if (extendPathStack.contains(path)) { - return true; - } - - if (parent != null) { - return parent.extendPathStackContains(path); - } - - return false; - } - - public Optional popExtendPath() { - if (extendPathStack.isEmpty()) { - if (parent != null) { - return parent.popExtendPath(); - } - return Optional.empty(); - } - - return Optional.of(extendPathStack.pop()); + public CallStack getImportPathStack() { + return importPathStack; } - public void pushImportPath(String path, int lineNumber) { - if (importPathStackContains(path)) { - throw new ImportTagCycleException(path, lineNumber); - } - - importPathStack.push(path); + public CallStack getIncludePathStack() { + return includePathStack; } - public boolean importPathStackContains(String path) { - if (importPathStack.contains(path)) { - return true; - } - - if (parent != null) { - return parent.importPathStackContains(path); - } - - return false; - } - - public Optional popImportPath() { - if (importPathStack.isEmpty()) { - if (parent != null) { - return parent.popImportPath(); - } - return Optional.empty(); - } - - return Optional.of(importPathStack.pop()); - } - - public void pushIncludePath(String path, int lineNumber) { - if (includePathStackContains(path)) { - throw new IncludeTagCycleException(path, lineNumber); - } - - includePathStack.push(path); - } - - public boolean includePathStackContains(String path) { - if (includePathStack.contains(path)) { - return true; - } - - if (parent != null) { - return parent.includePathStackContains(path); - } - - return false; - } - - public Optional popIncludePath() { - if (includePathStack.isEmpty()) { - if (parent != null) { - return parent.popIncludePath(); - } - return Optional.empty(); - } - - return Optional.of(includePathStack.pop()); + public CallStack getMacroStack() { + return macroStack; } public int getRenderDepth() { diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index de9ae19b6..d99844b3f 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -191,7 +191,7 @@ public String render(Node root, boolean processExtendRoots) { output.addNode(node.render(this)); } - context.popExtendPath(); + context.getExtendPathStack().pop(); } } diff --git a/src/main/java/com/hubspot/jinjava/interpret/MacroTagCycleException.java b/src/main/java/com/hubspot/jinjava/interpret/MacroTagCycleException.java new file mode 100644 index 000000000..b817cce7b --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/interpret/MacroTagCycleException.java @@ -0,0 +1,11 @@ +package com.hubspot.jinjava.interpret; + +public class MacroTagCycleException extends TagCycleException { + + private static final long serialVersionUID = -7552850581260771832L; + + public MacroTagCycleException(String path, int lineNumber) { + super("Macro", path, lineNumber); + } + +} diff --git a/src/main/java/com/hubspot/jinjava/interpret/TagCycleException.java b/src/main/java/com/hubspot/jinjava/interpret/TagCycleException.java index 77471507b..843f95c61 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TagCycleException.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TagCycleException.java @@ -1,5 +1,7 @@ package com.hubspot.jinjava.interpret; +import java.util.Optional; + public class TagCycleException extends TemplateStateException { private static final long serialVersionUID = -3058494056577268723L; @@ -7,7 +9,7 @@ public class TagCycleException extends TemplateStateException { private final String tagName; public TagCycleException(String tagName, String path, int lineNumber) { - super(tagName + " tag cycle for path '" + path + "'", lineNumber); + super(tagName + " tag cycle for '" + path + "'", lineNumber); this.path = path; this.tagName = tagName; @@ -21,4 +23,23 @@ public String getTagName() { return tagName; } + public static TagCycleException create(Class clazz, String path, Optional linenumber) { + + final Integer line = linenumber.orElse(-1); + + if (clazz != null) { + if (clazz.equals(ExtendsTagCycleException.class)) { + return new ExtendsTagCycleException(path, line); + } else if (clazz.equals(ImportTagCycleException.class)) { + return new ImportTagCycleException(path, line); + } else if (clazz.equals(IncludeTagCycleException.class)) { + return new IncludeTagCycleException(path, line); + } else if (clazz.equals(MacroTagCycleException.class)) { + return new MacroTagCycleException(path, line); + } + } + + return new TagCycleException("", path, line); + } + } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java index 255cb85b3..f18e05e3b 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java @@ -86,7 +86,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { } String path = interpreter.resolveString(tokenizer.next(), tagNode.getLineNumber()); - interpreter.getContext().pushExtendPath(path, tagNode.getLineNumber()); + interpreter.getContext().getExtendPathStack().push(path, tagNode.getLineNumber()); try { String template = interpreter.getResource(path); diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java index 228de894f..040a0cd25 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java @@ -4,7 +4,6 @@ import java.util.List; import java.util.Map; -import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; import org.apache.commons.lang3.StringUtils; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; @@ -15,6 +14,7 @@ import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.TemplateError; +import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; import com.hubspot.jinjava.interpret.TemplateError.ErrorType; import com.hubspot.jinjava.interpret.TemplateSyntaxException; @@ -75,7 +75,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { String path = StringUtils.trimToEmpty(helper.get(0)); try { - interpreter.getContext().pushImportPath(path, tagNode.getLineNumber()); + interpreter.getContext().getImportPathStack().push(path, tagNode.getLineNumber()); } catch (ImportTagCycleException e) { interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.EXCEPTION, ErrorItem.TAG, "Import cycle detected for path: '" + path + "'", null, tagNode.getLineNumber(), e)); diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java index 43fcb382a..11410c9b7 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java @@ -17,7 +17,6 @@ import java.io.IOException; -import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; import org.apache.commons.lang3.StringUtils; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; @@ -27,6 +26,7 @@ import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.TemplateError; +import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; import com.hubspot.jinjava.interpret.TemplateError.ErrorType; import com.hubspot.jinjava.interpret.TemplateSyntaxException; @@ -58,7 +58,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { String templateFile = interpreter.resolveString(path, tagNode.getLineNumber()); try { - interpreter.getContext().pushIncludePath(templateFile, tagNode.getLineNumber()); + interpreter.getContext().getIncludePathStack().push(templateFile, tagNode.getLineNumber()); } catch (IncludeTagCycleException e) { interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.EXCEPTION, ErrorItem.TAG, "Include cycle detected for path: '" + templateFile + "'", null, tagNode.getLineNumber(), e)); @@ -81,7 +81,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { } catch (IOException e) { throw new InterpretException(e.getMessage(), e, tagNode.getLineNumber()); } finally { - interpreter.getContext().popIncludePath(); + interpreter.getContext().getIncludePathStack().pop(); } } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/MacroTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/MacroTag.java index 49e2b4d72..7a1117d79 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/MacroTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/MacroTag.java @@ -49,6 +49,9 @@ public class MacroTag implements Tag { private static final long serialVersionUID = 8397609322126956077L; + private static final Pattern MACRO_PATTERN = Pattern.compile("([a-zA-Z_][\\w_]*)[^\\(]*\\(([^\\)]*)\\)"); + private static final Splitter ARGS_SPLITTER = Splitter.on(',').omitEmptyStrings().trimResults(); + @Override public String getName() { return "macro"; @@ -103,8 +106,4 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { return ""; } - - private static final Pattern MACRO_PATTERN = Pattern.compile("([a-zA-Z_][\\w_]*)[^\\(]*\\(([^\\)]*)\\)"); - private static final Splitter ARGS_SPLITTER = Splitter.on(',').omitEmptyStrings().trimResults(); - } diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java index 71ee12101..d20f85409 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java @@ -116,6 +116,21 @@ public void testMacroUsedInForLoop() throws Exception { assertThat(tabs.select(".tools__description").get(3).text()).isEqualTo("body4"); } + @Test + public void itPreventsDirectMacroRecursion() throws IOException { + String template = Resources.toString(Resources.getResource("tags/macrotag/recursion.jinja"), StandardCharsets.UTF_8); + interpreter.render(template); + assertThat(interpreter.getErrors().get(0).getMessage()).contains("Cycle detected for macro 'hello'"); + } + + @Test + public void itPreventsIndirectMacroRecursion() throws IOException { + String template = Resources.toString(Resources.getResource("tags/macrotag/recursion_indirect.jinja"), StandardCharsets.UTF_8); + interpreter.render(template); + assertThat(interpreter.getErrors().get(0).getMessage()).contains("Cycle detected for macro 'goodbye'"); + } + + private Node snippet(String jinja) { return new TreeParser(interpreter, jinja).buildTree().getChildren().getFirst(); } diff --git a/src/test/resources/tags/macrotag/recursion.jinja b/src/test/resources/tags/macrotag/recursion.jinja new file mode 100644 index 000000000..8e27ba776 --- /dev/null +++ b/src/test/resources/tags/macrotag/recursion.jinja @@ -0,0 +1,5 @@ +{% macro hello() -%} + Hello {{ hello() }} +{%- endmacro %} + +{{ hello() }} diff --git a/src/test/resources/tags/macrotag/recursion_indirect.jinja b/src/test/resources/tags/macrotag/recursion_indirect.jinja new file mode 100644 index 000000000..b631b83f1 --- /dev/null +++ b/src/test/resources/tags/macrotag/recursion_indirect.jinja @@ -0,0 +1,9 @@ +{% macro hello() -%} + Hello, {{ goodbye() }} +{%- endmacro %} + +{% macro goodbye() -%} + Goodbye, {{ hello() }} +{%- endmacro %} + +{{ goodbye() }} From 415b7e207f0e7174876f1dfb822a03f032883d0c Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Tue, 26 Apr 2016 13:52:52 -0400 Subject: [PATCH 0138/2465] add line number --- .../com/hubspot/jinjava/el/JinjavaInterpreterResolver.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java index 3854d52c1..9b68dc087 100644 --- a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java @@ -200,7 +200,7 @@ private static DateTimeFormatter getFormatter(JinjavaInterpreter interpreter, Fo return StrftimeFormatter.formatter(d.getFormat(), interpreter.getConfig().getLocale()); } catch (IllegalArgumentException e) { interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.SYNTAX_ERROR, ErrorItem.OTHER, e.getMessage(), null, interpreter.getLineNumber(), null, - BasicTemplateErrorCategory.UNKNOWN, ImmutableMap.of("exception", e.getMessage()))); + BasicTemplateErrorCategory.UNKNOWN, ImmutableMap.of("exception", e.getMessage(), "lineNumber", String.valueOf(interpreter.getLineNumber())))); } } @@ -213,7 +213,7 @@ private static Locale getLocale(JinjavaInterpreter interpreter, FormattedDate d) return LocaleUtils.toLocale(d.getLanguage()); } catch (IllegalArgumentException e) { interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.SYNTAX_ERROR, ErrorItem.OTHER, e.getMessage(), null, interpreter.getLineNumber(), null, - BasicTemplateErrorCategory.UNKNOWN, ImmutableMap.of("exception", e.getMessage()))); + BasicTemplateErrorCategory.UNKNOWN, ImmutableMap.of("exception", e.getMessage(), "lineNumber", String.valueOf(interpreter.getLineNumber())))); } } From a4c6cd6d820cc19cb9a0ec6869dc045b9e35b43b Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 26 Apr 2016 14:18:08 -0400 Subject: [PATCH 0139/2465] don't guess direction of range. Negative steps need to be explictly passed --- .../com/hubspot/jinjava/lib/fn/Functions.java | 12 ++++++++++-- .../jinjava/lib/fn/RangeFunctionTest.java | 19 +++++++++++++++---- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java index 5b631b5ac..a5fbeb225 100644 --- a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java @@ -179,7 +179,16 @@ public static List range(Object arg1, Object... args) { step = Integer.parseInt(args[1].toString()); } + if (step == 0) { + return result; + } + if (start < end) { + + if (step < 0) { + return result; + } + for (int i = start; i < end; i += step) { if (result.size() >= RANGE_LIMIT) { break; @@ -188,9 +197,8 @@ public static List range(Object arg1, Object... args) { } } else { - // make sure we're decrementing if (step > 0) { - step = step * -1; + return result; } for (int i = start; i > end; i += step) { diff --git a/src/test/java/com/hubspot/jinjava/lib/fn/RangeFunctionTest.java b/src/test/java/com/hubspot/jinjava/lib/fn/RangeFunctionTest.java index 028d83453..4a33887a4 100644 --- a/src/test/java/com/hubspot/jinjava/lib/fn/RangeFunctionTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/fn/RangeFunctionTest.java @@ -13,22 +13,33 @@ public void itGeneratesSimpleRanges() { assertThat(Functions.range(1)).isEqualTo(Arrays.asList(0)); assertThat(Functions.range(2)).isEqualTo(Arrays.asList(0, 1)); assertThat(Functions.range(2, 4)).isEqualTo(Arrays.asList(2, 3)); + assertThat(Functions.range("2", "4")).isEqualTo(Arrays.asList(2, 3)); assertThat(Functions.range(2, 8, 2)).isEqualTo(Arrays.asList(2, 4, 6)); } @Test public void itGeneratesBackwardsRanges() { - assertThat(Functions.range(-2)).isEqualTo(Arrays.asList(0, -1)); - assertThat(Functions.range(2, -1)).isEqualTo(Arrays.asList(2, 1, 0)); + assertThat(Functions.range(2, -1, -1)).isEqualTo(Arrays.asList(2, 1, 0)); assertThat(Functions.range(8, 2, -2)).isEqualTo(Arrays.asList(8, 6, 4)); + assertThat(Functions.range(2, -1, "-1")).isEqualTo(Arrays.asList(2, 1, 0)); } @Test public void itHandlesBadRanges() { - assertThat(Functions.range(2, 2)).isEqualTo(Arrays.asList()); - assertThat(Functions.range(2, 2000, -5).size()).isEqualTo(Functions.RANGE_LIMIT); + assertThat(Functions.range(-2)).isEmpty(); + assertThat(Functions.range(-2, -4)).isEmpty(); + assertThat(Functions.range(-2, -2)).isEmpty(); + assertThat(Functions.range(2, 2)).isEmpty(); + assertThat(Functions.range(2, 2000, 0)).isEmpty(); + assertThat(Functions.range(2, 2000, -5)).isEmpty(); } + @Test + public void itHandlesBadValues() { + assertThat(Functions.range(2, "f")).isEmpty(); + } + + @Test public void itTruncatesHugeRanges() { assertThat(Functions.range(2, 200000000).size()).isEqualTo(Functions.RANGE_LIMIT); From e1e006d691e6dd909810ce08b9887b1918930c6b Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 26 Apr 2016 16:19:36 -0400 Subject: [PATCH 0140/2465] expect exception --- src/test/java/com/hubspot/jinjava/lib/fn/RangeFunctionTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/hubspot/jinjava/lib/fn/RangeFunctionTest.java b/src/test/java/com/hubspot/jinjava/lib/fn/RangeFunctionTest.java index 4a33887a4..b4d97854f 100644 --- a/src/test/java/com/hubspot/jinjava/lib/fn/RangeFunctionTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/fn/RangeFunctionTest.java @@ -34,7 +34,7 @@ public void itHandlesBadRanges() { assertThat(Functions.range(2, 2000, -5)).isEmpty(); } - @Test + @Test(expected = NumberFormatException.class) public void itHandlesBadValues() { assertThat(Functions.range(2, "f")).isEmpty(); } From 7b528f3529d1e7e5a6c901f3ec6ffd978ac28719 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 26 Apr 2016 16:23:39 -0400 Subject: [PATCH 0141/2465] doc update --- src/main/java/com/hubspot/jinjava/lib/fn/Functions.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java index a5fbeb225..2b10d7149 100644 --- a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java @@ -149,9 +149,9 @@ public static int movePointerToJustBeforeLastWord(int pointer, String s) { } @JinjavaDoc(value = "

    Return a list containing an arithmetic progression of integers.

    " + - "

    With one parameter, range will return a list from 0 up to or down to the value. " + - " With two parameters, the range will start at the first value and end at the second value. " + - " The third parameter specifies the step increment.

    All values can be negative.

    " + + "

    With one parameter, range will return a list from 0 up to (but not including) the value. " + + " With two parameters, the range will start at the first value and increment by 1 up to (but not including) the second value. " + + " The third parameter specifies the step increment.

    All values can be negative. Impossible ranges will return an empty list.

    " + "

    Ranges can generate a maximum of " + RANGE_LIMIT + " values.

    ", params = { @JinjavaParam(value = "start", type = "number", defaultValue = "0"), From a3af9fa44e55e8d7b1c0b8773230ca6e32c79d63 Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Wed, 27 Apr 2016 12:58:57 -0400 Subject: [PATCH 0142/2465] Fix merge --- src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java index 012531c20..265479da3 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java @@ -17,12 +17,10 @@ import java.io.IOException; -<<<<<<< HEAD import com.google.common.collect.ImmutableMap; import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory; -======= ->>>>>>> 87d9fc8e75e792bf32df219bbae5565ab3668e1e + import org.apache.commons.lang3.StringUtils; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; @@ -32,7 +30,6 @@ import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.TemplateError; -import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; import com.hubspot.jinjava.interpret.TemplateError.ErrorType; import com.hubspot.jinjava.interpret.TemplateSyntaxException; From 9074aa66ba4afe1baf7dc18f69354890225ec937 Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Wed, 27 Apr 2016 13:14:01 -0400 Subject: [PATCH 0143/2465] whitespace change --- src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java b/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java index 45cad1410..510259cfe 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java @@ -42,6 +42,7 @@ public Object eval(Bindings bindings, ELContext context) { e, BasicTemplateErrorCategory.CYCLE_DETECTED, ImmutableMap.of("name", getName()))); + return ""; } From 8a75d2495743b361296808008877383ffbef96c2 Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Wed, 27 Apr 2016 15:16:51 -0400 Subject: [PATCH 0144/2465] fix whitespace --- src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java b/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java index 510259cfe..45cad1410 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java @@ -42,7 +42,6 @@ public Object eval(Bindings bindings, ELContext context) { e, BasicTemplateErrorCategory.CYCLE_DETECTED, ImmutableMap.of("name", getName()))); - return ""; } From 1f701531b8e2ec0a6c9f467f41485eda95607615 Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Wed, 27 Apr 2016 16:11:15 -0400 Subject: [PATCH 0145/2465] Remove whitespace --- src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java b/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java index 45cad1410..10bc7c51b 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java @@ -42,6 +42,7 @@ public Object eval(Bindings bindings, ELContext context) { e, BasicTemplateErrorCategory.CYCLE_DETECTED, ImmutableMap.of("name", getName()))); + return ""; } From b9784a35f33dcc193a8b3b5052d8187ca9accb26 Mon Sep 17 00:00:00 2001 From: Jonathan Haber Date: Wed, 27 Apr 2016 16:32:46 -0400 Subject: [PATCH 0146/2465] Upgrade to checkstyle 2.17 --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 370a71415..e7f39c0e5 100644 --- a/pom.xml +++ b/pom.xml @@ -31,6 +31,7 @@ 1.8 false + 2.17 From ef89280d2388d01ddbe55a8b3c459a79e1dcfc8d Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Thu, 28 Apr 2016 11:03:24 -0400 Subject: [PATCH 0147/2465] Update CHANGES.md --- CHANGES.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 2d82f079c..dd345a856 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,17 @@ # Jinjava Releases # +### Version 2.1.8 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.8%22)) ### + +* Update ListFilter to work with strings +* Add additional category information and error message tokens to TemplateError +* Add RangeFunction +* Update detecting for recursive macros +* Update checkstyle to 2.17 + +* ### Version 2.1.7 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.7%22)) ### + +* Updated RawTag to not evaluate tags nested within it + ### Version 2.1.6 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.6%22)) ### * Added new bool filter to return boolean value from string From b567edda2c90c6ae33d7054ab4440419ac413b85 Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Thu, 28 Apr 2016 11:03:41 -0400 Subject: [PATCH 0148/2465] Update CHANGES.md --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index dd345a856..0f354d45a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,7 +8,7 @@ * Update detecting for recursive macros * Update checkstyle to 2.17 -* ### Version 2.1.7 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.7%22)) ### +### Version 2.1.7 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.7%22)) ### * Updated RawTag to not evaluate tags nested within it From 0ebf8812c1b0d4096ba2e2c25d4e705ef5001ab6 Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Thu, 28 Apr 2016 11:03:56 -0400 Subject: [PATCH 0149/2465] Update CHANGES.md --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 0f354d45a..dd9552bb5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,9 +2,9 @@ ### Version 2.1.8 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.8%22)) ### -* Update ListFilter to work with strings * Add additional category information and error message tokens to TemplateError * Add RangeFunction +* Update ListFilter to work with strings * Update detecting for recursive macros * Update checkstyle to 2.17 From fe7bd755a12473071d7a9a8369d05a2b5227cc05 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Thu, 28 Apr 2016 15:09:11 +0000 Subject: [PATCH 0150/2465] [maven-release-plugin] prepare release jinjava-2.1.8 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e7f39c0e5..fdd816de9 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.8-SNAPSHOT + 2.1.8 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.1.8 From 3ae7a81eb5a708ea82c8878d8d59e4470889225e Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Thu, 28 Apr 2016 15:09:11 +0000 Subject: [PATCH 0151/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index fdd816de9..c72c45d86 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.8 + 2.1.9-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.1.8 + HEAD From fcdfb8fd52b15373d01fbc93a920d43581ef59b8 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Thu, 28 Apr 2016 11:13:45 -0400 Subject: [PATCH 0152/2465] reword changes --- CHANGES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index dd9552bb5..743fe3e99 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,9 +3,9 @@ ### Version 2.1.8 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.8%22)) ### * Add additional category information and error message tokens to TemplateError -* Add RangeFunction +* Add range function * Update ListFilter to work with strings -* Update detecting for recursive macros +* Macros can create cycles * Update checkstyle to 2.17 ### Version 2.1.7 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.7%22)) ### From 6ba860ffa3343a13fab35cc6e620fe50d3816ae7 Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Thu, 5 May 2016 10:50:25 -0400 Subject: [PATCH 0153/2465] Add tracking dependencies for ImportTag --- src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java index 8e639a38b..8879dfd8a 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java @@ -88,6 +88,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { } String templateFile = interpreter.resolveString(path, tagNode.getLineNumber()); + interpreter.getContext().addDependency("coded_files", templateFile); try { String template = interpreter.getResource(templateFile); Node node = interpreter.parse(template); From 9919f3f0409a4b2661d0ed4e19bfae2b645825f1 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 9 May 2016 17:10:23 -0400 Subject: [PATCH 0154/2465] use numberutils for sane defaults --- .../java/com/hubspot/jinjava/lib/fn/Functions.java | 13 +++++++------ .../hubspot/jinjava/lib/fn/RangeFunctionTest.java | 1 - 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java index 2b10d7149..26a67af40 100644 --- a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java @@ -12,6 +12,7 @@ import java.util.Objects; import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.math.NumberUtils; import com.google.common.collect.Lists; import com.hubspot.jinjava.JinjavaConfig; @@ -167,16 +168,16 @@ public static List range(Object arg1, Object... args) { switch (args.length) { case 0: - end = Integer.parseInt(arg1.toString()); + end = NumberUtils.toInt(arg1.toString()); break; case 1: - start = Integer.parseInt(arg1.toString()); - end = Integer.parseInt(args[0].toString()); + start = NumberUtils.toInt(arg1.toString()); + end = NumberUtils.toInt(args[0].toString()); break; default: - start = Integer.parseInt(arg1.toString()); - end = Integer.parseInt(args[0].toString()); - step = Integer.parseInt(args[1].toString()); + start = NumberUtils.toInt(arg1.toString()); + end = NumberUtils.toInt(args[0].toString()); + step = NumberUtils.toInt(args[1].toString(), 1); } if (step == 0) { diff --git a/src/test/java/com/hubspot/jinjava/lib/fn/RangeFunctionTest.java b/src/test/java/com/hubspot/jinjava/lib/fn/RangeFunctionTest.java index b4d97854f..d3707d664 100644 --- a/src/test/java/com/hubspot/jinjava/lib/fn/RangeFunctionTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/fn/RangeFunctionTest.java @@ -34,7 +34,6 @@ public void itHandlesBadRanges() { assertThat(Functions.range(2, 2000, -5)).isEmpty(); } - @Test(expected = NumberFormatException.class) public void itHandlesBadValues() { assertThat(Functions.range(2, "f")).isEmpty(); } From 9d20ca4cceaec2407e94e3ca41bc0c2391d92543 Mon Sep 17 00:00:00 2001 From: Andrei Evseev Date: Thu, 12 May 2016 16:02:17 +0400 Subject: [PATCH 0155/2465] String range implemented according to #4. Now {{ theString[1:4] }} will render as a substring. --- .../jinjava/el/ext/AstRangeBracket.java | 44 +++++++--- .../jinjava/el/ext/RangeStringTest.java | 84 +++++++++++++++++++ .../jinjava/tree/ExpressionNodeTest.java | 21 +++-- .../resources/varblocks/string-range.html | 1 + 4 files changed, 130 insertions(+), 20 deletions(-) create mode 100644 src/test/java/com/hubspot/jinjava/el/ext/RangeStringTest.java create mode 100644 src/test/resources/varblocks/string-range.html diff --git a/src/main/java/com/hubspot/jinjava/el/ext/AstRangeBracket.java b/src/main/java/com/hubspot/jinjava/el/ext/AstRangeBracket.java index 228a2e7a0..ae684b987 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/AstRangeBracket.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/AstRangeBracket.java @@ -1,21 +1,19 @@ package com.hubspot.jinjava.el.ext; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Iterator; - -import javax.el.ELContext; -import javax.el.ELException; -import javax.el.PropertyNotFoundException; - import com.hubspot.jinjava.objects.collections.PyList; - import de.odysseus.el.misc.LocalMessages; import de.odysseus.el.tree.Bindings; import de.odysseus.el.tree.impl.ast.AstBracket; import de.odysseus.el.tree.impl.ast.AstNode; +import javax.el.ELContext; +import javax.el.ELException; +import javax.el.PropertyNotFoundException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; + public class AstRangeBracket extends AstBracket { protected final AstNode rangeMax; @@ -31,7 +29,8 @@ public Object eval(Bindings bindings, ELContext context) { if (base == null) { throw new PropertyNotFoundException(LocalMessages.get("error.property.base.null", prefix)); } - if (!Iterable.class.isAssignableFrom(base.getClass()) && !base.getClass().isArray()) { + boolean baseIsString = base.getClass().equals(String.class); + if (!Iterable.class.isAssignableFrom(base.getClass()) && !base.getClass().isArray() && !baseIsString) { throw new ELException("Property " + prefix + " is not a sequence."); } @@ -51,6 +50,14 @@ public Object eval(Bindings bindings, ELContext context) { throw new ELException("Range end is not a number"); } + int startNum = ((Number) start).intValue(); + int endNum = ((Number) end).intValue(); + + // https://github.com/HubSpot/jinjava/issues/52 + if (baseIsString) { + return evalString((String) base, startNum, endNum); + } + Iterable baseItr; if (base.getClass().isArray()) { @@ -61,8 +68,6 @@ public Object eval(Bindings bindings, ELContext context) { } PyList result = new PyList(new ArrayList<>()); - int startNum = ((Number) start).intValue(); - int endNum = ((Number) end).intValue(); int index = 0; Iterator baseIterator = baseItr.iterator(); @@ -81,6 +86,19 @@ public Object eval(Bindings bindings, ELContext context) { return result; } + private String evalString(String base, int startNum, int endNum) { + if (startNum > endNum) { + return ""; + } + if (startNum <= 0) { + startNum = 0; + } + if (endNum > base.length()) { + endNum = base.length(); + } + return base.substring(startNum, endNum); + } + @Override public String toString() { return "[:]"; diff --git a/src/test/java/com/hubspot/jinjava/el/ext/RangeStringTest.java b/src/test/java/com/hubspot/jinjava/el/ext/RangeStringTest.java new file mode 100644 index 000000000..6585e86f2 --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/el/ext/RangeStringTest.java @@ -0,0 +1,84 @@ +/** + * 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.hubspot.jinjava.el.ext; + +import com.hubspot.jinjava.Jinjava; +import org.junit.Before; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Created by anev on 11/05/16. + */ +public class RangeStringTest { + + Jinjava jinjava; + + @Before + public void setup() { + jinjava = new Jinjava(); + } + + @Test + public void testStringRangeSimple() { + Map context = new HashMap<>(); + context.put("theString", "theSimpleString"); + + assertThat(jinjava.render("{{ theString[0:4] }}", context)).isEqualTo("theS"); + } + + @Test + public void testStringRangeOutOfRange() { + Map context = new HashMap<>(); + context.put("theString", "theSimpleString"); + + assertThat(jinjava.render("{{ theString[0:400] }}", context)).isEqualTo("theSimpleString"); + } + + @Test + public void testStringRangeOutNegative() { + Map context = new HashMap<>(); + context.put("theString", "theSimpleString"); + + assertThat(jinjava.render("{{ theString[-4:4] }}", context)).isEqualTo("theS"); + } + + @Test + public void testStringRangeInvalidRange() { + Map context = new HashMap<>(); + context.put("theString", "theSimpleString"); + + assertThat(jinjava.render("{{ theString[4:2] }}", context)).isEmpty(); + } + + @Test + public void testStringRangeMultiLine() { + Map context = new HashMap<>(); + context.put("theString", "multi\nline\nstring"); + + assertThat(jinjava.render("{{ theString[3:8] }}", context)).isEqualTo("ti\nli"); + } + + @Test + public void testStringRangeCyrillic() { + Map context = new HashMap<>(); + context.put("theString", "Строка с non ascii символами"); + + assertThat(jinjava.render("{{ theString[1:4] }}", context)).isEqualTo("тро"); + } +} diff --git a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java index c1d11f9e0..d42f5c7c4 100644 --- a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java @@ -1,17 +1,16 @@ package com.hubspot.jinjava.tree; -import static org.assertj.core.api.Assertions.assertThat; - -import java.nio.charset.StandardCharsets; - -import org.junit.Before; -import org.junit.Test; - import com.google.common.base.Throwables; import com.google.common.io.Resources; import com.hubspot.jinjava.Jinjava; import com.hubspot.jinjava.interpret.Context; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import org.junit.Before; +import org.junit.Test; + +import java.nio.charset.StandardCharsets; + +import static org.assertj.core.api.Assertions.assertThat; public class ExpressionNodeTest { @@ -42,6 +41,14 @@ public void itAvoidsInfiniteRecursionWhenVarsContainBraceBlocks() throws Excepti assertThat(node.render(interpreter).toString()).isEqualTo("hello {{ place }}"); } + @Test + public void itRendersStringRange() throws Exception { + context.put("theString", "1234567890"); + + ExpressionNode node = fixture("string-range"); + assertThat(node.render(interpreter).toString()).isEqualTo("345"); + } + @Test public void valueExprWithOr() throws Exception { context.put("a", "foo"); diff --git a/src/test/resources/varblocks/string-range.html b/src/test/resources/varblocks/string-range.html new file mode 100644 index 000000000..cff8d517a --- /dev/null +++ b/src/test/resources/varblocks/string-range.html @@ -0,0 +1 @@ +{{ theString[2:5] }} \ No newline at end of file From c5a8ed51e292394d5d1529f2d8d18ae99b237d46 Mon Sep 17 00:00:00 2001 From: Andrei Evseev Date: Fri, 13 May 2016 16:45:13 +0400 Subject: [PATCH 0156/2465] ADDED: - support negative and unspecified indexes such as [-7:-3] and [3:] for compatibility with python and Jinja. FIXED: - import order in changed classes. --- .../jinjava/el/ext/AstRangeBracket.java | 56 +++++++++++------- .../jinjava/el/ext/ExtendedParser.java | 36 +++--------- .../jinjava/el/ext/RangeStringTest.java | 57 ++++++++++--------- .../jinjava/tree/ExpressionNodeTest.java | 13 +++-- 4 files changed, 81 insertions(+), 81 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/AstRangeBracket.java b/src/main/java/com/hubspot/jinjava/el/ext/AstRangeBracket.java index ae684b987..dfdb05ca4 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/AstRangeBracket.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/AstRangeBracket.java @@ -1,19 +1,21 @@ package com.hubspot.jinjava.el.ext; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; + +import javax.el.ELContext; +import javax.el.ELException; +import javax.el.PropertyNotFoundException; + import com.hubspot.jinjava.objects.collections.PyList; + import de.odysseus.el.misc.LocalMessages; import de.odysseus.el.tree.Bindings; import de.odysseus.el.tree.impl.ast.AstBracket; import de.odysseus.el.tree.impl.ast.AstNode; -import javax.el.ELContext; -import javax.el.ELException; -import javax.el.PropertyNotFoundException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Iterator; - public class AstRangeBracket extends AstBracket { protected final AstNode rangeMax; @@ -34,6 +36,11 @@ public Object eval(Bindings bindings, ELContext context) { throw new ELException("Property " + prefix + " is not a sequence."); } + // https://github.com/HubSpot/jinjava/issues/52 + if (baseIsString) { + return evalString((String) base, bindings, context); + } + Object start = property.eval(bindings, context); if (start == null && strict) { return Collections.emptyList(); @@ -53,11 +60,6 @@ public Object eval(Bindings bindings, ELContext context) { int startNum = ((Number) start).intValue(); int endNum = ((Number) end).intValue(); - // https://github.com/HubSpot/jinjava/issues/52 - if (baseIsString) { - return evalString((String) base, startNum, endNum); - } - Iterable baseItr; if (base.getClass().isArray()) { @@ -86,19 +88,33 @@ public Object eval(Bindings bindings, ELContext context) { return result; } - private String evalString(String base, int startNum, int endNum) { + private String evalString(String base, Bindings bindings, ELContext context) { + + int startNum = intVal(property, 0, base.length(), bindings, context); + int endNum = intVal(rangeMax, base.length(), base.length(), bindings, context); + endNum = Math.min(endNum, base.length()); + if (startNum > endNum) { return ""; } - if (startNum <= 0) { - startNum = 0; - } - if (endNum > base.length()) { - endNum = base.length(); - } return base.substring(startNum, endNum); } + private int intVal(AstNode node, int defVal, int baseLength, Bindings bindings, ELContext context) { + if (node == null) { + return defVal; + } + Object val = node.eval(bindings, context); + if (val == null) { + return defVal; + } + if (!(val instanceof Number)) { + throw new ELException("Range start/end is not a number"); + } + int result = ((Number) val).intValue(); + return result >= 0 ? result : baseLength + result; + } + @Override public String toString() { return "[:]"; diff --git a/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java b/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java index bcec87b7e..f9f1449d0 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java @@ -1,21 +1,6 @@ package com.hubspot.jinjava.el.ext; -import static de.odysseus.el.tree.impl.Builder.Feature.METHOD_INVOCATIONS; -import static de.odysseus.el.tree.impl.Builder.Feature.NULL_PROPERTIES; -import static de.odysseus.el.tree.impl.Scanner.Symbol.COLON; -import static de.odysseus.el.tree.impl.Scanner.Symbol.COMMA; -import static de.odysseus.el.tree.impl.Scanner.Symbol.IDENTIFIER; -import static de.odysseus.el.tree.impl.Scanner.Symbol.LBRACK; -import static de.odysseus.el.tree.impl.Scanner.Symbol.LPAREN; -import static de.odysseus.el.tree.impl.Scanner.Symbol.QUESTION; -import static de.odysseus.el.tree.impl.Scanner.Symbol.RBRACK; -import static de.odysseus.el.tree.impl.Scanner.Symbol.RPAREN; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import javax.el.ELException; @@ -28,15 +13,11 @@ import de.odysseus.el.tree.impl.Scanner.ScanException; import de.odysseus.el.tree.impl.Scanner.Symbol; import de.odysseus.el.tree.impl.Scanner.Token; -import de.odysseus.el.tree.impl.ast.AstBinary; -import de.odysseus.el.tree.impl.ast.AstBracket; -import de.odysseus.el.tree.impl.ast.AstDot; -import de.odysseus.el.tree.impl.ast.AstFunction; -import de.odysseus.el.tree.impl.ast.AstNested; -import de.odysseus.el.tree.impl.ast.AstNode; -import de.odysseus.el.tree.impl.ast.AstNull; -import de.odysseus.el.tree.impl.ast.AstParameters; -import de.odysseus.el.tree.impl.ast.AstProperty; +import de.odysseus.el.tree.impl.ast.*; + +import static de.odysseus.el.tree.impl.Builder.Feature.METHOD_INVOCATIONS; +import static de.odysseus.el.tree.impl.Builder.Feature.NULL_PROPERTIES; +import static de.odysseus.el.tree.impl.Scanner.Symbol.*; public class ExtendedParser extends Parser { @@ -264,6 +245,7 @@ protected AstNode literal() throws ScanException, ParseException { switch (getToken().getSymbol()) { case LBRACK: v = new AstList(params(LBRACK, RBRACK)); + break; case LPAREN: v = new AstTuple(params()); @@ -315,13 +297,13 @@ protected AstNode value() throws ScanException, ParseException { break; case LBRACK: consumeToken(); - AstNode property = expr(true); + AstNode property = expr(false); boolean strict = !context.isEnabled(NULL_PROPERTIES); Token nextToken = consumeToken(); if (nextToken.getSymbol() == COLON) { - AstNode rangeMax = expr(true); + AstNode rangeMax = expr(false); consumeToken(RBRACK); v = createAstRangeBracket(v, property, rangeMax, lvalue, strict); } else if (nextToken.getSymbol() == RBRACK) { diff --git a/src/test/java/com/hubspot/jinjava/el/ext/RangeStringTest.java b/src/test/java/com/hubspot/jinjava/el/ext/RangeStringTest.java index 6585e86f2..7bdc421a9 100644 --- a/src/test/java/com/hubspot/jinjava/el/ext/RangeStringTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ext/RangeStringTest.java @@ -2,9 +2,9 @@ * 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. @@ -13,13 +13,13 @@ */ package com.hubspot.jinjava.el.ext; +import java.util.Map; + +import com.google.common.collect.ImmutableMap; import com.hubspot.jinjava.Jinjava; import org.junit.Before; import org.junit.Test; -import java.util.HashMap; -import java.util.Map; - import static org.assertj.core.api.Assertions.assertThat; /** @@ -27,58 +27,59 @@ */ public class RangeStringTest { - Jinjava jinjava; + private Jinjava jinjava; + private Map context; @Before public void setup() { jinjava = new Jinjava(); + context = ImmutableMap.of("theString", "theSimpleString"); } @Test public void testStringRangeSimple() { - Map context = new HashMap<>(); - context.put("theString", "theSimpleString"); - assertThat(jinjava.render("{{ theString[0:4] }}", context)).isEqualTo("theS"); } @Test public void testStringRangeOutOfRange() { - Map context = new HashMap<>(); - context.put("theString", "theSimpleString"); - assertThat(jinjava.render("{{ theString[0:400] }}", context)).isEqualTo("theSimpleString"); } @Test - public void testStringRangeOutNegative() { - Map context = new HashMap<>(); - context.put("theString", "theSimpleString"); + public void testStringRangeInvalidRange() { + assertThat(jinjava.render("{{ theString[4:2] }}", context)).isEmpty(); + } - assertThat(jinjava.render("{{ theString[-4:4] }}", context)).isEqualTo("theS"); + @Test + public void testStringRangeNegative() { + assertThat(jinjava.render("{{ theString[-7:-4] }}", context)).isEqualTo("eSt"); } @Test - public void testStringRangeInvalidRange() { - Map context = new HashMap<>(); - context.put("theString", "theSimpleString"); + public void testStringRangeRightOnly() { + assertThat(jinjava.render("{{ theString[3:] }}", context)).isEqualTo("SimpleString"); + } - assertThat(jinjava.render("{{ theString[4:2] }}", context)).isEmpty(); + @Test + public void testStringRangeLeftOnly() { + assertThat(jinjava.render("{{ theString[:3] }}", context)).isEqualTo("the"); } @Test - public void testStringRangeMultiLine() { - Map context = new HashMap<>(); - context.put("theString", "multi\nline\nstring"); + public void testStringRangeNoRange() { + assertThat(jinjava.render("{{ theString[:] }}", context)).isEqualTo("theSimpleString"); + } - assertThat(jinjava.render("{{ theString[3:8] }}", context)).isEqualTo("ti\nli"); + @Test + public void testStringRangeMultiLine() { + Map localContext = ImmutableMap.of("theString", "multi\nline\nstring"); + assertThat(jinjava.render("{{ theString[3:8] }}", localContext)).isEqualTo("ti\nli"); } @Test public void testStringRangeCyrillic() { - Map context = new HashMap<>(); - context.put("theString", "Строка с non ascii символами"); - - assertThat(jinjava.render("{{ theString[1:4] }}", context)).isEqualTo("тро"); + Map localContext = ImmutableMap.of("theString", "Строка с non ascii символами"); + assertThat(jinjava.render("{{ theString[1:4] }}", localContext)).isEqualTo("тро"); } } diff --git a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java index d42f5c7c4..f64c81684 100644 --- a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java @@ -1,16 +1,17 @@ package com.hubspot.jinjava.tree; +import static org.assertj.core.api.Assertions.assertThat; + +import java.nio.charset.StandardCharsets; + +import org.junit.Before; +import org.junit.Test; + import com.google.common.base.Throwables; import com.google.common.io.Resources; import com.hubspot.jinjava.Jinjava; import com.hubspot.jinjava.interpret.Context; import com.hubspot.jinjava.interpret.JinjavaInterpreter; -import org.junit.Before; -import org.junit.Test; - -import java.nio.charset.StandardCharsets; - -import static org.assertj.core.api.Assertions.assertThat; public class ExpressionNodeTest { From ee60fd088a553dc27e48e5068e33f41f6315eb97 Mon Sep 17 00:00:00 2001 From: Andrei Evseev Date: Fri, 13 May 2016 16:50:33 +0400 Subject: [PATCH 0157/2465] FIXED: - import order --- .../jinjava/el/ext/ExtendedParser.java | 24 ++++++++++++++----- .../jinjava/el/ext/RangeStringTest.java | 4 ++-- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java b/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java index f9f1449d0..f3da857a1 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java @@ -1,6 +1,14 @@ package com.hubspot.jinjava.el.ext; -import java.util.*; +import static de.odysseus.el.tree.impl.Builder.Feature.METHOD_INVOCATIONS; +import static de.odysseus.el.tree.impl.Builder.Feature.NULL_PROPERTIES; +import static de.odysseus.el.tree.impl.Scanner.Symbol.*; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; import javax.el.ELException; @@ -13,11 +21,15 @@ import de.odysseus.el.tree.impl.Scanner.ScanException; import de.odysseus.el.tree.impl.Scanner.Symbol; import de.odysseus.el.tree.impl.Scanner.Token; -import de.odysseus.el.tree.impl.ast.*; - -import static de.odysseus.el.tree.impl.Builder.Feature.METHOD_INVOCATIONS; -import static de.odysseus.el.tree.impl.Builder.Feature.NULL_PROPERTIES; -import static de.odysseus.el.tree.impl.Scanner.Symbol.*; +import de.odysseus.el.tree.impl.ast.AstBinary; +import de.odysseus.el.tree.impl.ast.AstBracket; +import de.odysseus.el.tree.impl.ast.AstDot; +import de.odysseus.el.tree.impl.ast.AstFunction; +import de.odysseus.el.tree.impl.ast.AstNested; +import de.odysseus.el.tree.impl.ast.AstNode; +import de.odysseus.el.tree.impl.ast.AstNull; +import de.odysseus.el.tree.impl.ast.AstParameters; +import de.odysseus.el.tree.impl.ast.AstProperty; public class ExtendedParser extends Parser { diff --git a/src/test/java/com/hubspot/jinjava/el/ext/RangeStringTest.java b/src/test/java/com/hubspot/jinjava/el/ext/RangeStringTest.java index 7bdc421a9..76e493bd8 100644 --- a/src/test/java/com/hubspot/jinjava/el/ext/RangeStringTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ext/RangeStringTest.java @@ -13,6 +13,8 @@ */ package com.hubspot.jinjava.el.ext; +import static org.assertj.core.api.Assertions.assertThat; + import java.util.Map; import com.google.common.collect.ImmutableMap; @@ -20,8 +22,6 @@ import org.junit.Before; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; - /** * Created by anev on 11/05/16. */ From ee70b20b35b225b15f61d5e1f9a96ab91cd9e930 Mon Sep 17 00:00:00 2001 From: Andrei Evseev Date: Fri, 13 May 2016 16:58:37 +0400 Subject: [PATCH 0158/2465] FIXED: - avoid star import rule violation --- .../java/com/hubspot/jinjava/el/ext/ExtendedParser.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java b/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java index f3da857a1..1c803a4a6 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java @@ -2,7 +2,14 @@ import static de.odysseus.el.tree.impl.Builder.Feature.METHOD_INVOCATIONS; import static de.odysseus.el.tree.impl.Builder.Feature.NULL_PROPERTIES; -import static de.odysseus.el.tree.impl.Scanner.Symbol.*; +import static de.odysseus.el.tree.impl.Scanner.Symbol.COLON; +import static de.odysseus.el.tree.impl.Scanner.Symbol.COMMA; +import static de.odysseus.el.tree.impl.Scanner.Symbol.IDENTIFIER; +import static de.odysseus.el.tree.impl.Scanner.Symbol.LBRACK; +import static de.odysseus.el.tree.impl.Scanner.Symbol.LPAREN; +import static de.odysseus.el.tree.impl.Scanner.Symbol.QUESTION; +import static de.odysseus.el.tree.impl.Scanner.Symbol.RBRACK; +import static de.odysseus.el.tree.impl.Scanner.Symbol.RPAREN; import java.util.ArrayList; import java.util.Collections; From 3e5b3c973e7e8350ebb20b8bad8e254205c2af8d Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Thu, 19 May 2016 11:46:48 -0400 Subject: [PATCH 0159/2465] macro test --- .../hubspot/jinjava/lib/tag/MacroTagTest.java | 7 +++++ .../tags/macrotag/macros-calling-macros.jinja | 26 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 src/test/resources/tags/macrotag/macros-calling-macros.jinja diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java index d20f85409..457e21249 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java @@ -130,6 +130,13 @@ public void itPreventsIndirectMacroRecursion() throws IOException { assertThat(interpreter.getErrors().get(0).getMessage()).contains("Cycle detected for macro 'goodbye'"); } + @Test + public void itAllowsMacrosCallingMacrosUsingCall() throws IOException { + String template = Resources.toString(Resources.getResource("tags/macrotag/macros-calling-macros.jinja"), StandardCharsets.UTF_8); + String out = interpreter.render(template); + assertThat(out).contains("Hello World One"); + assertThat(out).contains("Hello World Two"); + } private Node snippet(String jinja) { return new TreeParser(interpreter, jinja).buildTree().getChildren().getFirst(); diff --git a/src/test/resources/tags/macrotag/macros-calling-macros.jinja b/src/test/resources/tags/macrotag/macros-calling-macros.jinja new file mode 100644 index 000000000..e1e7fe288 --- /dev/null +++ b/src/test/resources/tags/macrotag/macros-calling-macros.jinja @@ -0,0 +1,26 @@ +{% macro render_dialog_one(title, class) %} +

    +

    {{ title }}

    +
    + {{ caller() }} +
    +
    +{% endmacro %} + +{% macro render_dialog_two(title, class) %} +
    +

    {{ title }}

    +
    + {{ caller() }} +
    +
    +{% endmacro %} + +{% call render_dialog_one('Hello World One', 'greeting 1') %} +

    1) This is render_dialog_one

    + + {% call render_dialog_two('Hello World Two', 'greeting 2') %} +

    2) This is render_dialog_two

    + {% endcall %} + +{% endcall %} From 3be3f4e0d52f68bcafacd67f52c5169c97114b6e Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Thu, 19 May 2016 12:02:01 -0400 Subject: [PATCH 0160/2465] exclude caller from macro stack --- .../jinjava/el/ext/AstMacroFunction.java | 30 ++++++++++--------- .../hubspot/jinjava/lib/tag/MacroTagTest.java | 1 + 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java b/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java index 10bc7c51b..ff8816077 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java @@ -30,20 +30,22 @@ public Object eval(Bindings bindings, ELContext context) { MacroFunction macroFunction = interpreter.getContext().getGlobalMacro(getName()); if (macroFunction != null) { - try { - interpreter.getContext().getMacroStack().push(getName(), -1); - } catch (MacroTagCycleException e) { - interpreter.addError(new TemplateError(TemplateError.ErrorType.WARNING, - TemplateError.ErrorReason.EXCEPTION, - TemplateError.ErrorItem.TAG, - "Cycle detected for macro '" + getName() + "'", - null, - -1, - e, - BasicTemplateErrorCategory.CYCLE_DETECTED, - ImmutableMap.of("name", getName()))); - - return ""; + if (!macroFunction.isCaller()) { + try { + interpreter.getContext().getMacroStack().push(getName(), -1); + } catch (MacroTagCycleException e) { + interpreter.addError(new TemplateError(TemplateError.ErrorType.WARNING, + TemplateError.ErrorReason.EXCEPTION, + TemplateError.ErrorItem.TAG, + "Cycle detected for macro '" + getName() + "'", + null, + -1, + e, + BasicTemplateErrorCategory.CYCLE_DETECTED, + ImmutableMap.of("name", getName()))); + + return ""; + } } try { diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java index 457e21249..6b117a7ec 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java @@ -134,6 +134,7 @@ public void itPreventsIndirectMacroRecursion() throws IOException { public void itAllowsMacrosCallingMacrosUsingCall() throws IOException { String template = Resources.toString(Resources.getResource("tags/macrotag/macros-calling-macros.jinja"), StandardCharsets.UTF_8); String out = interpreter.render(template); + assertThat(interpreter.getErrors()).isEmpty(); assertThat(out).contains("Hello World One"); assertThat(out).contains("Hello World Two"); } From 88c0271f0c9d76b551f3076fac17cc89039c4a3e Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Thu, 19 May 2016 12:06:35 -0400 Subject: [PATCH 0161/2465] fix macro docs --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 743fe3e99..366cbc1ef 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,7 +5,7 @@ * Add additional category information and error message tokens to TemplateError * Add range function * Update ListFilter to work with strings -* Macros can create cycles +* Do not allow macros to called recursively * Update checkstyle to 2.17 ### Version 2.1.7 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.7%22)) ### From 3db5a875236f5135eacb14fd43f357d3ffa7fc9c Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Thu, 19 May 2016 13:55:35 -0400 Subject: [PATCH 0162/2465] 2.1.9 notes --- CHANGES.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 366cbc1ef..7f88ae9a4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,11 +1,15 @@ # Jinjava Releases # +### Version 2.1.9 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.9%22)) ### + +* Exclude 'caller' from recursive macro check + ### Version 2.1.8 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.8%22)) ### * Add additional category information and error message tokens to TemplateError * Add range function * Update ListFilter to work with strings -* Do not allow macros to called recursively +* Do not allow macros to be called recursively * Update checkstyle to 2.17 ### Version 2.1.7 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.7%22)) ### From 4270cbb85c7ec5699e052872be8f267fdc11dbdf Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Thu, 19 May 2016 18:04:49 +0000 Subject: [PATCH 0163/2465] [maven-release-plugin] prepare release jinjava-2.1.9 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c72c45d86..06b759a52 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.9-SNAPSHOT + 2.1.9 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.1.9 From 9364df30c1bd1405c31b5cb68b972e8a2243132a Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Thu, 19 May 2016 18:04:49 +0000 Subject: [PATCH 0164/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 06b759a52..304436615 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.9 + 2.1.10-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.1.9 + HEAD From 2cfdd10be7e9b741b240d134edf9ad2e089619ef Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Thu, 19 May 2016 15:56:22 -0400 Subject: [PATCH 0165/2465] set up for 2.1.10 --- CHANGES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 7f88ae9a4..eb772a646 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,8 @@ # Jinjava Releases # +### Version 2.1.10 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.10%22)) ### + + ### Version 2.1.9 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.9%22)) ### * Exclude 'caller' from recursive macro check From 3897b207511ff7016deaa3a54bd94df405de91a3 Mon Sep 17 00:00:00 2001 From: Kevin Baker Date: Wed, 1 Jun 2016 19:26:36 +0100 Subject: [PATCH 0166/2465] Add escapejs filter --- .../jinjava/lib/filter/EscapeJsFilter.java | 108 ++++++++++++++++++ .../jinjava/lib/filter/FilterLibrary.java | 1 + .../lib/filter/EscapeJsFilterTest.java | 48 ++++++++ 3 files changed, 157 insertions(+) create mode 100644 src/main/java/com/hubspot/jinjava/lib/filter/EscapeJsFilter.java create mode 100644 src/test/java/com/hubspot/jinjava/lib/filter/EscapeJsFilterTest.java diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/EscapeJsFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/EscapeJsFilter.java new file mode 100644 index 000000000..d1bad4778 --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/lib/filter/EscapeJsFilter.java @@ -0,0 +1,108 @@ +/********************************************************************** + Copyright (c) 2014 HubSpot 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.hubspot.jinjava.lib.filter; + +import java.util.Locale; +import java.util.Objects; + +import com.hubspot.jinjava.doc.annotations.JinjavaDoc; +import com.hubspot.jinjava.doc.annotations.JinjavaParam; +import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; + +@JinjavaDoc( + value = "Escapes strings so that they can be safely inserted into a JavaScript variable declaration", + params = { + @JinjavaParam(value = "s", desc = "String to escape") + }, + snippets = { + @JinjavaSnippet( + code = "{% set escape_string = \"This string can safely be inserted into JavaScript\" %}\n" + + "{{ escape_string|escapejs }}") + }) +public class EscapeJsFilter implements Filter { + + @Override + public Object filter(Object o, JinjavaInterpreter jinjavaInterpreter, String... strings) { + String input = Objects.toString(o, ""); + StringBuilder builder = new StringBuilder(); + + for (int i = 0; i < input.length(); i++) { + char ch = input.charAt(i); + + if (ch > 0xfff) { + builder.append("\\u"); + builder.append(toHex(ch)); + } else if (ch > 0xff) { + builder.append("\\u0"); + builder.append(toHex(ch)); + } else if (ch > 0x7f) { + builder.append("\\u00"); + builder.append(toHex(ch)); + } else if (ch < 32) { + switch (ch) { + case '\b' : + builder.append("\\b"); + break; + case '\f' : + builder.append("\\f"); + break; + case '\n' : + builder.append("\\n"); + break; + case '\t' : + builder.append("\\t"); + break; + case '\r' : + builder.append("\\r"); + break; + default : + if (ch > 0xf) { + builder.append("\\u00"); + builder.append(toHex(ch)); + } else { + builder.append("\\u000"); + builder.append(toHex(ch)); + } + break; + } + } else { + switch (ch) { + case '"' : + builder.append("\\\""); + break; + case '\\' : + builder.append("\\\\"); + break; + default : + builder.append(ch); + break; + } + } + } + + return builder.toString(); + } + + @Override + public String getName() { + return "escapejs"; + } + + private String toHex(char ch) { + return Integer.toHexString(ch).toUpperCase(Locale.ENGLISH); + } +} diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java b/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java index 5592ae5d1..406d5fb16 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java @@ -74,6 +74,7 @@ protected void registerDefaults() { EscapeFilter.class, EAliasedEscapeFilter.class, + EscapeJsFilter.class, ForceEscapeFilter.class, StripTagsFilter.class, UrlEncodeFilter.class, diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/EscapeJsFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/EscapeJsFilterTest.java new file mode 100644 index 000000000..ee9fa1844 --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/lib/filter/EscapeJsFilterTest.java @@ -0,0 +1,48 @@ +package com.hubspot.jinjava.lib.filter; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; + +import com.google.common.collect.ImmutableMap; +import com.hubspot.jinjava.Jinjava; + +public class EscapeJsFilterTest { + + Jinjava jinjava; + + @Before + public void setup() { + jinjava = new Jinjava(); + jinjava.getGlobalContext().registerClasses(EscapeJsFilter.class); + } + + @Test + public void testHandlesUnicdoe() { + Map vars = ImmutableMap.of("string", "A" + "\u00ea" + "\u00f1" + "\u00fc" + "C"); + assertThat(jinjava.render("{{ string|escapejs }}", vars)).isEqualTo("A\\u00EA\\u00F1\\u00FCC"); + } + + @Test + public void testHandlesNonPrintableCharacters() { + byte[] bytes = {0x4D, 0x13, 0x34, 0x20, 0x8}; + Map vars = ImmutableMap.of("string", new String(bytes)); + assertThat(jinjava.render("{{ string|escapejs }}", vars)).isEqualTo("M\\u00134 \\b"); + } + + @Test + public void testHandlesWhitespace() { + assertThat(jinjava.render("{{ 'Testing\nlinebreak\n'|escapejs }}", new HashMap<>())).isEqualTo("Testing\\nlinebreak\\n"); + assertThat(jinjava.render("{{ 'Testing\ttabbing\t'|escapejs }}", new HashMap<>())).isEqualTo("Testing\\ttabbing\\t"); + } + + @Test + public void testHandlesDoubleQuotes() { + assertThat(jinjava.render("{{ 'Testing a \"quote for the week\"'|escapejs }}", new HashMap<>())).isEqualTo("Testing a \\\"quote for the week\\\""); + } + +} From cd574cfc7985581294f2e69b35a1255da75d95c3 Mon Sep 17 00:00:00 2001 From: Kevin Baker Date: Thu, 2 Jun 2016 14:19:48 +0100 Subject: [PATCH 0167/2465] Don't do a one letter variable name --- .../java/com/hubspot/jinjava/lib/filter/EscapeJsFilter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/EscapeJsFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/EscapeJsFilter.java index d1bad4778..6426b186c 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/EscapeJsFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/EscapeJsFilter.java @@ -36,8 +36,8 @@ public class EscapeJsFilter implements Filter { @Override - public Object filter(Object o, JinjavaInterpreter jinjavaInterpreter, String... strings) { - String input = Objects.toString(o, ""); + public Object filter(Object objectToFilter, JinjavaInterpreter jinjavaInterpreter, String... strings) { + String input = Objects.toString(objectToFilter, ""); StringBuilder builder = new StringBuilder(); for (int i = 0; i < input.length(); i++) { From 9a021fc6d262b9e91f778435e78c88fc0a23a0aa Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Wed, 22 Jun 2016 10:00:30 -0400 Subject: [PATCH 0168/2465] Update errors --- .../com/hubspot/jinjava/el/JinjavaInterpreterResolver.java | 4 ++-- .../java/com/hubspot/jinjava/interpret/TemplateError.java | 6 ++++-- .../interpret/errorcategory/BasicTemplateErrorCategory.java | 5 ++++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java index 9b68dc087..09206dd5d 100644 --- a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java @@ -200,7 +200,7 @@ private static DateTimeFormatter getFormatter(JinjavaInterpreter interpreter, Fo return StrftimeFormatter.formatter(d.getFormat(), interpreter.getConfig().getLocale()); } catch (IllegalArgumentException e) { interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.SYNTAX_ERROR, ErrorItem.OTHER, e.getMessage(), null, interpreter.getLineNumber(), null, - BasicTemplateErrorCategory.UNKNOWN, ImmutableMap.of("exception", e.getMessage(), "lineNumber", String.valueOf(interpreter.getLineNumber())))); + BasicTemplateErrorCategory.UNKNOWN_DATE, ImmutableMap.of("date", d.getDate().toString(), "exception", e.getMessage(), "lineNumber", String.valueOf(interpreter.getLineNumber())))); } } @@ -213,7 +213,7 @@ private static Locale getLocale(JinjavaInterpreter interpreter, FormattedDate d) return LocaleUtils.toLocale(d.getLanguage()); } catch (IllegalArgumentException e) { interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.SYNTAX_ERROR, ErrorItem.OTHER, e.getMessage(), null, interpreter.getLineNumber(), null, - BasicTemplateErrorCategory.UNKNOWN, ImmutableMap.of("exception", e.getMessage(), "lineNumber", String.valueOf(interpreter.getLineNumber())))); + BasicTemplateErrorCategory.UNKNOWN_LOCALE, ImmutableMap.of("date", d.getDate().toString(), "exception", e.getMessage(), "lineNumber", String.valueOf(interpreter.getLineNumber())))); } } diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java index 8d9506e11..4cb904d92 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java @@ -68,8 +68,10 @@ public static TemplateError fromException(Exception ex, int lineNumber) { } public static TemplateError fromUnknownProperty(Object base, String variable, int lineNumber) { - return new TemplateError(ErrorType.WARNING, ErrorReason.UNKNOWN, ErrorItem.PROPERTY, String.format("Cannot resolve property '%s' in '%s'", variable, friendlyObjectToString(base)), - variable, lineNumber, null); + return new TemplateError(ErrorType.WARNING, ErrorReason.UNKNOWN, ErrorItem.PROPERTY, + String.format("Cannot resolve property '%s' in '%s'", variable, friendlyObjectToString(base)), + variable, lineNumber, null, BasicTemplateErrorCategory.UNKNOWN_PROPERTY, + ImmutableMap.of("property", variable, "lineNumber", String.valueOf(lineNumber))); } private static String friendlyObjectToString(Object o) { diff --git a/src/main/java/com/hubspot/jinjava/interpret/errorcategory/BasicTemplateErrorCategory.java b/src/main/java/com/hubspot/jinjava/interpret/errorcategory/BasicTemplateErrorCategory.java index 21cf488fe..0afe455a5 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/errorcategory/BasicTemplateErrorCategory.java +++ b/src/main/java/com/hubspot/jinjava/interpret/errorcategory/BasicTemplateErrorCategory.java @@ -4,5 +4,8 @@ public enum BasicTemplateErrorCategory implements TemplateErrorCategory { CYCLE_DETECTED, IMPORT_CYCLE_DETECTED, INCLUDE_CYCLE_DETECTED, - UNKNOWN + UNKNOWN, + UNKNOWN_DATE, + UNKNOWN_LOCALE, + UNKNOWN_PROPERTY } From 8f0dcbc6f580cef000c6e07870f9d77d866fe0a0 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 28 Jun 2016 11:35:58 -0400 Subject: [PATCH 0169/2465] test wasn't being run --- .../java/com/hubspot/jinjava/lib/fn/RangeFunctionTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/hubspot/jinjava/lib/fn/RangeFunctionTest.java b/src/test/java/com/hubspot/jinjava/lib/fn/RangeFunctionTest.java index d3707d664..ec5eff65e 100644 --- a/src/test/java/com/hubspot/jinjava/lib/fn/RangeFunctionTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/fn/RangeFunctionTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.Arrays; +import java.util.Collections; import org.junit.Test; @@ -10,7 +11,7 @@ public class RangeFunctionTest { @Test public void itGeneratesSimpleRanges() { - assertThat(Functions.range(1)).isEqualTo(Arrays.asList(0)); + assertThat(Functions.range(1)).isEqualTo(Collections.singletonList(0)); assertThat(Functions.range(2)).isEqualTo(Arrays.asList(0, 1)); assertThat(Functions.range(2, 4)).isEqualTo(Arrays.asList(2, 3)); assertThat(Functions.range("2", "4")).isEqualTo(Arrays.asList(2, 3)); @@ -34,11 +35,11 @@ public void itHandlesBadRanges() { assertThat(Functions.range(2, 2000, -5)).isEmpty(); } + @Test public void itHandlesBadValues() { assertThat(Functions.range(2, "f")).isEmpty(); } - @Test public void itTruncatesHugeRanges() { assertThat(Functions.range(2, 200000000).size()).isEqualTo(Functions.RANGE_LIMIT); From 50988939253853ba196a435b4a35052c753ebab4 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 28 Jun 2016 11:36:24 -0400 Subject: [PATCH 0170/2465] confirm list set by index works --- .../java/com/hubspot/jinjava/lib/tag/SetTagTest.java | 11 +++++++++++ src/test/resources/tags/settag/set-list-modify.jinja | 1 + 2 files changed, 12 insertions(+) create mode 100644 src/test/resources/tags/settag/set-list-modify.jinja diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/SetTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/SetTagTest.java index bb2bcf21f..c57b48a2e 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/SetTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/SetTagTest.java @@ -141,6 +141,17 @@ public void itSupportsListAppendFunc() throws Exception { assertThat(thelist).containsExactly("foo", "bar"); } + @Test + public void itSupportsListSetFunc() throws Exception { + context.put("show_count", Lists.newArrayList("foo")); + context.put("show", "bar"); + TagNode tagNode = (TagNode) fixture("set-list-modify"); + tag.interpret(tagNode, interpreter); + + List thelist = (List) context.get("show_count"); + assertThat(thelist).containsExactly("bar"); + } + @Test public void itSupportsMultiVar() throws Exception { context.put("bar", "mybar"); diff --git a/src/test/resources/tags/settag/set-list-modify.jinja b/src/test/resources/tags/settag/set-list-modify.jinja new file mode 100644 index 000000000..ce42c0b55 --- /dev/null +++ b/src/test/resources/tags/settag/set-list-modify.jinja @@ -0,0 +1 @@ +{% set _noop= show_count.set(0, show) %} From 6b7d08414aaf8efdd9c1de3299dcbd4f6217fbfa Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Thu, 7 Jul 2016 19:21:45 +0000 Subject: [PATCH 0171/2465] [maven-release-plugin] prepare release jinjava-2.1.10 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 304436615..c7231961e 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.10-SNAPSHOT + 2.1.10 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.1.10 From da22cee4e21ba5d29859414fc34fd45fdea0325d Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Thu, 7 Jul 2016 19:21:45 +0000 Subject: [PATCH 0172/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c7231961e..859acc9b0 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.10 + 2.1.11-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.1.10 + HEAD From 9043e54df95f6f258599149fc28847dc8780b2cb Mon Sep 17 00:00:00 2001 From: Meghan Nelson Date: Fri, 8 Jul 2016 10:08:35 -0400 Subject: [PATCH 0173/2465] Update CHANGES.md --- CHANGES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index eb772a646..e804c8081 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,10 @@ # Jinjava Releases # +### Version 2.1.11 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.11%22)) ### + +* Add additional specific error enums +* Add escapeJS filter + ### Version 2.1.10 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.10%22)) ### From d80e38e532d151c78576dab290b06900ebaac992 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 18 Jul 2016 21:21:00 -0400 Subject: [PATCH 0174/2465] ignore replace() on null arg --- .../java/com/hubspot/jinjava/lib/filter/ReplaceFilter.java | 2 +- .../com/hubspot/jinjava/lib/filter/ReplaceFilterTest.java | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/ReplaceFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/ReplaceFilter.java index 58e592a5c..3f5db04d5 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/ReplaceFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/ReplaceFilter.java @@ -39,7 +39,7 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args) { if (var == null) { - throw new InterpretException("filter " + getName() + " requires a var to operate on"); + return null; } if (args.length < 2) { throw new InterpretException("filter " + getName() + " requires two string args"); diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/ReplaceFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/ReplaceFilterTest.java index 1a093c44f..483db7786 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/ReplaceFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/ReplaceFilterTest.java @@ -25,9 +25,8 @@ public void expectsAtLeast2Args() { filter.filter("foo", interpreter); } - @Test(expected = InterpretException.class) - public void expectsFilterVar() { - filter.filter(null, interpreter, "foo", "bar"); + public void noopOnNullExpr() { + assertThat(filter.filter(null, interpreter, "foo", "bar")).isNull(); } @Test From ae73fb2ffad51d7d5621f97f1c94b5a351462a49 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 18 Jul 2016 21:27:17 -0400 Subject: [PATCH 0175/2465] Update CHANGES.md --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index e804c8081..aebecccc6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,7 @@ * Add additional specific error enums * Add escapeJS filter +* Allow null expressions as target of replace filter ### Version 2.1.10 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.10%22)) ### From f858cc20eba3662814aa707fadbddc778f718df7 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Tue, 19 Jul 2016 01:32:23 +0000 Subject: [PATCH 0176/2465] [maven-release-plugin] prepare release jinjava-2.1.11 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 859acc9b0..2659f1299 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.11-SNAPSHOT + 2.1.11 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.1.11 From 246952db171c6585eec517d7fca21f25ec7c9a2d Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Tue, 19 Jul 2016 01:32:23 +0000 Subject: [PATCH 0177/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 2659f1299..b88e2adcc 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.11 + 2.1.12-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.1.11 + HEAD From 9222aca9d5baf4e6b4b2716f4a7e7d0295b5d056 Mon Sep 17 00:00:00 2001 From: Markus Heiden Date: Mon, 12 Sep 2016 22:20:58 +0200 Subject: [PATCH 0178/2465] Fix for for tag with spaces --- .../com/hubspot/jinjava/lib/tag/ForTag.java | 20 +++++- .../hubspot/jinjava/lib/tag/ForTagTest.java | 62 ++++++++++++++++++- 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java index 7d84c585a..ddf81c6ee 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java @@ -71,7 +71,25 @@ public class ForTag implements Tag { @SuppressWarnings("unchecked") @Override public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { - List helper = new HelperStringTokenizer(tagNode.getHelpers()).splitComma(true).allTokens(); + + /* apdlv72@gmail.com + * Fix for issues with for-loops that contain whitespace in their range, e.g. + * "{% for i in range(1 * 1, 2 * 2) %}" + * This is because HelperStringTokenizer will split the range expressions also + * at white spaces and end up with [i, in, range(1, *, 1, 2, *, 2)]. + * To avoid this, the below fix will remove white space from the expression + * on the right side of the keyword "in". It will do so however only if there + * are no characters in this expression that indicate strings - namely ' and ". + * This avoids messing up expressions like {% for i in ['a ','b'] %} that + * contain spaces in the arguments. + * TODO A somewhat more sophisticated tokenizing/parsing of the for-loop expression. + */ + String helpers = tagNode.getHelpers(); + String parts[] = helpers.split("\\s+in\\s+"); + if (2==parts.length && !parts[1].contains("'") && !parts[1].contains("\"") ) { + helpers = parts[0] + " in " + parts[1].replace(" ", ""); + } + List helper = new HelperStringTokenizer(helpers).splitComma(true).allTokens(); List loopVars = Lists.newArrayList(); int inPos = 0; diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java index 245a59626..2e26f8e12 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java @@ -1,6 +1,7 @@ package com.hubspot.jinjava.lib.tag; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -31,10 +32,12 @@ public class ForTagTest { Context context; JinjavaInterpreter interpreter; + Jinjava jinjava; @Before public void setup() { - interpreter = new Jinjava().newInterpreter(); + jinjava = new Jinjava(); + interpreter = jinjava.newInterpreter(); context = interpreter.getContext(); tag = new ForTag(); @@ -113,6 +116,63 @@ public void forLoopSupportsAllLoopVarsInHublDocs() throws Exception { assertThat(dom.select(".item-0 .subnum").text()).isEqualTo("6 0"); } + ////////////// + + @Test + public void testForLoopConstants() { + + Map context = Maps.newHashMap(); + String template = "" + + "{% for i in range(1 * 1, 2 * 2) %}{{i}}{% endfor %}"; + + String rendered = jinjava.render(template, context); + assertEquals("123", rendered); + } + + @Test + public void testForLoopVariablesWithoutSpaces() { + + Map context = Maps.newHashMap(); + context.put("a", 2); + context.put("b", 3); + + String template = "" + + "{% for index in range(a*b,a*b+b) %}" + + "{{index}} " + + "{% endfor %}"; + + String rendered = jinjava.render(template, context); + assertEquals("6 7 8 ", rendered); + } + + @Test + public void testFoorLoopVariablesWithSpaces() { + + Map context = Maps.newHashMap(); + context.put("a", 2); + context.put("b", 3); + + String template = "" + + "{% for index in range(a * b, a * b + b) %}" + + "{{index}} " + + "{% endfor %}"; + + String rendered = jinjava.render(template, context); + assertEquals("6 7 8 ", rendered); + } + + @Test + public void testForLoopRangeWithStringsWithSpaces() { + Map context = Maps.newHashMap(); + String template = "" + + "{% for i in ['a ','b'] %}{{i}}{% endfor %}"; + String rendered = jinjava.render(template, context); + System.out.println(rendered); + assertEquals("a b", rendered); + } + + ////////////// + private Node fixture(String name) { try { return new TreeParser(interpreter, Resources.toString( From ad09c0c74cbc9aa1a135b4072c983e948b58a660 Mon Sep 17 00:00:00 2001 From: Markus Heiden Date: Mon, 12 Sep 2016 22:21:39 +0200 Subject: [PATCH 0179/2465] Add operators ** and // --- .../jinjava/el/ext/ExtendedParser.java | 17 ++++- .../jinjava/el/ext/ExtendedScanner.java | 6 ++ .../jinjava/el/ext/PowerOfOperator.java | 47 ++++++++++++ .../jinjava/el/ext/TruncDivOperator.java | 48 +++++++++++++ .../hubspot/jinjava/el/ext/PowerOfTest.java | 63 ++++++++++++++++ .../hubspot/jinjava/el/ext/TruncDivTest.java | 72 +++++++++++++++++++ 6 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/hubspot/jinjava/el/ext/PowerOfOperator.java create mode 100644 src/main/java/com/hubspot/jinjava/el/ext/TruncDivOperator.java create mode 100644 src/test/java/com/hubspot/jinjava/el/ext/PowerOfTest.java create mode 100644 src/test/java/com/hubspot/jinjava/el/ext/TruncDivTest.java diff --git a/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java b/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java index 1c803a4a6..742ad9eae 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java @@ -52,9 +52,16 @@ public class ExtendedParser extends Parser { static final Scanner.ExtensionToken LITERAL_DICT_START = new Scanner.ExtensionToken("{"); static final Scanner.ExtensionToken LITERAL_DICT_END = new Scanner.ExtensionToken("}"); + static final Scanner.ExtensionToken TRUNC_DIV = new Scanner.ExtensionToken("//"); + static final Scanner.ExtensionToken POWER_OF = new Scanner.ExtensionToken("**"); + static { ExtendedScanner.addKeyToken(IF); ExtendedScanner.addKeyToken(ELSE); + + ExtendedScanner.addKeyToken(TruncDivOperator.TOKEN); + ExtendedScanner.addKeyToken(PowerOfOperator.TOKEN); + ExtendedScanner.addKeyToken(CollectionMembershipOperator.TOKEN); } @@ -64,6 +71,8 @@ public ExtendedParser(Builder context, String input) { putExtensionHandler(AbsOperator.TOKEN, AbsOperator.HANDLER); putExtensionHandler(NamedParameterOperator.TOKEN, NamedParameterOperator.HANDLER); putExtensionHandler(StringConcatOperator.TOKEN, StringConcatOperator.HANDLER); + putExtensionHandler(TruncDivOperator.TOKEN, TruncDivOperator.HANDLER); + putExtensionHandler(PowerOfOperator.TOKEN, PowerOfOperator.HANDLER); putExtensionHandler(CollectionMembershipOperator.TOKEN, CollectionMembershipOperator.HANDLER); @@ -369,8 +378,14 @@ protected AstNode value() throws ScanException, ParseException { AstProperty exptestProperty = createAstDot(identifier(EXPTEST_PREFIX + exptestName), "evaluate", true); v = createAstMethod(exptestProperty, new AstParameters(exptestParams)); - } + } else if ("//".equals(getToken().getImage()) && lookahead(0).getSymbol() == IDENTIFIER) { + consumeToken(); // '//' + v = createAstBinary(v, mul(true), TruncDivOperator.OP); + } else if ("**".equals(getToken().getImage()) && lookahead(0).getSymbol() == IDENTIFIER) { + consumeToken(); // '**' + v = createAstBinary(v, mul(true), PowerOfOperator.OP); + } return v; } } diff --git a/src/main/java/com/hubspot/jinjava/el/ext/ExtendedScanner.java b/src/main/java/com/hubspot/jinjava/el/ext/ExtendedScanner.java index 01c29bdc7..da9e0ae85 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/ExtendedScanner.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/ExtendedScanner.java @@ -115,6 +115,12 @@ protected Token nextEval() throws ScanException { char c1 = getInput().charAt(getPosition()); char c2 = getPosition() < getInput().length() - 1 ? getInput().charAt(getPosition() + 1) : (char) 0; + if (c1 == '/' && c2 == '/') { + return ExtendedParser.TRUNC_DIV; + } + if (c1 == '*' && c2 == '*') { + return ExtendedParser.POWER_OF; + } if (c1 == '|' && c2 != '|') { return ExtendedParser.PIPE; } diff --git a/src/main/java/com/hubspot/jinjava/el/ext/PowerOfOperator.java b/src/main/java/com/hubspot/jinjava/el/ext/PowerOfOperator.java new file mode 100644 index 000000000..8152a2d25 --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/el/ext/PowerOfOperator.java @@ -0,0 +1,47 @@ +package com.hubspot.jinjava.el.ext; + +import de.odysseus.el.misc.TypeConverter; +import de.odysseus.el.tree.impl.Parser.ExtensionHandler; +import de.odysseus.el.tree.impl.Parser.ExtensionPoint; +import de.odysseus.el.tree.impl.Scanner; +import de.odysseus.el.tree.impl.ast.AstBinary; +import de.odysseus.el.tree.impl.ast.AstBinary.SimpleOperator; +import de.odysseus.el.tree.impl.ast.AstNode; + +public class PowerOfOperator extends SimpleOperator { + + @Override + protected Object apply(TypeConverter converter, Object a, Object b) { + boolean aInt = a instanceof Integer || a instanceof Long; + boolean bInt = b instanceof Integer || b instanceof Long; + boolean aNum = aInt || a instanceof Double || a instanceof Float; + boolean bNum = bInt || b instanceof Double || b instanceof Float; + + if (aInt && bInt) { + Long d = converter.convert(a, Long.class); + Long e = converter.convert(b, Long.class); + return (long)Math.pow(d, e); + } + if (aNum && bNum) { + Double d = converter.convert(a, Double.class); + Double e = converter.convert(b, Double.class); + return Math.pow(d, e); + } + throw new IllegalArgumentException("Unsupported operand type(s) for **: " + + "'" + a.getClass().getSimpleName() + "' and " + + "'" + b.getClass().getSimpleName() + "'"); + } + + public static final Scanner.ExtensionToken TOKEN = new Scanner.ExtensionToken("**"); + public static final PowerOfOperator OP = new PowerOfOperator(); + + public static final ExtensionHandler HANDLER = new ExtensionHandler(ExtensionPoint.MUL) { + @Override + public AstNode createAstNode(AstNode... children) { + return new AstBinary(children[0], children[1], OP); + } + }; + +} + + diff --git a/src/main/java/com/hubspot/jinjava/el/ext/TruncDivOperator.java b/src/main/java/com/hubspot/jinjava/el/ext/TruncDivOperator.java new file mode 100644 index 000000000..573ce93c0 --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/el/ext/TruncDivOperator.java @@ -0,0 +1,48 @@ +package com.hubspot.jinjava.el.ext; + +import de.odysseus.el.misc.TypeConverter; +import de.odysseus.el.tree.impl.Parser.ExtensionHandler; +import de.odysseus.el.tree.impl.Parser.ExtensionPoint; +import de.odysseus.el.tree.impl.Scanner; +import de.odysseus.el.tree.impl.ast.AstBinary; +import de.odysseus.el.tree.impl.ast.AstBinary.SimpleOperator; +import de.odysseus.el.tree.impl.ast.AstNode; + +public class TruncDivOperator extends SimpleOperator { + + @Override + protected Object apply(TypeConverter converter, Object a, Object b) { + + boolean aInt = a instanceof Integer || a instanceof Long; + boolean bInt = b instanceof Integer || b instanceof Long; + boolean aNum = aInt || a instanceof Double || a instanceof Float; + boolean bNum = bInt || b instanceof Double || b instanceof Float; + + if (aInt && bInt) { + Long d = converter.convert(a, Long.class); + Long e = converter.convert(b, Long.class); + return (long)Math.floor(d/e); + } + if (aNum && bNum) { + Double d = converter.convert(a, Double.class); + Double e = converter.convert(b, Double.class); + return (double)Math.floor(d/e); + } + throw new IllegalArgumentException("Unsupported operand type(s) for //: " + + "'" + a.getClass().getSimpleName() + "' and " + + "'" + b.getClass().getSimpleName() + "'"); + } + + public static final Scanner.ExtensionToken TOKEN = new Scanner.ExtensionToken("//"); + public static final TruncDivOperator OP = new TruncDivOperator(); + + public static final ExtensionHandler HANDLER = new ExtensionHandler(ExtensionPoint.MUL) { + @Override + public AstNode createAstNode(AstNode... children) { + return new AstBinary(children[0], children[1], OP); + } + }; + +} + + diff --git a/src/test/java/com/hubspot/jinjava/el/ext/PowerOfTest.java b/src/test/java/com/hubspot/jinjava/el/ext/PowerOfTest.java new file mode 100644 index 000000000..8e85863d1 --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/el/ext/PowerOfTest.java @@ -0,0 +1,63 @@ +package com.hubspot.jinjava.el.ext; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; + +import com.google.common.collect.Maps; +import com.hubspot.jinjava.Jinjava; +import com.hubspot.jinjava.interpret.FatalTemplateErrorsException; + +public class PowerOfTest { + + @Before + public void setUp() { + jinja = new Jinjava(); + } + + @Test + public void testPowerOfInteger() { + + Map context = Maps.newHashMap(); + context.put("base", 2); + context.put("exponent", 8); + + String template = "{% set x = base ** exponent %}{{x}}"; + String rendered = jinja.render(template, context); + assertEquals("256", rendered); + } + + @Test + public void testPowerOfFractional() { + + Map context = Maps.newHashMap(); + context.put("base", 2); + context.put("exponent", 8.0); + + String template = "{% set x = base ** exponent %}{{x}}"; + String rendered = jinja.render(template, context); + assertEquals("256.0", rendered); + } + + @Test + public void test04PowerOfStringFails() { + + Map context = Maps.newHashMap(); + context.put("base", "2"); + context.put("exponent", "8"); + + String template = "{% set x = base ** exponent %}{{x}}"; + try { + jinja.render(template, context); + } catch (FatalTemplateErrorsException e) { + String msg = e.getMessage(); + assertTrue(msg.contains("Unsupported operand type(s)")); + } + } + + private Jinjava jinja; +} diff --git a/src/test/java/com/hubspot/jinjava/el/ext/TruncDivTest.java b/src/test/java/com/hubspot/jinjava/el/ext/TruncDivTest.java new file mode 100644 index 000000000..5dda9ae67 --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/el/ext/TruncDivTest.java @@ -0,0 +1,72 @@ +package com.hubspot.jinjava.el.ext; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; + +import com.google.common.collect.Maps; +import com.hubspot.jinjava.Jinjava; +import com.hubspot.jinjava.interpret.FatalTemplateErrorsException; + +public class TruncDivTest { + + @Before + public void setUp() { + jinja = new Jinjava(); + } + + /** + * Test the truncated division operator "//" with integer values + */ + @Test + public void testTruncDivInteger() { + Map context = Maps.newHashMap(); + context.put("dividend", 5); + context.put("divisor", 2); + + String template = "{% set x = dividend // divisor %}{{x}}"; + String rendered = jinja.render(template, context); + assertEquals("2", rendered); + } + + /** + * Test the truncated division operator "//" with fractional values + */ + @Test + public void testTruncDivFractional() { + + Map context = Maps.newHashMap(); + context.put("dividend", 5.0); + context.put("divisor", 2); + + String template = "{% set x = dividend // divisor %}{{x}}"; + String rendered = jinja.render(template, context); + assertEquals("2.0", rendered); + } + + /** + * Test the truncated division operator "//" with strings + */ + @Test + public void testTruncDivStringFails() { + + Map context = Maps.newHashMap(); + context.put("dividend", "5"); + context.put("divisor", "2"); + + String template = "{% set x = dividend // divisor %}{{x}}"; + try { + jinja.render(template, context); + } catch (FatalTemplateErrorsException e) { + String msg = e.getMessage(); + assertTrue(msg.contains("Unsupported operand type(s)")); + } + } + + + private Jinjava jinja; +} From 7c41516e21db2ad4696340f190969108881a2539 Mon Sep 17 00:00:00 2001 From: Markus Heiden Date: Mon, 12 Sep 2016 22:28:13 +0200 Subject: [PATCH 0180/2465] Make findbugs happy --- .../java/com/hubspot/jinjava/el/ext/TruncDivOperator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/TruncDivOperator.java b/src/main/java/com/hubspot/jinjava/el/ext/TruncDivOperator.java index 573ce93c0..878c7ea2a 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/TruncDivOperator.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/TruncDivOperator.java @@ -21,12 +21,12 @@ protected Object apply(TypeConverter converter, Object a, Object b) { if (aInt && bInt) { Long d = converter.convert(a, Long.class); Long e = converter.convert(b, Long.class); - return (long)Math.floor(d/e); + return Math.floorDiv(d, e); } if (aNum && bNum) { Double d = converter.convert(a, Double.class); Double e = converter.convert(b, Double.class); - return (double)Math.floor(d/e); + return Math.floor(d/e); } throw new IllegalArgumentException("Unsupported operand type(s) for //: " + "'" + a.getClass().getSimpleName() + "' and " From 6451dd0466cbea4e34b4694dbd3a049517dc7634 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 13 Sep 2016 11:22:37 -0400 Subject: [PATCH 0181/2465] remove extra spaces --- src/test/java/com/hubspot/jinjava/el/ext/PowerOfTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/hubspot/jinjava/el/ext/PowerOfTest.java b/src/test/java/com/hubspot/jinjava/el/ext/PowerOfTest.java index 8e85863d1..0c992d7e5 100644 --- a/src/test/java/com/hubspot/jinjava/el/ext/PowerOfTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ext/PowerOfTest.java @@ -20,7 +20,7 @@ public void setUp() { } @Test - public void testPowerOfInteger() { + public void testPowerOfInteger() { Map context = Maps.newHashMap(); context.put("base", 2); @@ -32,7 +32,7 @@ public void testPowerOfInteger() { } @Test - public void testPowerOfFractional() { + public void testPowerOfFractional() { Map context = Maps.newHashMap(); context.put("base", 2); @@ -44,7 +44,7 @@ public void testPowerOfFractional() { } @Test - public void test04PowerOfStringFails() { + public void test04PowerOfStringFails() { Map context = Maps.newHashMap(); context.put("base", "2"); From 9c60136f59a5e89acb4808e3828eb957c87c1a7b Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 13 Sep 2016 11:23:43 -0400 Subject: [PATCH 0182/2465] remove slashes --- src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java index 2e26f8e12..45e7fefa6 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java @@ -116,8 +116,6 @@ public void forLoopSupportsAllLoopVarsInHublDocs() throws Exception { assertThat(dom.select(".item-0 .subnum").text()).isEqualTo("6 0"); } - ////////////// - @Test public void testForLoopConstants() { @@ -171,8 +169,6 @@ public void testForLoopRangeWithStringsWithSpaces() { assertEquals("a b", rendered); } - ////////////// - private Node fixture(String name) { try { return new TreeParser(interpreter, Resources.toString( From 21020e1206e90770ec8da3b93945e85b56521cd1 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 13 Sep 2016 11:24:26 -0400 Subject: [PATCH 0183/2465] remove extra spaces --- src/test/java/com/hubspot/jinjava/el/ext/TruncDivTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/hubspot/jinjava/el/ext/TruncDivTest.java b/src/test/java/com/hubspot/jinjava/el/ext/TruncDivTest.java index 5dda9ae67..253fa2498 100644 --- a/src/test/java/com/hubspot/jinjava/el/ext/TruncDivTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ext/TruncDivTest.java @@ -23,7 +23,7 @@ public void setUp() { * Test the truncated division operator "//" with integer values */ @Test - public void testTruncDivInteger() { + public void testTruncDivInteger() { Map context = Maps.newHashMap(); context.put("dividend", 5); context.put("divisor", 2); @@ -37,7 +37,7 @@ public void testTruncDivInteger() { * Test the truncated division operator "//" with fractional values */ @Test - public void testTruncDivFractional() { + public void testTruncDivFractional() { Map context = Maps.newHashMap(); context.put("dividend", 5.0); @@ -52,7 +52,7 @@ public void testTruncDivFractional() { * Test the truncated division operator "//" with strings */ @Test - public void testTruncDivStringFails() { + public void testTruncDivStringFails() { Map context = Maps.newHashMap(); context.put("dividend", "5"); From 9bb489819a7364bfff7694d50e7770e62270783b Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 16 Sep 2016 10:20:53 -0400 Subject: [PATCH 0184/2465] test map attributes on custom objects --- .../jinjava/el/ExpressionResolverTest.java | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java index 6c12ada6b..1417e25bd 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java @@ -3,16 +3,19 @@ import static org.assertj.core.api.Assertions.assertThat; import java.math.BigDecimal; +import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Set; import org.junit.Before; import org.junit.Test; import com.google.common.collect.ForwardingList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.hubspot.jinjava.Jinjava; @@ -95,6 +98,7 @@ public void itResolvesDictValWithBracket() throws Exception { Object val = interpreter.resolveELExpression("thedict['foo']", -1); assertThat(val).isEqualTo("bar"); assertThat(interpreter.getContext().wasExpressionResolved("thedict['foo']")).isTrue(); + } @Test @@ -108,6 +112,86 @@ public void itResolvesDictValWithDotParam() throws Exception { assertThat(interpreter.getContext().wasExpressionResolved("thedict.foo")).isTrue(); } + @Test + public void itResolvesMapValOnCustomObject() throws Exception { + + MyCustomMap dict = new MyCustomMap(); + context.put("thedict", dict); + + Object val = interpreter.resolveELExpression("thedict['foo']", -1); + assertThat(val).isEqualTo("bar"); + assertThat(interpreter.getContext().wasExpressionResolved("thedict['foo']")).isTrue(); + + Object val2 = interpreter.resolveELExpression("thedict.two", -1); + assertThat(val2).isEqualTo("2"); + assertThat(interpreter.getContext().wasExpressionResolved("thedict.two")).isTrue(); + } + + public static final class MyCustomMap implements Map { + + Map data = ImmutableMap.of("foo", "bar", "two", "2"); + + @Override + public int size() { + return data.size(); + } + + @Override + public boolean isEmpty() { + return data.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return data.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return data.containsValue(value); + } + + @Override + public String get(Object key) { + return data.get(key); + } + + @Override + public String put(String key, String value) { + return null; + } + + @Override + public String remove(Object key) { + return null; + } + + @Override + public void putAll(Map m) { + + } + + @Override + public void clear() { + + } + + @Override + public Set keySet() { + return data.keySet(); + } + + @Override + public Collection values() { + return data.values(); + } + + @Override + public Set> entrySet() { + return data.entrySet(); + } + } + @Test public void itResolvesInnerDictVal() throws Exception { Map dict = Maps.newHashMap(); From 07005e665725198d9e36420039fa51490c2ca98c Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 16 Sep 2016 11:06:33 -0400 Subject: [PATCH 0185/2465] add performance test to determine relative speed of method invocation vs map lookup --- .../el/ExpressionResolverPerformanceTest.java | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 src/test/java/com/hubspot/jinjava/el/ExpressionResolverPerformanceTest.java diff --git a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverPerformanceTest.java b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverPerformanceTest.java new file mode 100644 index 000000000..f4946c7f4 --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverPerformanceTest.java @@ -0,0 +1,150 @@ +package com.hubspot.jinjava.el; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +import com.hubspot.jinjava.Jinjava; +import com.hubspot.jinjava.interpret.Context; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; + +public class ExpressionResolverPerformanceTest { + + public static void main(String[] args) { + PerformanceTester tester = new PerformanceTester(); + tester.run(); + } + + public static class PerformanceTester { + + private JinjavaInterpreter interpreter; + private Context context; + private long startTime; + + public PerformanceTester() { + + interpreter = new Jinjava().newInterpreter(); + context = interpreter.getContext(); + context.put("customMap", new CustomMap()); + context.put("customObject", new CustomObject()); + } + + public void run() { + + int iterations = 1000000; + + testMapResolver(iterations); + testMethodResolver(iterations); + } + + public void startTimer() { + startTime = System.currentTimeMillis(); + } + + public void stopTimer() { + System.out.println(String.format("%d msec", System.currentTimeMillis() - startTime)); + } + + public void testMapResolver(int iterations) { + System.out.println("map resolver"); + startTimer(); + + for (int i = 0; i < iterations; i++) { + Object val = interpreter.resolveELExpression("customMap.get(\"thing\")", -1); + assertThat(val).isEqualTo("hey"); + } + + stopTimer(); + } + + public void testMethodResolver(int iterations) { + System.out.println("method resolver"); + startTimer(); + + for (int i = 0; i < iterations; i++) { + Object val = interpreter.resolveELExpression("customObject.getThing()", -1); + assertThat(val).isEqualTo("hey"); + } + + stopTimer(); + } + + } + + static class CustomObject { + + public CustomObject() { + } + + public String getThing() { + return "hey"; + } + + } + + static class CustomMap implements Map { + + @Override + public String get(Object key) { + return "hey"; + } + + @Override + public int size() { + return 0; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean containsKey(Object key) { + return false; + } + + @Override + public boolean containsValue(Object value) { + return false; + } + + @Override + public String put(String key, String value) { + return null; + } + + @Override + public String remove(Object key) { + return null; + } + + @Override + public void putAll(Map m) { + + } + + @Override + public void clear() { + + } + + @Override + public Set keySet() { + return null; + } + + @Override + public Collection values() { + return null; + } + + @Override + public Set> entrySet() { + return null; + } + } + +} From c9c7d0c9345e8ebe2dadea8f651a867a6bb3c53f Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 16 Sep 2016 11:08:25 -0400 Subject: [PATCH 0186/2465] test method resolution on map objects --- .../jinjava/el/ExpressionResolverTest.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java index 1417e25bd..8ce33a68f 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java @@ -98,7 +98,6 @@ public void itResolvesDictValWithBracket() throws Exception { Object val = interpreter.resolveELExpression("thedict['foo']", -1); assertThat(val).isEqualTo("bar"); assertThat(interpreter.getContext().wasExpressionResolved("thedict['foo']")).isTrue(); - } @Test @@ -127,9 +126,25 @@ public void itResolvesMapValOnCustomObject() throws Exception { assertThat(interpreter.getContext().wasExpressionResolved("thedict.two")).isTrue(); } + @Test + public void itResolvesOtherMethodsOnCustomMapObject() throws Exception { + + MyCustomMap dict = new MyCustomMap(); + context.put("thedict", dict); + + Object val = interpreter.resolveELExpression("thedict.size", -1); + assertThat(val).isEqualTo("777"); + + Object val1 = interpreter.resolveELExpression("thedict.size()", -1); + assertThat(val1).isEqualTo(3); + + Object val2 = interpreter.resolveELExpression("thedict.items()", -1); + assertThat(val2.toString()).isEqualTo("[foo=bar, two=2, size=777]"); + } + public static final class MyCustomMap implements Map { - Map data = ImmutableMap.of("foo", "bar", "two", "2"); + Map data = ImmutableMap.of("foo", "bar", "two", "2", "size", "777"); @Override public int size() { From 115db520cedbe26ad2f6a78dc6b33ef315c49af6 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 16 Sep 2016 11:13:06 -0400 Subject: [PATCH 0187/2465] put iterations in test result --- .../hubspot/jinjava/el/ExpressionResolverPerformanceTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverPerformanceTest.java b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverPerformanceTest.java index f4946c7f4..1f0c8b2ba 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverPerformanceTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverPerformanceTest.java @@ -48,7 +48,7 @@ public void stopTimer() { } public void testMapResolver(int iterations) { - System.out.println("map resolver"); + System.out.println("map resolver with " + iterations + " iterations"); startTimer(); for (int i = 0; i < iterations; i++) { @@ -60,7 +60,7 @@ public void testMapResolver(int iterations) { } public void testMethodResolver(int iterations) { - System.out.println("method resolver"); + System.out.println("method resolver with " + iterations + " iterations"); startTimer(); for (int i = 0; i < iterations; i++) { From 1c92e06068fc6627ecdd7dbd365c404fa251a83f Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Wed, 21 Sep 2016 13:25:46 -0400 Subject: [PATCH 0188/2465] pass arguments from selectattr to expression tests --- .../com/hubspot/jinjava/lib/filter/SelectAttrFilter.java | 9 ++++++++- .../hubspot/jinjava/lib/filter/SelectAttrFilterTest.java | 7 +++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java index 7e07478d8..3a7a1ac36 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java @@ -1,6 +1,7 @@ package com.hubspot.jinjava.lib.filter; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; @@ -41,6 +42,8 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args) throw new InterpretException(getName() + " filter requires an attr to filter on", interpreter.getLineNumber()); } + String[] expArgs = new String[]{}; + String attr = args[0]; ExpTest expTest = interpreter.getContext().getExpTest("truthy"); @@ -49,6 +52,10 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args) if (expTest == null) { throw new InterpretException("No expression test defined with name '" + args[1] + "'", interpreter.getLineNumber()); } + + if (args.length > 2) { + expArgs = Arrays.copyOfRange(args, 2, args.length); + } } ForLoop loop = ObjectIterator.getLoop(var); @@ -56,7 +63,7 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args) Object val = loop.next(); Object attrVal = interpreter.resolveProperty(val, attr); - if (expTest.evaluate(attrVal, interpreter)) { + if (expTest.evaluate(attrVal, interpreter, expArgs)) { result.add(val); } } diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/SelectAttrFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/SelectAttrFilterTest.java index 99dc766c5..20836f7d0 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/SelectAttrFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/SelectAttrFilterTest.java @@ -33,6 +33,13 @@ public void selectAttrWithExp() { .isEqualTo("[2]"); } + @Test + public void selectAttrWithIsEqualToExp() { + assertThat(jinjava.render("{{ users|selectattr('email', 'equalto', 'bar@bar.com') }}", new HashMap())) + .isEqualTo("[1]"); + } + + public static class User { private int num; private boolean isActive; From df7942ddb4f279d637b1e6ba851ada5d779b66b7 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Wed, 21 Sep 2016 15:14:49 -0400 Subject: [PATCH 0189/2465] cast to object array --- .../java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java index 3a7a1ac36..17b7462e2 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java @@ -63,7 +63,7 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args) Object val = loop.next(); Object attrVal = interpreter.resolveProperty(val, attr); - if (expTest.evaluate(attrVal, interpreter, expArgs)) { + if (expTest.evaluate(attrVal, interpreter, (Object[]) expArgs)) { result.add(val); } } From 621c1b782445a75f4f0bb0b7d67fc89c9fed267b Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Thu, 13 Oct 2016 16:30:13 -0400 Subject: [PATCH 0190/2465] Update CHANGES.md --- CHANGES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index aebecccc6..7f1ef1fe4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,10 @@ # Jinjava Releases # +### Version 2.1.12 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.12%22)) ### + +* Added ** and // operators +* Fixed issue with passing arguments to expression tests + ### Version 2.1.11 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.11%22)) ### * Add additional specific error enums From 5859cb098873e9b46f123857060a1eedc2b1a8d9 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Thu, 13 Oct 2016 20:35:22 +0000 Subject: [PATCH 0191/2465] [maven-release-plugin] prepare release jinjava-2.1.12 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b88e2adcc..a8f3e6a1e 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.12-SNAPSHOT + 2.1.12 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.1.12 From ef004a19357eecd6e055f0e3af14456b9fbf3c97 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Thu, 13 Oct 2016 20:35:22 +0000 Subject: [PATCH 0192/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a8f3e6a1e..4765145d7 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.12 + 2.1.13-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.1.12 + HEAD From 2818d857b0b91eb6a029462bbe7343809ae6c9fd Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 28 Oct 2016 17:54:25 -0400 Subject: [PATCH 0193/2465] set restrictions on tags, expressions that can be called within a context --- .../java/com/hubspot/jinjava/Jinjava.java | 2 +- .../hubspot/jinjava/interpret/Context.java | 33 ++++++++++----- .../jinjava/interpret/JinjavaInterpreter.java | 8 +++- .../hubspot/jinjava/lib/SimpleLibrary.java | 26 ++++++++++-- .../jinjava/lib/exptest/ExpTestLibrary.java | 6 ++- .../jinjava/lib/filter/FilterLibrary.java | 6 ++- .../jinjava/lib/fn/FunctionLibrary.java | 6 ++- .../hubspot/jinjava/lib/tag/TagLibrary.java | 6 ++- .../jinjava/el/ExpressionResolverTest.java | 40 +++++++++++++++++++ 9 files changed, 110 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/Jinjava.java b/src/main/java/com/hubspot/jinjava/Jinjava.java index 43279e62d..e62e6983f 100644 --- a/src/main/java/com/hubspot/jinjava/Jinjava.java +++ b/src/main/java/com/hubspot/jinjava/Jinjava.java @@ -186,7 +186,7 @@ public RenderResult renderForResult(String template, Map bindings) { * @return result object containing rendered output, render context, and any encountered errors */ public RenderResult renderForResult(String template, Map bindings, JinjavaConfig renderConfig) { - Context context = new Context(globalContext, bindings); + Context context = new Context(globalContext, bindings, null); JinjavaInterpreter parentInterpreter = JinjavaInterpreter.getCurrent(); if (parentInterpreter != null) { diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index 4611cf00f..3e34975ce 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -44,6 +44,12 @@ public class Context extends ScopeMap { private final SetMultimap dependencies = HashMultimap.create(); + public enum Library { + EXP_TEST, + FILTER, + TAG + } + private final CallStack extendPathStack; private final CallStack importPathStack; private final CallStack includePathStack; @@ -64,28 +70,35 @@ public class Context extends ScopeMap { private List superBlock; public Context() { - this(null); + this(null, null, null); } public Context(Context parent) { + this(parent, null, null); + } + + public Context(Context parent, Map bindings, Map> disabled) { super(parent); + if (bindings != null) { + this.putAll(bindings); + } + this.parent = parent; - this.expTestLibrary = new ExpTestLibrary(parent == null); - this.filterLibrary = new FilterLibrary(parent == null); - this.functionLibrary = new FunctionLibrary(parent == null); - this.tagLibrary = new TagLibrary(parent == null); + this.extendPathStack = new CallStack(parent == null ? null : parent.getExtendPathStack(), ExtendsTagCycleException.class); this.importPathStack = new CallStack(parent == null ? null : parent.getImportPathStack(), ImportTagCycleException.class); this.includePathStack = new CallStack(parent == null ? null : parent.getIncludePathStack(), IncludeTagCycleException.class); this.macroStack = new CallStack(parent == null ? null : parent.getMacroStack(), MacroTagCycleException.class); - } - public Context(Context parent, Map bindings) { - this(parent); - if (bindings != null) { - this.putAll(bindings); + if (disabled == null) { + disabled = new HashMap>(); } + + this.expTestLibrary = new ExpTestLibrary(parent == null, disabled.get(Library.EXP_TEST)); + this.filterLibrary = new FilterLibrary(parent == null, disabled.get(Library.FILTER)); + this.tagLibrary = new TagLibrary(parent == null, disabled.get(Library.TAG)); + this.functionLibrary = new FunctionLibrary(parent == null, null); } @Override diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index d99844b3f..87bb1c416 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -22,8 +22,10 @@ import java.util.Collections; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.Stack; import org.apache.commons.lang3.StringUtils; @@ -93,7 +95,11 @@ public void addBlock(String name, LinkedList value) { * */ public InterpreterScopeClosable enterScope() { - context = new Context(context); + return enterScope(null); + } + + public InterpreterScopeClosable enterScope(Map> disabled) { + context = new Context(context, null, disabled); return new InterpreterScopeClosable(); } diff --git a/src/main/java/com/hubspot/jinjava/lib/SimpleLibrary.java b/src/main/java/com/hubspot/jinjava/lib/SimpleLibrary.java index e7368708c..aa57aa061 100644 --- a/src/main/java/com/hubspot/jinjava/lib/SimpleLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/SimpleLibrary.java @@ -20,18 +20,32 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.el.MethodNotFoundException; import org.apache.commons.lang3.StringUtils; import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableSet; public abstract class SimpleLibrary { private Map lib = new HashMap(); + private Set disabled = new HashSet(); protected SimpleLibrary(boolean registerDefaults) { + this(registerDefaults, null); + } + + protected SimpleLibrary(boolean registerDefaults, Set disabled) { + if (disabled != null) { + this.disabled = ImmutableSet.copyOf(disabled); + } if (registerDefaults) { registerDefaults(); } @@ -40,6 +54,10 @@ protected SimpleLibrary(boolean registerDefaults) { protected abstract void registerDefaults(); public T fetch(String item) { + if (disabled.contains(item)) { + throw new MethodNotFoundException("'" + item + "' is disabled in this context "); + } + return lib.get(StringUtils.lowerCase(item)); } @@ -65,12 +83,14 @@ public void register(T obj) { } public void register(String name, T obj) { - lib.put(name, obj); - ENGINE_LOG.debug(getClass().getSimpleName() + ": Registered " + obj.getName()); + if (!disabled.contains(obj.getName().toLowerCase())) { + lib.put(name, obj); + ENGINE_LOG.debug(getClass().getSimpleName() + ": Registered " + obj.getName()); + } } public Collection entries() { - return lib.values(); + return lib.values().stream().filter(t -> !disabled.contains(t.getName())).collect(Collectors.toSet()); } } diff --git a/src/main/java/com/hubspot/jinjava/lib/exptest/ExpTestLibrary.java b/src/main/java/com/hubspot/jinjava/lib/exptest/ExpTestLibrary.java index 9591bd1c3..d932ccd52 100644 --- a/src/main/java/com/hubspot/jinjava/lib/exptest/ExpTestLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/exptest/ExpTestLibrary.java @@ -1,11 +1,13 @@ package com.hubspot.jinjava.lib.exptest; +import java.util.Set; + import com.hubspot.jinjava.lib.SimpleLibrary; public class ExpTestLibrary extends SimpleLibrary { - public ExpTestLibrary(boolean registerDefaults) { - super(registerDefaults); + public ExpTestLibrary(boolean registerDefaults, Set disabled) { + super(registerDefaults, disabled); } @Override diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java b/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java index 406d5fb16..81004cbb1 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java @@ -15,12 +15,14 @@ **********************************************************************/ package com.hubspot.jinjava.lib.filter; +import java.util.Set; + import com.hubspot.jinjava.lib.SimpleLibrary; public class FilterLibrary extends SimpleLibrary { - public FilterLibrary(boolean registerDefaults) { - super(registerDefaults); + public FilterLibrary(boolean registerDefaults, Set disabled) { + super(registerDefaults, disabled); } @Override diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/FunctionLibrary.java b/src/main/java/com/hubspot/jinjava/lib/fn/FunctionLibrary.java index 10fa480ae..e66ae161b 100644 --- a/src/main/java/com/hubspot/jinjava/lib/fn/FunctionLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/FunctionLibrary.java @@ -1,12 +1,14 @@ package com.hubspot.jinjava.lib.fn; +import java.util.Set; + import com.google.common.collect.Lists; import com.hubspot.jinjava.lib.SimpleLibrary; public class FunctionLibrary extends SimpleLibrary { - public FunctionLibrary(boolean registerDefaults) { - super(registerDefaults); + public FunctionLibrary(boolean registerDefaults, Set disabled) { + super(registerDefaults, disabled); } @Override diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/TagLibrary.java b/src/main/java/com/hubspot/jinjava/lib/tag/TagLibrary.java index 2c24c71d0..b378c79b7 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/TagLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/TagLibrary.java @@ -15,12 +15,14 @@ **********************************************************************/ package com.hubspot.jinjava.lib.tag; +import java.util.Set; + import com.hubspot.jinjava.lib.SimpleLibrary; public class TagLibrary extends SimpleLibrary { - public TagLibrary(boolean registerDefaults) { - super(registerDefaults); + public TagLibrary(boolean registerDefaults, Set disabled) { + super(registerDefaults, disabled); } @Override diff --git a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java index 8ce33a68f..92800d14c 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java @@ -11,11 +11,14 @@ import java.util.Optional; import java.util.Set; +import javax.el.MethodNotFoundException; + import org.junit.Before; import org.junit.Test; import com.google.common.collect.ForwardingList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.hubspot.jinjava.Jinjava; @@ -331,6 +334,43 @@ public void blackListedMethods() throws Exception { assertThat(e.getMessage()).contains("Cannot find method 'wait'"); } + @Test(expected = MethodNotFoundException.class) + public void itBlocksDisabledTags() throws Exception { + + Map> disabled = ImmutableMap.of(Context.Library.TAG, ImmutableSet.of("raw")); + assertThat(interpreter.render("{% raw %}foo{% endraw %}")).isEqualTo("foo"); + + try (JinjavaInterpreter.InterpreterScopeClosable c = interpreter.enterScope(disabled)) { + interpreter.render("{% raw %} foo {% endraw %}"); + } + } + + @Test + public void itBlocksDisabledFilters() throws Exception { + + Map> disabled = ImmutableMap.of(Context.Library.FILTER, ImmutableSet.of("truncate")); + assertThat(interpreter.resolveELExpression("\"hey\"|truncate(2)", -1)).isEqualTo("h..."); + + try (JinjavaInterpreter.InterpreterScopeClosable c = interpreter.enterScope(disabled)) { + interpreter.resolveELExpression("\"hey\"|truncate(2)", -1); + TemplateError e = interpreter.getErrors().get(0); + assertThat(e.getMessage()).contains("truncate' is disabled in this context"); + } + } + + @Test + public void itBlocksDisabledExpTests() throws Exception { + + Map> disabled = ImmutableMap.of(Context.Library.EXP_TEST, ImmutableSet.of("even")); + assertThat(interpreter.render("{% if 2 is even %}yes{% endif %}")).isEqualTo("yes"); + + try (JinjavaInterpreter.InterpreterScopeClosable c = interpreter.enterScope(disabled)) { + interpreter.render("{% if 2 is even %}yes{% endif %}"); + TemplateError e = interpreter.getErrors().get(0); + assertThat(e.getMessage()).contains("even' is disabled in this context"); + } + } + @Test public void presentOptionalProperty() { context.put("myobj", new OptionalProperty(null, "foo")); From 3f00606094134d7f3db3509074c2bd7b56d70134 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 31 Oct 2016 10:52:10 -0400 Subject: [PATCH 0194/2465] test disabled tags in includes --- .../hubspot/jinjava/el/ExpressionResolverTest.java | 14 ++++++++++++++ src/test/resources/tags/includetag/raw.html | 1 + 2 files changed, 15 insertions(+) create mode 100644 src/test/resources/tags/includetag/raw.html diff --git a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java index 92800d14c..78fe590ba 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java @@ -23,6 +23,7 @@ import com.google.common.collect.Maps; import com.hubspot.jinjava.Jinjava; import com.hubspot.jinjava.interpret.Context; +import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.TemplateError; import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; @@ -345,6 +346,19 @@ public void itBlocksDisabledTags() throws Exception { } } + @Test(expected = InterpretException.class) + public void itBlocksDisabledTagsInIncludes() throws Exception { + + final String jinja = "top {% include \"tags/includetag/raw.html\" %}"; + + Map> disabled = ImmutableMap.of(Context.Library.TAG, ImmutableSet.of("raw")); + assertThat(interpreter.render(jinja)).isEqualTo("top before raw after\n"); + + try (JinjavaInterpreter.InterpreterScopeClosable c = interpreter.enterScope(disabled)) { + interpreter.render(jinja); + } + } + @Test public void itBlocksDisabledFilters() throws Exception { diff --git a/src/test/resources/tags/includetag/raw.html b/src/test/resources/tags/includetag/raw.html new file mode 100644 index 000000000..1a85407de --- /dev/null +++ b/src/test/resources/tags/includetag/raw.html @@ -0,0 +1 @@ +before{% raw %} raw{% endraw %} after From ed8acdd489251159c785b8a523f4a3c36b387965 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 31 Oct 2016 16:47:53 -0400 Subject: [PATCH 0195/2465] support for disabling functions just like we can disable tags, filters and expression tests --- .../java/com/hubspot/jinjava/Jinjava.java | 2 +- .../com/hubspot/jinjava/JinjavaConfig.java | 26 +++++++++-- .../jinjava/el/ExpressionResolver.java | 4 +- .../jinjava/el/MacroFunctionMapper.java | 8 +++- .../hubspot/jinjava/interpret/Context.java | 19 +++++--- .../jinjava/el/ExpressionResolverTest.java | 45 +++++++++++++++---- 6 files changed, 82 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/Jinjava.java b/src/main/java/com/hubspot/jinjava/Jinjava.java index e62e6983f..a76366017 100644 --- a/src/main/java/com/hubspot/jinjava/Jinjava.java +++ b/src/main/java/com/hubspot/jinjava/Jinjava.java @@ -186,7 +186,7 @@ public RenderResult renderForResult(String template, Map bindings) { * @return result object containing rendered output, render context, and any encountered errors */ public RenderResult renderForResult(String template, Map bindings, JinjavaConfig renderConfig) { - Context context = new Context(globalContext, bindings, null); + Context context = new Context(globalContext, bindings, renderConfig.getDisabled()); JinjavaInterpreter parentInterpreter = JinjavaInterpreter.getCurrent(); if (parentInterpreter != null) { diff --git a/src/main/java/com/hubspot/jinjava/JinjavaConfig.java b/src/main/java/com/hubspot/jinjava/JinjavaConfig.java index 78264d731..7127ca997 100644 --- a/src/main/java/com/hubspot/jinjava/JinjavaConfig.java +++ b/src/main/java/com/hubspot/jinjava/JinjavaConfig.java @@ -19,7 +19,13 @@ import java.nio.charset.StandardCharsets; import java.time.ZoneId; import java.time.ZoneOffset; +import java.util.HashMap; import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import com.hubspot.jinjava.interpret.Context; +import com.hubspot.jinjava.interpret.Context.Library; public class JinjavaConfig { @@ -33,22 +39,25 @@ public class JinjavaConfig { private final boolean readOnlyResolver; + private Map> disabled; + public static Builder newBuilder() { return new Builder(); } public JinjavaConfig() { - this(StandardCharsets.UTF_8, Locale.ENGLISH, ZoneOffset.UTC, 10, false, false, true); + this(StandardCharsets.UTF_8, Locale.ENGLISH, ZoneOffset.UTC, 10, new HashMap<>(), false, false, true); } public JinjavaConfig(Charset charset, Locale locale, ZoneId timeZone, int maxRenderDepth) { - this(charset, locale, timeZone, maxRenderDepth, false, false, true); + this(charset, locale, timeZone, maxRenderDepth, new HashMap<>(), false, false, true); } private JinjavaConfig(Charset charset, Locale locale, ZoneId timeZone, int maxRenderDepth, + Map> disabled, boolean trimBlocks, boolean lstripBlocks, boolean readOnlyResolver) { @@ -56,6 +65,7 @@ private JinjavaConfig(Charset charset, this.locale = locale; this.timeZone = timeZone; this.maxRenderDepth = maxRenderDepth; + this.disabled = disabled; this.trimBlocks = trimBlocks; this.lstripBlocks = lstripBlocks; this.readOnlyResolver = readOnlyResolver; @@ -89,11 +99,16 @@ public boolean isReadOnlyResolver() { return readOnlyResolver; } + public Map> getDisabled() { + return disabled; + } + public static class Builder { private Charset charset = StandardCharsets.UTF_8; private Locale locale = Locale.ENGLISH; private ZoneId timeZone = ZoneOffset.UTC; private int maxRenderDepth = 10; + private Map> disabled = new HashMap<>(); private boolean trimBlocks; private boolean lstripBlocks; @@ -117,6 +132,11 @@ public Builder withTimeZone(ZoneId timeZone) { return this; } + public Builder withDisabled(Map> disabled) { + this.disabled = disabled; + return this; + } + public Builder withMaxRenderDepth(int maxRenderDepth) { this.maxRenderDepth = maxRenderDepth; return this; @@ -138,7 +158,7 @@ public Builder withReadOnlyResolver(boolean readOnlyResolver) { } public JinjavaConfig build() { - return new JinjavaConfig(charset, locale, timeZone, maxRenderDepth, trimBlocks, lstripBlocks, readOnlyResolver); + return new JinjavaConfig(charset, locale, timeZone, maxRenderDepth, disabled, trimBlocks, lstripBlocks, readOnlyResolver); } } diff --git a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java index 18d8d4fac..0485825f8 100644 --- a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java @@ -9,10 +9,9 @@ import javax.el.PropertyNotFoundException; import javax.el.ValueExpression; -import com.google.common.collect.ImmutableMap; -import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory; import org.apache.commons.lang3.StringUtils; +import com.google.common.collect.ImmutableMap; import com.hubspot.jinjava.el.ext.NamedParameter; import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; @@ -21,6 +20,7 @@ import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; import com.hubspot.jinjava.interpret.TemplateError.ErrorType; import com.hubspot.jinjava.interpret.TemplateSyntaxException; +import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory; import com.hubspot.jinjava.lib.fn.ELFunctionDefinition; import de.odysseus.el.tree.TreeBuilderException; diff --git a/src/main/java/com/hubspot/jinjava/el/MacroFunctionMapper.java b/src/main/java/com/hubspot/jinjava/el/MacroFunctionMapper.java index c697cdf67..f52721e40 100644 --- a/src/main/java/com/hubspot/jinjava/el/MacroFunctionMapper.java +++ b/src/main/java/com/hubspot/jinjava/el/MacroFunctionMapper.java @@ -15,6 +15,10 @@ public class MacroFunctionMapper extends FunctionMapper { private Map map = Collections.emptyMap(); + private static String buildFunctionName(String prefix, String name) { + return prefix + ":" + name; + } + @Override public Method resolveFunction(String prefix, String localName) { MacroFunction macroFunction = JinjavaInterpreter.getCurrent().getContext().getGlobalMacro(localName); @@ -23,14 +27,14 @@ public Method resolveFunction(String prefix, String localName) { return AbstractCallableMethod.EVAL_METHOD; } - return map.get(prefix + ":" + localName); + return map.get(buildFunctionName(prefix, localName)); } public void setFunction(String prefix, String localName, Method method) { if (map.isEmpty()) { map = new HashMap(); } - map.put(prefix + ":" + localName, method); + map.put(buildFunctionName(prefix, localName), method); } } diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index 3e34975ce..2d80b82db 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableSet; @@ -43,10 +44,12 @@ public class Context extends ScopeMap { public static final String GLOBAL_MACROS_SCOPE_KEY = "__macros__"; private final SetMultimap dependencies = HashMultimap.create(); + private Map> disabled; public enum Library { EXP_TEST, FILTER, + FUNCTION, TAG } @@ -79,6 +82,7 @@ public Context(Context parent) { public Context(Context parent, Map bindings, Map> disabled) { super(parent); + this.disabled = disabled; if (bindings != null) { this.putAll(bindings); @@ -86,9 +90,12 @@ public Context(Context parent, Map bindings, Map this.parent = parent; - this.extendPathStack = new CallStack(parent == null ? null : parent.getExtendPathStack(), ExtendsTagCycleException.class); - this.importPathStack = new CallStack(parent == null ? null : parent.getImportPathStack(), ImportTagCycleException.class); - this.includePathStack = new CallStack(parent == null ? null : parent.getIncludePathStack(), IncludeTagCycleException.class); + this.extendPathStack = new CallStack(parent == null ? null : parent.getExtendPathStack(), + ExtendsTagCycleException.class); + this.importPathStack = new CallStack(parent == null ? null : parent.getImportPathStack(), + ImportTagCycleException.class); + this.includePathStack = new CallStack(parent == null ? null : parent.getIncludePathStack(), + IncludeTagCycleException.class); this.macroStack = new CallStack(parent == null ? null : parent.getMacroStack(), MacroTagCycleException.class); if (disabled == null) { @@ -98,7 +105,7 @@ public Context(Context parent, Map bindings, Map this.expTestLibrary = new ExpTestLibrary(parent == null, disabled.get(Library.EXP_TEST)); this.filterLibrary = new FilterLibrary(parent == null, disabled.get(Library.FILTER)); this.tagLibrary = new TagLibrary(parent == null, disabled.get(Library.TAG)); - this.functionLibrary = new FunctionLibrary(parent == null, null); + this.functionLibrary = new FunctionLibrary(parent == null, disabled.get(Library.FUNCTION)); } @Override @@ -282,7 +289,9 @@ public Collection getAllFunctions() { fns.addAll(parent.getAllFunctions()); } - return fns; + final Set disabledFunctions = disabled == null ? new HashSet<>() : disabled.getOrDefault(Library.FUNCTION, + new HashSet<>()); + return fns.stream().filter(f -> !disabledFunctions.contains(f.getName())).collect(Collectors.toList()); } public void registerFunction(ELFunctionDefinition f) { diff --git a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java index 78fe590ba..008c99fda 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java @@ -1,6 +1,7 @@ package com.hubspot.jinjava.el; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; import java.math.BigDecimal; import java.util.Collection; @@ -22,9 +23,12 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.hubspot.jinjava.Jinjava; +import com.hubspot.jinjava.JinjavaConfig; import com.hubspot.jinjava.interpret.Context; +import com.hubspot.jinjava.interpret.Context.Library; import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.interpret.RenderResult; import com.hubspot.jinjava.interpret.TemplateError; import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; import com.hubspot.jinjava.objects.PyWrapper; @@ -35,10 +39,12 @@ public class ExpressionResolverTest { private JinjavaInterpreter interpreter; private Context context; + private Jinjava jinjava; @Before public void setup() { - interpreter = new Jinjava().newInterpreter(); + jinjava = new Jinjava(); + interpreter = jinjava.newInterpreter(); context = interpreter.getContext(); } @@ -66,7 +72,8 @@ public void testTuplesAreImmutable() throws Exception { @Test public void itCanCompareStrings() throws Exception { context.put("foo", "white"); - assertThat(interpreter.resolveELExpression("'2013-12-08 16:00:00+00:00' > '2013-12-08 13:00:00+00:00'", -1)).isEqualTo(Boolean.TRUE); + assertThat(interpreter.resolveELExpression("'2013-12-08 16:00:00+00:00' > '2013-12-08 13:00:00+00:00'", + -1)).isEqualTo(Boolean.TRUE); assertThat(interpreter.resolveELExpression("foo == \"white\"", -1)).isEqualTo(Boolean.TRUE); } @@ -144,7 +151,7 @@ public void itResolvesOtherMethodsOnCustomMapObject() throws Exception { Object val2 = interpreter.resolveELExpression("thedict.items()", -1); assertThat(val2.toString()).isEqualTo("[foo=bar, two=2, size=777]"); - } + } public static final class MyCustomMap implements Map { @@ -338,7 +345,7 @@ public void blackListedMethods() throws Exception { @Test(expected = MethodNotFoundException.class) public void itBlocksDisabledTags() throws Exception { - Map> disabled = ImmutableMap.of(Context.Library.TAG, ImmutableSet.of("raw")); + Map> disabled = ImmutableMap.of(Context.Library.TAG, ImmutableSet.of("raw")); assertThat(interpreter.render("{% raw %}foo{% endraw %}")).isEqualTo("foo"); try (JinjavaInterpreter.InterpreterScopeClosable c = interpreter.enterScope(disabled)) { @@ -351,7 +358,7 @@ public void itBlocksDisabledTagsInIncludes() throws Exception { final String jinja = "top {% include \"tags/includetag/raw.html\" %}"; - Map> disabled = ImmutableMap.of(Context.Library.TAG, ImmutableSet.of("raw")); + Map> disabled = ImmutableMap.of(Context.Library.TAG, ImmutableSet.of("raw")); assertThat(interpreter.render(jinja)).isEqualTo("top before raw after\n"); try (JinjavaInterpreter.InterpreterScopeClosable c = interpreter.enterScope(disabled)) { @@ -362,7 +369,7 @@ public void itBlocksDisabledTagsInIncludes() throws Exception { @Test public void itBlocksDisabledFilters() throws Exception { - Map> disabled = ImmutableMap.of(Context.Library.FILTER, ImmutableSet.of("truncate")); + Map> disabled = ImmutableMap.of(Context.Library.FILTER, ImmutableSet.of("truncate")); assertThat(interpreter.resolveELExpression("\"hey\"|truncate(2)", -1)).isEqualTo("h..."); try (JinjavaInterpreter.InterpreterScopeClosable c = interpreter.enterScope(disabled)) { @@ -372,10 +379,28 @@ public void itBlocksDisabledFilters() throws Exception { } } + @Test + public void itBlocksDisabledFunctions() throws Exception { + + Map> disabled = ImmutableMap.of(Library.FUNCTION, ImmutableSet.of(":range")); + + String template = "hi {% for i in range(1, 3) %}{{i}} {% endfor %}"; + + String rendered = jinjava.render(template, context); + assertEquals("hi 1 2 ", rendered); + + final JinjavaConfig config = JinjavaConfig.newBuilder().withDisabled(disabled).build(); + + final RenderResult renderResult = jinjava.renderForResult(template, context, config); + assertEquals("hi ", renderResult.getOutput()); + TemplateError e = renderResult.getErrors().get(0); + assertThat(e.getMessage()).contains("Could not resolve function 'range'"); + } + @Test public void itBlocksDisabledExpTests() throws Exception { - Map> disabled = ImmutableMap.of(Context.Library.EXP_TEST, ImmutableSet.of("even")); + Map> disabled = ImmutableMap.of(Context.Library.EXP_TEST, ImmutableSet.of("even")); assertThat(interpreter.render("{% if 2 is even %}yes{% endif %}")).isEqualTo("yes"); try (JinjavaInterpreter.InterpreterScopeClosable c = interpreter.enterScope(disabled)) { @@ -402,7 +427,8 @@ public void emptyOptionalProperty() { @Test public void presentNestedOptionalProperty() { context.put("myobj", new OptionalProperty(new MyClass(new Date(0)), "foo")); - assertThat(Objects.toString(interpreter.resolveELExpression("myobj.nested.date", -1))).isEqualTo("1970-01-01 00:00:00"); + assertThat(Objects.toString(interpreter.resolveELExpression("myobj.nested.date", -1))).isEqualTo( + "1970-01-01 00:00:00"); assertThat(interpreter.getErrors()).isEmpty(); } @@ -416,7 +442,8 @@ public void emptyNestedOptionalProperty() { @Test public void presentNestedNestedOptionalProperty() { context.put("myobj", new NestedOptionalProperty(new OptionalProperty(new MyClass(new Date(0)), "foo"))); - assertThat(Objects.toString(interpreter.resolveELExpression("myobj.nested.nested.date", -1))).isEqualTo("1970-01-01 00:00:00"); + assertThat(Objects.toString(interpreter.resolveELExpression("myobj.nested.nested.date", -1))).isEqualTo( + "1970-01-01 00:00:00"); assertThat(interpreter.getErrors()).isEmpty(); } From 2a8caa2baf1f4d6f6f944f311cfc99a8980a70a7 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 31 Oct 2016 17:28:30 -0400 Subject: [PATCH 0196/2465] restore parent, bindings constructor for Context --- src/main/java/com/hubspot/jinjava/interpret/Context.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index 2d80b82db..f322de68a 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -80,6 +80,10 @@ public Context(Context parent) { this(parent, null, null); } + public Context(Context parent, Map bindings) { + this(parent, bindings, null); + } + public Context(Context parent, Map bindings, Map> disabled) { super(parent); this.disabled = disabled; From 0d69fd093945c798eb115a055a37ec014909b79a Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 1 Nov 2016 14:02:40 -0400 Subject: [PATCH 0197/2465] throw exception on disabled function --- .../com/hubspot/jinjava/el/MacroFunctionMapper.java | 13 +++++++++++-- .../java/com/hubspot/jinjava/interpret/Context.java | 5 +++++ .../java/com/hubspot/jinjava/lib/SimpleLibrary.java | 2 +- .../hubspot/jinjava/el/ExpressionResolverTest.java | 2 +- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/MacroFunctionMapper.java b/src/main/java/com/hubspot/jinjava/el/MacroFunctionMapper.java index f52721e40..0456fe5b5 100644 --- a/src/main/java/com/hubspot/jinjava/el/MacroFunctionMapper.java +++ b/src/main/java/com/hubspot/jinjava/el/MacroFunctionMapper.java @@ -6,8 +6,10 @@ import java.util.Map; import javax.el.FunctionMapper; +import javax.el.MethodNotFoundException; import com.hubspot.jinjava.el.ext.AbstractCallableMethod; +import com.hubspot.jinjava.interpret.Context; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.lib.fn.MacroFunction; @@ -21,13 +23,20 @@ private static String buildFunctionName(String prefix, String name) { @Override public Method resolveFunction(String prefix, String localName) { - MacroFunction macroFunction = JinjavaInterpreter.getCurrent().getContext().getGlobalMacro(localName); + final Context context = JinjavaInterpreter.getCurrent().getContext(); + MacroFunction macroFunction = context.getGlobalMacro(localName); if (macroFunction != null) { return AbstractCallableMethod.EVAL_METHOD; } - return map.get(buildFunctionName(prefix, localName)); + final String functionName = buildFunctionName(prefix, localName); + + if (context.isFunctionDisabled(functionName)) { + throw new MethodNotFoundException("'" + functionName + "' is disabled in this context"); + } + + return map.get(functionName); } public void setFunction(String prefix, String localName, Method method) { diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index f322de68a..23691b68e 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -17,6 +17,7 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -275,6 +276,10 @@ public void registerFilter(Filter f) { filterLibrary.addFilter(f); } + public boolean isFunctionDisabled(String name) { + return disabled != null && disabled.getOrDefault(Library.FUNCTION, Collections.emptySet()).contains(name); + } + public ELFunctionDefinition getFunction(String name) { ELFunctionDefinition f = functionLibrary.getFunction(name); if (f != null) { diff --git a/src/main/java/com/hubspot/jinjava/lib/SimpleLibrary.java b/src/main/java/com/hubspot/jinjava/lib/SimpleLibrary.java index aa57aa061..03e01a4cd 100644 --- a/src/main/java/com/hubspot/jinjava/lib/SimpleLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/SimpleLibrary.java @@ -55,7 +55,7 @@ protected SimpleLibrary(boolean registerDefaults, Set disabled) { public T fetch(String item) { if (disabled.contains(item)) { - throw new MethodNotFoundException("'" + item + "' is disabled in this context "); + throw new MethodNotFoundException("'" + item + "' is disabled in this context"); } return lib.get(StringUtils.lowerCase(item)); diff --git a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java index 008c99fda..a476f47de 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java @@ -394,7 +394,7 @@ public void itBlocksDisabledFunctions() throws Exception { final RenderResult renderResult = jinjava.renderForResult(template, context, config); assertEquals("hi ", renderResult.getOutput()); TemplateError e = renderResult.getErrors().get(0); - assertThat(e.getMessage()).contains("Could not resolve function 'range'"); + assertThat(e.getMessage()).contains("':range' is disabled in this context"); } @Test From ca639215115e1b7870be3242fa2a4f8b956da627 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Wed, 2 Nov 2016 17:52:52 -0400 Subject: [PATCH 0198/2465] Update CHANGES.md --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 7f1ef1fe4..a1982c4de 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Jinjava Releases # +### Version 2.1.13 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.13%22)) ### + +* Added support for disabling specific functions, filters and tags + ### Version 2.1.12 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.12%22)) ### * Added ** and // operators From fe404b9a38f647871c359dba49f4b77992b6d3c1 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Wed, 2 Nov 2016 21:56:25 +0000 Subject: [PATCH 0199/2465] [maven-release-plugin] prepare release jinjava-2.1.13 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 4765145d7..c7c559efd 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.13-SNAPSHOT + 2.1.13 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.1.13 From 67e8389d6b0adfeb19fc91f20bb14bb0374a9d34 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Wed, 2 Nov 2016 21:56:25 +0000 Subject: [PATCH 0200/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c7c559efd..0315a9f23 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.13 + 2.1.14-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.1.13 + HEAD From 947e87caea407d07c390c61f6f708c5e0f9aca75 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 7 Nov 2016 17:09:20 -0500 Subject: [PATCH 0201/2465] add function and filter to get unix timestamps --- .../jinjava/lib/filter/FilterLibrary.java | 1 + .../lib/filter/UnixTimestampFilter.java | 29 ++++++++++++ .../jinjava/lib/fn/FunctionLibrary.java | 1 + .../com/hubspot/jinjava/lib/fn/Functions.java | 44 ++++++++++++++----- .../lib/filter/DateTimeFormatFilterTest.java | 1 - .../lib/filter/UnixTimestampFilterTest.java | 37 ++++++++++++++++ .../lib/fn/UnixTimestampFunctionTest.java | 19 ++++++++ 7 files changed, 119 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilter.java create mode 100644 src/test/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilterTest.java create mode 100644 src/test/java/com/hubspot/jinjava/lib/fn/UnixTimestampFunctionTest.java diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java b/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java index 81004cbb1..e7b308b6f 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java @@ -58,6 +58,7 @@ protected void registerDefaults() { DatetimeFilter.class, DateTimeFormatFilter.class, + UnixTimestampFilter.class, AbsFilter.class, AddFilter.class, diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilter.java new file mode 100644 index 000000000..1eba42bdb --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilter.java @@ -0,0 +1,29 @@ +package com.hubspot.jinjava.lib.filter; + +import com.hubspot.jinjava.doc.annotations.JinjavaDoc; +import com.hubspot.jinjava.doc.annotations.JinjavaParam; +import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.lib.fn.Functions; + +@JinjavaDoc( + value = "gets the unix timestamp value (in millseconds) of a date object", + params = { + @JinjavaParam(value = "value", defaultValue = "current time", desc = "The date variable"), + }, + snippets = { + @JinjavaSnippet(code = "{% local_dt|unixtimestamp"), + }) +public class UnixTimestampFilter implements Filter { + + @Override + public String getName() { + return "unixtimestamp"; + } + + @Override + public Object filter(Object var, JinjavaInterpreter interpreter, String... args) { + return Functions.getUnixTimestamp(var); + } + +} diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/FunctionLibrary.java b/src/main/java/com/hubspot/jinjava/lib/fn/FunctionLibrary.java index e66ae161b..2bdd384cd 100644 --- a/src/main/java/com/hubspot/jinjava/lib/fn/FunctionLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/FunctionLibrary.java @@ -14,6 +14,7 @@ public FunctionLibrary(boolean registerDefaults, Set disabled) { @Override protected void registerDefaults() { register(new ELFunctionDefinition("", "datetimeformat", Functions.class, "dateTimeFormat", Object.class, String[].class)); + register(new ELFunctionDefinition("", "unixtimestamp", Functions.class, "getUnixTimestamp", Object.class)); register(new ELFunctionDefinition("", "truncate", Functions.class, "truncate", Object.class, Object[].class)); register(new ELFunctionDefinition("", "range", Functions.class, "range", Object.class, Object[].class)); diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java index 26a67af40..6bb94bf65 100644 --- a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java @@ -59,6 +59,26 @@ public static List immutableListOf(Object... items) { @JinjavaParam(value = "format", defaultValue = StrftimeFormatter.DEFAULT_DATE_FORMAT) }) public static String dateTimeFormat(Object var, String... format) { + ZonedDateTime d = getDateTimeArg(var); + + if (d == null) { + return ""; + } + + Locale locale = JinjavaInterpreter.getCurrentMaybe() + .map(JinjavaInterpreter::getConfig) + .map(JinjavaConfig::getLocale) + .orElse(Locale.ENGLISH); + + if (format.length > 0) { + return StrftimeFormatter.format(d, format[0], locale); + } else { + return StrftimeFormatter.format(d, locale); + } + } + + private static ZonedDateTime getDateTimeArg(Object var) { + ZonedDateTime d = null; if (var == null) { @@ -70,23 +90,23 @@ public static String dateTimeFormat(Object var, String... format) { } else if (var instanceof ZonedDateTime) { d = (ZonedDateTime) var; } else if (!ZonedDateTime.class.isAssignableFrom(var.getClass())) { - throw new InterpretException("Input to datetimeformat function must be a date object, was: " + var.getClass()); + throw new InterpretException("Input to function must be a date object, was: " + var.getClass()); } - if (d == null) { - return ""; - } + return d; + } - Locale locale = JinjavaInterpreter.getCurrentMaybe() - .map(JinjavaInterpreter::getConfig) - .map(JinjavaConfig::getLocale) - .orElse(Locale.ENGLISH); + @JinjavaDoc(value = "gets the unix timestamp milliseconds value of a datetime", params = { + @JinjavaParam(value = "var", type = "date", defaultValue = "current time"), + }) + public static Object getUnixTimestamp(Object var) { + ZonedDateTime d = getDateTimeArg(var); - if (format.length > 0) { - return StrftimeFormatter.format(d, format[0], locale); - } else { - return StrftimeFormatter.format(d, locale); + if (d == null) { + return 0L; } + + return d.toEpochSecond() * 1000; } private static final int DEFAULT_TRUNCATE_LENGTH = 255; diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilterTest.java index 6a5f71e8f..1121778cf 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilterTest.java @@ -52,7 +52,6 @@ public void itUsesSpecifiedFormatString() throws Exception { public void itHandlesVarsAndLiterals() throws Exception { interpreter.getContext().put("d", d); interpreter.getContext().put("foo", "%Y-%m"); - assertThat(interpreter.renderFlat("{{ d|datetimeformat(foo) }}")).isEqualTo("2013-11"); assertThat(interpreter.renderFlat("{{ d|datetimeformat(\"%Y-%m-%d\") }}")).isEqualTo("2013-11-06"); assertThat(interpreter.getErrors()).isEmpty(); diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilterTest.java new file mode 100644 index 000000000..ff4b8abf6 --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilterTest.java @@ -0,0 +1,37 @@ +package com.hubspot.jinjava.lib.filter; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.ZonedDateTime; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.hubspot.jinjava.Jinjava; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; + +public class UnixTimestampFilterTest { + + JinjavaInterpreter interpreter; + + private final ZonedDateTime d = ZonedDateTime.parse("2013-11-06T14:22:00.000+00:00[UTC]"); + private final String timestamp = new Long(d.toEpochSecond() * 1000).toString(); + + @Before + public void setup() { + interpreter = new Jinjava().newInterpreter(); + interpreter.getContext().put("d", d); + } + + @After + public void tearDown() throws Exception { + assertThat(interpreter.getErrors()).isEmpty(); + } + + @Test + public void itRendersFromDate() throws Exception { + assertThat(interpreter.renderFlat("{{ d|unixtimestamp }}")).isEqualTo(timestamp); + } + +} diff --git a/src/test/java/com/hubspot/jinjava/lib/fn/UnixTimestampFunctionTest.java b/src/test/java/com/hubspot/jinjava/lib/fn/UnixTimestampFunctionTest.java new file mode 100644 index 000000000..e92cc6f6f --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/lib/fn/UnixTimestampFunctionTest.java @@ -0,0 +1,19 @@ +package com.hubspot.jinjava.lib.fn; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.ZonedDateTime; + +import org.junit.Test; + +public class UnixTimestampFunctionTest { + + private final ZonedDateTime d = ZonedDateTime.parse("2013-11-06T14:22:00.000+00:00[UTC]"); + + @Test + public void itGetsUnixTimestamps() { + assertThat(Functions.getUnixTimestamp(d.toEpochSecond() * 1000)).isEqualTo(d.toEpochSecond() * 1000); + assertThat(Functions.getUnixTimestamp(null)).isEqualTo(ZonedDateTime.now().toEpochSecond() * 1000); + } + +} From 784714194950df3903d1b5aa15072ab421b29fd9 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 7 Nov 2016 17:14:26 -0500 Subject: [PATCH 0202/2465] test getting timestamp from datetime object --- .../com/hubspot/jinjava/lib/filter/UnixTimestampFilterTest.java | 2 +- .../com/hubspot/jinjava/lib/fn/UnixTimestampFunctionTest.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilterTest.java index ff4b8abf6..6e0835bac 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilterTest.java @@ -16,7 +16,7 @@ public class UnixTimestampFilterTest { JinjavaInterpreter interpreter; private final ZonedDateTime d = ZonedDateTime.parse("2013-11-06T14:22:00.000+00:00[UTC]"); - private final String timestamp = new Long(d.toEpochSecond() * 1000).toString(); + private final String timestamp = Long.toString(d.toEpochSecond() * 1000); @Before public void setup() { diff --git a/src/test/java/com/hubspot/jinjava/lib/fn/UnixTimestampFunctionTest.java b/src/test/java/com/hubspot/jinjava/lib/fn/UnixTimestampFunctionTest.java index e92cc6f6f..d655dea9b 100644 --- a/src/test/java/com/hubspot/jinjava/lib/fn/UnixTimestampFunctionTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/fn/UnixTimestampFunctionTest.java @@ -13,6 +13,7 @@ public class UnixTimestampFunctionTest { @Test public void itGetsUnixTimestamps() { assertThat(Functions.getUnixTimestamp(d.toEpochSecond() * 1000)).isEqualTo(d.toEpochSecond() * 1000); + assertThat(Functions.getUnixTimestamp(d)).isEqualTo(d.toEpochSecond() * 1000); assertThat(Functions.getUnixTimestamp(null)).isEqualTo(ZonedDateTime.now().toEpochSecond() * 1000); } From 8363502ffadcbdfa8a9a8b58179babd4f679781d Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 7 Nov 2016 17:20:26 -0500 Subject: [PATCH 0203/2465] spelling --- .../com/hubspot/jinjava/lib/filter/UnixTimestampFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilter.java index 1eba42bdb..07417b11e 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilter.java @@ -7,7 +7,7 @@ import com.hubspot.jinjava.lib.fn.Functions; @JinjavaDoc( - value = "gets the unix timestamp value (in millseconds) of a date object", + value = "gets the unix timestamp value (in milliseconds) of a date object", params = { @JinjavaParam(value = "value", defaultValue = "current time", desc = "The date variable"), }, From d15561bd5418386ec01061357082d3baf4513993 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 7 Nov 2016 17:22:16 -0500 Subject: [PATCH 0204/2465] rename function --- .../com/hubspot/jinjava/lib/filter/UnixTimestampFilter.java | 2 +- .../java/com/hubspot/jinjava/lib/fn/FunctionLibrary.java | 2 +- src/main/java/com/hubspot/jinjava/lib/fn/Functions.java | 2 +- .../hubspot/jinjava/lib/fn/UnixTimestampFunctionTest.java | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilter.java index 07417b11e..a3cc7cf5c 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilter.java @@ -23,7 +23,7 @@ public String getName() { @Override public Object filter(Object var, JinjavaInterpreter interpreter, String... args) { - return Functions.getUnixTimestamp(var); + return Functions.unixtimestamp(var); } } diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/FunctionLibrary.java b/src/main/java/com/hubspot/jinjava/lib/fn/FunctionLibrary.java index 2bdd384cd..f28524a10 100644 --- a/src/main/java/com/hubspot/jinjava/lib/fn/FunctionLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/FunctionLibrary.java @@ -14,7 +14,7 @@ public FunctionLibrary(boolean registerDefaults, Set disabled) { @Override protected void registerDefaults() { register(new ELFunctionDefinition("", "datetimeformat", Functions.class, "dateTimeFormat", Object.class, String[].class)); - register(new ELFunctionDefinition("", "unixtimestamp", Functions.class, "getUnixTimestamp", Object.class)); + register(new ELFunctionDefinition("", "unixtimestamp", Functions.class, "unixtimestamp", Object.class)); register(new ELFunctionDefinition("", "truncate", Functions.class, "truncate", Object.class, Object[].class)); register(new ELFunctionDefinition("", "range", Functions.class, "range", Object.class, Object[].class)); diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java index 6bb94bf65..2886fbde2 100644 --- a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java @@ -99,7 +99,7 @@ private static ZonedDateTime getDateTimeArg(Object var) { @JinjavaDoc(value = "gets the unix timestamp milliseconds value of a datetime", params = { @JinjavaParam(value = "var", type = "date", defaultValue = "current time"), }) - public static Object getUnixTimestamp(Object var) { + public static Object unixtimestamp(Object var) { ZonedDateTime d = getDateTimeArg(var); if (d == null) { diff --git a/src/test/java/com/hubspot/jinjava/lib/fn/UnixTimestampFunctionTest.java b/src/test/java/com/hubspot/jinjava/lib/fn/UnixTimestampFunctionTest.java index d655dea9b..b5f956656 100644 --- a/src/test/java/com/hubspot/jinjava/lib/fn/UnixTimestampFunctionTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/fn/UnixTimestampFunctionTest.java @@ -12,9 +12,9 @@ public class UnixTimestampFunctionTest { @Test public void itGetsUnixTimestamps() { - assertThat(Functions.getUnixTimestamp(d.toEpochSecond() * 1000)).isEqualTo(d.toEpochSecond() * 1000); - assertThat(Functions.getUnixTimestamp(d)).isEqualTo(d.toEpochSecond() * 1000); - assertThat(Functions.getUnixTimestamp(null)).isEqualTo(ZonedDateTime.now().toEpochSecond() * 1000); + assertThat(Functions.unixtimestamp(d.toEpochSecond() * 1000)).isEqualTo(d.toEpochSecond() * 1000); + assertThat(Functions.unixtimestamp(d)).isEqualTo(d.toEpochSecond() * 1000); + assertThat(Functions.unixtimestamp(null)).isEqualTo(ZonedDateTime.now().toEpochSecond() * 1000); } } From 3a259a50e53ccaadc86b81aea89e446ef027ef7e Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 7 Nov 2016 17:23:04 -0500 Subject: [PATCH 0205/2465] return long --- src/main/java/com/hubspot/jinjava/lib/fn/Functions.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java index 2886fbde2..25e5e2c37 100644 --- a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java @@ -99,11 +99,11 @@ private static ZonedDateTime getDateTimeArg(Object var) { @JinjavaDoc(value = "gets the unix timestamp milliseconds value of a datetime", params = { @JinjavaParam(value = "var", type = "date", defaultValue = "current time"), }) - public static Object unixtimestamp(Object var) { + public static long unixtimestamp(Object var) { ZonedDateTime d = getDateTimeArg(var); if (d == null) { - return 0L; + return 0; } return d.toEpochSecond() * 1000; From 9c8d3f644937314d1de57fac1d411def8e1f2079 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 7 Nov 2016 17:25:14 -0500 Subject: [PATCH 0206/2465] dry it up --- .../hubspot/jinjava/lib/fn/UnixTimestampFunctionTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/hubspot/jinjava/lib/fn/UnixTimestampFunctionTest.java b/src/test/java/com/hubspot/jinjava/lib/fn/UnixTimestampFunctionTest.java index b5f956656..3d3263fe9 100644 --- a/src/test/java/com/hubspot/jinjava/lib/fn/UnixTimestampFunctionTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/fn/UnixTimestampFunctionTest.java @@ -9,11 +9,12 @@ public class UnixTimestampFunctionTest { private final ZonedDateTime d = ZonedDateTime.parse("2013-11-06T14:22:00.000+00:00[UTC]"); + private final long epochMilliseconds = d.toEpochSecond() * 1000; @Test public void itGetsUnixTimestamps() { - assertThat(Functions.unixtimestamp(d.toEpochSecond() * 1000)).isEqualTo(d.toEpochSecond() * 1000); - assertThat(Functions.unixtimestamp(d)).isEqualTo(d.toEpochSecond() * 1000); + assertThat(Functions.unixtimestamp(epochMilliseconds)).isEqualTo(epochMilliseconds); + assertThat(Functions.unixtimestamp(d)).isEqualTo(epochMilliseconds); assertThat(Functions.unixtimestamp(null)).isEqualTo(ZonedDateTime.now().toEpochSecond() * 1000); } From 1c7d3f92acdb0cdd06126dfb19ed0e6402a1877c Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 7 Nov 2016 17:27:31 -0500 Subject: [PATCH 0207/2465] undo newline --- .../com/hubspot/jinjava/lib/filter/DateTimeFormatFilterTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilterTest.java index 1121778cf..6a5f71e8f 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilterTest.java @@ -52,6 +52,7 @@ public void itUsesSpecifiedFormatString() throws Exception { public void itHandlesVarsAndLiterals() throws Exception { interpreter.getContext().put("d", d); interpreter.getContext().put("foo", "%Y-%m"); + assertThat(interpreter.renderFlat("{{ d|datetimeformat(foo) }}")).isEqualTo("2013-11"); assertThat(interpreter.renderFlat("{{ d|datetimeformat(\"%Y-%m-%d\") }}")).isEqualTo("2013-11-06"); assertThat(interpreter.getErrors()).isEmpty(); From d6d88d488721fd8e2d14133d9f4f9385b82bb907 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 7 Nov 2016 17:30:23 -0500 Subject: [PATCH 0208/2465] tweak docs --- .../com/hubspot/jinjava/lib/filter/UnixTimestampFilter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilter.java index a3cc7cf5c..e482c95be 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilter.java @@ -7,12 +7,12 @@ import com.hubspot.jinjava.lib.fn.Functions; @JinjavaDoc( - value = "gets the unix timestamp value (in milliseconds) of a date object", + value = "Gets the UNIX timestamp value (in milliseconds) of a date object", params = { @JinjavaParam(value = "value", defaultValue = "current time", desc = "The date variable"), }, snippets = { - @JinjavaSnippet(code = "{% local_dt|unixtimestamp"), + @JinjavaSnippet(code = "{% mydatetime|unixtimestamp %}"), }) public class UnixTimestampFilter implements Filter { From ad38478cafe9c1c2f7806d2b4d32d75a8e3f4fb0 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 15 Nov 2016 17:13:28 -0500 Subject: [PATCH 0209/2465] pass arguments from selectattr to expression tests --- .../jinjava/lib/filter/RejectAttrFilter.java | 10 ++- .../lib/filter/RejectAttrFilterTest.java | 74 +++++++++++++++++++ 2 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 src/test/java/com/hubspot/jinjava/lib/filter/RejectAttrFilterTest.java diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/RejectAttrFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/RejectAttrFilter.java index 10bdf5ef3..4b599df56 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/RejectAttrFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/RejectAttrFilter.java @@ -1,6 +1,7 @@ package com.hubspot.jinjava.lib.filter; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; @@ -42,22 +43,27 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args) throw new InterpretException(getName() + " filter requires an attr to filter on", interpreter.getLineNumber()); } + String[] expArgs = new String[]{}; String attr = args[0]; ExpTest expTest = interpreter.getContext().getExpTest("truthy"); if (args.length > 1) { expTest = interpreter.getContext().getExpTest(args[1]); if (expTest == null) { - throw new InterpretException("No expression test defined with name '" + args[1] + "'", interpreter.getLineNumber()); + throw new InterpretException("No expression test defined with name '" + args[1] + "'", + interpreter.getLineNumber()); } } + if (args.length > 2) { + expArgs = Arrays.copyOfRange(args, 2, args.length); + } ForLoop loop = ObjectIterator.getLoop(var); while (loop.hasNext()) { Object val = loop.next(); Object attrVal = interpreter.resolveProperty(val, attr); - if (!expTest.evaluate(attrVal, interpreter)) { + if (!expTest.evaluate(attrVal, interpreter, (Object[]) expArgs)) { result.add(val); } } diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/RejectAttrFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/RejectAttrFilterTest.java new file mode 100644 index 000000000..aa079a9ab --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/lib/filter/RejectAttrFilterTest.java @@ -0,0 +1,74 @@ +package com.hubspot.jinjava.lib.filter; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.HashMap; + +import org.junit.Before; +import org.junit.Test; + +import com.google.common.collect.Lists; +import com.hubspot.jinjava.Jinjava; + +public class RejectAttrFilterTest { + + Jinjava jinjava; + + @Before + public void setup() { + jinjava = new Jinjava(); + jinjava.getGlobalContext().put("users", Lists.newArrayList( + new User(0, false, "foo@bar.com"), + new User(1, true, "bar@bar.com"), + new User(2, false, null))); + } + + @Test + public void rejectAttrWithNoExp() { + assertThat(jinjava.render("{{ users|rejectattr('is_active') }}", new HashMap())) + .isEqualTo("[0, 2]"); + } + + @Test + public void rejectAttrWithExp() { + assertThat(jinjava.render("{{ users|rejectattr('email', 'none') }}", new HashMap())) + .isEqualTo("[0, 1]"); + } + + @Test + public void selectAttrWithIsEqualToExp() { + assertThat(jinjava.render("{{ users|rejectattr('email', 'equalto', 'bar@bar.com') }}", new HashMap())) + .isEqualTo("[0, 2]"); + } + + + public static class User { + private int num; + private boolean isActive; + private String email; + + public User(int num, boolean isActive, String email) { + this.num = num; + this.isActive = isActive; + this.email = email; + } + + public int getNum() { + return num; + } + + public String getEmail() { + return email; + } + + public boolean getIsActive() { + return isActive; + } + + @Override + public String toString() { + return num + ""; + } + } + +} From ed363c136bdcc07512f6fa00f41fa4a4790e97ec Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 15 Nov 2016 22:22:52 -0500 Subject: [PATCH 0210/2465] respect - in tags to eliminate whitespace --- .../com/hubspot/jinjava/tree/TreeParser.java | 50 ++++++++++++++----- .../hubspot/jinjava/tree/parse/TextToken.java | 8 +++ .../hubspot/jinjava/tree/TreeParserTest.java | 28 +++++++++++ 3 files changed, 74 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/tree/TreeParser.java b/src/main/java/com/hubspot/jinjava/tree/TreeParser.java index bec51dc0e..317f12e66 100644 --- a/src/main/java/com/hubspot/jinjava/tree/TreeParser.java +++ b/src/main/java/com/hubspot/jinjava/tree/TreeParser.java @@ -20,6 +20,8 @@ import static com.hubspot.jinjava.tree.parse.TokenScannerSymbols.TOKEN_NOTE; import static com.hubspot.jinjava.tree.parse.TokenScannerSymbols.TOKEN_TAG; +import java.util.LinkedList; + import org.apache.commons.lang3.StringUtils; import com.google.common.collect.Iterators; @@ -56,15 +58,25 @@ public Node buildTree() { parent = root; while (scanner.hasNext()) { + Node node = nextNode(); + if (node != null) { + if (node instanceof TextNode + && parent instanceof TagNode + && parent.getChildren().isEmpty() + && parent.getMaster().isRightTrim()) { + node.getMaster().setLeftTrim(true); + } parent.getChildren().add(node); } } if (parent != root) { interpreter.addError(TemplateError.fromException( - new MissingEndTagException(((TagNode) parent).getEndName(), parent.getMaster().getImage(), parent.getLineNumber()))); + new MissingEndTagException(((TagNode) parent).getEndName(), + parent.getMaster().getImage(), + parent.getLineNumber()))); } return root; @@ -77,20 +89,21 @@ private Node nextNode() { Token token = scanner.next(); switch (token.getType()) { - case TOKEN_FIXED: - return text((TextToken) token); + case TOKEN_FIXED: + return text((TextToken) token); - case TOKEN_EXPR_START: - return expression((ExpressionToken) token); + case TOKEN_EXPR_START: + return expression((ExpressionToken) token); - case TOKEN_TAG: - return tag((TagToken) token); + case TOKEN_TAG: + return tag((TagToken) token); - case TOKEN_NOTE: - break; + case TOKEN_NOTE: + break; - default: - interpreter.addError(TemplateError.fromException(new UnexpectedTokenException(token.getImage(), token.getLineNumber()))); + default: + interpreter.addError(TemplateError.fromException(new UnexpectedTokenException(token.getImage(), + token.getLineNumber()))); } return null; @@ -139,6 +152,17 @@ private Node tag(TagToken tagToken) { } private void endTag(Tag tag, TagToken tagToken) { + + final LinkedList children = parent.getChildren(); + final Node lastChild = children.isEmpty() ? null : children.get(children.size() - 1); + + if (parent instanceof TagNode + && tagToken.isLeftTrim() + && lastChild != null + && lastChild instanceof TextNode) { + lastChild.getMaster().setRightTrim(true); + } + while (!(parent instanceof RootNode)) { TagNode parentTag = (TagNode) parent; parent = parent.getParent(); @@ -147,7 +171,9 @@ private void endTag(Tag tag, TagToken tagToken) { break; } else { interpreter.addError(TemplateError.fromException( - new TemplateSyntaxException(tagToken.getImage(), "Mismatched end tag, expected: " + parentTag.getEndName(), tagToken.getLineNumber()))); + new TemplateSyntaxException(tagToken.getImage(), + "Mismatched end tag, expected: " + parentTag.getEndName(), + tagToken.getLineNumber()))); } } } diff --git a/src/main/java/com/hubspot/jinjava/tree/parse/TextToken.java b/src/main/java/com/hubspot/jinjava/tree/parse/TextToken.java index 826ec0ac3..f2f64dac3 100644 --- a/src/main/java/com/hubspot/jinjava/tree/parse/TextToken.java +++ b/src/main/java/com/hubspot/jinjava/tree/parse/TextToken.java @@ -46,6 +46,14 @@ public String trim() { } public String output() { + + if (isLeftTrim() && isRightTrim()) { + return trim(); + } else if (isLeftTrim()) { + return StringUtils.stripStart(content, null); + } else if (isRightTrim()) { + return StringUtils.stripEnd(content, null); + } return content; } diff --git a/src/test/java/com/hubspot/jinjava/tree/TreeParserTest.java b/src/test/java/com/hubspot/jinjava/tree/TreeParserTest.java index b713d8d68..a80d0a4f9 100644 --- a/src/test/java/com/hubspot/jinjava/tree/TreeParserTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/TreeParserTest.java @@ -28,6 +28,34 @@ public void parseHtmlWithCommentLines() { assertThat(interpreter.getErrors()).isEmpty(); } + @Test + public void itStripsRightWhiteSpace() throws Exception { + String leftSpace = "{% for foo in [1,2,3] -%}\n.{{ foo }}\n{% endfor %}"; + final Node tree = new TreeParser(interpreter, leftSpace).buildTree(); + assertThat(interpreter.render(tree)).isEqualTo(".1\n.2\n.3\n"); + } + + @Test + public void itStripsLeftWhiteSpace() throws Exception { + String leftSpace = "{% for foo in [1,2,3] %}\n{{ foo }}.\n{%- endfor %}"; + final Node tree = new TreeParser(interpreter, leftSpace).buildTree(); + assertThat(interpreter.render(tree)).isEqualTo("\n1.\n2.\n3."); + } + + @Test + public void itStripsLeftAndRightWhiteSpace() throws Exception { + String leftSpace = "{% for foo in [1,2,3] -%}\n.{{ foo }}.\n{%- endfor %}"; + final Node tree = new TreeParser(interpreter, leftSpace).buildTree(); + assertThat(interpreter.render(tree)).isEqualTo(".1..2..3."); + } + + @Test + public void itPreservesInnerWhiteSpace() throws Exception { + String leftSpace = "{% for foo in [1,2,3] -%}\nL{% if true %}\n{{ foo }}\n{% endif %}R\n{%- endfor %}"; + final Node tree = new TreeParser(interpreter, leftSpace).buildTree(); + assertThat(interpreter.render(tree)).isEqualTo("L\n1\nRL\n2\nRL\n3\nR"); + } + @Test public void trimAndLstripBlocks() { interpreter = new Jinjava(JinjavaConfig.newBuilder().withLstripBlocks(true).withTrimBlocks(true).build()).newInterpreter(); From 8d1c172e391ed882779b755ab40852dee9fcc5a1 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 15 Nov 2016 22:26:03 -0500 Subject: [PATCH 0211/2465] test newlines and spaces --- src/test/java/com/hubspot/jinjava/tree/TreeParserTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/hubspot/jinjava/tree/TreeParserTest.java b/src/test/java/com/hubspot/jinjava/tree/TreeParserTest.java index a80d0a4f9..41f112661 100644 --- a/src/test/java/com/hubspot/jinjava/tree/TreeParserTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/TreeParserTest.java @@ -30,21 +30,21 @@ public void parseHtmlWithCommentLines() { @Test public void itStripsRightWhiteSpace() throws Exception { - String leftSpace = "{% for foo in [1,2,3] -%}\n.{{ foo }}\n{% endfor %}"; + String leftSpace = "{% for foo in [1,2,3] -%} \n .{{ foo }}\n{% endfor %}"; final Node tree = new TreeParser(interpreter, leftSpace).buildTree(); assertThat(interpreter.render(tree)).isEqualTo(".1\n.2\n.3\n"); } @Test public void itStripsLeftWhiteSpace() throws Exception { - String leftSpace = "{% for foo in [1,2,3] %}\n{{ foo }}.\n{%- endfor %}"; + String leftSpace = "{% for foo in [1,2,3] %}\n{{ foo }}. \n {%- endfor %}"; final Node tree = new TreeParser(interpreter, leftSpace).buildTree(); assertThat(interpreter.render(tree)).isEqualTo("\n1.\n2.\n3."); } @Test public void itStripsLeftAndRightWhiteSpace() throws Exception { - String leftSpace = "{% for foo in [1,2,3] -%}\n.{{ foo }}.\n{%- endfor %}"; + String leftSpace = "{% for foo in [1,2,3] -%} \n .{{ foo }}. \n {%- endfor %}"; final Node tree = new TreeParser(interpreter, leftSpace).buildTree(); assertThat(interpreter.render(tree)).isEqualTo(".1..2..3."); } From 56987b4497f096dcfc6cee0acd75d85fbb5e47e9 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Wed, 16 Nov 2016 22:40:35 -0500 Subject: [PATCH 0212/2465] trim whitespace outside of tags too --- .../com/hubspot/jinjava/tree/TreeParser.java | 44 ++++++++++++++++--- .../com/hubspot/jinjava/tree/parse/Token.java | 9 ++++ .../hubspot/jinjava/tree/TreeParserTest.java | 37 ++++++++++++---- 3 files changed, 75 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/tree/TreeParser.java b/src/main/java/com/hubspot/jinjava/tree/TreeParser.java index 317f12e66..3ef52f913 100644 --- a/src/main/java/com/hubspot/jinjava/tree/TreeParser.java +++ b/src/main/java/com/hubspot/jinjava/tree/TreeParser.java @@ -58,16 +58,9 @@ public Node buildTree() { parent = root; while (scanner.hasNext()) { - Node node = nextNode(); if (node != null) { - if (node instanceof TextNode - && parent instanceof TagNode - && parent.getChildren().isEmpty() - && parent.getMaster().isRightTrim()) { - node.getMaster().setLeftTrim(true); - } parent.getChildren().add(node); } } @@ -85,6 +78,7 @@ public Node buildTree() { /** * @return null if EOF or error */ + private Node nextNode() { Token token = scanner.next(); @@ -109,6 +103,15 @@ private Node nextNode() { return null; } + private Node getLastSibling() { + + if (parent == null || parent.getChildren().isEmpty()) { + return null; + } + + return parent.getChildren().getLast(); + } + private Node text(TextToken textToken) { if (interpreter.getConfig().isLstripBlocks()) { if (scanner.hasNext() && scanner.peek().getType() == TOKEN_TAG) { @@ -116,6 +119,22 @@ private Node text(TextToken textToken) { } } + final Node lastSibling = getLastSibling(); + + // if last sibling was a tag and has rightTrimAfterEnd, strip whitespace + if (lastSibling != null + && lastSibling instanceof TagNode + && lastSibling.getMaster().isRightTrimAfterEnd()) { + textToken.setLeftTrim(true); + } + + // for first TextNode child of TagNode where rightTrim is enabled, mark it for left trim + if (parent instanceof TagNode + && lastSibling == null + && parent.getMaster().isRightTrim()) { + textToken.setLeftTrim(true); + } + TextNode n = new TextNode(textToken); n.setParent(parent); return n; @@ -137,6 +156,15 @@ private Node tag(TagToken tagToken) { if (tag instanceof EndTag) { endTag(tag, tagToken); return null; + } else { + + // if a tag has left trim, mark the last sibling to trim right whitespace + if (tagToken.isLeftTrim()) { + final Node lastSibling = getLastSibling(); + if (lastSibling != null && lastSibling instanceof TextNode) { + lastSibling.getMaster().setRightTrim(true); + } + } } TagNode node = new TagNode(tag, tagToken); @@ -163,6 +191,8 @@ private void endTag(Tag tag, TagToken tagToken) { lastChild.getMaster().setRightTrim(true); } + parent.getMaster().setRightTrimAfterEnd(tagToken.isRightTrim()); + while (!(parent instanceof RootNode)) { TagNode parentTag = (TagNode) parent; parent = parent.getParent(); diff --git a/src/main/java/com/hubspot/jinjava/tree/parse/Token.java b/src/main/java/com/hubspot/jinjava/tree/parse/Token.java index cf889eaa1..a8c4a7796 100644 --- a/src/main/java/com/hubspot/jinjava/tree/parse/Token.java +++ b/src/main/java/com/hubspot/jinjava/tree/parse/Token.java @@ -36,6 +36,7 @@ public abstract class Token implements Serializable { private boolean leftTrim; private boolean rightTrim; + private boolean rightTrimAfterEnd; public Token(String image, int lineNumber) { this.image = image; @@ -59,6 +60,10 @@ public boolean isRightTrim() { return rightTrim; } + public boolean isRightTrimAfterEnd() { + return rightTrimAfterEnd; + } + public void setLeftTrim(boolean leftTrim) { this.leftTrim = leftTrim; } @@ -67,6 +72,10 @@ public void setRightTrim(boolean rightTrim) { this.rightTrim = rightTrim; } + public void setRightTrimAfterEnd(boolean rightTrimAfterEnd) { + this.rightTrimAfterEnd = rightTrimAfterEnd; + } + @Override public String toString() { return image; diff --git a/src/test/java/com/hubspot/jinjava/tree/TreeParserTest.java b/src/test/java/com/hubspot/jinjava/tree/TreeParserTest.java index 41f112661..bf082faf7 100644 --- a/src/test/java/com/hubspot/jinjava/tree/TreeParserTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/TreeParserTest.java @@ -30,32 +30,53 @@ public void parseHtmlWithCommentLines() { @Test public void itStripsRightWhiteSpace() throws Exception { - String leftSpace = "{% for foo in [1,2,3] -%} \n .{{ foo }}\n{% endfor %}"; - final Node tree = new TreeParser(interpreter, leftSpace).buildTree(); + String expression = "{% for foo in [1,2,3] -%} \n .{{ foo }}\n{% endfor %}"; + final Node tree = new TreeParser(interpreter, expression).buildTree(); assertThat(interpreter.render(tree)).isEqualTo(".1\n.2\n.3\n"); } @Test public void itStripsLeftWhiteSpace() throws Exception { - String leftSpace = "{% for foo in [1,2,3] %}\n{{ foo }}. \n {%- endfor %}"; - final Node tree = new TreeParser(interpreter, leftSpace).buildTree(); + String expression = "{% for foo in [1,2,3] %}\n{{ foo }}. \n {%- endfor %}"; + final Node tree = new TreeParser(interpreter, expression).buildTree(); assertThat(interpreter.render(tree)).isEqualTo("\n1.\n2.\n3."); } @Test public void itStripsLeftAndRightWhiteSpace() throws Exception { - String leftSpace = "{% for foo in [1,2,3] -%} \n .{{ foo }}. \n {%- endfor %}"; - final Node tree = new TreeParser(interpreter, leftSpace).buildTree(); + String expression = "{% for foo in [1,2,3] -%} \n .{{ foo }}. \n {%- endfor %}"; + final Node tree = new TreeParser(interpreter, expression).buildTree(); assertThat(interpreter.render(tree)).isEqualTo(".1..2..3."); } @Test public void itPreservesInnerWhiteSpace() throws Exception { - String leftSpace = "{% for foo in [1,2,3] -%}\nL{% if true %}\n{{ foo }}\n{% endif %}R\n{%- endfor %}"; - final Node tree = new TreeParser(interpreter, leftSpace).buildTree(); + String expression = "{% for foo in [1,2,3] -%}\nL{% if true %}\n{{ foo }}\n{% endif %}R\n{%- endfor %}"; + final Node tree = new TreeParser(interpreter, expression).buildTree(); assertThat(interpreter.render(tree)).isEqualTo("L\n1\nRL\n2\nRL\n3\nR"); } + @Test + public void itStripsLeftWhiteSpaceBeforeTag() throws Exception { + String expression = ".\n {%- for foo in [1,2,3] %} {{ foo }} {% endfor %} \n."; + final Node tree = new TreeParser(interpreter, expression).buildTree(); + assertThat(interpreter.render(tree)).isEqualTo(". 1 2 3 \n."); + } + + @Test + public void itStripsRightWhiteSpaceAfterTag() throws Exception { + String expression = ".\n {% for foo in [1,2,3] %} {{ foo }} {% endfor -%} \n."; + final Node tree = new TreeParser(interpreter, expression).buildTree(); + assertThat(interpreter.render(tree)).isEqualTo(".\n 1 2 3 ."); + } + + @Test + public void itStripsAllOuterWhiteSpace() throws Exception { + String expression = ".\n {%- for foo in [1,2,3] -%} {{ foo }} {%- endfor -%} \n."; + final Node tree = new TreeParser(interpreter, expression).buildTree(); + assertThat(interpreter.render(tree)).isEqualTo(".123."); + } + @Test public void trimAndLstripBlocks() { interpreter = new Jinjava(JinjavaConfig.newBuilder().withLstripBlocks(true).withTrimBlocks(true).build()).newInterpreter(); From c6193110397a59a152b8530b6d6f3db3b7ca55ca Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Wed, 16 Nov 2016 22:43:43 -0500 Subject: [PATCH 0213/2465] use getLastSibling --- .../java/com/hubspot/jinjava/tree/TreeParser.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/tree/TreeParser.java b/src/main/java/com/hubspot/jinjava/tree/TreeParser.java index 3ef52f913..0dff86743 100644 --- a/src/main/java/com/hubspot/jinjava/tree/TreeParser.java +++ b/src/main/java/com/hubspot/jinjava/tree/TreeParser.java @@ -20,8 +20,6 @@ import static com.hubspot.jinjava.tree.parse.TokenScannerSymbols.TOKEN_NOTE; import static com.hubspot.jinjava.tree.parse.TokenScannerSymbols.TOKEN_TAG; -import java.util.LinkedList; - import org.apache.commons.lang3.StringUtils; import com.google.common.collect.Iterators; @@ -181,14 +179,13 @@ private Node tag(TagToken tagToken) { private void endTag(Tag tag, TagToken tagToken) { - final LinkedList children = parent.getChildren(); - final Node lastChild = children.isEmpty() ? null : children.get(children.size() - 1); + final Node lastSibling = getLastSibling(); if (parent instanceof TagNode && tagToken.isLeftTrim() - && lastChild != null - && lastChild instanceof TextNode) { - lastChild.getMaster().setRightTrim(true); + && lastSibling != null + && lastSibling instanceof TextNode) { + lastSibling.getMaster().setRightTrim(true); } parent.getMaster().setRightTrimAfterEnd(tagToken.isRightTrim()); From 38c4e366669109aa975310ba6432a3f2553dfd9b Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Wed, 16 Nov 2016 22:45:14 -0500 Subject: [PATCH 0214/2465] less whitespace --- src/main/java/com/hubspot/jinjava/tree/TreeParser.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/tree/TreeParser.java b/src/main/java/com/hubspot/jinjava/tree/TreeParser.java index 0dff86743..6ff286eda 100644 --- a/src/main/java/com/hubspot/jinjava/tree/TreeParser.java +++ b/src/main/java/com/hubspot/jinjava/tree/TreeParser.java @@ -97,16 +97,13 @@ private Node nextNode() { interpreter.addError(TemplateError.fromException(new UnexpectedTokenException(token.getImage(), token.getLineNumber()))); } - return null; } private Node getLastSibling() { - if (parent == null || parent.getChildren().isEmpty()) { return null; } - return parent.getChildren().getLast(); } @@ -155,7 +152,6 @@ private Node tag(TagToken tagToken) { endTag(tag, tagToken); return null; } else { - // if a tag has left trim, mark the last sibling to trim right whitespace if (tagToken.isLeftTrim()) { final Node lastSibling = getLastSibling(); @@ -204,5 +200,4 @@ private void endTag(Tag tag, TagToken tagToken) { } } } - } From f69afb3b79dc7dfb20048a3eefd655931f7812dd Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 18 Nov 2016 16:25:56 -0500 Subject: [PATCH 0215/2465] latest release notes --- CHANGES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index a1982c4de..2a875dfad 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,10 @@ # Jinjava Releases # +### 2016-11-18 Version 2.1.14 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.14%22)) ### + +* Enabled manual whitespace control by ending or closing tags with `{%-` or `%}` +* Fixed issue with passing arguments to `rejectattr` + ### Version 2.1.13 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.13%22)) ### * Added support for disabling specific functions, filters and tags From 3d6b75ea7fd79337f732d7691d532af200206555 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 18 Nov 2016 16:26:16 -0500 Subject: [PATCH 0216/2465] missing - --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 2a875dfad..56735fd05 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,7 +2,7 @@ ### 2016-11-18 Version 2.1.14 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.14%22)) ### -* Enabled manual whitespace control by ending or closing tags with `{%-` or `%}` +* Enabled manual whitespace control by ending or closing tags with `{%-` or `-%}` * Fixed issue with passing arguments to `rejectattr` ### Version 2.1.13 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.13%22)) ### From d4c2cbe9de6d4d3ec987425b88e705865c7ed96f Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Fri, 18 Nov 2016 21:30:02 +0000 Subject: [PATCH 0217/2465] [maven-release-plugin] prepare release jinjava-2.1.14 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0315a9f23..12432094b 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.14-SNAPSHOT + 2.1.14 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.1.14 From 4f31bab9c2e80149171319e7d55427fde8d111d7 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Fri, 18 Nov 2016 21:30:03 +0000 Subject: [PATCH 0218/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 12432094b..74fd94925 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.14 + 2.1.15-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.1.14 + HEAD From 845633b9034a3f0d54c8c0a230e570a2367d55d9 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 18 Nov 2016 16:41:30 -0500 Subject: [PATCH 0219/2465] update to latest release version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 709a498b1..db9e3e6b8 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Get it: com.hubspot.jinjava jinjava - 2.1.0 + 2.1.14 ``` From 9a1dd46c9f2519e045e60374c573d57750aecda0 Mon Sep 17 00:00:00 2001 From: Gustaf Lindstedt Date: Tue, 22 Nov 2016 11:51:32 +0100 Subject: [PATCH 0220/2465] Arbitrary variable names when looping over dict Previously you could only use the variable names 'key' and 'value' when looping over a dictionary. This conflicted with the python implementation of jinja where the loop variables can have arbitrary names, and would have made nested looping over dictionary entries hard due to name collisions. --- .../com/hubspot/jinjava/lib/tag/ForTag.java | 16 +++++++++++++--- .../com/hubspot/jinjava/lib/tag/ForTagTest.java | 17 +++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java index ddf81c6ee..c1a8d7d70 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java @@ -40,7 +40,7 @@ /** * {% for a in b|f1:d,c %} * - * {% for key, value in my_dict %} + * {% for key, value in my_dict.items() %} * * @author anysome * @@ -55,6 +55,16 @@ code = "{% for item in items %}\n" + " {{ item }}\n" + "{% endfor %}"), + @JinjavaSnippet( + desc = "Iterating over dictionary values", + code = "{% for value in dictionary %}\n" + + " {{ value }}\n" + + "{% endfor %}"), + @JinjavaSnippet( + desc = "Iterating over dictionary entries", + code = "{% for key, value in dictionary.items() %}\n" + + " {{ key }}: {{ value }}\n" + + "{% endfor %}"), @JinjavaSnippet( desc = "Standard blog listing loop", code = "{% for content in contents %}\n" + @@ -128,9 +138,9 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { Map.Entry entry = (Entry) val; Object entryVal = null; - if ("key".equals(loopVar)) { + if (loopVars.indexOf(loopVar) == 0) { entryVal = entry.getKey(); - } else if ("value".equals(loopVar)) { + } else if (loopVars.indexOf(loopVar) == 1) { entryVal = entry.getValue(); } diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java index 45e7fefa6..82968f57a 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.Map; +import com.google.common.collect.ImmutableMap; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.select.Elements; @@ -73,6 +74,22 @@ public void forLoopMultipleLoopVars() throws Exception { assertThat(dom.select("p")).hasSize(2); } + @Test + public void forLoopMultipleLoopVarsArbitraryNames() throws Exception { + Map dict = ImmutableMap.of( + "grand", "ol'", + "adserving", "team"); + + context.put("the_dictionary", dict); + String template = "" + + "{% for foo, bar in the_dictionary.items() %}" + + "{{ foo }}: {{ bar }}\n" + + "{% endfor %}"; + + String rendered = jinjava.render(template, context); + assertEquals("grand: ol'\nadserving: team\n", rendered); + } + @Test public void forLoopLiteralLoopExpr() throws Exception { TagNode tagNode = (TagNode) fixture("literal-loop-expr"); From 4e6bdd1326f79c87c55205711d848faa5d529a55 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 22 Nov 2016 10:22:07 -0500 Subject: [PATCH 0221/2465] check for null token (root node) --- src/main/java/com/hubspot/jinjava/tree/TreeParser.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/tree/TreeParser.java b/src/main/java/com/hubspot/jinjava/tree/TreeParser.java index 6ff286eda..a0a9a8372 100644 --- a/src/main/java/com/hubspot/jinjava/tree/TreeParser.java +++ b/src/main/java/com/hubspot/jinjava/tree/TreeParser.java @@ -184,7 +184,9 @@ private void endTag(Tag tag, TagToken tagToken) { lastSibling.getMaster().setRightTrim(true); } - parent.getMaster().setRightTrimAfterEnd(tagToken.isRightTrim()); + if (parent.getMaster() != null) { // root node + parent.getMaster().setRightTrimAfterEnd(tagToken.isRightTrim()); + } while (!(parent instanceof RootNode)) { TagNode parentTag = (TagNode) parent; From e41b217a8064a69e5aa2a189e7b998899066cd41 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 22 Nov 2016 11:09:54 -0500 Subject: [PATCH 0222/2465] use correct conversion for day of week as number --- .../com/hubspot/jinjava/objects/date/StrftimeFormatter.java | 2 +- .../hubspot/jinjava/objects/date/StrftimeFormatterTest.java | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/objects/date/StrftimeFormatter.java b/src/main/java/com/hubspot/jinjava/objects/date/StrftimeFormatter.java index 916c13f48..9afd9089e 100644 --- a/src/main/java/com/hubspot/jinjava/objects/date/StrftimeFormatter.java +++ b/src/main/java/com/hubspot/jinjava/objects/date/StrftimeFormatter.java @@ -40,7 +40,7 @@ public class StrftimeFormatter { CONVERSIONS['p'] = "a"; CONVERSIONS['S'] = "ss"; CONVERSIONS['U'] = "ww"; - CONVERSIONS['w'] = "uu"; + CONVERSIONS['w'] = "e"; CONVERSIONS['W'] = "ww"; CONVERSIONS['x'] = "MM/dd/yy"; CONVERSIONS['X'] = "HH:mm:ss"; diff --git a/src/test/java/com/hubspot/jinjava/objects/date/StrftimeFormatterTest.java b/src/test/java/com/hubspot/jinjava/objects/date/StrftimeFormatterTest.java index 6fc51b8e6..15f24c249 100644 --- a/src/test/java/com/hubspot/jinjava/objects/date/StrftimeFormatterTest.java +++ b/src/test/java/com/hubspot/jinjava/objects/date/StrftimeFormatterTest.java @@ -54,6 +54,11 @@ public void testDate() { assertThat(StrftimeFormatter.format(d, "%x")).isEqualTo("11/06/13"); } + @Test + public void testDayOfWeekNumber() { + assertThat(StrftimeFormatter.format(d, "%w")).isEqualTo("4"); + } + @Test public void testTime() { assertThat(StrftimeFormatter.format(d, "%X")).isEqualTo("14:22:00"); From 64c87097836b388315235e75a5741e6c40a92603 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 22 Nov 2016 17:39:45 -0500 Subject: [PATCH 0223/2465] check for nulls when building exception string --- .../jinjava/el/ext/PowerOfOperator.java | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/PowerOfOperator.java b/src/main/java/com/hubspot/jinjava/el/ext/PowerOfOperator.java index 8152a2d25..a0f0de5d3 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/PowerOfOperator.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/PowerOfOperator.java @@ -12,24 +12,28 @@ public class PowerOfOperator extends SimpleOperator { @Override protected Object apply(TypeConverter converter, Object a, Object b) { - boolean aInt = a instanceof Integer || a instanceof Long; - boolean bInt = b instanceof Integer || b instanceof Long; - boolean aNum = aInt || a instanceof Double || a instanceof Float; - boolean bNum = bInt || b instanceof Double || b instanceof Float; - - if (aInt && bInt) { - Long d = converter.convert(a, Long.class); - Long e = converter.convert(b, Long.class); - return (long)Math.pow(d, e); - } - if (aNum && bNum) { - Double d = converter.convert(a, Double.class); - Double e = converter.convert(b, Double.class); - return Math.pow(d, e); - } - throw new IllegalArgumentException("Unsupported operand type(s) for **: " - + "'" + a.getClass().getSimpleName() + "' and " - + "'" + b.getClass().getSimpleName() + "'"); + boolean aInt = a instanceof Integer || a instanceof Long; + boolean bInt = b instanceof Integer || b instanceof Long; + boolean aNum = aInt || a instanceof Double || a instanceof Float; + boolean bNum = bInt || b instanceof Double || b instanceof Float; + + if (aInt && bInt) { + Long d = converter.convert(a, Long.class); + Long e = converter.convert(b, Long.class); + return (long) Math.pow(d, e); + } + if (aNum && bNum) { + Double d = converter.convert(a, Double.class); + Double e = converter.convert(b, Double.class); + return Math.pow(d, e); + } + throw new IllegalArgumentException(String.format("Unsupported operand type(s) for **: '%s' ('%s') and '%s' ('%s')", + a, + (a == null ? "null" : a.getClass().getSimpleName()), + b, + (b == null ? "null" : b.getClass().getSimpleName()) + ) + ); } public static final Scanner.ExtensionToken TOKEN = new Scanner.ExtensionToken("**"); From 85af1e9705c30f5640dc0baa9417c57aadb2266d Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 22 Nov 2016 17:40:23 -0500 Subject: [PATCH 0224/2465] check for nulls when building exception string in truncatediv operator --- .../jinjava/el/ext/TruncDivOperator.java | 46 ++++++++++--------- .../hubspot/jinjava/el/ext/TruncDivTest.java | 11 ++--- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/TruncDivOperator.java b/src/main/java/com/hubspot/jinjava/el/ext/TruncDivOperator.java index 878c7ea2a..947153fd8 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/TruncDivOperator.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/TruncDivOperator.java @@ -10,31 +10,35 @@ public class TruncDivOperator extends SimpleOperator { + public static final Scanner.ExtensionToken TOKEN = new Scanner.ExtensionToken("//"); + public static final TruncDivOperator OP = new TruncDivOperator(); + @Override protected Object apply(TypeConverter converter, Object a, Object b) { - boolean aInt = a instanceof Integer || a instanceof Long; - boolean bInt = b instanceof Integer || b instanceof Long; - boolean aNum = aInt || a instanceof Double || a instanceof Float; - boolean bNum = bInt || b instanceof Double || b instanceof Float; - - if (aInt && bInt) { - Long d = converter.convert(a, Long.class); - Long e = converter.convert(b, Long.class); - return Math.floorDiv(d, e); - } - if (aNum && bNum) { - Double d = converter.convert(a, Double.class); - Double e = converter.convert(b, Double.class); - return Math.floor(d/e); - } - throw new IllegalArgumentException("Unsupported operand type(s) for //: " - + "'" + a.getClass().getSimpleName() + "' and " - + "'" + b.getClass().getSimpleName() + "'"); - } + boolean aInt = a instanceof Integer || a instanceof Long; + boolean bInt = b instanceof Integer || b instanceof Long; + boolean aNum = aInt || a instanceof Double || a instanceof Float; + boolean bNum = bInt || b instanceof Double || b instanceof Float; - public static final Scanner.ExtensionToken TOKEN = new Scanner.ExtensionToken("//"); - public static final TruncDivOperator OP = new TruncDivOperator(); + if (aInt && bInt) { + Long d = converter.convert(a, Long.class); + Long e = converter.convert(b, Long.class); + return Math.floorDiv(d, e); + } + if (aNum && bNum) { + Double d = converter.convert(a, Double.class); + Double e = converter.convert(b, Double.class); + return Math.floor(d / e); + } + throw new IllegalArgumentException(String.format("Unsupported operand type(s) for //: '%s' ('%s') and '%s' ('%s')", + a, + (a == null ? "null" : a.getClass().getSimpleName()), + b, + (b == null ? "null" : b.getClass().getSimpleName()) + ) + ); + } public static final ExtensionHandler HANDLER = new ExtensionHandler(ExtensionPoint.MUL) { @Override diff --git a/src/test/java/com/hubspot/jinjava/el/ext/TruncDivTest.java b/src/test/java/com/hubspot/jinjava/el/ext/TruncDivTest.java index 253fa2498..e8ff1c79d 100644 --- a/src/test/java/com/hubspot/jinjava/el/ext/TruncDivTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ext/TruncDivTest.java @@ -1,7 +1,7 @@ package com.hubspot.jinjava.el.ext; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; import java.util.Map; @@ -14,6 +14,8 @@ public class TruncDivTest { + private Jinjava jinja; + @Before public void setUp() { jinja = new Jinjava(); @@ -58,15 +60,12 @@ public void testTruncDivStringFails() { context.put("dividend", "5"); context.put("divisor", "2"); - String template = "{% set x = dividend // divisor %}{{x}}"; + String template = "{% set x = dividend // divisor %}"; try { jinja.render(template, context); } catch (FatalTemplateErrorsException e) { String msg = e.getMessage(); - assertTrue(msg.contains("Unsupported operand type(s)")); + assertThat(msg).contains("Unsupported operand type(s): '5' ('String') and '2' ('String')"); } } - - - private Jinjava jinja; } From 95c764bc305b9da549b307d0bee1b86ad7873a1a Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 22 Nov 2016 17:40:51 -0500 Subject: [PATCH 0225/2465] reorder static --- .../java/com/hubspot/jinjava/el/ext/PowerOfOperator.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/PowerOfOperator.java b/src/main/java/com/hubspot/jinjava/el/ext/PowerOfOperator.java index a0f0de5d3..29815b76e 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/PowerOfOperator.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/PowerOfOperator.java @@ -10,6 +10,9 @@ public class PowerOfOperator extends SimpleOperator { + public static final Scanner.ExtensionToken TOKEN = new Scanner.ExtensionToken("**"); + public static final PowerOfOperator OP = new PowerOfOperator(); + @Override protected Object apply(TypeConverter converter, Object a, Object b) { boolean aInt = a instanceof Integer || a instanceof Long; @@ -36,9 +39,6 @@ protected Object apply(TypeConverter converter, Object a, Object b) { ); } - public static final Scanner.ExtensionToken TOKEN = new Scanner.ExtensionToken("**"); - public static final PowerOfOperator OP = new PowerOfOperator(); - public static final ExtensionHandler HANDLER = new ExtensionHandler(ExtensionPoint.MUL) { @Override public AstNode createAstNode(AstNode... children) { From c57105da7d12bf65e4177b84e56bf222ab64bdb8 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 22 Nov 2016 17:44:20 -0500 Subject: [PATCH 0226/2465] unquote type names --- src/main/java/com/hubspot/jinjava/el/ext/PowerOfOperator.java | 2 +- src/main/java/com/hubspot/jinjava/el/ext/TruncDivOperator.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/PowerOfOperator.java b/src/main/java/com/hubspot/jinjava/el/ext/PowerOfOperator.java index 29815b76e..420a22c73 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/PowerOfOperator.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/PowerOfOperator.java @@ -30,7 +30,7 @@ protected Object apply(TypeConverter converter, Object a, Object b) { Double e = converter.convert(b, Double.class); return Math.pow(d, e); } - throw new IllegalArgumentException(String.format("Unsupported operand type(s) for **: '%s' ('%s') and '%s' ('%s')", + throw new IllegalArgumentException(String.format("Unsupported operand type(s) for **: '%s' (%s) and '%s' (%s)", a, (a == null ? "null" : a.getClass().getSimpleName()), b, diff --git a/src/main/java/com/hubspot/jinjava/el/ext/TruncDivOperator.java b/src/main/java/com/hubspot/jinjava/el/ext/TruncDivOperator.java index 947153fd8..74f9b5182 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/TruncDivOperator.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/TruncDivOperator.java @@ -31,7 +31,7 @@ protected Object apply(TypeConverter converter, Object a, Object b) { Double e = converter.convert(b, Double.class); return Math.floor(d / e); } - throw new IllegalArgumentException(String.format("Unsupported operand type(s) for //: '%s' ('%s') and '%s' ('%s')", + throw new IllegalArgumentException(String.format("Unsupported operand type(s) for //: '%s' (%s) and '%s' (%s)", a, (a == null ? "null" : a.getClass().getSimpleName()), b, From 096143926ba0f3ba4f783b2754a4614705e28c93 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 22 Nov 2016 17:45:19 -0500 Subject: [PATCH 0227/2465] match test to new message --- src/test/java/com/hubspot/jinjava/el/ext/TruncDivTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/java/com/hubspot/jinjava/el/ext/TruncDivTest.java b/src/test/java/com/hubspot/jinjava/el/ext/TruncDivTest.java index e8ff1c79d..39d6bd34e 100644 --- a/src/test/java/com/hubspot/jinjava/el/ext/TruncDivTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ext/TruncDivTest.java @@ -64,8 +64,7 @@ public void testTruncDivStringFails() { try { jinja.render(template, context); } catch (FatalTemplateErrorsException e) { - String msg = e.getMessage(); - assertThat(msg).contains("Unsupported operand type(s): '5' ('String') and '2' ('String')"); + assertThat(e.getMessage()).contains("Unsupported operand type(s) for //: '5' (String) and '2' (String)"); } } } From e63b5646288aab5c01c47205249530f9ec770dbc Mon Sep 17 00:00:00 2001 From: "j.thomae" Date: Thu, 24 Nov 2016 08:06:10 +0100 Subject: [PATCH 0228/2465] removed compiler warnings --- .../com/hubspot/jinjava/lib/filter/Md5Filter.java | 12 ++++++------ .../com/hubspot/jinjava/objects/date/PyishDate.java | 3 +++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/Md5Filter.java b/src/main/java/com/hubspot/jinjava/lib/filter/Md5Filter.java index 49f93ba25..2a351e563 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/Md5Filter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/Md5Filter.java @@ -15,16 +15,16 @@ **********************************************************************/ package com.hubspot.jinjava.lib.filter; -import static com.hubspot.jinjava.util.Logging.ENGINE_LOG; +import com.hubspot.jinjava.doc.annotations.JinjavaDoc; +import com.hubspot.jinjava.doc.annotations.JinjavaParam; +import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; import java.nio.charset.Charset; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import com.hubspot.jinjava.doc.annotations.JinjavaDoc; -import com.hubspot.jinjava.doc.annotations.JinjavaParam; -import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; -import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import static com.hubspot.jinjava.util.Logging.ENGINE_LOG; @JinjavaDoc( value = "Calculates the md5 hash of the given object", @@ -71,7 +71,7 @@ private String md5(String str, Charset encoding) { @Override public Object filter(Object object, JinjavaInterpreter interpreter, String... arg) { if (object instanceof String) { - return md5((String) object, interpreter.getConfiguration().getCharset()); + return md5((String) object, interpreter.getConfig().getCharset()); } return object; } diff --git a/src/main/java/com/hubspot/jinjava/objects/date/PyishDate.java b/src/main/java/com/hubspot/jinjava/objects/date/PyishDate.java index 0abb867eb..63d4daa8e 100644 --- a/src/main/java/com/hubspot/jinjava/objects/date/PyishDate.java +++ b/src/main/java/com/hubspot/jinjava/objects/date/PyishDate.java @@ -53,16 +53,19 @@ public String strftime(String fmt) { return StrftimeFormatter.format(date, fmt); } + @SuppressWarnings("deprecation") @Override public int getYear() { return date.getYear(); } + @SuppressWarnings("deprecation") @Override public int getMonth() { return date.getMonthValue(); } + @SuppressWarnings("deprecation") @Override public int getDay() { return date.getDayOfMonth(); From 1d30858f5f8119b70a92bcf9dcc7b5126116952d Mon Sep 17 00:00:00 2001 From: "j.thomae" Date: Thu, 24 Nov 2016 08:41:35 +0100 Subject: [PATCH 0229/2465] added configuration flag for enabling macro recursion. fixes #91 --- .../com/hubspot/jinjava/JinjavaConfig.java | 21 +++++++++-- .../jinjava/el/ext/AstMacroFunction.java | 13 +++++-- .../hubspot/jinjava/interpret/CallStack.java | 9 +++++ .../jinjava/interpret/JinjavaInterpreter.java | 4 ++ .../hubspot/jinjava/lib/tag/MacroTagTest.java | 37 ++++++++++++++++--- .../tags/macrotag/ending-recursion.jinja | 5 +++ 6 files changed, 76 insertions(+), 13 deletions(-) create mode 100644 src/test/resources/tags/macrotag/ending-recursion.jinja diff --git a/src/main/java/com/hubspot/jinjava/JinjavaConfig.java b/src/main/java/com/hubspot/jinjava/JinjavaConfig.java index 7127ca997..bbb6155eb 100644 --- a/src/main/java/com/hubspot/jinjava/JinjavaConfig.java +++ b/src/main/java/com/hubspot/jinjava/JinjavaConfig.java @@ -38,6 +38,7 @@ public class JinjavaConfig { private final boolean lstripBlocks; private final boolean readOnlyResolver; + private final boolean enableRecursiveMacroCalls; private Map> disabled; @@ -46,11 +47,11 @@ public static Builder newBuilder() { } public JinjavaConfig() { - this(StandardCharsets.UTF_8, Locale.ENGLISH, ZoneOffset.UTC, 10, new HashMap<>(), false, false, true); + this(StandardCharsets.UTF_8, Locale.ENGLISH, ZoneOffset.UTC, 10, new HashMap<>(), false, false, true, false); } public JinjavaConfig(Charset charset, Locale locale, ZoneId timeZone, int maxRenderDepth) { - this(charset, locale, timeZone, maxRenderDepth, new HashMap<>(), false, false, true); + this(charset, locale, timeZone, maxRenderDepth, new HashMap<>(), false, false, true, false); } private JinjavaConfig(Charset charset, @@ -60,7 +61,8 @@ private JinjavaConfig(Charset charset, Map> disabled, boolean trimBlocks, boolean lstripBlocks, - boolean readOnlyResolver) { + boolean readOnlyResolver, + boolean enableRecursiveMacroCalls) { this.charset = charset; this.locale = locale; this.timeZone = timeZone; @@ -69,6 +71,7 @@ private JinjavaConfig(Charset charset, this.trimBlocks = trimBlocks; this.lstripBlocks = lstripBlocks; this.readOnlyResolver = readOnlyResolver; + this.enableRecursiveMacroCalls = enableRecursiveMacroCalls; } public Charset getCharset() { @@ -99,6 +102,10 @@ public boolean isReadOnlyResolver() { return readOnlyResolver; } + public boolean isEnableRecursiveMacroCalls() { + return enableRecursiveMacroCalls; + } + public Map> getDisabled() { return disabled; } @@ -114,6 +121,7 @@ public static class Builder { private boolean lstripBlocks; private boolean readOnlyResolver = true; + private boolean enableRecursiveMacroCalls; private Builder() {} @@ -152,13 +160,18 @@ public Builder withLstripBlocks(boolean lstripBlocks) { return this; } + public Builder withEnableRecursiveMacroCalls(boolean enableRecursiveMacroCalls) { + this.enableRecursiveMacroCalls = enableRecursiveMacroCalls; + return this; + } + public Builder withReadOnlyResolver(boolean readOnlyResolver) { this.readOnlyResolver = readOnlyResolver; return this; } public JinjavaConfig build() { - return new JinjavaConfig(charset, locale, timeZone, maxRenderDepth, disabled, trimBlocks, lstripBlocks, readOnlyResolver); + return new JinjavaConfig(charset, locale, timeZone, maxRenderDepth, disabled, trimBlocks, lstripBlocks, readOnlyResolver, enableRecursiveMacroCalls); } } diff --git a/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java b/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java index ff8816077..e9ac3d95c 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java @@ -6,12 +6,12 @@ import javax.el.ELException; import com.google.common.collect.ImmutableMap; +import com.hubspot.jinjava.interpret.CallStack; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.MacroTagCycleException; import com.hubspot.jinjava.interpret.TemplateError; import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory; import com.hubspot.jinjava.lib.fn.MacroFunction; - import de.odysseus.el.misc.LocalMessages; import de.odysseus.el.tree.Bindings; import de.odysseus.el.tree.impl.ast.AstFunction; @@ -30,9 +30,16 @@ public Object eval(Bindings bindings, ELContext context) { MacroFunction macroFunction = interpreter.getContext().getGlobalMacro(getName()); if (macroFunction != null) { + CallStack macroStack = interpreter.getContext().getMacroStack(); if (!macroFunction.isCaller()) { try { - interpreter.getContext().getMacroStack().push(getName(), -1); + boolean enableRecursiveMacroCalls = interpreter.getConfig().isEnableRecursiveMacroCalls(); + if (enableRecursiveMacroCalls) { + macroStack.pushWithoutCycleCheck(getName()); + } + else { + macroStack.push(getName(), -1); + } } catch (MacroTagCycleException e) { interpreter.addError(new TemplateError(TemplateError.ErrorType.WARNING, TemplateError.ErrorReason.EXCEPTION, @@ -55,7 +62,7 @@ public Object eval(Bindings bindings, ELContext context) { } catch (InvocationTargetException e) { throw new ELException(LocalMessages.get("error.function.invocation", getName()), e.getCause()); } finally { - interpreter.getContext().getMacroStack().pop(); + macroStack.pop(); } } diff --git a/src/main/java/com/hubspot/jinjava/interpret/CallStack.java b/src/main/java/com/hubspot/jinjava/interpret/CallStack.java index a3b713044..d5b8469d5 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/CallStack.java +++ b/src/main/java/com/hubspot/jinjava/interpret/CallStack.java @@ -26,6 +26,15 @@ public boolean contains(String path) { return false; } + /** + * This is added to allow for recursive macro calls. Adds the given path to the + * call stack without checking for a cycle. + * @param path the path to be added. + */ + public void pushWithoutCycleCheck(String path) { + stack.push(path); + } + public void push(String path, int lineNumber) { if (contains(path)) { throw TagCycleException.create(exceptionClass, path, Optional.of(lineNumber)); diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index 87bb1c416..b513bff36 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -69,6 +69,10 @@ public JinjavaInterpreter(JinjavaInterpreter orig) { this(orig.application, new Context(orig.context), orig.config); } + /** + * @deprecated use {{@link #getConfig()}} + */ + @Deprecated public JinjavaConfig getConfiguration() { return config; } diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java index 6b117a7ec..be4f4d3a8 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java @@ -20,6 +20,7 @@ import com.google.common.collect.Lists; import com.google.common.io.Resources; import com.hubspot.jinjava.Jinjava; +import com.hubspot.jinjava.JinjavaConfig; import com.hubspot.jinjava.interpret.Context; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.lib.fn.MacroFunction; @@ -107,7 +108,7 @@ public void testMacroUsedInForLoop() throws Exception { "tools_body_3", ImmutableMap.of("html", "body3"), "tools_body_4", ImmutableMap.of("html", "body4"))); - Document dom = Jsoup.parseBodyFragment(new Jinjava().render(Resources.toString(Resources.getResource(String.format("tags/macrotag/%s.jinja", "macro-used-in-forloop")), StandardCharsets.UTF_8), bindings)); + Document dom = Jsoup.parseBodyFragment(new Jinjava().render(fixtureText("macro-used-in-forloop"), bindings)); Element tabs = dom.select(".tabs").get(0); assertThat(tabs.select(".tools__description")).hasSize(4); assertThat(tabs.select(".tools__description").get(0).text()).isEqualTo("body1"); @@ -118,37 +119,61 @@ public void testMacroUsedInForLoop() throws Exception { @Test public void itPreventsDirectMacroRecursion() throws IOException { - String template = Resources.toString(Resources.getResource("tags/macrotag/recursion.jinja"), StandardCharsets.UTF_8); + String template = fixtureText("recursion"); interpreter.render(template); assertThat(interpreter.getErrors().get(0).getMessage()).contains("Cycle detected for macro 'hello'"); } @Test public void itPreventsIndirectMacroRecursion() throws IOException { - String template = Resources.toString(Resources.getResource("tags/macrotag/recursion_indirect.jinja"), StandardCharsets.UTF_8); + String template = fixtureText("recursion_indirect"); interpreter.render(template); assertThat(interpreter.getErrors().get(0).getMessage()).contains("Cycle detected for macro 'goodbye'"); } @Test public void itAllowsMacrosCallingMacrosUsingCall() throws IOException { - String template = Resources.toString(Resources.getResource("tags/macrotag/macros-calling-macros.jinja"), StandardCharsets.UTF_8); + String template = fixtureText("macros-calling-macros"); String out = interpreter.render(template); assertThat(interpreter.getErrors()).isEmpty(); assertThat(out).contains("Hello World One"); assertThat(out).contains("Hello World Two"); } + @Test + public void itAllowsMacroRecursionWhenEnabledInConfiguration() throws IOException { + // I need a different configuration here therefore + interpreter = new Jinjava(JinjavaConfig.newBuilder().withEnableRecursiveMacroCalls(true).build()).newInterpreter(); + JinjavaInterpreter.pushCurrent(interpreter); + + try { + String template = fixtureText("ending-recursion"); + String out = interpreter.render(template); + assertThat(interpreter.getErrors()).isEmpty(); + assertThat(out).contains("Hello Hello Hello Hello Hello"); + } + finally { + // and I need to cleanup my mess... + JinjavaInterpreter.popCurrent(); + } + } + + + private Node snippet(String jinja) { return new TreeParser(interpreter, jinja).buildTree().getChildren().getFirst(); } - private TagNode fixture(String name) { + private String fixtureText(String name) { try { - return (TagNode) snippet(Resources.toString(Resources.getResource(String.format("tags/macrotag/%s.jinja", name)), StandardCharsets.UTF_8)); + return Resources.toString(Resources.getResource(String.format("tags/macrotag/%s.jinja", name)), StandardCharsets.UTF_8); } catch (IOException e) { throw Throwables.propagate(e); } } + private TagNode fixture(String name) { + return (TagNode) snippet(fixtureText(name)); + } + } diff --git a/src/test/resources/tags/macrotag/ending-recursion.jinja b/src/test/resources/tags/macrotag/ending-recursion.jinja new file mode 100644 index 000000000..91b67c8ae --- /dev/null +++ b/src/test/resources/tags/macrotag/ending-recursion.jinja @@ -0,0 +1,5 @@ +{% macro hello(count) -%} + Hello {% if count > 0 %}{{ hello(count-1) }}{% endif %} +{%- endmacro %} + +{{ hello(5) }} From 3659f3452c90527af89e2bac6335807caceb63ca Mon Sep 17 00:00:00 2001 From: "j.thomae" Date: Thu, 24 Nov 2016 08:45:25 +0100 Subject: [PATCH 0230/2465] restored import order that was messed up by IDEA. --- .../java/com/hubspot/jinjava/lib/filter/Md5Filter.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/Md5Filter.java b/src/main/java/com/hubspot/jinjava/lib/filter/Md5Filter.java index 2a351e563..a828fa801 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/Md5Filter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/Md5Filter.java @@ -15,16 +15,16 @@ **********************************************************************/ package com.hubspot.jinjava.lib.filter; -import com.hubspot.jinjava.doc.annotations.JinjavaDoc; -import com.hubspot.jinjava.doc.annotations.JinjavaParam; -import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; -import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import static com.hubspot.jinjava.util.Logging.ENGINE_LOG; import java.nio.charset.Charset; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import static com.hubspot.jinjava.util.Logging.ENGINE_LOG; +import com.hubspot.jinjava.doc.annotations.JinjavaDoc; +import com.hubspot.jinjava.doc.annotations.JinjavaParam; +import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; @JinjavaDoc( value = "Calculates the md5 hash of the given object", From 4945814ba793822652b0e143db05759e801393d2 Mon Sep 17 00:00:00 2001 From: "j.thomae" Date: Fri, 25 Nov 2016 07:40:11 +0100 Subject: [PATCH 0231/2465] removed unnecessary variable declaration --- src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java b/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java index e9ac3d95c..d68e966e0 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java @@ -33,8 +33,7 @@ public Object eval(Bindings bindings, ELContext context) { CallStack macroStack = interpreter.getContext().getMacroStack(); if (!macroFunction.isCaller()) { try { - boolean enableRecursiveMacroCalls = interpreter.getConfig().isEnableRecursiveMacroCalls(); - if (enableRecursiveMacroCalls) { + if (interpreter.getConfig().isEnableRecursiveMacroCalls()) { macroStack.pushWithoutCycleCheck(getName()); } else { From 1a647b817b243ecbb2b8cd2c022c2a64f5386f8e Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 25 Nov 2016 10:10:29 -0500 Subject: [PATCH 0232/2465] collapse lines --- src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java b/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java index d68e966e0..d94dc0ed4 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java @@ -35,8 +35,7 @@ public Object eval(Bindings bindings, ELContext context) { try { if (interpreter.getConfig().isEnableRecursiveMacroCalls()) { macroStack.pushWithoutCycleCheck(getName()); - } - else { + } else { macroStack.push(getName(), -1); } } catch (MacroTagCycleException e) { From ac99522102f0a2d4a15f1a320a90ab6979fe3a6e Mon Sep 17 00:00:00 2001 From: Roopa Hiremath Chandrasekaraiah Date: Tue, 17 Jan 2017 11:56:59 -0800 Subject: [PATCH 0233/2465] Introduced StrictUndefined switch --- .../com/hubspot/jinjava/JinjavaConfig.java | 26 ++++++--- .../interpret/UnknownTokenException.java | 18 +++++++ .../hubspot/jinjava/tree/ExpressionNode.java | 7 ++- .../hubspot/jinjava/tree/StrictModeTest.java | 54 +++++++++++++++++++ 4 files changed, 98 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/hubspot/jinjava/interpret/UnknownTokenException.java create mode 100644 src/test/java/com/hubspot/jinjava/tree/StrictModeTest.java diff --git a/src/main/java/com/hubspot/jinjava/JinjavaConfig.java b/src/main/java/com/hubspot/jinjava/JinjavaConfig.java index bbb6155eb..0ed971161 100644 --- a/src/main/java/com/hubspot/jinjava/JinjavaConfig.java +++ b/src/main/java/com/hubspot/jinjava/JinjavaConfig.java @@ -41,17 +41,18 @@ public class JinjavaConfig { private final boolean enableRecursiveMacroCalls; private Map> disabled; + private final boolean strictUndefined; public static Builder newBuilder() { return new Builder(); } public JinjavaConfig() { - this(StandardCharsets.UTF_8, Locale.ENGLISH, ZoneOffset.UTC, 10, new HashMap<>(), false, false, true, false); + this(StandardCharsets.UTF_8, Locale.ENGLISH, ZoneOffset.UTC, 10, new HashMap<>(), false, false, true, false, false); } public JinjavaConfig(Charset charset, Locale locale, ZoneId timeZone, int maxRenderDepth) { - this(charset, locale, timeZone, maxRenderDepth, new HashMap<>(), false, false, true, false); + this(charset, locale, timeZone, maxRenderDepth, new HashMap<>(), false, false, true, false, false); } private JinjavaConfig(Charset charset, @@ -62,7 +63,8 @@ private JinjavaConfig(Charset charset, boolean trimBlocks, boolean lstripBlocks, boolean readOnlyResolver, - boolean enableRecursiveMacroCalls) { + boolean enableRecursiveMacroCalls, + boolean strictUndefined) { this.charset = charset; this.locale = locale; this.timeZone = timeZone; @@ -72,6 +74,7 @@ private JinjavaConfig(Charset charset, this.lstripBlocks = lstripBlocks; this.readOnlyResolver = readOnlyResolver; this.enableRecursiveMacroCalls = enableRecursiveMacroCalls; + this.strictUndefined = strictUndefined; } public Charset getCharset() { @@ -110,7 +113,11 @@ public Map> getDisabled() { return disabled; } - public static class Builder { + public boolean isStrictUndefined() { + return strictUndefined; +} + +public static class Builder { private Charset charset = StandardCharsets.UTF_8; private Locale locale = Locale.ENGLISH; private ZoneId timeZone = ZoneOffset.UTC; @@ -122,6 +129,7 @@ public static class Builder { private boolean readOnlyResolver = true; private boolean enableRecursiveMacroCalls; + private boolean strictUndefined; private Builder() {} @@ -169,10 +177,16 @@ public Builder withReadOnlyResolver(boolean readOnlyResolver) { this.readOnlyResolver = readOnlyResolver; return this; } - + + public Builder withStrictUndefined(boolean strictUndefined) { + this.strictUndefined = strictUndefined; + return this; + } + public JinjavaConfig build() { - return new JinjavaConfig(charset, locale, timeZone, maxRenderDepth, disabled, trimBlocks, lstripBlocks, readOnlyResolver, enableRecursiveMacroCalls); + return new JinjavaConfig(charset, locale, timeZone, maxRenderDepth, disabled, trimBlocks, lstripBlocks, readOnlyResolver, enableRecursiveMacroCalls, strictUndefined); } + } } diff --git a/src/main/java/com/hubspot/jinjava/interpret/UnknownTokenException.java b/src/main/java/com/hubspot/jinjava/interpret/UnknownTokenException.java new file mode 100644 index 000000000..6b64c9b73 --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/interpret/UnknownTokenException.java @@ -0,0 +1,18 @@ +package com.hubspot.jinjava.interpret; + +public class UnknownTokenException extends InterpretException { + + private final String token; + + public UnknownTokenException(String token, int lineNumber) { + super("Unknown token found: " + token, lineNumber); + this.token = token; + } + + public String getToken() { + return token; + } + + private static final long serialVersionUID = -388757722051666198L; + +} diff --git a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java index a4f8f3c36..2f5364c5b 100644 --- a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java @@ -20,6 +20,7 @@ import org.apache.commons.lang3.StringUtils; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.interpret.UnknownTokenException; import com.hubspot.jinjava.lib.filter.EscapeFilter; import com.hubspot.jinjava.tree.output.OutputNode; import com.hubspot.jinjava.tree.output.RenderedOutputNode; @@ -39,7 +40,11 @@ public ExpressionNode(ExpressionToken token) { @Override public OutputNode render(JinjavaInterpreter interpreter) { Object var = interpreter.resolveELExpression(master.getExpr(), getLineNumber()); - + + if(var == null && interpreter.getConfig().isStrictUndefined()){ + throw new UnknownTokenException(master.getExpr(),getLineNumber()); + } + String result = Objects.toString(var, ""); if (!StringUtils.equals(result, master.getImage()) && StringUtils.contains(result, "{{")) { diff --git a/src/test/java/com/hubspot/jinjava/tree/StrictModeTest.java b/src/test/java/com/hubspot/jinjava/tree/StrictModeTest.java new file mode 100644 index 000000000..db8d9ee7f --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/tree/StrictModeTest.java @@ -0,0 +1,54 @@ +package com.hubspot.jinjava.tree; + +import static org.junit.Assert.*; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; + +import com.hubspot.jinjava.Jinjava; +import com.hubspot.jinjava.JinjavaConfig; +import com.hubspot.jinjava.interpret.FatalTemplateErrorsException; + +public class StrictModeTest { + private static Jinjava jinjava; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + JinjavaConfig.Builder builder = JinjavaConfig.newBuilder(); + builder.withStrictUndefined(true); + JinjavaConfig config = builder.build(); + jinjava = new Jinjava(config); + + } + + @Test + public void checkExceptiontest() { + try { + Map context = new HashMap(); + context.put("token1", "test"); + String template = "hello {{ token1 }} and {{ token2 }}"; + String str = jinjava.render(template, context); + fail(); + } catch (FatalTemplateErrorsException e) { + assertTrue(e instanceof FatalTemplateErrorsException); + } + } + + @Test + public void noException() { + Map context = new HashMap(); + context.put("token1", "test"); + context.put("token2", "test1"); + String template = "hello {{ token1 }} and {{ token2 }}"; + String renderedTemplate = jinjava.render(template, context); + assertEquals("hello test and test1", renderedTemplate); + + } + +} From 3fd0cc8f1514367f03bdf44ca23f6b84f954c429 Mon Sep 17 00:00:00 2001 From: Jonathan Curran Date: Tue, 17 Jan 2017 14:23:44 -0600 Subject: [PATCH 0234/2465] Shade-relocate JUEL dependency and expose under separate classifier --- README.md | 24 ++++++++++++++++++++++++ pom.xml | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/README.md b/README.md index db9e3e6b8..c8017bbdd 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,30 @@ Get it: ``` +or if you have a conflicting EL implementation on your classpath: +```xml + + com.hubspot.jinjava + jinjava + 2.1.14 + with-shaded-juel + + + juel-api + de.odysseus.juel + + + juel-impl + de.odysseus.juel + + + juel-spi + de.odysseus.juel + + + +``` + or if you're stuck on java 7: ```xml diff --git a/pom.xml b/pom.xml index 74fd94925..c1cdb2e3d 100644 --- a/pom.xml +++ b/pom.xml @@ -32,6 +32,7 @@ 1.8 false 2.17 + with-shaded-juel @@ -145,6 +146,38 @@ + + org.apache.maven.plugins + maven-shade-plugin + + + package + + shade + + + true + ${classifier.juel} + + + de.odysseus.juel:juel-api + de.odysseus.juel:juel-impl + + + + + javax.el + jinjava.javax.el + + + de.odysseus.juel + jinjava.de.odysseus.juel + + + + + + From 99e1056d1908043d9395ccf5eba2010ee798f1da Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 17 Jan 2017 17:56:42 -0500 Subject: [PATCH 0235/2465] move uid up --- .../com/hubspot/jinjava/interpret/UnknownTokenException.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/UnknownTokenException.java b/src/main/java/com/hubspot/jinjava/interpret/UnknownTokenException.java index 6b64c9b73..292d6d94e 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/UnknownTokenException.java +++ b/src/main/java/com/hubspot/jinjava/interpret/UnknownTokenException.java @@ -2,6 +2,7 @@ public class UnknownTokenException extends InterpretException { + private static final long serialVersionUID = -388757722051666198L; private final String token; public UnknownTokenException(String token, int lineNumber) { @@ -12,7 +13,4 @@ public UnknownTokenException(String token, int lineNumber) { public String getToken() { return token; } - - private static final long serialVersionUID = -388757722051666198L; - } From 1b423cc296fc0378ee9e206845832d8143cf55fa Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 17 Jan 2017 18:05:48 -0500 Subject: [PATCH 0236/2465] fix spacing --- src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java index 2f5364c5b..c38027cda 100644 --- a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java @@ -41,8 +41,8 @@ public ExpressionNode(ExpressionToken token) { public OutputNode render(JinjavaInterpreter interpreter) { Object var = interpreter.resolveELExpression(master.getExpr(), getLineNumber()); - if(var == null && interpreter.getConfig().isStrictUndefined()){ - throw new UnknownTokenException(master.getExpr(),getLineNumber()); + if (var == null && interpreter.getConfig().isStrictUndefined()) { + throw new UnknownTokenException(master.getExpr(), getLineNumber()); } String result = Objects.toString(var, ""); From 770d1740559f11cd6cbcf08bf1ac31793a839ccf Mon Sep 17 00:00:00 2001 From: Jonathan Curran Date: Tue, 17 Jan 2017 17:54:58 -0600 Subject: [PATCH 0237/2465] Shade-relocate JUEL as default artifact --- pom.xml | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/pom.xml b/pom.xml index c1cdb2e3d..eb0af1e76 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,6 @@ 1.8 false 2.17 - with-shaded-juel @@ -60,11 +59,6 @@ de.odysseus.juel juel-impl - - de.odysseus.juel - juel-spi - runtime - org.apache.commons commons-lang3 @@ -118,11 +112,6 @@ juel-impl 2.2.7 - - de.odysseus.juel - juel-spi - 2.2.7 - @@ -156,8 +145,7 @@ shade - true - ${classifier.juel} + true de.odysseus.juel:juel-api @@ -170,8 +158,8 @@ jinjava.javax.el - de.odysseus.juel - jinjava.de.odysseus.juel + de.odysseus.el + jinjava.de.odysseus.el From 69d54274855a893e515c01512a4f627e483002fa Mon Sep 17 00:00:00 2001 From: Jonathan Curran Date: Tue, 17 Jan 2017 17:56:04 -0600 Subject: [PATCH 0238/2465] Remove unnecessary example --- README.md | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/README.md b/README.md index c8017bbdd..db9e3e6b8 100644 --- a/README.md +++ b/README.md @@ -21,30 +21,6 @@ Get it: ``` -or if you have a conflicting EL implementation on your classpath: -```xml - - com.hubspot.jinjava - jinjava - 2.1.14 - with-shaded-juel - - - juel-api - de.odysseus.juel - - - juel-impl - de.odysseus.juel - - - juel-spi - de.odysseus.juel - - - -``` - or if you're stuck on java 7: ```xml From b9bfb138749d8be52e4bfbae381467cd8f098d0d Mon Sep 17 00:00:00 2001 From: Roopa Hiremath Chandrasekaraiah Date: Wed, 18 Jan 2017 09:52:37 -0800 Subject: [PATCH 0239/2465] Renamed variables and code format --- .../com/hubspot/jinjava/JinjavaConfig.java | 46 ++++++++-------- .../interpret/UnknownTokenException.java | 18 +++---- .../hubspot/jinjava/tree/ExpressionNode.java | 10 ++-- .../jinjava/tree/FailOnUnknownTokensTest.java | 45 ++++++++++++++++ .../hubspot/jinjava/tree/StrictModeTest.java | 54 ------------------- 5 files changed, 82 insertions(+), 91 deletions(-) create mode 100644 src/test/java/com/hubspot/jinjava/tree/FailOnUnknownTokensTest.java delete mode 100644 src/test/java/com/hubspot/jinjava/tree/StrictModeTest.java diff --git a/src/main/java/com/hubspot/jinjava/JinjavaConfig.java b/src/main/java/com/hubspot/jinjava/JinjavaConfig.java index 0ed971161..723f14291 100644 --- a/src/main/java/com/hubspot/jinjava/JinjavaConfig.java +++ b/src/main/java/com/hubspot/jinjava/JinjavaConfig.java @@ -41,7 +41,7 @@ public class JinjavaConfig { private final boolean enableRecursiveMacroCalls; private Map> disabled; - private final boolean strictUndefined; + private final boolean failOnUnknownTokens; public static Builder newBuilder() { return new Builder(); @@ -55,16 +55,15 @@ public JinjavaConfig(Charset charset, Locale locale, ZoneId timeZone, int maxRen this(charset, locale, timeZone, maxRenderDepth, new HashMap<>(), false, false, true, false, false); } - private JinjavaConfig(Charset charset, - Locale locale, - ZoneId timeZone, + private JinjavaConfig(Charset charset, + Locale locale, + ZoneId timeZone, int maxRenderDepth, - Map> disabled, - boolean trimBlocks, - boolean lstripBlocks, + Map> disabled, + boolean trimBlocks, boolean lstripBlocks, boolean readOnlyResolver, - boolean enableRecursiveMacroCalls, - boolean strictUndefined) { + boolean enableRecursiveMacroCalls, + boolean failOnUnknownTokens) { this.charset = charset; this.locale = locale; this.timeZone = timeZone; @@ -74,7 +73,7 @@ private JinjavaConfig(Charset charset, this.lstripBlocks = lstripBlocks; this.readOnlyResolver = readOnlyResolver; this.enableRecursiveMacroCalls = enableRecursiveMacroCalls; - this.strictUndefined = strictUndefined; + this.failOnUnknownTokens = failOnUnknownTokens; } public Charset getCharset() { @@ -113,11 +112,11 @@ public Map> getDisabled() { return disabled; } - public boolean isStrictUndefined() { - return strictUndefined; -} + public boolean isFailOnUnknownTokens() { + return failOnUnknownTokens; + } -public static class Builder { + public static class Builder { private Charset charset = StandardCharsets.UTF_8; private Locale locale = Locale.ENGLISH; private ZoneId timeZone = ZoneOffset.UTC; @@ -129,7 +128,7 @@ public static class Builder { private boolean readOnlyResolver = true; private boolean enableRecursiveMacroCalls; - private boolean strictUndefined; + private boolean failOnUnknownTokens; private Builder() {} @@ -177,16 +176,17 @@ public Builder withReadOnlyResolver(boolean readOnlyResolver) { this.readOnlyResolver = readOnlyResolver; return this; } - - public Builder withStrictUndefined(boolean strictUndefined) { - this.strictUndefined = strictUndefined; - return this; - } - + + public Builder withStrictUndefined(boolean failOnUnknownTokens) { + this.failOnUnknownTokens = failOnUnknownTokens; + return this; + } + public JinjavaConfig build() { - return new JinjavaConfig(charset, locale, timeZone, maxRenderDepth, disabled, trimBlocks, lstripBlocks, readOnlyResolver, enableRecursiveMacroCalls, strictUndefined); + return new JinjavaConfig(charset, locale, timeZone, maxRenderDepth, disabled, trimBlocks, lstripBlocks, + readOnlyResolver, enableRecursiveMacroCalls, failOnUnknownTokens); } - + } } diff --git a/src/main/java/com/hubspot/jinjava/interpret/UnknownTokenException.java b/src/main/java/com/hubspot/jinjava/interpret/UnknownTokenException.java index 6b64c9b73..ab0052f7b 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/UnknownTokenException.java +++ b/src/main/java/com/hubspot/jinjava/interpret/UnknownTokenException.java @@ -2,17 +2,17 @@ public class UnknownTokenException extends InterpretException { - private final String token; + private final String token; - public UnknownTokenException(String token, int lineNumber) { - super("Unknown token found: " + token, lineNumber); - this.token = token; - } + public UnknownTokenException(String token, int lineNumber) { + super("Unknown token found: " + token, lineNumber); + this.token = token; + } - public String getToken() { - return token; - } + public String getToken() { + return token; + } - private static final long serialVersionUID = -388757722051666198L; + private static final long serialVersionUID = -388757722051666198L; } diff --git a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java index 2f5364c5b..128f109fb 100644 --- a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java @@ -40,11 +40,11 @@ public ExpressionNode(ExpressionToken token) { @Override public OutputNode render(JinjavaInterpreter interpreter) { Object var = interpreter.resolveELExpression(master.getExpr(), getLineNumber()); - - if(var == null && interpreter.getConfig().isStrictUndefined()){ - throw new UnknownTokenException(master.getExpr(),getLineNumber()); - } - + + if (var == null && interpreter.getConfig().isFailOnUnknownTokens()) { + throw new UnknownTokenException(master.getExpr(), getLineNumber()); + } + String result = Objects.toString(var, ""); if (!StringUtils.equals(result, master.getImage()) && StringUtils.contains(result, "{{")) { diff --git a/src/test/java/com/hubspot/jinjava/tree/FailOnUnknownTokensTest.java b/src/test/java/com/hubspot/jinjava/tree/FailOnUnknownTokensTest.java new file mode 100644 index 000000000..866040813 --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/tree/FailOnUnknownTokensTest.java @@ -0,0 +1,45 @@ +package com.hubspot.jinjava.tree; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.*; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; + +import com.hubspot.jinjava.Jinjava; +import com.hubspot.jinjava.JinjavaConfig; +import com.hubspot.jinjava.interpret.FatalTemplateErrorsException; + +public class FailOnUnknownTokensTest { + private static Jinjava jinjava; + + @Before + public void setUp() throws Exception { + JinjavaConfig.Builder builder = JinjavaConfig.newBuilder(); + builder.withStrictUndefined(true); + JinjavaConfig config = builder.build(); + jinjava = new Jinjava(config); + + } + + @Test(expected=FatalTemplateErrorsException.class) + public void itThrowsExceptionOnUnknownToken() { + Map context = new HashMap(); + context.put("token1", "test"); + String template = "hello {{ token1 }} and {{ token2 }}"; + String str = jinjava.render(template, context); + } + + @Test + public void itReplaceTokensWithoutException() { + Map context = new HashMap(); + context.put("token1", "test"); + context.put("token2", "test1"); + String template = "hello {{ token1 }} and {{ token2 }}"; + String renderedTemplate = jinjava.render(template, context); + assertThat(renderedTemplate).isEqualTo("hello test and test1"); + } +} diff --git a/src/test/java/com/hubspot/jinjava/tree/StrictModeTest.java b/src/test/java/com/hubspot/jinjava/tree/StrictModeTest.java deleted file mode 100644 index db8d9ee7f..000000000 --- a/src/test/java/com/hubspot/jinjava/tree/StrictModeTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.hubspot.jinjava.tree; - -import static org.junit.Assert.*; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.Before; -import org.junit.Test; - -import com.hubspot.jinjava.Jinjava; -import com.hubspot.jinjava.JinjavaConfig; -import com.hubspot.jinjava.interpret.FatalTemplateErrorsException; - -public class StrictModeTest { - private static Jinjava jinjava; - - /** - * @throws java.lang.Exception - */ - @Before - public void setUp() throws Exception { - JinjavaConfig.Builder builder = JinjavaConfig.newBuilder(); - builder.withStrictUndefined(true); - JinjavaConfig config = builder.build(); - jinjava = new Jinjava(config); - - } - - @Test - public void checkExceptiontest() { - try { - Map context = new HashMap(); - context.put("token1", "test"); - String template = "hello {{ token1 }} and {{ token2 }}"; - String str = jinjava.render(template, context); - fail(); - } catch (FatalTemplateErrorsException e) { - assertTrue(e instanceof FatalTemplateErrorsException); - } - } - - @Test - public void noException() { - Map context = new HashMap(); - context.put("token1", "test"); - context.put("token2", "test1"); - String template = "hello {{ token1 }} and {{ token2 }}"; - String renderedTemplate = jinjava.render(template, context); - assertEquals("hello test and test1", renderedTemplate); - - } - -} From 256c02ddfc8576f6cca6ca843ebd76db838ab887 Mon Sep 17 00:00:00 2001 From: Roopa Hiremath Chandrasekaraiah Date: Wed, 18 Jan 2017 10:06:55 -0800 Subject: [PATCH 0240/2465] Removed extra spaces --- .../java/com/hubspot/jinjava/JinjavaConfig.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/JinjavaConfig.java b/src/main/java/com/hubspot/jinjava/JinjavaConfig.java index 723f14291..73f7d1949 100644 --- a/src/main/java/com/hubspot/jinjava/JinjavaConfig.java +++ b/src/main/java/com/hubspot/jinjava/JinjavaConfig.java @@ -55,14 +55,16 @@ public JinjavaConfig(Charset charset, Locale locale, ZoneId timeZone, int maxRen this(charset, locale, timeZone, maxRenderDepth, new HashMap<>(), false, false, true, false, false); } - private JinjavaConfig(Charset charset, - Locale locale, - ZoneId timeZone, + private JinjavaConfig(Charset charset, + Locale locale, + ZoneId timeZone, int maxRenderDepth, - Map> disabled, - boolean trimBlocks, boolean lstripBlocks, + Map> disabled, + boolean trimBlocks, + boolean lstripBlocks, boolean readOnlyResolver, - boolean enableRecursiveMacroCalls, + boolean enableRecursiveMacroCalls, boolean failOnUnknownTokens) { this.charset = charset; this.locale = locale; @@ -183,8 +185,7 @@ public Builder withStrictUndefined(boolean failOnUnknownTokens) { } public JinjavaConfig build() { - return new JinjavaConfig(charset, locale, timeZone, maxRenderDepth, disabled, trimBlocks, lstripBlocks, - readOnlyResolver, enableRecursiveMacroCalls, failOnUnknownTokens); + return new JinjavaConfig(charset, locale, timeZone, maxRenderDepth, disabled, trimBlocks, lstripBlocks, readOnlyResolver, enableRecursiveMacroCalls, failOnUnknownTokens); } } From 741122d7248ed4967d2ad0e6f6f352f058904870 Mon Sep 17 00:00:00 2001 From: Jonathan Curran Date: Wed, 18 Jan 2017 12:54:58 -0600 Subject: [PATCH 0241/2465] Actually deploy shaded artifact as default --- .gitignore | 1 + pom.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 98b552ee2..98bd98285 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ .DS_Store # Maven +dependency-reduced-pom.xml log/ target/ diff --git a/pom.xml b/pom.xml index eb0af1e76..735e801ef 100644 --- a/pom.xml +++ b/pom.xml @@ -146,6 +146,7 @@ true + false de.odysseus.juel:juel-api From f78afcbbc11b393de4b4cec8af79f44d80ebf0c5 Mon Sep 17 00:00:00 2001 From: Roopa Hiremath Chandrasekaraiah Date: Wed, 18 Jan 2017 11:30:14 -0800 Subject: [PATCH 0242/2465] renamed method to withStrictUndefined --- src/main/java/com/hubspot/jinjava/JinjavaConfig.java | 2 +- .../java/com/hubspot/jinjava/tree/FailOnUnknownTokensTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/JinjavaConfig.java b/src/main/java/com/hubspot/jinjava/JinjavaConfig.java index 73f7d1949..448bd0f16 100644 --- a/src/main/java/com/hubspot/jinjava/JinjavaConfig.java +++ b/src/main/java/com/hubspot/jinjava/JinjavaConfig.java @@ -179,7 +179,7 @@ public Builder withReadOnlyResolver(boolean readOnlyResolver) { return this; } - public Builder withStrictUndefined(boolean failOnUnknownTokens) { + public Builder withFailOnUnknownTokens(boolean failOnUnknownTokens) { this.failOnUnknownTokens = failOnUnknownTokens; return this; } diff --git a/src/test/java/com/hubspot/jinjava/tree/FailOnUnknownTokensTest.java b/src/test/java/com/hubspot/jinjava/tree/FailOnUnknownTokensTest.java index 2105aa2f9..909a865d6 100644 --- a/src/test/java/com/hubspot/jinjava/tree/FailOnUnknownTokensTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/FailOnUnknownTokensTest.java @@ -19,7 +19,7 @@ public class FailOnUnknownTokensTest { @Before public void setUp() throws Exception { JinjavaConfig.Builder builder = JinjavaConfig.newBuilder(); - builder.withStrictUndefined(true); + builder.withFailOnUnknownTokens(true); JinjavaConfig config = builder.build(); jinjava = new Jinjava(config); From d0abbb9a3e832e0b24562c2c89f5334e8282a542 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Wed, 18 Jan 2017 16:17:11 -0500 Subject: [PATCH 0243/2465] 2.1.15 notes --- CHANGES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 56735fd05..c8be91cc6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,10 @@ # Jinjava Releases # +### 2017-01-18 Version 2.1.15 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.15%22)) ### + +* shaded JUEL +* added `failOnUnknownTokens` mode which is similar to Jinja's StrictUndefined + ### 2016-11-18 Version 2.1.14 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.14%22)) ### * Enabled manual whitespace control by ending or closing tags with `{%-` or `-%}` From 2870e2b4d74f945edf2163fc5dd5d63ea3e3a06b Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Wed, 18 Jan 2017 21:21:12 +0000 Subject: [PATCH 0244/2465] [maven-release-plugin] prepare release jinjava-2.1.15 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 735e801ef..48627e8cd 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.15-SNAPSHOT + 2.1.15 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.1.15 From 9f14acb414527f75805f343bc9002f76d4463b50 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Wed, 18 Jan 2017 21:21:12 +0000 Subject: [PATCH 0245/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 48627e8cd..4dbc1a15c 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.15 + 2.1.16-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.1.15 + HEAD From d1c7cf8a3ab37638072e74d1ae32d6e9559706e4 Mon Sep 17 00:00:00 2001 From: Olga Shestopalova Date: Fri, 27 Jan 2017 10:29:36 -0500 Subject: [PATCH 0246/2465] persist dependencies found in lower scopes --- src/main/java/com/hubspot/jinjava/interpret/Context.java | 4 ++++ .../com/hubspot/jinjava/interpret/JinjavaInterpreter.java | 1 + 2 files changed, 5 insertions(+) diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index 23691b68e..e2f329aac 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -368,6 +368,10 @@ public void addDependency(String type, String identification) { this.dependencies.get(type).add(identification); } + public void addDependencies(SetMultimap dependencies) { + this.dependencies.putAll(dependencies); + } + public SetMultimap getDependencies() { return this.dependencies; } diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index b513bff36..63f32c2f2 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -110,6 +110,7 @@ public InterpreterScopeClosable enterScope(Map> dis public void leaveScope() { Context parent = context.getParent(); if (parent != null) { + parent.addDependencies(context.getDependencies()); context = parent; } } From 97964d3fa22c6e3f14c88c70a63a0178bd6f7caa Mon Sep 17 00:00:00 2001 From: Olga Shestopalova Date: Fri, 27 Jan 2017 11:39:38 -0500 Subject: [PATCH 0247/2465] add test for bubbling up dependencies from scope --- .../jinjava/interpret/JinjavaInterpreterTest.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java index 3ae7c5e87..cb78f4643 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java @@ -132,6 +132,19 @@ public void enterScopeTryWithResources() { assertThat(interpreter.resolveELExpression("foo", 1)).isEqualTo("parent"); } + @Test + public void bubbleUpDependenciesFromLowerScope() { + String dependencyType = "foo"; + String dependencyIdentifier = "123"; + + interpreter.enterScope(); + interpreter.getContext().addDependency(dependencyType, dependencyIdentifier); + assertThat(interpreter.getContext().getDependencies().get(dependencyType)).contains(dependencyIdentifier); + interpreter.leaveScope(); + + assertThat(interpreter.getContext().getDependencies().get(dependencyType)).contains(dependencyIdentifier); + } + @Test public void parseWithSyntaxError() { RenderResult result = new Jinjava().renderForResult("{%}", new HashMap<>()); From fedc3d2549102fe1460bc2e57309a60bf644dced Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Wed, 8 Mar 2017 21:03:45 -0500 Subject: [PATCH 0248/2465] add disabled notices to error messages --- .../jinjava/el/ExpressionResolver.java | 3 + .../el/JinjavaInterpreterResolver.java | 70 +++++++++++-------- .../jinjava/el/MacroFunctionMapper.java | 6 +- .../jinjava/interpret/DisabledException.java | 15 ++++ .../jinjava/interpret/TemplateError.java | 9 ++- .../interpret/UnknownTagException.java | 6 +- .../hubspot/jinjava/lib/SimpleLibrary.java | 5 +- .../com/hubspot/jinjava/tree/TreeParser.java | 18 ++++- .../jinjava/el/ExpressionResolverTest.java | 23 ++++-- 9 files changed, 104 insertions(+), 51 deletions(-) create mode 100644 src/main/java/com/hubspot/jinjava/interpret/DisabledException.java diff --git a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java index 0485825f8..eecc3841f 100644 --- a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java @@ -13,6 +13,7 @@ import com.google.common.collect.ImmutableMap; import com.hubspot.jinjava.el.ext.NamedParameter; +import com.hubspot.jinjava.interpret.DisabledException; import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.TemplateError; @@ -77,6 +78,8 @@ public Object resolveExpression(String expression) { "Error parsing '" + expression + "': " + StringUtils.substringAfter(e.getMessage(), "': "), interpreter.getLineNumber(), e))); } catch (ELException e) { interpreter.addError(TemplateError.fromException(new TemplateSyntaxException(expression, e.getMessage(), interpreter.getLineNumber(), e))); + } catch (DisabledException e) { + interpreter.addError(new TemplateError(ErrorType.FATAL, ErrorReason.DISABLED, ErrorItem.FUNCTION, e.getMessage(), expression, interpreter.getLineNumber(), e)); } catch (Exception e) { interpreter.addError(TemplateError.fromException(new InterpretException( String.format("Error resolving expression [%s]: " + getRootCauseMessage(e), expression), e, interpreter.getLineNumber()))); diff --git a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java index 09206dd5d..54177f380 100644 --- a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java @@ -22,20 +22,21 @@ import javax.el.PropertyNotFoundException; import javax.el.ResourceBundleELResolver; -import com.google.common.collect.ImmutableMap; -import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory; import org.apache.commons.lang3.LocaleUtils; import org.apache.commons.lang3.StringUtils; +import com.google.common.collect.ImmutableMap; import com.hubspot.jinjava.el.ext.AbstractCallableMethod; import com.hubspot.jinjava.el.ext.ExtendedParser; import com.hubspot.jinjava.el.ext.JinjavaBeanELResolver; import com.hubspot.jinjava.el.ext.JinjavaListELResolver; +import com.hubspot.jinjava.interpret.DisabledException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.TemplateError; import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; import com.hubspot.jinjava.interpret.TemplateError.ErrorType; +import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory; import com.hubspot.jinjava.objects.PyWrapper; import com.hubspot.jinjava.objects.collections.PyList; import com.hubspot.jinjava.objects.collections.PyMap; @@ -108,45 +109,52 @@ private Object getValue(ELContext context, Object base, Object property, boolean Object value = null; interpreter.getContext().addResolvedValue(propertyName); + ErrorItem item = ErrorItem.PROPERTY; - if (ExtendedParser.INTERPRETER.equals(property)) { - value = interpreter; - } else if (propertyName.startsWith(ExtendedParser.FILTER_PREFIX)) { - value = interpreter.getContext().getFilter(StringUtils.substringAfter(propertyName, ExtendedParser.FILTER_PREFIX)); - } else if (propertyName.startsWith(ExtendedParser.EXPTEST_PREFIX)) { - value = interpreter.getContext().getExpTest(StringUtils.substringAfter(propertyName, ExtendedParser.EXPTEST_PREFIX)); - } else { - if (base == null) { - // Look up property in context. - value = interpreter.retraceVariable((String) property, interpreter.getLineNumber()); + try { + if (ExtendedParser.INTERPRETER.equals(property)) { + value = interpreter; + } else if (propertyName.startsWith(ExtendedParser.FILTER_PREFIX)) { + item = ErrorItem.FILTER; + value = interpreter.getContext().getFilter(StringUtils.substringAfter(propertyName, ExtendedParser.FILTER_PREFIX)); + } else if (propertyName.startsWith(ExtendedParser.EXPTEST_PREFIX)) { + item = ErrorItem.EXPRESSION_TEST; + value = interpreter.getContext().getExpTest(StringUtils.substringAfter(propertyName, ExtendedParser.EXPTEST_PREFIX)); } else { - // Get property of base object. - try { - if (base instanceof Optional) { - Optional optBase = (Optional) base; - if (!optBase.isPresent()) { - return null; + if (base == null) { + // Look up property in context. + value = interpreter.retraceVariable((String) property, interpreter.getLineNumber()); + } else { + // Get property of base object. + try { + if (base instanceof Optional) { + Optional optBase = (Optional) base; + if (!optBase.isPresent()) { + return null; + } + + base = optBase.get(); } - base = optBase.get(); - } + value = super.getValue(context, base, propertyName); - value = super.getValue(context, base, propertyName); + if (value instanceof Optional) { + Optional optValue = (Optional) value; + if (!optValue.isPresent()) { + return null; + } - if (value instanceof Optional) { - Optional optValue = (Optional) value; - if (!optValue.isPresent()) { - return null; + value = optValue.get(); + } + } catch (PropertyNotFoundException e) { + if (errOnUnknownProp) { + interpreter.addError(TemplateError.fromUnknownProperty(base, propertyName, interpreter.getLineNumber())); } - - value = optValue.get(); - } - } catch (PropertyNotFoundException e) { - if (errOnUnknownProp) { - interpreter.addError(TemplateError.fromUnknownProperty(base, propertyName, interpreter.getLineNumber())); } } } + } catch (DisabledException e) { + interpreter.addError(new TemplateError(ErrorType.FATAL, ErrorReason.DISABLED, item, e.getMessage(), propertyName, interpreter.getLineNumber(), e)); } context.setPropertyResolved(true); diff --git a/src/main/java/com/hubspot/jinjava/el/MacroFunctionMapper.java b/src/main/java/com/hubspot/jinjava/el/MacroFunctionMapper.java index 0456fe5b5..11766b860 100644 --- a/src/main/java/com/hubspot/jinjava/el/MacroFunctionMapper.java +++ b/src/main/java/com/hubspot/jinjava/el/MacroFunctionMapper.java @@ -6,10 +6,10 @@ import java.util.Map; import javax.el.FunctionMapper; -import javax.el.MethodNotFoundException; import com.hubspot.jinjava.el.ext.AbstractCallableMethod; import com.hubspot.jinjava.interpret.Context; +import com.hubspot.jinjava.interpret.DisabledException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.lib.fn.MacroFunction; @@ -33,7 +33,7 @@ public Method resolveFunction(String prefix, String localName) { final String functionName = buildFunctionName(prefix, localName); if (context.isFunctionDisabled(functionName)) { - throw new MethodNotFoundException("'" + functionName + "' is disabled in this context"); + throw new DisabledException(functionName); } return map.get(functionName); @@ -41,7 +41,7 @@ public Method resolveFunction(String prefix, String localName) { public void setFunction(String prefix, String localName, Method method) { if (map.isEmpty()) { - map = new HashMap(); + map = new HashMap<>(); } map.put(buildFunctionName(prefix, localName), method); } diff --git a/src/main/java/com/hubspot/jinjava/interpret/DisabledException.java b/src/main/java/com/hubspot/jinjava/interpret/DisabledException.java new file mode 100644 index 000000000..d1521a2b2 --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/interpret/DisabledException.java @@ -0,0 +1,15 @@ +package com.hubspot.jinjava.interpret; + +public class DisabledException extends InterpretException { + + private final String token; + + public DisabledException(String token) { + super("'" + token + "' is disabled in this context"); + this.token = token; + } + + public String getToken() { + return token; + } +} diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java index 4cb904d92..dacf60495 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java @@ -3,12 +3,12 @@ import java.util.Map; import java.util.regex.Pattern; -import com.google.common.collect.ImmutableMap; -import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory; -import com.hubspot.jinjava.interpret.errorcategory.TemplateErrorCategory; import org.apache.commons.lang3.exception.ExceptionUtils; import com.google.common.base.Objects; +import com.google.common.collect.ImmutableMap; +import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory; +import com.hubspot.jinjava.interpret.errorcategory.TemplateErrorCategory; public class TemplateError { public enum ErrorType { @@ -22,6 +22,7 @@ public enum ErrorReason { BAD_URL, EXCEPTION, MISSING, + DISABLED, OTHER } @@ -31,6 +32,8 @@ public enum ErrorItem { TAG, FUNCTION, PROPERTY, + FILTER, + EXPRESSION_TEST, OTHER } diff --git a/src/main/java/com/hubspot/jinjava/interpret/UnknownTagException.java b/src/main/java/com/hubspot/jinjava/interpret/UnknownTagException.java index 5c075f436..31fb766a4 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/UnknownTagException.java +++ b/src/main/java/com/hubspot/jinjava/interpret/UnknownTagException.java @@ -8,10 +8,10 @@ public class UnknownTagException extends TemplateSyntaxException { private final String tag; private final String defintion; - public UnknownTagException(String tag, String defintion, int lineNumber) { - super(defintion, "Unknown tag: " + tag, lineNumber); + public UnknownTagException(String tag, String definition, int lineNumber) { + super(definition, "Unknown tag: " + tag, lineNumber); this.tag = tag; - this.defintion = defintion; + this.defintion = definition; } public UnknownTagException(TagToken tagToken) { diff --git a/src/main/java/com/hubspot/jinjava/lib/SimpleLibrary.java b/src/main/java/com/hubspot/jinjava/lib/SimpleLibrary.java index 03e01a4cd..52c733ded 100644 --- a/src/main/java/com/hubspot/jinjava/lib/SimpleLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/SimpleLibrary.java @@ -26,12 +26,11 @@ import java.util.Set; import java.util.stream.Collectors; -import javax.el.MethodNotFoundException; - import org.apache.commons.lang3.StringUtils; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableSet; +import com.hubspot.jinjava.interpret.DisabledException; public abstract class SimpleLibrary { @@ -55,7 +54,7 @@ protected SimpleLibrary(boolean registerDefaults, Set disabled) { public T fetch(String item) { if (disabled.contains(item)) { - throw new MethodNotFoundException("'" + item + "' is disabled in this context"); + throw new DisabledException(item); } return lib.get(StringUtils.lowerCase(item)); diff --git a/src/main/java/com/hubspot/jinjava/tree/TreeParser.java b/src/main/java/com/hubspot/jinjava/tree/TreeParser.java index a0a9a8372..402c7bf2d 100644 --- a/src/main/java/com/hubspot/jinjava/tree/TreeParser.java +++ b/src/main/java/com/hubspot/jinjava/tree/TreeParser.java @@ -24,9 +24,13 @@ import com.google.common.collect.Iterators; import com.google.common.collect.PeekingIterator; +import com.hubspot.jinjava.interpret.DisabledException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.MissingEndTagException; import com.hubspot.jinjava.interpret.TemplateError; +import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; +import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; +import com.hubspot.jinjava.interpret.TemplateError.ErrorType; import com.hubspot.jinjava.interpret.TemplateSyntaxException; import com.hubspot.jinjava.interpret.UnexpectedTokenException; import com.hubspot.jinjava.interpret.UnknownTagException; @@ -142,9 +146,17 @@ private Node expression(ExpressionToken expressionToken) { } private Node tag(TagToken tagToken) { - Tag tag = interpreter.getContext().getTag(tagToken.getTagName()); - if (tag == null) { - interpreter.addError(TemplateError.fromException(new UnknownTagException(tagToken))); + + Tag tag; + try { + tag = interpreter.getContext().getTag(tagToken.getTagName()); + if (tag == null) { + interpreter.addError(TemplateError.fromException(new UnknownTagException(tagToken))); + return null; + } + } catch (DisabledException e) { + interpreter.addError(new TemplateError(ErrorType.FATAL, ErrorReason.DISABLED, ErrorItem.TAG, + e.getMessage(), tagToken.getTagName(), interpreter.getLineNumber(), e)); return null; } diff --git a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java index a476f47de..1f60429c5 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java @@ -12,8 +12,6 @@ import java.util.Optional; import java.util.Set; -import javax.el.MethodNotFoundException; - import org.junit.Before; import org.junit.Test; @@ -26,10 +24,10 @@ import com.hubspot.jinjava.JinjavaConfig; import com.hubspot.jinjava.interpret.Context; import com.hubspot.jinjava.interpret.Context.Library; -import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.RenderResult; import com.hubspot.jinjava.interpret.TemplateError; +import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; import com.hubspot.jinjava.objects.PyWrapper; import com.hubspot.jinjava.objects.date.PyishDate; @@ -342,7 +340,7 @@ public void blackListedMethods() throws Exception { assertThat(e.getMessage()).contains("Cannot find method 'wait'"); } - @Test(expected = MethodNotFoundException.class) + @Test public void itBlocksDisabledTags() throws Exception { Map> disabled = ImmutableMap.of(Context.Library.TAG, ImmutableSet.of("raw")); @@ -351,9 +349,14 @@ public void itBlocksDisabledTags() throws Exception { try (JinjavaInterpreter.InterpreterScopeClosable c = interpreter.enterScope(disabled)) { interpreter.render("{% raw %} foo {% endraw %}"); } + + TemplateError e = interpreter.getErrors().get(0); + assertThat(e.getItem()).isEqualTo(ErrorItem.TAG); + assertThat(e.getReason()).isEqualTo(ErrorReason.DISABLED); + assertThat(e.getMessage()).contains("'raw' is disabled in this context"); } - @Test(expected = InterpretException.class) + @Test public void itBlocksDisabledTagsInIncludes() throws Exception { final String jinja = "top {% include \"tags/includetag/raw.html\" %}"; @@ -364,6 +367,10 @@ public void itBlocksDisabledTagsInIncludes() throws Exception { try (JinjavaInterpreter.InterpreterScopeClosable c = interpreter.enterScope(disabled)) { interpreter.render(jinja); } + TemplateError e = interpreter.getErrors().get(0); + assertThat(e.getItem()).isEqualTo(ErrorItem.TAG); + assertThat(e.getReason()).isEqualTo(ErrorReason.DISABLED); + assertThat(e.getMessage()).contains("'raw' is disabled in this context"); } @Test @@ -375,6 +382,8 @@ public void itBlocksDisabledFilters() throws Exception { try (JinjavaInterpreter.InterpreterScopeClosable c = interpreter.enterScope(disabled)) { interpreter.resolveELExpression("\"hey\"|truncate(2)", -1); TemplateError e = interpreter.getErrors().get(0); + assertThat(e.getItem()).isEqualTo(ErrorItem.FILTER); + assertThat(e.getReason()).isEqualTo(ErrorReason.DISABLED); assertThat(e.getMessage()).contains("truncate' is disabled in this context"); } } @@ -394,6 +403,8 @@ public void itBlocksDisabledFunctions() throws Exception { final RenderResult renderResult = jinjava.renderForResult(template, context, config); assertEquals("hi ", renderResult.getOutput()); TemplateError e = renderResult.getErrors().get(0); + assertThat(e.getItem()).isEqualTo(ErrorItem.FUNCTION); + assertThat(e.getReason()).isEqualTo(ErrorReason.DISABLED); assertThat(e.getMessage()).contains("':range' is disabled in this context"); } @@ -406,6 +417,8 @@ public void itBlocksDisabledExpTests() throws Exception { try (JinjavaInterpreter.InterpreterScopeClosable c = interpreter.enterScope(disabled)) { interpreter.render("{% if 2 is even %}yes{% endif %}"); TemplateError e = interpreter.getErrors().get(0); + assertThat(e.getItem()).isEqualTo(ErrorItem.EXPRESSION_TEST); + assertThat(e.getReason()).isEqualTo(ErrorReason.DISABLED); assertThat(e.getMessage()).contains("even' is disabled in this context"); } } From 841830d314391c306a2d05afd9c10a523e937e79 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Thu, 9 Mar 2017 12:58:37 -0500 Subject: [PATCH 0249/2465] Update CHANGES.md --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index c8be91cc6..3ee27729f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Jinjava Releases # +### 2017-03-09 Version 2.1.16 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.16%22)) ### + +* disabled functions, filters and tags now add to template errors rather than throwing a fatal exception + ### 2017-01-18 Version 2.1.15 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.15%22)) ### * shaded JUEL From 7a6fc80560b2d47b8981403270e6e87152c605e3 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Thu, 9 Mar 2017 18:03:19 +0000 Subject: [PATCH 0250/2465] [maven-release-plugin] prepare release jinjava-2.1.16 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 4dbc1a15c..156a0ad22 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.16-SNAPSHOT + 2.1.16 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.1.16 From 7b9ba129997d3afa0b5692cf4281f53ff3374412 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Thu, 9 Mar 2017 18:03:19 +0000 Subject: [PATCH 0251/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 156a0ad22..4b8adcebe 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.16 + 2.1.17-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.1.16 + HEAD From c0776001d0f8617ae6e73187501712acd2e1f642 Mon Sep 17 00:00:00 2001 From: Jose Galarza Date: Mon, 20 Mar 2017 12:35:10 +0000 Subject: [PATCH 0252/2465] Adding named parameters to filters --- .../el/JinjavaInterpreterResolver.java | 53 ++++++++++++++----- .../jinjava/lib/filter/AdvancedFilter.java | 45 ++++++++++++++++ .../hubspot/jinjava/lib/filter/Filter.java | 20 +++++++ .../hubspot/jinjava/lib/filter/SumFilter.java | 12 ++--- 4 files changed, 110 insertions(+), 20 deletions(-) create mode 100644 src/main/java/com/hubspot/jinjava/lib/filter/AdvancedFilter.java diff --git a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java index 54177f380..6f2f0b932 100644 --- a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java @@ -7,12 +7,7 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; -import java.util.Date; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; +import java.util.*; import javax.el.ArrayELResolver; import javax.el.CompositeELResolver; @@ -22,14 +17,11 @@ import javax.el.PropertyNotFoundException; import javax.el.ResourceBundleELResolver; +import com.hubspot.jinjava.el.ext.*; import org.apache.commons.lang3.LocaleUtils; import org.apache.commons.lang3.StringUtils; import com.google.common.collect.ImmutableMap; -import com.hubspot.jinjava.el.ext.AbstractCallableMethod; -import com.hubspot.jinjava.el.ext.ExtendedParser; -import com.hubspot.jinjava.el.ext.JinjavaBeanELResolver; -import com.hubspot.jinjava.el.ext.JinjavaListELResolver; import com.hubspot.jinjava.interpret.DisabledException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.TemplateError; @@ -89,9 +81,7 @@ public Object invoke(ELContext context, Object base, Object method, // failed to access property, continue with method calls } - // TODO map named params to special arg in fn to invoke - // https://github.com/HubSpot/jinjava/issues/11 - return super.invoke(context, base, method, paramTypes, params); + return super.invoke(context, base, method, paramTypes, generateMethodParams(method, params)); } /** @@ -104,6 +94,43 @@ public Object getValue(ELContext context, Object base, Object property) { return getValue(context, base, property, true); } + /* + * We transform the AST parameters to something meaningful to Jinjava. + * + * Functions, expressions and tags will receive the parameters as they are, but filters + * have a different signature to what they have in the AST to support named parameters, so + * this method transforms their arguments to be the following: + * + * (Left Value, JinjavaInterpreter, Positional Arguments, Named Arguments) + */ + private Object[] generateMethodParams(Object method, Object[] astParams) { + if (!"filter".equals(method)) { + return astParams; // We only change the signature method for filters + } + + List methodParams = new ArrayList() {{ + add(astParams[0]); // Left Value + add(astParams[1]); // JinjavaInterpreter + }}; + + List args = new ArrayList<>(); + Map kwargs = new HashMap<>(); + + for (Object param: Arrays.asList(astParams).subList(methodParams.size(), astParams.length)) { + if (param instanceof NamedParameter) { + NamedParameter namedParameter = (NamedParameter) param; + kwargs.put(namedParameter.getName(), namedParameter.getValue()); + } else { + args.add(param); + } + } + + methodParams.add(args.toArray()); + methodParams.add(kwargs); + + return methodParams.toArray(); + } + private Object getValue(ELContext context, Object base, Object property, boolean errOnUnknownProp) { String propertyName = Objects.toString(property, ""); Object value = null; diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/AdvancedFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/AdvancedFilter.java new file mode 100644 index 000000000..c2b8bb7ee --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/lib/filter/AdvancedFilter.java @@ -0,0 +1,45 @@ +/********************************************************************** +Copyright (c) 2014 HubSpot 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.hubspot.jinjava.lib.filter; + +import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.lib.Importable; + +import java.util.HashMap; +import java.util.Map; + +public interface AdvancedFilter extends Importable, Filter { + + /** + * Filter the specified template variable within the context of a render process. {{ myvar|myfiltername(arg1,arg2) }} + * + * @param var + * the variable which this filter should operate on + * @param interpreter + * current interpreter context + * @param args + * any positional arguments passed to this filter invocation + * @param kwargs + * any named arguments passed to this filter invocation + * @return the filtered form of the given variable + */ + Object filter(Object var, JinjavaInterpreter interpreter, Object[] args, Map kwargs); + + // Default implementation to maintain backward-compatibility with old Filters + default Object filter(Object var, JinjavaInterpreter interpreter, String... args) { + return filter(var, interpreter, (Object[]) args, new HashMap<>()); + } +} diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/Filter.java b/src/main/java/com/hubspot/jinjava/lib/filter/Filter.java index bd9e096dc..54697df89 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/Filter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/Filter.java @@ -17,6 +17,11 @@ import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.lib.Importable; +import org.apache.commons.lang3.ArrayUtils; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; public interface Filter extends Importable { @@ -33,4 +38,19 @@ public interface Filter extends Importable { */ Object filter(Object var, JinjavaInterpreter interpreter, String... args); + /* + * The JinjaJava parser calls filters giving to them two list of parameters: + * - Positional arguments as Object[] + * - Named arguments as Map + * + * This default method transforms that call to a simple filter that only receives String positional arguments to + * maintain backward-compatibility with old filters that don't support named arguments. + */ + default Object filter(Object var, JinjavaInterpreter interpreter, Object[] args, Map kwargs) { + // We append the named arguments at the end of the positional ones + Object[] allArgs = ArrayUtils.addAll(args, kwargs.values().toArray()); + String[] stringArgs = Arrays.stream(allArgs).map(Object::toString).toArray(String[]::new); + + return filter(var, interpreter, stringArgs); + } } diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/SumFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/SumFilter.java index 7190dd1c2..e227ff50c 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/SumFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/SumFilter.java @@ -1,6 +1,7 @@ package com.hubspot.jinjava.lib.filter; import java.math.BigDecimal; +import java.util.Map; import java.util.Objects; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; @@ -25,7 +26,7 @@ desc = "Sum up only certain attributes", code = "Total: {{ items|sum(attribute='price') }}") }) -public class SumFilter implements Filter { +public class SumFilter implements AdvancedFilter { @Override public String getName() { @@ -33,18 +34,15 @@ public String getName() { } @Override - public Object filter(Object var, JinjavaInterpreter interpreter, String... args) { + public Object filter(Object var, JinjavaInterpreter interpreter, Object[] args, Map kwargs) { ForLoop loop = ObjectIterator.getLoop(var); BigDecimal sum = BigDecimal.ZERO; - String attr = null; + String attr = kwargs.containsKey("attribute") ? kwargs.get("attribute").toString() : null; if (args.length > 0) { - attr = args[0]; - } - if (args.length > 1) { try { - sum = sum.add(new BigDecimal(args[1])); + sum = sum.add(new BigDecimal(args[0].toString())); } catch (NumberFormatException e) { } } From 417a573393b371f0bd4aefda2102e68b3ebbcef2 Mon Sep 17 00:00:00 2001 From: Morris Singer Date: Thu, 23 Mar 2017 13:35:57 -0400 Subject: [PATCH 0253/2465] Remove joke Makes README more transparent and understandable at the expense of the joke. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index db9e3e6b8..b21c3888e 100644 --- a/README.md +++ b/README.md @@ -52,10 +52,10 @@ String renderedTemplate = jinjava.render(template, context); result: ```html -
    Hello, Handsome!
    +
    Hello, Jared!
    ``` -Voila! Hey, wait a minute... +Voila! Advanced Topics --------------- From 084226bdf1f18bf503b28cbfbdb24e52508af4c5 Mon Sep 17 00:00:00 2001 From: Nolan Johnson Date: Wed, 29 Mar 2017 08:12:13 -0600 Subject: [PATCH 0254/2465] correct function binding syntax in custom function example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b21c3888e..de35185b5 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ You can provide custom jinja tags, filters, and static functions to the template jinjava.getGlobalContext().registerTag(new MyCustomTag()); // define a custom filter implementing com.hubspot.jinjava.lib.Filter jinjava.getGlobalContext().registerFilter(new MyAwesomeFilter()); -// define a custom public static function (this one will bind to myfn.my_func('foo', 42)) +// define a custom public static function (this one will bind to myfn:my_func('foo', 42)) jinjava.getGlobalContext().registerFunction(new ELFunctionDefinition("myfn", "my_func", MyFuncsClass.class, "myFunc", String.class, Integer.class); From d99a9a0a76674cb92a9acab6239ca281b758ea1d Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Thu, 30 Mar 2017 18:03:25 -0400 Subject: [PATCH 0255/2465] Limit size of output --- .../com/hubspot/jinjava/JinjavaConfig.java | 21 +++++++++++++++---- .../jinjava/interpret/JinjavaInterpreter.java | 13 +++++++----- .../interpret/OutputTooBigException.java | 16 ++++++++++++++ .../output/BlockPlaceholderOutputNode.java | 5 +++++ .../jinjava/tree/output/OutputList.java | 13 ++++++++++++ .../jinjava/tree/output/OutputNode.java | 2 ++ .../tree/output/RenderedOutputNode.java | 4 ++++ 7 files changed, 65 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/hubspot/jinjava/interpret/OutputTooBigException.java diff --git a/src/main/java/com/hubspot/jinjava/JinjavaConfig.java b/src/main/java/com/hubspot/jinjava/JinjavaConfig.java index 448bd0f16..db5068489 100644 --- a/src/main/java/com/hubspot/jinjava/JinjavaConfig.java +++ b/src/main/java/com/hubspot/jinjava/JinjavaConfig.java @@ -33,6 +33,7 @@ public class JinjavaConfig { private final Locale locale; private final ZoneId timeZone; private final int maxRenderDepth; + private final long maxOutputSize; private final boolean trimBlocks; private final boolean lstripBlocks; @@ -48,11 +49,11 @@ public static Builder newBuilder() { } public JinjavaConfig() { - this(StandardCharsets.UTF_8, Locale.ENGLISH, ZoneOffset.UTC, 10, new HashMap<>(), false, false, true, false, false); + this(StandardCharsets.UTF_8, Locale.ENGLISH, ZoneOffset.UTC, 10, new HashMap<>(), false, false, true, false, false, 0); } public JinjavaConfig(Charset charset, Locale locale, ZoneId timeZone, int maxRenderDepth) { - this(charset, locale, timeZone, maxRenderDepth, new HashMap<>(), false, false, true, false, false); + this(charset, locale, timeZone, maxRenderDepth, new HashMap<>(), false, false, true, false, false, 0); } private JinjavaConfig(Charset charset, @@ -65,7 +66,8 @@ private JinjavaConfig(Charset charset, boolean lstripBlocks, boolean readOnlyResolver, boolean enableRecursiveMacroCalls, - boolean failOnUnknownTokens) { + boolean failOnUnknownTokens, + long maxOutputSize) { this.charset = charset; this.locale = locale; this.timeZone = timeZone; @@ -76,6 +78,7 @@ private JinjavaConfig(Charset charset, this.readOnlyResolver = readOnlyResolver; this.enableRecursiveMacroCalls = enableRecursiveMacroCalls; this.failOnUnknownTokens = failOnUnknownTokens; + this.maxOutputSize = maxOutputSize; } public Charset getCharset() { @@ -94,6 +97,10 @@ public int getMaxRenderDepth() { return maxRenderDepth; } + public long getMaxOutputSize() { + return maxOutputSize; + } + public boolean isTrimBlocks() { return trimBlocks; } @@ -123,6 +130,7 @@ public static class Builder { private Locale locale = Locale.ENGLISH; private ZoneId timeZone = ZoneOffset.UTC; private int maxRenderDepth = 10; + private long maxOutputSize = 0; // in bytes private Map> disabled = new HashMap<>(); private boolean trimBlocks; @@ -184,8 +192,13 @@ public Builder withFailOnUnknownTokens(boolean failOnUnknownTokens) { return this; } + public Builder withMaxOutputSize(long maxOutputSize) { + this.maxOutputSize = maxOutputSize; + return this; + } + public JinjavaConfig build() { - return new JinjavaConfig(charset, locale, timeZone, maxRenderDepth, disabled, trimBlocks, lstripBlocks, readOnlyResolver, enableRecursiveMacroCalls, failOnUnknownTokens); + return new JinjavaConfig(charset, locale, timeZone, maxRenderDepth, disabled, trimBlocks, lstripBlocks, readOnlyResolver, enableRecursiveMacroCalls, failOnUnknownTokens, maxOutputSize); } } diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index 63f32c2f2..55a64e74c 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -40,6 +40,7 @@ import com.hubspot.jinjava.tree.TreeParser; import com.hubspot.jinjava.tree.output.BlockPlaceholderOutputNode; import com.hubspot.jinjava.tree.output.OutputList; +import com.hubspot.jinjava.tree.output.OutputNode; import com.hubspot.jinjava.util.Variable; import com.hubspot.jinjava.util.WhitespaceUtils; @@ -185,21 +186,23 @@ public String render(Node root) { * @return rendered result */ public String render(Node root, boolean processExtendRoots) { - OutputList output = new OutputList(); + OutputList output = new OutputList(config.getMaxOutputSize()); for (Node node : root.getChildren()) { lineNumber = node.getLineNumber(); - output.addNode(node.render(this)); + OutputNode out = node.render(this); + output.addNode(out); } // render all extend parents, keeping the last as the root output if (processExtendRoots) { while (!extendParentRoots.isEmpty()) { Node parentRoot = extendParentRoots.removeFirst(); - output = new OutputList(); + output = new OutputList(config.getMaxOutputSize()); for (Node node : parentRoot.getChildren()) { - output.addNode(node.render(this)); + OutputNode out = node.render(this); + output.addNode(out); } context.getExtendPathStack().pop(); @@ -226,7 +229,7 @@ private void resolveBlockStubs(OutputList output, Stack blockNames) { List superBlock = Iterables.get(blockChain, 1, null); context.setSuperBlock(superBlock); - OutputList blockValueBuilder = new OutputList(); + OutputList blockValueBuilder = new OutputList(config.getMaxOutputSize()); for (Node child : block) { blockValueBuilder.addNode(child.render(this)); diff --git a/src/main/java/com/hubspot/jinjava/interpret/OutputTooBigException.java b/src/main/java/com/hubspot/jinjava/interpret/OutputTooBigException.java new file mode 100644 index 000000000..94b58f45e --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/interpret/OutputTooBigException.java @@ -0,0 +1,16 @@ +package com.hubspot.jinjava.interpret; + +public class OutputTooBigException extends RuntimeException { + + private final long size; + + public OutputTooBigException(long size) { + this.size = size; + } + + @Override + public String getMessage() { + return String.format("%d byte output rendered", size); + } + +} diff --git a/src/main/java/com/hubspot/jinjava/tree/output/BlockPlaceholderOutputNode.java b/src/main/java/com/hubspot/jinjava/tree/output/BlockPlaceholderOutputNode.java index 524b1e919..50ae763df 100644 --- a/src/main/java/com/hubspot/jinjava/tree/output/BlockPlaceholderOutputNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/output/BlockPlaceholderOutputNode.java @@ -30,6 +30,11 @@ public String getValue() { return output; } + @Override + public long getSize() { + return output == null ? 0 : output.getBytes().length; + } + @Override public String toString() { return getValue(); diff --git a/src/main/java/com/hubspot/jinjava/tree/output/OutputList.java b/src/main/java/com/hubspot/jinjava/tree/output/OutputList.java index 313fcb18a..1347d13fa 100644 --- a/src/main/java/com/hubspot/jinjava/tree/output/OutputList.java +++ b/src/main/java/com/hubspot/jinjava/tree/output/OutputList.java @@ -3,12 +3,25 @@ import java.util.LinkedList; import java.util.List; +import com.hubspot.jinjava.interpret.OutputTooBigException; + public class OutputList { private final List nodes = new LinkedList<>(); private final List blocks = new LinkedList<>(); + private long maxOutputSize; + + public OutputList(long maxOutputSize) { + + this.maxOutputSize = maxOutputSize; + } public void addNode(OutputNode node) { + + if (maxOutputSize > 0 && node.getSize() > maxOutputSize) { + throw new OutputTooBigException(node.getSize()); + } + nodes.add(node); if (node instanceof BlockPlaceholderOutputNode) { diff --git a/src/main/java/com/hubspot/jinjava/tree/output/OutputNode.java b/src/main/java/com/hubspot/jinjava/tree/output/OutputNode.java index a02e07abd..8be5ebd55 100644 --- a/src/main/java/com/hubspot/jinjava/tree/output/OutputNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/output/OutputNode.java @@ -4,4 +4,6 @@ public interface OutputNode { String getValue(); + long getSize(); + } diff --git a/src/main/java/com/hubspot/jinjava/tree/output/RenderedOutputNode.java b/src/main/java/com/hubspot/jinjava/tree/output/RenderedOutputNode.java index 4b4eddffc..cedd76392 100644 --- a/src/main/java/com/hubspot/jinjava/tree/output/RenderedOutputNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/output/RenderedOutputNode.java @@ -18,4 +18,8 @@ public String toString() { return getValue(); } + @Override + public long getSize() { + return output == null ? 0 : output.length(); + } } From c799afdc219f0765d7867678139380cc55c23b55 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Thu, 30 Mar 2017 18:04:11 -0400 Subject: [PATCH 0256/2465] TypeOfFilter --- .../el/JinjavaInterpreterResolver.java | 1 + .../jinjava/lib/filter/FilterLibrary.java | 1 + .../jinjava/lib/filter/TypeOfFilter.java | 66 +++++++++++++++ .../jinjava/lib/filter/TypeOfFilterTest.java | 84 +++++++++++++++++++ 4 files changed, 152 insertions(+) create mode 100644 src/main/java/com/hubspot/jinjava/lib/filter/TypeOfFilter.java create mode 100644 src/test/java/com/hubspot/jinjava/lib/filter/TypeOfFilterTest.java diff --git a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java index 54177f380..377b2a130 100644 --- a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java @@ -175,6 +175,7 @@ Object wrap(Object value) { return new PyList((List) value); } if (Map.class.isAssignableFrom(value.getClass())) { + // FIXME: ensure keys are actually strings, if not, convert them return new PyMap((Map) value); } diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java b/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java index e7b308b6f..c15128fde 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java @@ -96,6 +96,7 @@ protected void registerDefaults() { SafeFilter.class, TitleFilter.class, TrimFilter.class, + TypeOfFilter.class, WordCountFilter.class, WordWrapFilter.class); } diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/TypeOfFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/TypeOfFilter.java new file mode 100644 index 000000000..23a8cd2c0 --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/lib/filter/TypeOfFilter.java @@ -0,0 +1,66 @@ +package com.hubspot.jinjava.lib.filter; + +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Set; + +import com.google.common.collect.ImmutableSet; +import com.hubspot.jinjava.doc.annotations.JinjavaDoc; +import com.hubspot.jinjava.el.ext.AstDict; +import com.hubspot.jinjava.el.ext.AstList; +import com.hubspot.jinjava.el.ext.AstTuple; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.objects.collections.PyList; +import com.hubspot.jinjava.objects.collections.PyMap; +import com.hubspot.jinjava.objects.date.PyishDate; + + +@JinjavaDoc( + value = "Get a string that describes the type of the object") +public class TypeOfFilter implements Filter { + + private static final Set> SIMPLE_NAME_TYPES = ImmutableSet.of(String.class); + + @Override + public String getName() { + return "typeof"; + } + + @Override + public Object filter(Object var, JinjavaInterpreter interpreter, String... args) { + if (var == null) { + return "null"; + } + + if (var.getClass() == AstDict.class || var.getClass() == PyMap.class) { + return "dict"; + } + + if (var.getClass() == AstList.class || var.getClass() == ArrayList.class || var.getClass() == PyList.class) { + return "list"; + } + + if (var.getClass() == Boolean.class) { + return "boolean"; + } + + if (var.getClass() == AstTuple.class) { + return "tuple"; + } + + if (var.getClass() == PyishDate.class || var.getClass() == ZonedDateTime.class) { + return "datetime"; + } + + if (Number.class.isAssignableFrom(var.getClass())) { + return "number"; + } + + if (SIMPLE_NAME_TYPES.contains(var.getClass())) { + return var.getClass().getSimpleName().toLowerCase(); + } + + return "unknown"; + } + +} diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/TypeOfFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/TypeOfFilterTest.java new file mode 100644 index 000000000..930dae701 --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/lib/filter/TypeOfFilterTest.java @@ -0,0 +1,84 @@ +package com.hubspot.jinjava.lib.filter; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; + +import com.hubspot.jinjava.Jinjava; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; + +public class TypeOfFilterTest { + + JinjavaInterpreter interpreter; + TypeOfFilter filter; + private Jinjava jinjava; + private Map context; + + @Before + public void setup() { + jinjava = new Jinjava(); + context = new HashMap<>(); + interpreter = jinjava.newInterpreter(); + filter = new TypeOfFilter(); + } + + @Test + public void testString() { + assertThat(filter.filter(" foo ", interpreter)).isEqualTo("string"); + assertThat(interpreter.renderFlat("{{ \"123\"|typeof }}")).isEqualTo("string"); + } + + @Test + public void testInteger() { + assertThat(filter.filter(123, interpreter)).isEqualTo("number"); + } + + @Test + public void testDouble() { + assertThat(filter.filter(123.3345d, interpreter)).isEqualTo("number"); + } + + @Test + public void testDate() { + assertThat(filter.filter(ZonedDateTime.parse("2013-11-06T14:22:00.000+00:00[UTC]"), interpreter)).isEqualTo("datetime"); + } + + @Test + public void testList() { + assertThat(filter.filter(new ArrayList<>(), interpreter)).isEqualTo("list"); + assertThat(interpreter.renderFlat("{{ [1,2,3]|typeof }}")).isEqualTo("list"); + } + + @Test + public void testMap() throws Exception { + assertThat(interpreter.renderFlat("{{ [1,2,3]|typeof }}")).isEqualTo("dict"); + } + + @Test + public void testNumber() throws Exception { + assertThat(interpreter.renderFlat("{{ 123|typeof }}")).isEqualTo("number"); + assertThat(interpreter.renderFlat("{{ 3446.5|typeof }}")).isEqualTo("number"); + assertThat(interpreter.renderFlat("{{ (123/3446.5)|typeof }}")).isEqualTo("number"); + } + + @Test + public void testBool() { + assertThat(interpreter.renderFlat("{{ 1|bool|typeof }}")).isEqualTo("boolean"); + assertThat(interpreter.renderFlat("{{ 0|bool|typeof }}")).isEqualTo("boolean"); + } + + @Test + public void testDict() throws Exception { + assertThat(interpreter.renderFlat("{{ 1|bool|typeof }}")).isEqualTo("boolean"); + + + } + + // chararray? +} From b9c7cd222c2453bbe75802451e311a05fe7097c5 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Thu, 30 Mar 2017 18:13:14 -0400 Subject: [PATCH 0257/2465] added test --- .../interpret/JinjavaInterpreterTest.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java index cb78f4643..a6cfb5d1d 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java @@ -10,6 +10,7 @@ import com.google.common.collect.Lists; import com.hubspot.jinjava.Jinjava; +import com.hubspot.jinjava.JinjavaConfig; import com.hubspot.jinjava.interpret.JinjavaInterpreter.InterpreterScopeClosable; import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; import com.hubspot.jinjava.tree.TextNode; @@ -101,7 +102,8 @@ public void multiWordNumberSnakeCase() { @Test public void triesBeanMethodFirst() { - assertThat(interpreter.resolveProperty(ZonedDateTime.parse("2013-09-19T12:12:12+00:00"), "year").toString()).isEqualTo("2013"); + assertThat(interpreter.resolveProperty(ZonedDateTime.parse("2013-09-19T12:12:12+00:00"), "year") + .toString()).isEqualTo("2013"); } @Test @@ -152,4 +154,17 @@ public void parseWithSyntaxError() { assertThat(result.getErrors().get(0).getReason()).isEqualTo(ErrorReason.SYNTAX_ERROR); } + @Test + public void itLimitsOutputSize() throws Exception { + + JinjavaConfig outputSizeLimitedConfig = JinjavaConfig.newBuilder().withMaxOutputSize(20).build(); + String output = "123456789012345678901234567890"; + + RenderResult renderResult = new Jinjava().renderForResult(output, new HashMap<>()); + assertThat(renderResult.getOutput()).isEqualTo(output); + assertThat(renderResult.getErrors()).isEmpty(); + + renderResult = new Jinjava(outputSizeLimitedConfig).renderForResult(output, new HashMap<>()); + assertThat(renderResult.getErrors().get(0).getMessage()).contains("OutputTooBigException"); + } } From fdcfb35d16f7e74147a0f9d3f26e6d8cfb4f0313 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Thu, 30 Mar 2017 20:28:40 -0400 Subject: [PATCH 0258/2465] use hasErrors --- .../com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java index a6cfb5d1d..549e47211 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java @@ -162,7 +162,7 @@ public void itLimitsOutputSize() throws Exception { RenderResult renderResult = new Jinjava().renderForResult(output, new HashMap<>()); assertThat(renderResult.getOutput()).isEqualTo(output); - assertThat(renderResult.getErrors()).isEmpty(); + assertThat(renderResult.hasErrors()).isFalse(); renderResult = new Jinjava(outputSizeLimitedConfig).renderForResult(output, new HashMap<>()); assertThat(renderResult.getErrors().get(0).getMessage()).contains("OutputTooBigException"); From 46831033d66e789ca309eee9b340ea1f9e09dc99 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Thu, 30 Mar 2017 20:44:17 -0400 Subject: [PATCH 0259/2465] implement testDict, simplify --- .../jinjava/lib/filter/TypeOfFilterTest.java | 20 +++---------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/TypeOfFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/TypeOfFilterTest.java index 930dae701..0915a173b 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/TypeOfFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/TypeOfFilterTest.java @@ -5,7 +5,6 @@ import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.HashMap; -import java.util.Map; import org.junit.Before; import org.junit.Test; @@ -17,14 +16,10 @@ public class TypeOfFilterTest { JinjavaInterpreter interpreter; TypeOfFilter filter; - private Jinjava jinjava; - private Map context; @Before public void setup() { - jinjava = new Jinjava(); - context = new HashMap<>(); - interpreter = jinjava.newInterpreter(); + interpreter = new Jinjava().newInterpreter(); filter = new TypeOfFilter(); } @@ -56,8 +51,8 @@ public void testList() { } @Test - public void testMap() throws Exception { - assertThat(interpreter.renderFlat("{{ [1,2,3]|typeof }}")).isEqualTo("dict"); + public void testDict() throws Exception { + assertThat(filter.filter(new HashMap<>(), interpreter)).isEqualTo("dict"); } @Test @@ -72,13 +67,4 @@ public void testBool() { assertThat(interpreter.renderFlat("{{ 1|bool|typeof }}")).isEqualTo("boolean"); assertThat(interpreter.renderFlat("{{ 0|bool|typeof }}")).isEqualTo("boolean"); } - - @Test - public void testDict() throws Exception { - assertThat(interpreter.renderFlat("{{ 1|bool|typeof }}")).isEqualTo("boolean"); - - - } - - // chararray? } From 447e7a529fa9b1dc911ac529cb38327978d5b8ac Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Thu, 30 Mar 2017 20:56:00 -0400 Subject: [PATCH 0260/2465] Match https://docs.python.org/2/library/types.html --- .../jinjava/lib/filter/TypeOfFilter.java | 31 ++++++++++--------- .../jinjava/lib/filter/TypeOfFilterTest.java | 24 ++++++++------ 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/TypeOfFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/TypeOfFilter.java index 23a8cd2c0..acc658be7 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/TypeOfFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/TypeOfFilter.java @@ -1,17 +1,14 @@ package com.hubspot.jinjava.lib.filter; import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.Set; +import java.util.List; +import java.util.Map; -import com.google.common.collect.ImmutableSet; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.el.ext.AstDict; import com.hubspot.jinjava.el.ext.AstList; import com.hubspot.jinjava.el.ext.AstTuple; import com.hubspot.jinjava.interpret.JinjavaInterpreter; -import com.hubspot.jinjava.objects.collections.PyList; -import com.hubspot.jinjava.objects.collections.PyMap; import com.hubspot.jinjava.objects.date.PyishDate; @@ -19,8 +16,6 @@ value = "Get a string that describes the type of the object") public class TypeOfFilter implements Filter { - private static final Set> SIMPLE_NAME_TYPES = ImmutableSet.of(String.class); - @Override public String getName() { return "typeof"; @@ -32,16 +27,16 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args) return "null"; } - if (var.getClass() == AstDict.class || var.getClass() == PyMap.class) { + if (var.getClass() == AstDict.class || Map.class.isAssignableFrom(var.getClass())) { return "dict"; } - if (var.getClass() == AstList.class || var.getClass() == ArrayList.class || var.getClass() == PyList.class) { + if (var.getClass() == AstList.class || List.class.isAssignableFrom(var.getClass())) { return "list"; } if (var.getClass() == Boolean.class) { - return "boolean"; + return "bool"; } if (var.getClass() == AstTuple.class) { @@ -52,12 +47,20 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args) return "datetime"; } - if (Number.class.isAssignableFrom(var.getClass())) { - return "number"; + if (Integer.class.isAssignableFrom(var.getClass())) { + return "int"; + } + + if (Long.class.isAssignableFrom(var.getClass())) { + return "long"; + } + + if (Float.class.isAssignableFrom(var.getClass()) || Double.class.isAssignableFrom(var.getClass())) { + return "float"; } - if (SIMPLE_NAME_TYPES.contains(var.getClass())) { - return var.getClass().getSimpleName().toLowerCase(); + if (String.class.isAssignableFrom(var.getClass())) { + return "str"; } return "unknown"; diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/TypeOfFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/TypeOfFilterTest.java index 0915a173b..fde0b5258 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/TypeOfFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/TypeOfFilterTest.java @@ -25,18 +25,24 @@ public void setup() { @Test public void testString() { - assertThat(filter.filter(" foo ", interpreter)).isEqualTo("string"); - assertThat(interpreter.renderFlat("{{ \"123\"|typeof }}")).isEqualTo("string"); + assertThat(filter.filter(" foo ", interpreter)).isEqualTo("str"); + assertThat(interpreter.renderFlat("{{ \"123\"|typeof }}")).isEqualTo("str"); } @Test public void testInteger() { - assertThat(filter.filter(123, interpreter)).isEqualTo("number"); + assertThat(filter.filter(123, interpreter)).isEqualTo("int"); } + @Test + public void testLong() { + assertThat(filter.filter(123L, interpreter)).isEqualTo("long"); + } + + @Test public void testDouble() { - assertThat(filter.filter(123.3345d, interpreter)).isEqualTo("number"); + assertThat(filter.filter(123.3345d, interpreter)).isEqualTo("float"); } @Test @@ -57,14 +63,14 @@ public void testDict() throws Exception { @Test public void testNumber() throws Exception { - assertThat(interpreter.renderFlat("{{ 123|typeof }}")).isEqualTo("number"); - assertThat(interpreter.renderFlat("{{ 3446.5|typeof }}")).isEqualTo("number"); - assertThat(interpreter.renderFlat("{{ (123/3446.5)|typeof }}")).isEqualTo("number"); + assertThat(interpreter.renderFlat("{{ 123|typeof }}")).isEqualTo("long"); + assertThat(interpreter.renderFlat("{{ 3446.5|typeof }}")).isEqualTo("float"); + assertThat(interpreter.renderFlat("{{ (123/3446.5)|typeof }}")).isEqualTo("float"); } @Test public void testBool() { - assertThat(interpreter.renderFlat("{{ 1|bool|typeof }}")).isEqualTo("boolean"); - assertThat(interpreter.renderFlat("{{ 0|bool|typeof }}")).isEqualTo("boolean"); + assertThat(interpreter.renderFlat("{{ 1|bool|typeof }}")).isEqualTo("bool"); + assertThat(interpreter.renderFlat("{{ 0|bool|typeof }}")).isEqualTo("bool"); } } From f4c1f5e4010bd26db54eabc8c44726d2ce601a0b Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Thu, 30 Mar 2017 21:24:57 -0400 Subject: [PATCH 0261/2465] Convert to a function to closer match python --- .../jinjava/lib/filter/FilterLibrary.java | 13 ++-- .../jinjava/lib/fn/FunctionLibrary.java | 1 + .../TypeFunction.java} | 15 +--- .../jinjava/lib/filter/TypeOfFilterTest.java | 76 ------------------- .../jinjava/lib/fn/TypeFunctionTest.java | 64 ++++++++++++++++ 5 files changed, 75 insertions(+), 94 deletions(-) rename src/main/java/com/hubspot/jinjava/lib/{filter/TypeOfFilter.java => fn/TypeFunction.java} (77%) delete mode 100644 src/test/java/com/hubspot/jinjava/lib/filter/TypeOfFilterTest.java create mode 100644 src/test/java/com/hubspot/jinjava/lib/fn/TypeFunctionTest.java diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java b/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java index c15128fde..5a7da5b3a 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java @@ -31,12 +31,12 @@ protected void registerDefaults() { AttrFilter.class, PrettyPrintFilter.class, - DefaultFilter.class, + DefaultFilter.class, DAliasedDefaultFilter.class, FileSizeFormatFilter.class, UrlizeFilter.class, - BatchFilter.class, + BatchFilter.class, CountFilter.class, DictSortFilter.class, FirstFilter.class, @@ -56,11 +56,11 @@ protected void registerDefaults() { SplitFilter.class, UniqueFilter.class, - DatetimeFilter.class, + DatetimeFilter.class, DateTimeFormatFilter.class, UnixTimestampFilter.class, - AbsFilter.class, + AbsFilter.class, AddFilter.class, BoolFilter.class, CutFilter.class, @@ -75,7 +75,7 @@ protected void registerDefaults() { RoundFilter.class, SumFilter.class, - EscapeFilter.class, + EscapeFilter.class, EAliasedEscapeFilter.class, EscapeJsFilter.class, ForceEscapeFilter.class, @@ -83,7 +83,7 @@ protected void registerDefaults() { UrlEncodeFilter.class, XmlAttrFilter.class, - CapitalizeFilter.class, + CapitalizeFilter.class, CenterFilter.class, FormatFilter.class, IndentFilter.class, @@ -96,7 +96,6 @@ protected void registerDefaults() { SafeFilter.class, TitleFilter.class, TrimFilter.class, - TypeOfFilter.class, WordCountFilter.class, WordWrapFilter.class); } diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/FunctionLibrary.java b/src/main/java/com/hubspot/jinjava/lib/fn/FunctionLibrary.java index f28524a10..e06d5daf5 100644 --- a/src/main/java/com/hubspot/jinjava/lib/fn/FunctionLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/FunctionLibrary.java @@ -17,6 +17,7 @@ protected void registerDefaults() { register(new ELFunctionDefinition("", "unixtimestamp", Functions.class, "unixtimestamp", Object.class)); register(new ELFunctionDefinition("", "truncate", Functions.class, "truncate", Object.class, Object[].class)); register(new ELFunctionDefinition("", "range", Functions.class, "range", Object.class, Object[].class)); + register(new ELFunctionDefinition("", "type", TypeFunction.class, "type", Object.class)); register(new ELFunctionDefinition("", "super", Functions.class, "renderSuperBlock")); diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/TypeOfFilter.java b/src/main/java/com/hubspot/jinjava/lib/fn/TypeFunction.java similarity index 77% rename from src/main/java/com/hubspot/jinjava/lib/filter/TypeOfFilter.java rename to src/main/java/com/hubspot/jinjava/lib/fn/TypeFunction.java index acc658be7..c53d7be92 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/TypeOfFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/TypeFunction.java @@ -1,4 +1,4 @@ -package com.hubspot.jinjava.lib.filter; +package com.hubspot.jinjava.lib.fn; import java.time.ZonedDateTime; import java.util.List; @@ -8,21 +8,14 @@ import com.hubspot.jinjava.el.ext.AstDict; import com.hubspot.jinjava.el.ext.AstList; import com.hubspot.jinjava.el.ext.AstTuple; -import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.objects.date.PyishDate; @JinjavaDoc( - value = "Get a string that describes the type of the object") -public class TypeOfFilter implements Filter { + value = "Get a string that describes the type of the object, similar to Python's type()") +public class TypeFunction { - @Override - public String getName() { - return "typeof"; - } - - @Override - public Object filter(Object var, JinjavaInterpreter interpreter, String... args) { + public static String type(Object var) { if (var == null) { return "null"; } diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/TypeOfFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/TypeOfFilterTest.java deleted file mode 100644 index fde0b5258..000000000 --- a/src/test/java/com/hubspot/jinjava/lib/filter/TypeOfFilterTest.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.hubspot.jinjava.lib.filter; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.HashMap; - -import org.junit.Before; -import org.junit.Test; - -import com.hubspot.jinjava.Jinjava; -import com.hubspot.jinjava.interpret.JinjavaInterpreter; - -public class TypeOfFilterTest { - - JinjavaInterpreter interpreter; - TypeOfFilter filter; - - @Before - public void setup() { - interpreter = new Jinjava().newInterpreter(); - filter = new TypeOfFilter(); - } - - @Test - public void testString() { - assertThat(filter.filter(" foo ", interpreter)).isEqualTo("str"); - assertThat(interpreter.renderFlat("{{ \"123\"|typeof }}")).isEqualTo("str"); - } - - @Test - public void testInteger() { - assertThat(filter.filter(123, interpreter)).isEqualTo("int"); - } - - @Test - public void testLong() { - assertThat(filter.filter(123L, interpreter)).isEqualTo("long"); - } - - - @Test - public void testDouble() { - assertThat(filter.filter(123.3345d, interpreter)).isEqualTo("float"); - } - - @Test - public void testDate() { - assertThat(filter.filter(ZonedDateTime.parse("2013-11-06T14:22:00.000+00:00[UTC]"), interpreter)).isEqualTo("datetime"); - } - - @Test - public void testList() { - assertThat(filter.filter(new ArrayList<>(), interpreter)).isEqualTo("list"); - assertThat(interpreter.renderFlat("{{ [1,2,3]|typeof }}")).isEqualTo("list"); - } - - @Test - public void testDict() throws Exception { - assertThat(filter.filter(new HashMap<>(), interpreter)).isEqualTo("dict"); - } - - @Test - public void testNumber() throws Exception { - assertThat(interpreter.renderFlat("{{ 123|typeof }}")).isEqualTo("long"); - assertThat(interpreter.renderFlat("{{ 3446.5|typeof }}")).isEqualTo("float"); - assertThat(interpreter.renderFlat("{{ (123/3446.5)|typeof }}")).isEqualTo("float"); - } - - @Test - public void testBool() { - assertThat(interpreter.renderFlat("{{ 1|bool|typeof }}")).isEqualTo("bool"); - assertThat(interpreter.renderFlat("{{ 0|bool|typeof }}")).isEqualTo("bool"); - } -} diff --git a/src/test/java/com/hubspot/jinjava/lib/fn/TypeFunctionTest.java b/src/test/java/com/hubspot/jinjava/lib/fn/TypeFunctionTest.java new file mode 100644 index 000000000..0f3400144 --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/lib/fn/TypeFunctionTest.java @@ -0,0 +1,64 @@ +package com.hubspot.jinjava.lib.fn; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.HashMap; + +import org.junit.Before; +import org.junit.Test; + +import com.hubspot.jinjava.Jinjava; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; + +public class TypeFunctionTest { + + private JinjavaInterpreter interpreter; + + @Before + public void setup() throws NoSuchMethodException { + + interpreter = new Jinjava().newInterpreter(); + } + + @Test + public void testString() { + assertThat(TypeFunction.type(" foo ")).isEqualTo("str"); + } + + @Test + public void testInteger() { + assertThat(TypeFunction.type(123)).isEqualTo("int"); + } + + @Test + public void testLong() { + assertThat(TypeFunction.type(123L)).isEqualTo("long"); + } + + @Test + public void testDouble() { + assertThat(TypeFunction.type(123.3345d)).isEqualTo("float"); + } + + @Test + public void testDate() { + assertThat(TypeFunction.type(ZonedDateTime.parse("2013-11-06T14:22:00.000+00:00[UTC]"))).isEqualTo("datetime"); + } + + @Test + public void testList() { + assertThat(TypeFunction.type(new ArrayList<>())).isEqualTo("list"); + } + + @Test + public void testDict() throws Exception { + assertThat(TypeFunction.type(new HashMap<>())).isEqualTo("dict"); + } + + @Test + public void testBool() { + assertThat(TypeFunction.type(Boolean.FALSE)).isEqualTo("bool"); + } +} From f5ed8d3a91d500bd650217efcb377f61f02157d4 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Thu, 30 Mar 2017 21:38:11 -0400 Subject: [PATCH 0262/2465] Make this data-driven --- .../hubspot/jinjava/lib/fn/TypeFunction.java | 62 +++++++++---------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/TypeFunction.java b/src/main/java/com/hubspot/jinjava/lib/fn/TypeFunction.java index c53d7be92..aa97ffdb3 100644 --- a/src/main/java/com/hubspot/jinjava/lib/fn/TypeFunction.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/TypeFunction.java @@ -3,7 +3,9 @@ import java.time.ZonedDateTime; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import com.google.common.collect.ImmutableMap; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.el.ext.AstDict; import com.hubspot.jinjava.el.ext.AstList; @@ -15,45 +17,41 @@ value = "Get a string that describes the type of the object, similar to Python's type()") public class TypeFunction { + private static Map, String> CLASS_TYPE_TO_NAME = ImmutableMap., String>builder() + .put(AstDict.class, "dict") + .put(AstList.class, "list") + .put(AstTuple.class, "tuple") + .put(Boolean.class, "bool") + .put(PyishDate.class, "datetime") + .put(ZonedDateTime.class, "datetime") + .build(); + + private static Map, String> ASSIGNABLE_TYPE_TO_NAME = ImmutableMap., String>builder() + .put(Boolean.class, "bool") + .put(Double.class, "float") + .put(Float.class, "float") + .put(Integer.class, "int") + .put(List.class, "list") + .put(Long.class, "long") + .put(Map.class, "dict") + .put(String.class, "str") + .build(); + public static String type(Object var) { if (var == null) { return "null"; } - if (var.getClass() == AstDict.class || Map.class.isAssignableFrom(var.getClass())) { - return "dict"; - } - - if (var.getClass() == AstList.class || List.class.isAssignableFrom(var.getClass())) { - return "list"; - } - - if (var.getClass() == Boolean.class) { - return "bool"; - } - - if (var.getClass() == AstTuple.class) { - return "tuple"; - } - - if (var.getClass() == PyishDate.class || var.getClass() == ZonedDateTime.class) { - return "datetime"; - } - - if (Integer.class.isAssignableFrom(var.getClass())) { - return "int"; - } - - if (Long.class.isAssignableFrom(var.getClass())) { - return "long"; - } - - if (Float.class.isAssignableFrom(var.getClass()) || Double.class.isAssignableFrom(var.getClass())) { - return "float"; + for (Entry, String> entry : CLASS_TYPE_TO_NAME.entrySet()) { + if (var.getClass() == entry.getKey()) { + return entry.getValue(); + } } - if (String.class.isAssignableFrom(var.getClass())) { - return "str"; + for (Entry, String> entry : ASSIGNABLE_TYPE_TO_NAME.entrySet()) { + if (entry.getKey().isAssignableFrom(var.getClass())) { + return entry.getValue(); + } } return "unknown"; From 513e198dad980058c7ab1c33e736eab450a50896 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Thu, 30 Mar 2017 21:46:46 -0400 Subject: [PATCH 0263/2465] spelling nits --- src/main/java/com/hubspot/jinjava/lib/filter/Filter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/Filter.java b/src/main/java/com/hubspot/jinjava/lib/filter/Filter.java index 54697df89..ccf486ef4 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/Filter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/Filter.java @@ -39,7 +39,7 @@ public interface Filter extends Importable { Object filter(Object var, JinjavaInterpreter interpreter, String... args); /* - * The JinjaJava parser calls filters giving to them two list of parameters: + * The JinJava parser calls filters giving to them two lists of parameters: * - Positional arguments as Object[] * - Named arguments as Map * From 1872cb6dcddd2f43f910bcb949ed9951fbffa0cf Mon Sep 17 00:00:00 2001 From: Jose Galarza Date: Fri, 31 Mar 2017 10:10:14 +0100 Subject: [PATCH 0264/2465] Unfolding imports --- .../jinjava/el/JinjavaInterpreterResolver.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java index 6f2f0b932..3aca1fffe 100644 --- a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java @@ -7,7 +7,15 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; import javax.el.ArrayELResolver; import javax.el.CompositeELResolver; @@ -17,11 +25,16 @@ import javax.el.PropertyNotFoundException; import javax.el.ResourceBundleELResolver; -import com.hubspot.jinjava.el.ext.*; import org.apache.commons.lang3.LocaleUtils; import org.apache.commons.lang3.StringUtils; import com.google.common.collect.ImmutableMap; +import com.hubspot.jinjava.el.ext.AbstractCallableMethod; +import com.hubspot.jinjava.el.ext.ExtendedParser; +import com.hubspot.jinjava.el.ext.JinjavaBeanELResolver; +import com.hubspot.jinjava.el.ext.JinjavaListELResolver; +import com.hubspot.jinjava.el.ext.NamedParameter; + import com.hubspot.jinjava.interpret.DisabledException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.TemplateError; From 3a36389d39224454a76968a3cdc33e7e7a78eeac Mon Sep 17 00:00:00 2001 From: Jose Galarza Date: Fri, 31 Mar 2017 10:48:44 +0100 Subject: [PATCH 0265/2465] Avoid some unnecessary object creation --- .../jinjava/el/JinjavaInterpreterResolver.java | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java index 3aca1fffe..de54f73fc 100644 --- a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java @@ -121,15 +121,11 @@ private Object[] generateMethodParams(Object method, Object[] astParams) { return astParams; // We only change the signature method for filters } - List methodParams = new ArrayList() {{ - add(astParams[0]); // Left Value - add(astParams[1]); // JinjavaInterpreter - }}; - List args = new ArrayList<>(); Map kwargs = new HashMap<>(); - for (Object param: Arrays.asList(astParams).subList(methodParams.size(), astParams.length)) { + // 2 -> Ignore the Left Value (0) and the JinjavaInterpreter (1) + for (Object param: Arrays.asList(astParams).subList(2, astParams.length)) { if (param instanceof NamedParameter) { NamedParameter namedParameter = (NamedParameter) param; kwargs.put(namedParameter.getName(), namedParameter.getValue()); @@ -138,10 +134,7 @@ private Object[] generateMethodParams(Object method, Object[] astParams) { } } - methodParams.add(args.toArray()); - methodParams.add(kwargs); - - return methodParams.toArray(); + return new Object[] {astParams[0], astParams[1], args.toArray(), kwargs}; } private Object getValue(ELContext context, Object base, Object property, boolean errOnUnknownProp) { From 9c4c6afb95023493f74efc477bf25041c82bebd3 Mon Sep 17 00:00:00 2001 From: Jose Galarza Date: Fri, 31 Mar 2017 11:26:47 +0100 Subject: [PATCH 0266/2465] Testing advanced filters --- .../lib/filter/AdvancedFilterTest.java | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 src/test/java/com/hubspot/jinjava/lib/filter/AdvancedFilterTest.java diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/AdvancedFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/AdvancedFilterTest.java new file mode 100644 index 000000000..a20012248 --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/lib/filter/AdvancedFilterTest.java @@ -0,0 +1,126 @@ +package com.hubspot.jinjava.lib.filter; + +import com.hubspot.jinjava.Jinjava; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +public class AdvancedFilterTest { + + Jinjava jinjava; + + @Test + public void testOnlyArgs() { + jinjava = new Jinjava(); + + Object[] expectedArgs = new Object[] {3L, 1L}; + Map expectedKwargs = new HashMap<>(); + + jinjava.getGlobalContext().registerFilter(new MyMirrorFilter(expectedArgs, expectedKwargs)); + + assertThat(jinjava.render("{{ 'test'|mirror(3, 1) }}", new HashMap<>())).isEqualTo("test"); + } + + @Test + public void testOnlyKwargs() { + jinjava = new Jinjava(); + + Object[] expectedArgs = new Object[] {}; + Map expectedKwargs = new HashMap() {{ + put("named10", "str"); + put("named2", 3L); + put("namedB", true); + }}; + + jinjava.getGlobalContext().registerFilter(new MyMirrorFilter(expectedArgs, expectedKwargs)); + + assertThat(jinjava.render("{{ 'test'|mirror(named2=3, named10='str', namedB=true) }}", new HashMap<>())).isEqualTo("test"); + } + + @Test + public void testMixedArgsAndKwargs() { + jinjava = new Jinjava(); + + Object[] expectedArgs = new Object[] {1L, 2L}; + Map expectedKwargs = new HashMap() {{ + put("named", "test"); + }}; + + jinjava.getGlobalContext().registerFilter(new MyMirrorFilter(expectedArgs, expectedKwargs)); + + assertThat(jinjava.render("{{ 'test'|mirror(1, 2, named='test') }}", new HashMap<>())).isEqualTo("test"); + } + + @Test + public void testUnorderedArgsAndKwargs() { + jinjava = new Jinjava(); + + Object[] expectedArgs = new Object[] {"1", 2L}; + Map expectedKwargs = new HashMap() {{ + put("named", "test"); + }}; + + jinjava.getGlobalContext().registerFilter(new MyMirrorFilter(expectedArgs, expectedKwargs)); + + assertThat(jinjava.render("{{ 'test'|mirror('1', named='test', 2) }}", new HashMap<>())).isEqualTo("test"); + } + + @Test + public void testRepeatedKwargs() { + jinjava = new Jinjava(); + + Object[] expectedArgs = new Object[] {true}; + Map expectedKwargs = new HashMap() {{ + put("named", "overwrite"); + }}; + + jinjava.getGlobalContext().registerFilter(new MyMirrorFilter(expectedArgs, expectedKwargs)); + + assertThat(jinjava.render("{{ 'test'|mirror(true, named='test', named='overwrite') }}", new HashMap<>())).isEqualTo("test"); + } + + private static class MyMirrorFilter implements AdvancedFilter { + private Object[] expectedArgs; + private Map expectedKwargs; + + MyMirrorFilter(Object[] args, Map kwargs) { + this.expectedArgs = args; + this.expectedKwargs = kwargs; + } + + @Override + public String getName() { + return "mirror"; + } + + @Override + public Object filter(Object var, JinjavaInterpreter interpreter, Object[] args, Map kwargs) { + if (!Arrays.equals(expectedArgs, args)) { + throw new RuntimeException( + "Args are different than expected: " + + Arrays.toString(args) + + " to " + + Arrays.toString(expectedArgs) + ); + } + + if (!expectedKwargs.equals(kwargs)) { + throw new RuntimeException( + "Kwargs are different than expected: " + + Arrays.toString(kwargs.entrySet().toArray()) + + " to " + + Arrays.toString(expectedKwargs.entrySet().toArray()) + ); + } + + return var; + } + } + +} From 26083b8188131c11a4a202eb337147f3d2a2b9f5 Mon Sep 17 00:00:00 2001 From: James Magnarelli Date: Fri, 31 Mar 2017 11:46:04 -0400 Subject: [PATCH 0267/2465] Add test to show that max output size is applied to total output rather than just individual node sizes. --- .../jinjava/interpret/JinjavaInterpreterTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java index 549e47211..49f3a2c12 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java @@ -167,4 +167,18 @@ public void itLimitsOutputSize() throws Exception { renderResult = new Jinjava(outputSizeLimitedConfig).renderForResult(output, new HashMap<>()); assertThat(renderResult.getErrors().get(0).getMessage()).contains("OutputTooBigException"); } + + @Test + public void itLimitsOutputSizeWhenSumOfNodeSizesExceedsMax() throws Exception { + JinjavaConfig outputSizeLimitedConfig = JinjavaConfig.newBuilder().withMaxOutputSize(19).build(); + String input = "1234567890{% block testchild %}abcdefghij{% endblock %}"; + String output = "1234567890abcdefghij"; // Note that this exceeds the max size + + RenderResult renderResult = new Jinjava().renderForResult(input, new HashMap<>()); + assertThat(renderResult.getOutput()).isEqualTo(output); + assertThat(renderResult.hasErrors()).isFalse(); + + renderResult = new Jinjava(outputSizeLimitedConfig).renderForResult(input, new HashMap<>()); + assertThat(renderResult.getErrors().get(0).getMessage()).contains("OutputTooBigException"); + } } From 6347b412c65a578d6bfddfaf7f78005ed9f9758a Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 31 Mar 2017 13:09:53 -0400 Subject: [PATCH 0268/2465] remove extra space --- src/main/java/com/hubspot/jinjava/tree/output/OutputList.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/tree/output/OutputList.java b/src/main/java/com/hubspot/jinjava/tree/output/OutputList.java index 1347d13fa..374ed3fa0 100644 --- a/src/main/java/com/hubspot/jinjava/tree/output/OutputList.java +++ b/src/main/java/com/hubspot/jinjava/tree/output/OutputList.java @@ -12,7 +12,6 @@ public class OutputList { private long maxOutputSize; public OutputList(long maxOutputSize) { - this.maxOutputSize = maxOutputSize; } From 026d0069d817068ba0c94b5debb1ceaf1c014ec2 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 31 Mar 2017 15:17:48 -0400 Subject: [PATCH 0269/2465] Enforce max size limit after block resolution Previously I was only checking size as we rendered individual output nodes. This helped prevent any one node from getting too big and short circuiting the render, but it was possible to have output that was too big when adding up the output nodes and rendered blocks. --- .../interpret/OutputTooBigException.java | 6 +++-- .../jinjava/tree/output/OutputList.java | 23 +++++++++++++++---- .../interpret/JinjavaInterpreterTest.java | 5 ++-- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/OutputTooBigException.java b/src/main/java/com/hubspot/jinjava/interpret/OutputTooBigException.java index 94b58f45e..f24655a11 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/OutputTooBigException.java +++ b/src/main/java/com/hubspot/jinjava/interpret/OutputTooBigException.java @@ -2,15 +2,17 @@ public class OutputTooBigException extends RuntimeException { + private long maxSize; private final long size; - public OutputTooBigException(long size) { + public OutputTooBigException(long maxSize, long size) { + this.maxSize = maxSize; this.size = size; } @Override public String getMessage() { - return String.format("%d byte output rendered", size); + return String.format("%d byte output rendered, over limit of %d bytes", size, maxSize); } } diff --git a/src/main/java/com/hubspot/jinjava/tree/output/OutputList.java b/src/main/java/com/hubspot/jinjava/tree/output/OutputList.java index 374ed3fa0..e22ceec52 100644 --- a/src/main/java/com/hubspot/jinjava/tree/output/OutputList.java +++ b/src/main/java/com/hubspot/jinjava/tree/output/OutputList.java @@ -9,7 +9,8 @@ public class OutputList { private final List nodes = new LinkedList<>(); private final List blocks = new LinkedList<>(); - private long maxOutputSize; + private final long maxOutputSize; + private long currentSize; public OutputList(long maxOutputSize) { this.maxOutputSize = maxOutputSize; @@ -17,14 +18,22 @@ public OutputList(long maxOutputSize) { public void addNode(OutputNode node) { - if (maxOutputSize > 0 && node.getSize() > maxOutputSize) { - throw new OutputTooBigException(node.getSize()); + if (maxOutputSize > 0 && currentSize + node.getSize() > maxOutputSize) { + throw new OutputTooBigException(maxOutputSize, currentSize + node.getSize()); } + currentSize += node.getSize(); nodes.add(node); if (node instanceof BlockPlaceholderOutputNode) { - blocks.add((BlockPlaceholderOutputNode) node); + BlockPlaceholderOutputNode blockNode = (BlockPlaceholderOutputNode) node; + + if (maxOutputSize > 0 && currentSize + blockNode.getSize() > maxOutputSize) { + throw new OutputTooBigException(maxOutputSize, currentSize + blockNode.getSize()); + } + + currentSize += blockNode.getSize(); + blocks.add(blockNode); } } @@ -35,7 +44,13 @@ public List getBlocks() { public String getValue() { StringBuilder val = new StringBuilder(); + long valueSize = 0; + for (OutputNode node : nodes) { + if (maxOutputSize > 0 && valueSize + node.getSize() > maxOutputSize) { + throw new OutputTooBigException(maxOutputSize, valueSize + node.getSize()); + } + valueSize += node.getSize(); val.append(node.getValue()); } diff --git a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java index 49f3a2c12..c6dd71241 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java @@ -171,14 +171,15 @@ public void itLimitsOutputSize() throws Exception { @Test public void itLimitsOutputSizeWhenSumOfNodeSizesExceedsMax() throws Exception { JinjavaConfig outputSizeLimitedConfig = JinjavaConfig.newBuilder().withMaxOutputSize(19).build(); - String input = "1234567890{% block testchild %}abcdefghij{% endblock %}"; - String output = "1234567890abcdefghij"; // Note that this exceeds the max size + String input = "1234567890{% block testchild %}1234567890{% endblock %}"; + String output = "12345678901234567890"; // Note that this exceeds the max size RenderResult renderResult = new Jinjava().renderForResult(input, new HashMap<>()); assertThat(renderResult.getOutput()).isEqualTo(output); assertThat(renderResult.hasErrors()).isFalse(); renderResult = new Jinjava(outputSizeLimitedConfig).renderForResult(input, new HashMap<>()); + assertThat(renderResult.hasErrors()).isTrue(); assertThat(renderResult.getErrors().get(0).getMessage()).contains("OutputTooBigException"); } } From 502c6f3595761ef729447f45f08c9275f226a95c Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 31 Mar 2017 15:39:41 -0400 Subject: [PATCH 0270/2465] fix usage of detault encoding --- .../jinjava/tree/output/BlockPlaceholderOutputNode.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/tree/output/BlockPlaceholderOutputNode.java b/src/main/java/com/hubspot/jinjava/tree/output/BlockPlaceholderOutputNode.java index 50ae763df..95eb6bc95 100644 --- a/src/main/java/com/hubspot/jinjava/tree/output/BlockPlaceholderOutputNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/output/BlockPlaceholderOutputNode.java @@ -1,5 +1,9 @@ package com.hubspot.jinjava.tree.output; +import java.nio.charset.Charset; + +import com.google.common.base.Charsets; + public class BlockPlaceholderOutputNode implements OutputNode { private final String blockName; @@ -32,7 +36,7 @@ public String getValue() { @Override public long getSize() { - return output == null ? 0 : output.getBytes().length; + return output == null ? 0 : output.getBytes(Charset.forName(Charsets.UTF_8.name())).length; } @Override From 85a9267284c5171a53e54710f728b5c1a00a2769 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 31 Mar 2017 15:43:15 -0400 Subject: [PATCH 0271/2465] 2.1.17 --- CHANGES.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 3ee27729f..512599030 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Jinjava Releases # +### 2017-03-31 Version 2.1.17 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.17%22)) ### + +* added config option to limit the rendered output size +* added named parameter support for filters +* added `type()` function + ### 2017-03-09 Version 2.1.16 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.16%22)) ### * disabled functions, filters and tags now add to template errors rather than throwing a fatal exception From 27ae83f389f0f5a6649e56f7cd7b0d3f3f0f8b39 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Fri, 31 Mar 2017 19:50:56 +0000 Subject: [PATCH 0272/2465] [maven-release-plugin] prepare release jinjava-2.1.17 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 4b8adcebe..dabc6f3cd 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.17-SNAPSHOT + 2.1.17 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.1.17 From fc91ceeffaa5135551606f8a175b8b83e26daf06 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Fri, 31 Mar 2017 19:50:56 +0000 Subject: [PATCH 0273/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index dabc6f3cd..6bc0d2383 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.17 + 2.1.18-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.1.17 + HEAD From e13c07d22feda6a4782b08f168ce519ee63998ee Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Wed, 5 Apr 2017 13:27:41 -0400 Subject: [PATCH 0274/2465] use correct length() --- .../com/hubspot/jinjava/tree/output/RenderedOutputNode.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/tree/output/RenderedOutputNode.java b/src/main/java/com/hubspot/jinjava/tree/output/RenderedOutputNode.java index cedd76392..c68fe35cf 100644 --- a/src/main/java/com/hubspot/jinjava/tree/output/RenderedOutputNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/output/RenderedOutputNode.java @@ -1,5 +1,9 @@ package com.hubspot.jinjava.tree.output; +import java.nio.charset.Charset; + +import com.google.common.base.Charsets; + public class RenderedOutputNode implements OutputNode { private final String output; @@ -20,6 +24,6 @@ public String toString() { @Override public long getSize() { - return output == null ? 0 : output.length(); + return output == null ? 0 : output.getBytes(Charset.forName(Charsets.UTF_8.name())).length; } } From 00078c6a090556b5a78f3d47708845da60390cb0 Mon Sep 17 00:00:00 2001 From: Steve Gutz Date: Fri, 7 Apr 2017 16:50:23 -0400 Subject: [PATCH 0275/2465] Use Collections.emptyIterator() instead of Iterators.emptyIterator() --- src/main/java/com/hubspot/jinjava/util/ObjectIterator.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/util/ObjectIterator.java b/src/main/java/com/hubspot/jinjava/util/ObjectIterator.java index 94d6c1d1a..4dbdbf2f3 100644 --- a/src/main/java/com/hubspot/jinjava/util/ObjectIterator.java +++ b/src/main/java/com/hubspot/jinjava/util/ObjectIterator.java @@ -16,6 +16,7 @@ package com.hubspot.jinjava.util; import java.util.Collection; +import java.util.Collections; import java.util.Iterator; import java.util.Map; @@ -28,7 +29,7 @@ private ObjectIterator() {} @SuppressWarnings("unchecked") public static ForLoop getLoop(Object obj) { if (obj == null) { - return new ForLoop(Iterators.emptyIterator(), 0); + return new ForLoop(Collections.emptyIterator(), 0); } // collection if (obj instanceof Collection) { From ce1972c577149983357d89c0ea6a7425f5ea6f10 Mon Sep 17 00:00:00 2001 From: wgoodrich Date: Mon, 10 Apr 2017 10:24:49 -0400 Subject: [PATCH 0276/2465] avoid npe when param is null --- .../hubspot/jinjava/lib/filter/Filter.java | 12 +++++----- .../lib/filter/AdvancedFilterTest.java | 24 +++++++++++++++---- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/Filter.java b/src/main/java/com/hubspot/jinjava/lib/filter/Filter.java index ccf486ef4..e95220809 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/Filter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/Filter.java @@ -15,14 +15,14 @@ **********************************************************************/ package com.hubspot.jinjava.lib.filter; -import com.hubspot.jinjava.interpret.JinjavaInterpreter; -import com.hubspot.jinjava.lib.Importable; -import org.apache.commons.lang3.ArrayUtils; - import java.util.Arrays; -import java.util.Collection; import java.util.Map; +import org.apache.commons.lang3.ArrayUtils; + +import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.lib.Importable; + public interface Filter extends Importable { /** @@ -49,7 +49,7 @@ public interface Filter extends Importable { default Object filter(Object var, JinjavaInterpreter interpreter, Object[] args, Map kwargs) { // We append the named arguments at the end of the positional ones Object[] allArgs = ArrayUtils.addAll(args, kwargs.values().toArray()); - String[] stringArgs = Arrays.stream(allArgs).map(Object::toString).toArray(String[]::new); + String[] stringArgs = Arrays.stream(allArgs).map(arg -> arg != null ? arg.toString() : null).toArray(String[]::new); return filter(var, interpreter, stringArgs); } diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/AdvancedFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/AdvancedFilterTest.java index a20012248..74d9fd78d 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/AdvancedFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/AdvancedFilterTest.java @@ -1,15 +1,15 @@ package com.hubspot.jinjava.lib.filter; -import com.hubspot.jinjava.Jinjava; -import com.hubspot.jinjava.interpret.JinjavaInterpreter; -import org.junit.Before; -import org.junit.Test; +import static org.assertj.core.api.Assertions.assertThat; import java.util.Arrays; import java.util.HashMap; import java.util.Map; -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Test; + +import com.hubspot.jinjava.Jinjava; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; public class AdvancedFilterTest { @@ -43,6 +43,20 @@ public void testOnlyKwargs() { assertThat(jinjava.render("{{ 'test'|mirror(named2=3, named10='str', namedB=true) }}", new HashMap<>())).isEqualTo("test"); } + @Test + public void itTestsNullKwargs() { + jinjava = new Jinjava(); + + Object[] expectedArgs = new Object[] {}; + Map expectedKwargs = new HashMap() {{ + put("named1", null); + }}; + + jinjava.getGlobalContext().registerFilter(new MyMirrorFilter(expectedArgs, expectedKwargs)); + + assertThat(jinjava.render("{{ 'test'|divide(named1) }}", new HashMap<>())).isEqualTo("test"); + } + @Test public void testMixedArgsAndKwargs() { jinjava = new Jinjava(); From d1200456cdf7444f514254d7ac34b043db24a7f4 Mon Sep 17 00:00:00 2001 From: wgoodrich Date: Mon, 10 Apr 2017 13:25:11 -0400 Subject: [PATCH 0277/2465] clean up null check on arg --- src/main/java/com/hubspot/jinjava/lib/filter/Filter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/Filter.java b/src/main/java/com/hubspot/jinjava/lib/filter/Filter.java index e95220809..5c9bd1eef 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/Filter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/Filter.java @@ -17,6 +17,7 @@ import java.util.Arrays; import java.util.Map; +import java.util.Objects; import org.apache.commons.lang3.ArrayUtils; @@ -49,7 +50,7 @@ public interface Filter extends Importable { default Object filter(Object var, JinjavaInterpreter interpreter, Object[] args, Map kwargs) { // We append the named arguments at the end of the positional ones Object[] allArgs = ArrayUtils.addAll(args, kwargs.values().toArray()); - String[] stringArgs = Arrays.stream(allArgs).map(arg -> arg != null ? arg.toString() : null).toArray(String[]::new); + String[] stringArgs = Arrays.stream(allArgs).map(arg -> Objects.toString(arg, null)).toArray(String[]::new); return filter(var, interpreter, stringArgs); } From ceb79d16e669f178ccc7675a1fe7e2689099526f Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Mon, 10 Apr 2017 19:30:50 +0000 Subject: [PATCH 0278/2465] [maven-release-plugin] prepare release jinjava-2.1.18 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6bc0d2383..979d4302b 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.18-SNAPSHOT + 2.1.18 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.1.18 From 27469f4fb560aaafb7171aad6e777c19d9037ffc Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Mon, 10 Apr 2017 19:30:50 +0000 Subject: [PATCH 0279/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 979d4302b..5708bd608 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.18 + 2.1.19-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.1.18 + HEAD From a1fc2621ead1c46f571ca88df8fb6954103f4db8 Mon Sep 17 00:00:00 2001 From: bgoodies Date: Tue, 11 Apr 2017 08:59:13 -0400 Subject: [PATCH 0280/2465] Update CHANGES.md --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 512599030..1590c7c9b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Jinjava Releases # +### 2017-04-10 Version 2.1.18 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.18%22)) ### + +* fix bug when passing null argument to `filter` + ### 2017-03-31 Version 2.1.17 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.17%22)) ### * added config option to limit the rendered output size From 73d9f41103145e6839d57f3b3ac547ecc680b331 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 11 Apr 2017 21:29:24 -0400 Subject: [PATCH 0281/2465] Sort named parameters --- .../jinjava/el/JinjavaInterpreterResolver.java | 5 ++--- .../hubspot/jinjava/lib/filter/SortFilterTest.java | 11 +++++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java index 24c4417a2..13da955d2 100644 --- a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java @@ -10,7 +10,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Date; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -34,7 +34,6 @@ import com.hubspot.jinjava.el.ext.JinjavaBeanELResolver; import com.hubspot.jinjava.el.ext.JinjavaListELResolver; import com.hubspot.jinjava.el.ext.NamedParameter; - import com.hubspot.jinjava.interpret.DisabledException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.TemplateError; @@ -122,7 +121,7 @@ private Object[] generateMethodParams(Object method, Object[] astParams) { } List args = new ArrayList<>(); - Map kwargs = new HashMap<>(); + Map kwargs = new LinkedHashMap<>(); // 2 -> Ignore the Left Value (0) and the JinjavaInterpreter (1) for (Object param: Arrays.asList(astParams).subList(2, astParams.length)) { diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/SortFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/SortFilterTest.java index 01bebbec5..b1c7be9ce 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/SortFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/SortFilterTest.java @@ -35,6 +35,13 @@ public void sortStringsCaseSensitive() { assertThat(render("(false, true)", "foo", "Foo", "bar")).isEqualTo("Foobarfoo"); } + @Test + public void sortWithNamedAttributes() throws Exception { + // even if named attributes were never supported for this filter, ensure parameters are passed in order and it works + assertThat(render("(reverse=false, case_sensitive=false, attribute='foo.date')", + new MyBar(new MyFoo(new Date(250L))), new MyBar(new MyFoo(new Date(0L))), new MyBar(new MyFoo(new Date(100000000L))))).isEqualTo("0250100000000"); + } + @Test public void sortStringsCaseInsensitive() { assertThat(render("()", "foo", "Foo", "bar")).isEqualTo("barfooFoo"); @@ -65,7 +72,7 @@ String render(String sortExtra, Object... items) { public static class MyFoo { private Date date; - public MyFoo(Date date) { + MyFoo(Date date) { this.date = date; } @@ -82,7 +89,7 @@ public String toString() { public static class MyBar { private MyFoo foo; - public MyBar(MyFoo foo) { + MyBar(MyFoo foo) { this.foo = foo; } From 00ca87dcfa61547b97aea8cc60f64961c76d528d Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 11 Apr 2017 21:36:52 -0400 Subject: [PATCH 0282/2465] Update CHANGES.md --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 1590c7c9b..dc68404be 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Jinjava Releases # +### 2017-04-11 Version 2.1.19 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.19%22)) ### + +* preserve order of named parameters + ### 2017-04-10 Version 2.1.18 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.18%22)) ### * fix bug when passing null argument to `filter` From f7ee77db735f358bdbe343c471e8f2a9b645244f Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Wed, 12 Apr 2017 01:41:17 +0000 Subject: [PATCH 0283/2465] [maven-release-plugin] prepare release jinjava-2.1.19 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 5708bd608..adeed1c37 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.19-SNAPSHOT + 2.1.19 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.1.19 From d2faf4bef19bb40368f903c1e65e2cc2455ef045 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Wed, 12 Apr 2017 01:41:17 +0000 Subject: [PATCH 0284/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index adeed1c37..61cba3bdb 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.19 + 2.1.20-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.1.19 + HEAD From 487b5e472a7b8b447cb0e25b0bad35fc0ac4604c Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Sun, 16 Apr 2017 10:01:41 -0400 Subject: [PATCH 0285/2465] Track resolved functions in context --- .../com/hubspot/jinjava/el/MacroFunctionMapper.java | 4 ++++ .../java/com/hubspot/jinjava/interpret/Context.java | 9 +++++++++ .../java/com/hubspot/jinjava/lib/SimpleLibrary.java | 2 +- .../com/hubspot/jinjava/el/ExpressionResolverTest.java | 10 ++++++++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/el/MacroFunctionMapper.java b/src/main/java/com/hubspot/jinjava/el/MacroFunctionMapper.java index 11766b860..3983247e6 100644 --- a/src/main/java/com/hubspot/jinjava/el/MacroFunctionMapper.java +++ b/src/main/java/com/hubspot/jinjava/el/MacroFunctionMapper.java @@ -36,6 +36,10 @@ public Method resolveFunction(String prefix, String localName) { throw new DisabledException(functionName); } + if (map.containsKey(functionName)) { + context.addResolvedFunction(functionName); + } + return map.get(functionName); } diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index e2f329aac..a1cf20b63 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -61,6 +61,7 @@ public enum Library { private final Set resolvedExpressions = new HashSet<>(); private final Set resolvedValues = new HashSet<>(); + private final Set resolvedFunctions = new HashSet<>(); private final ExpTestLibrary expTestLibrary; private final FilterLibrary filterLibrary; @@ -192,6 +193,14 @@ public boolean wasValueResolved(String value) { return resolvedValues.contains(value); } + public Set getResolvedFunctions() { + return ImmutableSet.copyOf(resolvedFunctions); + } + + public void addResolvedFunction(String value) { + resolvedFunctions.add(value); + } + public List getSuperBlock() { if (superBlock != null) { return superBlock; diff --git a/src/main/java/com/hubspot/jinjava/lib/SimpleLibrary.java b/src/main/java/com/hubspot/jinjava/lib/SimpleLibrary.java index 52c733ded..97598beb6 100644 --- a/src/main/java/com/hubspot/jinjava/lib/SimpleLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/SimpleLibrary.java @@ -34,7 +34,7 @@ public abstract class SimpleLibrary { - private Map lib = new HashMap(); + private Map lib = new HashMap<>(); private Set disabled = new HashSet(); protected SimpleLibrary(boolean registerDefaults) { diff --git a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java index 1f60429c5..c1bc8bf1a 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java @@ -423,6 +423,16 @@ public void itBlocksDisabledExpTests() throws Exception { } } + @Test + public void itStoresResolvedFunctions() throws Exception { + context.put("datetime", 12345); + final JinjavaConfig config = JinjavaConfig.newBuilder().build(); + String template = "{% for i in range(1, 5) %}{{i}} {% endfor %}\n{{ unixtimestamp(datetime) }}"; + final RenderResult renderResult = jinjava.renderForResult(template, context, config); + assertThat(renderResult.getOutput()).isEqualTo("1 2 3 4 \n12000"); + assertThat(renderResult.getContext().getResolvedFunctions()).hasSameElementsAs(ImmutableSet.of(":range", ":unixtimestamp")); + } + @Test public void presentOptionalProperty() { context.put("myobj", new OptionalProperty(null, "foo")); From 9b95267c45c307cd7654ddc4ee8179f5a2fce6be Mon Sep 17 00:00:00 2001 From: Steve Gutz Date: Fri, 21 Apr 2017 14:55:48 -0400 Subject: [PATCH 0286/2465] Stop using Guava Objects methods deprecated in 19 --- .../jinjava/interpret/TemplateError.java | 21 +++++++++---------- .../com/hubspot/jinjava/lib/tag/MacroTag.java | 4 ++-- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java index dacf60495..141fdf545 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java @@ -5,7 +5,6 @@ import org.apache.commons.lang3.exception.ExceptionUtils; -import com.google.common.base.Objects; import com.google.common.collect.ImmutableMap; import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory; import com.hubspot.jinjava.interpret.errorcategory.TemplateErrorCategory; @@ -191,16 +190,16 @@ public TemplateError serializable() { @Override public String toString() { - return Objects.toStringHelper(this) - .add("severity", severity) - .add("reason", reason) - .add("message", message) - .add("fieldName", fieldName) - .add("lineno", lineno) - .add("item", item) - .add("category", category) - .add("categoryErrors", categoryErrors) - .toString(); + return "TemplateError{" + + "severity=" + severity + + ", reason=" + reason + + ", item=" + item + + ", message='" + message + '\'' + + ", fieldName='" + fieldName + '\'' + + ", lineno=" + lineno + + ", category=" + category + + ", categoryErrors=" + categoryErrors + + '}'; } } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/MacroTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/MacroTag.java index 7a1117d79..72824ca59 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/MacroTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/MacroTag.java @@ -7,8 +7,8 @@ import org.apache.commons.lang3.StringUtils; -import com.google.common.base.Objects; import com.google.common.base.Splitter; +import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaParam; @@ -70,7 +70,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { } String name = matcher.group(1); - String args = Objects.firstNonNull(matcher.group(2), ""); + String args = Strings.nullToEmpty(matcher.group(2)); LinkedHashMap argNamesWithDefaults = new LinkedHashMap<>(); From 64ac1f77b1e04b812a30ab295f4e91642d320e27 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 12 May 2017 10:50:16 -0400 Subject: [PATCH 0287/2465] remove FileResourceLocator as a default --- CHANGES.md | 4 ++++ README.md | 22 ++++++++++++++++--- pom.xml | 7 +++++- .../java/com/hubspot/jinjava/Jinjava.java | 4 +--- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index dc68404be..5ad99659d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Jinjava Releases # +### 2017-05-12 Version 2.2.0 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.0%22)) ### + +* Removes `FileResourceLocator` as a default `ResourceLocator` to close a security hole. See the [README](README.md#template-loading) for details on how to reenable it. + ### 2017-04-11 Version 2.1.19 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.1.19%22)) ### * preserve order of named parameters diff --git a/README.md b/README.md index de35185b5..661be790b 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ jinjava Java-based template engine based on django template syntax, adapted to render jinja templates (at least the subset of jinja in use in HubSpot content). Currently used in production to render thousands of websites with hundreds of millions of page views per month on the [HubSpot COS](http://www.hubspot.com/products/sites). + *Note*: Requires Java >= 8. Originally forked from [jangod](https://code.google.com/p/jangod/). Get it: @@ -17,10 +18,12 @@ Get it: com.hubspot.jinjava jinjava - 2.1.14 + { LATEST_VERSION } ``` +where LATEST_VERSION is the [latest version from CHANGES](CHANGES.md). + or if you're stuck on java 7: ```xml @@ -67,8 +70,12 @@ Jinjava needs to know how to interpret template paths, so it can properly handle {% extends "foo/bar/base.html" %} ``` -By default, it will load a ```FileLocator```; you will likely want to provide your own implementation of -```ResourceLoader``` to hook into your application's template repository, and then tell jinjava about it: +By default, it will load only a `ClasspathResourceLocator`. If you want to allow Jinjava to load any file from the +file system, you can add a `FileResourceLocator`. Be aware the security risks of allowing user input to prevent a user +from adding code such as `{% include '/etc/password' %}`. + +You will likely want to provide your own implementation of +`ResourceLoader` to hook into your application's template repository, and then tell jinjava about it: ```java JinjavaConfig config = new JinjavaConfig(); @@ -77,6 +84,15 @@ Jinjava jinjava = new Jinjava(config); jinjava.setResourceLocator(new MyCustomResourceLocator()); ``` +To use more than one `ResourceLocator`, use a `CascadingResourceLocator`. + +```java +JinjavaConfig config = new JinjavaConfig(); + +Jinjava jinjava = new Jinjava(config); +jinjava.setResourceLocator(new MyCustomResourceLocator(), new FileResourceLocator()); +``` + ### Custom tags, filters and functions You can provide custom jinja tags, filters, and static functions to the template engine. diff --git a/pom.xml b/pom.xml index 61cba3bdb..cf7cf0fb8 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.1.20-SNAPSHOT + 2.2.0-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -26,6 +26,11 @@ Jared Stehler jstehler@hubspot.com + + boulter + Jeff Boulter + boulter@hubspot.com + diff --git a/src/main/java/com/hubspot/jinjava/Jinjava.java b/src/main/java/com/hubspot/jinjava/Jinjava.java index a76366017..3e3265136 100644 --- a/src/main/java/com/hubspot/jinjava/Jinjava.java +++ b/src/main/java/com/hubspot/jinjava/Jinjava.java @@ -33,9 +33,7 @@ import com.hubspot.jinjava.interpret.RenderResult; import com.hubspot.jinjava.interpret.TemplateError; import com.hubspot.jinjava.interpret.TemplateError.ErrorType; -import com.hubspot.jinjava.loader.CascadingResourceLocator; import com.hubspot.jinjava.loader.ClasspathResourceLocator; -import com.hubspot.jinjava.loader.FileLocator; import com.hubspot.jinjava.loader.ResourceLocator; import de.odysseus.el.ExpressionFactoryImpl; @@ -87,7 +85,7 @@ public Jinjava(JinjavaConfig globalConfig) { TypeConverter converter = new TruthyTypeConverter(); this.expressionFactory = new ExpressionFactoryImpl(expConfig, converter); - this.resourceLocator = new CascadingResourceLocator(new ClasspathResourceLocator(), new FileLocator()); + this.resourceLocator = new ClasspathResourceLocator(); } /** From bae759ad3ba2668d156d1d293ba57e92181fa9c4 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Fri, 12 May 2017 15:09:07 +0000 Subject: [PATCH 0288/2465] [maven-release-plugin] prepare release jinjava-2.2.0 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index cf7cf0fb8..cc85bd5ee 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.2.0-SNAPSHOT + 2.2.0 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.2.0 From 0f9f438823b0621079f6640417dade5540aec9ba Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Fri, 12 May 2017 15:09:07 +0000 Subject: [PATCH 0289/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index cc85bd5ee..e1118e2ae 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.2.0 + 2.2.1-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.2.0 + HEAD From f2dcebef47e011b718c2dd322484fa081cb3d43a Mon Sep 17 00:00:00 2001 From: Olga Shestopalova Date: Wed, 14 Jun 2017 11:41:03 -0400 Subject: [PATCH 0290/2465] show fieldname in unknown tag error --- .../java/com/hubspot/jinjava/interpret/TemplateError.java | 8 +++++++- .../com/hubspot/jinjava/interpret/TemplateErrorTest.java | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java index 141fdf545..974f6759f 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java @@ -52,7 +52,13 @@ public static TemplateError fromSyntaxError(InterpretException ex) { } public static TemplateError fromException(TemplateSyntaxException ex) { - return new TemplateError(ErrorType.FATAL, ErrorReason.SYNTAX_ERROR, ErrorItem.OTHER, ExceptionUtils.getMessage(ex), null, ex.getLineNumber(), ex); + String fieldName = null; + + if (ex instanceof UnknownTagException) { + fieldName = ((UnknownTagException) ex).getTag(); + } + + return new TemplateError(ErrorType.FATAL, ErrorReason.SYNTAX_ERROR, ErrorItem.OTHER, ExceptionUtils.getMessage(ex), fieldName, ex.getLineNumber(), ex); } public static TemplateError fromException(Exception ex) { diff --git a/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java b/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java index 2564908a3..d1feeb371 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java @@ -20,4 +20,10 @@ public void itUsesOverloadedToStringForBaseObject() { assertThat(e.getMessage()).isEqualTo("Cannot resolve property 'other' in '{foo=bar}'"); } + @Test + public void itShowsFieldNameForUnknownTagError() { + TemplateError e = TemplateError.fromException(new UnknownTagException("unknown", "{% unknown() %}", 11)); + assertThat(e.getFieldName()).isEqualTo("unknown"); + } + } From 7c2ecd58bc52759fc16f0fc768c92314ffda8159 Mon Sep 17 00:00:00 2001 From: Olga Shestopalova Date: Wed, 14 Jun 2017 14:10:19 -0400 Subject: [PATCH 0291/2465] update CHANGES.md --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 5ad99659d..4f4e885d4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Jinjava Releases # +### 2017-06-14 Version 2.2.1 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.1%22)) ### + +* Includes field name in unknown tag error + ### 2017-05-12 Version 2.2.0 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.0%22)) ### * Removes `FileResourceLocator` as a default `ResourceLocator` to close a security hole. See the [README](README.md#template-loading) for details on how to reenable it. From a80bc261d971e48f68a061451c3032e3ef4b7265 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Wed, 14 Jun 2017 18:20:07 +0000 Subject: [PATCH 0292/2465] [maven-release-plugin] prepare release jinjava-2.2.1 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e1118e2ae..bdfb5f32b 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.2.1-SNAPSHOT + 2.2.1 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.2.1 From e77b33aec853ff4def957c558aa7812401e2b16e Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Wed, 14 Jun 2017 18:20:07 +0000 Subject: [PATCH 0293/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index bdfb5f32b..dec215216 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.2.1 + 2.2.2-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.2.1 + HEAD From 85bc9dc71d1d0ae886373271b18dee0284bdf592 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 19 Jun 2017 14:20:39 -0400 Subject: [PATCH 0294/2465] confirm for loops work with scalar values --- .../java/com/hubspot/jinjava/lib/tag/ForTagTest.java | 10 +++++++++- src/test/resources/tags/fortag/loop-with-scalar.jinja | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 src/test/resources/tags/fortag/loop-with-scalar.jinja diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java index 82968f57a..a4dc0a9e8 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java @@ -8,7 +8,6 @@ import java.util.List; import java.util.Map; -import com.google.common.collect.ImmutableMap; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.select.Elements; @@ -17,6 +16,7 @@ import com.google.common.base.Splitter; import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.io.Resources; @@ -53,6 +53,14 @@ public void forLoopUsingLoopLastVar() throws Exception { assertThat(dom.select("h3")).hasSize(3); } + @Test + public void forLoopUsingScalarValue() throws Exception { + context.put("the_list", 999L); + TagNode tagNode = (TagNode) fixture("loop-with-scalar"); + String output = tag.interpret(tagNode, interpreter); + assertThat(output.trim()).isEqualTo("

    The Number: 999

    "); + } + @Test public void forLoopNestedFor() throws Exception { TagNode tagNode = (TagNode) fixture("nested-fors"); diff --git a/src/test/resources/tags/fortag/loop-with-scalar.jinja b/src/test/resources/tags/fortag/loop-with-scalar.jinja new file mode 100644 index 000000000..6d000e655 --- /dev/null +++ b/src/test/resources/tags/fortag/loop-with-scalar.jinja @@ -0,0 +1,3 @@ +{% for number in the_list %} +

    The Number: {{ number }}

    +{% endfor %} From e2841e9e8485743fbc4e2a9043c9d8b64415e408 Mon Sep 17 00:00:00 2001 From: Libo Song Date: Mon, 17 Jul 2017 17:52:58 -0400 Subject: [PATCH 0295/2465] Disable interpreting variables in expression. --- .../java/com/hubspot/jinjava/tree/ExpressionNode.java | 11 ----------- .../com/hubspot/jinjava/tree/ExpressionNodeTest.java | 2 +- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java index 128f109fb..05064a318 100644 --- a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java @@ -17,15 +17,12 @@ import java.util.Objects; -import org.apache.commons.lang3.StringUtils; - import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.UnknownTokenException; import com.hubspot.jinjava.lib.filter.EscapeFilter; import com.hubspot.jinjava.tree.output.OutputNode; import com.hubspot.jinjava.tree.output.RenderedOutputNode; import com.hubspot.jinjava.tree.parse.ExpressionToken; -import com.hubspot.jinjava.util.Logging; public class ExpressionNode extends Node { private static final long serialVersionUID = 341642231109911346L; @@ -47,14 +44,6 @@ public OutputNode render(JinjavaInterpreter interpreter) { String result = Objects.toString(var, ""); - if (!StringUtils.equals(result, master.getImage()) && StringUtils.contains(result, "{{")) { - try { - result = interpreter.renderFlat(result); - } catch (Exception e) { - Logging.ENGINE_LOG.warn("Error rendering variable node result", e); - } - } - if (interpreter.getContext().isAutoEscape()) { result = EscapeFilter.escapeHtmlEntities(result); } diff --git a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java index f64c81684..54fc1f4e9 100644 --- a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java @@ -30,7 +30,7 @@ public void itRendersResultAsTemplateWhenContainingVarBlocks() throws Exception context.put("place", "world"); ExpressionNode node = fixture("simplevar"); - assertThat(node.render(interpreter).toString()).isEqualTo("hello world"); + assertThat(node.render(interpreter).toString()).isEqualTo("hello {{ place }}"); } @Test From fee261d4f7e3f10f8e0dd83897e17e57441b5bb4 Mon Sep 17 00:00:00 2001 From: Libo Song Date: Wed, 19 Jul 2017 16:43:34 -0400 Subject: [PATCH 0296/2465] Make the nested interpretation configurable. --- .../com/hubspot/jinjava/JinjavaConfig.java | 21 +++++++++++++++---- .../hubspot/jinjava/tree/ExpressionNode.java | 13 ++++++++++++ .../jinjava/tree/ExpressionNodeTest.java | 15 ++++++++++++- 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/JinjavaConfig.java b/src/main/java/com/hubspot/jinjava/JinjavaConfig.java index db5068489..c670c1780 100644 --- a/src/main/java/com/hubspot/jinjava/JinjavaConfig.java +++ b/src/main/java/com/hubspot/jinjava/JinjavaConfig.java @@ -43,17 +43,18 @@ public class JinjavaConfig { private Map> disabled; private final boolean failOnUnknownTokens; + private final boolean noNestedInterpretation; public static Builder newBuilder() { return new Builder(); } public JinjavaConfig() { - this(StandardCharsets.UTF_8, Locale.ENGLISH, ZoneOffset.UTC, 10, new HashMap<>(), false, false, true, false, false, 0); + this(StandardCharsets.UTF_8, Locale.ENGLISH, ZoneOffset.UTC, 10, new HashMap<>(), false, false, true, false, false, 0, false); } public JinjavaConfig(Charset charset, Locale locale, ZoneId timeZone, int maxRenderDepth) { - this(charset, locale, timeZone, maxRenderDepth, new HashMap<>(), false, false, true, false, false, 0); + this(charset, locale, timeZone, maxRenderDepth, new HashMap<>(), false, false, true, false, false, 0, false); } private JinjavaConfig(Charset charset, @@ -67,7 +68,8 @@ private JinjavaConfig(Charset charset, boolean readOnlyResolver, boolean enableRecursiveMacroCalls, boolean failOnUnknownTokens, - long maxOutputSize) { + long maxOutputSize, + boolean noNestedInterpretation) { this.charset = charset; this.locale = locale; this.timeZone = timeZone; @@ -79,6 +81,7 @@ private JinjavaConfig(Charset charset, this.enableRecursiveMacroCalls = enableRecursiveMacroCalls; this.failOnUnknownTokens = failOnUnknownTokens; this.maxOutputSize = maxOutputSize; + this.noNestedInterpretation = noNestedInterpretation; } public Charset getCharset() { @@ -125,6 +128,10 @@ public boolean isFailOnUnknownTokens() { return failOnUnknownTokens; } + public boolean isNoNestedInterpretation() { + return noNestedInterpretation; + } + public static class Builder { private Charset charset = StandardCharsets.UTF_8; private Locale locale = Locale.ENGLISH; @@ -139,6 +146,7 @@ public static class Builder { private boolean readOnlyResolver = true; private boolean enableRecursiveMacroCalls; private boolean failOnUnknownTokens; + private boolean noNestedInterpretation; private Builder() {} @@ -197,8 +205,13 @@ public Builder withMaxOutputSize(long maxOutputSize) { return this; } + public Builder withNoNestedInpterpretation(boolean noNestedInterpretation) { + this.noNestedInterpretation = noNestedInterpretation; + return this; + } + public JinjavaConfig build() { - return new JinjavaConfig(charset, locale, timeZone, maxRenderDepth, disabled, trimBlocks, lstripBlocks, readOnlyResolver, enableRecursiveMacroCalls, failOnUnknownTokens, maxOutputSize); + return new JinjavaConfig(charset, locale, timeZone, maxRenderDepth, disabled, trimBlocks, lstripBlocks, readOnlyResolver, enableRecursiveMacroCalls, failOnUnknownTokens, maxOutputSize, noNestedInterpretation); } } diff --git a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java index 05064a318..09b9bc317 100644 --- a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java @@ -17,12 +17,15 @@ import java.util.Objects; +import org.apache.commons.lang3.StringUtils; + import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.UnknownTokenException; import com.hubspot.jinjava.lib.filter.EscapeFilter; import com.hubspot.jinjava.tree.output.OutputNode; import com.hubspot.jinjava.tree.output.RenderedOutputNode; import com.hubspot.jinjava.tree.parse.ExpressionToken; +import com.hubspot.jinjava.util.Logging; public class ExpressionNode extends Node { private static final long serialVersionUID = 341642231109911346L; @@ -44,6 +47,16 @@ public OutputNode render(JinjavaInterpreter interpreter) { String result = Objects.toString(var, ""); + if (!interpreter.getConfig().isNoNestedInterpretation()) { + if (!StringUtils.equals(result, master.getImage()) && StringUtils.contains(result, "{{")) { + try { + result = interpreter.renderFlat(result); + } catch (Exception e) { + Logging.ENGINE_LOG.warn("Error rendering variable node result", e); + } + } + } + if (interpreter.getContext().isAutoEscape()) { result = EscapeFilter.escapeHtmlEntities(result); } diff --git a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java index 54fc1f4e9..68255eb07 100644 --- a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java @@ -10,6 +10,7 @@ import com.google.common.base.Throwables; import com.google.common.io.Resources; import com.hubspot.jinjava.Jinjava; +import com.hubspot.jinjava.JinjavaConfig; import com.hubspot.jinjava.interpret.Context; import com.hubspot.jinjava.interpret.JinjavaInterpreter; @@ -30,7 +31,19 @@ public void itRendersResultAsTemplateWhenContainingVarBlocks() throws Exception context.put("place", "world"); ExpressionNode node = fixture("simplevar"); - assertThat(node.render(interpreter).toString()).isEqualTo("hello {{ place }}"); + assertThat(node.render(interpreter).toString()).isEqualTo("hello world"); + } + + @Test + public void itRendersResultWithoutInterpretateExpression() throws Exception { + final JinjavaConfig config = JinjavaConfig.newBuilder().withNoNestedInpterpretation(true).build(); + JinjavaInterpreter noNestedInterpreter = new Jinjava(config).newInterpreter(); + Context contextNoNestedInterpretation = noNestedInterpreter.getContext(); + contextNoNestedInterpretation.put("myvar", "hello {{ place }}"); + contextNoNestedInterpretation.put("place", "world"); + + ExpressionNode node = fixture("simplevar"); + assertThat(node.render(noNestedInterpreter).toString()).isEqualTo("hello {{ place }}"); } @Test From 389ca9b5222fbf532b7602baf76d10ef668ac32d Mon Sep 17 00:00:00 2001 From: Libo Song Date: Wed, 19 Jul 2017 17:06:30 -0400 Subject: [PATCH 0297/2465] Rename the the configuration name to enableNestedInterpretation. --- .../com/hubspot/jinjava/JinjavaConfig.java | 22 +++++++++---------- .../hubspot/jinjava/tree/ExpressionNode.java | 2 +- .../jinjava/tree/ExpressionNodeTest.java | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/JinjavaConfig.java b/src/main/java/com/hubspot/jinjava/JinjavaConfig.java index c670c1780..3e39d7dd9 100644 --- a/src/main/java/com/hubspot/jinjava/JinjavaConfig.java +++ b/src/main/java/com/hubspot/jinjava/JinjavaConfig.java @@ -43,18 +43,18 @@ public class JinjavaConfig { private Map> disabled; private final boolean failOnUnknownTokens; - private final boolean noNestedInterpretation; + private final boolean enableNestedInterpretation; public static Builder newBuilder() { return new Builder(); } public JinjavaConfig() { - this(StandardCharsets.UTF_8, Locale.ENGLISH, ZoneOffset.UTC, 10, new HashMap<>(), false, false, true, false, false, 0, false); + this(StandardCharsets.UTF_8, Locale.ENGLISH, ZoneOffset.UTC, 10, new HashMap<>(), false, false, true, false, false, 0, true); } public JinjavaConfig(Charset charset, Locale locale, ZoneId timeZone, int maxRenderDepth) { - this(charset, locale, timeZone, maxRenderDepth, new HashMap<>(), false, false, true, false, false, 0, false); + this(charset, locale, timeZone, maxRenderDepth, new HashMap<>(), false, false, true, false, false, 0, true); } private JinjavaConfig(Charset charset, @@ -69,7 +69,7 @@ private JinjavaConfig(Charset charset, boolean enableRecursiveMacroCalls, boolean failOnUnknownTokens, long maxOutputSize, - boolean noNestedInterpretation) { + boolean enableNestedInterpretation) { this.charset = charset; this.locale = locale; this.timeZone = timeZone; @@ -81,7 +81,7 @@ private JinjavaConfig(Charset charset, this.enableRecursiveMacroCalls = enableRecursiveMacroCalls; this.failOnUnknownTokens = failOnUnknownTokens; this.maxOutputSize = maxOutputSize; - this.noNestedInterpretation = noNestedInterpretation; + this.enableNestedInterpretation = enableNestedInterpretation; } public Charset getCharset() { @@ -128,8 +128,8 @@ public boolean isFailOnUnknownTokens() { return failOnUnknownTokens; } - public boolean isNoNestedInterpretation() { - return noNestedInterpretation; + public boolean isEnableNestedInterpretation() { + return enableNestedInterpretation; } public static class Builder { @@ -146,7 +146,7 @@ public static class Builder { private boolean readOnlyResolver = true; private boolean enableRecursiveMacroCalls; private boolean failOnUnknownTokens; - private boolean noNestedInterpretation; + private boolean enableNestedInterpretation; private Builder() {} @@ -205,13 +205,13 @@ public Builder withMaxOutputSize(long maxOutputSize) { return this; } - public Builder withNoNestedInpterpretation(boolean noNestedInterpretation) { - this.noNestedInterpretation = noNestedInterpretation; + public Builder withEnableNestedInterpretation(boolean enableNestedInterpretation) { + this.enableNestedInterpretation = enableNestedInterpretation; return this; } public JinjavaConfig build() { - return new JinjavaConfig(charset, locale, timeZone, maxRenderDepth, disabled, trimBlocks, lstripBlocks, readOnlyResolver, enableRecursiveMacroCalls, failOnUnknownTokens, maxOutputSize, noNestedInterpretation); + return new JinjavaConfig(charset, locale, timeZone, maxRenderDepth, disabled, trimBlocks, lstripBlocks, readOnlyResolver, enableRecursiveMacroCalls, failOnUnknownTokens, maxOutputSize, enableNestedInterpretation); } } diff --git a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java index 09b9bc317..396488884 100644 --- a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java @@ -47,7 +47,7 @@ public OutputNode render(JinjavaInterpreter interpreter) { String result = Objects.toString(var, ""); - if (!interpreter.getConfig().isNoNestedInterpretation()) { + if (interpreter.getConfig().isEnableNestedInterpretation()) { if (!StringUtils.equals(result, master.getImage()) && StringUtils.contains(result, "{{")) { try { result = interpreter.renderFlat(result); diff --git a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java index 68255eb07..9f0631652 100644 --- a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java @@ -36,7 +36,7 @@ public void itRendersResultAsTemplateWhenContainingVarBlocks() throws Exception @Test public void itRendersResultWithoutInterpretateExpression() throws Exception { - final JinjavaConfig config = JinjavaConfig.newBuilder().withNoNestedInpterpretation(true).build(); + final JinjavaConfig config = JinjavaConfig.newBuilder().withEnableNestedInterpretation(false).build(); JinjavaInterpreter noNestedInterpreter = new Jinjava(config).newInterpreter(); Context contextNoNestedInterpretation = noNestedInterpreter.getContext(); contextNoNestedInterpretation.put("myvar", "hello {{ place }}"); From b779b362ed63d6b9c2ea17c1690da6d49383cec4 Mon Sep 17 00:00:00 2001 From: Libo Song Date: Wed, 19 Jul 2017 17:43:01 -0400 Subject: [PATCH 0298/2465] Rename the the configuration name to nestedInterpretationEnabled. --- .../com/hubspot/jinjava/JinjavaConfig.java | 18 +++++++++--------- .../hubspot/jinjava/tree/ExpressionNode.java | 2 +- .../jinjava/tree/ExpressionNodeTest.java | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/JinjavaConfig.java b/src/main/java/com/hubspot/jinjava/JinjavaConfig.java index 3e39d7dd9..869d7f4d9 100644 --- a/src/main/java/com/hubspot/jinjava/JinjavaConfig.java +++ b/src/main/java/com/hubspot/jinjava/JinjavaConfig.java @@ -43,7 +43,7 @@ public class JinjavaConfig { private Map> disabled; private final boolean failOnUnknownTokens; - private final boolean enableNestedInterpretation; + private final boolean nestedInterpretationEnabled; public static Builder newBuilder() { return new Builder(); @@ -69,7 +69,7 @@ private JinjavaConfig(Charset charset, boolean enableRecursiveMacroCalls, boolean failOnUnknownTokens, long maxOutputSize, - boolean enableNestedInterpretation) { + boolean nestedInterpretationEnabled) { this.charset = charset; this.locale = locale; this.timeZone = timeZone; @@ -81,7 +81,7 @@ private JinjavaConfig(Charset charset, this.enableRecursiveMacroCalls = enableRecursiveMacroCalls; this.failOnUnknownTokens = failOnUnknownTokens; this.maxOutputSize = maxOutputSize; - this.enableNestedInterpretation = enableNestedInterpretation; + this.nestedInterpretationEnabled = nestedInterpretationEnabled; } public Charset getCharset() { @@ -128,8 +128,8 @@ public boolean isFailOnUnknownTokens() { return failOnUnknownTokens; } - public boolean isEnableNestedInterpretation() { - return enableNestedInterpretation; + public boolean isNestedInterpretationEnabled() { + return nestedInterpretationEnabled; } public static class Builder { @@ -146,7 +146,7 @@ public static class Builder { private boolean readOnlyResolver = true; private boolean enableRecursiveMacroCalls; private boolean failOnUnknownTokens; - private boolean enableNestedInterpretation; + private boolean nestedInterpretationEnabled; private Builder() {} @@ -205,13 +205,13 @@ public Builder withMaxOutputSize(long maxOutputSize) { return this; } - public Builder withEnableNestedInterpretation(boolean enableNestedInterpretation) { - this.enableNestedInterpretation = enableNestedInterpretation; + public Builder withNestedInterpretationEnabled(boolean nestedInterpretationEnabled) { + this.nestedInterpretationEnabled = nestedInterpretationEnabled; return this; } public JinjavaConfig build() { - return new JinjavaConfig(charset, locale, timeZone, maxRenderDepth, disabled, trimBlocks, lstripBlocks, readOnlyResolver, enableRecursiveMacroCalls, failOnUnknownTokens, maxOutputSize, enableNestedInterpretation); + return new JinjavaConfig(charset, locale, timeZone, maxRenderDepth, disabled, trimBlocks, lstripBlocks, readOnlyResolver, enableRecursiveMacroCalls, failOnUnknownTokens, maxOutputSize, nestedInterpretationEnabled); } } diff --git a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java index 396488884..117467d12 100644 --- a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java @@ -47,7 +47,7 @@ public OutputNode render(JinjavaInterpreter interpreter) { String result = Objects.toString(var, ""); - if (interpreter.getConfig().isEnableNestedInterpretation()) { + if (interpreter.getConfig().isNestedInterpretationEnabled()) { if (!StringUtils.equals(result, master.getImage()) && StringUtils.contains(result, "{{")) { try { result = interpreter.renderFlat(result); diff --git a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java index 9f0631652..3641f78c0 100644 --- a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java @@ -36,7 +36,7 @@ public void itRendersResultAsTemplateWhenContainingVarBlocks() throws Exception @Test public void itRendersResultWithoutInterpretateExpression() throws Exception { - final JinjavaConfig config = JinjavaConfig.newBuilder().withEnableNestedInterpretation(false).build(); + final JinjavaConfig config = JinjavaConfig.newBuilder().withNestedInterpretationEnabled(false).build(); JinjavaInterpreter noNestedInterpreter = new Jinjava(config).newInterpreter(); Context contextNoNestedInterpretation = noNestedInterpreter.getContext(); contextNoNestedInterpretation.put("myvar", "hello {{ place }}"); From 597cdd8c805ffcf131016215b086e5ce08bddcf9 Mon Sep 17 00:00:00 2001 From: Libo Song Date: Wed, 19 Jul 2017 17:53:45 -0400 Subject: [PATCH 0299/2465] Fix typo. --- src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java index 3641f78c0..3342fb6e0 100644 --- a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java @@ -35,7 +35,7 @@ public void itRendersResultAsTemplateWhenContainingVarBlocks() throws Exception } @Test - public void itRendersResultWithoutInterpretateExpression() throws Exception { + public void itRendersResultWithoutInterpreterExpression() throws Exception { final JinjavaConfig config = JinjavaConfig.newBuilder().withNestedInterpretationEnabled(false).build(); JinjavaInterpreter noNestedInterpreter = new Jinjava(config).newInterpreter(); Context contextNoNestedInterpretation = noNestedInterpreter.getContext(); From 43af21e8963cb85f3de09aad8ce5336f1430d9fd Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Wed, 19 Jul 2017 17:55:37 -0400 Subject: [PATCH 0300/2465] tweak name --- src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java index 3342fb6e0..f45d23de6 100644 --- a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java @@ -35,7 +35,7 @@ public void itRendersResultAsTemplateWhenContainingVarBlocks() throws Exception } @Test - public void itRendersResultWithoutInterpreterExpression() throws Exception { + public void itRendersResultWithoutNestedExpressionInterpretation() throws Exception { final JinjavaConfig config = JinjavaConfig.newBuilder().withNestedInterpretationEnabled(false).build(); JinjavaInterpreter noNestedInterpreter = new Jinjava(config).newInterpreter(); Context contextNoNestedInterpretation = noNestedInterpreter.getContext(); From 3c9b61c1e19d348cbc6b99aced5dcacdf7f99b93 Mon Sep 17 00:00:00 2001 From: hs-lsong Date: Wed, 19 Jul 2017 18:10:05 -0400 Subject: [PATCH 0301/2465] Update CHANGES.md A new release that fixes https://github.com/HubSpot/jinjava/issues/121 --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 4f4e885d4..6ee0b2db3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Jinjava Releases # +### 2017-06-14 Version 2.2.2 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.1%22)) ### + +* Disable interpretation of nested expressions with a configuration. + ### 2017-06-14 Version 2.2.1 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.1%22)) ### * Includes field name in unknown tag error From bcc92c5aa8ce2ff5d9e801439987d4425c018ed1 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Wed, 19 Jul 2017 22:16:54 +0000 Subject: [PATCH 0302/2465] [maven-release-plugin] prepare release jinjava-2.2.2 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index dec215216..c4ad5e2a5 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.2.2-SNAPSHOT + 2.2.2 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.2.2 From 57a3cabe2ddad89c9e069fb9b02b3174c5ea5322 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Wed, 19 Jul 2017 22:16:54 +0000 Subject: [PATCH 0303/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c4ad5e2a5..b47f7b8ec 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.2.2 + 2.2.3-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.2.2 + HEAD From 9e7eb88b9e358e0b82838eae247023cb395cf63a Mon Sep 17 00:00:00 2001 From: Libo Song Date: Fri, 21 Jul 2017 11:25:00 -0400 Subject: [PATCH 0304/2465] default nestedInterpretationEnabled to be true in JinjavaConfig builder. --- src/main/java/com/hubspot/jinjava/JinjavaConfig.java | 2 +- .../com/hubspot/jinjava/tree/ExpressionNodeTest.java | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/JinjavaConfig.java b/src/main/java/com/hubspot/jinjava/JinjavaConfig.java index 869d7f4d9..6fbfd9404 100644 --- a/src/main/java/com/hubspot/jinjava/JinjavaConfig.java +++ b/src/main/java/com/hubspot/jinjava/JinjavaConfig.java @@ -146,7 +146,7 @@ public static class Builder { private boolean readOnlyResolver = true; private boolean enableRecursiveMacroCalls; private boolean failOnUnknownTokens; - private boolean nestedInterpretationEnabled; + private boolean nestedInterpretationEnabled = true; private Builder() {} diff --git a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java index f45d23de6..6e1944503 100644 --- a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java @@ -46,6 +46,18 @@ public void itRendersResultWithoutNestedExpressionInterpretation() throws Except assertThat(node.render(noNestedInterpreter).toString()).isEqualTo("hello {{ place }}"); } + @Test + public void itRendersResultWithDefaultBuilder() throws Exception { + final JinjavaConfig config = JinjavaConfig.newBuilder().build(); + JinjavaInterpreter noNestedInterpreter = new Jinjava(config).newInterpreter(); + Context contextNoNestedInterpretation = noNestedInterpreter.getContext(); + contextNoNestedInterpretation.put("myvar", "hello {{ place }}"); + contextNoNestedInterpretation.put("place", "world"); + + ExpressionNode node = fixture("simplevar"); + assertThat(node.render(noNestedInterpreter).toString()).isEqualTo("hello world"); + } + @Test public void itAvoidsInfiniteRecursionWhenVarsContainBraceBlocks() throws Exception { context.put("myvar", "hello {{ place }}"); From 48ea86ca38047cb67aa91ba5a7e4e19fd9a3b216 Mon Sep 17 00:00:00 2001 From: Libo Song Date: Fri, 21 Jul 2017 12:53:25 -0400 Subject: [PATCH 0305/2465] Update. --- src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java index 6e1944503..d98ce4946 100644 --- a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java @@ -47,7 +47,7 @@ public void itRendersResultWithoutNestedExpressionInterpretation() throws Except } @Test - public void itRendersResultWithDefaultBuilder() throws Exception { + public void itRendersWithNestedExpressionInterpretationByDefault() throws Exception { final JinjavaConfig config = JinjavaConfig.newBuilder().build(); JinjavaInterpreter noNestedInterpreter = new Jinjava(config).newInterpreter(); Context contextNoNestedInterpretation = noNestedInterpreter.getContext(); From 0bc7258f669a8e3d026f49bdce0a8e1bcbdf3ee6 Mon Sep 17 00:00:00 2001 From: hs-lsong Date: Fri, 21 Jul 2017 13:09:03 -0400 Subject: [PATCH 0306/2465] Update CHANGES.md --- CHANGES.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 6ee0b2db3..346b801bb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,10 @@ # Jinjava Releases # -### 2017-06-14 Version 2.2.2 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.1%22)) ### +### 2017-07-21 Version 2.2.3 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.3%22)) ### + +* Make nested expressions configuration default to true. + +### 2017-07-19 Version 2.2.2 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.2%22)) ### * Disable interpretation of nested expressions with a configuration. From 18e07099c48b69a8913d6b5a020d20f2cb629ade Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Fri, 21 Jul 2017 17:16:50 +0000 Subject: [PATCH 0307/2465] [maven-release-plugin] prepare release jinjava-2.2.3 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b47f7b8ec..f01e05685 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.2.3-SNAPSHOT + 2.2.3 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.2.3 From b2b9911f5bf1ed588e611cd0150307534b704cb1 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Fri, 21 Jul 2017 17:16:51 +0000 Subject: [PATCH 0308/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f01e05685..88bb94ec3 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.2.3 + 2.2.4-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.2.3 + HEAD From d5470c303a374f9515097eeb58f2aabcc511576f Mon Sep 17 00:00:00 2001 From: Jose Galarza Date: Tue, 1 Aug 2017 16:39:08 +0300 Subject: [PATCH 0309/2465] Fixing filters with uppercase letters --- .../hubspot/jinjava/lib/SimpleLibrary.java | 4 +- .../CamelCaseRegisteringFilterTest.java | 37 +++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 src/test/java/com/hubspot/jinjava/lib/filter/CamelCaseRegisteringFilterTest.java diff --git a/src/main/java/com/hubspot/jinjava/lib/SimpleLibrary.java b/src/main/java/com/hubspot/jinjava/lib/SimpleLibrary.java index 97598beb6..c40e406b5 100644 --- a/src/main/java/com/hubspot/jinjava/lib/SimpleLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/SimpleLibrary.java @@ -57,7 +57,7 @@ public T fetch(String item) { throw new DisabledException(item); } - return lib.get(StringUtils.lowerCase(item)); + return lib.get(item); } @SafeVarargs @@ -82,7 +82,7 @@ public void register(T obj) { } public void register(String name, T obj) { - if (!disabled.contains(obj.getName().toLowerCase())) { + if (!disabled.contains(obj.getName())) { lib.put(name, obj); ENGINE_LOG.debug(getClass().getSimpleName() + ": Registered " + obj.getName()); } diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/CamelCaseRegisteringFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/CamelCaseRegisteringFilterTest.java new file mode 100644 index 000000000..2df0b0fee --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/lib/filter/CamelCaseRegisteringFilterTest.java @@ -0,0 +1,37 @@ +package com.hubspot.jinjava.lib.filter; + +import com.hubspot.jinjava.Jinjava; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import org.junit.Test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +public class CamelCaseRegisteringFilterTest { + + Jinjava jinjava; + + @Test + public void testCamelCaseIsRegistered() { + jinjava = new Jinjava(); + jinjava.getGlobalContext().registerFilter(new ReturnHelloFilter()); + + assertThat(jinjava.render("{{ 'test'|returnHello }}", new HashMap<>())).isEqualTo("Hello"); + } + + private static class ReturnHelloFilter implements AdvancedFilter { + @Override + public String getName() { + return "returnHello"; + } + + @Override + public Object filter(Object var, JinjavaInterpreter interpreter, Object[] args, Map kwargs) { + return "Hello"; + } + } + +} From 1e45682bb333c51a551790669e32ab66d0fae5b3 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 1 Aug 2017 09:48:53 -0400 Subject: [PATCH 0310/2465] update test name --- .../jinjava/lib/filter/CamelCaseRegisteringFilterTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/CamelCaseRegisteringFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/CamelCaseRegisteringFilterTest.java index 2df0b0fee..4232c47a8 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/CamelCaseRegisteringFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/CamelCaseRegisteringFilterTest.java @@ -15,7 +15,7 @@ public class CamelCaseRegisteringFilterTest { Jinjava jinjava; @Test - public void testCamelCaseIsRegistered() { + public void itAllowsCamelCasedFilterNames() { jinjava = new Jinjava(); jinjava.getGlobalContext().registerFilter(new ReturnHelloFilter()); From b0b083533a0fe5a611d365ae5de46061a67eca08 Mon Sep 17 00:00:00 2001 From: Justin Peterson Date: Tue, 1 Aug 2017 14:25:09 -0400 Subject: [PATCH 0311/2465] Add function to apply resolved tags to context --- .../java/com/hubspot/jinjava/interpret/Context.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index a1cf20b63..d945f007a 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -221,6 +221,18 @@ public void removeSuperBlock() { this.superBlock = null; } + /** + * Take all resolved strings from a context object and apply them to this context. + * Useful for passing resolved values up a tag hierarchy. + * + * @param context - context object to apply resolved values from. + */ + public void applyResolvedFrom(Context context) { + context.getResolvedExpressions().forEach(this::addResolvedExpression); + context.getResolvedFunctions().forEach(this::addResolvedFunction); + context.getResolvedValues().forEach(this::addResolvedValue); + } + @SafeVarargs @SuppressWarnings("unchecked") public final void registerClasses(Class... classes) { From 70237318f22e30c3f157dc9115ac4a7a8f0f070b Mon Sep 17 00:00:00 2001 From: Justin Peterson Date: Tue, 1 Aug 2017 14:25:21 -0400 Subject: [PATCH 0312/2465] Add unit test --- .../jinjava/interpret/ContextTest.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/test/java/com/hubspot/jinjava/interpret/ContextTest.java diff --git a/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java b/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java new file mode 100644 index 000000000..195173d94 --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java @@ -0,0 +1,37 @@ +package com.hubspot.jinjava.interpret; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Before; +import org.junit.Test; + +public class ContextTest { + private static final String RESOLVED_EXPRESSION = "exp" ; + private static final String RESOLVED_FUNCTION = "func" ; + private static final String RESOLVED_VALUE = "val" ; + + private Context context; + + @Before + public void setUp() { + context = new Context(); + } + + @Test + public void itAppliesResolvedValuesFromAnotherContextObject() { + Context appliedFrom = new Context(); + appliedFrom.addResolvedValue(RESOLVED_VALUE); + appliedFrom.addResolvedFunction(RESOLVED_FUNCTION); + appliedFrom.addResolvedExpression(RESOLVED_EXPRESSION); + + assertThat(context.getResolvedValues()).doesNotContain(RESOLVED_VALUE); + assertThat(context.getResolvedFunctions()).doesNotContain(RESOLVED_FUNCTION); + assertThat(context.getResolvedExpressions()).doesNotContain(RESOLVED_EXPRESSION); + + context.applyResolvedFrom(appliedFrom); + + assertThat(context.getResolvedValues()).contains(RESOLVED_VALUE); + assertThat(context.getResolvedFunctions()).contains(RESOLVED_FUNCTION); + assertThat(context.getResolvedExpressions()).contains(RESOLVED_EXPRESSION); + } +} From d653791858c3a3eaac7f9418fbc0eff988b94bb0 Mon Sep 17 00:00:00 2001 From: Justin Peterson Date: Tue, 1 Aug 2017 14:43:02 -0400 Subject: [PATCH 0313/2465] Apply -> addResolvedFrom --- src/main/java/com/hubspot/jinjava/interpret/Context.java | 2 +- src/test/java/com/hubspot/jinjava/interpret/ContextTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index d945f007a..c75f2eadf 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -227,7 +227,7 @@ public void removeSuperBlock() { * * @param context - context object to apply resolved values from. */ - public void applyResolvedFrom(Context context) { + public void addResolvedFrom(Context context) { context.getResolvedExpressions().forEach(this::addResolvedExpression); context.getResolvedFunctions().forEach(this::addResolvedFunction); context.getResolvedValues().forEach(this::addResolvedValue); diff --git a/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java b/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java index 195173d94..46c22091b 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java @@ -28,7 +28,7 @@ public void itAppliesResolvedValuesFromAnotherContextObject() { assertThat(context.getResolvedFunctions()).doesNotContain(RESOLVED_FUNCTION); assertThat(context.getResolvedExpressions()).doesNotContain(RESOLVED_EXPRESSION); - context.applyResolvedFrom(appliedFrom); + context.addResolvedFrom(appliedFrom); assertThat(context.getResolvedValues()).contains(RESOLVED_VALUE); assertThat(context.getResolvedFunctions()).contains(RESOLVED_FUNCTION); From 2ff663ac4fe2c129508eb33401feda1039d21540 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 1 Aug 2017 14:47:30 -0400 Subject: [PATCH 0314/2465] update naming --- .../com/hubspot/jinjava/interpret/ContextTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java b/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java index 46c22091b..743c32377 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java @@ -18,17 +18,17 @@ public void setUp() { } @Test - public void itAppliesResolvedValuesFromAnotherContextObject() { - Context appliedFrom = new Context(); - appliedFrom.addResolvedValue(RESOLVED_VALUE); - appliedFrom.addResolvedFunction(RESOLVED_FUNCTION); - appliedFrom.addResolvedExpression(RESOLVED_EXPRESSION); + public void itAddsResolvedValuesFromAnotherContextObject() { + Context donor = new Context(); + donor.addResolvedValue(RESOLVED_VALUE); + donor.addResolvedFunction(RESOLVED_FUNCTION); + donor.addResolvedExpression(RESOLVED_EXPRESSION); assertThat(context.getResolvedValues()).doesNotContain(RESOLVED_VALUE); assertThat(context.getResolvedFunctions()).doesNotContain(RESOLVED_FUNCTION); assertThat(context.getResolvedExpressions()).doesNotContain(RESOLVED_EXPRESSION); - context.addResolvedFrom(appliedFrom); + context.addResolvedFrom(donor); assertThat(context.getResolvedValues()).contains(RESOLVED_VALUE); assertThat(context.getResolvedFunctions()).contains(RESOLVED_FUNCTION); From 3637e221dc84bf6b24072f0a8fb9db66e206c891 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Tue, 1 Aug 2017 19:50:06 +0000 Subject: [PATCH 0315/2465] [maven-release-plugin] prepare release jinjava-2.2.4 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 88bb94ec3..17fcb3b37 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.2.4-SNAPSHOT + 2.2.4 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.2.4 From 21e4fd907cd4ead5497f7cceb23a6fa50fb8fde9 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Tue, 1 Aug 2017 19:50:06 +0000 Subject: [PATCH 0316/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 17fcb3b37..ed133288a 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.2.4 + 2.2.5-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.2.4 + HEAD From 72f27223f29520aa509b0dcb1f5b74054d09d802 Mon Sep 17 00:00:00 2001 From: Justin Peterson Date: Tue, 1 Aug 2017 15:54:19 -0400 Subject: [PATCH 0317/2465] Add release 2.2.4 to change log --- CHANGES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 346b801bb..0c7ddc032 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,10 @@ # Jinjava Releases # +### 2017-07-21 Version 2.2.4 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.4%22)) ### + +* Allow the use of filters including upper case letters: https://github.com/HubSpot/jinjava/pull/132 +* Add function to apply resolved strings from one Context object to another: https://github.com/HubSpot/jinjava/pull/133 + ### 2017-07-21 Version 2.2.3 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.3%22)) ### * Make nested expressions configuration default to true. From b5bee8eb1a392801f903247d8b8bde9ae52b065e Mon Sep 17 00:00:00 2001 From: Justin Peterson Date: Tue, 1 Aug 2017 15:54:53 -0400 Subject: [PATCH 0318/2465] Update latest change to today's date --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 0c7ddc032..8d08e42b2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # Jinjava Releases # -### 2017-07-21 Version 2.2.4 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.4%22)) ### +### 2017-08-01 Version 2.2.4 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.4%22)) ### * Allow the use of filters including upper case letters: https://github.com/HubSpot/jinjava/pull/132 * Add function to apply resolved strings from one Context object to another: https://github.com/HubSpot/jinjava/pull/133 From a3b90536b8b3e988fc4769cfad8a9fcc0fedc532 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Wed, 2 Aug 2017 16:46:49 -0400 Subject: [PATCH 0319/2465] Add configurable random number generator --- .../com/hubspot/jinjava/JinjavaConfig.java | 23 +++- .../jinjava/interpret/JinjavaInterpreter.java | 17 +++ .../jinjava/lib/filter/RandomFilter.java | 11 +- .../jinjava/lib/filter/ShuffleFilter.java | 4 +- .../ConstantZeroRandomNumberGenerator.java | 117 ++++++++++++++++++ .../random/RandomNumberGeneratorStrategy.java | 6 + .../jinjava/lib/filter/ShuffleFilterTest.java | 35 ++++-- 7 files changed, 193 insertions(+), 20 deletions(-) create mode 100644 src/main/java/com/hubspot/jinjava/random/ConstantZeroRandomNumberGenerator.java create mode 100644 src/main/java/com/hubspot/jinjava/random/RandomNumberGeneratorStrategy.java diff --git a/src/main/java/com/hubspot/jinjava/JinjavaConfig.java b/src/main/java/com/hubspot/jinjava/JinjavaConfig.java index 6fbfd9404..1db4686b2 100644 --- a/src/main/java/com/hubspot/jinjava/JinjavaConfig.java +++ b/src/main/java/com/hubspot/jinjava/JinjavaConfig.java @@ -26,6 +26,7 @@ import com.hubspot.jinjava.interpret.Context; import com.hubspot.jinjava.interpret.Context.Library; +import com.hubspot.jinjava.random.RandomNumberGeneratorStrategy; public class JinjavaConfig { @@ -44,17 +45,18 @@ public class JinjavaConfig { private Map> disabled; private final boolean failOnUnknownTokens; private final boolean nestedInterpretationEnabled; + private final RandomNumberGeneratorStrategy randomNumberGenerator; public static Builder newBuilder() { return new Builder(); } public JinjavaConfig() { - this(StandardCharsets.UTF_8, Locale.ENGLISH, ZoneOffset.UTC, 10, new HashMap<>(), false, false, true, false, false, 0, true); + this(StandardCharsets.UTF_8, Locale.ENGLISH, ZoneOffset.UTC, 10, new HashMap<>(), false, false, true, false, false, 0, true, RandomNumberGeneratorStrategy.THREAD_LOCAL); } public JinjavaConfig(Charset charset, Locale locale, ZoneId timeZone, int maxRenderDepth) { - this(charset, locale, timeZone, maxRenderDepth, new HashMap<>(), false, false, true, false, false, 0, true); + this(charset, locale, timeZone, maxRenderDepth, new HashMap<>(), false, false, true, false, false, 0, true, RandomNumberGeneratorStrategy.THREAD_LOCAL); } private JinjavaConfig(Charset charset, @@ -69,7 +71,8 @@ private JinjavaConfig(Charset charset, boolean enableRecursiveMacroCalls, boolean failOnUnknownTokens, long maxOutputSize, - boolean nestedInterpretationEnabled) { + boolean nestedInterpretationEnabled, + RandomNumberGeneratorStrategy randomNumberGenerator) { this.charset = charset; this.locale = locale; this.timeZone = timeZone; @@ -82,6 +85,7 @@ private JinjavaConfig(Charset charset, this.failOnUnknownTokens = failOnUnknownTokens; this.maxOutputSize = maxOutputSize; this.nestedInterpretationEnabled = nestedInterpretationEnabled; + this.randomNumberGenerator = randomNumberGenerator; } public Charset getCharset() { @@ -104,6 +108,10 @@ public long getMaxOutputSize() { return maxOutputSize; } + public RandomNumberGeneratorStrategy getRandomNumberGeneratorStrategy() { + return randomNumberGenerator; + } + public boolean isTrimBlocks() { return trimBlocks; } @@ -147,6 +155,7 @@ public static class Builder { private boolean enableRecursiveMacroCalls; private boolean failOnUnknownTokens; private boolean nestedInterpretationEnabled = true; + private RandomNumberGeneratorStrategy randomNumberGeneratorStrategy = RandomNumberGeneratorStrategy.THREAD_LOCAL; private Builder() {} @@ -175,6 +184,12 @@ public Builder withMaxRenderDepth(int maxRenderDepth) { return this; } + public Builder withRandomNumberGeneratorStrategy(RandomNumberGeneratorStrategy randomNumberGeneratorStrategy) { + this.randomNumberGeneratorStrategy = randomNumberGeneratorStrategy; + return this; + } + + public Builder withTrimBlocks(boolean trimBlocks) { this.trimBlocks = trimBlocks; return this; @@ -211,7 +226,7 @@ public Builder withNestedInterpretationEnabled(boolean nestedInterpretationEnabl } public JinjavaConfig build() { - return new JinjavaConfig(charset, locale, timeZone, maxRenderDepth, disabled, trimBlocks, lstripBlocks, readOnlyResolver, enableRecursiveMacroCalls, failOnUnknownTokens, maxOutputSize, nestedInterpretationEnabled); + return new JinjavaConfig(charset, locale, timeZone, maxRenderDepth, disabled, trimBlocks, lstripBlocks, readOnlyResolver, enableRecursiveMacroCalls, failOnUnknownTokens, maxOutputSize, nestedInterpretationEnabled, randomNumberGeneratorStrategy); } } diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index 55a64e74c..869328296 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -25,8 +25,10 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Random; import java.util.Set; import java.util.Stack; +import java.util.concurrent.ThreadLocalRandom; import org.apache.commons.lang3.StringUtils; @@ -36,6 +38,8 @@ import com.hubspot.jinjava.Jinjava; import com.hubspot.jinjava.JinjavaConfig; import com.hubspot.jinjava.el.ExpressionResolver; +import com.hubspot.jinjava.random.ConstantZeroRandomNumberGenerator; +import com.hubspot.jinjava.random.RandomNumberGeneratorStrategy; import com.hubspot.jinjava.tree.Node; import com.hubspot.jinjava.tree.TreeParser; import com.hubspot.jinjava.tree.output.BlockPlaceholderOutputNode; @@ -54,6 +58,7 @@ public class JinjavaInterpreter { private final ExpressionResolver expressionResolver; private final Jinjava application; + private final Random random; private int lineNumber = -1; private final List errors = new LinkedList<>(); @@ -63,6 +68,14 @@ public JinjavaInterpreter(Jinjava application, Context context, JinjavaConfig re this.config = renderConfig; this.application = application; + if (config.getRandomNumberGeneratorStrategy() == RandomNumberGeneratorStrategy.THREAD_LOCAL) { + random = ThreadLocalRandom.current(); + } else if (config.getRandomNumberGeneratorStrategy() == RandomNumberGeneratorStrategy.CONSTANT_ZERO) { + random = new ConstantZeroRandomNumberGenerator(); + } else { + throw new IllegalStateException("No random number generator with strategy " + config.getRandomNumberGeneratorStrategy()); + } + this.expressionResolver = new ExpressionResolver(this, application.getExpressionFactory()); } @@ -116,6 +129,10 @@ public void leaveScope() { } } + public Random getRandom() { + return random; + } + public class InterpreterScopeClosable implements AutoCloseable { @Override diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/RandomFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/RandomFilter.java index 3042f74ac..9da58e3eb 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/RandomFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/RandomFilter.java @@ -20,7 +20,6 @@ import java.util.Collection; import java.util.Iterator; import java.util.Map; -import java.util.concurrent.ThreadLocalRandom; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaParam; @@ -52,7 +51,7 @@ public Object filter(Object object, JinjavaInterpreter interpreter, String... ar if (size == 0) { return null; } - int index = ThreadLocalRandom.current().nextInt(size); + int index = interpreter.getRandom().nextInt(size); while (index-- > 0) { it.next(); } @@ -64,7 +63,7 @@ public Object filter(Object object, JinjavaInterpreter interpreter, String... ar if (size == 0) { return null; } - int index = ThreadLocalRandom.current().nextInt(size); + int index = interpreter.getRandom().nextInt(size); return Array.get(object, index); } // map @@ -75,7 +74,7 @@ public Object filter(Object object, JinjavaInterpreter interpreter, String... ar if (size == 0) { return null; } - int index = ThreadLocalRandom.current().nextInt(size); + int index = interpreter.getRandom().nextInt(size); while (index-- > 0) { it.next(); } @@ -83,12 +82,12 @@ public Object filter(Object object, JinjavaInterpreter interpreter, String... ar } // number if (object instanceof Number) { - return ThreadLocalRandom.current().nextLong(((Number) object).longValue()); + return interpreter.getRandom().nextInt(((Number) object).intValue()); } // string if (object instanceof String) { try { - return ThreadLocalRandom.current().nextLong(new BigDecimal((String) object).longValue()); + return interpreter.getRandom().nextInt(new BigDecimal((String) object).intValue()); } catch (Exception e) { return 0; } diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/ShuffleFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/ShuffleFilter.java index d9c0f861b..eed0da0c7 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/ShuffleFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/ShuffleFilter.java @@ -29,8 +29,8 @@ public String getName() { @Override public Object filter(Object var, JinjavaInterpreter interpreter, String... args) { if (var instanceof Collection) { - List list = new ArrayList((Collection) var); - Collections.shuffle(list); + List list = new ArrayList<>((Collection) var); + Collections.shuffle(list, interpreter.getRandom()); return list; } diff --git a/src/main/java/com/hubspot/jinjava/random/ConstantZeroRandomNumberGenerator.java b/src/main/java/com/hubspot/jinjava/random/ConstantZeroRandomNumberGenerator.java new file mode 100644 index 000000000..83eddd05e --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/random/ConstantZeroRandomNumberGenerator.java @@ -0,0 +1,117 @@ +package com.hubspot.jinjava.random; + +import java.util.Random; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; + +/** + * A random number generator that always returns 0. Useful for testing code when you want the output to be constant. + */ +public class ConstantZeroRandomNumberGenerator extends Random { + + @Override + protected int next(int bits) { + return 0; + } + + @Override + public int nextInt() { + return 0; + } + + @Override + public int nextInt(int bound) { + return 0; + } + + @Override + public long nextLong() { + return 0; + } + + @Override + public boolean nextBoolean() { + return false; + } + + @Override + public float nextFloat() { + return 0f; + } + + @Override + public double nextDouble() { + return 0; + } + + @Override + public synchronized double nextGaussian() { + return 0; + } + + @Override + public void nextBytes(byte[] bytes) { + throw new UnsupportedOperationException(); + } + + @Override + public IntStream ints(long streamSize) { + throw new UnsupportedOperationException(); + } + + @Override + public IntStream ints() { + throw new UnsupportedOperationException(); + } + + @Override + public IntStream ints(long streamSize, int randomNumberOrigin, int randomNumberBound) { + throw new UnsupportedOperationException(); + } + + @Override + public IntStream ints(int randomNumberOrigin, int randomNumberBound) { + throw new UnsupportedOperationException(); + } + + @Override + public LongStream longs(long streamSize) { + throw new UnsupportedOperationException(); + } + + @Override + public LongStream longs() { + throw new UnsupportedOperationException(); + } + + @Override + public LongStream longs(long streamSize, long randomNumberOrigin, long randomNumberBound) { + throw new UnsupportedOperationException(); + } + + @Override + public LongStream longs(long randomNumberOrigin, long randomNumberBound) { + throw new UnsupportedOperationException(); + } + + @Override + public DoubleStream doubles(long streamSize) { + throw new UnsupportedOperationException(); + } + + @Override + public DoubleStream doubles() { + throw new UnsupportedOperationException(); + } + + @Override + public DoubleStream doubles(long streamSize, double randomNumberOrigin, double randomNumberBound) { + throw new UnsupportedOperationException(); + } + + @Override + public DoubleStream doubles(double randomNumberOrigin, double randomNumberBound) { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/java/com/hubspot/jinjava/random/RandomNumberGeneratorStrategy.java b/src/main/java/com/hubspot/jinjava/random/RandomNumberGeneratorStrategy.java new file mode 100644 index 000000000..57ca80ab3 --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/random/RandomNumberGeneratorStrategy.java @@ -0,0 +1,6 @@ +package com.hubspot.jinjava.random; + +public enum RandomNumberGeneratorStrategy { + THREAD_LOCAL, + CONSTANT_ZERO +} diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/ShuffleFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/ShuffleFilterTest.java index 48b4f4bb4..4a4d1fe83 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/ShuffleFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/ShuffleFilterTest.java @@ -2,27 +2,31 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; +import static org.mockito.Mockito.*; import java.util.Arrays; import java.util.List; +import java.util.concurrent.ThreadLocalRandom; -import org.junit.Before; import org.junit.Test; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.random.ConstantZeroRandomNumberGenerator; + public class ShuffleFilterTest { - ShuffleFilter filter; + ShuffleFilter filter = new ShuffleFilter(); - @Before - public void setup() { - this.filter = new ShuffleFilter(); - } + JinjavaInterpreter interpreter = mock(JinjavaInterpreter.class); @SuppressWarnings("unchecked") @Test - public void shuffleItems() { + public void itShufflesItems() { + + when(interpreter.getRandom()).thenReturn(ThreadLocalRandom.current()); + List before = Arrays.asList("1", "2", "3", "4", "5", "6", "7", "8", "9"); - List after = (List) filter.filter(before, null); + List after = (List) filter.filter(before, interpreter); assertThat(before).isSorted(); assertThat(after).containsAll(before); @@ -35,4 +39,19 @@ public void shuffleItems() { } } + @SuppressWarnings("unchecked") + @Test + public void itShufflesConsistentlyWithConstantRandom() { + + when(interpreter.getRandom()).thenReturn(new ConstantZeroRandomNumberGenerator()); + + List before = Arrays.asList("1", "2", "3", "4", "5", "6", "7", "8", "9"); + List after = (List) filter.filter(before, interpreter); + + assertThat(before).isSorted(); + assertThat(after).containsAll(before); + + assertThat(after).containsExactly("2", "3", "4", "5", "6", "7", "8", "9", "1"); + } + } From 0fb7ccc87e2a8707608e5b7bb899b7fe05e10ce0 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Wed, 2 Aug 2017 18:33:06 -0400 Subject: [PATCH 0320/2465] Update CHANGES.md --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 8d08e42b2..897e33563 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Jinjava Releases # +### 2017-08-02 Version 2.2.5 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.5%22)) ### + +* Enable configuration of a [non-random number generator](https://github.com/HubSpot/jinjava/pull/135) for tests + ### 2017-08-01 Version 2.2.4 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.4%22)) ### * Allow the use of filters including upper case letters: https://github.com/HubSpot/jinjava/pull/132 From c3e72bdcdb0b4cde3b243be37c0e3e23f90572e6 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Wed, 2 Aug 2017 22:37:44 +0000 Subject: [PATCH 0321/2465] [maven-release-plugin] prepare release jinjava-2.2.5 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ed133288a..b957001cc 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.2.5-SNAPSHOT + 2.2.5 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.2.5 From e095d46c8f7ba3ce664dec33724a66ebfc718abe Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Wed, 2 Aug 2017 22:37:44 +0000 Subject: [PATCH 0322/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b957001cc..2765baa8c 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.2.5 + 2.2.6-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.2.5 + HEAD From ee6a50ffcfcc2c07cb6be88a80195ef715690d72 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Thu, 3 Aug 2017 16:01:14 -0400 Subject: [PATCH 0323/2465] Limit length of strings while building --- .../jinjava/lib/filter/EscapeJsFilter.java | 3 +- .../com/hubspot/jinjava/lib/fn/Functions.java | 3 +- .../hubspot/jinjava/lib/fn/MacroFunction.java | 3 +- .../jinjava/lib/tag/AutoEscapeTag.java | 3 +- .../com/hubspot/jinjava/lib/tag/ForTag.java | 3 +- .../com/hubspot/jinjava/lib/tag/IfTag.java | 3 +- .../com/hubspot/jinjava/lib/tag/RawTag.java | 3 +- .../jinjava/tree/output/OutputList.java | 9 +-- .../jinjava/tree/parse/ExpressionToken.java | 3 +- .../util/LengthLimitingStringBuilder.java | 62 +++++++++++++++++++ .../util/LengthLimitingStringBuilderTest.java | 17 +++++ 11 files changed, 96 insertions(+), 16 deletions(-) create mode 100644 src/main/java/com/hubspot/jinjava/util/LengthLimitingStringBuilder.java create mode 100644 src/test/java/com/hubspot/jinjava/util/LengthLimitingStringBuilderTest.java diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/EscapeJsFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/EscapeJsFilter.java index 6426b186c..c6dfb7326 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/EscapeJsFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/EscapeJsFilter.java @@ -22,6 +22,7 @@ import com.hubspot.jinjava.doc.annotations.JinjavaParam; import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.util.LengthLimitingStringBuilder; @JinjavaDoc( value = "Escapes strings so that they can be safely inserted into a JavaScript variable declaration", @@ -38,7 +39,7 @@ public class EscapeJsFilter implements Filter { @Override public Object filter(Object objectToFilter, JinjavaInterpreter jinjavaInterpreter, String... strings) { String input = Objects.toString(objectToFilter, ""); - StringBuilder builder = new StringBuilder(); + LengthLimitingStringBuilder builder = new LengthLimitingStringBuilder(jinjavaInterpreter.getConfig().getMaxOutputSize()); for (int i = 0; i < input.length(); i++) { char ch = input.charAt(i); diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java index 25e5e2c37..768c29e76 100644 --- a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java @@ -24,6 +24,7 @@ import com.hubspot.jinjava.objects.date.PyishDate; import com.hubspot.jinjava.objects.date.StrftimeFormatter; import com.hubspot.jinjava.tree.Node; +import com.hubspot.jinjava.util.LengthLimitingStringBuilder; public class Functions { @@ -38,7 +39,7 @@ public class Functions { }) public static String renderSuperBlock() { JinjavaInterpreter interpreter = JinjavaInterpreter.getCurrent(); - StringBuilder result = new StringBuilder(); + LengthLimitingStringBuilder result = new LengthLimitingStringBuilder(interpreter.getConfig().getMaxOutputSize()); List superBlock = interpreter.getContext().getSuperBlock(); if (superBlock != null) { diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/MacroFunction.java b/src/main/java/com/hubspot/jinjava/lib/fn/MacroFunction.java index d9bd8a615..295518793 100644 --- a/src/main/java/com/hubspot/jinjava/lib/fn/MacroFunction.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/MacroFunction.java @@ -9,6 +9,7 @@ import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.JinjavaInterpreter.InterpreterScopeClosable; import com.hubspot.jinjava.tree.Node; +import com.hubspot.jinjava.util.LengthLimitingStringBuilder; /** * Function definition parsed from a jinjava template, stored in global macros registry in interpreter context. @@ -63,7 +64,7 @@ public Object doEvaluate(Map argMap, Map kwargMa // varargs list interpreter.getContext().put("varargs", varArgs); - StringBuilder result = new StringBuilder(); + LengthLimitingStringBuilder result = new LengthLimitingStringBuilder(interpreter.getConfig().getMaxOutputSize()); for (Node node : content) { result.append(node.render(interpreter)); diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/AutoEscapeTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/AutoEscapeTag.java index 957da78c4..28de5717d 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/AutoEscapeTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/AutoEscapeTag.java @@ -9,6 +9,7 @@ import com.hubspot.jinjava.interpret.JinjavaInterpreter.InterpreterScopeClosable; import com.hubspot.jinjava.tree.Node; import com.hubspot.jinjava.tree.TagNode; +import com.hubspot.jinjava.util.LengthLimitingStringBuilder; @JinjavaDoc( value = "Autoescape the tag's contents", @@ -39,7 +40,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { boolean escapeFlag = BooleanUtils.toBoolean(StringUtils.isNotBlank(boolFlagStr) ? boolFlagStr : "true"); interpreter.getContext().setAutoEscape(escapeFlag); - StringBuilder result = new StringBuilder(); + LengthLimitingStringBuilder result = new LengthLimitingStringBuilder(interpreter.getConfig().getMaxOutputSize()); for (Node child : tagNode.getChildren()) { result.append(child.render(interpreter)); diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java index c1a8d7d70..ec5efa18d 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java @@ -36,6 +36,7 @@ import com.hubspot.jinjava.util.ForLoop; import com.hubspot.jinjava.util.HelperStringTokenizer; import com.hubspot.jinjava.util.ObjectIterator; +import com.hubspot.jinjava.util.LengthLimitingStringBuilder; /** * {% for a in b|f1:d,c %} @@ -125,7 +126,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { try (InterpreterScopeClosable c = interpreter.enterScope()) { interpreter.getContext().put(LOOP, loop); - StringBuilder buff = new StringBuilder(); + LengthLimitingStringBuilder buff = new LengthLimitingStringBuilder(interpreter.getConfig().getMaxOutputSize()); while (loop.hasNext()) { Object val = loop.next(); diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/IfTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/IfTag.java index e6a6eb0eb..e822d2a49 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/IfTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/IfTag.java @@ -25,6 +25,7 @@ import com.hubspot.jinjava.interpret.TemplateSyntaxException; import com.hubspot.jinjava.tree.Node; import com.hubspot.jinjava.tree.TagNode; +import com.hubspot.jinjava.util.LengthLimitingStringBuilder; import com.hubspot.jinjava.util.ObjectTruthValue; @JinjavaDoc( @@ -64,7 +65,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { nextIfElseTagNode = findNextIfElseTagNode(nodeIterator); } - StringBuilder sb = new StringBuilder(); + LengthLimitingStringBuilder sb = new LengthLimitingStringBuilder(interpreter.getConfig().getMaxOutputSize()); if (nextIfElseTagNode != null) { while (nodeIterator.hasNext()) { Node n = nodeIterator.next(); diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/RawTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/RawTag.java index c644d738b..258d03b2d 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/RawTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/RawTag.java @@ -7,6 +7,7 @@ import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.tree.Node; import com.hubspot.jinjava.tree.TagNode; +import com.hubspot.jinjava.util.LengthLimitingStringBuilder; @JinjavaDoc( value = "Process all inner HubL as plain text", @@ -32,7 +33,7 @@ public String getEndTagName() { @Override public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { - StringBuilder result = new StringBuilder(); + LengthLimitingStringBuilder result = new LengthLimitingStringBuilder(interpreter.getConfig().getMaxOutputSize()); for (Node n : tagNode.getChildren()) { result.append(renderNodeRaw(n)); diff --git a/src/main/java/com/hubspot/jinjava/tree/output/OutputList.java b/src/main/java/com/hubspot/jinjava/tree/output/OutputList.java index e22ceec52..e8ccf4922 100644 --- a/src/main/java/com/hubspot/jinjava/tree/output/OutputList.java +++ b/src/main/java/com/hubspot/jinjava/tree/output/OutputList.java @@ -4,6 +4,7 @@ import java.util.List; import com.hubspot.jinjava.interpret.OutputTooBigException; +import com.hubspot.jinjava.util.LengthLimitingStringBuilder; public class OutputList { @@ -42,15 +43,9 @@ public List getBlocks() { } public String getValue() { - StringBuilder val = new StringBuilder(); - - long valueSize = 0; + LengthLimitingStringBuilder val = new LengthLimitingStringBuilder(maxOutputSize); for (OutputNode node : nodes) { - if (maxOutputSize > 0 && valueSize + node.getSize() > maxOutputSize) { - throw new OutputTooBigException(maxOutputSize, valueSize + node.getSize()); - } - valueSize += node.getSize(); val.append(node.getValue()); } diff --git a/src/main/java/com/hubspot/jinjava/tree/parse/ExpressionToken.java b/src/main/java/com/hubspot/jinjava/tree/parse/ExpressionToken.java index efb1fd71f..863e91c6a 100644 --- a/src/main/java/com/hubspot/jinjava/tree/parse/ExpressionToken.java +++ b/src/main/java/com/hubspot/jinjava/tree/parse/ExpressionToken.java @@ -32,8 +32,7 @@ public ExpressionToken(String image, int lineNumber) { @Override public String toString() { - StringBuilder s = new StringBuilder("{{ ").append(getExpr()).append("}}"); - return s.toString(); + return "{{ " + getExpr() + "}}"; } @Override diff --git a/src/main/java/com/hubspot/jinjava/util/LengthLimitingStringBuilder.java b/src/main/java/com/hubspot/jinjava/util/LengthLimitingStringBuilder.java new file mode 100644 index 000000000..4b8a723a9 --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/util/LengthLimitingStringBuilder.java @@ -0,0 +1,62 @@ +package com.hubspot.jinjava.util; + +import java.io.Serializable; +import java.util.stream.IntStream; + +import com.hubspot.jinjava.interpret.OutputTooBigException; + +public class LengthLimitingStringBuilder implements Serializable, CharSequence { + + private static final long serialVersionUID = -1891922886257965755L; + + private final StringBuilder builder; + private long length = 0; + private final long maxLength; + + public LengthLimitingStringBuilder(long maxLength) { + builder = new StringBuilder(); + this.maxLength = maxLength; + } + + @Override + public int length() { + return builder.length(); + } + + @Override + public char charAt(int index) { + return builder.charAt(index); + } + + @Override + public CharSequence subSequence(int start, int end) { + return builder.subSequence(start, end); + } + + @Override + public String toString() { + return builder.toString(); + } + + @Override + public IntStream chars() { + return builder.chars(); + } + + @Override + public IntStream codePoints() { + return builder.codePoints(); + } + + public void append(Object obj) { + append(String.valueOf(obj)); + } + + public void append(String str) { + length += str.length(); + if (maxLength > 0 && length > maxLength) { + throw new OutputTooBigException(maxLength, length); + } + builder.append(str); + } +} diff --git a/src/test/java/com/hubspot/jinjava/util/LengthLimitingStringBuilderTest.java b/src/test/java/com/hubspot/jinjava/util/LengthLimitingStringBuilderTest.java new file mode 100644 index 000000000..7b2e03283 --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/util/LengthLimitingStringBuilderTest.java @@ -0,0 +1,17 @@ +package com.hubspot.jinjava.util; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.Test; + +import com.hubspot.jinjava.interpret.OutputTooBigException; + +public class LengthLimitingStringBuilderTest { + + @Test + public void itLimitsStringLength() throws Exception { + LengthLimitingStringBuilder sb = new LengthLimitingStringBuilder(10); + sb.append("0123456789"); + assertThatThrownBy(() -> sb.append("1")).isInstanceOf(OutputTooBigException.class); + } +} From fe33c5f16b57cb4bd2af0171417444225c4a2102 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Thu, 3 Aug 2017 16:05:47 -0400 Subject: [PATCH 0324/2465] add test for 0 length --- .../jinjava/util/LengthLimitingStringBuilderTest.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/test/java/com/hubspot/jinjava/util/LengthLimitingStringBuilderTest.java b/src/test/java/com/hubspot/jinjava/util/LengthLimitingStringBuilderTest.java index 7b2e03283..99b35d37a 100644 --- a/src/test/java/com/hubspot/jinjava/util/LengthLimitingStringBuilderTest.java +++ b/src/test/java/com/hubspot/jinjava/util/LengthLimitingStringBuilderTest.java @@ -14,4 +14,11 @@ public void itLimitsStringLength() throws Exception { sb.append("0123456789"); assertThatThrownBy(() -> sb.append("1")).isInstanceOf(OutputTooBigException.class); } + + @Test + public void itDoesNotLimitWithZeroLength() throws Exception { + LengthLimitingStringBuilder sb = new LengthLimitingStringBuilder(0); + sb.append("0123456789"); + } + } From 3136e2617b3777ef50c668d11af5ce97519e72dd Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Thu, 3 Aug 2017 17:55:30 -0400 Subject: [PATCH 0325/2465] Update CHANGES.md --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 897e33563..efd600405 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Jinjava Releases # +### 2017-08-03 Version 2.2.6 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.6%22)) ### + +* Limit size of output when [building strings](https://github.com/HubSpot/jinjava/pull/137) + ### 2017-08-02 Version 2.2.5 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.5%22)) ### * Enable configuration of a [non-random number generator](https://github.com/HubSpot/jinjava/pull/135) for tests From 0d66878abf7bbaa18f9aaab6b77f0518a1811d65 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Thu, 3 Aug 2017 22:00:57 +0000 Subject: [PATCH 0326/2465] [maven-release-plugin] prepare release jinjava-2.2.6 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 2765baa8c..9bd7cce2b 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.2.6-SNAPSHOT + 2.2.6 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.2.6 From 9c7a7c5ee67a92b88da31d7c38e66d58a3c66f11 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Thu, 3 Aug 2017 22:00:57 +0000 Subject: [PATCH 0327/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9bd7cce2b..b40df96e0 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.2.6 + 2.2.7-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.2.6 + HEAD From 5205b91a3cff3f20e64505863381a9f9f59458f7 Mon Sep 17 00:00:00 2001 From: Padraig Farrell Date: Sat, 12 Aug 2017 10:35:34 +0100 Subject: [PATCH 0328/2465] Delegate PyMap toString to underlying Map --- .../java/com/hubspot/jinjava/objects/collections/PyMap.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/hubspot/jinjava/objects/collections/PyMap.java b/src/main/java/com/hubspot/jinjava/objects/collections/PyMap.java index 927187c01..2b6126369 100644 --- a/src/main/java/com/hubspot/jinjava/objects/collections/PyMap.java +++ b/src/main/java/com/hubspot/jinjava/objects/collections/PyMap.java @@ -19,6 +19,11 @@ protected Map delegate() { return map; } + @Override + public String toString() { + return delegate().toString(); + } + public Map toMap() { return map; } From a6e3d11ef616892adba4ed29ac125175dd705a5c Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Sat, 12 Aug 2017 21:50:27 -0400 Subject: [PATCH 0329/2465] Update CHANGES.md --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index efd600405..11668ca4e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,10 @@ ### 2017-08-03 Version 2.2.6 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.6%22)) ### +* Delegate toString() method on PyMap + +### 2017-08-03 Version 2.2.6 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.6%22)) ### + * Limit size of output when [building strings](https://github.com/HubSpot/jinjava/pull/137) ### 2017-08-02 Version 2.2.5 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.5%22)) ### From 9b13198d88b8c32d013fdcab1f2d40c36b49fe21 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Sat, 12 Aug 2017 21:51:17 -0400 Subject: [PATCH 0330/2465] Update CHANGES.md --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 11668ca4e..2f3f7e325 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # Jinjava Releases # -### 2017-08-03 Version 2.2.6 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.6%22)) ### +### 2017-08-12 Version 2.2.7 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.7%22)) ### * Delegate toString() method on PyMap From 45fc8c3bd68baf7da168f716aec6d347bdf69754 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Sun, 13 Aug 2017 01:54:46 +0000 Subject: [PATCH 0331/2465] [maven-release-plugin] prepare release jinjava-2.2.7 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b40df96e0..f3908ae80 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.2.7-SNAPSHOT + 2.2.7 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.2.7 From 947a7e7fdcaed070a451de9c1d2dd651fad77239 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Sun, 13 Aug 2017 01:54:46 +0000 Subject: [PATCH 0332/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f3908ae80..52995dc97 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.2.7 + 2.2.8-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.2.7 + HEAD From b596a1cdaacd2d26634d6b178af20f59ea2e76ba Mon Sep 17 00:00:00 2001 From: Libo Song Date: Mon, 14 Aug 2017 17:13:37 -0400 Subject: [PATCH 0333/2465] Prevent recursion in Jinjava. --- .../hubspot/jinjava/interpret/Context.java | 15 ++++++++++ .../jinjava/interpret/JinjavaInterpreter.java | 12 ++++++-- .../hubspot/jinjava/lib/tag/MacroTagTest.java | 22 +++++++++++++- .../jinjava/tree/ExpressionNodeTest.java | 29 +++++++++++++++++++ 4 files changed, 75 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index c75f2eadf..32735cb96 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Stack; import java.util.stream.Collectors; import com.google.common.collect.HashMultimap; @@ -74,6 +75,8 @@ public enum Library { private Boolean autoEscape; private List superBlock; + private final Stack renderStack = new Stack<>(); + public Context() { this(null, null, null); } @@ -385,6 +388,18 @@ public void setRenderDepth(int renderDepth) { this.renderDepth = renderDepth; } + public void pushRenderStack(String template) { + renderStack.push(template); + } + + public String popRenderStack() { + return renderStack.pop(); + } + + public Stack getRenderStack() { + return renderStack; + } + public void addDependency(String type, String identification) { this.dependencies.get(type).add(identification); } diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index 869328296..aed8f700e 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -45,6 +45,7 @@ import com.hubspot.jinjava.tree.output.BlockPlaceholderOutputNode; import com.hubspot.jinjava.tree.output.OutputList; import com.hubspot.jinjava.tree.output.OutputNode; +import com.hubspot.jinjava.tree.output.RenderedOutputNode; import com.hubspot.jinjava.util.Variable; import com.hubspot.jinjava.util.WhitespaceUtils; @@ -207,8 +208,15 @@ public String render(Node root, boolean processExtendRoots) { for (Node node : root.getChildren()) { lineNumber = node.getLineNumber(); - OutputNode out = node.render(this); - output.addNode(out); + if (context.getRenderStack().contains(node.getMaster().getImage())) { + // This is a circular rendering. Stop rendering it here. + output.addNode(new RenderedOutputNode(node.getMaster().getImage())); + } else { + context.pushRenderStack(node.getMaster().getImage()); + OutputNode out = node.render(this); + context.popRenderStack(); + output.addNode(out); + } } // render all extend parents, keeping the last as the root output diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java index be4f4d3a8..4ac1258db 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java @@ -158,7 +158,27 @@ public void itAllowsMacroRecursionWhenEnabledInConfiguration() throws IOExceptio } } - + @Test + public void itPreventsRecursionForMacroWithVar() { + String jinja = "{%- macro allSpans(spans) %}" + + "{%- for span in spans %}" + + "{{ span.tag }}" + + "{%- endfor %}" + + "{%- endmacro %}" + + "{%- set spans = {" + + " 'html_1' : {" + + " 'tag' : 'html {{ selector }}'," + + " 'span' : 12" + + " }" + + "} %}" + + "{% set selector='{{spans}}' %}" + + "{{ allSpans(spans, '') }}" + + ""; + Node node = new TreeParser(interpreter, jinja).buildTree(); + assertThat(JinjavaInterpreter.getCurrent() == interpreter).isTrue(); + assertThat(interpreter.render(node)).isEqualTo( + "html {html_1={tag=html {html_1={tag=html {{ selector }}, span=12}}, span=12}}"); + } private Node snippet(String jinja) { return new TreeParser(interpreter, jinja).buildTree().getChildren().getFirst(); diff --git a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java index d98ce4946..d38cc9f1d 100644 --- a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java @@ -67,6 +67,35 @@ public void itAvoidsInfiniteRecursionWhenVarsContainBraceBlocks() throws Excepti assertThat(node.render(interpreter).toString()).isEqualTo("hello {{ place }}"); } + @Test + public void itAvoidsInfiniteRecursionOfItself() throws Exception { + context.put("myvar", "hello {{myvar}}"); + + ExpressionNode node = fixture("simplevar"); + // It renders once, and then stop further rendering after detecting recursion. + assertThat(node.render(interpreter).toString()).isEqualTo("hello hello {{myvar}}"); + } + + @Test + public void itNoRecursionHere() throws Exception { + context.put("myvar", "hello {{ place }}"); + context.put("place", "{{location}}"); + context.put("location", "this is a place."); + + ExpressionNode node = fixture("simplevar"); + assertThat(node.render(interpreter).toString()).isEqualTo("hello this is a place."); + } + + @Test + public void itAvoidsInfiniteRecursion() throws Exception { + context.put("myvar", "hello {{ place }}"); + context.put("place", "there, {{ location }}"); + context.put("location", "this is {{ place }}"); + + ExpressionNode node = fixture("simplevar"); + assertThat(node.render(interpreter).toString()).isEqualTo("hello there, this is {{ place }}"); + } + @Test public void itRendersStringRange() throws Exception { context.put("theString", "1234567890"); From b6c407384dfd720746f0a354f09811ebd20433b9 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 15 Aug 2017 11:24:58 -0400 Subject: [PATCH 0334/2465] update test names --- .../java/com/hubspot/jinjava/tree/ExpressionNodeTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java index d38cc9f1d..559fa4bf3 100644 --- a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java @@ -68,7 +68,7 @@ public void itAvoidsInfiniteRecursionWhenVarsContainBraceBlocks() throws Excepti } @Test - public void itAvoidsInfiniteRecursionOfItself() throws Exception { + public void itDoesNotRescursivelyEvaluateExpressionsOfSelf() throws Exception { context.put("myvar", "hello {{myvar}}"); ExpressionNode node = fixture("simplevar"); @@ -77,7 +77,7 @@ public void itAvoidsInfiniteRecursionOfItself() throws Exception { } @Test - public void itNoRecursionHere() throws Exception { + public void itDoesNotRescursivelyEvaluateExpressions() throws Exception { context.put("myvar", "hello {{ place }}"); context.put("place", "{{location}}"); context.put("location", "this is a place."); @@ -87,7 +87,7 @@ public void itNoRecursionHere() throws Exception { } @Test - public void itAvoidsInfiniteRecursion() throws Exception { + public void itDoesNotRescursivelyEvaluateMoreExpressions() throws Exception { context.put("myvar", "hello {{ place }}"); context.put("place", "there, {{ location }}"); context.put("location", "this is {{ place }}"); From 6b48a8ffffd369a206102657674621c8cfc688a1 Mon Sep 17 00:00:00 2001 From: Libo Song Date: Tue, 15 Aug 2017 11:52:54 -0400 Subject: [PATCH 0335/2465] Encapsulate the stack, simplified the test case. --- .../hubspot/jinjava/interpret/Context.java | 4 ++-- .../jinjava/interpret/JinjavaInterpreter.java | 2 +- .../hubspot/jinjava/lib/tag/MacroTagTest.java | 20 +++++++++---------- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index 32735cb96..3992b1e50 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -396,8 +396,8 @@ public String popRenderStack() { return renderStack.pop(); } - public Stack getRenderStack() { - return renderStack; + public boolean doesRenderStackContain(String template) { + return renderStack.contains(template); } public void addDependency(String type, String identification) { diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index aed8f700e..fc72e9446 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -208,7 +208,7 @@ public String render(Node root, boolean processExtendRoots) { for (Node node : root.getChildren()) { lineNumber = node.getLineNumber(); - if (context.getRenderStack().contains(node.getMaster().getImage())) { + if (context.doesRenderStackContain(node.getMaster().getImage())) { // This is a circular rendering. Stop rendering it here. output.addNode(new RenderedOutputNode(node.getMaster().getImage())); } else { diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java index 4ac1258db..37c5d8c8e 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java @@ -160,24 +160,22 @@ public void itAllowsMacroRecursionWhenEnabledInConfiguration() throws IOExceptio @Test public void itPreventsRecursionForMacroWithVar() { - String jinja = "{%- macro allSpans(spans) %}" + - "{%- for span in spans %}" + - "{{ span.tag }}" + + String jinja = "{%- macro func(var) %}" + + "{%- for f in var %}" + + "{{ f.val }}" + "{%- endfor %}" + "{%- endmacro %}" + - "{%- set spans = {" + - " 'html_1' : {" + - " 'tag' : 'html {{ selector }}'," + - " 'span' : 12" + + "{%- set var = {" + + " 'f' : {" + + " 'val': '{{ self }}'," + " }" + "} %}" + - "{% set selector='{{spans}}' %}" + - "{{ allSpans(spans, '') }}" + + "{% set self='{{var}}' %}" + + "{{ func(var) }}" + ""; Node node = new TreeParser(interpreter, jinja).buildTree(); - assertThat(JinjavaInterpreter.getCurrent() == interpreter).isTrue(); assertThat(interpreter.render(node)).isEqualTo( - "html {html_1={tag=html {html_1={tag=html {{ selector }}, span=12}}, span=12}}"); + "{f={val={f={val={{ self }}}}}}"); } private Node snippet(String jinja) { From c36018c48134f591141aaa7a4f2e5ab00c21b039 Mon Sep 17 00:00:00 2001 From: Libo Song Date: Tue, 15 Aug 2017 14:51:04 -0400 Subject: [PATCH 0336/2465] Fix cases for failsOnUnknownTokens. --- .../jinjava/el/ExpressionResolver.java | 4 ++ .../el/JinjavaInterpreterResolver.java | 8 +++ .../jinjava/interpret/JinjavaInterpreter.java | 2 + .../hubspot/jinjava/tree/ExpressionNode.java | 5 -- .../jinjava/tree/ExpressionNodeTest.java | 50 +++++++++++++++++++ 5 files changed, 64 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java index eecc3841f..362c0f2d4 100644 --- a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java @@ -21,6 +21,7 @@ import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; import com.hubspot.jinjava.interpret.TemplateError.ErrorType; import com.hubspot.jinjava.interpret.TemplateSyntaxException; +import com.hubspot.jinjava.interpret.UnknownTokenException; import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory; import com.hubspot.jinjava.lib.fn.ELFunctionDefinition; @@ -80,6 +81,9 @@ public Object resolveExpression(String expression) { interpreter.addError(TemplateError.fromException(new TemplateSyntaxException(expression, e.getMessage(), interpreter.getLineNumber(), e))); } catch (DisabledException e) { interpreter.addError(new TemplateError(ErrorType.FATAL, ErrorReason.DISABLED, ErrorItem.FUNCTION, e.getMessage(), expression, interpreter.getLineNumber(), e)); + } catch (UnknownTokenException e) { + // Re-throw the exception because you only get this when the config failOnUnknownTokens is enabled. + throw e; } catch (Exception e) { interpreter.addError(TemplateError.fromException(new InterpretException( String.format("Error resolving expression [%s]: " + getRootCauseMessage(e), expression), e, interpreter.getLineNumber()))); diff --git a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java index 13da955d2..352f321ef 100644 --- a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java @@ -40,6 +40,7 @@ import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; import com.hubspot.jinjava.interpret.TemplateError.ErrorType; +import com.hubspot.jinjava.interpret.UnknownTokenException; import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory; import com.hubspot.jinjava.objects.PyWrapper; import com.hubspot.jinjava.objects.collections.PyList; @@ -93,6 +94,13 @@ public Object invoke(ELContext context, Object base, Object method, // failed to access property, continue with method calls } + if (interpreter.getConfig().isFailOnUnknownTokens()) { + for (Object param : params) { + if (param == null) { + throw new UnknownTokenException("", 0); + } + } + } return super.invoke(context, base, method, paramTypes, generateMethodParams(method, params)); } diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index fc72e9446..3305aaef4 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -294,6 +294,8 @@ public Object retraceVariable(String variable, int lineNumber) { Object obj = context.get(varName); if (obj != null) { obj = var.resolve(obj); + } else if (getConfig().isFailOnUnknownTokens()) { + throw new UnknownTokenException(variable, getLineNumber()); } return obj; } diff --git a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java index 117467d12..9d212eb76 100644 --- a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java @@ -20,7 +20,6 @@ import org.apache.commons.lang3.StringUtils; import com.hubspot.jinjava.interpret.JinjavaInterpreter; -import com.hubspot.jinjava.interpret.UnknownTokenException; import com.hubspot.jinjava.lib.filter.EscapeFilter; import com.hubspot.jinjava.tree.output.OutputNode; import com.hubspot.jinjava.tree.output.RenderedOutputNode; @@ -41,10 +40,6 @@ public ExpressionNode(ExpressionToken token) { public OutputNode render(JinjavaInterpreter interpreter) { Object var = interpreter.resolveELExpression(master.getExpr(), getLineNumber()); - if (var == null && interpreter.getConfig().isFailOnUnknownTokens()) { - throw new UnknownTokenException(master.getExpr(), getLineNumber()); - } - String result = Objects.toString(var, ""); if (interpreter.getConfig().isNestedInterpretationEnabled()) { diff --git a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java index 559fa4bf3..5a4643862 100644 --- a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java @@ -1,6 +1,7 @@ package com.hubspot.jinjava.tree; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import java.nio.charset.StandardCharsets; @@ -13,6 +14,7 @@ import com.hubspot.jinjava.JinjavaConfig; import com.hubspot.jinjava.interpret.Context; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.interpret.UnknownTokenException; public class ExpressionNodeTest { @@ -104,6 +106,54 @@ public void itRendersStringRange() throws Exception { assertThat(node.render(interpreter).toString()).isEqualTo("345"); } + @Test + public void itFailsOnUnknownTokensVariables() throws Exception { + final JinjavaConfig config = JinjavaConfig.newBuilder().withFailOnUnknownTokens(true).build(); + JinjavaInterpreter jinjavaInterpreter = new Jinjava(config).newInterpreter(); + + String jinja = "{{ UnknownToken }}"; + Node node = new TreeParser(jinjavaInterpreter, jinja).buildTree(); + assertThatThrownBy(() -> jinjavaInterpreter.render(node)) + .isInstanceOf(UnknownTokenException.class) + .hasMessage("Unknown token found: UnknownToken"); + } + + @Test + public void itFailsOnUnknownTokensOfLoops() throws Exception { + final JinjavaConfig config = JinjavaConfig.newBuilder().withFailOnUnknownTokens(true).build(); + JinjavaInterpreter jinjavaInterpreter = new Jinjava(config).newInterpreter(); + + String jinja = "{% for v in values %} {{ v }} {% endfor %}"; + Node node = new TreeParser(jinjavaInterpreter, jinja).buildTree(); + assertThatThrownBy(() -> jinjavaInterpreter.render(node)) + .isInstanceOf(UnknownTokenException.class) + .hasMessage("Unknown token found: values"); + } + + @Test + public void itFailsOnUnknownTokensOfIf() throws Exception { + final JinjavaConfig config = JinjavaConfig.newBuilder().withFailOnUnknownTokens(true).build(); + JinjavaInterpreter jinjavaInterpreter = new Jinjava(config).newInterpreter(); + + String jinja = "{% if bad %} BAD {% endif %}"; + Node node = new TreeParser(jinjavaInterpreter, jinja).buildTree(); + assertThatThrownBy(() -> jinjavaInterpreter.render(node)) + .isInstanceOf(UnknownTokenException.class) + .hasMessage("Unknown token found: bad"); + } + + @Test + public void itFailsOnUnknownTokensWithFilter() throws Exception { + final JinjavaConfig config = JinjavaConfig.newBuilder().withFailOnUnknownTokens(true).build(); + JinjavaInterpreter jinjavaInterpreter = new Jinjava(config).newInterpreter(); + + String jinja = "{{ UnknownToken | default('abc') }}"; + Node node = new TreeParser(jinjavaInterpreter, jinja).buildTree(); + assertThatThrownBy(() -> jinjavaInterpreter.render(node)) + .isInstanceOf(UnknownTokenException.class) + .hasMessage("Unknown token found: UnknownToken"); + } + @Test public void valueExprWithOr() throws Exception { context.put("a", "foo"); From fbab1e803bf7065427722199574adabfb1052bf3 Mon Sep 17 00:00:00 2001 From: Libo Song Date: Tue, 15 Aug 2017 14:58:47 -0400 Subject: [PATCH 0337/2465] Update. --- .../hubspot/jinjava/el/JinjavaInterpreterResolver.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java index 352f321ef..13da955d2 100644 --- a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java @@ -40,7 +40,6 @@ import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; import com.hubspot.jinjava.interpret.TemplateError.ErrorType; -import com.hubspot.jinjava.interpret.UnknownTokenException; import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory; import com.hubspot.jinjava.objects.PyWrapper; import com.hubspot.jinjava.objects.collections.PyList; @@ -94,13 +93,6 @@ public Object invoke(ELContext context, Object base, Object method, // failed to access property, continue with method calls } - if (interpreter.getConfig().isFailOnUnknownTokens()) { - for (Object param : params) { - if (param == null) { - throw new UnknownTokenException("", 0); - } - } - } return super.invoke(context, base, method, paramTypes, generateMethodParams(method, params)); } From 9c4634e7a8c58a44af8935bd58dcde9b4c6800bc Mon Sep 17 00:00:00 2001 From: Libo Song Date: Tue, 15 Aug 2017 15:33:50 -0400 Subject: [PATCH 0338/2465] Fix import tag tests for cycle detection. --- .../jinjava/interpret/JinjavaInterpreter.java | 15 ++++++++++++--- .../hubspot/jinjava/lib/tag/ImportTagTest.java | 4 ++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index fc72e9446..85d1efa23 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -33,11 +33,16 @@ import org.apache.commons.lang3.StringUtils; import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; import com.hubspot.jinjava.Jinjava; import com.hubspot.jinjava.JinjavaConfig; import com.hubspot.jinjava.el.ExpressionResolver; +import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; +import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; +import com.hubspot.jinjava.interpret.TemplateError.ErrorType; +import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory; import com.hubspot.jinjava.random.ConstantZeroRandomNumberGenerator; import com.hubspot.jinjava.random.RandomNumberGeneratorStrategy; import com.hubspot.jinjava.tree.Node; @@ -208,11 +213,15 @@ public String render(Node root, boolean processExtendRoots) { for (Node node : root.getChildren()) { lineNumber = node.getLineNumber(); - if (context.doesRenderStackContain(node.getMaster().getImage())) { + String renderStr = node.getMaster().getImage(); + if (context.doesRenderStackContain(renderStr)) { // This is a circular rendering. Stop rendering it here. - output.addNode(new RenderedOutputNode(node.getMaster().getImage())); + addError(new TemplateError(ErrorType.WARNING, ErrorReason.EXCEPTION, ErrorItem.TAG, + "Circular rendering detected: '" + renderStr + "'", null, getLineNumber(), + null, BasicTemplateErrorCategory.IMPORT_CYCLE_DETECTED, ImmutableMap.of("string", renderStr))); + output.addNode(new RenderedOutputNode(renderStr)); } else { - context.pushRenderStack(node.getMaster().getImage()); + context.pushRenderStack(renderStr); OutputNode out = node.render(this); context.popRenderStack(); output.addNode(out); diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/ImportTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/ImportTagTest.java index 6cc845a7a..a1322218c 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/ImportTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/ImportTagTest.java @@ -56,7 +56,7 @@ public void itAvoidsSimpleImportCycle() throws IOException { interpreter.render(Resources.toString(Resources.getResource("tags/importtag/imports-self.jinja"), StandardCharsets.UTF_8)); assertThat(context.get("c")).isEqualTo("hello"); - assertThat(interpreter.getErrors().get(0).getMessage()).contains("Import cycle detected for path:", "imports-self.jinja"); + assertThat(interpreter.getErrors().get(0).getMessage()).contains("Circular rendering detected:", "imports-self.jinja"); } @Test @@ -68,7 +68,7 @@ public void itAvoidsNestedImportCycle() throws IOException { assertThat(context.get("a")).isEqualTo("foo"); assertThat(context.get("b")).isEqualTo("bar"); - assertThat(interpreter.getErrors().get(0).getMessage()).contains("Import cycle detected for path:", "b-imports-a.jinja"); + assertThat(interpreter.getErrors().get(0).getMessage()).contains("Circular rendering detected:", "b-imports-a.jinja"); } @Test From aa5e52690ffe01bd7225f8cefcc9c18a25a42edd Mon Sep 17 00:00:00 2001 From: Libo Song Date: Tue, 15 Aug 2017 16:04:55 -0400 Subject: [PATCH 0339/2465] Update message for Rendering cycle. --- .../com/hubspot/jinjava/interpret/JinjavaInterpreter.java | 2 +- src/test/java/com/hubspot/jinjava/lib/tag/ImportTagTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index 85d1efa23..032253c81 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -217,7 +217,7 @@ public String render(Node root, boolean processExtendRoots) { if (context.doesRenderStackContain(renderStr)) { // This is a circular rendering. Stop rendering it here. addError(new TemplateError(ErrorType.WARNING, ErrorReason.EXCEPTION, ErrorItem.TAG, - "Circular rendering detected: '" + renderStr + "'", null, getLineNumber(), + "Rendering cycle detected: '" + renderStr + "'", null, getLineNumber(), null, BasicTemplateErrorCategory.IMPORT_CYCLE_DETECTED, ImmutableMap.of("string", renderStr))); output.addNode(new RenderedOutputNode(renderStr)); } else { diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/ImportTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/ImportTagTest.java index a1322218c..0d28f3e8e 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/ImportTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/ImportTagTest.java @@ -56,7 +56,7 @@ public void itAvoidsSimpleImportCycle() throws IOException { interpreter.render(Resources.toString(Resources.getResource("tags/importtag/imports-self.jinja"), StandardCharsets.UTF_8)); assertThat(context.get("c")).isEqualTo("hello"); - assertThat(interpreter.getErrors().get(0).getMessage()).contains("Circular rendering detected:", "imports-self.jinja"); + assertThat(interpreter.getErrors().get(0).getMessage()).contains("Rendering cycle detected:", "imports-self.jinja"); } @Test @@ -68,7 +68,7 @@ public void itAvoidsNestedImportCycle() throws IOException { assertThat(context.get("a")).isEqualTo("foo"); assertThat(context.get("b")).isEqualTo("bar"); - assertThat(interpreter.getErrors().get(0).getMessage()).contains("Circular rendering detected:", "b-imports-a.jinja"); + assertThat(interpreter.getErrors().get(0).getMessage()).contains("Rendering cycle detected:", "b-imports-a.jinja"); } @Test From 29f56224452851d3a6ce9456281f2fe60280c287 Mon Sep 17 00:00:00 2001 From: Marco Catania Date: Mon, 21 Aug 2017 17:26:37 +0100 Subject: [PATCH 0340/2465] add EscapeJson filter --- .../jinjava/lib/filter/EscapeJsonFilter.java | 34 ++++++++++++ .../jinjava/lib/filter/FilterLibrary.java | 1 + .../lib/filter/EscapeJsonFilterTest.java | 54 +++++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 src/main/java/com/hubspot/jinjava/lib/filter/EscapeJsonFilter.java create mode 100644 src/test/java/com/hubspot/jinjava/lib/filter/EscapeJsonFilterTest.java diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/EscapeJsonFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/EscapeJsonFilter.java new file mode 100644 index 000000000..1fdba4ed9 --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/lib/filter/EscapeJsonFilter.java @@ -0,0 +1,34 @@ +package com.hubspot.jinjava.lib.filter; + +import java.util.Objects; + +import org.apache.commons.lang3.StringEscapeUtils; + +import com.hubspot.jinjava.doc.annotations.JinjavaDoc; +import com.hubspot.jinjava.doc.annotations.JinjavaParam; +import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; + +@JinjavaDoc( + value = "Uses StringEscapeUtils.escapeJson() to escape strings so that they can be used in JSON values", + params = { + @JinjavaParam(value = "s", desc = "String to escape") + }, + snippets = { + @JinjavaSnippet( + code = "{{String that contains JavaScript|escapejson}}" + ) + }) + +public class EscapeJsonFilter implements Filter { + + @Override + public String getName() { + return "escapejson"; + } + + @Override + public Object filter(Object var, JinjavaInterpreter interpreter, String... args) { + return StringEscapeUtils.escapeJson(Objects.toString(var)); + } +} diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java b/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java index 5a7da5b3a..9ca2c7567 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java @@ -82,6 +82,7 @@ protected void registerDefaults() { StripTagsFilter.class, UrlEncodeFilter.class, XmlAttrFilter.class, + EscapeJsonFilter.class, CapitalizeFilter.class, CenterFilter.class, diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/EscapeJsonFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/EscapeJsonFilterTest.java new file mode 100644 index 000000000..da81f7611 --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/lib/filter/EscapeJsonFilterTest.java @@ -0,0 +1,54 @@ +package com.hubspot.jinjava.lib.filter; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; + +import com.google.common.collect.ImmutableMap; +import com.hubspot.jinjava.Jinjava; + +public class EscapeJsonFilterTest { + + Jinjava jinjava; + + @Before + public void setup() { + jinjava = new Jinjava(); + jinjava.getGlobalContext().registerClasses(EscapeJsonFilter.class); + } + + @Test + public void testHandlesUnicode() { + Map vars = ImmutableMap.of("string", "A" + "\"" + "\\" + "/"); + assertThat(jinjava.render("{{ string|escapejson }}", vars)).isEqualTo("A\\\"\\\\\\/"); + } + + @Test + public void testHandlesNonPrintableCharacters() { + byte[] bytes = {0x4D, 0x13, 0x34, 0x20, 0x8}; + Map vars = ImmutableMap.of("string", new String(bytes)); + assertThat(jinjava.render("{{ string|escapejson }}", vars)).isEqualTo("M\\u00134 \\b"); + } + + @Test + public void testHandlesWhitespace() { + assertThat(jinjava.render("{{ 'Testing\nlinebreak\n'|escapejson }}", new HashMap<>())).isEqualTo("Testing\\nlinebreak\\n"); + assertThat(jinjava.render("{{ 'Testing\ttabbing\t'|escapejson }}", new HashMap<>())).isEqualTo("Testing\\ttabbing\\t"); + } + + @Test + public void testHandlesDoubleQuotes() { + assertThat(jinjava.render("{{ 'Testing a \"quote for the week\"'|escapejson }}", new HashMap<>())).isEqualTo("Testing a \\\"quote for the week\\\""); + } + + @Test + public void testHandleSingleQuote() { + Map vars = ImmutableMap.of("string", "Testing a 'single quote' for the week"); + assertThat(jinjava.render("{{ string|escapejson }}", vars)).isEqualTo("Testing a 'single quote' for the week"); + } + +} From 1ef119fb9387a9c06f675838e5753dbfa4f30df9 Mon Sep 17 00:00:00 2001 From: Marco Catania Date: Mon, 21 Aug 2017 17:39:04 +0100 Subject: [PATCH 0341/2465] rm extra space --- .../com/hubspot/jinjava/lib/filter/EscapeJsonFilterTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/EscapeJsonFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/EscapeJsonFilterTest.java index da81f7611..514e38072 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/EscapeJsonFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/EscapeJsonFilterTest.java @@ -50,5 +50,4 @@ public void testHandleSingleQuote() { Map vars = ImmutableMap.of("string", "Testing a 'single quote' for the week"); assertThat(jinjava.render("{{ string|escapejson }}", vars)).isEqualTo("Testing a 'single quote' for the week"); } - } From 733c1a0dbaafa42e54504e0438d856d8be489e16 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 21 Aug 2017 12:49:52 -0400 Subject: [PATCH 0342/2465] update doc string --- .../java/com/hubspot/jinjava/lib/filter/EscapeJsonFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/EscapeJsonFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/EscapeJsonFilter.java index 1fdba4ed9..2820b0323 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/EscapeJsonFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/EscapeJsonFilter.java @@ -10,7 +10,7 @@ import com.hubspot.jinjava.interpret.JinjavaInterpreter; @JinjavaDoc( - value = "Uses StringEscapeUtils.escapeJson() to escape strings so that they can be used in JSON values", + value = "Escapes strings so that they can be used as JSON values", params = { @JinjavaParam(value = "s", desc = "String to escape") }, From 4c4ed17679e690131e017a26f228afc4774438b3 Mon Sep 17 00:00:00 2001 From: hs-lsong Date: Mon, 21 Aug 2017 13:03:49 -0400 Subject: [PATCH 0343/2465] Update CHANGES.md --- CHANGES.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 2f3f7e325..a2b66c3b6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Jinjava Releases # +### 2017-08-12 Version 2.2.8 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.8%22)) ### + +* Prevent recursion in Jinjava. +* Fix failsOnUnknownTokens. +* Add EscapeJson filter. + ### 2017-08-12 Version 2.2.7 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.7%22)) ### * Delegate toString() method on PyMap From bf98e3076ed9497eb03d71810891094eed86f5cd Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Mon, 21 Aug 2017 17:14:16 +0000 Subject: [PATCH 0344/2465] [maven-release-plugin] prepare release jinjava-2.2.8 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 52995dc97..3834992a1 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.2.8-SNAPSHOT + 2.2.8 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.2.8 From 5923e1963755b2f1164f720f6023a1c4e9db5ab1 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Mon, 21 Aug 2017 17:14:16 +0000 Subject: [PATCH 0345/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3834992a1..2ab8dfbf9 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.2.8 + 2.2.9-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.2.8 + HEAD From 31dc5fff6066f625cd6eae29e273b96e5bfe80b6 Mon Sep 17 00:00:00 2001 From: jpeterson Date: Mon, 28 Aug 2017 14:38:42 -0400 Subject: [PATCH 0346/2465] Pass resolved expressions up parent context chain --- src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java index 362c0f2d4..2943e647f 100644 --- a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java @@ -3,6 +3,7 @@ import static org.apache.commons.lang3.exception.ExceptionUtils.getRootCauseMessage; import java.util.List; +import java.util.Optional; import javax.el.ELException; import javax.el.ExpressionFactory; @@ -61,6 +62,7 @@ public Object resolveExpression(String expression) { } interpreter.getContext().addResolvedExpression(expression.trim()); + Optional.ofNullable(interpreter.getContext().getParent()).ifPresent(parent -> parent.addResolvedExpression(expression.trim())); try { String elExpression = "#{" + expression.trim() + "}"; From d5f4fa41d67724ec4ae169a8bd4f20bdbbce1817 Mon Sep 17 00:00:00 2001 From: jpeterson Date: Mon, 28 Aug 2017 14:51:55 -0400 Subject: [PATCH 0347/2465] Recursively apply resolved expressions to parents --- src/main/java/com/hubspot/jinjava/interpret/Context.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index 3992b1e50..c1fca7932 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -174,6 +174,9 @@ public void setAutoEscape(Boolean autoEscape) { public void addResolvedExpression(String expression) { resolvedExpressions.add(expression); + if (getParent() != null) { + getParent().addResolvedExpression(expression); + } } public Set getResolvedExpressions() { From 4e606b66d9ea2678f90ed874927ae1ffff7b4f5a Mon Sep 17 00:00:00 2001 From: jpeterson Date: Mon, 28 Aug 2017 14:52:12 -0400 Subject: [PATCH 0348/2465] Remove parent assignment in resolveExpression --- src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java index 2943e647f..362c0f2d4 100644 --- a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java @@ -3,7 +3,6 @@ import static org.apache.commons.lang3.exception.ExceptionUtils.getRootCauseMessage; import java.util.List; -import java.util.Optional; import javax.el.ELException; import javax.el.ExpressionFactory; @@ -62,7 +61,6 @@ public Object resolveExpression(String expression) { } interpreter.getContext().addResolvedExpression(expression.trim()); - Optional.ofNullable(interpreter.getContext().getParent()).ifPresent(parent -> parent.addResolvedExpression(expression.trim())); try { String elExpression = "#{" + expression.trim() + "}"; From f6cea69875294038279699b24b1cf352ebd827b3 Mon Sep 17 00:00:00 2001 From: jpeterson Date: Mon, 28 Aug 2017 14:55:59 -0400 Subject: [PATCH 0349/2465] Add unit test for recursive expressions --- .../java/com/hubspot/jinjava/interpret/ContextTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java b/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java index 743c32377..347fe92f0 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java @@ -34,4 +34,12 @@ public void itAddsResolvedValuesFromAnotherContextObject() { assertThat(context.getResolvedFunctions()).contains(RESOLVED_FUNCTION); assertThat(context.getResolvedExpressions()).contains(RESOLVED_EXPRESSION); } + + @Test + public void itRecursivelyAddsValuesUpTheContextChain() { + Context child = new Context(context); + child.addResolvedExpression(RESOLVED_EXPRESSION); + + assertThat(context.getResolvedExpressions().contains(RESOLVED_EXPRESSION)); + } } From c278d519085df188c93a5440241440ba4e0c3f98 Mon Sep 17 00:00:00 2001 From: jpeterson Date: Mon, 28 Aug 2017 16:27:55 -0400 Subject: [PATCH 0350/2465] Apply recursively to resolved expressions & values --- .../java/com/hubspot/jinjava/interpret/Context.java | 10 ++++++++-- .../com/hubspot/jinjava/interpret/ContextTest.java | 6 +++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index c1fca7932..6df434dda 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -189,6 +189,9 @@ public boolean wasExpressionResolved(String expression) { public void addResolvedValue(String value) { resolvedValues.add(value); + if (getParent() != null) { + getParent().addResolvedValue(value); + } } public Set getResolvedValues() { @@ -203,8 +206,11 @@ public Set getResolvedFunctions() { return ImmutableSet.copyOf(resolvedFunctions); } - public void addResolvedFunction(String value) { - resolvedFunctions.add(value); + public void addResolvedFunction(String function) { + resolvedFunctions.add(function); + if (getParent() != null) { + getParent().addResolvedFunction(function); + } } public List getSuperBlock() { diff --git a/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java b/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java index 347fe92f0..5c8ce03fd 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java @@ -38,8 +38,12 @@ public void itAddsResolvedValuesFromAnotherContextObject() { @Test public void itRecursivelyAddsValuesUpTheContextChain() { Context child = new Context(context); + child.addResolvedValue(RESOLVED_VALUE); + child.addResolvedFunction(RESOLVED_FUNCTION); child.addResolvedExpression(RESOLVED_EXPRESSION); - assertThat(context.getResolvedExpressions().contains(RESOLVED_EXPRESSION)); + assertThat(context.getResolvedValues()).contains(RESOLVED_VALUE); + assertThat(context.getResolvedFunctions()).contains(RESOLVED_FUNCTION); + assertThat(context.getResolvedExpressions()).contains(RESOLVED_EXPRESSION); } } From 7eaa10e83580316c3a938a63fddd30862ee30446 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Thu, 31 Aug 2017 16:53:50 +0000 Subject: [PATCH 0351/2465] [maven-release-plugin] prepare release jinjava-2.2.9 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 2ab8dfbf9..31f686b7b 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.2.9-SNAPSHOT + 2.2.9 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.2.9 From 440770e7cbf3c9e9daf09d9e3d2b6e706632d960 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Thu, 31 Aug 2017 16:53:50 +0000 Subject: [PATCH 0352/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 31f686b7b..59c28fb28 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.2.9 + 2.2.10-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.2.9 + HEAD From c974fadada2be4778af313f48c29180ed170f19e Mon Sep 17 00:00:00 2001 From: Justin Peterson Date: Thu, 31 Aug 2017 12:56:25 -0400 Subject: [PATCH 0353/2465] Release 2.2.9 description --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index a2b66c3b6..ed747b8de 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Jinjava Releases # +### 2017-08-12 Version 2.2.9 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.9%22)) ### + +* Apply resolved functions, expressions, and values to all [parents of Context object](https://github.com/HubSpot/jinjava/pull/147) + ### 2017-08-12 Version 2.2.8 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.8%22)) ### * Prevent recursion in Jinjava. From ef95f949ff1781f0b01f15a8b42856890b3fa852 Mon Sep 17 00:00:00 2001 From: Justin Peterson Date: Thu, 31 Aug 2017 12:56:52 -0400 Subject: [PATCH 0354/2465] Update date for release 2.2.9 --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index ed747b8de..faf9374be 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # Jinjava Releases # -### 2017-08-12 Version 2.2.9 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.9%22)) ### +### 2017-08-31 Version 2.2.9 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.9%22)) ### * Apply resolved functions, expressions, and values to all [parents of Context object](https://github.com/HubSpot/jinjava/pull/147) From e7825b6a54a659df225a9e833f1e93fd89a24ecf Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 24 Oct 2017 18:01:50 -0400 Subject: [PATCH 0355/2465] use code as field name --- .../java/com/hubspot/jinjava/interpret/TemplateError.java | 7 +------ .../com/hubspot/jinjava/interpret/TemplateErrorTest.java | 6 ++++++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java index 974f6759f..1aa78155c 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java @@ -52,12 +52,7 @@ public static TemplateError fromSyntaxError(InterpretException ex) { } public static TemplateError fromException(TemplateSyntaxException ex) { - String fieldName = null; - - if (ex instanceof UnknownTagException) { - fieldName = ((UnknownTagException) ex).getTag(); - } - + String fieldName = (ex instanceof UnknownTagException) ? ((UnknownTagException) ex).getTag() : ex.getCode(); return new TemplateError(ErrorType.FATAL, ErrorReason.SYNTAX_ERROR, ErrorItem.OTHER, ExceptionUtils.getMessage(ex), fieldName, ex.getLineNumber(), ex); } diff --git a/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java b/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java index d1feeb371..b1af97a95 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java @@ -26,4 +26,10 @@ public void itShowsFieldNameForUnknownTagError() { assertThat(e.getFieldName()).isEqualTo("unknown"); } + @Test + public void itShowsFieldNameForSyntaxError() { + TemplateError e = TemplateError.fromException(new TemplateSyntaxException("da codez", "{{ lolo lolo }}", 11)); + assertThat(e.getFieldName()).isEqualTo("da codez"); + } + } From aa1b7204a8db07244c7dc816f31bb5c9fca88e61 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 24 Oct 2017 20:53:41 -0400 Subject: [PATCH 0356/2465] Update CHANGES.md --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index faf9374be..99967d3c2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Jinjava Releases # +### 2017-10-24 Version 2.2.10 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.10%22)) ### + +* Use code of bad syntax as field name for `TemplateSyntaxException`s + ### 2017-08-31 Version 2.2.9 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.9%22)) ### * Apply resolved functions, expressions, and values to all [parents of Context object](https://github.com/HubSpot/jinjava/pull/147) From 0aeaabea83d0fa2ae6fd4e21997ef429862e2f3c Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Wed, 25 Oct 2017 00:58:44 +0000 Subject: [PATCH 0357/2465] [maven-release-plugin] prepare release jinjava-2.2.10 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 59c28fb28..359a568f4 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.2.10-SNAPSHOT + 2.2.10 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.2.10 From c1842f24caa923de0f78f68621a76e24e277f2ee Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Wed, 25 Oct 2017 00:58:44 +0000 Subject: [PATCH 0358/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 359a568f4..c4276491c 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.2.10 + 2.2.11-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.2.10 + HEAD From a76d393402e4bcd2effb6125b20d4da7ba0b6f25 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 27 Oct 2017 18:37:59 -0400 Subject: [PATCH 0359/2465] add line positions to error messages --- .../jinjava/el/ExpressionResolver.java | 2 +- .../el/JinjavaInterpreterResolver.java | 2 +- .../jinjava/el/ext/AstMacroFunction.java | 2 +- .../hubspot/jinjava/interpret/CallStack.java | 4 +- .../interpret/ExtendsTagCycleException.java | 4 +- .../interpret/ImportTagCycleException.java | 4 +- .../interpret/IncludeTagCycleException.java | 4 +- .../jinjava/interpret/InterpretException.java | 18 +++++++- .../jinjava/interpret/JinjavaInterpreter.java | 18 +++++--- .../interpret/MacroTagCycleException.java | 4 +- .../interpret/MissingEndTagException.java | 4 +- .../jinjava/interpret/TagCycleException.java | 19 +++++---- .../interpret/TemplateStateException.java | 8 ++-- .../interpret/TemplateSyntaxException.java | 14 +++++-- .../interpret/UnexpectedTokenException.java | 4 +- .../interpret/UnknownTagException.java | 6 +-- .../interpret/UnknownTokenException.java | 4 +- .../com/hubspot/jinjava/lib/tag/BlockTag.java | 2 +- .../com/hubspot/jinjava/lib/tag/CycleTag.java | 12 +++--- .../hubspot/jinjava/lib/tag/ExtendsTag.java | 8 ++-- .../com/hubspot/jinjava/lib/tag/ForTag.java | 4 +- .../com/hubspot/jinjava/lib/tag/FromTag.java | 6 +-- .../com/hubspot/jinjava/lib/tag/IfTag.java | 2 +- .../hubspot/jinjava/lib/tag/IfchangedTag.java | 4 +- .../hubspot/jinjava/lib/tag/ImportTag.java | 16 ++++--- .../hubspot/jinjava/lib/tag/IncludeTag.java | 15 ++++--- .../com/hubspot/jinjava/lib/tag/MacroTag.java | 2 +- .../com/hubspot/jinjava/lib/tag/SetTag.java | 8 ++-- .../hubspot/jinjava/tree/ExpressionNode.java | 5 ++- .../java/com/hubspot/jinjava/tree/Node.java | 10 ++++- .../com/hubspot/jinjava/tree/RootNode.java | 4 +- .../com/hubspot/jinjava/tree/TagNode.java | 9 ++-- .../com/hubspot/jinjava/tree/TextNode.java | 5 ++- .../com/hubspot/jinjava/tree/TreeParser.java | 8 ++-- .../jinjava/tree/parse/ExpressionToken.java | 7 ++-- .../hubspot/jinjava/tree/parse/NoteToken.java | 6 +-- .../hubspot/jinjava/tree/parse/TagToken.java | 8 ++-- .../hubspot/jinjava/tree/parse/TextToken.java | 6 +-- .../com/hubspot/jinjava/tree/parse/Token.java | 22 ++++++---- .../jinjava/tree/parse/TokenScanner.java | 42 ++++++++++--------- .../interpret/JinjavaInterpreterTest.java | 4 +- .../jinjava/interpret/TemplateErrorTest.java | 2 +- .../lib/filter/TruncateFilterTest.java | 2 +- .../jinjava/lib/tag/ImportTagTest.java | 2 +- .../hubspot/jinjava/lib/tag/MacroTagTest.java | 8 ++-- .../hubspot/jinjava/lib/tag/SetTagTest.java | 16 +++---- .../jinjava/tree/parse/TagTokenTest.java | 8 ++-- .../jinjava/tree/parse/TokenScannerTest.java | 10 ++++- .../resources/parse/tokenizer/positions.jinja | 7 ++++ 49 files changed, 225 insertions(+), 166 deletions(-) create mode 100644 src/test/resources/parse/tokenizer/positions.jinja diff --git a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java index 362c0f2d4..49faee69e 100644 --- a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java @@ -76,7 +76,7 @@ public Object resolveExpression(String expression) { BasicTemplateErrorCategory.UNKNOWN, ImmutableMap.of("exception", e.getMessage()))); } catch (TreeBuilderException e) { interpreter.addError(TemplateError.fromException(new TemplateSyntaxException(expression, - "Error parsing '" + expression + "': " + StringUtils.substringAfter(e.getMessage(), "': "), interpreter.getLineNumber(), e))); + "Error parsing '" + expression + "': " + StringUtils.substringAfter(e.getMessage(), "': "), interpreter.getLineNumber(), e.getPosition(), e))); } catch (ELException e) { interpreter.addError(TemplateError.fromException(new TemplateSyntaxException(expression, e.getMessage(), interpreter.getLineNumber(), e))); } catch (DisabledException e) { diff --git a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java index 13da955d2..2b0c883d9 100644 --- a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java @@ -155,7 +155,7 @@ private Object getValue(ELContext context, Object base, Object property, boolean } else { if (base == null) { // Look up property in context. - value = interpreter.retraceVariable((String) property, interpreter.getLineNumber()); + value = interpreter.retraceVariable((String) property, interpreter.getLineNumber(), -1); } else { // Get property of base object. try { diff --git a/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java b/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java index d94dc0ed4..d554c100d 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java @@ -36,7 +36,7 @@ public Object eval(Bindings bindings, ELContext context) { if (interpreter.getConfig().isEnableRecursiveMacroCalls()) { macroStack.pushWithoutCycleCheck(getName()); } else { - macroStack.push(getName(), -1); + macroStack.push(getName(), -1, -1); } } catch (MacroTagCycleException e) { interpreter.addError(new TemplateError(TemplateError.ErrorType.WARNING, diff --git a/src/main/java/com/hubspot/jinjava/interpret/CallStack.java b/src/main/java/com/hubspot/jinjava/interpret/CallStack.java index d5b8469d5..e07951f59 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/CallStack.java +++ b/src/main/java/com/hubspot/jinjava/interpret/CallStack.java @@ -35,9 +35,9 @@ public void pushWithoutCycleCheck(String path) { stack.push(path); } - public void push(String path, int lineNumber) { + public void push(String path, int lineNumber, int startPosition) { if (contains(path)) { - throw TagCycleException.create(exceptionClass, path, Optional.of(lineNumber)); + throw TagCycleException.create(exceptionClass, path, Optional.of(lineNumber), Optional.of(startPosition)); } stack.push(path); diff --git a/src/main/java/com/hubspot/jinjava/interpret/ExtendsTagCycleException.java b/src/main/java/com/hubspot/jinjava/interpret/ExtendsTagCycleException.java index 003fe9aad..b7cb4e70b 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/ExtendsTagCycleException.java +++ b/src/main/java/com/hubspot/jinjava/interpret/ExtendsTagCycleException.java @@ -3,8 +3,8 @@ public class ExtendsTagCycleException extends TagCycleException { private static final long serialVersionUID = 3183769038400532542L; - public ExtendsTagCycleException(String path, int lineNumber) { - super("Extends", path, lineNumber); + public ExtendsTagCycleException(String path, int lineNumber, int startPosition) { + super("Extends", path, lineNumber, startPosition); } } diff --git a/src/main/java/com/hubspot/jinjava/interpret/ImportTagCycleException.java b/src/main/java/com/hubspot/jinjava/interpret/ImportTagCycleException.java index 7d5933d3a..d7703e454 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/ImportTagCycleException.java +++ b/src/main/java/com/hubspot/jinjava/interpret/ImportTagCycleException.java @@ -3,8 +3,8 @@ public class ImportTagCycleException extends TagCycleException { private static final long serialVersionUID = 1092085697026161185L; - public ImportTagCycleException(String path, int lineNumber) { - super("Import", path, lineNumber); + public ImportTagCycleException(String path, int lineNumber, int startPosition) { + super("Import", path, lineNumber, startPosition); } } diff --git a/src/main/java/com/hubspot/jinjava/interpret/IncludeTagCycleException.java b/src/main/java/com/hubspot/jinjava/interpret/IncludeTagCycleException.java index e14efa869..76edb47e7 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/IncludeTagCycleException.java +++ b/src/main/java/com/hubspot/jinjava/interpret/IncludeTagCycleException.java @@ -3,8 +3,8 @@ public class IncludeTagCycleException extends TagCycleException { private static final long serialVersionUID = -5487642459443650227L; - public IncludeTagCycleException(String path, int lineNumber) { - super("Include", path, lineNumber); + public IncludeTagCycleException(String path, int lineNumber, int startPosition) { + super("Include", path, lineNumber, startPosition); } } diff --git a/src/main/java/com/hubspot/jinjava/interpret/InterpretException.java b/src/main/java/com/hubspot/jinjava/interpret/InterpretException.java index 1bbedb434..ac8873356 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/InterpretException.java +++ b/src/main/java/com/hubspot/jinjava/interpret/InterpretException.java @@ -17,9 +17,10 @@ public class InterpretException extends RuntimeException { - private static final long serialVersionUID = -3471306977643116138L; + private static final long serialVersionUID = -3471306977643126138L; private int lineNumber = -1; + private int startPosition = -1; public InterpretException(String msg) { super(msg); @@ -30,17 +31,30 @@ public InterpretException(String msg, Throwable e) { } public InterpretException(String msg, int lineNumber) { + this(msg,lineNumber, -1); + } + + public InterpretException(String msg, int lineNumber, int startPosition) { this(msg); this.lineNumber = lineNumber; + this.startPosition = startPosition; } - public InterpretException(String msg, Throwable e, int lineNumber) { + public InterpretException(String msg, Throwable e, int lineNumber, int startPosition) { this(msg, e); this.lineNumber = lineNumber; + this.startPosition = startPosition; + } + + public InterpretException(String msg, Throwable throwable, int lineNumber) { + this(msg, throwable, lineNumber, -1); } public int getLineNumber() { return lineNumber; } + public int getStartPosition() { + return startPosition; + } } diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index 7b98f1f8f..e4413eec2 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -292,9 +292,11 @@ private void resolveBlockStubs(OutputList output, Stack blockNames) { * name of variable in context * @param lineNumber * current line number, for error reporting + * @param startPosition + * current line position, for error reporting * @return resolved value for variable */ - public Object retraceVariable(String variable, int lineNumber) { + public Object retraceVariable(String variable, int lineNumber, int startPosition) { if (StringUtils.isBlank(variable)) { return ""; } @@ -304,7 +306,7 @@ public Object retraceVariable(String variable, int lineNumber) { if (obj != null) { obj = var.resolve(obj); } else if (getConfig().isFailOnUnknownTokens()) { - throw new UnknownTokenException(variable, getLineNumber()); + throw new UnknownTokenException(variable, lineNumber, startPosition); } return obj; } @@ -316,16 +318,18 @@ public Object retraceVariable(String variable, int lineNumber) { * name of variable in context * @param lineNumber * current line number, for error reporting + * @param startPosition + * current line position, for error reporting * @return resolved value for variable */ - public Object resolveObject(String variable, int lineNumber) { + public Object resolveObject(String variable, int lineNumber, int startPosition) { if (StringUtils.isBlank(variable)) { return ""; } if (WhitespaceUtils.isQuoted(variable)) { return WhitespaceUtils.unquote(variable); } else { - Object val = retraceVariable(variable, lineNumber); + Object val = retraceVariable(variable, lineNumber, startPosition); if (val == null) { return variable; } @@ -340,10 +344,12 @@ public Object resolveObject(String variable, int lineNumber) { * name of variable in context * @param lineNumber * current line number, for error reporting + * @param startPosition + * current line position, for error reporting * @return resolved value for variable */ - public String resolveString(String variable, int lineNumber) { - return Objects.toString(resolveObject(variable, lineNumber), ""); + public String resolveString(String variable, int lineNumber, int startPosition) { + return Objects.toString(resolveObject(variable, lineNumber, startPosition), ""); } public Context getContext() { diff --git a/src/main/java/com/hubspot/jinjava/interpret/MacroTagCycleException.java b/src/main/java/com/hubspot/jinjava/interpret/MacroTagCycleException.java index b817cce7b..c0ef0e8e2 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/MacroTagCycleException.java +++ b/src/main/java/com/hubspot/jinjava/interpret/MacroTagCycleException.java @@ -4,8 +4,8 @@ public class MacroTagCycleException extends TagCycleException { private static final long serialVersionUID = -7552850581260771832L; - public MacroTagCycleException(String path, int lineNumber) { - super("Macro", path, lineNumber); + public MacroTagCycleException(String path, int lineNumber, int startPosition) { + super("Macro", path, lineNumber, startPosition); } } diff --git a/src/main/java/com/hubspot/jinjava/interpret/MissingEndTagException.java b/src/main/java/com/hubspot/jinjava/interpret/MissingEndTagException.java index 76b5fb422..c0db2c895 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/MissingEndTagException.java +++ b/src/main/java/com/hubspot/jinjava/interpret/MissingEndTagException.java @@ -8,8 +8,8 @@ public class MissingEndTagException extends TemplateSyntaxException { private final String endTag; private final String startDefinition; - public MissingEndTagException(String endTag, String startDefintion, int lineNumber) { - super(startDefintion, "Missing end tag: " + endTag + " for tag defined as: " + StringUtils.abbreviate(startDefintion, 255), lineNumber); + public MissingEndTagException(String endTag, String startDefintion, int lineNumber, int startPosition) { + super(startDefintion, "Missing end tag: " + endTag + " for tag defined as: " + StringUtils.abbreviate(startDefintion, 255), lineNumber, startPosition); this.endTag = endTag; this.startDefinition = startDefintion; } diff --git a/src/main/java/com/hubspot/jinjava/interpret/TagCycleException.java b/src/main/java/com/hubspot/jinjava/interpret/TagCycleException.java index 843f95c61..6db3d0768 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TagCycleException.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TagCycleException.java @@ -8,8 +8,8 @@ public class TagCycleException extends TemplateStateException { private final String path; private final String tagName; - public TagCycleException(String tagName, String path, int lineNumber) { - super(tagName + " tag cycle for '" + path + "'", lineNumber); + public TagCycleException(String tagName, String path, int lineNumber, int startPosition) { + super(tagName + " tag cycle for '" + path + "'", lineNumber, startPosition); this.path = path; this.tagName = tagName; @@ -23,23 +23,24 @@ public String getTagName() { return tagName; } - public static TagCycleException create(Class clazz, String path, Optional linenumber) { + public static TagCycleException create(Class clazz, String path, Optional lineNumber, Optional startPosition) { - final Integer line = linenumber.orElse(-1); + final Integer line = lineNumber.orElse(-1); + final Integer position = startPosition.orElse(-1); if (clazz != null) { if (clazz.equals(ExtendsTagCycleException.class)) { - return new ExtendsTagCycleException(path, line); + return new ExtendsTagCycleException(path, line, position); } else if (clazz.equals(ImportTagCycleException.class)) { - return new ImportTagCycleException(path, line); + return new ImportTagCycleException(path, line, position); } else if (clazz.equals(IncludeTagCycleException.class)) { - return new IncludeTagCycleException(path, line); + return new IncludeTagCycleException(path, line, position); } else if (clazz.equals(MacroTagCycleException.class)) { - return new MacroTagCycleException(path, line); + return new MacroTagCycleException(path, line, position); } } - return new TagCycleException("", path, line); + return new TagCycleException("", path, line, position); } } diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateStateException.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateStateException.java index 87c8f0308..98df46ad5 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateStateException.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateStateException.java @@ -11,12 +11,12 @@ public TemplateStateException(String msg, Throwable e) { super(msg, e); } - public TemplateStateException(String msg, int lineNumber) { - super(msg, lineNumber); + public TemplateStateException(String msg, int lineNumber, int startPosition) { + super(msg, lineNumber, startPosition); } - public TemplateStateException(String msg, Throwable e, int lineNumber) { - super(msg, e, lineNumber); + public TemplateStateException(String msg, Throwable e, int lineNumber, int startPosition) { + super(msg, e, lineNumber, startPosition); } } diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateSyntaxException.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateSyntaxException.java index 99de097ad..0cacb05f3 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateSyntaxException.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateSyntaxException.java @@ -5,14 +5,22 @@ public class TemplateSyntaxException extends InterpretException { private final String code; + public TemplateSyntaxException(String code, String message, int lineNumber, int startPosition) { + super("Syntax error in '" + code + "': " + message, lineNumber, startPosition); + this.code = code; + } + public TemplateSyntaxException(String code, String message, int lineNumber) { - super("Syntax error in '" + code + "': " + message, lineNumber); + this(code, message, lineNumber, -1); + } + + public TemplateSyntaxException(String code, String message, int lineNumber, int startPosition, Throwable t) { + super(message, t, lineNumber, startPosition); this.code = code; } public TemplateSyntaxException(String code, String message, int lineNumber, Throwable t) { - super(message, t, lineNumber); - this.code = code; + this(code, message, lineNumber, -1, t); } public String getCode() { diff --git a/src/main/java/com/hubspot/jinjava/interpret/UnexpectedTokenException.java b/src/main/java/com/hubspot/jinjava/interpret/UnexpectedTokenException.java index 68815d036..f704d0cea 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/UnexpectedTokenException.java +++ b/src/main/java/com/hubspot/jinjava/interpret/UnexpectedTokenException.java @@ -5,8 +5,8 @@ public class UnexpectedTokenException extends TemplateSyntaxException { private final String token; - public UnexpectedTokenException(String token, int lineNumber) { - super(token, "Unexpected token: " + token, lineNumber); + public UnexpectedTokenException(String token, int lineNumber, int startPosition) { + super(token, "Unexpected token: " + token, lineNumber, startPosition); this.token = token; } diff --git a/src/main/java/com/hubspot/jinjava/interpret/UnknownTagException.java b/src/main/java/com/hubspot/jinjava/interpret/UnknownTagException.java index 31fb766a4..2bdbaac42 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/UnknownTagException.java +++ b/src/main/java/com/hubspot/jinjava/interpret/UnknownTagException.java @@ -8,14 +8,14 @@ public class UnknownTagException extends TemplateSyntaxException { private final String tag; private final String defintion; - public UnknownTagException(String tag, String definition, int lineNumber) { - super(definition, "Unknown tag: " + tag, lineNumber); + public UnknownTagException(String tag, String definition, int lineNumber, int startPosition) { + super(definition, "Unknown tag: " + tag, lineNumber, startPosition); this.tag = tag; this.defintion = definition; } public UnknownTagException(TagToken tagToken) { - this(tagToken.getTagName(), tagToken.getImage(), tagToken.getLineNumber()); + this(tagToken.getTagName(), tagToken.getImage(), tagToken.getLineNumber(), tagToken.getStartPosition()); } public String getTag() { diff --git a/src/main/java/com/hubspot/jinjava/interpret/UnknownTokenException.java b/src/main/java/com/hubspot/jinjava/interpret/UnknownTokenException.java index e4df0da02..6f29e47c6 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/UnknownTokenException.java +++ b/src/main/java/com/hubspot/jinjava/interpret/UnknownTokenException.java @@ -5,8 +5,8 @@ public class UnknownTokenException extends InterpretException { private static final long serialVersionUID = -388757722051666198L; private final String token; - public UnknownTokenException(String token, int lineNumber) { - super("Unknown token found: " + token, lineNumber); + public UnknownTokenException(String token, int lineNumber, int startPosition) { + super("Unknown token found: " + token, lineNumber, startPosition); this.token = token; } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/BlockTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/BlockTag.java index 4db41ddcf..60709bd34 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/BlockTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/BlockTag.java @@ -49,7 +49,7 @@ public class BlockTag implements Tag { public OutputNode interpretOutput(TagNode tagNode, JinjavaInterpreter interpreter) { HelperStringTokenizer tagData = new HelperStringTokenizer(tagNode.getHelpers()); if (!tagData.hasNext()) { - throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'block' expects an identifier", tagNode.getLineNumber()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'block' expects an identifier", tagNode.getLineNumber(), tagNode.getStartPosition()); } String blockName = WhitespaceUtils.unquote(tagData.next()); diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/CycleTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/CycleTag.java index 687fca9b5..56c492b84 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/CycleTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/CycleTag.java @@ -62,19 +62,19 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { HelperStringTokenizer items = new HelperStringTokenizer(helper.get(0)); items.splitComma(true); values = items.allTokens(); - Integer forindex = (Integer) interpreter.retraceVariable(LOOP_INDEX, tagNode.getLineNumber()); + Integer forindex = (Integer) interpreter.retraceVariable(LOOP_INDEX, tagNode.getLineNumber(), tagNode.getStartPosition()); if (forindex == null) { forindex = 0; } if (values.size() == 1) { var = values.get(0); - values = (List) interpreter.retraceVariable(var, tagNode.getLineNumber()); + values = (List) interpreter.retraceVariable(var, tagNode.getLineNumber(), tagNode.getStartPosition()); if (values == null) { - return interpreter.resolveString(var, tagNode.getLineNumber()); + return interpreter.resolveString(var, tagNode.getLineNumber(), tagNode.getStartPosition()); } } else { for (int i = 0; i < values.size(); i++) { - values.set(i, interpreter.resolveString(values.get(i), tagNode.getLineNumber())); + values.set(i, interpreter.resolveString(values.get(i), tagNode.getLineNumber(), tagNode.getStartPosition())); } } return values.get(forindex % values.size()); @@ -83,13 +83,13 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { items.splitComma(true); values = items.allTokens(); for (int i = 0; i < values.size(); i++) { - values.set(i, interpreter.resolveString(values.get(i), tagNode.getLineNumber())); + values.set(i, interpreter.resolveString(values.get(i), tagNode.getLineNumber(), tagNode.getStartPosition())); } var = helper.get(2); interpreter.getContext().put(var, values); return ""; } else { - throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'cycle' expects 1 or 3 helper(s), was: " + helper.size(), tagNode.getLineNumber()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'cycle' expects 1 or 3 helper(s), was: " + helper.size(), tagNode.getLineNumber(), tagNode.getStartPosition()); } } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java index f18e05e3b..e7673bd94 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ExtendsTag.java @@ -82,11 +82,11 @@ public class ExtendsTag implements Tag { public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { HelperStringTokenizer tokenizer = new HelperStringTokenizer(tagNode.getHelpers()); if (!tokenizer.hasNext()) { - throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'extends' expects template path", tagNode.getLineNumber()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'extends' expects template path", tagNode.getLineNumber(), tagNode.getStartPosition()); } - String path = interpreter.resolveString(tokenizer.next(), tagNode.getLineNumber()); - interpreter.getContext().getExtendPathStack().push(path, tagNode.getLineNumber()); + String path = interpreter.resolveString(tokenizer.next(), tagNode.getLineNumber(), tagNode.getStartPosition()); + interpreter.getContext().getExtendPathStack().push(path, tagNode.getLineNumber(), tagNode.getStartPosition()); try { String template = interpreter.getResource(path); @@ -97,7 +97,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { return ""; } catch (IOException e) { - throw new InterpretException(e.getMessage(), e, tagNode.getLineNumber()); + throw new InterpretException(e.getMessage(), e, tagNode.getLineNumber(), tagNode.getStartPosition()); } } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java index ec5efa18d..4b2cb4245 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java @@ -116,7 +116,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { } if (inPos >= helper.size()) { - throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'for' expects valid 'in' clause, got: " + tagNode.getHelpers(), tagNode.getLineNumber()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'for' expects valid 'in' clause, got: " + tagNode.getHelpers(), tagNode.getLineNumber(), tagNode.getStartPosition()); } String loopExpr = StringUtils.join(helper.subList(inPos + 1, helper.size()), ","); @@ -156,7 +156,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { } } } catch (Exception e) { - throw new InterpretException(e.getMessage(), e, tagNode.getLineNumber()); + throw new InterpretException(e.getMessage(), e, tagNode.getLineNumber(), tagNode.getStartPosition()); } } } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/FromTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/FromTag.java index a1c63d090..1fcc3d5c6 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/FromTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/FromTag.java @@ -51,10 +51,10 @@ public String getName() { public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { List helper = new HelperStringTokenizer(tagNode.getHelpers()).splitComma(true).allTokens(); if (helper.size() < 3 || !helper.get(1).equals("import")) { - throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'from' expects import list: " + helper, tagNode.getLineNumber()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'from' expects import list: " + helper, tagNode.getLineNumber(), tagNode.getStartPosition()); } - String templateFile = interpreter.resolveString(helper.get(0), tagNode.getLineNumber()); + String templateFile = interpreter.resolveString(helper.get(0), tagNode.getLineNumber(), tagNode.getStartPosition()); Map imports = new LinkedHashMap<>(); PeekingIterator args = Iterators.peekingIterator(helper.subList(2, helper.size()).iterator()); @@ -96,7 +96,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { return ""; } catch (IOException e) { - throw new InterpretException(e.getMessage(), e, tagNode.getLineNumber()); + throw new InterpretException(e.getMessage(), e, tagNode.getLineNumber(), tagNode.getStartPosition()); } } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/IfTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/IfTag.java index e822d2a49..89923c31f 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/IfTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/IfTag.java @@ -55,7 +55,7 @@ public class IfTag implements Tag { @Override public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { if (StringUtils.isBlank(tagNode.getHelpers())) { - throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'if' expects expression", tagNode.getLineNumber()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'if' expects expression", tagNode.getLineNumber(), tagNode.getStartPosition()); } Iterator nodeIterator = tagNode.getChildren().iterator(); diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/IfchangedTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/IfchangedTag.java index af1a30130..a706b94bf 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/IfchangedTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/IfchangedTag.java @@ -43,12 +43,12 @@ public class IfchangedTag implements Tag { @Override public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { if (StringUtils.isBlank(tagNode.getHelpers())) { - throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'ifchanged' expects a variable parameter", tagNode.getLineNumber()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'ifchanged' expects a variable parameter", tagNode.getLineNumber(), tagNode.getStartPosition()); } boolean isChanged = true; String var = tagNode.getHelpers().trim(); Object older = interpreter.getContext().get(LASTKEY + var); - Object test = interpreter.retraceVariable(var, tagNode.getLineNumber()); + Object test = interpreter.retraceVariable(var, tagNode.getLineNumber(), tagNode.getStartPosition()); if (older == null) { if (test == null) { isChanged = false; diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java index 8879dfd8a..ed21f3720 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java @@ -4,13 +4,9 @@ import java.util.List; import java.util.Map; - -import com.google.common.collect.ImmutableMap; -import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; -import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory; - import org.apache.commons.lang3.StringUtils; +import com.google.common.collect.ImmutableMap; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaParam; import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; @@ -19,9 +15,11 @@ import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.TemplateError; +import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; import com.hubspot.jinjava.interpret.TemplateError.ErrorType; import com.hubspot.jinjava.interpret.TemplateSyntaxException; +import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory; import com.hubspot.jinjava.lib.fn.MacroFunction; import com.hubspot.jinjava.tree.Node; import com.hubspot.jinjava.tree.TagNode; @@ -67,7 +65,7 @@ public String getName() { public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { List helper = new HelperStringTokenizer(tagNode.getHelpers()).allTokens(); if (helper.isEmpty()) { - throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'import' expects 1 helper, was: " + helper.size(), tagNode.getLineNumber()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'import' expects 1 helper, was: " + helper.size(), tagNode.getLineNumber(), tagNode.getStartPosition()); } String contextVar = ""; @@ -79,7 +77,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { String path = StringUtils.trimToEmpty(helper.get(0)); try { - interpreter.getContext().getImportPathStack().push(path, tagNode.getLineNumber()); + interpreter.getContext().getImportPathStack().push(path, tagNode.getLineNumber(), tagNode.getStartPosition()); } catch (ImportTagCycleException e) { interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.EXCEPTION, ErrorItem.TAG, "Import cycle detected for path: '" + path + "'", null, tagNode.getLineNumber(), @@ -87,7 +85,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { return ""; } - String templateFile = interpreter.resolveString(path, tagNode.getLineNumber()); + String templateFile = interpreter.resolveString(path, tagNode.getLineNumber(), tagNode.getStartPosition()); interpreter.getContext().addDependency("coded_files", templateFile); try { String template = interpreter.getResource(templateFile); @@ -112,7 +110,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { return ""; } catch (IOException e) { - throw new InterpretException(e.getMessage(), e, tagNode.getLineNumber()); + throw new InterpretException(e.getMessage(), e, tagNode.getLineNumber(), tagNode.getStartPosition()); } } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java index 265479da3..94dd27980 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java @@ -17,12 +17,9 @@ import java.io.IOException; -import com.google.common.collect.ImmutableMap; -import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; -import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory; - import org.apache.commons.lang3.StringUtils; +import com.google.common.collect.ImmutableMap; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaParam; import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; @@ -30,9 +27,11 @@ import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.TemplateError; +import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; import com.hubspot.jinjava.interpret.TemplateError.ErrorType; import com.hubspot.jinjava.interpret.TemplateSyntaxException; +import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory; import com.hubspot.jinjava.tree.Node; import com.hubspot.jinjava.tree.TagNode; import com.hubspot.jinjava.util.HelperStringTokenizer; @@ -54,14 +53,14 @@ public class IncludeTag implements Tag { public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { HelperStringTokenizer helper = new HelperStringTokenizer(tagNode.getHelpers()); if (!helper.hasNext()) { - throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'include' expects template path", tagNode.getLineNumber()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'include' expects template path", tagNode.getLineNumber(), tagNode.getStartPosition()); } String path = StringUtils.trimToEmpty(helper.next()); - String templateFile = interpreter.resolveString(path, tagNode.getLineNumber()); + String templateFile = interpreter.resolveString(path, tagNode.getLineNumber(), tagNode.getStartPosition()); try { - interpreter.getContext().getIncludePathStack().push(templateFile, tagNode.getLineNumber()); + interpreter.getContext().getIncludePathStack().push(templateFile, tagNode.getLineNumber(), tagNode.getStartPosition()); } catch (IncludeTagCycleException e) { interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.EXCEPTION, ErrorItem.TAG, "Include cycle detected for path: '" + templateFile + "'", null, tagNode.getLineNumber(), e, @@ -83,7 +82,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { return result; } catch (IOException e) { - throw new InterpretException(e.getMessage(), e, tagNode.getLineNumber()); + throw new InterpretException(e.getMessage(), e, tagNode.getLineNumber(), tagNode.getStartPosition()); } finally { interpreter.getContext().getIncludePathStack().pop(); } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/MacroTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/MacroTag.java index 72824ca59..f7da87fc4 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/MacroTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/MacroTag.java @@ -66,7 +66,7 @@ public String getEndTagName() { public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { Matcher matcher = MACRO_PATTERN.matcher(tagNode.getHelpers()); if (!matcher.find()) { - throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Unable to parse macro definition: " + tagNode.getHelpers(), tagNode.getLineNumber()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Unable to parse macro definition: " + tagNode.getHelpers(), tagNode.getLineNumber(), tagNode.getStartPosition()); } String name = matcher.group(1); diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/SetTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/SetTag.java index 52bf691f1..e8e885aa5 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/SetTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/SetTag.java @@ -64,7 +64,7 @@ public String getName() { @Override public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { if (!tagNode.getHelpers().contains("=")) { - throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'set' expects an assignment expression with '=', but was: " + tagNode.getHelpers(), tagNode.getLineNumber()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'set' expects an assignment expression with '=', but was: " + tagNode.getHelpers(), tagNode.getLineNumber(), tagNode.getStartPosition()); } int eqPos = tagNode.getHelpers().indexOf('='); @@ -72,10 +72,10 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { String expr = tagNode.getHelpers().substring(eqPos + 1, tagNode.getHelpers().length()); if (var.length() == 0) { - throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'set' requires a var name to assign to", tagNode.getLineNumber()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'set' requires a var name to assign to", tagNode.getLineNumber(), tagNode.getStartPosition()); } if (StringUtils.isBlank(expr)) { - throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'set' requires an expression to assign to a var", tagNode.getLineNumber()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'set' requires an expression to assign to a var", tagNode.getLineNumber(), tagNode.getStartPosition()); } String[] varTokens = var.split(","); @@ -86,7 +86,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { List exprVals = (List) interpreter.resolveELExpression("[" + expr + "]", tagNode.getLineNumber()); if (varTokens.length != exprVals.size()) { - throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'set' declares an uneven number of variables and assigned values", tagNode.getLineNumber()); + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'set' declares an uneven number of variables and assigned values", tagNode.getLineNumber(), tagNode.getStartPosition()); } for (int i = 0; i < varTokens.length; i++) { diff --git a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java index 9d212eb76..a2e62f212 100644 --- a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java @@ -27,12 +27,13 @@ import com.hubspot.jinjava.util.Logging; public class ExpressionNode extends Node { - private static final long serialVersionUID = 341642231109911346L; + + private static final long serialVersionUID = -6063173739682221042L; private final ExpressionToken master; public ExpressionNode(ExpressionToken token) { - super(token, token.getLineNumber()); + super(token, token.getLineNumber(), token.getStartPosition()); master = token; } diff --git a/src/main/java/com/hubspot/jinjava/tree/Node.java b/src/main/java/com/hubspot/jinjava/tree/Node.java index edeaa96a1..8968b7507 100644 --- a/src/main/java/com/hubspot/jinjava/tree/Node.java +++ b/src/main/java/com/hubspot/jinjava/tree/Node.java @@ -26,17 +26,19 @@ public abstract class Node implements Serializable { - private static final long serialVersionUID = 7323842986596895498L; + private static final long serialVersionUID = -6194634312533310816L; private final Token master; private final int lineNumber; + private final int startPosition; private Node parent = null; private LinkedList children = new LinkedList(); - public Node(Token master, int lineNumber) { + public Node(Token master, int lineNumber, int startPosition) { this.master = master; this.lineNumber = lineNumber; + this.startPosition = startPosition; } public Node getParent() { @@ -55,6 +57,10 @@ public int getLineNumber() { return lineNumber; } + public int getStartPosition() { + return startPosition; + } + public LinkedList getChildren() { return children; } diff --git a/src/main/java/com/hubspot/jinjava/tree/RootNode.java b/src/main/java/com/hubspot/jinjava/tree/RootNode.java index bf9512a0d..e644f82a9 100644 --- a/src/main/java/com/hubspot/jinjava/tree/RootNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/RootNode.java @@ -19,10 +19,10 @@ import com.hubspot.jinjava.tree.output.OutputNode; public class RootNode extends Node { - private static final long serialVersionUID = 97675838726004658L; + private static final long serialVersionUID = 5904181260202954424L; RootNode() { - super(null, 0); + super(null, 0, 0); } @Override diff --git a/src/main/java/com/hubspot/jinjava/tree/TagNode.java b/src/main/java/com/hubspot/jinjava/tree/TagNode.java index ee71bb8dd..92e3180a7 100644 --- a/src/main/java/com/hubspot/jinjava/tree/TagNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/TagNode.java @@ -22,14 +22,15 @@ import com.hubspot.jinjava.tree.parse.TagToken; public class TagNode extends Node { - private static final long serialVersionUID = 2405693063353887509L; + + private static final long serialVersionUID = -6971280448795354252L; private final Tag tag; private final TagToken master; private final String endName; public TagNode(Tag tag, TagToken token) { - super(token, token.getLineNumber()); + super(token, token.getLineNumber(), token.getStartPosition()); this.master = token; this.tag = tag; @@ -37,7 +38,7 @@ public TagNode(Tag tag, TagToken token) { } private TagNode(TagNode n) { - super(n.master, n.getLineNumber()); + super(n.master, n.getLineNumber(), n.getStartPosition()); tag = n.tag; master = n.master; @@ -51,7 +52,7 @@ public OutputNode render(JinjavaInterpreter interpreter) { } catch (InterpretException e) { throw e; } catch (Exception e) { - throw new InterpretException("Error rendering tag", e, master.getLineNumber()); + throw new InterpretException("Error rendering tag", e, master.getLineNumber(), master.getStartPosition()); } } diff --git a/src/main/java/com/hubspot/jinjava/tree/TextNode.java b/src/main/java/com/hubspot/jinjava/tree/TextNode.java index e9b5fccb1..2b855bac2 100644 --- a/src/main/java/com/hubspot/jinjava/tree/TextNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/TextNode.java @@ -21,12 +21,13 @@ import com.hubspot.jinjava.tree.parse.TextToken; public class TextNode extends Node { - private static final long serialVersionUID = 8488738480534354216L; + + private static final long serialVersionUID = 127827773323298439L; private final TextToken master; public TextNode(TextToken token) { - super(token, token.getLineNumber()); + super(token, token.getLineNumber(), token.getStartPosition()); master = token; } diff --git a/src/main/java/com/hubspot/jinjava/tree/TreeParser.java b/src/main/java/com/hubspot/jinjava/tree/TreeParser.java index 402c7bf2d..66278eaae 100644 --- a/src/main/java/com/hubspot/jinjava/tree/TreeParser.java +++ b/src/main/java/com/hubspot/jinjava/tree/TreeParser.java @@ -71,7 +71,7 @@ public Node buildTree() { interpreter.addError(TemplateError.fromException( new MissingEndTagException(((TagNode) parent).getEndName(), parent.getMaster().getImage(), - parent.getLineNumber()))); + parent.getLineNumber(), parent.getStartPosition()))); } return root; @@ -99,7 +99,7 @@ private Node nextNode() { default: interpreter.addError(TemplateError.fromException(new UnexpectedTokenException(token.getImage(), - token.getLineNumber()))); + token.getLineNumber(), token.getStartPosition()))); } return null; } @@ -114,7 +114,7 @@ private Node getLastSibling() { private Node text(TextToken textToken) { if (interpreter.getConfig().isLstripBlocks()) { if (scanner.hasNext() && scanner.peek().getType() == TOKEN_TAG) { - textToken = new TextToken(StringUtils.stripEnd(textToken.getImage(), "\t "), textToken.getLineNumber()); + textToken = new TextToken(StringUtils.stripEnd(textToken.getImage(), "\t "), textToken.getLineNumber(), textToken.getStartPosition()); } } @@ -210,7 +210,7 @@ private void endTag(Tag tag, TagToken tagToken) { interpreter.addError(TemplateError.fromException( new TemplateSyntaxException(tagToken.getImage(), "Mismatched end tag, expected: " + parentTag.getEndName(), - tagToken.getLineNumber()))); + tagToken.getLineNumber(), tagToken.getStartPosition()))); } } } diff --git a/src/main/java/com/hubspot/jinjava/tree/parse/ExpressionToken.java b/src/main/java/com/hubspot/jinjava/tree/parse/ExpressionToken.java index 863e91c6a..71118f238 100644 --- a/src/main/java/com/hubspot/jinjava/tree/parse/ExpressionToken.java +++ b/src/main/java/com/hubspot/jinjava/tree/parse/ExpressionToken.java @@ -22,12 +22,13 @@ import com.hubspot.jinjava.util.WhitespaceUtils; public class ExpressionToken extends Token { - private static final long serialVersionUID = 8307037212944170832L; + + private static final long serialVersionUID = 6336768632140743908L; private String expr; - public ExpressionToken(String image, int lineNumber) { - super(image, lineNumber); + public ExpressionToken(String image, int lineNumber, int startPosition) { + super(image, lineNumber, startPosition); } @Override diff --git a/src/main/java/com/hubspot/jinjava/tree/parse/NoteToken.java b/src/main/java/com/hubspot/jinjava/tree/parse/NoteToken.java index be81809de..c5ef3387f 100644 --- a/src/main/java/com/hubspot/jinjava/tree/parse/NoteToken.java +++ b/src/main/java/com/hubspot/jinjava/tree/parse/NoteToken.java @@ -19,10 +19,10 @@ public class NoteToken extends Token { - private static final long serialVersionUID = 6112027107603795408L; + private static final long serialVersionUID = -3859011447900311329L; - public NoteToken(String image, int lineNumber) { - super(image, lineNumber); + public NoteToken(String image, int lineNumber, int startPosition) { + super(image, lineNumber, startPosition); } @Override diff --git a/src/main/java/com/hubspot/jinjava/tree/parse/TagToken.java b/src/main/java/com/hubspot/jinjava/tree/parse/TagToken.java index 95de63b9b..d4f0edbfe 100644 --- a/src/main/java/com/hubspot/jinjava/tree/parse/TagToken.java +++ b/src/main/java/com/hubspot/jinjava/tree/parse/TagToken.java @@ -22,13 +22,13 @@ public class TagToken extends Token { - private static final long serialVersionUID = 2766011408032384360L; + private static final long serialVersionUID = -4927751270481832992L; private String tagName; private String helpers; - public TagToken(String image, int lineNumber) { - super(image, lineNumber); + public TagToken(String image, int lineNumber, int startPosition) { + super(image, lineNumber, startPosition); } @Override @@ -42,7 +42,7 @@ public int getType() { @Override protected void parse() { if (image.length() < 4) { - throw new TemplateSyntaxException(image, "Malformed tag token", getLineNumber()); + throw new TemplateSyntaxException(image, "Malformed tag token", getLineNumber(), getStartPosition()); } content = image.substring(2, image.length() - 2); diff --git a/src/main/java/com/hubspot/jinjava/tree/parse/TextToken.java b/src/main/java/com/hubspot/jinjava/tree/parse/TextToken.java index f2f64dac3..8cf50b461 100644 --- a/src/main/java/com/hubspot/jinjava/tree/parse/TextToken.java +++ b/src/main/java/com/hubspot/jinjava/tree/parse/TextToken.java @@ -21,10 +21,10 @@ public class TextToken extends Token { - private static final long serialVersionUID = -5015884072204770458L; + private static final long serialVersionUID = -6168990984496468543L; - public TextToken(String image, int lineNumber) { - super(image, lineNumber); + public TextToken(String image, int lineNumber, int startPosition) { + super(image, lineNumber, startPosition); } @Override diff --git a/src/main/java/com/hubspot/jinjava/tree/parse/Token.java b/src/main/java/com/hubspot/jinjava/tree/parse/Token.java index a8c4a7796..a37468945 100644 --- a/src/main/java/com/hubspot/jinjava/tree/parse/Token.java +++ b/src/main/java/com/hubspot/jinjava/tree/parse/Token.java @@ -26,21 +26,23 @@ public abstract class Token implements Serializable { - private static final long serialVersionUID = -7513379852268838992L; + private static final long serialVersionUID = 3359084948763661809L; protected final String image; // useful for some token type protected String content; protected final int lineNumber; + protected final int startPosition; private boolean leftTrim; private boolean rightTrim; private boolean rightTrimAfterEnd; - public Token(String image, int lineNumber) { + public Token(String image, int lineNumber, int startPosition) { this.image = image; this.lineNumber = lineNumber; + this.startPosition = startPosition; parse(); } @@ -76,6 +78,10 @@ public void setRightTrimAfterEnd(boolean rightTrimAfterEnd) { this.rightTrimAfterEnd = rightTrimAfterEnd; } + public int getStartPosition() { + return startPosition; + } + @Override public String toString() { return image; @@ -85,18 +91,18 @@ public String toString() { public abstract int getType(); - static Token newToken(int tokenKind, String image, int lineNumber) { + static Token newToken(int tokenKind, String image, int lineNumber, int startPosition) { switch (tokenKind) { case TOKEN_FIXED: - return new TextToken(image, lineNumber); + return new TextToken(image, lineNumber, startPosition); case TOKEN_NOTE: - return new NoteToken(image, lineNumber); + return new NoteToken(image, lineNumber, startPosition); case TOKEN_EXPR_START: - return new ExpressionToken(image, lineNumber); + return new ExpressionToken(image, lineNumber, startPosition); case TOKEN_TAG: - return new TagToken(image, lineNumber); + return new TagToken(image, lineNumber, startPosition); default: - throw new UnexpectedTokenException(String.valueOf((char) tokenKind), lineNumber); + throw new UnexpectedTokenException(String.valueOf((char) tokenKind), lineNumber, startPosition); } } diff --git a/src/main/java/com/hubspot/jinjava/tree/parse/TokenScanner.java b/src/main/java/com/hubspot/jinjava/tree/parse/TokenScanner.java index 81cdb2226..397cf853d 100644 --- a/src/main/java/com/hubspot/jinjava/tree/parse/TokenScanner.java +++ b/src/main/java/com/hubspot/jinjava/tree/parse/TokenScanner.java @@ -1,18 +1,18 @@ -/********************************************************************** - * Copyright (c) 2014 HubSpot 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. - **********************************************************************/ +/* + Copyright (c) 2014 HubSpot 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.hubspot.jinjava.tree.parse; import static com.hubspot.jinjava.tree.parse.TokenScannerSymbols.TOKEN_EXPR_END; @@ -45,6 +45,7 @@ public class TokenScanner extends AbstractIterator { private int inBlock = 0; private char inQuote = 0; private int currLine = 1; + private int lastNewlinePos = 0; public TokenScanner(String input, JinjavaConfig config) { this.config = config; @@ -61,10 +62,11 @@ public TokenScanner(String input, JinjavaConfig config) { inBlock = 0; inQuote = 0; currLine = 1; + lastNewlinePos = 0; } private Token getNextToken() { - char c = 0; + char c; while (currPost < length) { c = is[currPost++]; if (currPost == length) { @@ -200,6 +202,7 @@ private Token getNextToken() { break; case TOKEN_NEWLINE: currLine++; + lastNewlinePos = currPost; if (inComment > 0 || inBlock > 0) { continue; @@ -237,14 +240,15 @@ private Token getEndToken() { if (inComment > 0) { type = TOKEN_NOTE; } - return Token.newToken(type, String.valueOf(is, tokenStart, tokenLength), currLine); + return Token.newToken(type, String.valueOf(is, tokenStart, tokenLength), currLine, tokenStart - lastNewlinePos + 1); } private Token newToken(int kind) { - Token t = Token.newToken(kind, String.valueOf(is, lastStart, tokenLength), currLine); + Token t = Token.newToken(kind, String.valueOf(is, lastStart, tokenLength), currLine, lastStart - lastNewlinePos + 1); if (t instanceof TagToken) { if (config.isTrimBlocks() && currPost < length && is[currPost] == '\n') { + lastNewlinePos = currPost; ++currPost; ++tokenStart; } @@ -260,7 +264,7 @@ private Token newToken(int kind) { } if (inRaw > 0 && t.getType() != TOKEN_FIXED) { - return Token.newToken(TOKEN_FIXED, t.image, currLine); + return Token.newToken(TOKEN_FIXED, t.image, currLine, tokenStart); } return t; diff --git a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java index c6dd71241..73cb78c8f 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java @@ -40,14 +40,14 @@ public void resolveBlockStubsWithMissingNamedBlock() { @Test public void resolveBlockStubs() throws Exception { - interpreter.addBlock("foobar", Lists.newLinkedList(Lists.newArrayList((new TextNode(new TextToken("sparta", -1)))))); + interpreter.addBlock("foobar", Lists.newLinkedList(Lists.newArrayList((new TextNode(new TextToken("sparta", -1, -1)))))); String content = "this is {% block foobar %}foobar{% endblock %}!"; assertThat(interpreter.render(content)).isEqualTo("this is sparta!"); } @Test public void resolveBlockStubsWithSpecialChars() throws Exception { - interpreter.addBlock("foobar", Lists.newLinkedList(Lists.newArrayList(new TextNode(new TextToken("$150.00", -1))))); + interpreter.addBlock("foobar", Lists.newLinkedList(Lists.newArrayList(new TextNode(new TextToken("$150.00", -1, -1))))); String content = "this is {% block foobar %}foobar{% endblock %}!"; assertThat(interpreter.render(content)).isEqualTo("this is $150.00!"); } diff --git a/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java b/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java index b1af97a95..c0718dccc 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java @@ -22,7 +22,7 @@ public void itUsesOverloadedToStringForBaseObject() { @Test public void itShowsFieldNameForUnknownTagError() { - TemplateError e = TemplateError.fromException(new UnknownTagException("unknown", "{% unknown() %}", 11)); + TemplateError e = TemplateError.fromException(new UnknownTagException("unknown", "{% unknown() %}", 11, 3)); assertThat(e.getFieldName()).isEqualTo("unknown"); } diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/TruncateFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/TruncateFilterTest.java index d6065c5e4..fbbbeb7a5 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/TruncateFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/TruncateFilterTest.java @@ -26,7 +26,7 @@ public class TruncateFilterTest { @Before public void setup() { - when(interpreter.resolveString(anyString(), anyInt())).thenAnswer(new ReturnsArgumentAt(0)); + when(interpreter.resolveString(anyString(), anyInt(), anyInt())).thenAnswer(new ReturnsArgumentAt(0)); } @Test diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/ImportTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/ImportTagTest.java index 0d28f3e8e..9ece5c1b4 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/ImportTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/ImportTagTest.java @@ -79,7 +79,7 @@ public void importedContextExposesVars() { @Test public void importedContextExposesMacros() { assertThat(fixture("import")).contains(""); - MacroFunction fn = (MacroFunction) interpreter.resolveObject("pegasus.spacer", -1); + MacroFunction fn = (MacroFunction) interpreter.resolveObject("pegasus.spacer", -1, -1); assertThat(fn.getName()).isEqualTo("spacer"); assertThat(fn.getArguments()).containsExactly("orientation", "size"); assertThat(fn.getDefaults()).contains(entry("orientation", "h"), entry("size", 42)); diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java index 37c5d8c8e..c5f0a9e7e 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java @@ -51,7 +51,7 @@ public void testSimpleFn() { TagNode t = fixture("simple"); assertThat(t.render(interpreter).getValue()).isEmpty(); - MacroFunction fn = (MacroFunction) interpreter.resolveObject("__macros__.getPath", -1); + MacroFunction fn = (MacroFunction) interpreter.resolveObject("__macros__.getPath", -1, -1); assertThat(fn.getName()).isEqualTo("getPath"); assertThat(fn.getArguments()).isEmpty(); assertThat(fn.isCaller()).isFalse(); @@ -67,7 +67,7 @@ public void testFnWithArgs() { TagNode t = fixture("with-args"); assertThat(t.render(interpreter).getValue()).isEmpty(); - MacroFunction fn = (MacroFunction) interpreter.resolveObject("__macros__.section_link", -1); + MacroFunction fn = (MacroFunction) interpreter.resolveObject("__macros__.section_link", -1, -1); assertThat(fn.getName()).isEqualTo("section_link"); assertThat(fn.getArguments()).containsExactly("link", "text"); @@ -79,7 +79,7 @@ public void testFnWithArgsWithDefVals() { TagNode t = fixture("def-vals"); assertThat(t.render(interpreter).getValue()).isEmpty(); - MacroFunction fn = (MacroFunction) interpreter.resolveObject("__macros__.article", -1); + MacroFunction fn = (MacroFunction) interpreter.resolveObject("__macros__.article", -1, -1); assertThat(fn.getArguments()).containsExactly("title", "thumb", "link", "summary", "last"); assertThat(fn.getDefaults()).contains(entry("last", false)); @@ -94,7 +94,7 @@ public void testFnWithArrayDefVal() { TagNode t = fixture("array-def-val"); assertThat(t.render(interpreter).getValue()).isEmpty(); - MacroFunction fn = (MacroFunction) interpreter.resolveObject("__macros__.prefix", -1); + MacroFunction fn = (MacroFunction) interpreter.resolveObject("__macros__.prefix", -1, -1); assertThat(fn.getArguments()).containsExactly("property", "value", "prefixes", "prefixval"); assertThat(fn.getDefaults()).contains(entry("prefixes", Lists.newArrayList("webkit", "moz"))); } diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/SetTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/SetTagTest.java index c57b48a2e..07788591b 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/SetTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/SetTagTest.java @@ -52,7 +52,7 @@ public void itReturnsBlankString() throws Exception { public void itAssignsValToVar() throws Exception { TagNode tagNode = (TagNode) fixture("set-val"); tag.interpret(tagNode, interpreter); - assertThat(interpreter.resolveObject("primary_line_height", -1)).isEqualTo(42L); + assertThat(interpreter.resolveObject("primary_line_height", -1, -1)).isEqualTo(42L); } @Test @@ -60,7 +60,7 @@ public void itAssignsVarToVar() throws Exception { context.put("primary_font_size_num", 10); TagNode tagNode = (TagNode) fixture("set-var-exp"); tag.interpret(tagNode, interpreter); - assertThat(interpreter.resolveObject("primary_line_height", -1)).isEqualTo(15.0); + assertThat(interpreter.resolveObject("primary_line_height", -1, -1)).isEqualTo(15.0); } @Test @@ -79,7 +79,7 @@ public void itHandlesComplexDictWithConcats() throws Exception { TagNode tagNode = (TagNode) fixture("set-complex-dict"); tag.interpret(tagNode, interpreter); - Map dict = (Map) interpreter.resolveObject("styles", -1); + Map dict = (Map) interpreter.resolveObject("styles", -1, -1); assertThat(dict).contains( entry("heading", "color:bluecolor;text-shadow:2px 2px 1px rgba(0,0,0,.1);font-family:fontfam"), entry("module", "background:#ffffff;padding:123px;border:1px solid 10")); @@ -90,7 +90,7 @@ public void itConcatsStringsInExprs() throws Exception { context.put("lw_font_size_base", 42); TagNode tagNode = (TagNode) fixture("set-string-concat"); tag.interpret(tagNode, interpreter); - assertThat(interpreter.resolveObject("lw_font_size", -1)).isEqualTo("font-size: 42px; "); + assertThat(interpreter.resolveObject("lw_font_size", -1 ,-1)).isEqualTo("font-size: 42px; "); } @Test @@ -98,7 +98,7 @@ public void itConcatsStringsWithNestedExprs() throws Exception { context.put("lw_font_size_base", 10); TagNode tagNode = (TagNode) fixture("set-expr-concat"); tag.interpret(tagNode, interpreter); - assertThat(interpreter.resolveObject("lw_secondary_font_size", -1)).isEqualTo("font-size: 8px; "); + assertThat(interpreter.resolveObject("lw_secondary_font_size", -1, -1)).isEqualTo("font-size: 8px; "); } @Test @@ -106,7 +106,7 @@ public void itSupportsExpressionsWithFilters() throws Exception { context.put("max_size", "470"); TagNode tagNode = (TagNode) fixture("set-filter-expr"); tag.interpret(tagNode, interpreter); - assertThat(interpreter.resolveObject("ie_max_size", -1)).isEqualTo(440L); + assertThat(interpreter.resolveObject("ie_max_size", -1 ,-1)).isEqualTo(440L); } @Test @@ -115,7 +115,7 @@ public void itSupportsArraySyntax() throws Exception { TagNode tagNode = (TagNode) fixture("set-array"); tag.interpret(tagNode, interpreter); - List result = (List) interpreter.resolveObject("the_list", -1); + List result = (List) interpreter.resolveObject("the_list", -1,-1); assertThat(result).containsExactly(1L, 2L, 3L, 7L); } @@ -126,7 +126,7 @@ public void itSupportsDictionarySyntax() throws Exception { TagNode tagNode = (TagNode) fixture("set-dictionary"); tag.interpret(tagNode, interpreter); - Map result = (Map) interpreter.resolveObject("the_dictionary", -1); + Map result = (Map) interpreter.resolveObject("the_dictionary", -1,-1); assertThat(result).contains(entry("seven", 7L)); } diff --git a/src/test/java/com/hubspot/jinjava/tree/parse/TagTokenTest.java b/src/test/java/com/hubspot/jinjava/tree/parse/TagTokenTest.java index 192ea1173..3d11e38c0 100644 --- a/src/test/java/com/hubspot/jinjava/tree/parse/TagTokenTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/parse/TagTokenTest.java @@ -11,20 +11,20 @@ public class TagTokenTest { @Test public void testParseTag() { - TagToken t = new TagToken("{% foo %}", 1); + TagToken t = new TagToken("{% foo %}", 1, 2); assertThat(t.getTagName()).isEqualTo("foo"); } @Test public void testParseTagWithHelpers() { - TagToken t = new TagToken("{% foo bar %}", 1); + TagToken t = new TagToken("{% foo bar %}", 1,2); assertThat(t.getTagName()).isEqualTo("foo"); assertThat(t.getHelpers().trim()).isEqualTo("bar"); } @Test public void tagNameIsAllJavaIdentifiers() { - TagToken t = new TagToken("{%rich_text\"top_left\"%}", 1); + TagToken t = new TagToken("{%rich_text\"top_left\"%}", 1,2); assertThat(t.getTagName()).isEqualTo("rich_text"); assertThat(t.getHelpers()).isEqualTo("\"top_left\""); } @@ -32,7 +32,7 @@ public void tagNameIsAllJavaIdentifiers() { @Test public void itThrowsParseErrorWhenMalformed() { try { - new TagToken("{% ", 1); + new TagToken("{% ", 1,2); failBecauseExceptionWasNotThrown(TemplateSyntaxException.class); } catch (TemplateSyntaxException e) { assertThat(e).hasMessageContaining("Malformed"); diff --git a/src/test/java/com/hubspot/jinjava/tree/parse/TokenScannerTest.java b/src/test/java/com/hubspot/jinjava/tree/parse/TokenScannerTest.java index 16d9a8db2..13c5be0bb 100644 --- a/src/test/java/com/hubspot/jinjava/tree/parse/TokenScannerTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/parse/TokenScannerTest.java @@ -14,6 +14,7 @@ import org.junit.Before; import org.junit.Test; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.io.Resources; import com.hubspot.jinjava.JinjavaConfig; @@ -182,6 +183,12 @@ public void test17() { assertEquals("{#abc{#.b#}", scanner.next().image); } + @Test + public void itGetsPositionsOfTokens() { + List tokens = tokens("positions"); + assertThat(tokens.stream().map(Token::getStartPosition).collect(Collectors.toList())).isEqualTo(ImmutableList.of(1, 0, 4, 0, 6, 0, 6, 0, 4, 0, 4, 13, 25, 0, 1)); + } + @Test public void itProperlyTokenizesCommentBlocksContainingTags() { List tokens = tokens("comment-with-tags"); @@ -272,11 +279,10 @@ private List tokens(String fixture) { private TokenScanner fixture(String fixture) { try { - TokenScanner t = new TokenScanner( + return new TokenScanner( Resources.toString(Resources.getResource(String.format("parse/tokenizer/%s.jinja", fixture)), StandardCharsets.UTF_8), config); - return t; } catch (Exception e) { throw new RuntimeException(e); } diff --git a/src/test/resources/parse/tokenizer/positions.jinja b/src/test/resources/parse/tokenizer/positions.jinja new file mode 100644 index 000000000..e8fb2369c --- /dev/null +++ b/src/test/resources/parse/tokenizer/positions.jinja @@ -0,0 +1,7 @@ +{% if foo %} + {% if bar %} + {{ 1 }} + {# this is a comment #} + {% endif %} + {% raw %}hello world.{% endraw %} +{% endif From d6875c0db0b9fab2fb9fd666d0a350056e8f5064 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 27 Oct 2017 21:37:57 -0400 Subject: [PATCH 0360/2465] add some delegate methods for backwards compatibility --- .../jinjava/interpret/JinjavaInterpreter.java | 14 ++++++++++++++ .../jinjava/interpret/TemplateStateException.java | 8 ++++++++ src/main/java/com/hubspot/jinjava/tree/Node.java | 4 ++++ .../java/com/hubspot/jinjava/tree/parse/Token.java | 4 ++++ .../hubspot/jinjava/tree/parse/TagTokenTest.java | 6 +++--- 5 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index e4413eec2..60af096c1 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -311,6 +311,11 @@ public Object retraceVariable(String variable, int lineNumber, int startPosition return obj; } + public Object retraceVariable(String variable, int lineNumber) { + return retraceVariable(variable, lineNumber, -1); + } + + /** * Resolve a variable into an object value. If given a string literal (e.g. 'foo' or "foo"), this method returns the literal unquoted. If the variable is undefined in the context, this method returns the given variable string. * @@ -337,6 +342,10 @@ public Object resolveObject(String variable, int lineNumber, int startPosition) } } + public Object resolveObject(String variable, int lineNumber) { + return resolveObject(variable, lineNumber, -1); + } + /** * Resolve a variable into a string value. If given a string literal (e.g. 'foo' or "foo"), this method returns the literal unquoted. If the variable is undefined in the context, this method returns the given variable string. * @@ -352,6 +361,11 @@ public String resolveString(String variable, int lineNumber, int startPosition) return Objects.toString(resolveObject(variable, lineNumber, startPosition), ""); } + public String resolveString(String variable, int lineNumber) { + return resolveString(variable, lineNumber, -1); + } + + public Context getContext() { return context; } diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateStateException.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateStateException.java index 98df46ad5..8ba2dd381 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateStateException.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateStateException.java @@ -15,8 +15,16 @@ public TemplateStateException(String msg, int lineNumber, int startPosition) { super(msg, lineNumber, startPosition); } + public TemplateStateException(String msg, int lineNumber) { + super(msg, lineNumber, -1); + } + public TemplateStateException(String msg, Throwable e, int lineNumber, int startPosition) { super(msg, e, lineNumber, startPosition); } + public TemplateStateException(String msg, Throwable e, int lineNumber) { + super(msg, e, lineNumber, -1); + } + } diff --git a/src/main/java/com/hubspot/jinjava/tree/Node.java b/src/main/java/com/hubspot/jinjava/tree/Node.java index 8968b7507..6c1726fd4 100644 --- a/src/main/java/com/hubspot/jinjava/tree/Node.java +++ b/src/main/java/com/hubspot/jinjava/tree/Node.java @@ -35,6 +35,10 @@ public abstract class Node implements Serializable { private Node parent = null; private LinkedList children = new LinkedList(); + public Node(Token master, int lineNumber) { + this(master, lineNumber, -1); + } + public Node(Token master, int lineNumber, int startPosition) { this.master = master; this.lineNumber = lineNumber; diff --git a/src/main/java/com/hubspot/jinjava/tree/parse/Token.java b/src/main/java/com/hubspot/jinjava/tree/parse/Token.java index a37468945..0be0bc76e 100644 --- a/src/main/java/com/hubspot/jinjava/tree/parse/Token.java +++ b/src/main/java/com/hubspot/jinjava/tree/parse/Token.java @@ -46,6 +46,10 @@ public Token(String image, int lineNumber, int startPosition) { parse(); } + public Token(String image, int lineNumber) { + this(image, lineNumber, -1); + } + public String getImage() { return image; } diff --git a/src/test/java/com/hubspot/jinjava/tree/parse/TagTokenTest.java b/src/test/java/com/hubspot/jinjava/tree/parse/TagTokenTest.java index 3d11e38c0..2c15edf06 100644 --- a/src/test/java/com/hubspot/jinjava/tree/parse/TagTokenTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/parse/TagTokenTest.java @@ -17,14 +17,14 @@ public void testParseTag() { @Test public void testParseTagWithHelpers() { - TagToken t = new TagToken("{% foo bar %}", 1,2); + TagToken t = new TagToken("{% foo bar %}", 1, 2); assertThat(t.getTagName()).isEqualTo("foo"); assertThat(t.getHelpers().trim()).isEqualTo("bar"); } @Test public void tagNameIsAllJavaIdentifiers() { - TagToken t = new TagToken("{%rich_text\"top_left\"%}", 1,2); + TagToken t = new TagToken("{%rich_text\"top_left\"%}", 1, 2); assertThat(t.getTagName()).isEqualTo("rich_text"); assertThat(t.getHelpers()).isEqualTo("\"top_left\""); } @@ -32,7 +32,7 @@ public void tagNameIsAllJavaIdentifiers() { @Test public void itThrowsParseErrorWhenMalformed() { try { - new TagToken("{% ", 1,2); + new TagToken("{% ", 1, 2); failBecauseExceptionWasNotThrown(TemplateSyntaxException.class); } catch (TemplateSyntaxException e) { assertThat(e).hasMessageContaining("Malformed"); From 9134abb12d77e04453c50bcb556aaf78cae992e5 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 30 Oct 2017 15:27:56 -0400 Subject: [PATCH 0361/2465] log start position of token when creating an TemplateError --- .../jinjava/el/ExpressionResolver.java | 4 +- .../el/JinjavaInterpreterResolver.java | 8 +- .../jinjava/el/ext/AstMacroFunction.java | 3 +- .../jinjava/interpret/JinjavaInterpreter.java | 2 +- .../jinjava/interpret/TemplateError.java | 77 +++++++++++++++++-- .../hubspot/jinjava/lib/tag/ImportTag.java | 2 +- .../hubspot/jinjava/lib/tag/IncludeTag.java | 2 +- .../com/hubspot/jinjava/tree/TreeParser.java | 2 +- .../jinjava/interpret/TemplateErrorTest.java | 4 +- 9 files changed, 83 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java index 49faee69e..89f3d0b1f 100644 --- a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java @@ -72,7 +72,7 @@ public Object resolveExpression(String expression) { return result; } catch (PropertyNotFoundException e) { - interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.UNKNOWN, ErrorItem.PROPERTY, e.getMessage(), "", interpreter.getLineNumber(), e, + interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.UNKNOWN, ErrorItem.PROPERTY, e.getMessage(), "", interpreter.getLineNumber(), -1, e, BasicTemplateErrorCategory.UNKNOWN, ImmutableMap.of("exception", e.getMessage()))); } catch (TreeBuilderException e) { interpreter.addError(TemplateError.fromException(new TemplateSyntaxException(expression, @@ -80,7 +80,7 @@ public Object resolveExpression(String expression) { } catch (ELException e) { interpreter.addError(TemplateError.fromException(new TemplateSyntaxException(expression, e.getMessage(), interpreter.getLineNumber(), e))); } catch (DisabledException e) { - interpreter.addError(new TemplateError(ErrorType.FATAL, ErrorReason.DISABLED, ErrorItem.FUNCTION, e.getMessage(), expression, interpreter.getLineNumber(), e)); + interpreter.addError(new TemplateError(ErrorType.FATAL, ErrorReason.DISABLED, ErrorItem.FUNCTION, e.getMessage(), expression, interpreter.getLineNumber(), -1, e)); } catch (UnknownTokenException e) { // Re-throw the exception because you only get this when the config failOnUnknownTokens is enabled. throw e; diff --git a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java index 2b0c883d9..7c61a613d 100644 --- a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java @@ -180,13 +180,13 @@ private Object getValue(ELContext context, Object base, Object property, boolean } } catch (PropertyNotFoundException e) { if (errOnUnknownProp) { - interpreter.addError(TemplateError.fromUnknownProperty(base, propertyName, interpreter.getLineNumber())); + interpreter.addError(TemplateError.fromUnknownProperty(base, propertyName, interpreter.getLineNumber(), -1)); } } } } } catch (DisabledException e) { - interpreter.addError(new TemplateError(ErrorType.FATAL, ErrorReason.DISABLED, item, e.getMessage(), propertyName, interpreter.getLineNumber(), e)); + interpreter.addError(new TemplateError(ErrorType.FATAL, ErrorReason.DISABLED, item, e.getMessage(), propertyName, interpreter.getLineNumber(), -1, e)); } context.setPropertyResolved(true); @@ -240,7 +240,7 @@ private static DateTimeFormatter getFormatter(JinjavaInterpreter interpreter, Fo try { return StrftimeFormatter.formatter(d.getFormat(), interpreter.getConfig().getLocale()); } catch (IllegalArgumentException e) { - interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.SYNTAX_ERROR, ErrorItem.OTHER, e.getMessage(), null, interpreter.getLineNumber(), null, + interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.SYNTAX_ERROR, ErrorItem.OTHER, e.getMessage(), null, interpreter.getLineNumber(), -1, null, BasicTemplateErrorCategory.UNKNOWN_DATE, ImmutableMap.of("date", d.getDate().toString(), "exception", e.getMessage(), "lineNumber", String.valueOf(interpreter.getLineNumber())))); } } @@ -253,7 +253,7 @@ private static Locale getLocale(JinjavaInterpreter interpreter, FormattedDate d) try { return LocaleUtils.toLocale(d.getLanguage()); } catch (IllegalArgumentException e) { - interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.SYNTAX_ERROR, ErrorItem.OTHER, e.getMessage(), null, interpreter.getLineNumber(), null, + interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.SYNTAX_ERROR, ErrorItem.OTHER, e.getMessage(), null, interpreter.getLineNumber(), -1, null, BasicTemplateErrorCategory.UNKNOWN_LOCALE, ImmutableMap.of("date", d.getDate().toString(), "exception", e.getMessage(), "lineNumber", String.valueOf(interpreter.getLineNumber())))); } } diff --git a/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java b/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java index d554c100d..0d9b824c8 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java @@ -44,7 +44,8 @@ public Object eval(Bindings bindings, ELContext context) { TemplateError.ErrorItem.TAG, "Cycle detected for macro '" + getName() + "'", null, - -1, + e.getLineNumber(), + e.getStartPosition(), e, BasicTemplateErrorCategory.CYCLE_DETECTED, ImmutableMap.of("name", getName()))); diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index 60af096c1..458bc3875 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -217,7 +217,7 @@ public String render(Node root, boolean processExtendRoots) { if (context.doesRenderStackContain(renderStr)) { // This is a circular rendering. Stop rendering it here. addError(new TemplateError(ErrorType.WARNING, ErrorReason.EXCEPTION, ErrorItem.TAG, - "Rendering cycle detected: '" + renderStr + "'", null, getLineNumber(), + "Rendering cycle detected: '" + renderStr + "'", null, getLineNumber(), node.getStartPosition(), null, BasicTemplateErrorCategory.IMPORT_CYCLE_DETECTED, ImmutableMap.of("string", renderStr))); output.addNode(new RenderedOutputNode(renderStr)); } else { diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java index 1aa78155c..d3f9a117c 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java @@ -42,39 +42,50 @@ public enum ErrorItem { private final String message; private final String fieldName; private final int lineno; + private final int startPosition; private final TemplateErrorCategory category; private final Map categoryErrors; private final Exception exception; public static TemplateError fromSyntaxError(InterpretException ex) { - return new TemplateError(ErrorType.FATAL, ErrorReason.SYNTAX_ERROR, ErrorItem.OTHER, ExceptionUtils.getMessage(ex), null, ex.getLineNumber(), ex); + return new TemplateError(ErrorType.FATAL, ErrorReason.SYNTAX_ERROR, ErrorItem.OTHER, ExceptionUtils.getMessage(ex), null, ex.getLineNumber(), ex.getStartPosition(), ex); } public static TemplateError fromException(TemplateSyntaxException ex) { String fieldName = (ex instanceof UnknownTagException) ? ((UnknownTagException) ex).getTag() : ex.getCode(); - return new TemplateError(ErrorType.FATAL, ErrorReason.SYNTAX_ERROR, ErrorItem.OTHER, ExceptionUtils.getMessage(ex), fieldName, ex.getLineNumber(), ex); + return new TemplateError(ErrorType.FATAL, ErrorReason.SYNTAX_ERROR, ErrorItem.OTHER, ExceptionUtils.getMessage(ex), fieldName, ex.getLineNumber(), ex.getStartPosition(), ex); } public static TemplateError fromException(Exception ex) { int lineNumber = -1; + int startPosition = -1; if (ex instanceof InterpretException) { lineNumber = ((InterpretException) ex).getLineNumber(); + startPosition = ((InterpretException) ex).getStartPosition(); } - return new TemplateError(ErrorType.FATAL, ErrorReason.EXCEPTION, ErrorItem.OTHER, ExceptionUtils.getMessage(ex), null, lineNumber, ex, BasicTemplateErrorCategory.UNKNOWN, ImmutableMap.of()); + return new TemplateError(ErrorType.FATAL, ErrorReason.EXCEPTION, ErrorItem.OTHER, ExceptionUtils.getMessage(ex), null, lineNumber, startPosition, ex, BasicTemplateErrorCategory.UNKNOWN, ImmutableMap.of()); + } + + public static TemplateError fromException(Exception ex, int lineNumber, int startPosition) { + return new TemplateError(ErrorType.FATAL, ErrorReason.EXCEPTION, ErrorItem.OTHER, ExceptionUtils.getMessage(ex), null, lineNumber, startPosition, ex); } public static TemplateError fromException(Exception ex, int lineNumber) { - return new TemplateError(ErrorType.FATAL, ErrorReason.EXCEPTION, ErrorItem.OTHER, ExceptionUtils.getMessage(ex), null, lineNumber, ex); + return new TemplateError(ErrorType.FATAL, ErrorReason.EXCEPTION, ErrorItem.OTHER, ExceptionUtils.getMessage(ex), null, lineNumber, -1, ex); } public static TemplateError fromUnknownProperty(Object base, String variable, int lineNumber) { + return fromUnknownProperty(base, variable, lineNumber, -1); + } + + public static TemplateError fromUnknownProperty(Object base, String variable, int lineNumber, int startPosition) { return new TemplateError(ErrorType.WARNING, ErrorReason.UNKNOWN, ErrorItem.PROPERTY, String.format("Cannot resolve property '%s' in '%s'", variable, friendlyObjectToString(base)), - variable, lineNumber, null, BasicTemplateErrorCategory.UNKNOWN_PROPERTY, - ImmutableMap.of("property", variable, "lineNumber", String.valueOf(lineNumber))); + variable, lineNumber, startPosition,null, BasicTemplateErrorCategory.UNKNOWN_PROPERTY, + ImmutableMap.of("property", variable, "lineNumber", String.valueOf(lineNumber), "startPosition", String.valueOf(startPosition))); } private static String friendlyObjectToString(Object o) { @@ -107,6 +118,27 @@ public TemplateError(ErrorType severity, this.message = message; this.fieldName = fieldName; this.lineno = lineno; + this.startPosition = -1; + this.exception = exception; + this.category = BasicTemplateErrorCategory.UNKNOWN; + this.categoryErrors = null; + } + + public TemplateError(ErrorType severity, + ErrorReason reason, + ErrorItem item, + String message, + String fieldName, + int lineno, + int startPosition, + Exception exception) { + this.severity = severity; + this.reason = reason; + this.item = item; + this.message = message; + this.fieldName = fieldName; + this.lineno = lineno; + this.startPosition = startPosition; this.exception = exception; this.category = BasicTemplateErrorCategory.UNKNOWN; this.categoryErrors = null; @@ -127,6 +159,7 @@ public TemplateError(ErrorType severity, this.message = message; this.fieldName = fieldName; this.lineno = lineno; + this.startPosition = -1; this.exception = exception; this.category = category; this.categoryErrors = categoryErrors; @@ -134,9 +167,32 @@ public TemplateError(ErrorType severity, public TemplateError(ErrorType severity, ErrorReason reason, + ErrorItem item, String message, String fieldName, int lineno, + int startPosition, + Exception exception, + TemplateErrorCategory category, + Map categoryErrors) { + this.severity = severity; + this.reason = reason; + this.item = item; + this.message = message; + this.fieldName = fieldName; + this.lineno = lineno; + this.startPosition = startPosition; + this.exception = exception; + this.category = category; + this.categoryErrors = categoryErrors; + } + + public TemplateError(ErrorType severity, + ErrorReason reason, + String message, + String fieldName, + int lineno, + int startPosition, Exception exception) { this.severity = severity; this.reason = reason; @@ -144,6 +200,7 @@ public TemplateError(ErrorType severity, this.message = message; this.fieldName = fieldName; this.lineno = lineno; + this.startPosition = startPosition; this.exception = exception; this.category = BasicTemplateErrorCategory.UNKNOWN; this.categoryErrors = null; @@ -173,6 +230,10 @@ public int getLineno() { return lineno; } + public int getStartPosition() { + return startPosition; + } + public Exception getException() { return exception; } @@ -186,7 +247,7 @@ public Map getCategoryErrors() { } public TemplateError serializable() { - return new TemplateError(severity, reason, item, message, fieldName, lineno, null, category, categoryErrors); + return new TemplateError(severity, reason, item, message, fieldName, lineno, startPosition, null, category, categoryErrors); } @Override @@ -198,9 +259,9 @@ public String toString() { ", message='" + message + '\'' + ", fieldName='" + fieldName + '\'' + ", lineno=" + lineno + + ", startPosition=" + startPosition + ", category=" + category + ", categoryErrors=" + categoryErrors + '}'; } - } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java index ed21f3720..f3ad019f8 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java @@ -80,7 +80,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { interpreter.getContext().getImportPathStack().push(path, tagNode.getLineNumber(), tagNode.getStartPosition()); } catch (ImportTagCycleException e) { interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.EXCEPTION, ErrorItem.TAG, - "Import cycle detected for path: '" + path + "'", null, tagNode.getLineNumber(), + "Import cycle detected for path: '" + path + "'", null, tagNode.getLineNumber(), tagNode.getStartPosition(), e, BasicTemplateErrorCategory.IMPORT_CYCLE_DETECTED, ImmutableMap.of("path", path))); return ""; } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java index 94dd27980..8158f772b 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java @@ -63,7 +63,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { interpreter.getContext().getIncludePathStack().push(templateFile, tagNode.getLineNumber(), tagNode.getStartPosition()); } catch (IncludeTagCycleException e) { interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.EXCEPTION, ErrorItem.TAG, - "Include cycle detected for path: '" + templateFile + "'", null, tagNode.getLineNumber(), e, + "Include cycle detected for path: '" + templateFile + "'", null, tagNode.getLineNumber(), tagNode.getStartPosition(), e, BasicTemplateErrorCategory.INCLUDE_CYCLE_DETECTED, ImmutableMap.of("path", templateFile))); return ""; } diff --git a/src/main/java/com/hubspot/jinjava/tree/TreeParser.java b/src/main/java/com/hubspot/jinjava/tree/TreeParser.java index 66278eaae..f861e6209 100644 --- a/src/main/java/com/hubspot/jinjava/tree/TreeParser.java +++ b/src/main/java/com/hubspot/jinjava/tree/TreeParser.java @@ -156,7 +156,7 @@ private Node tag(TagToken tagToken) { } } catch (DisabledException e) { interpreter.addError(new TemplateError(ErrorType.FATAL, ErrorReason.DISABLED, ErrorItem.TAG, - e.getMessage(), tagToken.getTagName(), interpreter.getLineNumber(), e)); + e.getMessage(), tagToken.getTagName(), interpreter.getLineNumber(), tagToken.getStartPosition(), e)); return null; } diff --git a/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java b/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java index c0718dccc..010faf57f 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java @@ -10,13 +10,13 @@ public class TemplateErrorTest { @Test public void itShowsFriendlyNameOfBaseObjectForPropNotFound() { - TemplateError e = TemplateError.fromUnknownProperty(new Object(), "foo", 123); + TemplateError e = TemplateError.fromUnknownProperty(new Object(), "foo", 123, 4); assertThat(e.getMessage()).isEqualTo("Cannot resolve property 'foo' in 'Object'"); } @Test public void itUsesOverloadedToStringForBaseObject() { - TemplateError e = TemplateError.fromUnknownProperty(ImmutableMap.of("foo", "bar"), "other", 123); + TemplateError e = TemplateError.fromUnknownProperty(ImmutableMap.of("foo", "bar"), "other", 123, 4); assertThat(e.getMessage()).isEqualTo("Cannot resolve property 'other' in '{foo=bar}'"); } From 82fba8b6f5df4cfc294c1f751e449e68bb3eb827 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 30 Oct 2017 17:39:51 -0400 Subject: [PATCH 0362/2465] Update CHANGES.md --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 99967d3c2..ad6107f0c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Jinjava Releases # +### 2017-10-30 Version 2.3.0 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.3.0%22)) ### + +* Add column numbers to error messages + ### 2017-10-24 Version 2.2.10 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.2.10%22)) ### * Use code of bad syntax as field name for `TemplateSyntaxException`s From dd04299ae1d0b696c8b5b8f99a3b43584ee7c80b Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 30 Oct 2017 17:44:19 -0400 Subject: [PATCH 0363/2465] COS -> CMS --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 661be790b..2208a4a17 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ jinjava -Java-based template engine based on django template syntax, adapted to render jinja templates (at least the subset of jinja in use in HubSpot content). Currently used in production to render thousands of websites with hundreds of millions of page views per month on the [HubSpot COS](http://www.hubspot.com/products/sites). +Java-based template engine based on django template syntax, adapted to render jinja templates (at least the subset of jinja in use in HubSpot content). Currently used in production to render thousands of websites with hundreds of millions of page views per month on the [HubSpot CMS](http://www.hubspot.com/products/sites). *Note*: Requires Java >= 8. Originally forked from [jangod](https://code.google.com/p/jangod/). From 93cb9e9a8930f2ca372a575963bc7a51eaef1788 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 30 Oct 2017 17:47:46 -0400 Subject: [PATCH 0364/2465] Update pom.xml --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c4276491c..0d604d49b 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.2.11-SNAPSHOT + 2.3.0-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava From 2e4a6ea439ac6ea8f92fe4b27bdd0cba96f54d50 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 30 Oct 2017 18:17:12 -0400 Subject: [PATCH 0365/2465] next version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0d604d49b..2a8098215 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.3.0-SNAPSHOT + 2.3.1-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava From f100bbd703fa3e9fc67d8076e881de495d774585 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 10 Nov 2017 17:38:22 -0500 Subject: [PATCH 0366/2465] add scope level to errors --- .../jinjava/interpret/JinjavaInterpreter.java | 5 ++- .../jinjava/interpret/TemplateError.java | 37 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index 458bc3875..0c3fd6bca 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -67,6 +67,7 @@ public class JinjavaInterpreter { private final Random random; private int lineNumber = -1; + private short scopeLevel = 1; private final List errors = new LinkedList<>(); public JinjavaInterpreter(Jinjava application, Context context, JinjavaConfig renderConfig) { @@ -124,11 +125,13 @@ public InterpreterScopeClosable enterScope() { public InterpreterScopeClosable enterScope(Map> disabled) { context = new Context(context, null, disabled); + scopeLevel++; return new InterpreterScopeClosable(); } public void leaveScope() { Context parent = context.getParent(); + scopeLevel--; if (parent != null) { parent.addDependencies(context.getDependencies()); context = parent; @@ -424,7 +427,7 @@ public int getLineNumber() { } public void addError(TemplateError templateError) { - this.errors.add(templateError); + this.errors.add(templateError.withScopeLevel(scopeLevel)); } public List getErrors() { diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java index d3f9a117c..f78dbfdf5 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java @@ -46,8 +46,14 @@ public enum ErrorItem { private final TemplateErrorCategory category; private final Map categoryErrors; + private short scopeLevel = 1; + private final Exception exception; + public TemplateError withScopeLevel(short scopeLevel) { + return new TemplateError(getSeverity(), getReason(), getItem(), getMessage(), getFieldName(), getLineno(), getStartPosition(), getCategory(), getCategoryErrors(), scopeLevel, getException()); + } + public static TemplateError fromSyntaxError(InterpretException ex) { return new TemplateError(ErrorType.FATAL, ErrorReason.SYNTAX_ERROR, ErrorItem.OTHER, ExceptionUtils.getMessage(ex), null, ex.getLineNumber(), ex.getStartPosition(), ex); } @@ -144,6 +150,32 @@ public TemplateError(ErrorType severity, this.categoryErrors = null; } + + public TemplateError(ErrorType severity, + ErrorReason reason, + ErrorItem item, + String message, + String fieldName, + int lineno, + int startPosition, + TemplateErrorCategory category, + Map categoryErrors, + short scopeLevel, + Exception exception) { + this.severity = severity; + this.reason = reason; + this.item = item; + this.message = message; + this.fieldName = fieldName; + this.lineno = lineno; + this.startPosition = startPosition; + this.exception = exception; + this.category = category; + this.categoryErrors = categoryErrors; + this.scopeLevel = scopeLevel; + } + + public TemplateError(ErrorType severity, ErrorReason reason, ErrorItem item, @@ -246,6 +278,10 @@ public Map getCategoryErrors() { return categoryErrors; } + public short getScopeLevel() { + return scopeLevel; + } + public TemplateError serializable() { return new TemplateError(severity, reason, item, message, fieldName, lineno, startPosition, null, category, categoryErrors); } @@ -260,6 +296,7 @@ public String toString() { ", fieldName='" + fieldName + '\'' + ", lineno=" + lineno + ", startPosition=" + startPosition + + ", scopeLevel=" + scopeLevel + ", category=" + category + ", categoryErrors=" + categoryErrors + '}'; From 3b5644bbfe4db58c1b536167ed3587de51449c58 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 10 Nov 2017 17:47:41 -0500 Subject: [PATCH 0367/2465] increase scope level with child interpreters too --- .../java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index 0c3fd6bca..5617f589a 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -88,6 +88,7 @@ public JinjavaInterpreter(Jinjava application, Context context, JinjavaConfig re public JinjavaInterpreter(JinjavaInterpreter orig) { this(orig.application, new Context(orig.context), orig.config); + scopeLevel++; } /** From e2a50674990a397045932d2421f6417ee2251357 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 10 Nov 2017 17:59:08 -0500 Subject: [PATCH 0368/2465] use int for scopeLevel and increment from parent interpreter --- .../com/hubspot/jinjava/interpret/JinjavaInterpreter.java | 8 ++++++-- .../java/com/hubspot/jinjava/interpret/TemplateError.java | 8 ++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index 5617f589a..e1a6e2e49 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -67,7 +67,7 @@ public class JinjavaInterpreter { private final Random random; private int lineNumber = -1; - private short scopeLevel = 1; + private int scopeLevel = 1; private final List errors = new LinkedList<>(); public JinjavaInterpreter(Jinjava application, Context context, JinjavaConfig renderConfig) { @@ -88,7 +88,7 @@ public JinjavaInterpreter(Jinjava application, Context context, JinjavaConfig re public JinjavaInterpreter(JinjavaInterpreter orig) { this(orig.application, new Context(orig.context), orig.config); - scopeLevel++; + scopeLevel = orig.getScopeLevel() + 1; } /** @@ -431,6 +431,10 @@ public void addError(TemplateError templateError) { this.errors.add(templateError.withScopeLevel(scopeLevel)); } + public int getScopeLevel() { + return scopeLevel; + } + public List getErrors() { return errors; } diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java index f78dbfdf5..acd526e90 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java @@ -46,11 +46,11 @@ public enum ErrorItem { private final TemplateErrorCategory category; private final Map categoryErrors; - private short scopeLevel = 1; + private int scopeLevel = 1; private final Exception exception; - public TemplateError withScopeLevel(short scopeLevel) { + public TemplateError withScopeLevel(int scopeLevel) { return new TemplateError(getSeverity(), getReason(), getItem(), getMessage(), getFieldName(), getLineno(), getStartPosition(), getCategory(), getCategoryErrors(), scopeLevel, getException()); } @@ -160,7 +160,7 @@ public TemplateError(ErrorType severity, int startPosition, TemplateErrorCategory category, Map categoryErrors, - short scopeLevel, + int scopeLevel, Exception exception) { this.severity = severity; this.reason = reason; @@ -278,7 +278,7 @@ public Map getCategoryErrors() { return categoryErrors; } - public short getScopeLevel() { + public int getScopeLevel() { return scopeLevel; } From 7c0920b5ff2ee705625b95c62733e0383e47d1bd Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 10 Nov 2017 18:10:21 -0500 Subject: [PATCH 0369/2465] include scopelevel in serialized template error --- src/main/java/com/hubspot/jinjava/interpret/TemplateError.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java index acd526e90..65ddf9d25 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java @@ -283,7 +283,7 @@ public int getScopeLevel() { } public TemplateError serializable() { - return new TemplateError(severity, reason, item, message, fieldName, lineno, startPosition, null, category, categoryErrors); + return new TemplateError(severity, reason, item, message, fieldName, lineno, startPosition, category, categoryErrors, scopeLevel, null); } @Override From 4114e58f96de6b6db41050baa28f91acc47058ad Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 10 Nov 2017 18:30:40 -0500 Subject: [PATCH 0370/2465] rearrange params a bit --- .../java/com/hubspot/jinjava/interpret/TemplateError.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java index 65ddf9d25..66d2a7295 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java @@ -51,7 +51,7 @@ public enum ErrorItem { private final Exception exception; public TemplateError withScopeLevel(int scopeLevel) { - return new TemplateError(getSeverity(), getReason(), getItem(), getMessage(), getFieldName(), getLineno(), getStartPosition(), getCategory(), getCategoryErrors(), scopeLevel, getException()); + return new TemplateError(getSeverity(), getReason(), getItem(), getMessage(), getFieldName(), getLineno(), getStartPosition(), getException(), getCategory(), getCategoryErrors(), scopeLevel); } public static TemplateError fromSyntaxError(InterpretException ex) { @@ -158,10 +158,10 @@ public TemplateError(ErrorType severity, String fieldName, int lineno, int startPosition, + Exception exception, TemplateErrorCategory category, Map categoryErrors, - int scopeLevel, - Exception exception) { + int scopeLevel) { this.severity = severity; this.reason = reason; this.item = item; @@ -283,7 +283,7 @@ public int getScopeLevel() { } public TemplateError serializable() { - return new TemplateError(severity, reason, item, message, fieldName, lineno, startPosition, category, categoryErrors, scopeLevel, null); + return new TemplateError(severity, reason, item, message, fieldName, lineno, startPosition, null, category, categoryErrors, scopeLevel); } @Override From 64641cefcc2715d7d3c8c4e4faddf1ea9f53486f Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 10 Nov 2017 20:55:04 -0500 Subject: [PATCH 0371/2465] rename to scopeDepth --- .../jinjava/interpret/JinjavaInterpreter.java | 14 +++++++------- .../jinjava/interpret/TemplateError.java | 18 +++++++++--------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index e1a6e2e49..1494726e8 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -67,7 +67,7 @@ public class JinjavaInterpreter { private final Random random; private int lineNumber = -1; - private int scopeLevel = 1; + private int scopeDepth = 1; private final List errors = new LinkedList<>(); public JinjavaInterpreter(Jinjava application, Context context, JinjavaConfig renderConfig) { @@ -88,7 +88,7 @@ public JinjavaInterpreter(Jinjava application, Context context, JinjavaConfig re public JinjavaInterpreter(JinjavaInterpreter orig) { this(orig.application, new Context(orig.context), orig.config); - scopeLevel = orig.getScopeLevel() + 1; + scopeDepth = orig.getScopeDepth() + 1; } /** @@ -126,13 +126,13 @@ public InterpreterScopeClosable enterScope() { public InterpreterScopeClosable enterScope(Map> disabled) { context = new Context(context, null, disabled); - scopeLevel++; + scopeDepth++; return new InterpreterScopeClosable(); } public void leaveScope() { Context parent = context.getParent(); - scopeLevel--; + scopeDepth--; if (parent != null) { parent.addDependencies(context.getDependencies()); context = parent; @@ -428,11 +428,11 @@ public int getLineNumber() { } public void addError(TemplateError templateError) { - this.errors.add(templateError.withScopeLevel(scopeLevel)); + this.errors.add(templateError.withScopeDepth(scopeDepth)); } - public int getScopeLevel() { - return scopeLevel; + public int getScopeDepth() { + return scopeDepth; } public List getErrors() { diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java index 66d2a7295..7ae9482c9 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java @@ -46,12 +46,12 @@ public enum ErrorItem { private final TemplateErrorCategory category; private final Map categoryErrors; - private int scopeLevel = 1; + private int scopeDepth = 1; private final Exception exception; - public TemplateError withScopeLevel(int scopeLevel) { - return new TemplateError(getSeverity(), getReason(), getItem(), getMessage(), getFieldName(), getLineno(), getStartPosition(), getException(), getCategory(), getCategoryErrors(), scopeLevel); + public TemplateError withScopeDepth(int scopeDepth) { + return new TemplateError(getSeverity(), getReason(), getItem(), getMessage(), getFieldName(), getLineno(), getStartPosition(), getException(), getCategory(), getCategoryErrors(), scopeDepth); } public static TemplateError fromSyntaxError(InterpretException ex) { @@ -161,7 +161,7 @@ public TemplateError(ErrorType severity, Exception exception, TemplateErrorCategory category, Map categoryErrors, - int scopeLevel) { + int scopeDepth) { this.severity = severity; this.reason = reason; this.item = item; @@ -172,7 +172,7 @@ public TemplateError(ErrorType severity, this.exception = exception; this.category = category; this.categoryErrors = categoryErrors; - this.scopeLevel = scopeLevel; + this.scopeDepth = scopeDepth; } @@ -278,12 +278,12 @@ public Map getCategoryErrors() { return categoryErrors; } - public int getScopeLevel() { - return scopeLevel; + public int getScopeDepth() { + return scopeDepth; } public TemplateError serializable() { - return new TemplateError(severity, reason, item, message, fieldName, lineno, startPosition, null, category, categoryErrors, scopeLevel); + return new TemplateError(severity, reason, item, message, fieldName, lineno, startPosition, null, category, categoryErrors, scopeDepth); } @Override @@ -296,7 +296,7 @@ public String toString() { ", fieldName='" + fieldName + '\'' + ", lineno=" + lineno + ", startPosition=" + startPosition + - ", scopeLevel=" + scopeLevel + + ", scopeDepth=" + scopeDepth + ", category=" + category + ", categoryErrors=" + categoryErrors + '}'; From 79a2b8a45eb54e1a2e5537d1f00aca41ba5de31c Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 10 Nov 2017 21:30:43 -0500 Subject: [PATCH 0372/2465] support expression tests for select filter that use arguments --- .../jinjava/lib/exptest/IsEqualToExpTest.java | 3 +-- .../hubspot/jinjava/lib/filter/SelectFilter.java | 16 ++++++++++++---- .../jinjava/lib/filter/SelectFilterTest.java | 11 ++++++++--- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/exptest/IsEqualToExpTest.java b/src/main/java/com/hubspot/jinjava/lib/exptest/IsEqualToExpTest.java index 25a3079f1..648b8d274 100644 --- a/src/main/java/com/hubspot/jinjava/lib/exptest/IsEqualToExpTest.java +++ b/src/main/java/com/hubspot/jinjava/lib/exptest/IsEqualToExpTest.java @@ -30,8 +30,7 @@ public String getName() { } @Override - public boolean evaluate(Object var, JinjavaInterpreter interpreter, - Object... args) { + public boolean evaluate(Object var, JinjavaInterpreter interpreter, Object... args) { if (args.length == 0) { throw new InterpretException(getName() + " test requires 1 argument"); } diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/SelectFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/SelectFilter.java index 8c5597071..fe4cec106 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/SelectFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/SelectFilter.java @@ -1,7 +1,9 @@ package com.hubspot.jinjava.lib.filter; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Map; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaParam; @@ -23,7 +25,7 @@ code = "{% set some_numbers = [10, 12, 13, 3, 5, 17, 22] %}\n" + "{% some_numbers|select('even') %}") }) -public class SelectFilter implements Filter { +public class SelectFilter implements AdvancedFilter { @Override public String getName() { @@ -31,14 +33,20 @@ public String getName() { } @Override - public Object filter(Object var, JinjavaInterpreter interpreter, String... args) { + public Object filter(Object var, JinjavaInterpreter interpreter, Object[] args, Map kwargs) { List result = new ArrayList<>(); if (args.length == 0) { throw new InterpretException(getName() + " requires an exp test to filter on", interpreter.getLineNumber()); } - ExpTest expTest = interpreter.getContext().getExpTest(args[0]); + Object[] expArgs = new Object[]{}; + + if (args.length > 1) { + expArgs = Arrays.copyOfRange(args, 1, args.length); + } + + ExpTest expTest = interpreter.getContext().getExpTest(args[0].toString()); if (expTest == null) { throw new InterpretException("No exp test defined for name '" + args[0] + "'", interpreter.getLineNumber()); } @@ -47,7 +55,7 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args) while (loop.hasNext()) { Object val = loop.next(); - if (expTest.evaluate(val, interpreter)) { + if (expTest.evaluate(val, interpreter, expArgs)) { result.add(val); } } diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/SelectFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/SelectFilterTest.java index e5f4d3e4c..1470bfd0b 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/SelectFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/SelectFilterTest.java @@ -17,13 +17,18 @@ public class SelectFilterTest { @Before public void setup() { jinjava = new Jinjava(); - jinjava.getGlobalContext().put("numbers", Lists.newArrayList(1, 2, 3, 4, 5)); + jinjava.getGlobalContext().put("numbers", Lists.newArrayList(1L, 2L, 3L, 4L, 5L)); } @Test public void testSelect() { - assertThat(jinjava.render("{{numbers|select('odd')}}", new HashMap())).isEqualTo("[1, 3, 5]"); - assertThat(jinjava.render("{{numbers|select('even')}}", new HashMap())).isEqualTo("[2, 4]"); + assertThat(jinjava.render("{{numbers|select('odd')}}", new HashMap<>())).isEqualTo("[1, 3, 5]"); + assertThat(jinjava.render("{{numbers|select('even')}}", new HashMap<>())).isEqualTo("[2, 4]"); + } + + @Test + public void testSelectWithEqualToAttr() { + assertThat(jinjava.render("{{numbers|select('equalto', 3)}}", new HashMap<>())).isEqualTo("[3]"); } } From 9f6908ebd081c14eb82a5bad20301021b5b1ec70 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 14 Nov 2017 10:45:47 -0500 Subject: [PATCH 0373/2465] Update CHANGES.md --- CHANGES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index ad6107f0c..837c5883f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,10 @@ # Jinjava Releases # +### 2017-11-14 Version 2.3.1 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.3.1%22)) ### + +* [select filter now supports expression tests with arguments like 'equalto'](https://github.com/HubSpot/jinjava/pull/158) +* [`TemplateError`s now include a scope depth](https://github.com/HubSpot/jinjava/pull/157) + ### 2017-10-30 Version 2.3.0 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.3.0%22)) ### * Add column numbers to error messages From 6bb29bbdbe0d0fa2285212d720aab5c521615dba Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 14 Nov 2017 11:38:21 -0500 Subject: [PATCH 0374/2465] version bump --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2a8098215..bc9436899 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.3.1-SNAPSHOT + 2.3.2-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava From 6e7b4fe2eda56c17dd86e66c08e3396a31772a21 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Tue, 14 Nov 2017 16:42:09 +0000 Subject: [PATCH 0375/2465] [maven-release-plugin] prepare release jinjava-2.3.2 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index bc9436899..338c6b998 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.3.2-SNAPSHOT + 2.3.2 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.3.2 From 15a168b77c3d7f5de2393276743221d747f53186 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Tue, 14 Nov 2017 16:42:09 +0000 Subject: [PATCH 0376/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 338c6b998..85f1c96c3 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.3.2 + 2.3.3-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.3.2 + HEAD From 232d46b6e6888eb5c7a3d7b5a20cce97eb969070 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 14 Nov 2017 11:53:36 -0500 Subject: [PATCH 0377/2465] Update CHANGES.md --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 837c5883f..3bb71530e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # Jinjava Releases # -### 2017-11-14 Version 2.3.1 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.3.1%22)) ### +### 2017-11-14 Version 2.3.2 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.3.2%22)) ### * [select filter now supports expression tests with arguments like 'equalto'](https://github.com/HubSpot/jinjava/pull/158) * [`TemplateError`s now include a scope depth](https://github.com/HubSpot/jinjava/pull/157) From 81d6adf4c04d610dc13643a2e932fbfb5138655d Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Thu, 16 Nov 2017 17:08:19 -0500 Subject: [PATCH 0378/2465] render nested code --- .../java/com/hubspot/jinjava/tree/ExpressionNode.java | 3 ++- .../com/hubspot/jinjava/tree/ExpressionNodeTest.java | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java index a2e62f212..e3abd2670 100644 --- a/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java +++ b/src/main/java/com/hubspot/jinjava/tree/ExpressionNode.java @@ -44,7 +44,8 @@ public OutputNode render(JinjavaInterpreter interpreter) { String result = Objects.toString(var, ""); if (interpreter.getConfig().isNestedInterpretationEnabled()) { - if (!StringUtils.equals(result, master.getImage()) && StringUtils.contains(result, "{{")) { + if (!StringUtils.equals(result, master.getImage()) && + (StringUtils.contains(result, "{{") || StringUtils.contains(result, "{%"))) { try { result = interpreter.renderFlat(result); } catch (Exception e) { diff --git a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java index 5a4643862..4d94ba6a8 100644 --- a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java @@ -60,6 +60,17 @@ public void itRendersWithNestedExpressionInterpretationByDefault() throws Except assertThat(node.render(noNestedInterpreter).toString()).isEqualTo("hello world"); } + @Test + public void itRendersNestedTags() throws Exception { + final JinjavaConfig config = JinjavaConfig.newBuilder().build(); + JinjavaInterpreter jinjava = new Jinjava(config).newInterpreter(); + Context context = jinjava.getContext(); + context.put("myvar", "hello {% if (true) %}nasty{% endif %}"); + + ExpressionNode node = fixture("simplevar"); + assertThat(node.render(jinjava).toString()).isEqualTo("hello nasty"); + } + @Test public void itAvoidsInfiniteRecursionWhenVarsContainBraceBlocks() throws Exception { context.put("myvar", "hello {{ place }}"); From dfc6878e77dfb51d1d21c6ca1666f8962ecf075e Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Thu, 16 Nov 2017 22:10:44 -0500 Subject: [PATCH 0379/2465] Update CHANGES.md --- CHANGES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 3bb71530e..263730b66 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,10 @@ # Jinjava Releases # +### 2017-11-14 Version 2.3.3 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.3.3%22)) ### + +* [always evaluate tags and control structures in nested expressions](https://github.com/HubSpot/jinjava/pull/161) + + ### 2017-11-14 Version 2.3.2 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.3.2%22)) ### * [select filter now supports expression tests with arguments like 'equalto'](https://github.com/HubSpot/jinjava/pull/158) From ed0e1566adb26ffdc3898b0b2aae1a4b8de1c7a0 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Thu, 16 Nov 2017 22:10:56 -0500 Subject: [PATCH 0380/2465] Update CHANGES.md --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 263730b66..6f27517fd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # Jinjava Releases # -### 2017-11-14 Version 2.3.3 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.3.3%22)) ### +### 2017-11-16 Version 2.3.3 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.3.3%22)) ### * [always evaluate tags and control structures in nested expressions](https://github.com/HubSpot/jinjava/pull/161) From a7a0a0b353617e6d16f868335b19886d9b5e2f2d Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Fri, 17 Nov 2017 03:14:43 +0000 Subject: [PATCH 0381/2465] [maven-release-plugin] prepare release jinjava-2.3.3 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 85f1c96c3..1a37298d5 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.3.3-SNAPSHOT + 2.3.3 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.3.3 From 33effd6aa9b8f773a917a5ed6bba2fc8e168db1d Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Fri, 17 Nov 2017 03:14:43 +0000 Subject: [PATCH 0382/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1a37298d5..2189ef65e 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.3.3 + 2.3.4-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.3.3 + HEAD From f6aa65606a757d7fb5d628ef0100b87d67df97d7 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Wed, 29 Nov 2017 15:59:47 -0500 Subject: [PATCH 0383/2465] Preserve order of objects in groupby filter. --- .../java/com/hubspot/jinjava/lib/filter/GroupByFilter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/GroupByFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/GroupByFilter.java index f0860fdd5..5c13ebdee 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/GroupByFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/GroupByFilter.java @@ -4,7 +4,7 @@ import java.util.List; import java.util.Objects; -import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.LinkedListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; @@ -49,7 +49,7 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args) String attr = args[0]; ForLoop loop = ObjectIterator.getLoop(var); - Multimap groupBuckets = ArrayListMultimap.create(); + Multimap groupBuckets = LinkedListMultimap.create(); while (loop.hasNext()) { Object val = loop.next(); From 2f3522de1a1c6956ac108dacde76be696e7ee7e7 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Wed, 29 Nov 2017 16:33:43 -0500 Subject: [PATCH 0384/2465] Add test. --- .../java/com/hubspot/jinjava/lib/filter/GroupByFilterTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/GroupByFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/GroupByFilterTest.java index d6e9dc62d..5dc3becfe 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/GroupByFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/GroupByFilterTest.java @@ -36,7 +36,9 @@ public void testGroupByAttr() throws Exception { new Person("female", "barb", "smith") )))); + String test = dom.select("ul.root > li").get(0).text(); assertThat(dom.select("ul.root > li")).hasSize(2); + assertThat(dom.select("ul.root > li").get(0).text()).contains("male jared"); assertThat(dom.select("ul.root > li.male > ul > li")).hasSize(3); assertThat(dom.select("ul.root > li.female > ul > li")).hasSize(2); } From 6193287a3336a37aa448da5da46596fecf293d43 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Wed, 29 Nov 2017 16:41:15 -0500 Subject: [PATCH 0385/2465] Small fix. --- .../java/com/hubspot/jinjava/lib/filter/GroupByFilterTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/GroupByFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/GroupByFilterTest.java index 5dc3becfe..e8d41e631 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/GroupByFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/GroupByFilterTest.java @@ -35,8 +35,7 @@ public void testGroupByAttr() throws Exception { new Person("male", "jim", "jones"), new Person("female", "barb", "smith") )))); - - String test = dom.select("ul.root > li").get(0).text(); + assertThat(dom.select("ul.root > li")).hasSize(2); assertThat(dom.select("ul.root > li").get(0).text()).contains("male jared"); assertThat(dom.select("ul.root > li.male > ul > li")).hasSize(3); From 07550e0523162a540acaa9f82b6d79eb7ab744c3 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Thu, 30 Nov 2017 10:09:44 -0500 Subject: [PATCH 0386/2465] Update CHANGES.md --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 6f27517fd..719fc20ec 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Jinjava Releases # +### 2017-11-30 Version 2.3.4 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.3.4%22)) ### + +* [Preserve groupby order of elements](https://github.com/HubSpot/jinjava/pull/163) + ### 2017-11-16 Version 2.3.3 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.3.3%22)) ### * [always evaluate tags and control structures in nested expressions](https://github.com/HubSpot/jinjava/pull/161) From 684b370516f7d50bf68b0fe7e12e9c202e58ffbe Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Thu, 30 Nov 2017 15:30:15 +0000 Subject: [PATCH 0387/2465] [maven-release-plugin] prepare release jinjava-2.3.4 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 2189ef65e..7a4d00dce 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.3.4-SNAPSHOT + 2.3.4 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.3.4 From c4c160a6b293c644137765c001782c5813826835 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Thu, 30 Nov 2017 15:30:15 +0000 Subject: [PATCH 0388/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7a4d00dce..1133fc8f1 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.3.4 + 2.3.5-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.3.4 + HEAD From 7edfd32d10b8778c2e14b6522f9ab67ca9982709 Mon Sep 17 00:00:00 2001 From: Nate Belisle Date: Mon, 11 Dec 2017 17:04:24 -0500 Subject: [PATCH 0389/2465] Add new EscapeJinjavaFilter --- .../lib/filter/EscapeJinjavaFilter.java | 69 +++++++++++++++++++ .../jinjava/lib/filter/FilterLibrary.java | 1 + .../lib/filter/EscapeJinjavaFilterTest.java | 28 ++++++++ 3 files changed, 98 insertions(+) create mode 100644 src/main/java/com/hubspot/jinjava/lib/filter/EscapeJinjavaFilter.java create mode 100644 src/test/java/com/hubspot/jinjava/lib/filter/EscapeJinjavaFilterTest.java diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/EscapeJinjavaFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/EscapeJinjavaFilter.java new file mode 100644 index 000000000..cceceedea --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/lib/filter/EscapeJinjavaFilter.java @@ -0,0 +1,69 @@ + +/********************************************************************** + * Copyright (c) 2017 HubSpot 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.hubspot.jinjava.lib.filter; + +import java.util.Objects; + +import org.apache.commons.lang3.StringUtils; + +import com.hubspot.jinjava.doc.annotations.JinjavaDoc; +import com.hubspot.jinjava.doc.annotations.JinjavaParam; +import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; + +@JinjavaDoc( + value = "Converts the characters { and } in string s to Jinjava-safe sequences. " + + "Use this filter if you need to display text that might contain such characters in Jinjava. " + + "Marks return value as markup string.", + params = { + @JinjavaParam(value = "s", desc = "String to escape") + }, + snippets = { + @JinjavaSnippet( + code = "{% set escape_string = \"{{This markup is printed as text}}\" %}\n" + + "{{ escape_string|escapeJinjava }}") + }) + +public class EscapeJinjavaFilter implements Filter { + + private static final String SLBRACE = "{"; + private static final String BLBRACE = "{"; + private static final String SRBRACE = "}"; + private static final String BRBRACE = "}"; + + private static final String[] TO_REPLACE = new String[] { + SLBRACE, SRBRACE + }; + private static final String[] REPLACE_WITH = new String[] { + BLBRACE, BRBRACE + }; + + public static String escapeJinjavaEntities(String input) { + return StringUtils.replaceEach(input, TO_REPLACE, REPLACE_WITH); + } + + @Override + public Object filter(Object object, JinjavaInterpreter interpreter, String... arg) { + return escapeJinjavaEntities(Objects.toString(object, "")); + } + + @Override + public String getName() { + return "escapeJinjava"; + } + +} diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java b/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java index 9ca2c7567..543fac729 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java @@ -83,6 +83,7 @@ protected void registerDefaults() { UrlEncodeFilter.class, XmlAttrFilter.class, EscapeJsonFilter.class, + EscapeJinjavaFilter.class, CapitalizeFilter.class, CenterFilter.class, diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/EscapeJinjavaFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/EscapeJinjavaFilterTest.java new file mode 100644 index 000000000..7a6add00a --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/lib/filter/EscapeJinjavaFilterTest.java @@ -0,0 +1,28 @@ +package com.hubspot.jinjava.lib.filter; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +import org.junit.Before; +import org.junit.Test; + +import com.hubspot.jinjava.interpret.JinjavaInterpreter; + +public class EscapeJinjavaFilterTest { + + JinjavaInterpreter interpreter; + EscapeJinjavaFilter f; + + @Before + public void setup() { + interpreter = mock(JinjavaInterpreter.class); + f = new EscapeJinjavaFilter(); + } + + @Test + public void testEscape() { + assertThat(f.filter("", interpreter)).isEqualTo(""); + assertThat(f.filter("{{ me & you }}", interpreter)).isEqualTo("{{ me & you }}"); + } + +} From 6ae42daaf209d4f9e4c7e91951783beccdbc210b Mon Sep 17 00:00:00 2001 From: Nate Belisle Date: Tue, 12 Dec 2017 10:58:53 -0500 Subject: [PATCH 0390/2465] use snake case for filtername --- .../com/hubspot/jinjava/lib/filter/EscapeJinjavaFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/EscapeJinjavaFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/EscapeJinjavaFilter.java index cceceedea..b69be61be 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/EscapeJinjavaFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/EscapeJinjavaFilter.java @@ -63,7 +63,7 @@ public Object filter(Object object, JinjavaInterpreter interpreter, String... ar @Override public String getName() { - return "escapeJinjava"; + return "escape_jinjava"; } } From 8288bdc23f835b7cf630f5d958b170531b975328 Mon Sep 17 00:00:00 2001 From: Nate Belisle Date: Fri, 26 Jan 2018 17:10:28 -0500 Subject: [PATCH 0391/2465] Add 2.3.5 release to CHANGES.md --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 719fc20ec..4b8e7c1c7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Jinjava Releases # +### 2017-11-30 Version 2.3.5 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.3.5%22)) ### + +* [Add new EscapeJinjavaFilter](https://github.com/HubSpot/jinjava/pull/168) + ### 2017-11-30 Version 2.3.4 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.3.4%22)) ### * [Preserve groupby order of elements](https://github.com/HubSpot/jinjava/pull/163) From d9ead8c90804d84806d8652d315e75ebdcf9555c Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Fri, 26 Jan 2018 22:14:50 +0000 Subject: [PATCH 0392/2465] [maven-release-plugin] prepare release jinjava-2.3.5 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1133fc8f1..be5ccaf3c 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.3.5-SNAPSHOT + 2.3.5 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.3.5 From 3a418902aebe26bcba2cfc1a358dbd1258fc086a Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Fri, 26 Jan 2018 22:14:51 +0000 Subject: [PATCH 0393/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index be5ccaf3c..83b888c5e 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.3.5 + 2.3.6-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.3.5 + HEAD From 0da3945d258d94f18c522ebdb18fa7de48c4bc41 Mon Sep 17 00:00:00 2001 From: Nate Belisle Date: Fri, 26 Jan 2018 17:21:29 -0500 Subject: [PATCH 0394/2465] Fix date --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 4b8e7c1c7..37e1930fd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # Jinjava Releases # -### 2017-11-30 Version 2.3.5 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.3.5%22)) ### +### 2017-01-26 Version 2.3.5 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.3.5%22)) ### * [Add new EscapeJinjavaFilter](https://github.com/HubSpot/jinjava/pull/168) From 9175a951a60691927f8de69e851ea795b25f85a0 Mon Sep 17 00:00:00 2001 From: Stanislav Bytsko Date: Thu, 1 Feb 2018 13:38:55 +0200 Subject: [PATCH 0395/2465] Don't put stacktrace in the exception message Exception stacktrace for each error could be easily extracted by doing ((FatalTemplateErrorsException) e).getErrors().get(n).getException(), if needed --- .../FatalTemplateErrorsException.java | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/FatalTemplateErrorsException.java b/src/main/java/com/hubspot/jinjava/interpret/FatalTemplateErrorsException.java index c222752ea..637116313 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/FatalTemplateErrorsException.java +++ b/src/main/java/com/hubspot/jinjava/interpret/FatalTemplateErrorsException.java @@ -1,6 +1,6 @@ package com.hubspot.jinjava.interpret; -import org.apache.commons.lang3.exception.ExceptionUtils; +import java.util.Collection; /** * Container exception thrown when fatal errors are encountered while rendering a template. @@ -13,24 +13,18 @@ public class FatalTemplateErrorsException extends InterpretException { private final String template; private final Iterable errors; - public FatalTemplateErrorsException(String template, Iterable errors) { + public FatalTemplateErrorsException(String template, Collection errors) { super(generateMessage(errors)); this.template = template; this.errors = errors; } - private static String generateMessage(Iterable errors) { - StringBuilder msg = new StringBuilder(); - - for (TemplateError error : errors) { - msg.append(error.toString()).append('\n'); - - if (error.getException() != null) { - msg.append(ExceptionUtils.getStackTrace(error.getException())).append('\n'); - } + private static String generateMessage(Collection errors) { + if (errors.isEmpty()) { + throw new IllegalArgumentException("FatalTemplateErrorsException should have at least one error"); } - return msg.toString(); + return errors.iterator().next().getMessage(); } public String getTemplate() { From a9f865bf80b588f15de3186870bc140931127706 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Thu, 8 Feb 2018 14:52:41 -0500 Subject: [PATCH 0396/2465] Add more exp tests for arrays. --- .../lib/exptest/ContainsAllExpTest.java | 56 +++++++++++++++++++ .../jinjava/lib/exptest/ContainsExpTest.java | 32 +++++++++++ .../jinjava/lib/exptest/ExpTestLibrary.java | 5 +- .../jinjava/lib/exptest/IsInExpTest.java | 32 +++++++++++ 4 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/hubspot/jinjava/lib/exptest/ContainsAllExpTest.java create mode 100644 src/main/java/com/hubspot/jinjava/lib/exptest/ContainsExpTest.java create mode 100644 src/main/java/com/hubspot/jinjava/lib/exptest/IsInExpTest.java diff --git a/src/main/java/com/hubspot/jinjava/lib/exptest/ContainsAllExpTest.java b/src/main/java/com/hubspot/jinjava/lib/exptest/ContainsAllExpTest.java new file mode 100644 index 000000000..6052d2f51 --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/lib/exptest/ContainsAllExpTest.java @@ -0,0 +1,56 @@ +package com.hubspot.jinjava.lib.exptest; + +import java.util.Iterator; +import java.util.Objects; +import java.util.Optional; + +import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.util.ForLoop; +import com.hubspot.jinjava.util.ObjectIterator; + +public class ContainsAllExpTest implements ExpTest { + + @Override + public boolean evaluate(Object var, JinjavaInterpreter interpreter, Object... args) { + + if (null == var || args.length == 0) { + return false; + } + + ForLoop loop = ObjectIterator.getLoop(args[0]); + while (loop.hasNext()) { + Object matchValue = loop.next(); + ForLoop varLoop = ObjectIterator.getLoop(var); + boolean matches = false; + while (varLoop.hasNext()) { + if (Objects.equals(matchValue, args[0])) { + matches = true; + break; + } + } + if (!matches) { + return false; + } + } + + return true; + } + + private Optional> getIterator(Object var) { + + if (Iterator.class.isAssignableFrom(var.getClass())) { + return Optional.of((Iterator) var); + } + + if (Iterable.class.isAssignableFrom(var.getClass())) { + return Optional.of(((Iterable) var).iterator()); + } + + return Optional.empty(); + } + + @Override + public String getName() { + return "containsall"; + } +} diff --git a/src/main/java/com/hubspot/jinjava/lib/exptest/ContainsExpTest.java b/src/main/java/com/hubspot/jinjava/lib/exptest/ContainsExpTest.java new file mode 100644 index 000000000..5a663f12e --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/lib/exptest/ContainsExpTest.java @@ -0,0 +1,32 @@ +package com.hubspot.jinjava.lib.exptest; + +import java.util.Objects; + +import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.util.ForLoop; +import com.hubspot.jinjava.util.ObjectIterator; + +public class ContainsExpTest implements ExpTest { + + @Override + public boolean evaluate(Object var, JinjavaInterpreter interpreter, Object... args) { + + if (null == var || args.length == 0) { + return false; + } + + ForLoop loop = ObjectIterator.getLoop(var); + while (loop.hasNext()) { + if (Objects.equals(loop.next(), args[0])) { + return true; + } + } + + return false; + } + + @Override + public String getName() { + return "contains"; + } +} diff --git a/src/main/java/com/hubspot/jinjava/lib/exptest/ExpTestLibrary.java b/src/main/java/com/hubspot/jinjava/lib/exptest/ExpTestLibrary.java index d932ccd52..e9bb6e2da 100644 --- a/src/main/java/com/hubspot/jinjava/lib/exptest/ExpTestLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/exptest/ExpTestLibrary.java @@ -30,7 +30,10 @@ protected void registerDefaults() { IsStringStartingWithExpTest.class, IsTruthyExpTest.class, IsUndefinedExpTest.class, - IsUpperExpTest.class); + IsUpperExpTest.class, + ContainsAllExpTest.class, + ContainsExpTest.class, + IsInExpTest.class); } public ExpTest getExpTest(String name) { diff --git a/src/main/java/com/hubspot/jinjava/lib/exptest/IsInExpTest.java b/src/main/java/com/hubspot/jinjava/lib/exptest/IsInExpTest.java new file mode 100644 index 000000000..ff670df2a --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/lib/exptest/IsInExpTest.java @@ -0,0 +1,32 @@ +package com.hubspot.jinjava.lib.exptest; + +import java.util.Objects; + +import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.util.ForLoop; +import com.hubspot.jinjava.util.ObjectIterator; + +public class IsInExpTest implements ExpTest { + + @Override + public boolean evaluate(Object var, JinjavaInterpreter interpreter, Object... args) { + + if (args == null || args.length == 0) { + return false; + } + + ForLoop loop = ObjectIterator.getLoop(args[0]); + while (loop.hasNext()) { + if (Objects.equals(loop.next(), var)) { + return true; + } + } + + return false; + } + + @Override + public String getName() { + return "in"; + } +} From 670967dc37174c4af8293d227c5799f03ba4db95 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Thu, 8 Feb 2018 14:53:23 -0500 Subject: [PATCH 0397/2465] Remove function. --- .../jinjava/lib/exptest/ContainsAllExpTest.java | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/exptest/ContainsAllExpTest.java b/src/main/java/com/hubspot/jinjava/lib/exptest/ContainsAllExpTest.java index 6052d2f51..a0a6c4171 100644 --- a/src/main/java/com/hubspot/jinjava/lib/exptest/ContainsAllExpTest.java +++ b/src/main/java/com/hubspot/jinjava/lib/exptest/ContainsAllExpTest.java @@ -1,8 +1,6 @@ package com.hubspot.jinjava.lib.exptest; -import java.util.Iterator; import java.util.Objects; -import java.util.Optional; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.util.ForLoop; @@ -36,19 +34,6 @@ public boolean evaluate(Object var, JinjavaInterpreter interpreter, Object... ar return true; } - private Optional> getIterator(Object var) { - - if (Iterator.class.isAssignableFrom(var.getClass())) { - return Optional.of((Iterator) var); - } - - if (Iterable.class.isAssignableFrom(var.getClass())) { - return Optional.of(((Iterable) var).iterator()); - } - - return Optional.empty(); - } - @Override public String getName() { return "containsall"; From 94f8fc747f67e62be9b25d8244fdb3311c6a9883 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Thu, 8 Feb 2018 15:02:18 -0500 Subject: [PATCH 0398/2465] Add null check. --- .../com/hubspot/jinjava/lib/exptest/ContainsAllExpTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/exptest/ContainsAllExpTest.java b/src/main/java/com/hubspot/jinjava/lib/exptest/ContainsAllExpTest.java index a0a6c4171..e452ace8b 100644 --- a/src/main/java/com/hubspot/jinjava/lib/exptest/ContainsAllExpTest.java +++ b/src/main/java/com/hubspot/jinjava/lib/exptest/ContainsAllExpTest.java @@ -11,7 +11,7 @@ public class ContainsAllExpTest implements ExpTest { @Override public boolean evaluate(Object var, JinjavaInterpreter interpreter, Object... args) { - if (null == var || args.length == 0) { + if (null == var || args.length == 0 || args[0] == null) { return false; } From 8ab3625028320ed25690476a885c9a2b20acd91a Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Thu, 8 Feb 2018 16:01:01 -0500 Subject: [PATCH 0399/2465] Add tests, change names. --- .../jinjava/lib/exptest/ExpTestLibrary.java | 6 +-- ...ExpTest.java => IsContainedInExpTest.java} | 4 +- ...pTest.java => IsContainingAllExpTest.java} | 6 +-- ...sExpTest.java => IsContainingExpTest.java} | 4 +- .../lib/exptest/IsContainedInExpTestTest.java | 47 ++++++++++++++++ .../exptest/IsContainingAllExpTestTest.java | 48 +++++++++++++++++ .../lib/exptest/IsContainingExpTestTest.java | 53 +++++++++++++++++++ 7 files changed, 158 insertions(+), 10 deletions(-) rename src/main/java/com/hubspot/jinjava/lib/exptest/{IsInExpTest.java => IsContainedInExpTest.java} (88%) rename src/main/java/com/hubspot/jinjava/lib/exptest/{ContainsAllExpTest.java => IsContainingAllExpTest.java} (85%) rename src/main/java/com/hubspot/jinjava/lib/exptest/{ContainsExpTest.java => IsContainingExpTest.java} (88%) create mode 100644 src/test/java/com/hubspot/jinjava/lib/exptest/IsContainedInExpTestTest.java create mode 100644 src/test/java/com/hubspot/jinjava/lib/exptest/IsContainingAllExpTestTest.java create mode 100644 src/test/java/com/hubspot/jinjava/lib/exptest/IsContainingExpTestTest.java diff --git a/src/main/java/com/hubspot/jinjava/lib/exptest/ExpTestLibrary.java b/src/main/java/com/hubspot/jinjava/lib/exptest/ExpTestLibrary.java index e9bb6e2da..297cf3d7d 100644 --- a/src/main/java/com/hubspot/jinjava/lib/exptest/ExpTestLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/exptest/ExpTestLibrary.java @@ -31,9 +31,9 @@ protected void registerDefaults() { IsTruthyExpTest.class, IsUndefinedExpTest.class, IsUpperExpTest.class, - ContainsAllExpTest.class, - ContainsExpTest.class, - IsInExpTest.class); + IsContainingAllExpTest.class, + IsContainingExpTest.class, + IsContainedInExpTest.class); } public ExpTest getExpTest(String name) { diff --git a/src/main/java/com/hubspot/jinjava/lib/exptest/IsInExpTest.java b/src/main/java/com/hubspot/jinjava/lib/exptest/IsContainedInExpTest.java similarity index 88% rename from src/main/java/com/hubspot/jinjava/lib/exptest/IsInExpTest.java rename to src/main/java/com/hubspot/jinjava/lib/exptest/IsContainedInExpTest.java index ff670df2a..16795a227 100644 --- a/src/main/java/com/hubspot/jinjava/lib/exptest/IsInExpTest.java +++ b/src/main/java/com/hubspot/jinjava/lib/exptest/IsContainedInExpTest.java @@ -6,7 +6,7 @@ import com.hubspot.jinjava.util.ForLoop; import com.hubspot.jinjava.util.ObjectIterator; -public class IsInExpTest implements ExpTest { +public class IsContainedInExpTest implements ExpTest { @Override public boolean evaluate(Object var, JinjavaInterpreter interpreter, Object... args) { @@ -27,6 +27,6 @@ public boolean evaluate(Object var, JinjavaInterpreter interpreter, Object... ar @Override public String getName() { - return "in"; + return "containedin"; } } diff --git a/src/main/java/com/hubspot/jinjava/lib/exptest/ContainsAllExpTest.java b/src/main/java/com/hubspot/jinjava/lib/exptest/IsContainingAllExpTest.java similarity index 85% rename from src/main/java/com/hubspot/jinjava/lib/exptest/ContainsAllExpTest.java rename to src/main/java/com/hubspot/jinjava/lib/exptest/IsContainingAllExpTest.java index e452ace8b..4db9f6853 100644 --- a/src/main/java/com/hubspot/jinjava/lib/exptest/ContainsAllExpTest.java +++ b/src/main/java/com/hubspot/jinjava/lib/exptest/IsContainingAllExpTest.java @@ -6,7 +6,7 @@ import com.hubspot.jinjava.util.ForLoop; import com.hubspot.jinjava.util.ObjectIterator; -public class ContainsAllExpTest implements ExpTest { +public class IsContainingAllExpTest implements ExpTest { @Override public boolean evaluate(Object var, JinjavaInterpreter interpreter, Object... args) { @@ -21,7 +21,7 @@ public boolean evaluate(Object var, JinjavaInterpreter interpreter, Object... ar ForLoop varLoop = ObjectIterator.getLoop(var); boolean matches = false; while (varLoop.hasNext()) { - if (Objects.equals(matchValue, args[0])) { + if (Objects.equals(matchValue, varLoop.next())) { matches = true; break; } @@ -36,6 +36,6 @@ public boolean evaluate(Object var, JinjavaInterpreter interpreter, Object... ar @Override public String getName() { - return "containsall"; + return "containingall"; } } diff --git a/src/main/java/com/hubspot/jinjava/lib/exptest/ContainsExpTest.java b/src/main/java/com/hubspot/jinjava/lib/exptest/IsContainingExpTest.java similarity index 88% rename from src/main/java/com/hubspot/jinjava/lib/exptest/ContainsExpTest.java rename to src/main/java/com/hubspot/jinjava/lib/exptest/IsContainingExpTest.java index 5a663f12e..03000287f 100644 --- a/src/main/java/com/hubspot/jinjava/lib/exptest/ContainsExpTest.java +++ b/src/main/java/com/hubspot/jinjava/lib/exptest/IsContainingExpTest.java @@ -6,7 +6,7 @@ import com.hubspot.jinjava.util.ForLoop; import com.hubspot.jinjava.util.ObjectIterator; -public class ContainsExpTest implements ExpTest { +public class IsContainingExpTest implements ExpTest { @Override public boolean evaluate(Object var, JinjavaInterpreter interpreter, Object... args) { @@ -27,6 +27,6 @@ public boolean evaluate(Object var, JinjavaInterpreter interpreter, Object... ar @Override public String getName() { - return "contains"; + return "containing"; } } diff --git a/src/test/java/com/hubspot/jinjava/lib/exptest/IsContainedInExpTestTest.java b/src/test/java/com/hubspot/jinjava/lib/exptest/IsContainedInExpTestTest.java new file mode 100644 index 000000000..c2f8f3f0a --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/lib/exptest/IsContainedInExpTestTest.java @@ -0,0 +1,47 @@ +package com.hubspot.jinjava.lib.exptest; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.HashMap; + +import org.junit.Before; +import org.junit.Test; + +import com.hubspot.jinjava.Jinjava; + +public class IsContainedInExpTestTest { + + private static final String IN_TEMPLATE = "{%% if %s is containedin %s %%}pass{%% else %%}fail{%% endif %%}"; + + private Jinjava jinjava; + + @Before + public void setup() { + jinjava = new Jinjava(); + } + + @Test + public void itPassesOnValueInSequence() { + assertThat(jinjava.render(String.format(IN_TEMPLATE, "2", "[1, 2, 3]"), new HashMap<>())).isEqualTo("pass"); + } + + @Test + public void itPassesOnNullValueInSequence() { + assertThat(jinjava.render(String.format(IN_TEMPLATE, "null", "[1, 2, null]"), new HashMap<>())).isEqualTo("pass"); + } + + @Test + public void itFailsOnValueNotInSequence() { + assertThat(jinjava.render(String.format(IN_TEMPLATE, "4", "[1, 2, 3]"), new HashMap<>())).isEqualTo("fail"); + } + + @Test + public void itFailsOnNullValueNotInSequence() { + assertThat(jinjava.render(String.format(IN_TEMPLATE, "null", "[1, 2, 3]"), new HashMap<>())).isEqualTo("fail"); + } + + @Test + public void itFailsOnNullSequence() { + assertThat(jinjava.render(String.format(IN_TEMPLATE, "2", "null"), new HashMap<>())).isEqualTo("fail"); + } +} diff --git a/src/test/java/com/hubspot/jinjava/lib/exptest/IsContainingAllExpTestTest.java b/src/test/java/com/hubspot/jinjava/lib/exptest/IsContainingAllExpTestTest.java new file mode 100644 index 000000000..d4c7faea7 --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/lib/exptest/IsContainingAllExpTestTest.java @@ -0,0 +1,48 @@ +package com.hubspot.jinjava.lib.exptest; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.HashMap; + +import org.junit.Before; +import org.junit.Test; + +import com.hubspot.jinjava.Jinjava; + +public class IsContainingAllExpTestTest { + + private static final String CONTAINING_TEMPLATE = "{%% if %s is containingall %s %%}pass{%% else %%}fail{%% endif %%}"; + + private Jinjava jinjava; + + @Before + public void setup() { + + jinjava = new Jinjava(); + } + + @Test + public void itPassesOnContainedValues() { + assertThat(jinjava.render(String.format(CONTAINING_TEMPLATE, "[1, 2, 3]", "[1, 2]"), new HashMap<>())).isEqualTo("pass"); + } + + @Test + public void itPassesOnContainedDuplicatedValues() { + assertThat(jinjava.render(String.format(CONTAINING_TEMPLATE, "[1, 2, 3]", "[1, 2, 2]"), new HashMap<>())).isEqualTo("pass"); + } + + @Test + public void itFailsOnOnlySomeContainedValues() { + assertThat(jinjava.render(String.format(CONTAINING_TEMPLATE, "[1, 2, 3]", "[1, 2, 4]"), new HashMap<>())).isEqualTo("fail"); + } + + @Test + public void itFailsOnNullSequence() { + assertThat(jinjava.render(String.format(CONTAINING_TEMPLATE, "null", "[1, 2, 4]"), new HashMap<>())).isEqualTo("fail"); + } + + @Test + public void itFailsOnNullValues() { + assertThat(jinjava.render(String.format(CONTAINING_TEMPLATE, "[1, 2, 3]", "null"), new HashMap<>())).isEqualTo("fail"); + } +} diff --git a/src/test/java/com/hubspot/jinjava/lib/exptest/IsContainingExpTestTest.java b/src/test/java/com/hubspot/jinjava/lib/exptest/IsContainingExpTestTest.java new file mode 100644 index 000000000..a86c6ada5 --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/lib/exptest/IsContainingExpTestTest.java @@ -0,0 +1,53 @@ +package com.hubspot.jinjava.lib.exptest; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.HashMap; + +import org.junit.Before; +import org.junit.Test; + +import com.hubspot.jinjava.Jinjava; + +public class IsContainingExpTestTest { + + private static final String CONTAINING_TEMPLATE = "{%% if %s is containing %s %%}pass{%% else %%}fail{%% endif %%}"; + + private Jinjava jinjava; + + @Before + public void setup() { + + jinjava = new Jinjava(); + } + + @Test + public void itPassesOnContainedValue() { + assertThat(jinjava.render(String.format(CONTAINING_TEMPLATE, "[1, 2, 3]", "2"), new HashMap<>())).isEqualTo("pass"); + } + + @Test + public void itPassesOnNullContainedValue() { + assertThat(jinjava.render(String.format(CONTAINING_TEMPLATE, "[1, 2, null]", "null"), new HashMap<>())).isEqualTo("pass"); + } + + @Test + public void itFailsOnMissingValue() { + assertThat(jinjava.render(String.format(CONTAINING_TEMPLATE, "[1, 2, 3]", "4"), new HashMap<>())).isEqualTo("fail"); + } + + @Test + public void itFailsOnEmptyValue() { + assertThat(jinjava.render(String.format(CONTAINING_TEMPLATE, "[1, 2, 3]", ""), new HashMap<>())).isEqualTo("fail"); + } + + @Test + public void itFailsOnNullValue() { + assertThat(jinjava.render(String.format(CONTAINING_TEMPLATE, "[1, 2, 3]", "null"), new HashMap<>())).isEqualTo("fail"); + } + + @Test + public void itFailsOnNullSequence() { + assertThat(jinjava.render(String.format(CONTAINING_TEMPLATE, "null", "2"), new HashMap<>())).isEqualTo("fail"); + } +} From d7233681b30c0fc42b98186e98f77515b5720290 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Thu, 8 Feb 2018 16:05:19 -0500 Subject: [PATCH 0400/2465] Change containedin to within --- .../java/com/hubspot/jinjava/lib/exptest/ExpTestLibrary.java | 2 +- .../{IsContainedInExpTest.java => IsWithinExpTest.java} | 4 ++-- ...IsContainedInExpTestTest.java => IsWithinExpTestTest.java} | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/main/java/com/hubspot/jinjava/lib/exptest/{IsContainedInExpTest.java => IsWithinExpTest.java} (88%) rename src/test/java/com/hubspot/jinjava/lib/exptest/{IsContainedInExpTestTest.java => IsWithinExpTestTest.java} (88%) diff --git a/src/main/java/com/hubspot/jinjava/lib/exptest/ExpTestLibrary.java b/src/main/java/com/hubspot/jinjava/lib/exptest/ExpTestLibrary.java index 297cf3d7d..7a01d7cab 100644 --- a/src/main/java/com/hubspot/jinjava/lib/exptest/ExpTestLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/exptest/ExpTestLibrary.java @@ -33,7 +33,7 @@ protected void registerDefaults() { IsUpperExpTest.class, IsContainingAllExpTest.class, IsContainingExpTest.class, - IsContainedInExpTest.class); + IsWithinExpTest.class); } public ExpTest getExpTest(String name) { diff --git a/src/main/java/com/hubspot/jinjava/lib/exptest/IsContainedInExpTest.java b/src/main/java/com/hubspot/jinjava/lib/exptest/IsWithinExpTest.java similarity index 88% rename from src/main/java/com/hubspot/jinjava/lib/exptest/IsContainedInExpTest.java rename to src/main/java/com/hubspot/jinjava/lib/exptest/IsWithinExpTest.java index 16795a227..df22645e1 100644 --- a/src/main/java/com/hubspot/jinjava/lib/exptest/IsContainedInExpTest.java +++ b/src/main/java/com/hubspot/jinjava/lib/exptest/IsWithinExpTest.java @@ -6,7 +6,7 @@ import com.hubspot.jinjava.util.ForLoop; import com.hubspot.jinjava.util.ObjectIterator; -public class IsContainedInExpTest implements ExpTest { +public class IsWithinExpTest implements ExpTest { @Override public boolean evaluate(Object var, JinjavaInterpreter interpreter, Object... args) { @@ -27,6 +27,6 @@ public boolean evaluate(Object var, JinjavaInterpreter interpreter, Object... ar @Override public String getName() { - return "containedin"; + return "within"; } } diff --git a/src/test/java/com/hubspot/jinjava/lib/exptest/IsContainedInExpTestTest.java b/src/test/java/com/hubspot/jinjava/lib/exptest/IsWithinExpTestTest.java similarity index 88% rename from src/test/java/com/hubspot/jinjava/lib/exptest/IsContainedInExpTestTest.java rename to src/test/java/com/hubspot/jinjava/lib/exptest/IsWithinExpTestTest.java index c2f8f3f0a..2673d226d 100644 --- a/src/test/java/com/hubspot/jinjava/lib/exptest/IsContainedInExpTestTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/exptest/IsWithinExpTestTest.java @@ -9,9 +9,9 @@ import com.hubspot.jinjava.Jinjava; -public class IsContainedInExpTestTest { +public class IsWithinExpTestTest { - private static final String IN_TEMPLATE = "{%% if %s is containedin %s %%}pass{%% else %%}fail{%% endif %%}"; + private static final String IN_TEMPLATE = "{%% if %s is within %s %%}pass{%% else %%}fail{%% endif %%}"; private Jinjava jinjava; From 1aa36c64e86b13a0480b0ad8688946e4fa33d94b Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Thu, 8 Feb 2018 16:07:14 -0500 Subject: [PATCH 0401/2465] Slight fix. --- .../hubspot/jinjava/lib/exptest/IsContainingAllExpTestTest.java | 1 - .../com/hubspot/jinjava/lib/exptest/IsContainingExpTestTest.java | 1 - 2 files changed, 2 deletions(-) diff --git a/src/test/java/com/hubspot/jinjava/lib/exptest/IsContainingAllExpTestTest.java b/src/test/java/com/hubspot/jinjava/lib/exptest/IsContainingAllExpTestTest.java index d4c7faea7..a7a1f1d14 100644 --- a/src/test/java/com/hubspot/jinjava/lib/exptest/IsContainingAllExpTestTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/exptest/IsContainingAllExpTestTest.java @@ -17,7 +17,6 @@ public class IsContainingAllExpTestTest { @Before public void setup() { - jinjava = new Jinjava(); } diff --git a/src/test/java/com/hubspot/jinjava/lib/exptest/IsContainingExpTestTest.java b/src/test/java/com/hubspot/jinjava/lib/exptest/IsContainingExpTestTest.java index a86c6ada5..ddc8467b7 100644 --- a/src/test/java/com/hubspot/jinjava/lib/exptest/IsContainingExpTestTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/exptest/IsContainingExpTestTest.java @@ -17,7 +17,6 @@ public class IsContainingExpTestTest { @Before public void setup() { - jinjava = new Jinjava(); } From d6ce5bd1dd6621427b68270d170fc06661c64eb6 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Fri, 9 Feb 2018 13:31:45 -0500 Subject: [PATCH 0402/2465] Update CHANGES.md --- CHANGES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 37e1930fd..6e3cb199f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,10 @@ # Jinjava Releases # +### 2017-01-26 Version 2.3.6 ([Maven Central]()) ### + +* [Add more sequence expression tests](https://github.com/HubSpot/jinjava/pull/175) +* [Don't put stack trace in the exception message](https://github.com/HubSpot/jinjava/pull/174) + ### 2017-01-26 Version 2.3.5 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.3.5%22)) ### * [Add new EscapeJinjavaFilter](https://github.com/HubSpot/jinjava/pull/168) From ff924a8752b741116c03505a02e6568276019c37 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Fri, 9 Feb 2018 13:32:11 -0500 Subject: [PATCH 0403/2465] Update CHANGES.md --- CHANGES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 6e3cb199f..92c4b9759 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,11 +1,11 @@ # Jinjava Releases # -### 2017-01-26 Version 2.3.6 ([Maven Central]()) ### +### 2018-02-9 Version 2.3.6 ([Maven Central]()) ### * [Add more sequence expression tests](https://github.com/HubSpot/jinjava/pull/175) * [Don't put stack trace in the exception message](https://github.com/HubSpot/jinjava/pull/174) -### 2017-01-26 Version 2.3.5 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.3.5%22)) ### +### 2018-01-26 Version 2.3.5 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.3.5%22)) ### * [Add new EscapeJinjavaFilter](https://github.com/HubSpot/jinjava/pull/168) From 1303c9d18b093bd3c42ce048cb2c20b5bb594c95 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Fri, 9 Feb 2018 18:36:22 +0000 Subject: [PATCH 0404/2465] [maven-release-plugin] prepare release jinjava-2.3.6 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 83b888c5e..e490e5122 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.3.6-SNAPSHOT + 2.3.6 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.3.6 From a6e48fe731b5e07d8552c1b7bf2a6f30ac1426e7 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Fri, 9 Feb 2018 18:36:22 +0000 Subject: [PATCH 0405/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e490e5122..3b2274db0 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.3.6 + 2.3.7-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.3.6 + HEAD From ee40b9da4319968f65c1418fd764bfefd86616de Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 20 Feb 2018 17:17:09 -0500 Subject: [PATCH 0406/2465] tune down warning for null string passed to truncate --- src/main/java/com/hubspot/jinjava/lib/fn/Functions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java index 768c29e76..dba63730a 100644 --- a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java @@ -128,7 +128,7 @@ public static Object truncate(Object var, Object... arg) { try { length = Integer.parseInt(Objects.toString(arg[0])); } catch (Exception e) { - ENGINE_LOG.warn("truncate(): error setting length for {}, using default {}", arg[0], DEFAULT_TRUNCATE_LENGTH); + ENGINE_LOG.info("truncate(): error setting length for {}, using default {}", arg[0], DEFAULT_TRUNCATE_LENGTH); } } From d81a59c0543e2df68b174cfb6a3788cc09194959 Mon Sep 17 00:00:00 2001 From: Padraig Farrell Date: Mon, 26 Feb 2018 15:45:31 +0000 Subject: [PATCH 0407/2465] Make int/float parsing locale aware --- .../jinjava/lib/filter/FloatFilter.java | 16 +++++++++- .../hubspot/jinjava/lib/filter/IntFilter.java | 16 ++++++++-- .../jinjava/lib/filter/IntFilterTest.java | 32 ++++++++++++++++++- 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/FloatFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/FloatFilter.java index 866b19a7d..f5bf4e7db 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/FloatFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/FloatFilter.java @@ -1,5 +1,8 @@ package com.hubspot.jinjava.lib.filter; +import java.text.NumberFormat; +import java.util.Locale; + import org.apache.commons.lang3.math.NumberUtils; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; @@ -37,11 +40,22 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args) return defaultVal; } + if (Float.class.isAssignableFrom(var.getClass())) { + return var; + } if (Number.class.isAssignableFrom(var.getClass())) { return ((Number) var).floatValue(); } - return NumberUtils.toFloat(var.toString(), defaultVal); + Locale locale = interpreter.getConfig().getLocale(); + NumberFormat numberFormat = NumberFormat.getNumberInstance(locale); + float result; + try { + result = numberFormat.parse(var.toString()).floatValue(); + } catch (Exception e) { + result = defaultVal; + } + return result; } } diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/IntFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/IntFilter.java index 1b5f7b7d6..49e55c646 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/IntFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/IntFilter.java @@ -1,5 +1,8 @@ package com.hubspot.jinjava.lib.filter; +import java.text.NumberFormat; +import java.util.Locale; + import org.apache.commons.lang3.math.NumberUtils; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; @@ -8,7 +11,7 @@ import com.hubspot.jinjava.interpret.JinjavaInterpreter; /** - * int(value, default=0) Convert the value into an integer. If the conversion doesn’t work it will return 0. You can override this default using the first parameter. + * int(value, default=0) Convert the value into an integer. If the conversion doesn't work it will return 0. You can override this default using the first parameter. */ @JinjavaDoc( value = "Convert the value into an integer.", @@ -46,7 +49,16 @@ public Object filter(Object var, JinjavaInterpreter interpreter, return ((Number) var).intValue(); } - return NumberUtils.toInt(var.toString(), defaultVal); + Locale locale = interpreter.getConfig().getLocale(); + NumberFormat numberFormat = NumberFormat.getNumberInstance(locale); + numberFormat.setParseIntegerOnly(true); + int result; + try { + result = numberFormat.parse(var.toString()).intValue(); + } catch (Exception e) { + result = defaultVal; + } + return result; } } diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/IntFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/IntFilterTest.java index 62fd7f55f..e0a3874f1 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/IntFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/IntFilterTest.java @@ -2,14 +2,22 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.nio.charset.StandardCharsets; +import java.time.ZoneOffset; +import java.util.Locale; + import org.junit.Before; import org.junit.Test; import com.hubspot.jinjava.Jinjava; +import com.hubspot.jinjava.JinjavaConfig; import com.hubspot.jinjava.interpret.JinjavaInterpreter; public class IntFilterTest { + private static final Locale FRENCH_LOCALE = new Locale("fr", "FR"); + private static final JinjavaConfig FRENCH_LOCALE_CONFIG = new JinjavaConfig(StandardCharsets.UTF_8, FRENCH_LOCALE, ZoneOffset.UTC, 10); + IntFilter filter; JinjavaInterpreter interpreter; @@ -21,7 +29,7 @@ public void setup() { @Test public void itReturnsSameWhenVarIsNumber() { - Integer var = Integer.valueOf(123); + Integer var = 123; assertThat(filter.filter(var, interpreter)).isSameAs(var); } @@ -41,9 +49,31 @@ public void itReturnsVarAsInt() { assertThat(filter.filter("123", interpreter)).isEqualTo(123); } + @Test + public void itInterpretsUsCommasAndPeriodsWithUsLocale() { + assertThat(filter.filter("123,123.12", interpreter)).isEqualTo(123123); + } + + @Test + public void itInterpretsFrenchCommasAndPeriodsWithUsLocale() { + assertThat(filter.filter("123.123,12", interpreter)).isEqualTo(123); + } + @Test public void itReturnsDefaultWhenUnableToParseVar() { assertThat(filter.filter("foo", interpreter)).isEqualTo(0); } + @Test + public void itInterpretsUsCommasAndPeriodsWithFrenchLocale() { + interpreter = new Jinjava(FRENCH_LOCALE_CONFIG).newInterpreter(); + assertThat(filter.filter("123,123.12", interpreter)).isEqualTo(123); + } + + @Test + public void itInterpretsFrenchCommasAndPeriodsWithFrenchLocale() { + interpreter = new Jinjava(FRENCH_LOCALE_CONFIG).newInterpreter(); + assertThat(filter.filter("123\u00A0123,12", interpreter)).isEqualTo(123123); + } + } From 7f5bf1f8b84aee9738cf400b9056083e597c302d Mon Sep 17 00:00:00 2001 From: Padraig Farrell Date: Mon, 26 Feb 2018 16:30:29 +0000 Subject: [PATCH 0408/2465] Add tests for FloatFilter --- .../jinjava/lib/filter/FloatFilterTest.java | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 src/test/java/com/hubspot/jinjava/lib/filter/FloatFilterTest.java diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/FloatFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/FloatFilterTest.java new file mode 100644 index 000000000..df9503db0 --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/lib/filter/FloatFilterTest.java @@ -0,0 +1,94 @@ +/********************************************************************** + Copyright (c) 2018 HubSpot 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.hubspot.jinjava.lib.filter; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.nio.charset.StandardCharsets; +import java.time.ZoneOffset; +import java.util.Locale; + +import org.junit.Before; +import org.junit.Test; + +import com.hubspot.jinjava.Jinjava; +import com.hubspot.jinjava.JinjavaConfig; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; + +public class FloatFilterTest { + + private static final Locale FRENCH_LOCALE = new Locale("fr", "FR"); + private static final JinjavaConfig FRENCH_LOCALE_CONFIG = new JinjavaConfig(StandardCharsets.UTF_8, FRENCH_LOCALE, ZoneOffset.UTC, 10); + + FloatFilter filter; + JinjavaInterpreter interpreter; + + @Before + public void setup() { + interpreter = new Jinjava().newInterpreter(); + filter = new FloatFilter(); + } + + @Test + public void itReturnsSameWhenVarIsNumber() { + Float var = 123.4f; + assertThat(filter.filter(var, interpreter)).isSameAs(var); + } + + @Test + public void itReturnsDefaultWhenVarIsNull() { + assertThat(filter.filter(null, interpreter)).isEqualTo(0.0f); + assertThat(filter.filter(null, interpreter, "123.45")).isEqualTo(123.45f); + } + + @Test + public void itIgnoresGivenDefaultIfNaN() { + assertThat(filter.filter(null, interpreter, "foo")).isEqualTo(0.0f); + } + + @Test + public void itReturnsVarAsFloat() { + assertThat(filter.filter("123.45", interpreter)).isEqualTo(123.45f); + } + + @Test + public void itInterpretsUsCommasAndPeriodsWithUsLocale() { + assertThat(filter.filter("123,123.45", interpreter)).isEqualTo(123123.45f); + } + + @Test + public void itInterpretsFrenchCommasAndPeriodsWithUsLocale() { + assertThat(filter.filter("123.123,45", interpreter)).isEqualTo(123.123f); + } + + @Test + public void itReturnsDefaultWhenUnableToParseVar() { + assertThat(filter.filter("foo", interpreter)).isEqualTo(0.0f); + } + + @Test + public void itInterpretsUsCommasAndPeriodsWithFrenchLocale() { + interpreter = new Jinjava(FRENCH_LOCALE_CONFIG).newInterpreter(); + assertThat(filter.filter("123,123.12", interpreter)).isEqualTo(123.123f); + } + + @Test + public void itInterpretsFrenchCommasAndPeriodsWithFrenchLocale() { + interpreter = new Jinjava(FRENCH_LOCALE_CONFIG).newInterpreter(); + assertThat(filter.filter("123\u00A0123,45", interpreter)).isEqualTo(123123.45f); + } + +} From 81ca61982c495cd73de6d2edba39342b32694b37 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Mon, 26 Feb 2018 12:48:46 -0500 Subject: [PATCH 0409/2465] Update CHANGES.md --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 92c4b9759..e8991d31d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # Jinjava Releases # -### 2018-02-9 Version 2.3.6 ([Maven Central]()) ### +### 2018-02-9 Version 2.3.6 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.3.6%22)) ### * [Add more sequence expression tests](https://github.com/HubSpot/jinjava/pull/175) * [Don't put stack trace in the exception message](https://github.com/HubSpot/jinjava/pull/174) From 61325bfa8914f6c1581d56d14f9042586ef2112d Mon Sep 17 00:00:00 2001 From: Padraig Farrell Date: Mon, 26 Feb 2018 18:01:16 +0000 Subject: [PATCH 0410/2465] Bump to 2.4.0 for possibly breaking int/float filter change --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3b2274db0..0c0270af5 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.3.7-SNAPSHOT + 2.4.0-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava From 8172319e86f4f2c94cfbe91f7223526899df9b8c Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 26 Feb 2018 13:06:49 -0500 Subject: [PATCH 0411/2465] Update CHANGES.md --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index e8991d31d..eeae9e34e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # Jinjava Releases # -### 2018-02-9 Version 2.3.6 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.3.6%22)) ### +### 2018-02-09 Version 2.3.6 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.3.6%22)) ### * [Add more sequence expression tests](https://github.com/HubSpot/jinjava/pull/175) * [Don't put stack trace in the exception message](https://github.com/HubSpot/jinjava/pull/174) From c7f8358521f637a5f68e6f56beef1da28a601035 Mon Sep 17 00:00:00 2001 From: Padraig Farrell Date: Mon, 26 Feb 2018 18:10:41 +0000 Subject: [PATCH 0412/2465] Update changes.md for 2.4.0 --- CHANGES.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e8991d31d..9c40b1ac8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,10 @@ # Jinjava Releases # -### 2018-02-9 Version 2.3.6 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.3.6%22)) ### +### 2018-02-26 Version 2.4.0 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.0%7Cjar)) ### + +* [Make int/float parsing locale aware](https://github.com/HubSpot/jinjava/pull/178) + +### 2018-02-9 Version 2.3.6 ([Maven Central]()) ### * [Add more sequence expression tests](https://github.com/HubSpot/jinjava/pull/175) * [Don't put stack trace in the exception message](https://github.com/HubSpot/jinjava/pull/174) @@ -17,7 +21,6 @@ * [always evaluate tags and control structures in nested expressions](https://github.com/HubSpot/jinjava/pull/161) - ### 2017-11-14 Version 2.3.2 ([Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.hubspot.jinjava%22%20AND%20v%3A%222.3.2%22)) ### * [select filter now supports expression tests with arguments like 'equalto'](https://github.com/HubSpot/jinjava/pull/158) From 5347bdc4010a9568b37dc2799884a703dab15e05 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Wed, 28 Feb 2018 14:04:16 +0000 Subject: [PATCH 0413/2465] [maven-release-plugin] prepare release jinjava-2.4.0 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0c0270af5..3eee1c23d 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.4.0-SNAPSHOT + 2.4.0 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.4.0 From 2542bb7db5e74dfe904ec299d6ce72c56ca832ee Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Wed, 28 Feb 2018 14:04:16 +0000 Subject: [PATCH 0414/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3eee1c23d..a70508a21 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.4.0 + 2.4.1-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.4.0 + HEAD From 60ddf945ec6e5bd210b5035a5148d1acba68014c Mon Sep 17 00:00:00 2001 From: Padraig Farrell Date: Thu, 1 Mar 2018 15:04:07 +0000 Subject: [PATCH 0415/2465] Bump to 2.4.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0c0270af5..a70508a21 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.4.0-SNAPSHOT + 2.4.1-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava From 0932d025d2a9ac962454f9d6f074f3ddd1561136 Mon Sep 17 00:00:00 2001 From: Steve Gutz Date: Thu, 1 Mar 2018 12:08:22 -0500 Subject: [PATCH 0416/2465] Enable blazar --- .blazar-enabled | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .blazar-enabled diff --git a/.blazar-enabled b/.blazar-enabled new file mode 100644 index 000000000..e69de29bb From 87c29efa76490eb8db7c2e20bdb00036ff5aad99 Mon Sep 17 00:00:00 2001 From: Padraig Farrell Date: Mon, 5 Mar 2018 13:52:48 +0000 Subject: [PATCH 0417/2465] Update jinjava-benchmark to reference latest jinjava version --- benchmark/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/pom.xml b/benchmark/pom.xml index 3d9acf8d6..af6dea9f8 100644 --- a/benchmark/pom.xml +++ b/benchmark/pom.xml @@ -41,7 +41,7 @@ com.hubspot.jinjava jinjava - 2.1.3-SNAPSHOT + 2.4.1-SNAPSHOT commons-io From d5a995feccffb14c26f499a84741989aa14ac69e Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Mon, 2 Apr 2018 12:58:45 -0400 Subject: [PATCH 0418/2465] Upgrade selectattr to AdvancedFilter. --- .../jinjava/lib/filter/SelectAttrFilter.java | 23 +++++++++++++------ .../lib/filter/SelectAttrFilterTest.java | 12 +++++++--- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java index 17b7462e2..775108614 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaParam; @@ -27,7 +28,7 @@ "
    Post in listing markup
    \n" + "{% endfor %}") }) -public class SelectAttrFilter implements Filter { +public class SelectAttrFilter implements AdvancedFilter { @Override public String getName() { @@ -35,20 +36,29 @@ public String getName() { } @Override - public Object filter(Object var, JinjavaInterpreter interpreter, String... args) { + public Object filter(Object var, JinjavaInterpreter interpreter, Object[] args, Map kwargs) { List result = new ArrayList<>(); if (args.length == 0) { throw new InterpretException(getName() + " filter requires an attr to filter on", interpreter.getLineNumber()); } - String[] expArgs = new String[]{}; + if (!(args[0] instanceof String)) { + throw new InterpretException(getName() + " filter requires the filter attr arg to be a string", interpreter.getLineNumber()); + } + + Object[] expArgs = new String[]{}; - String attr = args[0]; + String attr = (String) args[0]; ExpTest expTest = interpreter.getContext().getExpTest("truthy"); if (args.length > 1) { - expTest = interpreter.getContext().getExpTest(args[1]); + + if (!(args[1] instanceof String)) { + throw new InterpretException(getName() + " filter requires the expression test arg to be a string", interpreter.getLineNumber()); + } + + expTest = interpreter.getContext().getExpTest((String) args[1]); if (expTest == null) { throw new InterpretException("No expression test defined with name '" + args[1] + "'", interpreter.getLineNumber()); } @@ -63,12 +73,11 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args) Object val = loop.next(); Object attrVal = interpreter.resolveProperty(val, attr); - if (expTest.evaluate(attrVal, interpreter, (Object[]) expArgs)) { + if (expTest.evaluate(attrVal, interpreter, expArgs)) { result.add(val); } } return result; } - } diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/SelectAttrFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/SelectAttrFilterTest.java index 20836f7d0..d5a964e79 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/SelectAttrFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/SelectAttrFilterTest.java @@ -39,19 +39,25 @@ public void selectAttrWithIsEqualToExp() { .isEqualTo("[1]"); } + @Test + public void selectAttrWithNumericIsEqualToExp() { + assertThat(jinjava.render("{{ users|selectattr('num', 'equalto', 1) }}", new HashMap())) + .isEqualTo("[1]"); + } + public static class User { - private int num; + private long num; private boolean isActive; private String email; - public User(int num, boolean isActive, String email) { + public User(long num, boolean isActive, String email) { this.num = num; this.isActive = isActive; this.email = email; } - public int getNum() { + public long getNum() { return num; } From 9605478725d23b75336b2e97eb43d6c050ca3582 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 16 Apr 2018 17:11:03 -0400 Subject: [PATCH 0419/2465] remove string array constructor method reference --- src/main/java/com/hubspot/jinjava/lib/filter/Filter.java | 6 ++++-- .../com/hubspot/jinjava/lib/filter/TruncateHtmlFilter.java | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/Filter.java b/src/main/java/com/hubspot/jinjava/lib/filter/Filter.java index 5c9bd1eef..560f9ca5a 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/Filter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/Filter.java @@ -16,8 +16,10 @@ package com.hubspot.jinjava.lib.filter; import java.util.Arrays; +import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.stream.Collectors; import org.apache.commons.lang3.ArrayUtils; @@ -50,8 +52,8 @@ public interface Filter extends Importable { default Object filter(Object var, JinjavaInterpreter interpreter, Object[] args, Map kwargs) { // We append the named arguments at the end of the positional ones Object[] allArgs = ArrayUtils.addAll(args, kwargs.values().toArray()); - String[] stringArgs = Arrays.stream(allArgs).map(arg -> Objects.toString(arg, null)).toArray(String[]::new); - return filter(var, interpreter, stringArgs); + List stringArgs = Arrays.stream(allArgs).map(arg -> Objects.toString(arg, null)).collect(Collectors.toList()); + return filter(var, interpreter, stringArgs.toArray(new String[]{})); } } diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/TruncateHtmlFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/TruncateHtmlFilter.java index 5275f1bda..06018c0e2 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/TruncateHtmlFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/TruncateHtmlFilter.java @@ -76,7 +76,7 @@ private static class ContentTruncatingNodeVisitor implements NodeVisitor { private String ending; private boolean killwords; - public ContentTruncatingNodeVisitor(int maxTextLen, String ending, boolean killwords) { + ContentTruncatingNodeVisitor(int maxTextLen, String ending, boolean killwords) { this.maxTextLen = maxTextLen; this.ending = ending; this.killwords = killwords; From 3fc8ede33676209291ad4dde6eb6dd6b60308f71 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 16 Apr 2018 17:11:40 -0400 Subject: [PATCH 0420/2465] minor cleanups --- .../java/com/hubspot/jinjava/lib/filter/DictSortFilter.java | 5 ++--- src/main/java/com/hubspot/jinjava/lib/filter/SortFilter.java | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/DictSortFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/DictSortFilter.java index 70fbb4bdd..c41fc7519 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/DictSortFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/DictSortFilter.java @@ -1,7 +1,6 @@ package com.hubspot.jinjava.lib.filter; import java.io.Serializable; -import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; @@ -56,7 +55,7 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args) Map dict = (Map) var; List> sorted = Lists.newArrayList(dict.entrySet()); - Collections.sort(sorted, new MapEntryComparator(caseSensitive, sortByKey)); + sorted.sort(new MapEntryComparator(caseSensitive, sortByKey)); return sorted; } @@ -67,7 +66,7 @@ private static class MapEntryComparator implements Comparator result = Lists.newArrayList(ObjectIterator.getLoop(var)); - Collections.sort(result, new ObjectComparator(interpreter, reverse, caseSensitive, attr)); + result.sort(new ObjectComparator(interpreter, reverse, caseSensitive, attr)); return result; } @@ -73,7 +72,7 @@ private static class ObjectComparator implements Comparator { private final boolean caseSensitive; private final Variable variable; - public ObjectComparator(JinjavaInterpreter interpreter, boolean reverse, boolean caseSensitive, String attr) { + ObjectComparator(JinjavaInterpreter interpreter, boolean reverse, boolean caseSensitive, String attr) { this.reverse = reverse; this.caseSensitive = caseSensitive; From 0dcc8b2b02c5e8127f114d048173c251dfca9280 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 16 Apr 2018 17:11:58 -0400 Subject: [PATCH 0421/2465] use latest basepom --- pom.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index a70508a21..941ffd476 100644 --- a/pom.xml +++ b/pom.xml @@ -1,10 +1,11 @@ - + 4.0.0 com.hubspot basepom - 12.5 + 18.3 com.hubspot.jinjava @@ -24,12 +25,10 @@ jaredstehler Jared Stehler - jstehler@hubspot.com boulter Jeff Boulter - boulter@hubspot.com From ab30e47bd2b589568ca18974235a4c1a15baf062 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 16 Apr 2018 17:12:25 -0400 Subject: [PATCH 0422/2465] remove unnecessary mock --- .../hubspot/jinjava/lib/filter/TruncateFilterTest.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/TruncateFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/TruncateFilterTest.java index fbbbeb7a5..e17f94465 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/TruncateFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/TruncateFilterTest.java @@ -1,17 +1,12 @@ package com.hubspot.jinjava.lib.filter; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.when; import org.apache.commons.lang3.StringUtils; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.internal.stubbing.answers.ReturnsArgumentAt; import org.mockito.runners.MockitoJUnitRunner; import com.hubspot.jinjava.interpret.JinjavaInterpreter; @@ -24,11 +19,6 @@ public class TruncateFilterTest { @InjectMocks TruncateFilter filter; - @Before - public void setup() { - when(interpreter.resolveString(anyString(), anyInt(), anyInt())).thenAnswer(new ReturnsArgumentAt(0)); - } - @Test public void itPassesThroughSmallEnoughText() throws Exception { String s = StringUtils.rightPad("", 255, 'x'); From 70edae6d033d3480afac6c566995d3917d8653a2 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 16 Apr 2018 17:12:37 -0400 Subject: [PATCH 0423/2465] java 9 fix --- .../com/hubspot/jinjava/objects/date/StrftimeFormatterTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/hubspot/jinjava/objects/date/StrftimeFormatterTest.java b/src/test/java/com/hubspot/jinjava/objects/date/StrftimeFormatterTest.java index 15f24c249..dc6d86196 100644 --- a/src/test/java/com/hubspot/jinjava/objects/date/StrftimeFormatterTest.java +++ b/src/test/java/com/hubspot/jinjava/objects/date/StrftimeFormatterTest.java @@ -80,7 +80,7 @@ public void testPaddedMinFmt() { @Test public void testFinnishMonths() { assertThat(StrftimeFormatter.formatter("long").withLocale(Locale.forLanguageTag("fi")).format(d)) - .isEqualTo("6. marraskuuta 2013 klo 14.22.00"); + .startsWith("6. marraskuuta 2013 klo 14.22.00"); } } From 4916b55ce1ef353d4f3e7d882222b00e927ba022 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 16 Apr 2018 17:22:37 -0400 Subject: [PATCH 0424/2465] remove old properties --- pom.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/pom.xml b/pom.xml index 941ffd476..3dc297fef 100644 --- a/pom.xml +++ b/pom.xml @@ -34,8 +34,6 @@ 1.8 - false - 2.17 From 47f28a5df40ffa80c774fc5331393836f769fc10 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 16 Apr 2018 20:33:58 -0400 Subject: [PATCH 0425/2465] add test case and parser --- .../jinjava/el/ext/ExtendedParser.java | 19 +++++++++++-- .../lib/exptest/NegatedExpTestTest.java | 27 +++++++++++++++++++ .../hubspot/jinjava/lib/tag/IfTagTest.java | 2 +- 3 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 src/test/java/com/hubspot/jinjava/lib/exptest/NegatedExpTestTest.java diff --git a/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java b/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java index 742ad9eae..f3cdc2b49 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java @@ -181,7 +181,7 @@ protected AstParameters params(Symbol left, Symbol right) throws ScanException, List l = Collections.emptyList(); AstNode v = expr(false); if (v != null) { - l = new ArrayList(); + l = new ArrayList<>(); l.add(v); while (getToken().getSymbol() == COMMA) { consumeToken(); @@ -363,8 +363,23 @@ protected AstNode value() throws ScanException, ParseException { AstProperty filterProperty = createAstDot(identifier(FILTER_PREFIX + filterName), "filter", true); v = createAstMethod(filterProperty, new AstParameters(filterParams)); // function("filter:" + filterName, new AstParameters(filterParams)); - } while ("|".equals(getToken().getImage())); + } else if ("is".equals(getToken().getImage()) && + "not".equals(lookahead(0).getImage()) && + lookahead(1).getSymbol() == IDENTIFIER) { + consumeToken(); // 'is' + consumeToken(); // 'not' + String exptestName = consumeToken().getImage(); + List exptestParams = Lists.newArrayList(v, interpreter()); + + // optional exptest arg + AstNode arg = expr(false); + if (arg != null) { + exptestParams.add(arg); + } + + AstProperty exptestProperty = createAstDot(identifier(EXPTEST_PREFIX + exptestName), "evaluate", true); + v = createAstMethod(exptestProperty, new AstParameters(exptestParams)); } else if ("is".equals(getToken().getImage()) && lookahead(0).getSymbol() == IDENTIFIER) { consumeToken(); // 'is' String exptestName = consumeToken().getImage(); diff --git a/src/test/java/com/hubspot/jinjava/lib/exptest/NegatedExpTestTest.java b/src/test/java/com/hubspot/jinjava/lib/exptest/NegatedExpTestTest.java new file mode 100644 index 000000000..5e8fccf32 --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/lib/exptest/NegatedExpTestTest.java @@ -0,0 +1,27 @@ +package com.hubspot.jinjava.lib.exptest; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.HashMap; + +import org.junit.Before; +import org.junit.Test; + +import com.hubspot.jinjava.Jinjava; + +public class NegatedExpTestTest { + + private static final String TEMPLATE = "{%% if %s is not %s %%}pass{%% else %%}fail{%% endif %%}"; + + private Jinjava jinjava; + + @Before + public void setup() { + jinjava = new Jinjava(); + } + + @Test + public void itNegatesDefined() { + assertThat(jinjava.render(String.format(TEMPLATE, "blah", "defined"), new HashMap<>())).isEqualTo("pass"); + } +} diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/IfTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/IfTagTest.java index e664a4013..d3a5e1841 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/IfTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/IfTagTest.java @@ -28,7 +28,7 @@ public class IfTagTest { IfTag tag; Jinjava jinjava; - Context context; + private Context context; @Before public void setup() { From e8c983656f3ceb2923daccdc84cd4eb4fb19c96f Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 16 Apr 2018 20:36:12 -0400 Subject: [PATCH 0426/2465] add more blazar memory --- .blazar | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .blazar diff --git a/.blazar b/.blazar new file mode 100644 index 000000000..160bd6e2d --- /dev/null +++ b/.blazar @@ -0,0 +1,2 @@ +buildResources: + memoryMb: 11264 From ce68c1ca8f5d60ef7a051586c555470101fe39de Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 16 Apr 2018 20:39:39 -0400 Subject: [PATCH 0427/2465] even more blazar memory --- .blazar | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.blazar b/.blazar index 160bd6e2d..7d430eb7d 100644 --- a/.blazar +++ b/.blazar @@ -1,2 +1,2 @@ buildResources: - memoryMb: 11264 + memoryMb: 14336 From 720e12f25ca20b2d9d89f69370351d42f65e47a1 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 16 Apr 2018 20:42:47 -0400 Subject: [PATCH 0428/2465] Update .blazar --- .blazar | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.blazar b/.blazar index 7d430eb7d..890e9ce3c 100644 --- a/.blazar +++ b/.blazar @@ -1,2 +1,2 @@ buildResources: - memoryMb: 14336 + memoryMb: 20680 From 7815fb77f5d96468f886e222bcb6872d4288ae86 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 16 Apr 2018 20:58:23 -0400 Subject: [PATCH 0429/2465] correct .blazar.yaml name --- .blazar | 2 -- .blazar.yaml | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 .blazar create mode 100644 .blazar.yaml diff --git a/.blazar b/.blazar deleted file mode 100644 index 890e9ce3c..000000000 --- a/.blazar +++ /dev/null @@ -1,2 +0,0 @@ -buildResources: - memoryMb: 20680 diff --git a/.blazar.yaml b/.blazar.yaml new file mode 100644 index 000000000..5727ecbff --- /dev/null +++ b/.blazar.yaml @@ -0,0 +1,2 @@ +buildResources: + memoryMb: 10240 From 7188a75aa7e7d124025fe64a75173952f65dad19 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 16 Apr 2018 20:58:42 -0400 Subject: [PATCH 0430/2465] Delete .blazar --- .blazar | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .blazar diff --git a/.blazar b/.blazar deleted file mode 100644 index 7d430eb7d..000000000 --- a/.blazar +++ /dev/null @@ -1,2 +0,0 @@ -buildResources: - memoryMb: 14336 From 3a95b22edbaf801349d2f4f722f1f3c0deb49a95 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 17 Apr 2018 10:48:56 -0400 Subject: [PATCH 0431/2465] negate expression tests --- .../java/com/hubspot/jinjava/el/ext/ExtendedParser.java | 2 +- .../java/com/hubspot/jinjava/lib/exptest/ExpTest.java | 4 ++++ .../hubspot/jinjava/lib/exptest/NegatedExpTestTest.java | 8 ++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java b/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java index f3cdc2b49..74c156e20 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java @@ -378,7 +378,7 @@ protected AstNode value() throws ScanException, ParseException { exptestParams.add(arg); } - AstProperty exptestProperty = createAstDot(identifier(EXPTEST_PREFIX + exptestName), "evaluate", true); + AstProperty exptestProperty = createAstDot(identifier(EXPTEST_PREFIX + exptestName), "evaluateNegated", true); v = createAstMethod(exptestProperty, new AstParameters(exptestParams)); } else if ("is".equals(getToken().getImage()) && lookahead(0).getSymbol() == IDENTIFIER) { consumeToken(); // 'is' diff --git a/src/main/java/com/hubspot/jinjava/lib/exptest/ExpTest.java b/src/main/java/com/hubspot/jinjava/lib/exptest/ExpTest.java index 6f2679330..607ec7504 100644 --- a/src/main/java/com/hubspot/jinjava/lib/exptest/ExpTest.java +++ b/src/main/java/com/hubspot/jinjava/lib/exptest/ExpTest.java @@ -18,4 +18,8 @@ public interface ExpTest extends Importable { boolean evaluate(Object var, JinjavaInterpreter interpreter, Object... args); + default boolean evaluateNegated(Object var, JinjavaInterpreter interpreter, Object... args) { + return !evaluate(var, interpreter, args); + } + } diff --git a/src/test/java/com/hubspot/jinjava/lib/exptest/NegatedExpTestTest.java b/src/test/java/com/hubspot/jinjava/lib/exptest/NegatedExpTestTest.java index 5e8fccf32..6ba66c3aa 100644 --- a/src/test/java/com/hubspot/jinjava/lib/exptest/NegatedExpTestTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/exptest/NegatedExpTestTest.java @@ -12,6 +12,7 @@ public class NegatedExpTestTest { private static final String TEMPLATE = "{%% if %s is not %s %%}pass{%% else %%}fail{%% endif %%}"; + private static final String CONTAINING_TEMPLATE = "{%% if %s is not containing %s %%}pass{%% else %%}fail{%% endif %%}"; private Jinjava jinjava; @@ -24,4 +25,11 @@ public void setup() { public void itNegatesDefined() { assertThat(jinjava.render(String.format(TEMPLATE, "blah", "defined"), new HashMap<>())).isEqualTo("pass"); } + + @Test + public void itNegatesContaining() { + assertThat(jinjava.render(String.format(CONTAINING_TEMPLATE, "[1, 2, 3]", "4"), new HashMap<>())).isEqualTo("pass"); + } } + + From d68ff143bbd7af1c5f0edb07f5a28f7901423282 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 17 Apr 2018 11:49:20 -0400 Subject: [PATCH 0432/2465] cleanup and TODO --- .../el/ext/CollectionMembershipOperator.java | 4 ++-- .../com/hubspot/jinjava/el/ext/OrOperator.java | 2 +- .../jinjava/el/ExtendedSyntaxBuilderTest.java | 17 +++++++++++++++-- .../com/hubspot/jinjava/lib/tag/ForTagTest.java | 4 ++-- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/CollectionMembershipOperator.java b/src/main/java/com/hubspot/jinjava/el/ext/CollectionMembershipOperator.java index b3916f792..999832113 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/CollectionMembershipOperator.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/CollectionMembershipOperator.java @@ -22,11 +22,11 @@ protected Object apply(TypeConverter converter, Object o1, Object o2) { } if (CharSequence.class.isAssignableFrom(o2.getClass())) { - return Boolean.valueOf(StringUtils.contains((CharSequence) o2, Objects.toString(o1, ""))); + return StringUtils.contains((CharSequence) o2, Objects.toString(o1, "")); } if (Collection.class.isAssignableFrom(o2.getClass())) { - return Boolean.valueOf(((Collection) o2).contains(o1)); + return ((Collection) o2).contains(o1); } return Boolean.FALSE; diff --git a/src/main/java/com/hubspot/jinjava/el/ext/OrOperator.java b/src/main/java/com/hubspot/jinjava/el/ext/OrOperator.java index aa80ac635..a32f95c8b 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/OrOperator.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/OrOperator.java @@ -11,7 +11,7 @@ public class OrOperator implements Operator { @Override public Object eval(Bindings bindings, ELContext context, AstNode left, AstNode right) { Object leftResult = left.eval(bindings, context); - if (bindings.convert(leftResult, Boolean.class).booleanValue()) { + if (bindings.convert(leftResult, Boolean.class)) { return leftResult; } diff --git a/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java b/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java index 1cae557ef..e24d34af2 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java @@ -24,8 +24,8 @@ @SuppressWarnings("unchecked") public class ExtendedSyntaxBuilderTest { - Context context; - JinjavaInterpreter interpreter; + private Context context; + private JinjavaInterpreter interpreter; @Before public void setup() { @@ -110,6 +110,19 @@ public void objInCollectionOperator() { assertThat(val("12 in [1, 12, 3]")).isEqualTo(true); } + // TODO: support negated collection membership. See CollectionMembershipOperator +// @Test +// public void stringNotInStringOperator() { +// assertThat(val("'foo' not in 'foobar'")).isEqualTo(false); +// assertThat(val("'gg' not in 'foobar'")).isEqualTo(true); +// } + +// @Test +// public void objNotInCollectionOperator() { +// assertThat(val("12 not in [1, 2, 3]")).isEqualTo(true); +// assertThat(val("12 not in [1, 12, 3]")).isEqualTo(false); +// } + @Test public void conditionalExprWithNoElse() { context.put("foo", "bar"); diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java index a4dc0a9e8..57c12dcca 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java @@ -31,7 +31,7 @@ public class ForTagTest { ForTag tag; - Context context; + private Context context; JinjavaInterpreter interpreter; Jinjava jinjava; @@ -169,7 +169,7 @@ public void testForLoopVariablesWithoutSpaces() { } @Test - public void testFoorLoopVariablesWithSpaces() { + public void testForLoopVariablesWithSpaces() { Map context = Maps.newHashMap(); context.put("a", 2); From 08d2a91074ece83236375de198eca535ad1fe0a8 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 17 Apr 2018 13:00:24 -0400 Subject: [PATCH 0433/2465] add control test --- .../com/hubspot/jinjava/lib/exptest/NegatedExpTestTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/hubspot/jinjava/lib/exptest/NegatedExpTestTest.java b/src/test/java/com/hubspot/jinjava/lib/exptest/NegatedExpTestTest.java index 6ba66c3aa..607db38d9 100644 --- a/src/test/java/com/hubspot/jinjava/lib/exptest/NegatedExpTestTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/exptest/NegatedExpTestTest.java @@ -11,7 +11,7 @@ public class NegatedExpTestTest { - private static final String TEMPLATE = "{%% if %s is not %s %%}pass{%% else %%}fail{%% endif %%}"; + private static final String TEMPLATE = "{%% if %s is %s %s %%}pass{%% else %%}fail{%% endif %%}"; private static final String CONTAINING_TEMPLATE = "{%% if %s is not containing %s %%}pass{%% else %%}fail{%% endif %%}"; private Jinjava jinjava; @@ -23,7 +23,8 @@ public void setup() { @Test public void itNegatesDefined() { - assertThat(jinjava.render(String.format(TEMPLATE, "blah", "defined"), new HashMap<>())).isEqualTo("pass"); + assertThat(jinjava.render(String.format(TEMPLATE, "blah", "", "defined"), new HashMap<>())).isEqualTo("fail"); + assertThat(jinjava.render(String.format(TEMPLATE, "blah", "not", "defined"), new HashMap<>())).isEqualTo("pass"); } @Test From 166b97010fc574585ab67a4a92a0408174ae17a8 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 17 Apr 2018 15:19:10 -0400 Subject: [PATCH 0434/2465] correctly report column positions of syntax errors --- .../jinjava/el/ExpressionResolver.java | 8 ++--- .../jinjava/interpret/JinjavaInterpreter.java | 7 ++++ .../jinjava/el/ExtendedSyntaxBuilderTest.java | 32 +++++++++++++------ 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java index 89f3d0b1f..16ede7947 100644 --- a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java @@ -72,21 +72,21 @@ public Object resolveExpression(String expression) { return result; } catch (PropertyNotFoundException e) { - interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.UNKNOWN, ErrorItem.PROPERTY, e.getMessage(), "", interpreter.getLineNumber(), -1, e, + interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.UNKNOWN, ErrorItem.PROPERTY, e.getMessage(), "", interpreter.getLineNumber(), interpreter.getPosition(), e, BasicTemplateErrorCategory.UNKNOWN, ImmutableMap.of("exception", e.getMessage()))); } catch (TreeBuilderException e) { interpreter.addError(TemplateError.fromException(new TemplateSyntaxException(expression, - "Error parsing '" + expression + "': " + StringUtils.substringAfter(e.getMessage(), "': "), interpreter.getLineNumber(), e.getPosition(), e))); + "Error parsing '" + expression + "': " + StringUtils.substringAfter(e.getMessage(), "': "), interpreter.getLineNumber(), interpreter.getPosition() + e.getPosition(), e))); } catch (ELException e) { interpreter.addError(TemplateError.fromException(new TemplateSyntaxException(expression, e.getMessage(), interpreter.getLineNumber(), e))); } catch (DisabledException e) { - interpreter.addError(new TemplateError(ErrorType.FATAL, ErrorReason.DISABLED, ErrorItem.FUNCTION, e.getMessage(), expression, interpreter.getLineNumber(), -1, e)); + interpreter.addError(new TemplateError(ErrorType.FATAL, ErrorReason.DISABLED, ErrorItem.FUNCTION, e.getMessage(), expression, interpreter.getLineNumber(), interpreter.getPosition(), e)); } catch (UnknownTokenException e) { // Re-throw the exception because you only get this when the config failOnUnknownTokens is enabled. throw e; } catch (Exception e) { interpreter.addError(TemplateError.fromException(new InterpretException( - String.format("Error resolving expression [%s]: " + getRootCauseMessage(e), expression), e, interpreter.getLineNumber()))); + String.format("Error resolving expression [%s]: " + getRootCauseMessage(e), expression), e, interpreter.getLineNumber(), interpreter.getPosition()))); } return ""; diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index 1494726e8..7c8a9ab68 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -67,6 +67,7 @@ public class JinjavaInterpreter { private final Random random; private int lineNumber = -1; + private int position = 0; private int scopeDepth = 1; private final List errors = new LinkedList<>(); @@ -217,6 +218,7 @@ public String render(Node root, boolean processExtendRoots) { for (Node node : root.getChildren()) { lineNumber = node.getLineNumber(); + position = node.getStartPosition(); String renderStr = node.getMaster().getImage(); if (context.doesRenderStackContain(renderStr)) { // This is a circular rendering. Stop rendering it here. @@ -393,6 +395,7 @@ public JinjavaConfig getConfig() { */ public Object resolveELExpression(String expression, int lineNumber) { this.lineNumber = lineNumber; + this.position = 0; return expressionResolver.resolveExpression(expression); } @@ -427,6 +430,10 @@ public int getLineNumber() { return lineNumber; } + public int getPosition() { + return position; + } + public void addError(TemplateError templateError) { this.errors.add(templateError.withScopeDepth(scopeDepth)); } diff --git a/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java b/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java index 1cae557ef..14182ce8b 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java @@ -24,8 +24,8 @@ @SuppressWarnings("unchecked") public class ExtendedSyntaxBuilderTest { - Context context; - JinjavaInterpreter interpreter; + private Context context; + private JinjavaInterpreter interpreter; @Before public void setup() { @@ -164,7 +164,7 @@ public void complexMapLiteral() { } @Test - public void itParsesDictWithVariableRefs() throws Exception { + public void itParsesDictWithVariableRefs() { List theList = Lists.newArrayList(1L, 2L, 3L); context.put("the_list", theList); context.put("i_am_seven", 7L); @@ -181,7 +181,7 @@ public void itParsesDictWithVariableRefs() throws Exception { } @Test - public void itReturnsLeftResultForOrExpr() throws Exception { + public void itReturnsLeftResultForOrExpr() { context.put("left", "foo"); context.put("right", "bar"); @@ -189,7 +189,7 @@ public void itReturnsLeftResultForOrExpr() throws Exception { } @Test - public void itReturnsRightResultForOrExpr() throws Exception { + public void itReturnsRightResultForOrExpr() { context.put("right", "bar"); assertThat(val("left or right")).isEqualTo("bar"); @@ -205,7 +205,7 @@ private String fixture(String name) { } @Test - public void testParseExp() throws Exception { + public void testParseExp() { context.put("foo", "fff"); context.put("a", "aaa"); context.put("b", "bbb"); @@ -214,7 +214,7 @@ public void testParseExp() throws Exception { } @Test - public void listRangeSyntax() throws Exception { + public void listRangeSyntax() { List theList = Lists.newArrayList(1, 2, 3, 4, 5); context.put("mylist", theList); assertThat(val("mylist[0:3]")).isEqualTo(Lists.newArrayList(1, 2, 3)); @@ -223,7 +223,7 @@ public void listRangeSyntax() throws Exception { } @Test - public void invalidNestedAssignmentExpr() throws Exception { + public void invalidNestedAssignmentExpr() { assertThat(val("content.template_path = 'Custom/Email/Responsive/testing.html'")).isEqualTo(""); assertThat(interpreter.getErrors()).isNotEmpty(); assertThat(interpreter.getErrors().get(0).getReason()).isEqualTo(ErrorReason.SYNTAX_ERROR); @@ -231,7 +231,7 @@ public void invalidNestedAssignmentExpr() throws Exception { } @Test - public void invalidIdentifierAssignmentExpr() throws Exception { + public void invalidIdentifierAssignmentExpr() { assertThat(val("content = 'Custom/Email/Responsive/testing.html'")).isEqualTo(""); assertThat(interpreter.getErrors()).isNotEmpty(); assertThat(interpreter.getErrors().get(0).getReason()).isEqualTo(ErrorReason.SYNTAX_ERROR); @@ -239,12 +239,24 @@ public void invalidIdentifierAssignmentExpr() throws Exception { } @Test - public void invalidPipeOperatorExpr() throws Exception { + public void invalidPipeOperatorExpr() { assertThat(val("topics|1")).isEqualTo(""); assertThat(interpreter.getErrors()).isNotEmpty(); assertThat(interpreter.getErrors().get(0).getReason()).isEqualTo(ErrorReason.SYNTAX_ERROR); } + @Test + public void itReturnsCorrectSyntaxErrorPositions() { + assertThat(interpreter.render("hi {{ missing thing }}{{ missing thing }}\nI am {{ blah blabbity }} too")).isEqualTo("hi \nI am too"); + assertThat(interpreter.getErrors().size()).isEqualTo(3); + assertThat(interpreter.getErrors().get(0).getLineno()).isEqualTo(1); + assertThat(interpreter.getErrors().get(0).getStartPosition()).isEqualTo(14); + assertThat(interpreter.getErrors().get(1).getLineno()).isEqualTo(1); + assertThat(interpreter.getErrors().get(1).getStartPosition()).isEqualTo(33); + assertThat(interpreter.getErrors().get(2).getLineno()).isEqualTo(2); + assertThat(interpreter.getErrors().get(2).getStartPosition()).isEqualTo(13); + } + private Object val(String expr) { return interpreter.resolveELExpression(expr, -1); } From 4e6a325107e7f3a9e5480f1cef8b041e3d83a7ff Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 17 Apr 2018 15:27:40 -0400 Subject: [PATCH 0435/2465] do not reset position when resolving one expression --- .../java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index 7c8a9ab68..3ff3291ae 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -395,8 +395,6 @@ public JinjavaConfig getConfig() { */ public Object resolveELExpression(String expression, int lineNumber) { this.lineNumber = lineNumber; - this.position = 0; - return expressionResolver.resolveExpression(expression); } From e65ee0bd5ca1658a1403d8e3c3990402a09ec962 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 17 Apr 2018 16:33:29 -0400 Subject: [PATCH 0436/2465] preserve case of unknown tokens for error reporting --- .../interpret/UnknownTagException.java | 16 +++++++++---- .../com/hubspot/jinjava/tree/TreeParser.java | 5 ++-- .../hubspot/jinjava/tree/parse/TagToken.java | 10 ++++++-- .../jinjava/interpret/TemplateErrorTest.java | 23 +++++++++++++++++-- .../jinjava/tree/FailOnUnknownTokensTest.java | 10 ++++---- 5 files changed, 47 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/UnknownTagException.java b/src/main/java/com/hubspot/jinjava/interpret/UnknownTagException.java index 2bdbaac42..ff004c06d 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/UnknownTagException.java +++ b/src/main/java/com/hubspot/jinjava/interpret/UnknownTagException.java @@ -6,24 +6,32 @@ public class UnknownTagException extends TemplateSyntaxException { private static final long serialVersionUID = 1L; private final String tag; - private final String defintion; + private final String definition; public UnknownTagException(String tag, String definition, int lineNumber, int startPosition) { super(definition, "Unknown tag: " + tag, lineNumber, startPosition); this.tag = tag; - this.defintion = definition; + this.definition = definition; } public UnknownTagException(TagToken tagToken) { - this(tagToken.getTagName(), tagToken.getImage(), tagToken.getLineNumber(), tagToken.getStartPosition()); + this(tagToken.getRawTagName(), tagToken.getImage(), tagToken.getLineNumber(), tagToken.getStartPosition()); } public String getTag() { return tag; } + /** + * @deprecated use correct spelling + */ + @Deprecated public String getDefintion() { - return defintion; + return definition; + } + + public String getDefinition() { + return definition; } } diff --git a/src/main/java/com/hubspot/jinjava/tree/TreeParser.java b/src/main/java/com/hubspot/jinjava/tree/TreeParser.java index f861e6209..d77abe1b5 100644 --- a/src/main/java/com/hubspot/jinjava/tree/TreeParser.java +++ b/src/main/java/com/hubspot/jinjava/tree/TreeParser.java @@ -121,8 +121,7 @@ private Node text(TextToken textToken) { final Node lastSibling = getLastSibling(); // if last sibling was a tag and has rightTrimAfterEnd, strip whitespace - if (lastSibling != null - && lastSibling instanceof TagNode + if (lastSibling instanceof TagNode && lastSibling.getMaster().isRightTrimAfterEnd()) { textToken.setLeftTrim(true); } @@ -167,7 +166,7 @@ private Node tag(TagToken tagToken) { // if a tag has left trim, mark the last sibling to trim right whitespace if (tagToken.isLeftTrim()) { final Node lastSibling = getLastSibling(); - if (lastSibling != null && lastSibling instanceof TextNode) { + if (lastSibling instanceof TextNode) { lastSibling.getMaster().setRightTrim(true); } } diff --git a/src/main/java/com/hubspot/jinjava/tree/parse/TagToken.java b/src/main/java/com/hubspot/jinjava/tree/parse/TagToken.java index d4f0edbfe..166b3fe52 100644 --- a/src/main/java/com/hubspot/jinjava/tree/parse/TagToken.java +++ b/src/main/java/com/hubspot/jinjava/tree/parse/TagToken.java @@ -25,6 +25,7 @@ public class TagToken extends Token { private static final long serialVersionUID = -4927751270481832992L; private String tagName; + private String rawTagName; private String helpers; public TagToken(String image, int lineNumber, int startPosition) { @@ -69,12 +70,17 @@ else if (nameStart != -1 && !Character.isJavaIdentifierPart(c)) { } if (pos < content.length()) { - tagName = content.substring(nameStart, pos).toLowerCase(); + rawTagName = content.substring(nameStart, pos); helpers = content.substring(pos); } else { - tagName = content.toLowerCase().trim(); + rawTagName = content.trim(); helpers = ""; } + tagName = rawTagName.toLowerCase(); + } + + public String getRawTagName() { + return rawTagName; } public String getTagName() { diff --git a/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java b/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java index 010faf57f..3a5afeb10 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java @@ -2,12 +2,26 @@ import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Before; import org.junit.Test; import com.google.common.collect.ImmutableMap; +import com.hubspot.jinjava.Jinjava; public class TemplateErrorTest { + private Context context; + private JinjavaInterpreter interpreter; + + @Before + public void setup() { + interpreter = new Jinjava().newInterpreter(); + JinjavaInterpreter.pushCurrent(interpreter); + + context = interpreter.getContext(); + } + + @Test public void itShowsFriendlyNameOfBaseObjectForPropNotFound() { TemplateError e = TemplateError.fromUnknownProperty(new Object(), "foo", 123, 4); @@ -22,8 +36,8 @@ public void itUsesOverloadedToStringForBaseObject() { @Test public void itShowsFieldNameForUnknownTagError() { - TemplateError e = TemplateError.fromException(new UnknownTagException("unknown", "{% unknown() %}", 11, 3)); - assertThat(e.getFieldName()).isEqualTo("unknown"); + TemplateError e = TemplateError.fromException(new UnknownTagException("unKnown", "{% unKnown() %}", 11, 3)); + assertThat(e.getFieldName()).isEqualTo("unKnown"); } @Test @@ -32,4 +46,9 @@ public void itShowsFieldNameForSyntaxError() { assertThat(e.getFieldName()).isEqualTo("da codez"); } + @Test + public void itRetainsFieldNameCaseForUnknownToken() { + interpreter.render("{% unKnown() %}"); + assertThat(interpreter.getErrors().get(0).getFieldName()).isEqualTo("unKnown"); + } } diff --git a/src/test/java/com/hubspot/jinjava/tree/FailOnUnknownTokensTest.java b/src/test/java/com/hubspot/jinjava/tree/FailOnUnknownTokensTest.java index 909a865d6..0ae9e5a7a 100644 --- a/src/test/java/com/hubspot/jinjava/tree/FailOnUnknownTokensTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/FailOnUnknownTokensTest.java @@ -1,7 +1,6 @@ package com.hubspot.jinjava.tree; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.*; import java.util.HashMap; import java.util.Map; @@ -17,25 +16,24 @@ public class FailOnUnknownTokensTest { private static Jinjava jinjava; @Before - public void setUp() throws Exception { + public void setUp() { JinjavaConfig.Builder builder = JinjavaConfig.newBuilder(); builder.withFailOnUnknownTokens(true); JinjavaConfig config = builder.build(); jinjava = new Jinjava(config); - } @Test(expected = FatalTemplateErrorsException.class) public void itThrowsExceptionOnUnknownToken() { - Map context = new HashMap(); + Map context = new HashMap<>(); context.put("token1", "test"); String template = "hello {{ token1 }} and {{ token2 }}"; - String str = jinjava.render(template, context); + jinjava.render(template, context); } @Test public void itReplaceTokensWithoutException() { - Map context = new HashMap(); + Map context = new HashMap<>(); context.put("token1", "test"); context.put("token2", "test1"); String template = "hello {{ token1 }} and {{ token2 }}"; From d6e456ef1060453a09821ce5de8c3469c4d446eb Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 17 Apr 2018 22:04:56 -0400 Subject: [PATCH 0437/2465] populate fieldname for TemplateSyntaxException --- .../java/com/hubspot/jinjava/Jinjava.java | 4 ++++ .../com/hubspot/jinjava/lib/tag/ForTag.java | 2 +- .../jinjava/interpret/TemplateErrorTest.java | 15 ++++++++----- .../hubspot/jinjava/lib/tag/ForTagTest.java | 21 +++++++++---------- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/Jinjava.java b/src/main/java/com/hubspot/jinjava/Jinjava.java index 3e3265136..619dc2431 100644 --- a/src/main/java/com/hubspot/jinjava/Jinjava.java +++ b/src/main/java/com/hubspot/jinjava/Jinjava.java @@ -33,6 +33,7 @@ import com.hubspot.jinjava.interpret.RenderResult; import com.hubspot.jinjava.interpret.TemplateError; import com.hubspot.jinjava.interpret.TemplateError.ErrorType; +import com.hubspot.jinjava.interpret.TemplateSyntaxException; import com.hubspot.jinjava.loader.ClasspathResourceLocator; import com.hubspot.jinjava.loader.ResourceLocator; @@ -198,6 +199,9 @@ public RenderResult renderForResult(String template, Map bindings, Ji String result = interpreter.render(template); return new RenderResult(result, interpreter.getContext(), interpreter.getErrors()); } catch (InterpretException e) { + if (e instanceof TemplateSyntaxException) { + return new RenderResult(TemplateError.fromException((TemplateSyntaxException) e), interpreter.getContext(), interpreter.getErrors()); + } return new RenderResult(TemplateError.fromSyntaxError(e), interpreter.getContext(), interpreter.getErrors()); } catch (Exception e) { return new RenderResult(TemplateError.fromException(e), interpreter.getContext(), interpreter.getErrors()); diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java index 4b2cb4245..d04301aef 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java @@ -116,7 +116,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { } if (inPos >= helper.size()) { - throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'for' expects valid 'in' clause, got: " + tagNode.getHelpers(), tagNode.getLineNumber(), tagNode.getStartPosition()); + throw new TemplateSyntaxException(tagNode.getHelpers().trim(), "Tag 'for' expects valid 'in' clause, got: " + tagNode.getHelpers(), tagNode.getLineNumber(), tagNode.getStartPosition()); } String loopExpr = StringUtils.join(helper.subList(inPos + 1, helper.size()), ","); diff --git a/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java b/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java index 3a5afeb10..1b1734399 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java @@ -10,18 +10,16 @@ public class TemplateErrorTest { - private Context context; private JinjavaInterpreter interpreter; + private Jinjava jinjava; @Before public void setup() { - interpreter = new Jinjava().newInterpreter(); + jinjava = new Jinjava(); + interpreter = jinjava.newInterpreter(); JinjavaInterpreter.pushCurrent(interpreter); - - context = interpreter.getContext(); } - @Test public void itShowsFriendlyNameOfBaseObjectForPropNotFound() { TemplateError e = TemplateError.fromUnknownProperty(new Object(), "foo", 123, 4); @@ -51,4 +49,11 @@ public void itRetainsFieldNameCaseForUnknownToken() { interpreter.render("{% unKnown() %}"); assertThat(interpreter.getErrors().get(0).getFieldName()).isEqualTo("unKnown"); } + + @Test + public void itSetsFieldNameCaseForSyntaxErrorInFor() { + RenderResult renderResult = jinjava.renderForResult("{% for item inna navigation %}{% endfor %}", ImmutableMap.of()); + assertThat(renderResult.getErrors().get(0).getFieldName()).isEqualTo("item inna navigation"); + } + } diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java index 57c12dcca..7105bc4b4 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java @@ -15,7 +15,6 @@ import org.junit.Test; import com.google.common.base.Splitter; -import com.google.common.base.Throwables; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -45,7 +44,7 @@ public void setup() { } @Test - public void forLoopUsingLoopLastVar() throws Exception { + public void forLoopUsingLoopLastVar() { context.put("the_list", Lists.newArrayList(1L, 2L, 3L, 7L)); TagNode tagNode = (TagNode) fixture("loop-last-var"); Document dom = Jsoup.parseBodyFragment(tag.interpret(tagNode, interpreter)); @@ -54,7 +53,7 @@ public void forLoopUsingLoopLastVar() throws Exception { } @Test - public void forLoopUsingScalarValue() throws Exception { + public void forLoopUsingScalarValue() { context.put("the_list", 999L); TagNode tagNode = (TagNode) fixture("loop-with-scalar"); String output = tag.interpret(tagNode, interpreter); @@ -62,7 +61,7 @@ public void forLoopUsingScalarValue() throws Exception { } @Test - public void forLoopNestedFor() throws Exception { + public void forLoopNestedFor() { TagNode tagNode = (TagNode) fixture("nested-fors"); assertThat(Splitter.on("\n").trimResults().omitEmptyStrings().split( tag.interpret(tagNode, interpreter))) @@ -70,7 +69,7 @@ public void forLoopNestedFor() throws Exception { } @Test - public void forLoopMultipleLoopVars() throws Exception { + public void forLoopMultipleLoopVars() { Map dict = Maps.newHashMap(); dict.put("foo", "one"); dict.put("bar", 2L); @@ -83,7 +82,7 @@ public void forLoopMultipleLoopVars() throws Exception { } @Test - public void forLoopMultipleLoopVarsArbitraryNames() throws Exception { + public void forLoopMultipleLoopVarsArbitraryNames() { Map dict = ImmutableMap.of( "grand", "ol'", "adserving", "team"); @@ -99,13 +98,13 @@ public void forLoopMultipleLoopVarsArbitraryNames() throws Exception { } @Test - public void forLoopLiteralLoopExpr() throws Exception { + public void forLoopLiteralLoopExpr() { TagNode tagNode = (TagNode) fixture("literal-loop-expr"); assertThat(tag.interpret(tagNode, interpreter)).isEqualTo("012345"); } @Test - public void forLoopWithNestedCycle() throws Exception { + public void forLoopWithNestedCycle() { context.put("cycle1", "odd"); context.put("cycle2", "even"); @@ -115,13 +114,13 @@ public void forLoopWithNestedCycle() throws Exception { } @Test - public void forLoopIndexVar() throws Exception { + public void forLoopIndexVar() { TagNode tagNode = (TagNode) fixture("loop-index-var"); assertThat(tag.interpret(tagNode, interpreter)).isEqualTo("012345"); } @Test - public void forLoopSupportsAllLoopVarsInHublDocs() throws Exception { + public void forLoopSupportsAllLoopVarsInHublDocs() { TagNode tagNode = (TagNode) fixture("hubl-docs-loop-vars"); Document dom = Jsoup.parseBodyFragment(tag.interpret(tagNode, interpreter)); @@ -200,7 +199,7 @@ private Node fixture(String name) { Resources.getResource(String.format("tags/fortag/%s.jinja", name)), StandardCharsets.UTF_8)) .buildTree().getChildren().getFirst(); } catch (IOException e) { - throw Throwables.propagate(e); + throw new RuntimeException(e); } } From 0b5c62eb231fd77f9fa70e0d4d5c96ceca3aeb68 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Wed, 18 Apr 2018 09:55:53 -0400 Subject: [PATCH 0438/2465] update release notes --- CHANGES.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 2e8234434..66baf467c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,14 @@ # Jinjava Releases # +### 2018-04-18 Version 2.4.1 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.1%7Cjar)) ### + +* [Use `AdvancedFilter` for `selectattr` filter](https://github.com/HubSpot/jinjava/pull/183) +* [Java 9 Support](https://github.com/HubSpot/jinjava/pull/186) +* [Adds negation for expressions ("is not") ](https://github.com/HubSpot/jinjava/pull/187) +* [Fix column numbers in syntax errors](https://github.com/HubSpot/jinjava/pull/188) +* [When reporting errors, preserve casing](https://github.com/HubSpot/jinjava/pull/189) +* [Populate `fieldName` in `TemplateSyntaxException`s](https://github.com/HubSpot/jinjava/pull/190) + ### 2018-02-26 Version 2.4.0 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.0%7Cjar)) ### * [Make int/float parsing locale aware](https://github.com/HubSpot/jinjava/pull/178) From 85a630aa96211216dc39db7f6887092c33b141e4 Mon Sep 17 00:00:00 2001 From: Padraig Farrell Date: Wed, 18 Apr 2018 17:04:15 +0100 Subject: [PATCH 0439/2465] Make int and float parsing more strict --- .../jinjava/lib/filter/FloatFilter.java | 8 +++- .../hubspot/jinjava/lib/filter/IntFilter.java | 10 ++++- .../jinjava/lib/filter/FloatFilterTest.java | 34 ++++++++++++--- .../jinjava/lib/filter/IntFilterTest.java | 42 ++++++++++++++++--- 4 files changed, 80 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/FloatFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/FloatFilter.java index f5bf4e7db..7086a1058 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/FloatFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/FloatFilter.java @@ -1,6 +1,7 @@ package com.hubspot.jinjava.lib.filter; import java.text.NumberFormat; +import java.text.ParsePosition; import java.util.Locale; import org.apache.commons.lang3.math.NumberUtils; @@ -47,14 +48,19 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args) return ((Number) var).floatValue(); } + String input = var.toString(); Locale locale = interpreter.getConfig().getLocale(); NumberFormat numberFormat = NumberFormat.getNumberInstance(locale); + ParsePosition pp = new ParsePosition(0); float result; try { - result = numberFormat.parse(var.toString()).floatValue(); + result = numberFormat.parse(input, pp).floatValue(); } catch (Exception e) { result = defaultVal; } + if (pp.getErrorIndex() != -1 || pp.getIndex() != input.length()) { + result = defaultVal; + } return result; } diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/IntFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/IntFilter.java index 49e55c646..eb1f3a2fd 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/IntFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/IntFilter.java @@ -1,6 +1,8 @@ package com.hubspot.jinjava.lib.filter; +import java.text.DecimalFormatSymbols; import java.text.NumberFormat; +import java.text.ParsePosition; import java.util.Locale; import org.apache.commons.lang3.math.NumberUtils; @@ -49,15 +51,19 @@ public Object filter(Object var, JinjavaInterpreter interpreter, return ((Number) var).intValue(); } + String input = var.toString().trim(); Locale locale = interpreter.getConfig().getLocale(); NumberFormat numberFormat = NumberFormat.getNumberInstance(locale); - numberFormat.setParseIntegerOnly(true); + ParsePosition pp = new ParsePosition(0); int result; try { - result = numberFormat.parse(var.toString()).intValue(); + result = numberFormat.parse(input, pp).intValue(); } catch (Exception e) { result = defaultVal; } + if (pp.getErrorIndex() != -1 || pp.getIndex() != input.length()) { + result = defaultVal; + } return result; } diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/FloatFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/FloatFilterTest.java index df9503db0..0b2d669f7 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/FloatFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/FloatFilterTest.java @@ -64,14 +64,39 @@ public void itReturnsVarAsFloat() { assertThat(filter.filter("123.45", interpreter)).isEqualTo(123.45f); } + @Test + public void itReturnsVarWithTrailingPercentAsDefault() { + assertThat(filter.filter("123%", interpreter)).isEqualTo(0.0f); + } + + @Test + public void itReturnsVarWithLeadingLettersAsDefault() { + assertThat(filter.filter("abc123", interpreter)).isEqualTo(0.0f); + } + + @Test + public void itReturnsVarWithTrailingLettersAsDefault() { + assertThat(filter.filter("123abc", interpreter)).isEqualTo(0.0f); + } + + @Test + public void itReturnsVarWithLeadingCurrencySymbolAsDefault() { + assertThat(filter.filter("$123", interpreter)).isEqualTo(0.0f); + } + + @Test + public void itReturnsVarWithTrailingCurrencySymbolAsDefault() { + assertThat(filter.filter("123$", interpreter)).isEqualTo(0.0f); + } + @Test public void itInterpretsUsCommasAndPeriodsWithUsLocale() { assertThat(filter.filter("123,123.45", interpreter)).isEqualTo(123123.45f); } @Test - public void itInterpretsFrenchCommasAndPeriodsWithUsLocale() { - assertThat(filter.filter("123.123,45", interpreter)).isEqualTo(123.123f); + public void itDoesntInterpretFrenchCommasAndPeriodsWithUsLocale() { + assertThat(filter.filter("123.123,45", interpreter)).isEqualTo(0.0f); } @Test @@ -80,9 +105,9 @@ public void itReturnsDefaultWhenUnableToParseVar() { } @Test - public void itInterpretsUsCommasAndPeriodsWithFrenchLocale() { + public void itDoesntInterpretUsCommasAndPeriodsWithFrenchLocale() { interpreter = new Jinjava(FRENCH_LOCALE_CONFIG).newInterpreter(); - assertThat(filter.filter("123,123.12", interpreter)).isEqualTo(123.123f); + assertThat(filter.filter("123,123.12", interpreter)).isEqualTo(0.0f); } @Test @@ -90,5 +115,4 @@ public void itInterpretsFrenchCommasAndPeriodsWithFrenchLocale() { interpreter = new Jinjava(FRENCH_LOCALE_CONFIG).newInterpreter(); assertThat(filter.filter("123\u00A0123,45", interpreter)).isEqualTo(123123.45f); } - } diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/IntFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/IntFilterTest.java index e0a3874f1..48a87a7cd 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/IntFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/IntFilterTest.java @@ -6,6 +6,7 @@ import java.time.ZoneOffset; import java.util.Locale; +import org.apache.commons.lang3.math.NumberUtils; import org.junit.Before; import org.junit.Test; @@ -50,13 +51,43 @@ public void itReturnsVarAsInt() { } @Test - public void itInterpretsUsCommasAndPeriodsWithUsLocale() { + public void itReturnsVarWithLeadingPercentAsDefault() { + assertThat(filter.filter("%60", interpreter)).isEqualTo(0); + } + + @Test + public void itReturnsVarWithTrailingPercentAsDefault() { + assertThat(filter.filter("60%", interpreter)).isEqualTo(0); + } + + @Test + public void itReturnsVarWithLeadingLettersAsDefault() { + assertThat(filter.filter("abc60", interpreter)).isEqualTo(0); + } + + @Test + public void itReturnsVarWithTrailingLettersAsDefault() { + assertThat(filter.filter("60abc", interpreter)).isEqualTo(0); + } + + @Test + public void itReturnsVarWithLeadingCurrencySymbolAsDefault() { + assertThat(filter.filter("$60", interpreter)).isEqualTo(0); + } + + @Test + public void itReturnsVarWithTrailingCurrencySymbolAsDefault() { + assertThat(filter.filter("60$", interpreter)).isEqualTo(0); + } + + @Test + public void itDoesntInterpretsUsCommasAndPeriodsWithUsLocale() { assertThat(filter.filter("123,123.12", interpreter)).isEqualTo(123123); } @Test - public void itInterpretsFrenchCommasAndPeriodsWithUsLocale() { - assertThat(filter.filter("123.123,12", interpreter)).isEqualTo(123); + public void itDoesntInterpreFrenchCommasAndPeriodsWithUsLocale() { + assertThat(filter.filter("123.123,12", interpreter)).isEqualTo(0); } @Test @@ -65,9 +96,9 @@ public void itReturnsDefaultWhenUnableToParseVar() { } @Test - public void itInterpretsUsCommasAndPeriodsWithFrenchLocale() { + public void itDoesntInterpretUsCommasAndPeriodsWithFrenchLocale() { interpreter = new Jinjava(FRENCH_LOCALE_CONFIG).newInterpreter(); - assertThat(filter.filter("123,123.12", interpreter)).isEqualTo(123); + assertThat(filter.filter("123,123.12", interpreter)).isEqualTo(0); } @Test @@ -75,5 +106,4 @@ public void itInterpretsFrenchCommasAndPeriodsWithFrenchLocale() { interpreter = new Jinjava(FRENCH_LOCALE_CONFIG).newInterpreter(); assertThat(filter.filter("123\u00A0123,12", interpreter)).isEqualTo(123123); } - } From a52368f1960404ba88752ee6f6ee3b3c9ebac208 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Wed, 18 Apr 2018 13:35:21 -0400 Subject: [PATCH 0440/2465] replace position in error message too --- .../jinjava/el/ExpressionResolver.java | 5 +- .../jinjava/interpret/RenderResult.java | 2 +- .../jinjava/el/ExpressionResolverTest.java | 54 +++++++++---------- .../jinjava/el/ExtendedSyntaxBuilderTest.java | 3 ++ 4 files changed, 35 insertions(+), 29 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java index 16ede7947..c9782f921 100644 --- a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java @@ -75,8 +75,11 @@ public Object resolveExpression(String expression) { interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.UNKNOWN, ErrorItem.PROPERTY, e.getMessage(), "", interpreter.getLineNumber(), interpreter.getPosition(), e, BasicTemplateErrorCategory.UNKNOWN, ImmutableMap.of("exception", e.getMessage()))); } catch (TreeBuilderException e) { + int position = interpreter.getPosition() + e.getPosition(); + // replacing the position in the string like this isn't great, but JUEL's parser does not allow passing in a starting position + String errorMessage = StringUtils.substringAfter(e.getMessage(), "': ").replaceFirst("position [0-9]+", "position " + position); interpreter.addError(TemplateError.fromException(new TemplateSyntaxException(expression, - "Error parsing '" + expression + "': " + StringUtils.substringAfter(e.getMessage(), "': "), interpreter.getLineNumber(), interpreter.getPosition() + e.getPosition(), e))); + "Error parsing '" + expression + "': " + errorMessage, interpreter.getLineNumber(), position, e))); } catch (ELException e) { interpreter.addError(TemplateError.fromException(new TemplateSyntaxException(expression, e.getMessage(), interpreter.getLineNumber(), e))); } catch (DisabledException e) { diff --git a/src/main/java/com/hubspot/jinjava/interpret/RenderResult.java b/src/main/java/com/hubspot/jinjava/interpret/RenderResult.java index 09ec31078..029565fce 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/RenderResult.java +++ b/src/main/java/com/hubspot/jinjava/interpret/RenderResult.java @@ -20,7 +20,7 @@ public RenderResult(String output, Context context, List errors) public RenderResult(TemplateError fromException, Context context, List errors) { this.output = ""; this.context = context; - this.errors = ImmutableList. builder().add(fromException).addAll(errors).build(); + this.errors = ImmutableList.builder().add(fromException).addAll(errors).build(); } public RenderResult(String result) { diff --git a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java index c1bc8bf1a..3edd003ec 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java @@ -47,28 +47,28 @@ public void setup() { } @Test - public void itResolvesListLiterals() throws Exception { + public void itResolvesListLiterals() { Object val = interpreter.resolveELExpression("['0.5','50']", -1); List list = (List) val; assertThat(list).containsExactly("0.5", "50"); } @Test - public void itResolvesImmutableListLiterals() throws Exception { + public void itResolvesImmutableListLiterals() { Object val = interpreter.resolveELExpression("('0.5','50')", -1); List list = (List) val; assertThat(list).containsExactly("0.5", "50"); } @Test(expected = UnsupportedOperationException.class) - public void testTuplesAreImmutable() throws Exception { + public void testTuplesAreImmutable() { Object val = interpreter.resolveELExpression("('0.5','50')", -1); List list = (List) val; list.add("foo"); } @Test - public void itCanCompareStrings() throws Exception { + public void itCanCompareStrings() { context.put("foo", "white"); assertThat(interpreter.resolveELExpression("'2013-12-08 16:00:00+00:00' > '2013-12-08 13:00:00+00:00'", -1)).isEqualTo(Boolean.TRUE); @@ -76,7 +76,7 @@ public void itCanCompareStrings() throws Exception { } @Test - public void itResolvesUntrimmedExprs() throws Exception { + public void itResolvesUntrimmedExprs() { context.put("foo", "bar"); Object val = interpreter.resolveELExpression(" foo ", -1); assertThat(val).isEqualTo("bar"); @@ -84,7 +84,7 @@ public void itResolvesUntrimmedExprs() throws Exception { } @Test - public void itResolvesMathVals() throws Exception { + public void itResolvesMathVals() { context.put("i_am_seven", 7L); Object val = interpreter.resolveELExpression("(i_am_seven * 2 + 1)/3", -1); assertThat(val).isEqualTo(5.0); @@ -92,14 +92,14 @@ public void itResolvesMathVals() throws Exception { } @Test - public void itResolvesListVal() throws Exception { + public void itResolvesListVal() { context.put("thelist", Lists.newArrayList(1L, 2L, 3L)); Object val = interpreter.resolveELExpression("thelist[1]", -1); assertThat(val).isEqualTo(2L); } @Test - public void itResolvesDictValWithBracket() throws Exception { + public void itResolvesDictValWithBracket() { Map dict = Maps.newHashMap(); dict.put("foo", "bar"); context.put("thedict", dict); @@ -110,7 +110,7 @@ public void itResolvesDictValWithBracket() throws Exception { } @Test - public void itResolvesDictValWithDotParam() throws Exception { + public void itResolvesDictValWithDotParam() { Map dict = Maps.newHashMap(); dict.put("foo", "bar"); context.put("thedict", dict); @@ -121,7 +121,7 @@ public void itResolvesDictValWithDotParam() throws Exception { } @Test - public void itResolvesMapValOnCustomObject() throws Exception { + public void itResolvesMapValOnCustomObject() { MyCustomMap dict = new MyCustomMap(); context.put("thedict", dict); @@ -136,7 +136,7 @@ public void itResolvesMapValOnCustomObject() throws Exception { } @Test - public void itResolvesOtherMethodsOnCustomMapObject() throws Exception { + public void itResolvesOtherMethodsOnCustomMapObject() { MyCustomMap dict = new MyCustomMap(); context.put("thedict", dict); @@ -217,7 +217,7 @@ public Set> entrySet() { } @Test - public void itResolvesInnerDictVal() throws Exception { + public void itResolvesInnerDictVal() { Map dict = Maps.newHashMap(); Map inner = Maps.newHashMap(); inner.put("test", "val"); @@ -229,7 +229,7 @@ public void itResolvesInnerDictVal() throws Exception { } @Test - public void itResolvesInnerListVal() throws Exception { + public void itResolvesInnerListVal() { Map dict = Maps.newHashMap(); List inner = Lists.newArrayList("val"); dict.put("inner", inner); @@ -257,14 +257,14 @@ public int getTotalCount() { } @Test - public void itRecordsFilterNames() throws Exception { + public void itRecordsFilterNames() { Object val = interpreter.resolveELExpression("2.3 | round", -1); assertThat(val).isEqualTo(new BigDecimal(2)); assertThat(interpreter.getContext().wasValueResolved("filter:round")).isTrue(); } @Test - public void callCustomListProperty() throws Exception { + public void callCustomListProperty() { List myList = new MyCustomList<>(Lists.newArrayList(1, 2, 3, 4)); context.put("mylist", myList); @@ -273,7 +273,7 @@ public void callCustomListProperty() throws Exception { } @Test - public void complexInWithOrCondition() throws Exception { + public void complexInWithOrCondition() { context.put("foo", "this is
    something"); context.put("bar", "this is
    something"); @@ -283,7 +283,7 @@ public void complexInWithOrCondition() throws Exception { } @Test - public void unknownProperty() throws Exception { + public void unknownProperty() { interpreter.resolveELExpression("foo", 23); assertThat(interpreter.getErrors()).isEmpty(); @@ -300,7 +300,7 @@ public void unknownProperty() throws Exception { } @Test - public void syntaxError() throws Exception { + public void syntaxError() { interpreter.resolveELExpression("(*&W", 123); assertThat(interpreter.getErrors()).hasSize(1); @@ -311,7 +311,7 @@ public void syntaxError() throws Exception { } @Test - public void itWrapsDates() throws Exception { + public void itWrapsDates() { context.put("myobj", new MyClass(new Date(0))); Object result = interpreter.resolveELExpression("myobj.date", -1); assertThat(result).isInstanceOf(PyishDate.class); @@ -319,7 +319,7 @@ public void itWrapsDates() throws Exception { } @Test - public void blackListedProperties() throws Exception { + public void blackListedProperties() { context.put("myobj", new MyClass(new Date(0))); interpreter.resolveELExpression("myobj.class.methods[0]", -1); @@ -331,7 +331,7 @@ public void blackListedProperties() throws Exception { } @Test - public void blackListedMethods() throws Exception { + public void blackListedMethods() { context.put("myobj", new MyClass(new Date(0))); interpreter.resolveELExpression("myobj.wait()", -1); @@ -341,7 +341,7 @@ public void blackListedMethods() throws Exception { } @Test - public void itBlocksDisabledTags() throws Exception { + public void itBlocksDisabledTags() { Map> disabled = ImmutableMap.of(Context.Library.TAG, ImmutableSet.of("raw")); assertThat(interpreter.render("{% raw %}foo{% endraw %}")).isEqualTo("foo"); @@ -357,7 +357,7 @@ public void itBlocksDisabledTags() throws Exception { } @Test - public void itBlocksDisabledTagsInIncludes() throws Exception { + public void itBlocksDisabledTagsInIncludes() { final String jinja = "top {% include \"tags/includetag/raw.html\" %}"; @@ -374,7 +374,7 @@ public void itBlocksDisabledTagsInIncludes() throws Exception { } @Test - public void itBlocksDisabledFilters() throws Exception { + public void itBlocksDisabledFilters() { Map> disabled = ImmutableMap.of(Context.Library.FILTER, ImmutableSet.of("truncate")); assertThat(interpreter.resolveELExpression("\"hey\"|truncate(2)", -1)).isEqualTo("h..."); @@ -389,7 +389,7 @@ public void itBlocksDisabledFilters() throws Exception { } @Test - public void itBlocksDisabledFunctions() throws Exception { + public void itBlocksDisabledFunctions() { Map> disabled = ImmutableMap.of(Library.FUNCTION, ImmutableSet.of(":range")); @@ -409,7 +409,7 @@ public void itBlocksDisabledFunctions() throws Exception { } @Test - public void itBlocksDisabledExpTests() throws Exception { + public void itBlocksDisabledExpTests() { Map> disabled = ImmutableMap.of(Context.Library.EXP_TEST, ImmutableSet.of("even")); assertThat(interpreter.render("{% if 2 is even %}yes{% endif %}")).isEqualTo("yes"); @@ -424,7 +424,7 @@ public void itBlocksDisabledExpTests() throws Exception { } @Test - public void itStoresResolvedFunctions() throws Exception { + public void itStoresResolvedFunctions() { context.put("datetime", 12345); final JinjavaConfig config = JinjavaConfig.newBuilder().build(); String template = "{% for i in range(1, 5) %}{{i}} {% endfor %}\n{{ unixtimestamp(datetime) }}"; diff --git a/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java b/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java index 811b9a835..53dac0eb1 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java @@ -263,10 +263,13 @@ public void itReturnsCorrectSyntaxErrorPositions() { assertThat(interpreter.render("hi {{ missing thing }}{{ missing thing }}\nI am {{ blah blabbity }} too")).isEqualTo("hi \nI am too"); assertThat(interpreter.getErrors().size()).isEqualTo(3); assertThat(interpreter.getErrors().get(0).getLineno()).isEqualTo(1); + assertThat(interpreter.getErrors().get(0).getMessage()).contains("position 14"); assertThat(interpreter.getErrors().get(0).getStartPosition()).isEqualTo(14); assertThat(interpreter.getErrors().get(1).getLineno()).isEqualTo(1); + assertThat(interpreter.getErrors().get(1).getMessage()).contains("position 33"); assertThat(interpreter.getErrors().get(1).getStartPosition()).isEqualTo(33); assertThat(interpreter.getErrors().get(2).getLineno()).isEqualTo(2); + assertThat(interpreter.getErrors().get(2).getMessage()).contains("position 13"); assertThat(interpreter.getErrors().get(2).getStartPosition()).isEqualTo(13); } From b4233328b7b1923e0d2920def90fcb1b058af589 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Wed, 18 Apr 2018 17:48:37 -0400 Subject: [PATCH 0441/2465] more accurate fieldName for TemplateSyntaxException --- .../java/com/hubspot/jinjava/el/ExpressionResolver.java | 7 +++++-- .../com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java | 3 +++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java index c9782f921..9641bcf7e 100644 --- a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java @@ -37,6 +37,9 @@ public class ExpressionResolver { private final JinjavaInterpreterResolver resolver; private final JinjavaELContext elContext; + private static final String EXPRESSION_START_TOKEN = "#{"; + private static final String EXPRESSION_END_TOKEN = "}"; + public ExpressionResolver(JinjavaInterpreter interpreter, ExpressionFactory expressionFactory) { this.interpreter = interpreter; this.expressionFactory = expressionFactory; @@ -63,7 +66,7 @@ public Object resolveExpression(String expression) { interpreter.getContext().addResolvedExpression(expression.trim()); try { - String elExpression = "#{" + expression.trim() + "}"; + String elExpression = EXPRESSION_START_TOKEN + expression.trim() + EXPRESSION_END_TOKEN; ValueExpression valueExp = expressionFactory.createValueExpression(elContext, elExpression, Object.class); Object result = valueExp.getValue(elContext); @@ -78,7 +81,7 @@ public Object resolveExpression(String expression) { int position = interpreter.getPosition() + e.getPosition(); // replacing the position in the string like this isn't great, but JUEL's parser does not allow passing in a starting position String errorMessage = StringUtils.substringAfter(e.getMessage(), "': ").replaceFirst("position [0-9]+", "position " + position); - interpreter.addError(TemplateError.fromException(new TemplateSyntaxException(expression, + interpreter.addError(TemplateError.fromException(new TemplateSyntaxException(expression.substring(e.getPosition() - EXPRESSION_START_TOKEN.length()), "Error parsing '" + expression + "': " + errorMessage, interpreter.getLineNumber(), position, e))); } catch (ELException e) { interpreter.addError(TemplateError.fromException(new TemplateSyntaxException(expression, e.getMessage(), interpreter.getLineNumber(), e))); diff --git a/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java b/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java index 53dac0eb1..8d5134ea2 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java @@ -265,12 +265,15 @@ public void itReturnsCorrectSyntaxErrorPositions() { assertThat(interpreter.getErrors().get(0).getLineno()).isEqualTo(1); assertThat(interpreter.getErrors().get(0).getMessage()).contains("position 14"); assertThat(interpreter.getErrors().get(0).getStartPosition()).isEqualTo(14); + assertThat(interpreter.getErrors().get(0).getFieldName()).isEqualTo("thing"); assertThat(interpreter.getErrors().get(1).getLineno()).isEqualTo(1); assertThat(interpreter.getErrors().get(1).getMessage()).contains("position 33"); assertThat(interpreter.getErrors().get(1).getStartPosition()).isEqualTo(33); + assertThat(interpreter.getErrors().get(1).getFieldName()).isEqualTo("thing"); assertThat(interpreter.getErrors().get(2).getLineno()).isEqualTo(2); assertThat(interpreter.getErrors().get(2).getMessage()).contains("position 13"); assertThat(interpreter.getErrors().get(2).getStartPosition()).isEqualTo(13); + assertThat(interpreter.getErrors().get(2).getFieldName()).isEqualTo("blabbity"); } private Object val(String expr) { From 1319105420c7336430d0e4d347511cdf9a3e66f0 Mon Sep 17 00:00:00 2001 From: Padraig Farrell Date: Thu, 19 Apr 2018 10:42:14 +0100 Subject: [PATCH 0442/2465] Add a few more int/float parsing tests and fix test names --- .../hubspot/jinjava/lib/filter/FloatFilterTest.java | 1 + .../hubspot/jinjava/lib/filter/IntFilterTest.java | 13 ++++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/FloatFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/FloatFilterTest.java index 0b2d669f7..1c19279d0 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/FloatFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/FloatFilterTest.java @@ -62,6 +62,7 @@ public void itIgnoresGivenDefaultIfNaN() { @Test public void itReturnsVarAsFloat() { assertThat(filter.filter("123.45", interpreter)).isEqualTo(123.45f); + assertThat(filter.filter("1.100000", interpreter)).isEqualTo(1.100000f); } @Test diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/IntFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/IntFilterTest.java index 48a87a7cd..3761a0627 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/IntFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/IntFilterTest.java @@ -47,7 +47,14 @@ public void itIgnoresGivenDefaultIfNaN() { @Test public void itReturnsVarAsInt() { - assertThat(filter.filter("123", interpreter)).isEqualTo(123); + assertThat(filter.filter("123", interpreter)) + .isInstanceOf(Integer.class) + .isEqualTo(123); + } + + @Test + public void itReturnsVarWithFloatingPointAsInt() { + assertThat(filter.filter("123.1", interpreter)).isEqualTo(123); } @Test @@ -81,12 +88,12 @@ public void itReturnsVarWithTrailingCurrencySymbolAsDefault() { } @Test - public void itDoesntInterpretsUsCommasAndPeriodsWithUsLocale() { + public void itInterpretsUsCommasAndPeriodsWithUsLocale() { assertThat(filter.filter("123,123.12", interpreter)).isEqualTo(123123); } @Test - public void itDoesntInterpreFrenchCommasAndPeriodsWithUsLocale() { + public void itDoesntInterpretFrenchCommasAndPeriodsWithUsLocale() { assertThat(filter.filter("123.123,12", interpreter)).isEqualTo(0); } From e68556c3a5dc33a23af683edaa37acc3bbe67cdd Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Sun, 22 Apr 2018 17:54:52 -0400 Subject: [PATCH 0443/2465] Update CHANGES.md --- CHANGES.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 66baf467c..de6e0e932 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # Jinjava Releases # -### 2018-04-18 Version 2.4.1 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.1%7Cjar)) ### +### 2018-04-22 Version 2.4.1 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.1%7Cjar)) ### * [Use `AdvancedFilter` for `selectattr` filter](https://github.com/HubSpot/jinjava/pull/183) * [Java 9 Support](https://github.com/HubSpot/jinjava/pull/186) @@ -8,6 +8,7 @@ * [Fix column numbers in syntax errors](https://github.com/HubSpot/jinjava/pull/188) * [When reporting errors, preserve casing](https://github.com/HubSpot/jinjava/pull/189) * [Populate `fieldName` in `TemplateSyntaxException`s](https://github.com/HubSpot/jinjava/pull/190) +* [Reintroduce stricter parsing in int and float filters](https://github.com/HubSpot/jinjava/pull/191) ### 2018-02-26 Version 2.4.0 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.0%7Cjar)) ### From f198ce15eccb4e6ce9c03344284c1f79a32f55fe Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Sun, 22 Apr 2018 21:58:18 +0000 Subject: [PATCH 0444/2465] [maven-release-plugin] prepare release jinjava-2.4.1 --- pom.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 3dc297fef..b0cadf6ab 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,4 @@ - + 4.0.0 @@ -10,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.4.1-SNAPSHOT + 2.4.1 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -18,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.4.1 From 2c39c4e2454378158a3416fe4fd3a768b24e3487 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Sun, 22 Apr 2018 21:58:18 +0000 Subject: [PATCH 0445/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b0cadf6ab..c06b82e04 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.4.1 + 2.4.2-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.4.1 + HEAD From 8a0cb92c859a117bae1533e65e3fa6147e939f1f Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 1 Jun 2018 21:01:28 -0400 Subject: [PATCH 0446/2465] upgrade jsoup --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c06b82e04..0b9a59ebc 100644 --- a/pom.xml +++ b/pom.xml @@ -101,7 +101,7 @@ org.jsoup jsoup - 1.8.1 + 1.10.3 de.odysseus.juel From ab3a4047cd75c958c2351499a95d0d1c2fb33120 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 1 Jun 2018 21:14:11 -0400 Subject: [PATCH 0447/2465] Update CHANGES.md --- CHANGES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index de6e0e932..0fc7a73f4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,8 @@ # Jinjava Releases # +### 2018-06-01 Version 2.4.2 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.2%7Cjar)) ### +* [Upgrade jsoup to address CVE](https://github.com/HubSpot/jinjava/pull/200) + ### 2018-04-22 Version 2.4.1 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.1%7Cjar)) ### * [Use `AdvancedFilter` for `selectattr` filter](https://github.com/HubSpot/jinjava/pull/183) From 92fd96e04133f3e6013228d86702faf0e6a71834 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Sat, 2 Jun 2018 01:17:50 +0000 Subject: [PATCH 0448/2465] [maven-release-plugin] prepare release jinjava-2.4.2 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0b9a59ebc..f165face5 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.4.2-SNAPSHOT + 2.4.2 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.4.2 From 85c2e47afe7b0216be865c90cf4a59cfde9d104f Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Sat, 2 Jun 2018 01:17:50 +0000 Subject: [PATCH 0449/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f165face5..196a03801 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.4.2 + 2.4.3-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.4.2 + HEAD From 144123773336cfdddba7b64779780220215d0152 Mon Sep 17 00:00:00 2001 From: Jessica Scott Date: Fri, 8 Jun 2018 16:48:44 -0400 Subject: [PATCH 0450/2465] recursively wrap date objects for lists and maps --- .../el/JinjavaInterpreterResolver.java | 13 +++++++--- .../interpret/JinjavaInterpreterTest.java | 26 +++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java index 7c61a613d..ec70a4fcd 100644 --- a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java @@ -10,12 +10,14 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Date; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.stream.Collectors; import javax.el.ArrayELResolver; import javax.el.CompositeELResolver; @@ -204,11 +206,16 @@ Object wrap(Object value) { } if (List.class.isAssignableFrom(value.getClass())) { - return new PyList((List) value); + List list = (List) value; + return new PyList(list.stream().map(this::wrap).collect(Collectors.toList())); } if (Map.class.isAssignableFrom(value.getClass())) { - // FIXME: ensure keys are actually strings, if not, convert them - return new PyMap((Map) value); + Map map = (Map) value; + Map wrapped = new HashMap<>(); + for (Map.Entry e : map.entrySet()) { + wrapped.put(e.getKey().toString(), wrap(e.getValue())); + } + return new PyMap(wrapped); } if (Date.class.isAssignableFrom(value.getClass())) { diff --git a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java index 73cb78c8f..f111ffef8 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java @@ -3,16 +3,23 @@ import static org.assertj.core.api.Assertions.assertThat; import java.time.ZonedDateTime; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.junit.Before; import org.junit.Test; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.hubspot.jinjava.Jinjava; import com.hubspot.jinjava.JinjavaConfig; import com.hubspot.jinjava.interpret.JinjavaInterpreter.InterpreterScopeClosable; import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; +import com.hubspot.jinjava.objects.collections.PyList; +import com.hubspot.jinjava.objects.collections.PyMap; +import com.hubspot.jinjava.objects.date.PyishDate; import com.hubspot.jinjava.tree.TextNode; import com.hubspot.jinjava.tree.parse.TextToken; @@ -106,6 +113,25 @@ public void triesBeanMethodFirst() { .toString()).isEqualTo("2013"); } + @Test + public void itWrapsDateChildren() { + ZonedDateTime testDate = ZonedDateTime.parse("2013-09-19T12:12:12+00:00"); + + List dateList = Arrays.asList(testDate); + Object result = interpreter.resolveProperty(ImmutableMap.of("dates", dateList), "dates"); + assertThat(result).isOfAnyClassIn(PyList.class); + PyList pyList = (PyList) result; + assertThat(pyList).containsOnly(new PyishDate(testDate)); + + + Map dateMap = ImmutableMap.of("date", ZonedDateTime.parse("2013-09-19T12:12:12+00:00")); + result = interpreter.resolveProperty(ImmutableMap.of("dates", dateMap), "dates"); + assertThat(result).isOfAnyClassIn(PyMap.class); + PyMap pyMap = (PyMap) result; + assertThat(pyMap).containsOnlyKeys("date"); + assertThat(pyMap.get("date")).isEqualTo(new PyishDate(testDate)); + } + @Test public void enterScopeTryFinally() { interpreter.getContext().put("foo", "parent"); From cb5f3119a52ac025ef283ed6f5c51cb5ea9fdf37 Mon Sep 17 00:00:00 2001 From: Jessica Scott Date: Fri, 8 Jun 2018 16:54:00 -0400 Subject: [PATCH 0451/2465] unnecessary line --- .../com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java index f111ffef8..f2454cf28 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java @@ -123,7 +123,6 @@ public void itWrapsDateChildren() { PyList pyList = (PyList) result; assertThat(pyList).containsOnly(new PyishDate(testDate)); - Map dateMap = ImmutableMap.of("date", ZonedDateTime.parse("2013-09-19T12:12:12+00:00")); result = interpreter.resolveProperty(ImmutableMap.of("dates", dateMap), "dates"); assertThat(result).isOfAnyClassIn(PyMap.class); From 351c23bcbcbde7d74638455d954a15d736778833 Mon Sep 17 00:00:00 2001 From: Jessica Scott Date: Fri, 8 Jun 2018 18:03:16 -0400 Subject: [PATCH 0452/2465] different strategy -- instead of modifying the list, just wrap while we're in a for loop --- .../jinjava/el/JinjavaInterpreterResolver.java | 13 +++---------- .../java/com/hubspot/jinjava/lib/tag/ForTag.java | 2 ++ 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java index ec70a4fcd..7c61a613d 100644 --- a/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java @@ -10,14 +10,12 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Date; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.stream.Collectors; import javax.el.ArrayELResolver; import javax.el.CompositeELResolver; @@ -206,16 +204,11 @@ Object wrap(Object value) { } if (List.class.isAssignableFrom(value.getClass())) { - List list = (List) value; - return new PyList(list.stream().map(this::wrap).collect(Collectors.toList())); + return new PyList((List) value); } if (Map.class.isAssignableFrom(value.getClass())) { - Map map = (Map) value; - Map wrapped = new HashMap<>(); - for (Map.Entry e : map.entrySet()) { - wrapped.put(e.getKey().toString(), wrap(e.getValue())); - } - return new PyMap(wrapped); + // FIXME: ensure keys are actually strings, if not, convert them + return new PyMap((Map) value); } if (Date.class.isAssignableFrom(value.getClass())) { diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java index d04301aef..9057826a1 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java @@ -17,6 +17,7 @@ import java.beans.Introspector; import java.beans.PropertyDescriptor; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -129,6 +130,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { LengthLimitingStringBuilder buff = new LengthLimitingStringBuilder(interpreter.getConfig().getMaxOutputSize()); while (loop.hasNext()) { Object val = loop.next(); + val = interpreter.resolveProperty(val, Collections.emptyList()); // set item variables if (loopVars.size() == 1) { From 54c911847e39354299536ee8b40210200f5956f4 Mon Sep 17 00:00:00 2001 From: Jessica Scott Date: Fri, 8 Jun 2018 18:12:22 -0400 Subject: [PATCH 0453/2465] need a different test --- .../interpret/JinjavaInterpreterTest.java | 25 ------------------- .../hubspot/jinjava/lib/tag/ForTagTest.java | 14 +++++++++++ 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java index f2454cf28..73cb78c8f 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java @@ -3,23 +3,16 @@ import static org.assertj.core.api.Assertions.assertThat; import java.time.ZonedDateTime; -import java.util.Arrays; import java.util.HashMap; -import java.util.List; -import java.util.Map; import org.junit.Before; import org.junit.Test; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.hubspot.jinjava.Jinjava; import com.hubspot.jinjava.JinjavaConfig; import com.hubspot.jinjava.interpret.JinjavaInterpreter.InterpreterScopeClosable; import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; -import com.hubspot.jinjava.objects.collections.PyList; -import com.hubspot.jinjava.objects.collections.PyMap; -import com.hubspot.jinjava.objects.date.PyishDate; import com.hubspot.jinjava.tree.TextNode; import com.hubspot.jinjava.tree.parse.TextToken; @@ -113,24 +106,6 @@ public void triesBeanMethodFirst() { .toString()).isEqualTo("2013"); } - @Test - public void itWrapsDateChildren() { - ZonedDateTime testDate = ZonedDateTime.parse("2013-09-19T12:12:12+00:00"); - - List dateList = Arrays.asList(testDate); - Object result = interpreter.resolveProperty(ImmutableMap.of("dates", dateList), "dates"); - assertThat(result).isOfAnyClassIn(PyList.class); - PyList pyList = (PyList) result; - assertThat(pyList).containsOnly(new PyishDate(testDate)); - - Map dateMap = ImmutableMap.of("date", ZonedDateTime.parse("2013-09-19T12:12:12+00:00")); - result = interpreter.resolveProperty(ImmutableMap.of("dates", dateMap), "dates"); - assertThat(result).isOfAnyClassIn(PyMap.class); - PyMap pyMap = (PyMap) result; - assertThat(pyMap).containsOnlyKeys("date"); - assertThat(pyMap.get("date")).isEqualTo(new PyishDate(testDate)); - } - @Test public void enterScopeTryFinally() { interpreter.getContext().put("foo", "parent"); diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java index 7105bc4b4..e00989d62 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java @@ -5,6 +5,7 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.Date; import java.util.List; import java.util.Map; @@ -22,6 +23,7 @@ import com.hubspot.jinjava.Jinjava; import com.hubspot.jinjava.interpret.Context; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.objects.date.PyishDate; import com.hubspot.jinjava.tree.Node; import com.hubspot.jinjava.tree.TagNode; import com.hubspot.jinjava.tree.TreeParser; @@ -193,6 +195,18 @@ public void testForLoopRangeWithStringsWithSpaces() { assertEquals("a b", rendered); } + @Test + public void testForLoopWithDates() { + Map context = Maps.newHashMap(); + Date testDate = new Date(); + context.put("the_list", Lists.newArrayList(new Date())); + String template = "" + + "{% for i in the_list %}{{i}}{% endfor %}"; + String rendered = jinjava.render(template, context); + System.out.println(rendered); + assertEquals(new PyishDate(testDate).toString(), rendered); + } + private Node fixture(String name) { try { return new TreeParser(interpreter, Resources.toString( From 75ba431bb11a9b1f350851b73380f495737415f8 Mon Sep 17 00:00:00 2001 From: Jessica Scott Date: Mon, 11 Jun 2018 16:55:49 -0400 Subject: [PATCH 0454/2465] fix test --- src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java index e00989d62..a243d2432 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java @@ -199,7 +199,7 @@ public void testForLoopRangeWithStringsWithSpaces() { public void testForLoopWithDates() { Map context = Maps.newHashMap(); Date testDate = new Date(); - context.put("the_list", Lists.newArrayList(new Date())); + context.put("the_list", Lists.newArrayList(testDate)); String template = "" + "{% for i in the_list %}{{i}}{% endfor %}"; String rendered = jinjava.render(template, context); From 3a4e7212b4eb1da0f5aac579b1c11046287a7f70 Mon Sep 17 00:00:00 2001 From: Jessica Scott Date: Wed, 13 Jun 2018 07:16:02 -0400 Subject: [PATCH 0455/2465] add `wrap` methods to `JinjavaInterepreter` and `ExpressionResolver` so that it can be accessed by tags. --- .../com/hubspot/jinjava/el/ExpressionResolver.java | 11 +++++++++++ .../hubspot/jinjava/interpret/JinjavaInterpreter.java | 11 +++++++++++ src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java | 6 ++---- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java index 9641bcf7e..2d38290b6 100644 --- a/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java @@ -127,4 +127,15 @@ public Object resolveProperty(Object object, List propertyNames) { return value; } + + /** + * Wrap an object in it's PyIsh equivalent + * + * @param object + * Bean. + * @return Wrapped bean. + */ + public Object wrap(Object object) { + return resolver.wrap(object); + } } diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index 3ff3291ae..5a1501e57 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -424,6 +424,17 @@ public Object resolveProperty(Object object, List propertyNames) { return expressionResolver.resolveProperty(object, propertyNames); } + /** + * Wrap an object in it's PyIsh equivalent + * + * @param object + * Bean. + * @return Wrapped bean. + */ + public Object wrap(Object object) { + return expressionResolver.wrap(object); + } + public int getLineNumber() { return lineNumber; } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java index 9057826a1..c3d160c85 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java @@ -17,7 +17,6 @@ import java.beans.Introspector; import java.beans.PropertyDescriptor; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -36,8 +35,8 @@ import com.hubspot.jinjava.tree.TagNode; import com.hubspot.jinjava.util.ForLoop; import com.hubspot.jinjava.util.HelperStringTokenizer; -import com.hubspot.jinjava.util.ObjectIterator; import com.hubspot.jinjava.util.LengthLimitingStringBuilder; +import com.hubspot.jinjava.util.ObjectIterator; /** * {% for a in b|f1:d,c %} @@ -129,8 +128,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { LengthLimitingStringBuilder buff = new LengthLimitingStringBuilder(interpreter.getConfig().getMaxOutputSize()); while (loop.hasNext()) { - Object val = loop.next(); - val = interpreter.resolveProperty(val, Collections.emptyList()); + Object val = interpreter.wrap(loop.next()); // set item variables if (loopVars.size() == 1) { From 60706dd6b0cba10e1ae1190e9542101c14208fa2 Mon Sep 17 00:00:00 2001 From: jessbrandi Date: Wed, 13 Jun 2018 14:07:06 -0400 Subject: [PATCH 0456/2465] Update CHANGES.md --- CHANGES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 0fc7a73f4..82cc7ec70 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,8 @@ # Jinjava Releases # +### 2018-06-13 Version 2.4.3 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.3%7Cjar)) ### +* [Wrap items in for loop with "PyIsh" equivalents](https://github.com/HubSpot/jinjava/pull/202) + ### 2018-06-01 Version 2.4.2 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.2%7Cjar)) ### * [Upgrade jsoup to address CVE](https://github.com/HubSpot/jinjava/pull/200) From e79e997b67dd7ceaf963146839c3a1dad8a799f4 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Wed, 13 Jun 2018 18:10:58 +0000 Subject: [PATCH 0457/2465] [maven-release-plugin] prepare release jinjava-2.4.3 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 196a03801..2492488db 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.4.3-SNAPSHOT + 2.4.3 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.4.3 From 3835778580f9fea2d024cc345b5a3981ec7a5d76 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Wed, 13 Jun 2018 18:10:58 +0000 Subject: [PATCH 0458/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 2492488db..762c9eb1b 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.4.3 + 2.4.4-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.4.3 + HEAD From 416016368f65d3498c64019a9e704c8d47182323 Mon Sep 17 00:00:00 2001 From: Oliver Hanser Date: Thu, 28 Jun 2018 13:57:28 +0200 Subject: [PATCH 0459/2465] Fix sporadic test failures introduced in e65ee0b --- .../jinjava/interpret/TemplateErrorTest.java | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java b/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java index 1b1734399..75944336a 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java @@ -2,7 +2,6 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Before; import org.junit.Test; import com.google.common.collect.ImmutableMap; @@ -10,16 +9,6 @@ public class TemplateErrorTest { - private JinjavaInterpreter interpreter; - private Jinjava jinjava; - - @Before - public void setup() { - jinjava = new Jinjava(); - interpreter = jinjava.newInterpreter(); - JinjavaInterpreter.pushCurrent(interpreter); - } - @Test public void itShowsFriendlyNameOfBaseObjectForPropNotFound() { TemplateError e = TemplateError.fromUnknownProperty(new Object(), "foo", 123, 4); @@ -46,13 +35,14 @@ public void itShowsFieldNameForSyntaxError() { @Test public void itRetainsFieldNameCaseForUnknownToken() { + JinjavaInterpreter interpreter = new Jinjava().newInterpreter(); interpreter.render("{% unKnown() %}"); assertThat(interpreter.getErrors().get(0).getFieldName()).isEqualTo("unKnown"); } @Test public void itSetsFieldNameCaseForSyntaxErrorInFor() { - RenderResult renderResult = jinjava.renderForResult("{% for item inna navigation %}{% endfor %}", ImmutableMap.of()); + RenderResult renderResult = new Jinjava().renderForResult("{% for item inna navigation %}{% endfor %}", ImmutableMap.of()); assertThat(renderResult.getErrors().get(0).getFieldName()).isEqualTo("item inna navigation"); } From c36b42d8bdb9591e2a19645454f5ff0fc833fad9 Mon Sep 17 00:00:00 2001 From: Oliver Hanser Date: Thu, 7 Jun 2018 11:42:15 +0200 Subject: [PATCH 0460/2465] fix calling macros with kwargs --- .../com/hubspot/jinjava/lib/fn/MacroFunction.java | 2 +- .../com/hubspot/jinjava/lib/tag/MacroTagTest.java | 11 +++++++++++ src/test/resources/tags/macrotag/list_kwargs.jinja | 5 +++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 src/test/resources/tags/macrotag/list_kwargs.jinja diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/MacroFunction.java b/src/main/java/com/hubspot/jinjava/lib/fn/MacroFunction.java index 295518793..faef0dfcd 100644 --- a/src/main/java/com/hubspot/jinjava/lib/fn/MacroFunction.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/MacroFunction.java @@ -60,7 +60,7 @@ public Object doEvaluate(Map argMap, Map kwargMa interpreter.getContext().put(argEntry.getKey(), argEntry.getValue()); } // parameter map - interpreter.getContext().put("kwargs", argMap); + interpreter.getContext().put("kwargs", kwargMap); // varargs list interpreter.getContext().put("varargs", varArgs); diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java index c5f0a9e7e..73a1fed1a 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java @@ -74,6 +74,17 @@ public void testFnWithArgs() { assertThat(snippet("{{section_link('mylink', 'mytext')}}").render(interpreter).getValue().trim()).isEqualTo("link: mylink, text: mytext"); } + @Test + public void testFnWithKwArgs() { + TagNode t = fixture("list_kwargs"); + assertThat(t.render(interpreter).getValue()).isEmpty(); + + String out = snippet("{{ list_kwargs(foo=\"bar\", bas='baz', answer=42) }}").render(interpreter).getValue().trim(); + assertThat(out).contains("foo=bar"); + assertThat(out).contains("bas=baz"); + assertThat(out).contains("answer=42"); + } + @Test public void testFnWithArgsWithDefVals() { TagNode t = fixture("def-vals"); diff --git a/src/test/resources/tags/macrotag/list_kwargs.jinja b/src/test/resources/tags/macrotag/list_kwargs.jinja new file mode 100644 index 000000000..2acbde314 --- /dev/null +++ b/src/test/resources/tags/macrotag/list_kwargs.jinja @@ -0,0 +1,5 @@ +{% macro list_kwargs() %} + {% for k,v in kwargs.items() %} + {{k}}={{v}} + {% endfor %} +{% endmacro %} From 3270dfc14e8a6dbd7e6e896b0aab541f06245bc8 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Mon, 9 Jul 2018 13:22:14 -0400 Subject: [PATCH 0461/2465] limit size of strings in TemplateErrors --- .../hubspot/jinjava/interpret/TemplateError.java | 10 ++++++++-- .../jinjava/interpret/TemplateErrorTest.java | 13 +++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java index 7ae9482c9..13d6c0aee 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TemplateError.java @@ -10,6 +10,10 @@ import com.hubspot.jinjava.interpret.errorcategory.TemplateErrorCategory; public class TemplateError { + + private static final Pattern GENERIC_TOSTRING_PATTERN = Pattern.compile("@[0-9a-z]{4,}$"); + private static final int MAX_STRING_LENGTH = 1024; + public enum ErrorType { FATAL, WARNING @@ -101,6 +105,10 @@ private static String friendlyObjectToString(Object o) { String s = o.toString(); + if (s.length() > MAX_STRING_LENGTH) { + s = s.substring(0, MAX_STRING_LENGTH) + "..."; + } + if (!GENERIC_TOSTRING_PATTERN.matcher(s).find()) { return s; } @@ -109,8 +117,6 @@ private static String friendlyObjectToString(Object o) { return c.getSimpleName(); } - private static final Pattern GENERIC_TOSTRING_PATTERN = Pattern.compile("@[0-9a-z]{4,}$"); - public TemplateError(ErrorType severity, ErrorReason reason, ErrorItem item, diff --git a/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java b/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java index 75944336a..9df710a3f 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java @@ -46,4 +46,17 @@ public void itSetsFieldNameCaseForSyntaxErrorInFor() { assertThat(renderResult.getErrors().get(0).getFieldName()).isEqualTo("item inna navigation"); } + @Test + public void itLimitsErrorStringToAReasonableSize() { + + String veryLong = ""; + + for (int i = 0; i < 1500; i++) { + veryLong = veryLong.concat("0"); + } + + TemplateError e = TemplateError.fromUnknownProperty(ImmutableMap.of("foo", veryLong), "other", 123, 4); + assertThat(e.getMessage()).startsWith("Cannot resolve property 'other' in '{foo="); + assertThat(e.getMessage().length()).isLessThan(1500); + } } From 16aae74750daa868dadf3e885c83a458956df471 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Tue, 10 Jul 2018 11:36:50 -0400 Subject: [PATCH 0462/2465] Update CHANGES.md --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 82cc7ec70..20df1fad3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Jinjava Releases # +### 2018-07-10 Version 2.4.4 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.4%7Cjar)) ### +* [Fix calling macros with kwargs](https://github.com/HubSpot/jinjava/pull/208) +* [Limit the size of strings in TemplateErrors](https://github.com/HubSpot/jinjava/pull/209) + ### 2018-06-13 Version 2.4.3 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.3%7Cjar)) ### * [Wrap items in for loop with "PyIsh" equivalents](https://github.com/HubSpot/jinjava/pull/202) From f68dd572d1731ba947f3fda9c411c0c50fd9e487 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Tue, 10 Jul 2018 15:40:34 +0000 Subject: [PATCH 0463/2465] [maven-release-plugin] prepare release jinjava-2.4.4 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 762c9eb1b..0e16dbfeb 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.4.4-SNAPSHOT + 2.4.4 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.4.4 From 863d8cfbc59b6734cd4353f02133389d57a3f471 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Tue, 10 Jul 2018 15:40:34 +0000 Subject: [PATCH 0464/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0e16dbfeb..9be99fe14 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.4.4 + 2.4.5-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.4.4 + HEAD From 06757d79f76e155c6d3a9f02f7714aa53bb36d1e Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Tue, 17 Jul 2018 17:00:42 -0400 Subject: [PATCH 0465/2465] Fix equalto expression test. --- .../com/hubspot/jinjava/lib/exptest/IsEqualToExpTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/exptest/IsEqualToExpTest.java b/src/main/java/com/hubspot/jinjava/lib/exptest/IsEqualToExpTest.java index 648b8d274..d1d59431c 100644 --- a/src/main/java/com/hubspot/jinjava/lib/exptest/IsEqualToExpTest.java +++ b/src/main/java/com/hubspot/jinjava/lib/exptest/IsEqualToExpTest.java @@ -1,13 +1,14 @@ package com.hubspot.jinjava.lib.exptest; -import java.util.Objects; - import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaParam; import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; +import com.hubspot.jinjava.el.TruthyTypeConverter; import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import de.odysseus.el.misc.BooleanOperations; + @JinjavaDoc( value = "Check if an object has the same value as another object", params = { @@ -35,7 +36,7 @@ public boolean evaluate(Object var, JinjavaInterpreter interpreter, Object... ar throw new InterpretException(getName() + " test requires 1 argument"); } - return Objects.equals(var, args[0]); + return BooleanOperations.eq(new TruthyTypeConverter(), var, args[0]); } } From 13725cc501a6ef3e345a1176eb8b969865a0fdc8 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Tue, 17 Jul 2018 17:28:12 -0400 Subject: [PATCH 0466/2465] add tests. --- .../lib/exptest/IsEqualToExpTestTest.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/test/java/com/hubspot/jinjava/lib/exptest/IsEqualToExpTestTest.java diff --git a/src/test/java/com/hubspot/jinjava/lib/exptest/IsEqualToExpTestTest.java b/src/test/java/com/hubspot/jinjava/lib/exptest/IsEqualToExpTestTest.java new file mode 100644 index 000000000..a6bd08ef7 --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/lib/exptest/IsEqualToExpTestTest.java @@ -0,0 +1,48 @@ +package com.hubspot.jinjava.lib.exptest; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.HashMap; + +import org.junit.Before; +import org.junit.Test; + +import com.hubspot.jinjava.Jinjava; + +public class IsEqualToExpTestTest { + + private static final String EQUAL_TEMPLATE = "{{ %s is equalto %s }}"; + + private Jinjava jinjava; + + @Before + public void setup() { + jinjava = new Jinjava(); + } + + @Test + public void itEquatesNumbers() { + assertThat(jinjava.render(String.format(EQUAL_TEMPLATE, "4", "4"), new HashMap<>())).isEqualTo("true"); + assertThat(jinjava.render(String.format(EQUAL_TEMPLATE, "4", "5"), new HashMap<>())).isEqualTo("false"); + } + + @Test + public void itEquatesStrings() { + assertThat(jinjava.render(String.format(EQUAL_TEMPLATE, "\"jinjava\"", "\"jinjava\""), new HashMap<>())).isEqualTo("true"); + assertThat(jinjava.render(String.format(EQUAL_TEMPLATE, "\"jinjava\"", "\"not jinjava\""), new HashMap<>())).isEqualTo("false"); + } + + @Test + public void itEquatesBooleans() { + assertThat(jinjava.render(String.format(EQUAL_TEMPLATE, "true", "true"), new HashMap<>())).isEqualTo("true"); + assertThat(jinjava.render(String.format(EQUAL_TEMPLATE, "true", "false"), new HashMap<>())).isEqualTo("false"); + } + + @Test + public void itEquatesDifferentTypes() { + assertThat(jinjava.render(String.format(EQUAL_TEMPLATE, "4", "\"4\""), new HashMap<>())).isEqualTo("true"); + assertThat(jinjava.render(String.format(EQUAL_TEMPLATE, "4", "\"5\""), new HashMap<>())).isEqualTo("false"); + assertThat(jinjava.render(String.format(EQUAL_TEMPLATE, "'c'", "\"c\""), new HashMap<>())).isEqualTo("true"); + assertThat(jinjava.render(String.format(EQUAL_TEMPLATE, "'c'", "\"b\""), new HashMap<>())).isEqualTo("false"); + } +} From 1057e7aae3d7215ae48dbaee9d937f5cb4c1caf5 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Tue, 17 Jul 2018 17:31:14 -0400 Subject: [PATCH 0467/2465] make static. --- .../com/hubspot/jinjava/lib/exptest/IsEqualToExpTest.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/exptest/IsEqualToExpTest.java b/src/main/java/com/hubspot/jinjava/lib/exptest/IsEqualToExpTest.java index d1d59431c..fa81ffe49 100644 --- a/src/main/java/com/hubspot/jinjava/lib/exptest/IsEqualToExpTest.java +++ b/src/main/java/com/hubspot/jinjava/lib/exptest/IsEqualToExpTest.java @@ -8,6 +8,7 @@ import com.hubspot.jinjava.interpret.JinjavaInterpreter; import de.odysseus.el.misc.BooleanOperations; +import de.odysseus.el.misc.TypeConverter; @JinjavaDoc( value = "Check if an object has the same value as another object", @@ -25,6 +26,8 @@ }) public class IsEqualToExpTest implements ExpTest { + private static final TypeConverter TYPE_CONVERTER = new TruthyTypeConverter(); + @Override public String getName() { return "equalto"; @@ -36,7 +39,7 @@ public boolean evaluate(Object var, JinjavaInterpreter interpreter, Object... ar throw new InterpretException(getName() + " test requires 1 argument"); } - return BooleanOperations.eq(new TruthyTypeConverter(), var, args[0]); + return BooleanOperations.eq(TYPE_CONVERTER, var, args[0]); } } From dbd5b1ceefd08f5ed4b2069c00ecfe3bcd2a6669 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Mon, 23 Jul 2018 16:48:53 -0400 Subject: [PATCH 0468/2465] Add filter to convert objects to JSON. --- pom.xml | 4 ++ .../jinjava/lib/filter/ToJsonFilter.java | 39 +++++++++++++++++++ .../jinjava/lib/filter/ToJsonFilterTest.java | 36 +++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 src/main/java/com/hubspot/jinjava/lib/filter/ToJsonFilter.java create mode 100644 src/test/java/com/hubspot/jinjava/lib/filter/ToJsonFilterTest.java diff --git a/pom.xml b/pom.xml index 9be99fe14..f40cedd37 100644 --- a/pom.xml +++ b/pom.xml @@ -68,6 +68,10 @@ com.google.code.findbugs annotations + + com.fasterxml.jackson.core + jackson-databind + ch.qos.logback diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/ToJsonFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/ToJsonFilter.java new file mode 100644 index 000000000..b44bb4acc --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/lib/filter/ToJsonFilter.java @@ -0,0 +1,39 @@ +package com.hubspot.jinjava.lib.filter; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.hubspot.jinjava.doc.annotations.JinjavaDoc; +import com.hubspot.jinjava.doc.annotations.JinjavaParam; +import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; +import com.hubspot.jinjava.interpret.InterpretException; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; + + +@JinjavaDoc( + value = "Writes objects as JSON strings", + params = { + @JinjavaParam(value = "o", desc = "Object to write to JSON") + }, + snippets = { + @JinjavaSnippet( + code = "{{object|escapejson}}" + ) + }) +public class ToJsonFilter implements Filter { + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + @Override + public Object filter(Object var, JinjavaInterpreter interpreter, String... args) { + try { + return OBJECT_MAPPER.writeValueAsString(var); + } catch (JsonProcessingException e) { + throw new InterpretException("Could not write object as string for `escapejson` filter.", e, interpreter.getLineNumber()); + } + } + + @Override + public String getName() { + return "tojson"; + } +} diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/ToJsonFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/ToJsonFilterTest.java new file mode 100644 index 000000000..594362fee --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/lib/filter/ToJsonFilterTest.java @@ -0,0 +1,36 @@ +package com.hubspot.jinjava.lib.filter; + +import static org.assertj.core.api.Java6Assertions.assertThat; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; + +import com.hubspot.jinjava.Jinjava; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; + +public class ToJsonFilterTest { + + JinjavaInterpreter interpreter; + ToJsonFilter filter; + + @Before + public void setup() { + interpreter = new Jinjava().newInterpreter(); + filter = new ToJsonFilter(); + } + + @Test + public void itWritesObjectAsString() { + + int[] testArray = new int[] {4, 1, 2}; + assertThat(filter.filter(testArray, interpreter)).isEqualTo("[4,1,2]"); + + Map testMap = new HashMap<>(); + testMap.put("testArray", testArray); + testMap.put("testString", "testString"); + assertThat(filter.filter(testMap, interpreter)).isEqualTo("{\"testArray\":[4,1,2],\"testString\":\"testString\"}"); + } +} From 319ccb227394e8a6d54a435806c4042504d41704 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Mon, 23 Jul 2018 16:50:43 -0400 Subject: [PATCH 0469/2465] Slight doc change. --- .../java/com/hubspot/jinjava/lib/filter/ToJsonFilter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/ToJsonFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/ToJsonFilter.java index b44bb4acc..5ba4ca7a5 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/ToJsonFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/ToJsonFilter.java @@ -10,13 +10,13 @@ @JinjavaDoc( - value = "Writes objects as JSON strings", + value = "Writes object as a JSON string", params = { @JinjavaParam(value = "o", desc = "Object to write to JSON") }, snippets = { @JinjavaSnippet( - code = "{{object|escapejson}}" + code = "{{object|tojson}}" ) }) public class ToJsonFilter implements Filter { From ed08c459036ff38e7a6d6a4735d9eb24802eac8f Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Mon, 23 Jul 2018 16:52:33 -0400 Subject: [PATCH 0470/2465] Add dep. --- pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pom.xml b/pom.xml index f40cedd37..48d3fa829 100644 --- a/pom.xml +++ b/pom.xml @@ -72,6 +72,10 @@ com.fasterxml.jackson.core jackson-databind + + com.fasterxml.jackson.core + jackson-core + ch.qos.logback From d424919977bc5d4c0f755aa5bf4649d76dd24aa4 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Fri, 10 Aug 2018 12:10:53 -0400 Subject: [PATCH 0471/2465] Add filter to convert JSON string to Map. --- .../jinjava/lib/filter/FromJsonFilter.java | 46 ++++++++++++++++ .../jinjava/lib/filter/ToJsonFilter.java | 2 +- .../lib/filter/FromJsonFilterTest.java | 55 +++++++++++++++++++ .../jinjava/lib/filter/ToJsonFilterTest.java | 4 +- 4 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/hubspot/jinjava/lib/filter/FromJsonFilter.java create mode 100644 src/test/java/com/hubspot/jinjava/lib/filter/FromJsonFilterTest.java diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/FromJsonFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/FromJsonFilter.java new file mode 100644 index 000000000..47034367f --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/lib/filter/FromJsonFilter.java @@ -0,0 +1,46 @@ +package com.hubspot.jinjava.lib.filter; + +import java.io.IOException; +import java.util.HashMap; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.hubspot.jinjava.doc.annotations.JinjavaDoc; +import com.hubspot.jinjava.doc.annotations.JinjavaParam; +import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; +import com.hubspot.jinjava.interpret.InterpretException; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; + +@JinjavaDoc( + value = "Converts JSON string to Object", + params = { + @JinjavaParam(value = "s", desc = "JSON String to write to object") + }, + snippets = { + @JinjavaSnippet( + code = "{{object|fromJson}}" + ) + }) +public class FromJsonFilter implements Filter { + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + @Override + public Object filter(Object var, JinjavaInterpreter interpreter, String... args) { + try { + + if (var instanceof String) { + return OBJECT_MAPPER.readValue((String) var, HashMap.class); + } else { + throw new InterpretException(String.format("%s filter requires a string parameter", getName())); + } + + } catch (IOException e) { + throw new InterpretException("Could not convert JSON string to object in `fromjson` filter.", e, interpreter.getLineNumber()); + } + } + + @Override + public String getName() { + return "fromjson"; + } +} diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/ToJsonFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/ToJsonFilter.java index 5ba4ca7a5..ef657641f 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/ToJsonFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/ToJsonFilter.java @@ -28,7 +28,7 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args) try { return OBJECT_MAPPER.writeValueAsString(var); } catch (JsonProcessingException e) { - throw new InterpretException("Could not write object as string for `escapejson` filter.", e, interpreter.getLineNumber()); + throw new InterpretException("Could not write object as string for `tojson` filter.", e, interpreter.getLineNumber()); } } diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/FromJsonFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/FromJsonFilterTest.java new file mode 100644 index 000000000..005d980bd --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/lib/filter/FromJsonFilterTest.java @@ -0,0 +1,55 @@ +package com.hubspot.jinjava.lib.filter; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Arrays; +import java.util.HashMap; + +import org.junit.Before; +import org.junit.Test; + +import com.hubspot.jinjava.Jinjava; +import com.hubspot.jinjava.interpret.InterpretException; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; + + +public class FromJsonFilterTest { + + private JinjavaInterpreter interpreter; + private FromJsonFilter filter; + + @Before + public void setup() { + interpreter = new Jinjava().newInterpreter(); + filter = new FromJsonFilter(); + } + + @Test + public void itReadsStringAsObject() { + + String nestedJson = "{\"first\":[1,2,3],\"nested\":{\"second\":\"string\",\"third\":4}}"; + + HashMap node = (HashMap) filter.filter(nestedJson, interpreter); + assertThat(node.get("first")).isEqualTo(Arrays.asList(1, 2, 3)); + + HashMap nested = (HashMap) node.get("nested"); + assertThat(nested.get("second")).isEqualTo("string"); + assertThat(nested.get("third")).isEqualTo(4); + } + + @Test(expected = InterpretException.class) + public void itFailsWhenStringIsNotJson() { + + String nestedJson = "blah"; + + filter.filter(nestedJson, interpreter); + } + + @Test(expected = InterpretException.class) + public void itFailsWhenParameterIsNotString() { + + Integer nestedJson = 456; + + filter.filter(nestedJson, interpreter); + } +} diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/ToJsonFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/ToJsonFilterTest.java index 594362fee..bb6a20818 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/ToJsonFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/ToJsonFilterTest.java @@ -13,8 +13,8 @@ public class ToJsonFilterTest { - JinjavaInterpreter interpreter; - ToJsonFilter filter; + private JinjavaInterpreter interpreter; + private ToJsonFilter filter; @Before public void setup() { From bff5fa4f6c8d1ac942526d4caf9a4ef55d74d5b9 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Fri, 10 Aug 2018 14:39:21 -0400 Subject: [PATCH 0472/2465] Register json filters. --- .../java/com/hubspot/jinjava/lib/filter/FilterLibrary.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java b/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java index 543fac729..c28d60d00 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java @@ -99,7 +99,9 @@ protected void registerDefaults() { TitleFilter.class, TrimFilter.class, WordCountFilter.class, - WordWrapFilter.class); + WordWrapFilter.class, + ToJsonFilter.class, + FromJsonFilter.class); } public Filter getFilter(String filterName) { From c9c1464ee02bbb9ebd7ff311d618b445ce3cd666 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Fri, 10 Aug 2018 15:12:50 -0400 Subject: [PATCH 0473/2465] Include raw object in groups. --- .../jinjava/lib/filter/GroupByFilter.java | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/GroupByFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/GroupByFilter.java index 5c13ebdee..cb75378cf 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/GroupByFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/GroupByFilter.java @@ -1,7 +1,9 @@ package com.hubspot.jinjava.lib.filter; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import com.google.common.collect.LinkedListMultimap; @@ -50,18 +52,24 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args) ForLoop loop = ObjectIterator.getLoop(var); Multimap groupBuckets = LinkedListMultimap.create(); + Map groupMapRaw = new HashMap<>(); while (loop.hasNext()) { Object val = loop.next(); - String grouper = Objects.toString(interpreter.resolveProperty(val, attr)); + Object resolvedProperty = interpreter.resolveProperty(val, attr); + String grouper = Objects.toString(resolvedProperty); groupBuckets.put(grouper, val); + + if (!groupMapRaw.containsKey(grouper)) { + groupMapRaw.put(grouper, resolvedProperty); + } } List groups = new ArrayList<>(); for (String grouper : groupBuckets.keySet()) { List list = Lists.newArrayList(groupBuckets.get(grouper)); - groups.add(new Group(grouper, list)); + groups.add(new Group(grouper, groupMapRaw.get(grouper), list)); } return groups; @@ -69,10 +77,12 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args) public static class Group { private final String grouper; + private final Object grouperObject private final List list; - public Group(String grouper, List list) { + public Group(String grouper, Object grouperObject, List list) { this.grouper = grouper; + this.grouperObject = grouperObject; this.list = list; } @@ -80,6 +90,10 @@ public String getGrouper() { return grouper; } + public Object getGrouperObject() { + return grouperObject; + } + public List getList() { return list; } From 701193465e86b5c83f347678ac38baa193d53f68 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Fri, 10 Aug 2018 15:13:06 -0400 Subject: [PATCH 0474/2465] Include raw object in groups. --- src/main/java/com/hubspot/jinjava/lib/filter/GroupByFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/GroupByFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/GroupByFilter.java index cb75378cf..8418c0815 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/GroupByFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/GroupByFilter.java @@ -77,7 +77,7 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args) public static class Group { private final String grouper; - private final Object grouperObject + private final Object grouperObject; private final List list; public Group(String grouper, Object grouperObject, List list) { From a50db6bbda9987d80bd0d2cdeb76e29093090264 Mon Sep 17 00:00:00 2001 From: Libo Song Date: Fri, 10 Aug 2018 15:32:58 -0400 Subject: [PATCH 0475/2465] Make jinjajav interpreter render timings trackable.. --- .../jinjava/interpret/JinjavaInterpreter.java | 20 +++++++++++++++++++ .../jinjava/interpret/RenderTimings.java | 9 +++++++++ 2 files changed, 29 insertions(+) create mode 100644 src/main/java/com/hubspot/jinjava/interpret/RenderTimings.java diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index 5a1501e57..600b31c38 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -479,4 +479,24 @@ public static void popCurrent() { } } + public void startRender(String name) { + RenderTimings renderTimings = (RenderTimings) getContext().get("request"); + if (renderTimings != null) { + renderTimings.start(this, name); + } + } + + public void endRender(String name) { + RenderTimings renderTimings = (RenderTimings) getContext().get("request"); + if (renderTimings != null) { + renderTimings.end(this, name); + } + } + + public void endRender(String name, Map data) { + RenderTimings renderTimings = (RenderTimings) getContext().get("request"); + if (renderTimings != null) { + renderTimings.end(this, name, data); + } + } } diff --git a/src/main/java/com/hubspot/jinjava/interpret/RenderTimings.java b/src/main/java/com/hubspot/jinjava/interpret/RenderTimings.java new file mode 100644 index 000000000..2e4300289 --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/interpret/RenderTimings.java @@ -0,0 +1,9 @@ +package com.hubspot.jinjava.interpret; + +import java.util.Map; + +public interface RenderTimings { + void start(JinjavaInterpreter interpreter, String name); + void end(JinjavaInterpreter interpreter, String name); + void end(JinjavaInterpreter interpreter, String name, Map data); +} From f21c6dad712cdc3cc2b1bc2c42956b061b65d119 Mon Sep 17 00:00:00 2001 From: Libo Song Date: Thu, 16 Aug 2018 12:34:14 -0400 Subject: [PATCH 0476/2465] Detect fromTag cycle. --- .../com/hubspot/jinjava/interpret/Context.java | 15 +++++++++++++++ .../jinjava/interpret/FromTagCycleException.java | 10 ++++++++++ .../jinjava/interpret/JinjavaInterpreter.java | 6 +++++- .../jinjava/interpret/TagCycleException.java | 2 ++ .../BasicTemplateErrorCategory.java | 1 + .../com/hubspot/jinjava/lib/tag/FromTag.java | 16 ++++++++++++++++ .../com/hubspot/jinjava/lib/tag/FromTagTest.java | 9 +++++++++ .../resources/tags/macrotag/from-recursion.jinja | 4 ++++ 8 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/hubspot/jinjava/interpret/FromTagCycleException.java create mode 100644 src/test/resources/tags/macrotag/from-recursion.jinja diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index 6df434dda..7d704b2e4 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. **********************************************************************/ + package com.hubspot.jinjava.interpret; import java.util.ArrayList; @@ -59,6 +60,7 @@ public enum Library { private final CallStack importPathStack; private final CallStack includePathStack; private final CallStack macroStack; + private final CallStack fromStack; private final Set resolvedExpressions = new HashSet<>(); private final Set resolvedValues = new HashSet<>(); @@ -106,6 +108,8 @@ public Context(Context parent, Map bindings, Map this.includePathStack = new CallStack(parent == null ? null : parent.getIncludePathStack(), IncludeTagCycleException.class); this.macroStack = new CallStack(parent == null ? null : parent.getMacroStack(), MacroTagCycleException.class); + this.fromStack = new CallStack(parent == null ? null : parent.getFromStack(), + FromTagCycleException.class); if (disabled == null) { disabled = new HashMap>(); @@ -377,10 +381,21 @@ public CallStack getIncludePathStack() { return includePathStack; } + private CallStack getFromStack() { + return fromStack; + } + public CallStack getMacroStack() { return macroStack; } + public void pushFromStack(String path, int lineNumber, int startPosition) { + fromStack.push(path, lineNumber, startPosition); + } + public void popFromStack() { + fromStack.pop(); + } + public int getRenderDepth() { if (renderDepth != -1) { return renderDepth; diff --git a/src/main/java/com/hubspot/jinjava/interpret/FromTagCycleException.java b/src/main/java/com/hubspot/jinjava/interpret/FromTagCycleException.java new file mode 100644 index 000000000..8657fe404 --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/interpret/FromTagCycleException.java @@ -0,0 +1,10 @@ +package com.hubspot.jinjava.interpret; + +public class FromTagCycleException extends TagCycleException { + private static final long serialVersionUID = -5487642459443650227L; + + public FromTagCycleException(String path, int lineNumber, int startPosition) { + super("From", path, lineNumber, startPosition); + } + +} diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index 600b31c38..f6366669a 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. **********************************************************************/ + package com.hubspot.jinjava.interpret; import static com.hubspot.jinjava.util.Logging.ENGINE_LOG; @@ -444,7 +445,10 @@ public int getPosition() { } public void addError(TemplateError templateError) { - this.errors.add(templateError.withScopeDepth(scopeDepth)); + // Limit the number of error. + if (errors.size() < 20) { + this.errors.add(templateError.withScopeDepth(scopeDepth)); + } } public int getScopeDepth() { diff --git a/src/main/java/com/hubspot/jinjava/interpret/TagCycleException.java b/src/main/java/com/hubspot/jinjava/interpret/TagCycleException.java index 6db3d0768..fe0e1ebb3 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/TagCycleException.java +++ b/src/main/java/com/hubspot/jinjava/interpret/TagCycleException.java @@ -37,6 +37,8 @@ public static TagCycleException create(Class clazz, return new IncludeTagCycleException(path, line, position); } else if (clazz.equals(MacroTagCycleException.class)) { return new MacroTagCycleException(path, line, position); + } else if (clazz.equals(FromTagCycleException.class)) { + return new FromTagCycleException(path, line, position); } } diff --git a/src/main/java/com/hubspot/jinjava/interpret/errorcategory/BasicTemplateErrorCategory.java b/src/main/java/com/hubspot/jinjava/interpret/errorcategory/BasicTemplateErrorCategory.java index 0afe455a5..ccfcaa912 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/errorcategory/BasicTemplateErrorCategory.java +++ b/src/main/java/com/hubspot/jinjava/interpret/errorcategory/BasicTemplateErrorCategory.java @@ -4,6 +4,7 @@ public enum BasicTemplateErrorCategory implements TemplateErrorCategory { CYCLE_DETECTED, IMPORT_CYCLE_DETECTED, INCLUDE_CYCLE_DETECTED, + FROM_CYCLE_DETECTED, UNKNOWN, UNKNOWN_DATE, UNKNOWN_LOCALE, diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/FromTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/FromTag.java index 1fcc3d5c6..8c000e413 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/FromTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/FromTag.java @@ -5,14 +5,21 @@ import java.util.List; import java.util.Map; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterators; import com.google.common.collect.PeekingIterator; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaParam; import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; +import com.hubspot.jinjava.interpret.FromTagCycleException; import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.interpret.TemplateError; +import com.hubspot.jinjava.interpret.TemplateError.ErrorItem; +import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; +import com.hubspot.jinjava.interpret.TemplateError.ErrorType; import com.hubspot.jinjava.interpret.TemplateSyntaxException; +import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory; import com.hubspot.jinjava.lib.fn.MacroFunction; import com.hubspot.jinjava.tree.Node; import com.hubspot.jinjava.tree.TagNode; @@ -55,6 +62,15 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { } String templateFile = interpreter.resolveString(helper.get(0), tagNode.getLineNumber(), tagNode.getStartPosition()); + try { + interpreter.getContext().pushFromStack(templateFile, tagNode.getLineNumber(), tagNode.getStartPosition()); + } catch (FromTagCycleException e) { + interpreter.addError(new TemplateError(ErrorType.WARNING, ErrorReason.EXCEPTION, ErrorItem.TAG, + "From cycle detected for path: '" + templateFile + "'", null, tagNode.getLineNumber(), tagNode + .getStartPosition(), e, + BasicTemplateErrorCategory.FROM_CYCLE_DETECTED, ImmutableMap.of("path", templateFile))); + return ""; + } Map imports = new LinkedHashMap<>(); PeekingIterator args = Iterators.peekingIterator(helper.subList(2, helper.size()).iterator()); diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/FromTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/FromTagTest.java index 1de50f41e..83c5f7379 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/FromTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/FromTagTest.java @@ -1,6 +1,7 @@ package com.hubspot.jinjava.lib.tag; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertTrue; import java.io.IOException; import java.nio.charset.Charset; @@ -15,6 +16,7 @@ import com.hubspot.jinjava.Jinjava; import com.hubspot.jinjava.interpret.Context; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory; import com.hubspot.jinjava.loader.ResourceLocator; public class FromTagTest { @@ -54,6 +56,13 @@ public void importedContextExposesVars() { .contains("wrap-padding: padding-left:42px;padding-right:42px"); } + @Test + public void importedCycleDected() { + fixture("from-recursion"); + assertTrue(interpreter.getErrors().stream() + .anyMatch(e -> e.getCategory() == BasicTemplateErrorCategory.FROM_CYCLE_DETECTED)); + } + private String fixture(String name) { try { return interpreter.renderFlat(Resources.toString( diff --git a/src/test/resources/tags/macrotag/from-recursion.jinja b/src/test/resources/tags/macrotag/from-recursion.jinja new file mode 100644 index 000000000..ef98a5110 --- /dev/null +++ b/src/test/resources/tags/macrotag/from-recursion.jinja @@ -0,0 +1,4 @@ +{% from "from-recursion.jinja" import wrap_padding as wp, spacer %} + +wrap-padding: {{ wp }} +wrap-spacer: {{ spacer() }} From 5b0af3a9e4d56284ab1a3865adb0ba16b5c1c18f Mon Sep 17 00:00:00 2001 From: Libo Song Date: Thu, 16 Aug 2018 13:03:39 -0400 Subject: [PATCH 0477/2465] Add missing pop, indirect cycle test. --- .../hubspot/jinjava/interpret/Context.java | 1 + .../com/hubspot/jinjava/lib/tag/FromTag.java | 62 ++++++++++--------- .../hubspot/jinjava/lib/tag/FromTagTest.java | 7 +++ .../resources/tags/macrotag/from-a-to-b.jinja | 4 ++ .../resources/tags/macrotag/from-b-to-c.jinja | 4 ++ .../resources/tags/macrotag/from-c-to-a.jinja | 4 ++ 6 files changed, 53 insertions(+), 29 deletions(-) create mode 100644 src/test/resources/tags/macrotag/from-a-to-b.jinja create mode 100644 src/test/resources/tags/macrotag/from-b-to-c.jinja create mode 100644 src/test/resources/tags/macrotag/from-c-to-a.jinja diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index 7d704b2e4..6cb3d7a40 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -392,6 +392,7 @@ public CallStack getMacroStack() { public void pushFromStack(String path, int lineNumber, int startPosition) { fromStack.push(path, lineNumber, startPosition); } + public void popFromStack() { fromStack.pop(); } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/FromTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/FromTag.java index 8c000e413..559914e51 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/FromTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/FromTag.java @@ -71,48 +71,52 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { BasicTemplateErrorCategory.FROM_CYCLE_DETECTED, ImmutableMap.of("path", templateFile))); return ""; } - Map imports = new LinkedHashMap<>(); - - PeekingIterator args = Iterators.peekingIterator(helper.subList(2, helper.size()).iterator()); + try { + Map imports = new LinkedHashMap<>(); - while (args.hasNext()) { - String fromName = args.next(); - String importName = fromName; + PeekingIterator args = Iterators.peekingIterator(helper.subList(2, helper.size()).iterator()); - if (args.hasNext() && args.peek() != null && args.peek().equals("as")) { - args.next(); - importName = args.next(); - } + while (args.hasNext()) { + String fromName = args.next(); + String importName = fromName; - imports.put(fromName, importName); - } + if (args.hasNext() && args.peek() != null && args.peek().equals("as")) { + args.next(); + importName = args.next(); + } - try { - String template = interpreter.getResource(templateFile); - Node node = interpreter.parse(template); + imports.put(fromName, importName); + } - JinjavaInterpreter child = new JinjavaInterpreter(interpreter); - child.render(node); + try { + String template = interpreter.getResource(templateFile); + Node node = interpreter.parse(template); - interpreter.getErrors().addAll(child.getErrors()); + JinjavaInterpreter child = new JinjavaInterpreter(interpreter); + child.render(node); - for (Map.Entry importMapping : imports.entrySet()) { - Object val = child.getContext().getGlobalMacro(importMapping.getKey()); + interpreter.getErrors().addAll(child.getErrors()); - if (val != null) { - interpreter.getContext().addGlobalMacro((MacroFunction) val); - } else { - val = child.getContext().get(importMapping.getKey()); + for (Map.Entry importMapping : imports.entrySet()) { + Object val = child.getContext().getGlobalMacro(importMapping.getKey()); if (val != null) { - interpreter.getContext().put(importMapping.getValue(), val); + interpreter.getContext().addGlobalMacro((MacroFunction) val); + } else { + val = child.getContext().get(importMapping.getKey()); + + if (val != null) { + interpreter.getContext().put(importMapping.getValue(), val); + } } } - } - return ""; - } catch (IOException e) { - throw new InterpretException(e.getMessage(), e, tagNode.getLineNumber(), tagNode.getStartPosition()); + return ""; + } catch (IOException e) { + throw new InterpretException(e.getMessage(), e, tagNode.getLineNumber(), tagNode.getStartPosition()); + } + } finally { + interpreter.getContext().popFromStack(); } } diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/FromTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/FromTagTest.java index 83c5f7379..6c4100bb2 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/FromTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/FromTagTest.java @@ -63,6 +63,13 @@ public void importedCycleDected() { .anyMatch(e -> e.getCategory() == BasicTemplateErrorCategory.FROM_CYCLE_DETECTED)); } + @Test + public void importedIndirectCycleDected() { + fixture("from-a-to-b"); + assertTrue(interpreter.getErrors().stream() + .anyMatch(e -> e.getCategory() == BasicTemplateErrorCategory.FROM_CYCLE_DETECTED)); + } + private String fixture(String name) { try { return interpreter.renderFlat(Resources.toString( diff --git a/src/test/resources/tags/macrotag/from-a-to-b.jinja b/src/test/resources/tags/macrotag/from-a-to-b.jinja new file mode 100644 index 000000000..a7ff13e82 --- /dev/null +++ b/src/test/resources/tags/macrotag/from-a-to-b.jinja @@ -0,0 +1,4 @@ +{% from "from-b-to-c.jinja" import wrap_padding as wp, spacer %} + +wrap-padding: {{ wp }} +wrap-spacer: {{ spacer() }} diff --git a/src/test/resources/tags/macrotag/from-b-to-c.jinja b/src/test/resources/tags/macrotag/from-b-to-c.jinja new file mode 100644 index 000000000..f0525647b --- /dev/null +++ b/src/test/resources/tags/macrotag/from-b-to-c.jinja @@ -0,0 +1,4 @@ +{% from "from-c-to-a.jinja" import wrap_padding as wp, spacer %} + +wrap-padding: {{ wp }} +wrap-spacer: {{ spacer() }} diff --git a/src/test/resources/tags/macrotag/from-c-to-a.jinja b/src/test/resources/tags/macrotag/from-c-to-a.jinja new file mode 100644 index 000000000..f0525647b --- /dev/null +++ b/src/test/resources/tags/macrotag/from-c-to-a.jinja @@ -0,0 +1,4 @@ +{% from "from-c-to-a.jinja" import wrap_padding as wp, spacer %} + +wrap-padding: {{ wp }} +wrap-spacer: {{ spacer() }} From 2e6ffaaae53729982bb4cafa07561b22f24fe8b9 Mon Sep 17 00:00:00 2001 From: Libo Song Date: Thu, 16 Aug 2018 15:16:58 -0400 Subject: [PATCH 0478/2465] Limit the nubmer errors. --- .../java/com/hubspot/jinjava/Jinjava.java | 8 ++-- .../jinjava/interpret/JinjavaInterpreter.java | 21 +++++++++- .../com/hubspot/jinjava/lib/tag/FromTag.java | 2 +- .../hubspot/jinjava/lib/tag/ImportTag.java | 2 +- .../hubspot/jinjava/lib/tag/IncludeTag.java | 2 +- .../jinjava/el/ExpressionResolverTest.java | 36 ++++++++-------- .../jinjava/el/ExtendedSyntaxBuilderTest.java | 42 +++++++++---------- .../jinjava/interpret/TemplateErrorTest.java | 2 +- .../lib/filter/DateTimeFormatFilterTest.java | 2 +- .../lib/filter/UnixTimestampFilterTest.java | 2 +- .../hubspot/jinjava/lib/tag/FromTagTest.java | 4 +- .../jinjava/lib/tag/ImportTagTest.java | 6 +-- .../hubspot/jinjava/lib/tag/MacroTagTest.java | 8 ++-- .../hubspot/jinjava/tree/TreeParserTest.java | 2 +- 14 files changed, 78 insertions(+), 61 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/Jinjava.java b/src/main/java/com/hubspot/jinjava/Jinjava.java index 619dc2431..28f60831a 100644 --- a/src/main/java/com/hubspot/jinjava/Jinjava.java +++ b/src/main/java/com/hubspot/jinjava/Jinjava.java @@ -197,14 +197,14 @@ public RenderResult renderForResult(String template, Map bindings, Ji try { String result = interpreter.render(template); - return new RenderResult(result, interpreter.getContext(), interpreter.getErrors()); + return new RenderResult(result, interpreter.getContext(), interpreter.getErrorsCopy()); } catch (InterpretException e) { if (e instanceof TemplateSyntaxException) { - return new RenderResult(TemplateError.fromException((TemplateSyntaxException) e), interpreter.getContext(), interpreter.getErrors()); + return new RenderResult(TemplateError.fromException((TemplateSyntaxException) e), interpreter.getContext(), interpreter.getErrorsCopy()); } - return new RenderResult(TemplateError.fromSyntaxError(e), interpreter.getContext(), interpreter.getErrors()); + return new RenderResult(TemplateError.fromSyntaxError(e), interpreter.getContext(), interpreter.getErrorsCopy()); } catch (Exception e) { - return new RenderResult(TemplateError.fromException(e), interpreter.getContext(), interpreter.getErrors()); + return new RenderResult(TemplateError.fromException(e), interpreter.getContext(), interpreter.getErrorsCopy()); } finally { JinjavaInterpreter.popCurrent(); } diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index f6366669a..45e81100e 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -36,6 +36,7 @@ import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import com.hubspot.jinjava.Jinjava; import com.hubspot.jinjava.JinjavaConfig; @@ -71,6 +72,7 @@ public class JinjavaInterpreter { private int position = 0; private int scopeDepth = 1; private final List errors = new LinkedList<>(); + private static final int MAX_ERROR_SIZE = 100; public JinjavaInterpreter(Jinjava application, Context context, JinjavaConfig renderConfig) { this.context = context; @@ -446,7 +448,7 @@ public int getPosition() { public void addError(TemplateError templateError) { // Limit the number of error. - if (errors.size() < 20) { + if (errors.size() < MAX_ERROR_SIZE) { this.errors.add(templateError.withScopeDepth(scopeDepth)); } } @@ -455,8 +457,23 @@ public int getScopeDepth() { return scopeDepth; } + public void addAllErrors(Collection other) { + if (errors.size() >= MAX_ERROR_SIZE) { + return; + } + other.stream() + .limit(MAX_ERROR_SIZE - errors.size()) + .forEach(errors::add); + } + + // We cannot just remove this, other projects may depend on it. public List getErrors() { - return errors; + return getErrorsCopy(); + } + + // Explicitly indicate this returns a copy of the errors list. + public List getErrorsCopy() { + return Lists.newArrayList(errors); } private static final ThreadLocal> CURRENT_INTERPRETER = ThreadLocal.withInitial(Stack::new); diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/FromTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/FromTag.java index 559914e51..4825567fe 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/FromTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/FromTag.java @@ -95,7 +95,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { JinjavaInterpreter child = new JinjavaInterpreter(interpreter); child.render(node); - interpreter.getErrors().addAll(child.getErrors()); + interpreter.addAllErrors(child.getErrorsCopy()); for (Map.Entry importMapping : imports.entrySet()) { Object val = child.getContext().getGlobalMacro(importMapping.getKey()); diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java index f3ad019f8..3cfd859b0 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java @@ -97,7 +97,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { JinjavaInterpreter child = new JinjavaInterpreter(interpreter); child.render(node); - interpreter.getErrors().addAll(child.getErrors()); + interpreter.addAllErrors(child.getErrorsCopy()); Map childBindings = child.getContext().getSessionBindings(); for (Map.Entry macro : child.getContext().getGlobalMacros().entrySet()) { diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java index 8158f772b..6dc0c00cb 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/IncludeTag.java @@ -77,7 +77,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { JinjavaInterpreter child = new JinjavaInterpreter(interpreter); String result = child.render(node); - interpreter.getErrors().addAll(child.getErrors()); + interpreter.addAllErrors(child.getErrorsCopy()); return result; diff --git a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java index 3edd003ec..a360a5f87 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java @@ -285,14 +285,14 @@ public void complexInWithOrCondition() { @Test public void unknownProperty() { interpreter.resolveELExpression("foo", 23); - assertThat(interpreter.getErrors()).isEmpty(); + assertThat(interpreter.getErrorsCopy()).isEmpty(); context.put("foo", new Object()); interpreter.resolveELExpression("foo.bar", 23); - assertThat(interpreter.getErrors()).hasSize(1); + assertThat(interpreter.getErrorsCopy()).hasSize(1); - TemplateError e = interpreter.getErrors().get(0); + TemplateError e = interpreter.getErrorsCopy().get(0); assertThat(e.getReason()).isEqualTo(ErrorReason.UNKNOWN); assertThat(e.getLineno()).isEqualTo(23); assertThat(e.getFieldName()).isEqualTo("bar"); @@ -302,9 +302,9 @@ public void unknownProperty() { @Test public void syntaxError() { interpreter.resolveELExpression("(*&W", 123); - assertThat(interpreter.getErrors()).hasSize(1); + assertThat(interpreter.getErrorsCopy()).hasSize(1); - TemplateError e = interpreter.getErrors().get(0); + TemplateError e = interpreter.getErrorsCopy().get(0); assertThat(e.getReason()).isEqualTo(ErrorReason.SYNTAX_ERROR); assertThat(e.getLineno()).isEqualTo(123); assertThat(e.getMessage()).contains("invalid character"); @@ -323,8 +323,8 @@ public void blackListedProperties() { context.put("myobj", new MyClass(new Date(0))); interpreter.resolveELExpression("myobj.class.methods[0]", -1); - assertThat(interpreter.getErrors()).isNotEmpty(); - TemplateError e = interpreter.getErrors().get(0); + assertThat(interpreter.getErrorsCopy()).isNotEmpty(); + TemplateError e = interpreter.getErrorsCopy().get(0); assertThat(e.getReason()).isEqualTo(ErrorReason.UNKNOWN); assertThat(e.getFieldName()).isEqualTo("class"); assertThat(e.getMessage()).contains("Cannot resolve property 'class'"); @@ -335,8 +335,8 @@ public void blackListedMethods() { context.put("myobj", new MyClass(new Date(0))); interpreter.resolveELExpression("myobj.wait()", -1); - assertThat(interpreter.getErrors()).isNotEmpty(); - TemplateError e = interpreter.getErrors().get(0); + assertThat(interpreter.getErrorsCopy()).isNotEmpty(); + TemplateError e = interpreter.getErrorsCopy().get(0); assertThat(e.getMessage()).contains("Cannot find method 'wait'"); } @@ -350,7 +350,7 @@ public void itBlocksDisabledTags() { interpreter.render("{% raw %} foo {% endraw %}"); } - TemplateError e = interpreter.getErrors().get(0); + TemplateError e = interpreter.getErrorsCopy().get(0); assertThat(e.getItem()).isEqualTo(ErrorItem.TAG); assertThat(e.getReason()).isEqualTo(ErrorReason.DISABLED); assertThat(e.getMessage()).contains("'raw' is disabled in this context"); @@ -367,7 +367,7 @@ public void itBlocksDisabledTagsInIncludes() { try (JinjavaInterpreter.InterpreterScopeClosable c = interpreter.enterScope(disabled)) { interpreter.render(jinja); } - TemplateError e = interpreter.getErrors().get(0); + TemplateError e = interpreter.getErrorsCopy().get(0); assertThat(e.getItem()).isEqualTo(ErrorItem.TAG); assertThat(e.getReason()).isEqualTo(ErrorReason.DISABLED); assertThat(e.getMessage()).contains("'raw' is disabled in this context"); @@ -381,7 +381,7 @@ public void itBlocksDisabledFilters() { try (JinjavaInterpreter.InterpreterScopeClosable c = interpreter.enterScope(disabled)) { interpreter.resolveELExpression("\"hey\"|truncate(2)", -1); - TemplateError e = interpreter.getErrors().get(0); + TemplateError e = interpreter.getErrorsCopy().get(0); assertThat(e.getItem()).isEqualTo(ErrorItem.FILTER); assertThat(e.getReason()).isEqualTo(ErrorReason.DISABLED); assertThat(e.getMessage()).contains("truncate' is disabled in this context"); @@ -416,7 +416,7 @@ public void itBlocksDisabledExpTests() { try (JinjavaInterpreter.InterpreterScopeClosable c = interpreter.enterScope(disabled)) { interpreter.render("{% if 2 is even %}yes{% endif %}"); - TemplateError e = interpreter.getErrors().get(0); + TemplateError e = interpreter.getErrorsCopy().get(0); assertThat(e.getItem()).isEqualTo(ErrorItem.EXPRESSION_TEST); assertThat(e.getReason()).isEqualTo(ErrorReason.DISABLED); assertThat(e.getMessage()).contains("even' is disabled in this context"); @@ -437,14 +437,14 @@ public void itStoresResolvedFunctions() { public void presentOptionalProperty() { context.put("myobj", new OptionalProperty(null, "foo")); assertThat(interpreter.resolveELExpression("myobj.val", -1)).isEqualTo("foo"); - assertThat(interpreter.getErrors()).isEmpty(); + assertThat(interpreter.getErrorsCopy()).isEmpty(); } @Test public void emptyOptionalProperty() { context.put("myobj", new OptionalProperty(null, null)); assertThat(interpreter.resolveELExpression("myobj.val", -1)).isNull(); - assertThat(interpreter.getErrors()).isEmpty(); + assertThat(interpreter.getErrorsCopy()).isEmpty(); } @Test @@ -452,14 +452,14 @@ public void presentNestedOptionalProperty() { context.put("myobj", new OptionalProperty(new MyClass(new Date(0)), "foo")); assertThat(Objects.toString(interpreter.resolveELExpression("myobj.nested.date", -1))).isEqualTo( "1970-01-01 00:00:00"); - assertThat(interpreter.getErrors()).isEmpty(); + assertThat(interpreter.getErrorsCopy()).isEmpty(); } @Test public void emptyNestedOptionalProperty() { context.put("myobj", new OptionalProperty(null, null)); assertThat(interpreter.resolveELExpression("myobj.nested.date", -1)).isNull(); - assertThat(interpreter.getErrors()).isEmpty(); + assertThat(interpreter.getErrorsCopy()).isEmpty(); } @Test @@ -467,7 +467,7 @@ public void presentNestedNestedOptionalProperty() { context.put("myobj", new NestedOptionalProperty(new OptionalProperty(new MyClass(new Date(0)), "foo"))); assertThat(Objects.toString(interpreter.resolveELExpression("myobj.nested.nested.date", -1))).isEqualTo( "1970-01-01 00:00:00"); - assertThat(interpreter.getErrors()).isEmpty(); + assertThat(interpreter.getErrorsCopy()).isEmpty(); } public static final class MyClass { diff --git a/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java b/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java index 8d5134ea2..f8b90afd3 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExtendedSyntaxBuilderTest.java @@ -238,42 +238,42 @@ public void listRangeSyntax() { @Test public void invalidNestedAssignmentExpr() { assertThat(val("content.template_path = 'Custom/Email/Responsive/testing.html'")).isEqualTo(""); - assertThat(interpreter.getErrors()).isNotEmpty(); - assertThat(interpreter.getErrors().get(0).getReason()).isEqualTo(ErrorReason.SYNTAX_ERROR); - assertThat(interpreter.getErrors().get(0).getMessage()).containsIgnoringCase("identifier"); + assertThat(interpreter.getErrorsCopy()).isNotEmpty(); + assertThat(interpreter.getErrorsCopy().get(0).getReason()).isEqualTo(ErrorReason.SYNTAX_ERROR); + assertThat(interpreter.getErrorsCopy().get(0).getMessage()).containsIgnoringCase("identifier"); } @Test public void invalidIdentifierAssignmentExpr() { assertThat(val("content = 'Custom/Email/Responsive/testing.html'")).isEqualTo(""); - assertThat(interpreter.getErrors()).isNotEmpty(); - assertThat(interpreter.getErrors().get(0).getReason()).isEqualTo(ErrorReason.SYNTAX_ERROR); - assertThat(interpreter.getErrors().get(0).getMessage()).containsIgnoringCase("'='"); + assertThat(interpreter.getErrorsCopy()).isNotEmpty(); + assertThat(interpreter.getErrorsCopy().get(0).getReason()).isEqualTo(ErrorReason.SYNTAX_ERROR); + assertThat(interpreter.getErrorsCopy().get(0).getMessage()).containsIgnoringCase("'='"); } @Test public void invalidPipeOperatorExpr() { assertThat(val("topics|1")).isEqualTo(""); - assertThat(interpreter.getErrors()).isNotEmpty(); - assertThat(interpreter.getErrors().get(0).getReason()).isEqualTo(ErrorReason.SYNTAX_ERROR); + assertThat(interpreter.getErrorsCopy()).isNotEmpty(); + assertThat(interpreter.getErrorsCopy().get(0).getReason()).isEqualTo(ErrorReason.SYNTAX_ERROR); } @Test public void itReturnsCorrectSyntaxErrorPositions() { assertThat(interpreter.render("hi {{ missing thing }}{{ missing thing }}\nI am {{ blah blabbity }} too")).isEqualTo("hi \nI am too"); - assertThat(interpreter.getErrors().size()).isEqualTo(3); - assertThat(interpreter.getErrors().get(0).getLineno()).isEqualTo(1); - assertThat(interpreter.getErrors().get(0).getMessage()).contains("position 14"); - assertThat(interpreter.getErrors().get(0).getStartPosition()).isEqualTo(14); - assertThat(interpreter.getErrors().get(0).getFieldName()).isEqualTo("thing"); - assertThat(interpreter.getErrors().get(1).getLineno()).isEqualTo(1); - assertThat(interpreter.getErrors().get(1).getMessage()).contains("position 33"); - assertThat(interpreter.getErrors().get(1).getStartPosition()).isEqualTo(33); - assertThat(interpreter.getErrors().get(1).getFieldName()).isEqualTo("thing"); - assertThat(interpreter.getErrors().get(2).getLineno()).isEqualTo(2); - assertThat(interpreter.getErrors().get(2).getMessage()).contains("position 13"); - assertThat(interpreter.getErrors().get(2).getStartPosition()).isEqualTo(13); - assertThat(interpreter.getErrors().get(2).getFieldName()).isEqualTo("blabbity"); + assertThat(interpreter.getErrorsCopy().size()).isEqualTo(3); + assertThat(interpreter.getErrorsCopy().get(0).getLineno()).isEqualTo(1); + assertThat(interpreter.getErrorsCopy().get(0).getMessage()).contains("position 14"); + assertThat(interpreter.getErrorsCopy().get(0).getStartPosition()).isEqualTo(14); + assertThat(interpreter.getErrorsCopy().get(0).getFieldName()).isEqualTo("thing"); + assertThat(interpreter.getErrorsCopy().get(1).getLineno()).isEqualTo(1); + assertThat(interpreter.getErrorsCopy().get(1).getMessage()).contains("position 33"); + assertThat(interpreter.getErrorsCopy().get(1).getStartPosition()).isEqualTo(33); + assertThat(interpreter.getErrorsCopy().get(1).getFieldName()).isEqualTo("thing"); + assertThat(interpreter.getErrorsCopy().get(2).getLineno()).isEqualTo(2); + assertThat(interpreter.getErrorsCopy().get(2).getMessage()).contains("position 13"); + assertThat(interpreter.getErrorsCopy().get(2).getStartPosition()).isEqualTo(13); + assertThat(interpreter.getErrorsCopy().get(2).getFieldName()).isEqualTo("blabbity"); } private Object val(String expr) { diff --git a/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java b/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java index 9df710a3f..ff80af7a0 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/TemplateErrorTest.java @@ -37,7 +37,7 @@ public void itShowsFieldNameForSyntaxError() { public void itRetainsFieldNameCaseForUnknownToken() { JinjavaInterpreter interpreter = new Jinjava().newInterpreter(); interpreter.render("{% unKnown() %}"); - assertThat(interpreter.getErrors().get(0).getFieldName()).isEqualTo("unKnown"); + assertThat(interpreter.getErrorsCopy().get(0).getFieldName()).isEqualTo("unKnown"); } @Test diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilterTest.java index 6a5f71e8f..cd74e55a3 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilterTest.java @@ -55,7 +55,7 @@ public void itHandlesVarsAndLiterals() throws Exception { assertThat(interpreter.renderFlat("{{ d|datetimeformat(foo) }}")).isEqualTo("2013-11"); assertThat(interpreter.renderFlat("{{ d|datetimeformat(\"%Y-%m-%d\") }}")).isEqualTo("2013-11-06"); - assertThat(interpreter.getErrors()).isEmpty(); + assertThat(interpreter.getErrorsCopy()).isEmpty(); } } diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilterTest.java index 6e0835bac..f3897da6b 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/UnixTimestampFilterTest.java @@ -26,7 +26,7 @@ public void setup() { @After public void tearDown() throws Exception { - assertThat(interpreter.getErrors()).isEmpty(); + assertThat(interpreter.getErrorsCopy()).isEmpty(); } @Test diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/FromTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/FromTagTest.java index 6c4100bb2..8d404786d 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/FromTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/FromTagTest.java @@ -59,14 +59,14 @@ public void importedContextExposesVars() { @Test public void importedCycleDected() { fixture("from-recursion"); - assertTrue(interpreter.getErrors().stream() + assertTrue(interpreter.getErrorsCopy().stream() .anyMatch(e -> e.getCategory() == BasicTemplateErrorCategory.FROM_CYCLE_DETECTED)); } @Test public void importedIndirectCycleDected() { fixture("from-a-to-b"); - assertTrue(interpreter.getErrors().stream() + assertTrue(interpreter.getErrorsCopy().stream() .anyMatch(e -> e.getCategory() == BasicTemplateErrorCategory.FROM_CYCLE_DETECTED)); } diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/ImportTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/ImportTagTest.java index 9ece5c1b4..a5df05fdd 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/ImportTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/ImportTagTest.java @@ -56,7 +56,7 @@ public void itAvoidsSimpleImportCycle() throws IOException { interpreter.render(Resources.toString(Resources.getResource("tags/importtag/imports-self.jinja"), StandardCharsets.UTF_8)); assertThat(context.get("c")).isEqualTo("hello"); - assertThat(interpreter.getErrors().get(0).getMessage()).contains("Rendering cycle detected:", "imports-self.jinja"); + assertThat(interpreter.getErrorsCopy().get(0).getMessage()).contains("Rendering cycle detected:", "imports-self.jinja"); } @Test @@ -68,7 +68,7 @@ public void itAvoidsNestedImportCycle() throws IOException { assertThat(context.get("a")).isEqualTo("foo"); assertThat(context.get("b")).isEqualTo("bar"); - assertThat(interpreter.getErrors().get(0).getMessage()).contains("Rendering cycle detected:", "b-imports-a.jinja"); + assertThat(interpreter.getErrorsCopy().get(0).getMessage()).contains("Rendering cycle detected:", "b-imports-a.jinja"); } @Test @@ -95,7 +95,7 @@ public void importedContextDoesntExposePrivateMacros() { public void importedContextFnsProperlyResolveScopedVars() { String result = fixture("imports-macro-referencing-macro"); - assertThat(interpreter.getErrors()).isEmpty(); + assertThat(interpreter.getErrorsCopy()).isEmpty(); assertThat(result) .contains("using public fn: public fn: foo") .contains("using private fn: private fn: bar") diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java index 73a1fed1a..2fc42140c 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/MacroTagTest.java @@ -132,21 +132,21 @@ public void testMacroUsedInForLoop() throws Exception { public void itPreventsDirectMacroRecursion() throws IOException { String template = fixtureText("recursion"); interpreter.render(template); - assertThat(interpreter.getErrors().get(0).getMessage()).contains("Cycle detected for macro 'hello'"); + assertThat(interpreter.getErrorsCopy().get(0).getMessage()).contains("Cycle detected for macro 'hello'"); } @Test public void itPreventsIndirectMacroRecursion() throws IOException { String template = fixtureText("recursion_indirect"); interpreter.render(template); - assertThat(interpreter.getErrors().get(0).getMessage()).contains("Cycle detected for macro 'goodbye'"); + assertThat(interpreter.getErrorsCopy().get(0).getMessage()).contains("Cycle detected for macro 'goodbye'"); } @Test public void itAllowsMacrosCallingMacrosUsingCall() throws IOException { String template = fixtureText("macros-calling-macros"); String out = interpreter.render(template); - assertThat(interpreter.getErrors()).isEmpty(); + assertThat(interpreter.getErrorsCopy()).isEmpty(); assertThat(out).contains("Hello World One"); assertThat(out).contains("Hello World Two"); } @@ -160,7 +160,7 @@ public void itAllowsMacroRecursionWhenEnabledInConfiguration() throws IOExceptio try { String template = fixtureText("ending-recursion"); String out = interpreter.render(template); - assertThat(interpreter.getErrors()).isEmpty(); + assertThat(interpreter.getErrorsCopy()).isEmpty(); assertThat(out).contains("Hello Hello Hello Hello Hello"); } finally { diff --git a/src/test/java/com/hubspot/jinjava/tree/TreeParserTest.java b/src/test/java/com/hubspot/jinjava/tree/TreeParserTest.java index bf082faf7..1963d8369 100644 --- a/src/test/java/com/hubspot/jinjava/tree/TreeParserTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/TreeParserTest.java @@ -25,7 +25,7 @@ public void setup() { @Test public void parseHtmlWithCommentLines() { parse("parse/tokenizer/comment-plus.jinja"); - assertThat(interpreter.getErrors()).isEmpty(); + assertThat(interpreter.getErrorsCopy()).isEmpty(); } @Test From 2028eb63cfeaddfee95d2d83b74dfa9b180a749c Mon Sep 17 00:00:00 2001 From: hs-lsong Date: Thu, 16 Aug 2018 16:50:45 -0400 Subject: [PATCH 0479/2465] Update CHANGES.md --- CHANGES.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 20df1fad3..e7d524eb1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,15 @@ # Jinjava Releases # +### 2018-07-10 Version 2.4.5 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.5%7Cjar)) ### +* [Limit the number errors](https://github.com/HubSpot/jinjava/pull/222) +* [Detect fromTag cycle](https://github.com/HubSpot/jinjava/pull/221) +* [Make jinjajava interpreter render timings trackable] (https://github.com/HubSpot/jinjava/pull/219) +* [Add raw object to group in groupby filter](https://github.com/HubSpot/jinjava/pull/218) +* [Register json filters](https://github.com/HubSpot/jinjava/pull/216) +* [Add filter to convert JSON string to Map](https://github.com/HubSpot/jinjava/pull/215) +* [Add filter to convert objects to JSON](https://github.com/HubSpot/jinjava/pull/213) +* [Deepen equalto expression test comparison](https://github.com/HubSpot/jinjava/pull/211) + ### 2018-07-10 Version 2.4.4 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.4%7Cjar)) ### * [Fix calling macros with kwargs](https://github.com/HubSpot/jinjava/pull/208) * [Limit the size of strings in TemplateErrors](https://github.com/HubSpot/jinjava/pull/209) From d67aaa7e1c67c6edd59aeea48024e82f1a699234 Mon Sep 17 00:00:00 2001 From: hs-lsong Date: Thu, 16 Aug 2018 16:52:00 -0400 Subject: [PATCH 0480/2465] Update CHANGES.md update the date. --- CHANGES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e7d524eb1..72d396b23 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,9 +1,9 @@ # Jinjava Releases # -### 2018-07-10 Version 2.4.5 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.5%7Cjar)) ### +### 2018-08-16 Version 2.4.5 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.5%7Cjar)) ### * [Limit the number errors](https://github.com/HubSpot/jinjava/pull/222) * [Detect fromTag cycle](https://github.com/HubSpot/jinjava/pull/221) -* [Make jinjajava interpreter render timings trackable] (https://github.com/HubSpot/jinjava/pull/219) +* [Make jinjajava interpreter render timings trackable](https://github.com/HubSpot/jinjava/pull/219) * [Add raw object to group in groupby filter](https://github.com/HubSpot/jinjava/pull/218) * [Register json filters](https://github.com/HubSpot/jinjava/pull/216) * [Add filter to convert JSON string to Map](https://github.com/HubSpot/jinjava/pull/215) From 376927b71d331b9adf519e5ce3fa641d3d493ba5 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Thu, 16 Aug 2018 20:58:14 +0000 Subject: [PATCH 0481/2465] [maven-release-plugin] prepare release jinjava-2.4.5 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 48d3fa829..42827c55a 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.4.5-SNAPSHOT + 2.4.5 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.4.5 From f129a88a4bb22fc9ee0e6e0a3bf547788f2cbf43 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Thu, 16 Aug 2018 20:58:14 +0000 Subject: [PATCH 0482/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 42827c55a..3e56eab9a 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.4.5 + 2.4.6-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.4.5 + HEAD From dff736bde4d91fd946453a5474ace08d74d8aa1e Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Wed, 22 Aug 2018 17:01:56 -0400 Subject: [PATCH 0483/2465] clear resolved things in global context after render --- .../java/com/hubspot/jinjava/Jinjava.java | 1 + .../hubspot/jinjava/interpret/Context.java | 11 ++++++++-- .../jinjava/interpret/ContextTest.java | 21 +++++++++++++++++++ .../interpret/JinjavaInterpreterTest.java | 14 ++++++------- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/Jinjava.java b/src/main/java/com/hubspot/jinjava/Jinjava.java index 28f60831a..e2b1d2d2b 100644 --- a/src/main/java/com/hubspot/jinjava/Jinjava.java +++ b/src/main/java/com/hubspot/jinjava/Jinjava.java @@ -206,6 +206,7 @@ public RenderResult renderForResult(String template, Map bindings, Ji } catch (Exception e) { return new RenderResult(TemplateError.fromException(e), interpreter.getContext(), interpreter.getErrorsCopy()); } finally { + globalContext.reset(); JinjavaInterpreter.popCurrent(); } } diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java index 6cb3d7a40..88ef53568 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/Context.java +++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java @@ -112,7 +112,7 @@ public Context(Context parent, Map bindings, Map FromTagCycleException.class); if (disabled == null) { - disabled = new HashMap>(); + disabled = new HashMap<>(); } this.expTestLibrary = new ExpTestLibrary(parent == null, disabled.get(Library.EXP_TEST)); @@ -121,6 +121,13 @@ public Context(Context parent, Map bindings, Map this.functionLibrary = new FunctionLibrary(parent == null, disabled.get(Library.FUNCTION)); } + public void reset() { + // clear anything that pushes up to its parent's values + resolvedExpressions.clear(); + resolvedValues.clear(); + resolvedFunctions.clear(); + } + @Override public Context getParent() { return parent; @@ -162,7 +169,7 @@ public boolean isGlobalMacro(String identifier) { public boolean isAutoEscape() { if (autoEscape != null) { - return autoEscape.booleanValue(); + return autoEscape; } if (parent != null) { diff --git a/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java b/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java index 5c8ce03fd..960398b12 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java @@ -5,6 +5,9 @@ import org.junit.Before; import org.junit.Test; +import com.google.common.collect.ImmutableMap; +import com.hubspot.jinjava.Jinjava; + public class ContextTest { private static final String RESOLVED_EXPRESSION = "exp" ; private static final String RESOLVED_FUNCTION = "func" ; @@ -46,4 +49,22 @@ public void itRecursivelyAddsValuesUpTheContextChain() { assertThat(context.getResolvedFunctions()).contains(RESOLVED_FUNCTION); assertThat(context.getResolvedExpressions()).contains(RESOLVED_EXPRESSION); } + + @Test + public void itResetsGlobalContextAfterRender() { + + Jinjava jinjava = new Jinjava(); + Context globalContext = jinjava.getGlobalContext(); + + context.addResolvedExpression("exp"); + context.addDependency("dep", "mydep"); + + RenderResult result = jinjava.renderForResult("{{ foo + 1 }}", ImmutableMap.of("foo", 1)); + + assertThat(result.getOutput()).isEqualTo("2"); + assertThat(result.getContext().getResolvedExpressions()).containsOnly("foo + 1"); + assertThat(result.getContext().getResolvedValues()).containsOnly("foo"); + assertThat(globalContext.getResolvedExpressions()).isEmpty(); + assertThat(globalContext.getResolvedValues()).isEmpty(); + } } diff --git a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java index 73cb78c8f..7aad62dbe 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java @@ -18,8 +18,8 @@ public class JinjavaInterpreterTest { - Jinjava jinjava; - JinjavaInterpreter interpreter; + private Jinjava jinjava; + private JinjavaInterpreter interpreter; @Before public void setup() { @@ -39,21 +39,21 @@ public void resolveBlockStubsWithMissingNamedBlock() { } @Test - public void resolveBlockStubs() throws Exception { + public void resolveBlockStubs() { interpreter.addBlock("foobar", Lists.newLinkedList(Lists.newArrayList((new TextNode(new TextToken("sparta", -1, -1)))))); String content = "this is {% block foobar %}foobar{% endblock %}!"; assertThat(interpreter.render(content)).isEqualTo("this is sparta!"); } @Test - public void resolveBlockStubsWithSpecialChars() throws Exception { + public void resolveBlockStubsWithSpecialChars() { interpreter.addBlock("foobar", Lists.newLinkedList(Lists.newArrayList(new TextNode(new TextToken("$150.00", -1, -1))))); String content = "this is {% block foobar %}foobar{% endblock %}!"; assertThat(interpreter.render(content)).isEqualTo("this is $150.00!"); } @Test - public void resolveBlockStubsWithCycle() throws Exception { + public void resolveBlockStubsWithCycle() { String content = interpreter.render("{% block foo %}{% block foo %}{% endblock %}{% endblock %}"); assertThat(content).isEmpty(); } @@ -155,7 +155,7 @@ public void parseWithSyntaxError() { } @Test - public void itLimitsOutputSize() throws Exception { + public void itLimitsOutputSize() { JinjavaConfig outputSizeLimitedConfig = JinjavaConfig.newBuilder().withMaxOutputSize(20).build(); String output = "123456789012345678901234567890"; @@ -169,7 +169,7 @@ public void itLimitsOutputSize() throws Exception { } @Test - public void itLimitsOutputSizeWhenSumOfNodeSizesExceedsMax() throws Exception { + public void itLimitsOutputSizeWhenSumOfNodeSizesExceedsMax() { JinjavaConfig outputSizeLimitedConfig = JinjavaConfig.newBuilder().withMaxOutputSize(19).build(); String input = "1234567890{% block testchild %}1234567890{% endblock %}"; String output = "12345678901234567890"; // Note that this exceeds the max size From 40fa0bb7cc2eceb57bc65b64a073b95442b5602e Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Wed, 22 Aug 2018 17:04:54 -0400 Subject: [PATCH 0484/2465] remove manual things --- src/test/java/com/hubspot/jinjava/interpret/ContextTest.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java b/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java index 960398b12..4bd8c4b42 100644 --- a/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java +++ b/src/test/java/com/hubspot/jinjava/interpret/ContextTest.java @@ -56,9 +56,6 @@ public void itResetsGlobalContextAfterRender() { Jinjava jinjava = new Jinjava(); Context globalContext = jinjava.getGlobalContext(); - context.addResolvedExpression("exp"); - context.addDependency("dep", "mydep"); - RenderResult result = jinjava.renderForResult("{{ foo + 1 }}", ImmutableMap.of("foo", 1)); assertThat(result.getOutput()).isEqualTo("2"); From 4f28830ef9883e2c4e2a92a0d37b93c8620de2f7 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Wed, 29 Aug 2018 16:54:17 -0400 Subject: [PATCH 0485/2465] do not allow calling getClass --- .../com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java | 1 + .../java/com/hubspot/jinjava/el/ExpressionResolverTest.java | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java b/src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java index 1e7162f6b..92f96bfac 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java @@ -20,6 +20,7 @@ public class JinjavaBeanELResolver extends BeanELResolver { private static final Set RESTRICTED_METHODS = ImmutableSet. builder() .add("clone") .add("hashCode") + .add("getClass") .add("notify") .add("notifyAll") .add("wait") diff --git a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java index a360a5f87..9b03e3f2b 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java @@ -333,11 +333,11 @@ public void blackListedProperties() { @Test public void blackListedMethods() { context.put("myobj", new MyClass(new Date(0))); - interpreter.resolveELExpression("myobj.wait()", -1); + interpreter.resolveELExpression("myobj.getClass()", -1); assertThat(interpreter.getErrorsCopy()).isNotEmpty(); TemplateError e = interpreter.getErrorsCopy().get(0); - assertThat(e.getMessage()).contains("Cannot find method 'wait'"); + assertThat(e.getMessage()).contains("Cannot find method 'getClass'"); } @Test From 4f058a1ff89568f1d7d43cff1ce33ada9e70be56 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Wed, 29 Aug 2018 17:11:17 -0400 Subject: [PATCH 0486/2465] Update CHANGES.md --- CHANGES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 72d396b23..01bfd7031 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,10 @@ # Jinjava Releases # +### 2018-08-29 Version 2.4.6 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.6%7Cjar)) ### + +* [Fix a memory leak in the global context](https://github.com/HubSpot/jinjava/pull/227) +* [Do not allow calling getClass() on objects](https://github.com/HubSpot/jinjava/pull/230) + ### 2018-08-16 Version 2.4.5 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.5%7Cjar)) ### * [Limit the number errors](https://github.com/HubSpot/jinjava/pull/222) * [Detect fromTag cycle](https://github.com/HubSpot/jinjava/pull/221) From 3fbef40cd1ff00c46a7f77619788b5e0177b4110 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Wed, 29 Aug 2018 21:17:00 +0000 Subject: [PATCH 0487/2465] [maven-release-plugin] prepare release jinjava-2.4.6 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3e56eab9a..1e78099df 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.4.6-SNAPSHOT + 2.4.6 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.4.6 From fb7792b75f13a29f01ae414cc0932f7fb2897f67 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Wed, 29 Aug 2018 21:17:00 +0000 Subject: [PATCH 0488/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1e78099df..1e95b1ca1 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.4.6 + 2.4.7-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.4.6 + HEAD From 9f53617ab999fb7efc3c306e8f8244fc5358bfad Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 31 Aug 2018 13:20:28 -0400 Subject: [PATCH 0489/2465] disallow returning Class objects --- .../jinjava/el/ext/JinjavaBeanELResolver.java | 13 +++++++++--- .../jinjava/el/ExpressionResolverTest.java | 20 +++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java b/src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java index 92f96bfac..d422cb3e1 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java @@ -20,7 +20,7 @@ public class JinjavaBeanELResolver extends BeanELResolver { private static final Set RESTRICTED_METHODS = ImmutableSet. builder() .add("clone") .add("hashCode") - .add("getClass") + .add("forName") .add("notify") .add("notifyAll") .add("wait") @@ -45,7 +45,8 @@ public Class getType(ELContext context, Object base, Object property) { @Override public Object getValue(ELContext context, Object base, Object property) { - return super.getValue(context, base, validatePropertyName(property)); + Object result = super.getValue(context, base, validatePropertyName(property)); + return result instanceof Class ? null : result; } @Override @@ -63,7 +64,13 @@ public Object invoke(ELContext context, Object base, Object method, Class[] p if (method == null || RESTRICTED_METHODS.contains(method.toString())) { throw new MethodNotFoundException("Cannot find method '" + method + "' in " + base.getClass()); } - return super.invoke(context, base, method, paramTypes, params); + Object result = super.invoke(context, base, method, paramTypes, params); + + if (result instanceof Class) { + throw new MethodNotFoundException("Cannot find method '" + method + "' in " + base.getClass()); + } + + return result; } private String validatePropertyName(Object property) { diff --git a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java index 9b03e3f2b..222e8c1ed 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java @@ -330,8 +330,25 @@ public void blackListedProperties() { assertThat(e.getMessage()).contains("Cannot resolve property 'class'"); } + @Test + public void itWillNotReturnClassObjectProperties() { + context.put("myobj", new MyClass(new Date(0))); + Object clazz = interpreter.resolveELExpression("myobj.clazz", -1); + assertThat(clazz).isNull(); + } + @Test public void blackListedMethods() { + context.put("myobj", new MyClass(new Date(0))); + interpreter.resolveELExpression("myobj.wait()", -1); + + assertThat(interpreter.getErrorsCopy()).isNotEmpty(); + TemplateError e = interpreter.getErrorsCopy().get(0); + assertThat(e.getMessage()).contains("Cannot find method 'wait'"); + } + + @Test + public void itWillNotReturnClassObjects() { context.put("myobj", new MyClass(new Date(0))); interpreter.resolveELExpression("myobj.getClass()", -1); @@ -340,6 +357,7 @@ public void blackListedMethods() { assertThat(e.getMessage()).contains("Cannot find method 'getClass'"); } + @Test public void itBlocksDisabledTags() { @@ -477,6 +495,8 @@ public static final class MyClass { this.date = date; } + public Class getClazz() { return this.getClass(); } + public Date getDate() { return date; } From b862edee9b5da0ff7bda70f674565883fc53671d Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 31 Aug 2018 13:35:16 -0400 Subject: [PATCH 0490/2465] block class property as well --- .../com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java b/src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java index d422cb3e1..9e66c9602 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java @@ -13,11 +13,12 @@ * {@link BeanELResolver} supporting snake case property names. */ public class JinjavaBeanELResolver extends BeanELResolver { - private static final Set RESTRICTED_PROPERTIES = ImmutableSet. builder() + private static final Set RESTRICTED_PROPERTIES = ImmutableSet.builder() .add("class") .build(); - private static final Set RESTRICTED_METHODS = ImmutableSet. builder() + private static final Set RESTRICTED_METHODS = ImmutableSet.builder() + .add("class") .add("clone") .add("hashCode") .add("forName") From 41e1ee2f7e0c810def5c99c143f857cdb841b54a Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 31 Aug 2018 13:41:49 -0400 Subject: [PATCH 0491/2465] also block getDelcaringClass --- .../java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java b/src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java index 92f96bfac..f62166343 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java @@ -21,6 +21,7 @@ public class JinjavaBeanELResolver extends BeanELResolver { .add("clone") .add("hashCode") .add("getClass") + .add("getDeclaringClass") .add("notify") .add("notifyAll") .add("wait") From a7cad6c0d197d665069b8700cbcf474caf9a3c13 Mon Sep 17 00:00:00 2001 From: Jeff Boulter Date: Fri, 31 Aug 2018 15:54:09 -0400 Subject: [PATCH 0492/2465] Update CHANGES.md --- CHANGES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 01bfd7031..e3ea59dd7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,8 @@ # Jinjava Releases # +### 2018-08-31 Version 2.4.7 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.7%7Cjar)) ### +* [Do not allow returning objects of type Class](https://github.com/HubSpot/jinjava/pull/232) + ### 2018-08-29 Version 2.4.6 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.6%7Cjar)) ### * [Fix a memory leak in the global context](https://github.com/HubSpot/jinjava/pull/227) From a38ba02f764d2da39e2996d6becc59fac3863060 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Fri, 31 Aug 2018 19:57:13 +0000 Subject: [PATCH 0493/2465] [maven-release-plugin] prepare release jinjava-2.4.7 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1e95b1ca1..dcdf6788b 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.4.7-SNAPSHOT + 2.4.7 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.4.7 From 2b1108385b4bc4fcdc730d5dff95737b2f0721ba Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Fri, 31 Aug 2018 19:57:13 +0000 Subject: [PATCH 0494/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index dcdf6788b..730acaddf 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.4.7 + 2.4.8-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.4.7 + HEAD From a994996f65acb155082d055b7788ce58c01f46d6 Mon Sep 17 00:00:00 2001 From: Brian Krainer Date: Thu, 6 Sep 2018 14:45:44 -0400 Subject: [PATCH 0495/2465] update ** and // to support int/float literals --- .../jinjava/el/ext/ExtendedParser.java | 12 +--- .../hubspot/jinjava/el/ext/PowerOfTest.java | 63 ++++++++++++++----- .../hubspot/jinjava/el/ext/TruncDivTest.java | 50 ++++++++++++--- 3 files changed, 94 insertions(+), 31 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java b/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java index 74c156e20..f35d04f07 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/ExtendedParser.java @@ -52,8 +52,8 @@ public class ExtendedParser extends Parser { static final Scanner.ExtensionToken LITERAL_DICT_START = new Scanner.ExtensionToken("{"); static final Scanner.ExtensionToken LITERAL_DICT_END = new Scanner.ExtensionToken("}"); - static final Scanner.ExtensionToken TRUNC_DIV = new Scanner.ExtensionToken("//"); - static final Scanner.ExtensionToken POWER_OF = new Scanner.ExtensionToken("**"); + static final Scanner.ExtensionToken TRUNC_DIV = TruncDivOperator.TOKEN; + static final Scanner.ExtensionToken POWER_OF = PowerOfOperator.TOKEN; static { ExtendedScanner.addKeyToken(IF); @@ -393,14 +393,8 @@ protected AstNode value() throws ScanException, ParseException { AstProperty exptestProperty = createAstDot(identifier(EXPTEST_PREFIX + exptestName), "evaluate", true); v = createAstMethod(exptestProperty, new AstParameters(exptestParams)); - } else if ("//".equals(getToken().getImage()) && lookahead(0).getSymbol() == IDENTIFIER) { - consumeToken(); // '//' - v = createAstBinary(v, mul(true), TruncDivOperator.OP); - - } else if ("**".equals(getToken().getImage()) && lookahead(0).getSymbol() == IDENTIFIER) { - consumeToken(); // '**' - v = createAstBinary(v, mul(true), PowerOfOperator.OP); } + return v; } } diff --git a/src/test/java/com/hubspot/jinjava/el/ext/PowerOfTest.java b/src/test/java/com/hubspot/jinjava/el/ext/PowerOfTest.java index 0c992d7e5..97eea5f55 100644 --- a/src/test/java/com/hubspot/jinjava/el/ext/PowerOfTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ext/PowerOfTest.java @@ -21,31 +21,66 @@ public void setUp() { @Test public void testPowerOfInteger() { - Map context = Maps.newHashMap(); context.put("base", 2); - context.put("exponent", 8); - - String template = "{% set x = base ** exponent %}{{x}}"; - String rendered = jinja.render(template, context); - assertEquals("256", rendered); + context.put("evenExponent", 8); + context.put("oddExponent", 7); + context.put("negativeBase", -2); + context.put("negativeExponent", -8); + + String[][] testCases = { + { "{% set x = base ** evenExponent %}{{x}}", "256" }, + { "{% set x = 2 ** 8 %}{{x}}", "256" }, + { "{% set x = base ** 8 %}{{x}}", "256" }, + { "{% set x = 2 ** evenExponent %}{{x}}", "256" }, + { "{% set x = negativeBase ** evenExponent %}{{x}}", "256" }, + { "{% set x = -2 ** 8 %}{{x}}", "256" }, + { "{% set x = base ** negativeExponent %}{{x}}", "0" }, + { "{% set x = 2 ** -8 %}{{x}}", "0" }, + { "{% set x = negativeBase ** oddExponent %}{{x}}", "-128"}, + { "{% set x = -2 ** 7 %}{{x}}", "-128" } + }; + + for (String[] testCase : testCases ) { + String template = testCase[0]; + String expected = testCase[1]; + String rendered = jinja.render(template, context); + assertEquals(expected, rendered); + } } @Test public void testPowerOfFractional() { - Map context = Maps.newHashMap(); context.put("base", 2); - context.put("exponent", 8.0); - - String template = "{% set x = base ** exponent %}{{x}}"; - String rendered = jinja.render(template, context); - assertEquals("256.0", rendered); + context.put("evenExponent", 8.0); + context.put("oddExponent", 7.0); + context.put("negativeBase", -2); + context.put("negativeExponent", -8.0); + + String[][] testCases = { + { "{% set x = base ** evenExponent %}{{x}}", "256.0" }, + { "{% set x = 2 ** 8.0 %}{{x}}", "256.0" }, + { "{% set x = base ** 8.0 %}{{x}}", "256.0" }, + { "{% set x = 2 ** evenExponent %}{{x}}", "256.0" }, + { "{% set x = negativeBase ** evenExponent %}{{x}}", "256.0" }, + { "{% set x = -2 ** 8.0 %}{{x}}", "256.0" }, + { "{% set x = base ** negativeExponent %}{{x}}", "0.00390625" }, + { "{% set x = 2 ** -8.0 %}{{x}}", "0.00390625" }, + { "{% set x = negativeBase ** oddExponent %}{{x}}", "-128.0"}, + { "{% set x = -2 ** 7.0 %}{{x}}", "-128.0" } + }; + + for (String[] testCase : testCases ) { + String template = testCase[0]; + String expected = testCase[1]; + String rendered = jinja.render(template, context); + assertEquals(expected, rendered); + } } @Test - public void test04PowerOfStringFails() { - + public void testPowerOfStringFails() { Map context = Maps.newHashMap(); context.put("base", "2"); context.put("exponent", "8"); diff --git a/src/test/java/com/hubspot/jinjava/el/ext/TruncDivTest.java b/src/test/java/com/hubspot/jinjava/el/ext/TruncDivTest.java index 39d6bd34e..034d38123 100644 --- a/src/test/java/com/hubspot/jinjava/el/ext/TruncDivTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ext/TruncDivTest.java @@ -29,10 +29,28 @@ public void testTruncDivInteger() { Map context = Maps.newHashMap(); context.put("dividend", 5); context.put("divisor", 2); + context.put("negativeDividend", -5); + context.put("negativeDivisor", -2); - String template = "{% set x = dividend // divisor %}{{x}}"; - String rendered = jinja.render(template, context); - assertEquals("2", rendered); + String[][] testCases = { + { "{% set x = dividend // divisor %}{{x}}", "2" }, + { "{% set x = 5 // 2 %}{{x}}", "2" }, + { "{% set x = dividend // 2 %}{{x}}", "2" }, + { "{% set x = 5 // divisor %}{{x}}", "2" }, + { "{% set x = negativeDividend // divisor %}{{x}}", "-3" }, + { "{% set x = -5 // 2 %}{{x}}", "-3" }, + { "{% set x = dividend // negativeDivisor %}{{x}}", "-3" }, + { "{% set x = 5 // -2 %}{{x}}", "-3" }, + { "{% set x = negativeDividend // negativeDivisor %}{{x}}", "2" }, + { "{% set x = -5 // -2 %}{{x}}", "2" } + }; + + for (String[] testCase : testCases) { + String template = testCase[0]; + String expected = testCase[1]; + String rendered = jinja.render(template, context); + assertEquals(expected, rendered); + } } /** @@ -40,14 +58,31 @@ public void testTruncDivInteger() { */ @Test public void testTruncDivFractional() { - Map context = Maps.newHashMap(); context.put("dividend", 5.0); context.put("divisor", 2); + context.put("negativeDividend", -5.0); + context.put("negativeDivisor", -2); - String template = "{% set x = dividend // divisor %}{{x}}"; - String rendered = jinja.render(template, context); - assertEquals("2.0", rendered); + String[][] testCases = { + { "{% set x = dividend // divisor %}{{x}}", "2.0" }, + { "{% set x = 5.0 // 2 %}{{x}}", "2.0" }, + { "{% set x = dividend // 2 %}{{x}}", "2.0" }, + { "{% set x = 5.0 // divisor %}{{x}}", "2.0" }, + { "{% set x = negativeDividend // divisor %}{{x}}", "-3.0" }, + { "{% set x = -5.0 // 2 %}{{x}}", "-3.0" }, + { "{% set x = dividend // negativeDivisor %}{{x}}", "-3.0" }, + { "{% set x = 5.0 // -2 %}{{x}}", "-3.0" }, + { "{% set x = negativeDividend // negativeDivisor %}{{x}}", "2.0" }, + { "{% set x = -5.0 // -2 %}{{x}}", "2.0"} + }; + + for (String[] testCase : testCases) { + String template = testCase[0]; + String expected = testCase[1]; + String rendered = jinja.render(template, context); + assertEquals(expected, rendered); + } } /** @@ -55,7 +90,6 @@ public void testTruncDivFractional() { */ @Test public void testTruncDivStringFails() { - Map context = Maps.newHashMap(); context.put("dividend", "5"); context.put("divisor", "2"); From 5860b7da5b3f64dab9b7171a2cce6c52796aa1d6 Mon Sep 17 00:00:00 2001 From: Brian Krainer Date: Fri, 7 Sep 2018 10:36:10 -0400 Subject: [PATCH 0496/2465] update CHANGES.md --- CHANGES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index e3ea59dd7..80f2e1710 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,8 @@ # Jinjava Releases # +### 2018-09-07 Version 2.4.8 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.8%7Cjar)) ### +* [Bug fix for trunc division and power operations](https://github.com/HubSpot/jinjava/pull/234) + ### 2018-08-31 Version 2.4.7 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.7%7Cjar)) ### * [Do not allow returning objects of type Class](https://github.com/HubSpot/jinjava/pull/232) From 48f7d4ee8e331b242681bbb2d52851178380c22c Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Fri, 7 Sep 2018 14:57:24 +0000 Subject: [PATCH 0497/2465] [maven-release-plugin] prepare release jinjava-2.4.8 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 730acaddf..7e6d344b9 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.4.8-SNAPSHOT + 2.4.8 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.4.8 From 39417f7ee47ce657fde5dc2c8d064c36db5404a3 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Fri, 7 Sep 2018 14:57:24 +0000 Subject: [PATCH 0498/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7e6d344b9..1ed851b43 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.4.8 + 2.4.9-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.4.8 + HEAD From d4d97243f694dc9cb1ab1f48647684f5800867a0 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Wed, 19 Sep 2018 09:56:02 -0400 Subject: [PATCH 0499/2465] Adds filter to test if string is valid IP Address. --- .../jinjava/lib/filter/FilterLibrary.java | 1 + .../jinjava/lib/filter/IpAddrFilter.java | 50 +++++++++++++++++ .../jinjava/lib/filter/IpAddrFilterTest.java | 54 +++++++++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 src/main/java/com/hubspot/jinjava/lib/filter/IpAddrFilter.java create mode 100644 src/test/java/com/hubspot/jinjava/lib/filter/IpAddrFilterTest.java diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java b/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java index c28d60d00..1d86e7948 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/FilterLibrary.java @@ -74,6 +74,7 @@ protected void registerDefaults() { ReverseFilter.class, RoundFilter.class, SumFilter.class, + IpAddrFilter.class, EscapeFilter.class, EAliasedEscapeFilter.class, diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/IpAddrFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/IpAddrFilter.java new file mode 100644 index 000000000..91fbce8de --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/lib/filter/IpAddrFilter.java @@ -0,0 +1,50 @@ +package com.hubspot.jinjava.lib.filter; + +import java.util.regex.Pattern; + +import com.hubspot.jinjava.doc.annotations.JinjavaDoc; +import com.hubspot.jinjava.doc.annotations.JinjavaParam; +import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; + +@JinjavaDoc( + value = "Evaluates to true if the value is a valid IPv4 or IPv6 address", + params = { + @JinjavaParam(value = "value", type = "string", desc = "String to check IP Address"), + }, + snippets = { + @JinjavaSnippet( + desc = "This example is an alternative to using the is divisibleby expression test", + code = "{% set ip = '1.0.0.1' %}\n" + + "{% if ip|ipaddr %}\n" + + " The string is a valid IP address\n" + + "{% endif %}") + }) +public class IpAddrFilter implements Filter { + + private static final Pattern IP4_PATTERN = Pattern.compile("(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])"); + private static final Pattern IP6_PATTERN = Pattern.compile("^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$"); + private static final Pattern IP6_COMPRESSED_PATTERN = Pattern.compile("^((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)$"); + + @Override + public Object filter(Object object, JinjavaInterpreter interpreter, String... arg) { + + if (object == null) { + return false; + } + + if (object instanceof String) { + String address = (String) object; + return IP4_PATTERN.matcher(address).matches() + || IP6_PATTERN.matcher(address).matches() + || IP6_COMPRESSED_PATTERN.matcher(address).matches(); + } + return false; + } + + @Override + public String getName() { + return "ipaddr"; + } + +} diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/IpAddrFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/IpAddrFilterTest.java new file mode 100644 index 000000000..97e914b8e --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/lib/filter/IpAddrFilterTest.java @@ -0,0 +1,54 @@ +package com.hubspot.jinjava.lib.filter; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import org.junit.Before; +import org.junit.Test; + +import com.hubspot.jinjava.Jinjava; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; + +public class IpAddrFilterTest { + + private IpAddrFilter ipAddrFilter; + private JinjavaInterpreter interpreter; + + @Before + public void setup() { + ipAddrFilter = new IpAddrFilter(); + interpreter = new Jinjava().newInterpreter(); + } + + @Test + public void itAcceptsValidIpV4Address() { + assertThat(ipAddrFilter.filter("255.182.100.1", interpreter)).isEqualTo(true); + assertThat(ipAddrFilter.filter("125.0.100.1", interpreter)).isEqualTo(true); + assertThat(ipAddrFilter.filter("128.0.0.1", interpreter)).isEqualTo(true); + } + + @Test + public void itRejectsInvalidIpV4Address() { + assertThat(ipAddrFilter.filter("255.182.100.abc", interpreter)).isEqualTo(false); + assertThat(ipAddrFilter.filter("125.512.100.1", interpreter)).isEqualTo(false); + assertThat(ipAddrFilter.filter("125.512.100.1.1", interpreter)).isEqualTo(false); + assertThat(ipAddrFilter.filter("125.512.100", interpreter)).isEqualTo(false); + assertThat(ipAddrFilter.filter(104, interpreter)).isEqualTo(false); + } + + @Test + public void itAcceptsValidIpV6Address() { + assertThat(ipAddrFilter.filter("1200:0000:AB00:1234:0000:2552:7777:1313", interpreter)).isEqualTo(true); + assertThat(ipAddrFilter.filter("21DA:D3:0:2F3B:2AA:FF:FE28:9C5A", interpreter)).isEqualTo(true); + assertThat(ipAddrFilter.filter("2000::", interpreter)).isEqualTo(true); + } + + @Test + public void itRejectsInvalidIpV6Address() { + assertThat(ipAddrFilter.filter("1200::AB00:1234::2552:7777:1313", interpreter)).isEqualTo(false); + assertThat(ipAddrFilter.filter("1200:0000:AB00:1234:O000:2552:7777:1313", interpreter)).isEqualTo(false); + assertThat(ipAddrFilter.filter("1200::AB00:1234::2552:7777:1313:1232", interpreter)).isEqualTo(false); + assertThat(ipAddrFilter.filter("321", interpreter)).isEqualTo(false); + assertThat(ipAddrFilter.filter(104, interpreter)).isEqualTo(false); + } + +} From 8530ce17b2508d2118669f34b8333f06f958fb84 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Wed, 19 Sep 2018 09:59:46 -0400 Subject: [PATCH 0500/2465] doc change. --- src/main/java/com/hubspot/jinjava/lib/filter/IpAddrFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/IpAddrFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/IpAddrFilter.java index 91fbce8de..84e766bed 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/IpAddrFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/IpAddrFilter.java @@ -14,7 +14,7 @@ }, snippets = { @JinjavaSnippet( - desc = "This example is an alternative to using the is divisibleby expression test", + desc = "This example shows how to test if a string is a valid ip address", code = "{% set ip = '1.0.0.1' %}\n" + "{% if ip|ipaddr %}\n" + " The string is a valid IP address\n" + From e9a0934e986a75fbe4710dc67bc624139b2c7460 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Wed, 19 Sep 2018 10:10:03 -0400 Subject: [PATCH 0501/2465] trim. --- src/main/java/com/hubspot/jinjava/lib/filter/IpAddrFilter.java | 2 +- .../java/com/hubspot/jinjava/lib/filter/IpAddrFilterTest.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/IpAddrFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/IpAddrFilter.java index 84e766bed..cf4cb92cd 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/IpAddrFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/IpAddrFilter.java @@ -34,7 +34,7 @@ public Object filter(Object object, JinjavaInterpreter interpreter, String... ar } if (object instanceof String) { - String address = (String) object; + String address = ((String) object).trim(); return IP4_PATTERN.matcher(address).matches() || IP6_PATTERN.matcher(address).matches() || IP6_COMPRESSED_PATTERN.matcher(address).matches(); diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/IpAddrFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/IpAddrFilterTest.java index 97e914b8e..bd8eca03b 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/IpAddrFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/IpAddrFilterTest.java @@ -24,6 +24,7 @@ public void itAcceptsValidIpV4Address() { assertThat(ipAddrFilter.filter("255.182.100.1", interpreter)).isEqualTo(true); assertThat(ipAddrFilter.filter("125.0.100.1", interpreter)).isEqualTo(true); assertThat(ipAddrFilter.filter("128.0.0.1", interpreter)).isEqualTo(true); + assertThat(ipAddrFilter.filter(" 128.0.0.1 ", interpreter)).isEqualTo(true); } @Test From 8bc8fa47c1eb4ddd1e111a10ee49e7c3053e54c8 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Wed, 19 Sep 2018 10:10:58 -0400 Subject: [PATCH 0502/2465] remove duplicate test. --- .../java/com/hubspot/jinjava/lib/filter/IpAddrFilterTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/IpAddrFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/IpAddrFilterTest.java index bd8eca03b..a219d0852 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/IpAddrFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/IpAddrFilterTest.java @@ -49,7 +49,6 @@ public void itRejectsInvalidIpV6Address() { assertThat(ipAddrFilter.filter("1200:0000:AB00:1234:O000:2552:7777:1313", interpreter)).isEqualTo(false); assertThat(ipAddrFilter.filter("1200::AB00:1234::2552:7777:1313:1232", interpreter)).isEqualTo(false); assertThat(ipAddrFilter.filter("321", interpreter)).isEqualTo(false); - assertThat(ipAddrFilter.filter(104, interpreter)).isEqualTo(false); } } From 9e797765a62eae4a28a6cc9716633b45e7a8e5d1 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Mon, 8 Oct 2018 14:03:38 -0400 Subject: [PATCH 0503/2465] Implement nested properties for selectattr and rejectattr filters. --- .../jinjava/lib/filter/RejectAttrFilter.java | 3 +- .../jinjava/lib/filter/SelectAttrFilter.java | 6 ++- .../lib/filter/RejectAttrFilterTest.java | 47 +++++++++++++++++-- .../lib/filter/SelectAttrFilterTest.java | 43 ++++++++++++++++- 4 files changed, 90 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/RejectAttrFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/RejectAttrFilter.java index 4b599df56..093836a44 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/RejectAttrFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/RejectAttrFilter.java @@ -12,6 +12,7 @@ import com.hubspot.jinjava.lib.exptest.ExpTest; import com.hubspot.jinjava.util.ForLoop; import com.hubspot.jinjava.util.ObjectIterator; +import com.hubspot.jinjava.util.Variable; @JinjavaDoc( value = "Filters a sequence of objects by applying a test to an attribute of an object or the attribute and " @@ -61,7 +62,7 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args) ForLoop loop = ObjectIterator.getLoop(var); while (loop.hasNext()) { Object val = loop.next(); - Object attrVal = interpreter.resolveProperty(val, attr); + Object attrVal = new Variable(interpreter, String.format("%s.%s", val.toString(), attr)).resolve(val); if (!expTest.evaluate(attrVal, interpreter, (Object[]) expArgs)) { result.add(val); diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java index 775108614..e9ca2af9e 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.Map; +import com.google.common.base.Splitter; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaParam; import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; @@ -13,6 +14,7 @@ import com.hubspot.jinjava.lib.exptest.ExpTest; import com.hubspot.jinjava.util.ForLoop; import com.hubspot.jinjava.util.ObjectIterator; +import com.hubspot.jinjava.util.Variable; @JinjavaDoc( value = "Filters a sequence of objects by applying a test to an attribute of an object and only selecting the ones with the test succeeding.", @@ -30,6 +32,8 @@ }) public class SelectAttrFilter implements AdvancedFilter { + private static final Splitter PROPERTY_SPLITTER = Splitter.on('.'); + @Override public String getName() { return "selectattr"; @@ -71,8 +75,8 @@ public Object filter(Object var, JinjavaInterpreter interpreter, Object[] args, ForLoop loop = ObjectIterator.getLoop(var); while (loop.hasNext()) { Object val = loop.next(); - Object attrVal = interpreter.resolveProperty(val, attr); + Object attrVal = new Variable(interpreter, String.format("%s.%s", val.toString(), attr)).resolve(val); if (expTest.evaluate(attrVal, interpreter, expArgs)) { result.add(val); } diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/RejectAttrFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/RejectAttrFilterTest.java index aa079a9ab..3818ea624 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/RejectAttrFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/RejectAttrFilterTest.java @@ -18,9 +18,9 @@ public class RejectAttrFilterTest { public void setup() { jinjava = new Jinjava(); jinjava.getGlobalContext().put("users", Lists.newArrayList( - new User(0, false, "foo@bar.com"), - new User(1, true, "bar@bar.com"), - new User(2, false, null))); + new User(0, false, "foo@bar.com", new Option(0, "option0")), + new User(1, true, "bar@bar.com", new Option(1, "option1")), + new User(2, false, null, new Option(2, "option2")))); } @Test @@ -36,21 +36,32 @@ public void rejectAttrWithExp() { } @Test - public void selectAttrWithIsEqualToExp() { + public void rejectAttrWithIsEqualToExp() { assertThat(jinjava.render("{{ users|rejectattr('email', 'equalto', 'bar@bar.com') }}", new HashMap())) .isEqualTo("[0, 2]"); } + @Test + public void rejectAttrWithNestedProperty() { + assertThat(jinjava.render("{{ users|rejectattr('option.id', 'equalto', 1) }}", new HashMap())) + .isEqualTo("[0, 2]"); + + assertThat(jinjava.render("{{ users|rejectattr('option.name', 'equalto', 'option0') }}", new HashMap())) + .isEqualTo("[1, 2]"); + } + public static class User { private int num; private boolean isActive; private String email; + private Option option; - public User(int num, boolean isActive, String email) { + public User(int num, boolean isActive, String email, Option option) { this.num = num; this.isActive = isActive; this.email = email; + this.option = option; } public int getNum() { @@ -65,10 +76,36 @@ public boolean getIsActive() { return isActive; } + public Option getOption() { + return option; + } + @Override public String toString() { return num + ""; } } + public static class Option { + private long id; + private String name; + + public Option(long id, String name) { + this.id = id; + this.name = name; + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return id + ""; + } + } } diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/SelectAttrFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/SelectAttrFilterTest.java index d5a964e79..a8727c242 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/SelectAttrFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/SelectAttrFilterTest.java @@ -18,7 +18,9 @@ public class SelectAttrFilterTest { public void setup() { jinjava = new Jinjava(); jinjava.getGlobalContext().put("users", Lists.newArrayList( - new User(0, false, "foo@bar.com"), new User(1, true, "bar@bar.com"), new User(2, false, null))); + new User(0, false, "foo@bar.com", new Option(0, "option0")), + new User(1, true, "bar@bar.com", new Option(1, "option1")), + new User(2, false, null, new Option(2, "option2")))); } @Test @@ -45,16 +47,27 @@ public void selectAttrWithNumericIsEqualToExp() { .isEqualTo("[1]"); } + @Test + public void selectAttrWithNestedProperty() { + assertThat(jinjava.render("{{ users|selectattr('option.id', 'equalto', 1) }}", new HashMap())) + .isEqualTo("[1]"); + + assertThat(jinjava.render("{{ users|selectattr('option.name', 'equalto', 'option2') }}", new HashMap())) + .isEqualTo("[2]"); + } + public static class User { private long num; private boolean isActive; private String email; + private Option option; - public User(long num, boolean isActive, String email) { + public User(long num, boolean isActive, String email, Option option) { this.num = num; this.isActive = isActive; this.email = email; + this.option = option; } public long getNum() { @@ -69,10 +82,36 @@ public boolean getIsActive() { return isActive; } + public Option getOption() { + return option; + } + @Override public String toString() { return num + ""; } } + public static class Option { + private long id; + private String name; + + public Option(long id, String name) { + this.id = id; + this.name = name; + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return id + ""; + } + } } From ab3e259bc592b58ab8817d4b4b774eb963d80e0b Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Mon, 8 Oct 2018 14:04:34 -0400 Subject: [PATCH 0504/2465] slight fix. --- .../java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java index e9ca2af9e..262679d29 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java @@ -76,7 +76,7 @@ public Object filter(Object var, JinjavaInterpreter interpreter, Object[] args, while (loop.hasNext()) { Object val = loop.next(); - Object attrVal = new Variable(interpreter, String.format("%s.%s", val.toString(), attr)).resolve(val); + Object attrVal = new Variable(interpreter, String.format("%s.%s", "placeholder", attr)).resolve(val); if (expTest.evaluate(attrVal, interpreter, expArgs)) { result.add(val); } From 54ac18861e3be1901ab2f7ce0af6b82e9226e848 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Mon, 8 Oct 2018 14:15:26 -0400 Subject: [PATCH 0505/2465] better structure. --- .../jinjava/lib/filter/RejectAttrFilter.java | 47 ++----------------- .../jinjava/lib/filter/SelectAttrFilter.java | 7 ++- 2 files changed, 10 insertions(+), 44 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/RejectAttrFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/RejectAttrFilter.java index 093836a44..b5f9c9695 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/RejectAttrFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/RejectAttrFilter.java @@ -1,18 +1,11 @@ package com.hubspot.jinjava.lib.filter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import java.util.Map; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaParam; import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; -import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; -import com.hubspot.jinjava.lib.exptest.ExpTest; -import com.hubspot.jinjava.util.ForLoop; -import com.hubspot.jinjava.util.ObjectIterator; -import com.hubspot.jinjava.util.Variable; @JinjavaDoc( value = "Filters a sequence of objects by applying a test to an attribute of an object or the attribute and " @@ -29,7 +22,7 @@ "
    Post in listing markup
    \n" + "{% endfor %}") }) -public class RejectAttrFilter implements Filter { +public class RejectAttrFilter extends SelectAttrFilter implements AdvancedFilter { @Override public String getName() { @@ -37,39 +30,7 @@ public String getName() { } @Override - public Object filter(Object var, JinjavaInterpreter interpreter, String... args) { - List result = new ArrayList<>(); - - if (args.length == 0) { - throw new InterpretException(getName() + " filter requires an attr to filter on", interpreter.getLineNumber()); - } - - String[] expArgs = new String[]{}; - String attr = args[0]; - - ExpTest expTest = interpreter.getContext().getExpTest("truthy"); - if (args.length > 1) { - expTest = interpreter.getContext().getExpTest(args[1]); - if (expTest == null) { - throw new InterpretException("No expression test defined with name '" + args[1] + "'", - interpreter.getLineNumber()); - } - } - - if (args.length > 2) { - expArgs = Arrays.copyOfRange(args, 2, args.length); - } - ForLoop loop = ObjectIterator.getLoop(var); - while (loop.hasNext()) { - Object val = loop.next(); - Object attrVal = new Variable(interpreter, String.format("%s.%s", val.toString(), attr)).resolve(val); - - if (!expTest.evaluate(attrVal, interpreter, (Object[]) expArgs)) { - result.add(val); - } - } - - return result; + public Object filter(Object var, JinjavaInterpreter interpreter, Object[] args, Map kwargs) { + return applyFilter(var, interpreter, args, kwargs, false); } - } diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java index 262679d29..69be253b2 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java @@ -41,6 +41,10 @@ public String getName() { @Override public Object filter(Object var, JinjavaInterpreter interpreter, Object[] args, Map kwargs) { + return applyFilter(var, interpreter, args, kwargs, true); + } + + protected Object applyFilter(Object var, JinjavaInterpreter interpreter, Object[] args, Map kwargs, boolean acceptObjects) { List result = new ArrayList<>(); if (args.length == 0) { @@ -77,7 +81,8 @@ public Object filter(Object var, JinjavaInterpreter interpreter, Object[] args, Object val = loop.next(); Object attrVal = new Variable(interpreter, String.format("%s.%s", "placeholder", attr)).resolve(val); - if (expTest.evaluate(attrVal, interpreter, expArgs)) { + boolean pass = expTest.evaluate(attrVal, interpreter, expArgs); + if ((acceptObjects && pass) || (!acceptObjects && !pass)) { result.add(val); } } From 80ac006c354a87b2734a23d3f9d35e858c6698e5 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Mon, 8 Oct 2018 14:19:28 -0400 Subject: [PATCH 0506/2465] nit. --- .../java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java | 3 --- .../com/hubspot/jinjava/lib/filter/RejectAttrFilterTest.java | 3 +-- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java index 69be253b2..6e353247c 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java @@ -5,7 +5,6 @@ import java.util.List; import java.util.Map; -import com.google.common.base.Splitter; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaParam; import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; @@ -32,8 +31,6 @@ }) public class SelectAttrFilter implements AdvancedFilter { - private static final Splitter PROPERTY_SPLITTER = Splitter.on('.'); - @Override public String getName() { return "selectattr"; diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/RejectAttrFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/RejectAttrFilterTest.java index 3818ea624..78448e606 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/RejectAttrFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/RejectAttrFilterTest.java @@ -49,8 +49,7 @@ public void rejectAttrWithNestedProperty() { assertThat(jinjava.render("{{ users|rejectattr('option.name', 'equalto', 'option0') }}", new HashMap())) .isEqualTo("[1, 2]"); } - - + public static class User { private int num; private boolean isActive; From bbc12b661b7fb25112aec4c229ef1b0204f31bb0 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Mon, 8 Oct 2018 14:57:31 -0400 Subject: [PATCH 0507/2465] simplify. --- .../java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java | 3 +-- .../com/hubspot/jinjava/lib/filter/RejectAttrFilterTest.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java index 6e353247c..fbe2766b8 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java @@ -78,8 +78,7 @@ protected Object applyFilter(Object var, JinjavaInterpreter interpreter, Object[ Object val = loop.next(); Object attrVal = new Variable(interpreter, String.format("%s.%s", "placeholder", attr)).resolve(val); - boolean pass = expTest.evaluate(attrVal, interpreter, expArgs); - if ((acceptObjects && pass) || (!acceptObjects && !pass)) { + if (acceptObjects == expTest.evaluate(attrVal, interpreter, expArgs)) { result.add(val); } } diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/RejectAttrFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/RejectAttrFilterTest.java index 78448e606..ef233c4d7 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/RejectAttrFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/RejectAttrFilterTest.java @@ -49,7 +49,7 @@ public void rejectAttrWithNestedProperty() { assertThat(jinjava.render("{{ users|rejectattr('option.name', 'equalto', 'option0') }}", new HashMap())) .isEqualTo("[1, 2]"); } - + public static class User { private int num; private boolean isActive; From 6fc2d3cb8cbffbf9f3123121ac5d1c345494bfe0 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Thu, 11 Oct 2018 11:55:35 -0400 Subject: [PATCH 0508/2465] Implement 'do' tag --- .../com/hubspot/jinjava/lib/tag/DoTag.java | 40 ++++++++++++++++++ .../hubspot/jinjava/lib/tag/TagLibrary.java | 3 +- .../hubspot/jinjava/lib/tag/DoTagTest.java | 42 +++++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/hubspot/jinjava/lib/tag/DoTag.java create mode 100644 src/test/java/com/hubspot/jinjava/lib/tag/DoTagTest.java diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/DoTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/DoTag.java new file mode 100644 index 000000000..c2979f85e --- /dev/null +++ b/src/main/java/com/hubspot/jinjava/lib/tag/DoTag.java @@ -0,0 +1,40 @@ +package com.hubspot.jinjava.lib.tag; + +import org.apache.commons.lang3.StringUtils; + +import com.hubspot.jinjava.doc.annotations.JinjavaDoc; +import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.interpret.TemplateSyntaxException; +import com.hubspot.jinjava.tree.TagNode; + +@JinjavaDoc( + value = "Evaluates expression without printing out result.", + snippets = { + @JinjavaSnippet( + code = "{% do list.append('value 2') %}") + }) +public class DoTag implements Tag { + + @Override + public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { + + if (StringUtils.isBlank(tagNode.getHelpers())) { + throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'do' expects expression", tagNode.getLineNumber(), tagNode.getStartPosition()); + } + + String expression = tagNode.getHelpers(); + interpreter.resolveELExpression(expression, tagNode.getLineNumber()); + return ""; + } + + @Override + public String getEndTagName() { + return null; + } + + @Override + public String getName() { + return "do"; + } +} diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/TagLibrary.java b/src/main/java/com/hubspot/jinjava/lib/tag/TagLibrary.java index b378c79b7..f0a2c4875 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/TagLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/TagLibrary.java @@ -45,7 +45,8 @@ protected void registerDefaults() { PrintTag.class, RawTag.class, SetTag.class, - UnlessTag.class); + UnlessTag.class, + DoTag.class); } public Tag getTag(String tagName) { diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/DoTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/DoTagTest.java new file mode 100644 index 000000000..44c4297d0 --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/lib/tag/DoTagTest.java @@ -0,0 +1,42 @@ +package com.hubspot.jinjava.lib.tag; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Before; +import org.junit.Test; + +import com.google.common.collect.Maps; +import com.hubspot.jinjava.Jinjava; +import com.hubspot.jinjava.interpret.Context; +import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.interpret.RenderResult; +import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; + +public class DoTagTest { + + private Context context; + JinjavaInterpreter interpreter; + Jinjava jinjava; + + @Before + public void setup() { + jinjava = new Jinjava(); + interpreter = jinjava.newInterpreter(); + context = interpreter.getContext(); + } + + @Test + public void itResolvesExpressions() { + String template = "{% set output = [] %}{% do output.append('hey') %}{{ output }}"; + assertThat(jinjava.render(template, Maps.newHashMap())).isEqualTo("[hey]"); + } + + @Test + public void itAddsTemplateErrorOnEmptyExpression() { + String template = "{% do %}"; + RenderResult renderResult = jinjava.renderForResult(template, Maps.newHashMap()); + assertThat(renderResult.getErrors()).hasSize(1); + assertThat(renderResult.getErrors().get(0).getReason()).isEqualTo(ErrorReason.SYNTAX_ERROR); + } + +} From 19f90b9183cfc690226d8885ecc4977d64b3816f Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Thu, 11 Oct 2018 11:58:54 -0400 Subject: [PATCH 0509/2465] style nit. --- src/test/java/com/hubspot/jinjava/lib/tag/DoTagTest.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/DoTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/DoTagTest.java index 44c4297d0..c6f6ac7b5 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/DoTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/DoTagTest.java @@ -15,8 +15,8 @@ public class DoTagTest { private Context context; - JinjavaInterpreter interpreter; - Jinjava jinjava; + private JinjavaInterpreter interpreter; + private Jinjava jinjava; @Before public void setup() { @@ -38,5 +38,4 @@ public void itAddsTemplateErrorOnEmptyExpression() { assertThat(renderResult.getErrors()).hasSize(1); assertThat(renderResult.getErrors().get(0).getReason()).isEqualTo(ErrorReason.SYNTAX_ERROR); } - } From a5d68e89b8b0331594be707fe8ab8a478efdcb83 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Thu, 11 Oct 2018 12:02:38 -0400 Subject: [PATCH 0510/2465] one more nit. --- src/main/java/com/hubspot/jinjava/lib/tag/DoTag.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/DoTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/DoTag.java index c2979f85e..987e5322d 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/DoTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/DoTag.java @@ -22,9 +22,8 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { if (StringUtils.isBlank(tagNode.getHelpers())) { throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'do' expects expression", tagNode.getLineNumber(), tagNode.getStartPosition()); } - - String expression = tagNode.getHelpers(); - interpreter.resolveELExpression(expression, tagNode.getLineNumber()); + + interpreter.resolveELExpression(tagNode.getHelpers(), tagNode.getLineNumber()); return ""; } From 60626395bb4a9147aebd766f88a33eaedb2e393a Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Thu, 11 Oct 2018 12:06:48 -0400 Subject: [PATCH 0511/2465] remove spaces. --- src/main/java/com/hubspot/jinjava/lib/tag/DoTag.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/DoTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/DoTag.java index 987e5322d..e649a2266 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/DoTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/DoTag.java @@ -22,7 +22,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { if (StringUtils.isBlank(tagNode.getHelpers())) { throw new TemplateSyntaxException(tagNode.getMaster().getImage(), "Tag 'do' expects expression", tagNode.getLineNumber(), tagNode.getStartPosition()); } - + interpreter.resolveELExpression(tagNode.getHelpers(), tagNode.getLineNumber()); return ""; } From 4215c5926325d6fe21d1d8df0d9097cfdd5703d1 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Thu, 11 Oct 2018 13:25:58 -0400 Subject: [PATCH 0512/2465] Support timezone conversions in datetimeformat filter. --- .../lib/filter/DateTimeFormatFilter.java | 8 ++++-- .../com/hubspot/jinjava/lib/fn/Functions.java | 27 ++++++++++++++----- .../lib/filter/DateTimeFormatFilterTest.java | 17 ++++++++++++ 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilter.java index 53b959936..c89bc3b28 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilter.java @@ -1,5 +1,6 @@ package com.hubspot.jinjava.lib.filter; +import com.google.common.base.Joiner; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaParam; import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; @@ -11,7 +12,8 @@ value = "Formats a date object", params = { @JinjavaParam(value = "value", defaultValue = "current time", desc = "The date variable or UNIX timestamp to format"), - @JinjavaParam(value = "format", defaultValue = StrftimeFormatter.DEFAULT_DATE_FORMAT, desc = "The format of the date determined by the directives added to this parameter") + @JinjavaParam(value = "format", defaultValue = StrftimeFormatter.DEFAULT_DATE_FORMAT, desc = "The format of the date determined by the directives added to this parameter"), + @JinjavaParam(value = "timezone", defaultValue = "utc", desc = "Time zone of output date") }, snippets = { @JinjavaSnippet(code = "{% content.updated|datetimeformat('%B %e, %Y') %}"), @@ -19,6 +21,8 @@ }) public class DateTimeFormatFilter implements Filter { + private static final Joiner ERROR_MESSAGE_JOINER = Joiner.on(", "); + @Override public String getName() { return "datetimeformat"; @@ -29,7 +33,7 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args) { if (args.length > 0) { - return Functions.dateTimeFormat(var, args[0]); + return Functions.dateTimeFormat(var, args); } else { return Functions.dateTimeFormat(var); diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java index dba63730a..0b7894a6c 100644 --- a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java @@ -2,7 +2,9 @@ import static com.hubspot.jinjava.util.Logging.ENGINE_LOG; +import java.time.DateTimeException; import java.time.Instant; +import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.ArrayList; @@ -21,6 +23,7 @@ import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; import com.hubspot.jinjava.interpret.InterpretException; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.objects.date.InvalidDateFormatException; import com.hubspot.jinjava.objects.date.PyishDate; import com.hubspot.jinjava.objects.date.StrftimeFormatter; import com.hubspot.jinjava.tree.Node; @@ -57,10 +60,22 @@ public static List immutableListOf(Object... items) { @JinjavaDoc(value = "formats a date to a string", params = { @JinjavaParam(value = "var", type = "date", defaultValue = "current time"), - @JinjavaParam(value = "format", defaultValue = StrftimeFormatter.DEFAULT_DATE_FORMAT) + @JinjavaParam(value = "format", defaultValue = StrftimeFormatter.DEFAULT_DATE_FORMAT), + @JinjavaParam(value = "timezone", defaultValue = "utc", desc = "Time zone of output date") }) public static String dateTimeFormat(Object var, String... format) { - ZonedDateTime d = getDateTimeArg(var); + + ZoneId zoneOffset = ZoneOffset.UTC; + if (format.length > 1) { + String timezone = format[1]; + try { + zoneOffset = ZoneId.of(timezone); + } catch (DateTimeException e) { + throw new InvalidDateFormatException(timezone, e); + } + } + + ZonedDateTime d = getDateTimeArg(var, zoneOffset); if (d == null) { return ""; @@ -78,14 +93,14 @@ public static String dateTimeFormat(Object var, String... format) { } } - private static ZonedDateTime getDateTimeArg(Object var) { + private static ZonedDateTime getDateTimeArg(Object var, ZoneId zoneOffset) { ZonedDateTime d = null; if (var == null) { - d = ZonedDateTime.now(ZoneOffset.UTC); + d = ZonedDateTime.now(zoneOffset); } else if (var instanceof Number) { - d = ZonedDateTime.ofInstant(Instant.ofEpochMilli(((Number) var).longValue()), ZoneOffset.UTC); + d = ZonedDateTime.ofInstant(Instant.ofEpochMilli(((Number) var).longValue()), zoneOffset); } else if (var instanceof PyishDate) { d = ((PyishDate) var).toDateTime(); } else if (var instanceof ZonedDateTime) { @@ -101,7 +116,7 @@ private static ZonedDateTime getDateTimeArg(Object var) { @JinjavaParam(value = "var", type = "date", defaultValue = "current time"), }) public static long unixtimestamp(Object var) { - ZonedDateTime d = getDateTimeArg(var); + ZonedDateTime d = getDateTimeArg(var, ZoneOffset.UTC); if (d == null) { return 0; diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilterTest.java index cd74e55a3..16287c472 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilterTest.java @@ -11,6 +11,7 @@ import com.hubspot.jinjava.Jinjava; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.objects.date.InvalidDateFormatException; import com.hubspot.jinjava.objects.date.StrftimeFormatter; public class DateTimeFormatFilterTest { @@ -58,4 +59,20 @@ public void itHandlesVarsAndLiterals() throws Exception { assertThat(interpreter.getErrorsCopy()).isEmpty(); } + @Test + public void itSupportsTimezones() throws Exception { + assertThat(filter.filter(1539277785000L, interpreter, "%B %d, %Y, at %I:%M %p")).isEqualTo("October 11, 2018, at 05:09 PM"); + assertThat(filter.filter(1539277785000L, interpreter, "%B %d, %Y, at %I:%M %p", "America/New_York")).isEqualTo("October 11, 2018, at 01:09 PM"); + assertThat(filter.filter(1539277785000L, interpreter, "%B %d, %Y, at %I:%M %p", "UTC+8")).isEqualTo("October 12, 2018, at 01:09 AM"); + } + + @Test(expected = InvalidDateFormatException.class) + public void itThrowsExceptionOnInvalidTimezone() throws Exception { + filter.filter(1539277785000L, interpreter, "%B %d, %Y, at %I:%M %p", "Not a timezone"); + } + + @Test(expected = InvalidDateFormatException.class) + public void itThrowsExceptionOnInvalidDateformat() throws Exception { + filter.filter(1539277785000L, interpreter, "Not a format"); + } } From d42c40edbd8b6577252ba0263ffafd53baed5f90 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Thu, 11 Oct 2018 13:27:17 -0400 Subject: [PATCH 0513/2465] slight fix. --- .../com/hubspot/jinjava/lib/filter/DateTimeFormatFilter.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilter.java index c89bc3b28..5bbb9d03e 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilter.java @@ -1,6 +1,5 @@ package com.hubspot.jinjava.lib.filter; -import com.google.common.base.Joiner; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaParam; import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; @@ -21,8 +20,6 @@ }) public class DateTimeFormatFilter implements Filter { - private static final Joiner ERROR_MESSAGE_JOINER = Joiner.on(", "); - @Override public String getName() { return "datetimeformat"; @@ -38,7 +35,6 @@ public Object filter(Object var, JinjavaInterpreter interpreter, else { return Functions.dateTimeFormat(var); } - } } From f5e27a6c5f02e06c90e5d1e22b76268ba01beef4 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Thu, 11 Oct 2018 13:50:01 -0400 Subject: [PATCH 0514/2465] better zones. --- src/main/java/com/hubspot/jinjava/lib/fn/Functions.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java index 0b7894a6c..c473e7695 100644 --- a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java @@ -66,6 +66,7 @@ public static List immutableListOf(Object... items) { public static String dateTimeFormat(Object var, String... format) { ZoneId zoneOffset = ZoneOffset.UTC; + if (format.length > 1) { String timezone = format[1]; try { @@ -73,6 +74,8 @@ public static String dateTimeFormat(Object var, String... format) { } catch (DateTimeException e) { throw new InvalidDateFormatException(timezone, e); } + } else if (var instanceof ZonedDateTime) { + zoneOffset = ((ZonedDateTime) var).getZone(); } ZonedDateTime d = getDateTimeArg(var, zoneOffset); @@ -105,6 +108,7 @@ private static ZonedDateTime getDateTimeArg(Object var, ZoneId zoneOffset) { d = ((PyishDate) var).toDateTime(); } else if (var instanceof ZonedDateTime) { d = (ZonedDateTime) var; + d = d.withZoneSameInstant(zoneOffset); } else if (!ZonedDateTime.class.isAssignableFrom(var.getClass())) { throw new InterpretException("Input to function must be a date object, was: " + var.getClass()); } From dc28adbbe4e3abc651cc7370f6a87a9f7bfab87c Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Thu, 11 Oct 2018 13:53:45 -0400 Subject: [PATCH 0515/2465] Final conversions. --- src/main/java/com/hubspot/jinjava/lib/fn/Functions.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java index c473e7695..7d6b9e55c 100644 --- a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java @@ -76,6 +76,8 @@ public static String dateTimeFormat(Object var, String... format) { } } else if (var instanceof ZonedDateTime) { zoneOffset = ((ZonedDateTime) var).getZone(); + } else if (var instanceof PyishDate) { + zoneOffset = ((PyishDate) var).toDateTime().getZone(); } ZonedDateTime d = getDateTimeArg(var, zoneOffset); @@ -105,7 +107,9 @@ private static ZonedDateTime getDateTimeArg(Object var, ZoneId zoneOffset) { } else if (var instanceof Number) { d = ZonedDateTime.ofInstant(Instant.ofEpochMilli(((Number) var).longValue()), zoneOffset); } else if (var instanceof PyishDate) { - d = ((PyishDate) var).toDateTime(); + PyishDate pyishDate = ((PyishDate) var); + d = pyishDate.toDateTime(); + d = d.withZoneSameInstant(zoneOffset); } else if (var instanceof ZonedDateTime) { d = (ZonedDateTime) var; d = d.withZoneSameInstant(zoneOffset); From 1deb8ce0c3de88a85dae501bcfdebf6b5fc7f6e8 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Sun, 14 Oct 2018 13:15:50 -0400 Subject: [PATCH 0516/2465] Add 'prefix' function to ipaddr filter. --- .../jinjava/lib/filter/IpAddrFilter.java | 52 +++++++++++++++++-- .../jinjava/lib/filter/IpAddrFilterTest.java | 10 ++++ 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/IpAddrFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/IpAddrFilter.java index cf4cb92cd..ea008cd71 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/IpAddrFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/IpAddrFilter.java @@ -1,7 +1,9 @@ package com.hubspot.jinjava.lib.filter; +import java.util.List; import java.util.regex.Pattern; +import com.google.common.base.Splitter; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaParam; import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; @@ -26,22 +28,62 @@ public class IpAddrFilter implements Filter { private static final Pattern IP6_PATTERN = Pattern.compile("^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$"); private static final Pattern IP6_COMPRESSED_PATTERN = Pattern.compile("^((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)$"); + private static final Splitter PREFIX_SPLITTER = Splitter.on('/'); + private static final String PREFIX_STRING = "prefix"; + @Override - public Object filter(Object object, JinjavaInterpreter interpreter, String... arg) { + public Object filter(Object object, JinjavaInterpreter interpreter, String... args) { if (object == null) { return false; } + if (args.length > 0) { + String function = args[0].trim(); + if (function.equalsIgnoreCase(PREFIX_STRING)) { + return getPrefix(object); + } + } + if (object instanceof String) { - String address = ((String) object).trim(); - return IP4_PATTERN.matcher(address).matches() - || IP6_PATTERN.matcher(address).matches() - || IP6_COMPRESSED_PATTERN.matcher(address).matches(); + return validIp(((String) object).trim()); } + return false; } + private Integer getPrefix(Object object) { + + if (!(object instanceof String)) { + return null; + } + + String fullAddress = ((String) object).trim(); + + List parts = PREFIX_SPLITTER.splitToList(fullAddress); + if (parts.size() != 2) { + return null; + } + + String ipAddress = parts.get(0); + if (!validIp(ipAddress)) { + return null; + } + + String prefixString = parts.get(1); + try { + return Integer.parseInt(prefixString); + } catch (NumberFormatException ex) { + return null; + } + } + + private boolean validIp(String address) { + return IP4_PATTERN.matcher(address).matches() + || IP6_PATTERN.matcher(address).matches() + || IP6_COMPRESSED_PATTERN.matcher(address).matches(); + } + @Override public String getName() { return "ipaddr"; diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/IpAddrFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/IpAddrFilterTest.java index a219d0852..9a816f1d1 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/IpAddrFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/IpAddrFilterTest.java @@ -51,4 +51,14 @@ public void itRejectsInvalidIpV6Address() { assertThat(ipAddrFilter.filter("321", interpreter)).isEqualTo(false); } + @Test + public void itReturnsIpAddressPrefix() { + assertThat(ipAddrFilter.filter("255.182.100.1/24", interpreter, "prefix")).isEqualTo(24); + } + + @Test + public void itRejectsInvalidIpAddressPrefix() { + assertThat(ipAddrFilter.filter("255.182.100.abc/24", interpreter, "prefix")).isEqualTo(null); + } + } From 2573c9e5f542204c0473574e51326fa74b36e7e8 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Sun, 14 Oct 2018 13:20:22 -0400 Subject: [PATCH 0517/2465] fix. --- src/main/java/com/hubspot/jinjava/lib/filter/IpAddrFilter.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/IpAddrFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/IpAddrFilter.java index ea008cd71..a96c23a73 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/IpAddrFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/IpAddrFilter.java @@ -13,6 +13,7 @@ value = "Evaluates to true if the value is a valid IPv4 or IPv6 address", params = { @JinjavaParam(value = "value", type = "string", desc = "String to check IP Address"), + @JinjavaParam(value = "function", type = "string", desc = "Optional name of function. Supported functions: 'prefix'"), }, snippets = { @JinjavaSnippet( From b2ab994ed60414dec81fd4d7be57c40f96f7b156 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Mon, 15 Oct 2018 14:26:22 -0400 Subject: [PATCH 0518/2465] Update CHANGES.md --- CHANGES.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 80f2e1710..0637e1317 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,12 @@ # Jinjava Releases # +### 2018-10-15 Version 2.4.9 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.9%7Cjar)) ### +* [Add `ipaddr` filter to test valid IP addresses](https://github.com/HubSpot/jinjava/pull/237) +* [Enable nested properties for `selectattr` and `rejectattr`](https://github.com/HubSpot/jinjava/pull/238) +* [Add `do` tag to evaluate expressions without print](https://github.com/HubSpot/jinjava/pull/240) +* [Add support for timezone conversions in `datetimeformat` filter](https://github.com/HubSpot/jinjava/pull/241) +* [Add `prefix` function for `ipaddr` filter](https://github.com/HubSpot/jinjava/pull/243) + ### 2018-09-07 Version 2.4.8 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.8%7Cjar)) ### * [Bug fix for trunc division and power operations](https://github.com/HubSpot/jinjava/pull/234) From c39618f0b9bbd478c1d36fb2080d41de6abbfdf8 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Mon, 15 Oct 2018 18:31:06 +0000 Subject: [PATCH 0519/2465] [maven-release-plugin] prepare release jinjava-2.4.9 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1ed851b43..99baf416e 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.4.9-SNAPSHOT + 2.4.9 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.4.9 From ebbbcc827418cad010dcad1f8560207ff803e41d Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Mon, 15 Oct 2018 18:31:06 +0000 Subject: [PATCH 0520/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 99baf416e..1e40b202d 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.4.9 + 2.4.10-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.4.9 + HEAD From f9c49adbb791c360ef4dd9caff01f19060ca1882 Mon Sep 17 00:00:00 2001 From: David Byron Date: Thu, 18 Oct 2018 13:19:46 -0700 Subject: [PATCH 0521/2465] support negative list indeces so e.g. {{ (split_me|split('-'))[-2] }} works instead of {% set parts = split_me.split('-') %}{{ parts[parts|length - 2] }}" --- .../jinjava/el/ext/JinjavaListELResolver.java | 100 +++++++++++++++++- .../jinjava/el/ExpressionResolverTest.java | 35 ++++++ 2 files changed, 134 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/JinjavaListELResolver.java b/src/main/java/com/hubspot/jinjava/el/ext/JinjavaListELResolver.java index e413a6977..ec59e9f4f 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/JinjavaListELResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/JinjavaListELResolver.java @@ -1,5 +1,6 @@ package com.hubspot.jinjava.el.ext; +import java.util.List; import javax.el.ELContext; import javax.el.ListELResolver; @@ -30,12 +31,109 @@ public boolean isReadOnly(ELContext context, Object base, Object property) { @Override public Object getValue(ELContext context, Object base, Object property) { try { - return super.getValue(context, base, property); + final Object superValue = super.getValue(context, base, property); + if (superValue != null) { + return superValue; + } + return getValueNegativeIndex(context, base, property); } catch (IllegalArgumentException e) { return null; } } + /** + * Based on ListELResolver::getValue. If the base object is a list, and the + * given index is negative, returns the value at the given index. The index is + * specified by the property argument, and coerced into an integer. If the + * coercion could not be performed, an IllegalArgumentException is thrown. If + * the index is positive or out of bounds, null is returned. If the base is a + * List, the propertyResolved property of the ELContext object must be set to + * true by this resolver, before returning. If this property is not true after + * this method is called, the caller should ignore the return value. + * + *

    -1: the last element + * -2: the second to last element + * etc. + * + * @param context + * The context of this evaluation. + * @param base + * The list to analyze. Only bases of type List are handled by this resolver. + * @param property + * The index of the element in the list to return the acceptable type for. Will be + * coerced into an integer, but otherwise ignored by this resolver. + * @return If the propertyResolved property of ELContext was set to true, then the value at the + * given index or null if the index was out of bounds. Otherwise, undefined. + * @throws IllegalArgumentException + * if the property could not be coerced into an integer. + * @throws NullPointerException + * if context is null + * @throws ELException + * if an exception was thrown while performing the property or variable resolution. + * The thrown exception must be included as the cause property of this exception, if + * available. + */ + private Object getValueNegativeIndex(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException("context is null"); + } + + Object result = null; + if (isResolvable(base)) { + int index = toIndex(property); + List list = (List) base; + // Handle negative indeces. Assume 0 and positive indeces are handled + // elsewhere. + // + // For example, with a 4 element list, -4 means the element at index 0. + // -5 is out of bounds. + if ((index < 0) && (index >= (-1 * list.size()))) { + result = list.get(list.size() + index); + } + context.setPropertyResolved(true); + } + return result; + } + + /** + * Copied from the unfortunately private ListELResolver.isResolvable + */ + private static boolean isResolvable(Object base) { + return base instanceof List; + } + + /** + * Convert the given property to an index. Inspired by + * ListELResolver.toIndex, but without the base param since we only use it for + * getValue where base is null. + * + * @param property + * The name of the property to analyze. Will be coerced to a String. + * @return The index of property in base. + * @throws IllegalArgumentException + * if property cannot be coerced to an integer. + */ + private static int toIndex(Object property) { + int index = 0; + if (property instanceof Number) { + index = ((Number) property).intValue(); + } else if (property instanceof String) { + try { + // ListELResolver uses valueOf, but findbugs complains. + index = Integer.parseInt((String) property); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Cannot parse list index: " + property); + } + } else if (property instanceof Character) { + index = ((Character) property).charValue(); + } else if (property instanceof Boolean) { + index = ((Boolean) property).booleanValue() ? 1 : 0; + } else { + throw new IllegalArgumentException("Cannot coerce property to list index: " + property); + } + return index; + } + @Override public void setValue(ELContext context, Object base, Object property, Object value) { try { diff --git a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java index 222e8c1ed..2ab442708 100644 --- a/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java +++ b/src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java @@ -98,6 +98,41 @@ public void itResolvesListVal() { assertThat(val).isEqualTo(2L); } + @Test + public void itResolvesListStringNegative() { + context.put("thelist", Lists.newArrayList("foo", "bar", "blah")); + Object val = interpreter.resolveELExpression("thelist[-1]", -1); + assertThat(val).isEqualTo("blah"); + } + + @Test + public void itResolvesListStringNextToLast() { + context.put("thelist", Lists.newArrayList("foo", "bar", "blah")); + Object val = interpreter.resolveELExpression("thelist[-2]", -1); + assertThat(val).isEqualTo("bar"); + } + + @Test + public void itResolvesListStringNegativeIndicatingFirst() { + context.put("thelist", Lists.newArrayList("foo", "bar", "blah")); + Object val = interpreter.resolveELExpression("thelist[-3]", -1); + assertThat(val).isEqualTo("foo"); + } + + @Test + public void itResolvesListStringNegativeZero() { + context.put("thelist", Lists.newArrayList("foo", "bar", "blah")); + Object val = interpreter.resolveELExpression("thelist[-0]", -1); + assertThat(val).isEqualTo("foo"); + } + + @Test + public void itResolvesListStringNegativeOutOfBounds() { + context.put("thelist", Lists.newArrayList("foo", "bar", "blah")); + Object val = interpreter.resolveELExpression("thelist[-4]", -1); + assertThat(val).isEqualTo(null); + } + @Test public void itResolvesDictValWithBracket() { Map dict = Maps.newHashMap(); From 7d7949265b8976b083553be80506801a807ced91 Mon Sep 17 00:00:00 2001 From: David Byron Date: Thu, 18 Oct 2018 14:01:53 -0700 Subject: [PATCH 0522/2465] ditch getValueNegativeIndex and let the superclass do more --- .../jinjava/el/ext/JinjavaListELResolver.java | 70 +++---------------- 1 file changed, 11 insertions(+), 59 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/JinjavaListELResolver.java b/src/main/java/com/hubspot/jinjava/el/ext/JinjavaListELResolver.java index ec59e9f4f..df8e63985 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/JinjavaListELResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/JinjavaListELResolver.java @@ -31,73 +31,25 @@ public boolean isReadOnly(ELContext context, Object base, Object property) { @Override public Object getValue(ELContext context, Object base, Object property) { try { - final Object superValue = super.getValue(context, base, property); - if (superValue != null) { - return superValue; + // If we're dealing with a negative index, convert it to a positive one. + if (isResolvable(base)) { + int index = toIndex(property); + List list = (List) base; + if (index < 0) { + // Leave the range checking to the superclass. + index += list.size(); + } + return super.getValue(context, base, index); } - return getValueNegativeIndex(context, base, property); + return super.getValue(context, base, property); } catch (IllegalArgumentException e) { return null; } } /** - * Based on ListELResolver::getValue. If the base object is a list, and the - * given index is negative, returns the value at the given index. The index is - * specified by the property argument, and coerced into an integer. If the - * coercion could not be performed, an IllegalArgumentException is thrown. If - * the index is positive or out of bounds, null is returned. If the base is a - * List, the propertyResolved property of the ELContext object must be set to - * true by this resolver, before returning. If this property is not true after - * this method is called, the caller should ignore the return value. - * - *

    -1: the last element - * -2: the second to last element - * etc. - * - * @param context - * The context of this evaluation. - * @param base - * The list to analyze. Only bases of type List are handled by this resolver. - * @param property - * The index of the element in the list to return the acceptable type for. Will be - * coerced into an integer, but otherwise ignored by this resolver. - * @return If the propertyResolved property of ELContext was set to true, then the value at the - * given index or null if the index was out of bounds. Otherwise, undefined. - * @throws IllegalArgumentException - * if the property could not be coerced into an integer. - * @throws NullPointerException - * if context is null - * @throws ELException - * if an exception was thrown while performing the property or variable resolution. - * The thrown exception must be included as the cause property of this exception, if - * available. + * Copied from the unfortunately private ListELResolver.isResolvable */ - private Object getValueNegativeIndex(ELContext context, Object base, Object property) { - if (context == null) { - throw new NullPointerException("context is null"); - } - - Object result = null; - if (isResolvable(base)) { - int index = toIndex(property); - List list = (List) base; - // Handle negative indeces. Assume 0 and positive indeces are handled - // elsewhere. - // - // For example, with a 4 element list, -4 means the element at index 0. - // -5 is out of bounds. - if ((index < 0) && (index >= (-1 * list.size()))) { - result = list.get(list.size() + index); - } - context.setPropertyResolved(true); - } - return result; - } - - /** - * Copied from the unfortunately private ListELResolver.isResolvable - */ private static boolean isResolvable(Object base) { return base instanceof List; } From 60e5d9176aa759a57f73eab6c10cc08d69bca6c4 Mon Sep 17 00:00:00 2001 From: David Byron Date: Thu, 18 Oct 2018 14:21:28 -0700 Subject: [PATCH 0523/2465] call super.getValue once --- .../java/com/hubspot/jinjava/el/ext/JinjavaListELResolver.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/JinjavaListELResolver.java b/src/main/java/com/hubspot/jinjava/el/ext/JinjavaListELResolver.java index df8e63985..39b11bfdf 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/JinjavaListELResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/JinjavaListELResolver.java @@ -37,9 +37,8 @@ public Object getValue(ELContext context, Object base, Object property) { List list = (List) base; if (index < 0) { // Leave the range checking to the superclass. - index += list.size(); + property = index + list.size(); } - return super.getValue(context, base, index); } return super.getValue(context, base, property); } catch (IllegalArgumentException e) { From 6cda4efc04ad96459c8ad4287ec0c88a454cd931 Mon Sep 17 00:00:00 2001 From: David Byron Date: Thu, 18 Oct 2018 14:33:05 -0700 Subject: [PATCH 0524/2465] inline cast of base to List --- .../java/com/hubspot/jinjava/el/ext/JinjavaListELResolver.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/el/ext/JinjavaListELResolver.java b/src/main/java/com/hubspot/jinjava/el/ext/JinjavaListELResolver.java index 39b11bfdf..77c2bd970 100644 --- a/src/main/java/com/hubspot/jinjava/el/ext/JinjavaListELResolver.java +++ b/src/main/java/com/hubspot/jinjava/el/ext/JinjavaListELResolver.java @@ -34,10 +34,9 @@ public Object getValue(ELContext context, Object base, Object property) { // If we're dealing with a negative index, convert it to a positive one. if (isResolvable(base)) { int index = toIndex(property); - List list = (List) base; if (index < 0) { // Leave the range checking to the superclass. - property = index + list.size(); + property = index + ((List) base).size(); } } return super.getValue(context, base, property); From bcbc751eb9c56e12164fd93b503ac3f4a5bdc403 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Fri, 19 Oct 2018 22:34:59 -0400 Subject: [PATCH 0525/2465] Update CHANGES.md --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 0637e1317..9f89c709a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Jinjava Releases # + +### 2018-10-19 Version 2.4.10 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.10%7Cjar)) ### +* [Support negative array indices](https://github.com/HubSpot/jinjava/pull/245) + ### 2018-10-15 Version 2.4.9 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.9%7Cjar)) ### * [Add `ipaddr` filter to test valid IP addresses](https://github.com/HubSpot/jinjava/pull/237) * [Enable nested properties for `selectattr` and `rejectattr`](https://github.com/HubSpot/jinjava/pull/238) From 56e448de4b5a3aaa52486c481ab312f750ff0d59 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Sat, 20 Oct 2018 02:38:44 +0000 Subject: [PATCH 0526/2465] [maven-release-plugin] prepare release jinjava-2.4.10 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1e40b202d..3c4f99b1b 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.4.10-SNAPSHOT + 2.4.10 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.4.10 From 16667b4676030e597a3fd53e7c4bfd69474924c6 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Sat, 20 Oct 2018 02:38:44 +0000 Subject: [PATCH 0527/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3c4f99b1b..79ee06158 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.4.10 + 2.4.11-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.4.10 + HEAD From a77d398ea78ddb29c7ed2c17b030b4b10b47c255 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Tue, 23 Oct 2018 11:01:48 -0400 Subject: [PATCH 0528/2465] Add function for getting start of day. --- .../jinjava/lib/fn/FunctionLibrary.java | 1 + .../com/hubspot/jinjava/lib/fn/Functions.java | 20 +++++++++++- .../jinjava/lib/fn/TodayFunctionTest.java | 31 +++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/test/java/com/hubspot/jinjava/lib/fn/TodayFunctionTest.java diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/FunctionLibrary.java b/src/main/java/com/hubspot/jinjava/lib/fn/FunctionLibrary.java index e06d5daf5..fec636096 100644 --- a/src/main/java/com/hubspot/jinjava/lib/fn/FunctionLibrary.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/FunctionLibrary.java @@ -18,6 +18,7 @@ protected void registerDefaults() { register(new ELFunctionDefinition("", "truncate", Functions.class, "truncate", Object.class, Object[].class)); register(new ELFunctionDefinition("", "range", Functions.class, "range", Object.class, Object[].class)); register(new ELFunctionDefinition("", "type", TypeFunction.class, "type", Object.class)); + register(new ELFunctionDefinition("", "today", Functions.class, "today", String[].class)); register(new ELFunctionDefinition("", "super", Functions.class, "renderSuperBlock")); diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java index 7d6b9e55c..1a63dbf8b 100644 --- a/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java +++ b/src/main/java/com/hubspot/jinjava/lib/fn/Functions.java @@ -58,6 +58,25 @@ public static List immutableListOf(Object... items) { return Collections.unmodifiableList(Lists.newArrayList(items)); } + @JinjavaDoc(value = "return datetime of beginning of the day", params = { + @JinjavaParam(value = "timezone", type = "string", defaultValue = "utc", desc = "Optional timezone"), + }) + public static ZonedDateTime today(String... var) { + + ZoneId zoneOffset = ZoneOffset.UTC; + if (var.length > 0) { + String timezone = var[0]; + try { + zoneOffset = ZoneId.of(timezone); + } catch (DateTimeException e) { + throw new InvalidDateFormatException(timezone, e); + } + } + + ZonedDateTime dateTime = getDateTimeArg(null, zoneOffset); + return dateTime.toLocalDate().atStartOfDay(zoneOffset); + } + @JinjavaDoc(value = "formats a date to a string", params = { @JinjavaParam(value = "var", type = "date", defaultValue = "current time"), @JinjavaParam(value = "format", defaultValue = StrftimeFormatter.DEFAULT_DATE_FORMAT), @@ -81,7 +100,6 @@ public static String dateTimeFormat(Object var, String... format) { } ZonedDateTime d = getDateTimeArg(var, zoneOffset); - if (d == null) { return ""; } diff --git a/src/test/java/com/hubspot/jinjava/lib/fn/TodayFunctionTest.java b/src/test/java/com/hubspot/jinjava/lib/fn/TodayFunctionTest.java new file mode 100644 index 000000000..2787153a6 --- /dev/null +++ b/src/test/java/com/hubspot/jinjava/lib/fn/TodayFunctionTest.java @@ -0,0 +1,31 @@ +package com.hubspot.jinjava.lib.fn; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; + +import org.junit.Test; + +import com.hubspot.jinjava.objects.date.InvalidDateFormatException; + +public class TodayFunctionTest { + + @Test + public void itDefaultsToUtcTimezone() { + ZonedDateTime zonedDateTime = Functions.today(); + assertThat(zonedDateTime.getZone()).isEqualTo(ZoneOffset.UTC); + } + + @Test + public void itParsesTimezones() { + ZonedDateTime zonedDateTime = Functions.today("America/New_York"); + assertThat(zonedDateTime.getZone()).isEqualTo(ZoneId.of("America/New_York")); + } + + @Test(expected = InvalidDateFormatException.class) + public void itThrowsExceptionOnInvalidTimezone() { + ZonedDateTime zonedDateTime = Functions.today("Not a timezone"); + } +} From 5de7a5b95a079b8674c9c6c9e7808f0dd2f28824 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Tue, 23 Oct 2018 11:48:41 -0400 Subject: [PATCH 0529/2465] Update CHANGES.md --- CHANGES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 9f89c709a..12237a5df 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,7 @@ # Jinjava Releases # +### 2018-10-23 Version 2.4.11 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.11%7Cjar)) ### +* [Add function for getting start of day](https://github.com/HubSpot/jinjava/pull/247) ### 2018-10-19 Version 2.4.10 ([Maven Central](https://search.maven.org/#artifactdetails%7Ccom.hubspot.jinjava%7Cjinjava%7C2.4.10%7Cjar)) ### * [Support negative array indices](https://github.com/HubSpot/jinjava/pull/245) From 9f2be1b84039c9e91113eacdc98b3c3400b97b06 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Tue, 23 Oct 2018 15:51:57 +0000 Subject: [PATCH 0530/2465] [maven-release-plugin] prepare release jinjava-2.4.11 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 79ee06158..aee1c2195 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.4.11-SNAPSHOT + 2.4.11 Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - HEAD + jinjava-2.4.11 From 3d7461b48b88c5f50bd0aec6f01533d8a4e52de7 Mon Sep 17 00:00:00 2001 From: HubSpot PaaS Team Date: Tue, 23 Oct 2018 15:51:57 +0000 Subject: [PATCH 0531/2465] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index aee1c2195..3c6e9a4ca 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.hubspot.jinjava jinjava - 2.4.11 + 2.4.12-SNAPSHOT Jinja templating engine implemented in Java https://github.com/HubSpot/jinjava @@ -17,7 +17,7 @@ scm:git:git@github.com:HubSpot/jinjava.git scm:git:git@github.com:HubSpot/jinjava.git git@github.com:HubSpot/jinjava.git - jinjava-2.4.11 + HEAD From ef7ecc517128956a2e042e14f1c0370202a6b298 Mon Sep 17 00:00:00 2001 From: Matt Coley Date: Fri, 2 Nov 2018 11:05:36 -0400 Subject: [PATCH 0532/2465] Support expressions in selectattr. --- .../jinjava/lib/filter/SelectAttrFilter.java | 18 +++++++++++-- .../lib/filter/SelectAttrFilterTest.java | 26 ++++++++++++++++--- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java index fbe2766b8..c07e5e075 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java @@ -4,6 +4,7 @@ import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaParam; @@ -13,7 +14,6 @@ import com.hubspot.jinjava.lib.exptest.ExpTest; import com.hubspot.jinjava.util.ForLoop; import com.hubspot.jinjava.util.ObjectIterator; -import com.hubspot.jinjava.util.Variable; @JinjavaDoc( value = "Filters a sequence of objects by applying a test to an attribute of an object and only selecting the ones with the test succeeding.", @@ -77,7 +77,16 @@ protected Object applyFilter(Object var, JinjavaInterpreter interpreter, Object[ while (loop.hasNext()) { Object val = loop.next(); - Object attrVal = new Variable(interpreter, String.format("%s.%s", "placeholder", attr)).resolve(val); + // push temporary variable to be resolved + String tempValue = generateTempVariable(); + while (interpreter.getContext().containsKey(tempValue)) { + tempValue = generateTempVariable(); + } + + interpreter.getContext().put(tempValue, val); + + Object attrVal = interpreter.resolveELExpression(String.format("%s.%s", tempValue, attr), interpreter.getLineNumber()); + interpreter.getContext().remove(tempValue); if (acceptObjects == expTest.evaluate(attrVal, interpreter, expArgs)) { result.add(val); } @@ -85,4 +94,9 @@ protected Object applyFilter(Object var, JinjavaInterpreter interpreter, Object[ return result; } + + private String generateTempVariable() { + return "jj_temp_" + Math.abs(ThreadLocalRandom.current().nextInt()); + } + } diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/SelectAttrFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/SelectAttrFilterTest.java index a8727c242..46bc9d067 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/SelectAttrFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/SelectAttrFilterTest.java @@ -3,10 +3,12 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.HashMap; +import java.util.List; import org.junit.Before; import org.junit.Test; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.hubspot.jinjava.Jinjava; @@ -18,9 +20,9 @@ public class SelectAttrFilterTest { public void setup() { jinjava = new Jinjava(); jinjava.getGlobalContext().put("users", Lists.newArrayList( - new User(0, false, "foo@bar.com", new Option(0, "option0")), - new User(1, true, "bar@bar.com", new Option(1, "option1")), - new User(2, false, null, new Option(2, "option2")))); + new User(0, false, "foo@bar.com", new Option(0, "option0"), ImmutableList.of(new Option(0, "option0"))), + new User(1, true, "bar@bar.com", new Option(1, "option1"), ImmutableList.of(new Option(1, "option1"))), + new User(2, false, null, new Option(2, "option2"), ImmutableList.of(new Option(2, "option2"))))); } @Test @@ -56,18 +58,30 @@ public void selectAttrWithNestedProperty() { .isEqualTo("[2]"); } + @Test + public void selectAttrWithNestedFilter() { + + assertThat(jinjava.render("{{ users|selectattr(\"optionList|map('id')\", 'containing', 1) }}", new HashMap())) + .isEqualTo("[1]"); + + assertThat(jinjava.render("{{ users|selectattr(\"optionList|map('name')\", 'containing', 'option2') }}", new HashMap())) + .isEqualTo("[2]"); + } + public static class User { private long num; private boolean isActive; private String email; private Option option; + private List