You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2017/09/25 15:16:10 UTC

[1/4] incubator-freemarker git commit: New built-in, sequence. This can be used to work around situations where a listable value lacks some features that you need in the template (like it can't be listed twice, it can't tell its size, etc.), and you can'

Repository: incubator-freemarker
Updated Branches:
  refs/heads/2.3 dd60aef18 -> bdc902508


New built-in, sequence. This can be used to work around situations where a listable value lacks some features that you need in the template (like it can't be listed twice, it can't tell its size, etc.), and you can't modify the data-model to fix the problem. See more...


Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/2d0b4931
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/2d0b4931
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/2d0b4931

Branch: refs/heads/2.3
Commit: 2d0b49319a216e610a5aa0ade5dd24ae7881457d
Parents: a671110
Author: ddekany <dd...@apache.org>
Authored: Sun Sep 17 18:28:30 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Sun Sep 17 18:28:30 2017 +0200

----------------------------------------------------------------------
 src/main/java/freemarker/core/BuiltIn.java      |  4 +-
 .../freemarker/core/BuiltInsForSequences.java   | 29 +++++++
 .../freemarker/core/CollectionAndSequence.java  | 11 ++-
 .../freemarker/core/NonSequenceException.java   |  9 ++-
 .../core/UnexpectedTypeException.java           | 12 +++
 src/manual/en_US/book.xml                       | 84 ++++++++++++++++++--
 .../freemarker/core/SequenceBuiltInTest.java    | 82 +++++++++++++++++++
 7 files changed, 216 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/2d0b4931/src/main/java/freemarker/core/BuiltIn.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/BuiltIn.java b/src/main/java/freemarker/core/BuiltIn.java
index b4e3239..630f895 100644
--- a/src/main/java/freemarker/core/BuiltIn.java
+++ b/src/main/java/freemarker/core/BuiltIn.java
@@ -61,6 +61,7 @@ import freemarker.core.BuiltInsForSequences.lastBI;
 import freemarker.core.BuiltInsForSequences.reverseBI;
 import freemarker.core.BuiltInsForSequences.seq_containsBI;
 import freemarker.core.BuiltInsForSequences.seq_index_ofBI;
+import freemarker.core.BuiltInsForSequences.sequenceBI;
 import freemarker.core.BuiltInsForSequences.sortBI;
 import freemarker.core.BuiltInsForSequences.sort_byBI;
 import freemarker.core.BuiltInsForStringsMisc.evalBI;
@@ -83,7 +84,7 @@ abstract class BuiltIn extends Expression implements Cloneable {
 
     static final Set<String> CAMEL_CASE_NAMES = new TreeSet<String>();
     static final Set<String> SNAKE_CASE_NAMES = new TreeSet<String>();
-    static final int NUMBER_OF_BIS = 263;
+    static final int NUMBER_OF_BIS = 264;
     static final HashMap<String, BuiltIn> BUILT_INS_BY_NAME = new HashMap(NUMBER_OF_BIS * 3 / 2 + 1, 1f);
 
     static {
@@ -262,6 +263,7 @@ abstract class BuiltIn extends Expression implements Cloneable {
         putBI("seq_contains", "seqContains", new seq_containsBI());
         putBI("seq_index_of", "seqIndexOf", new seq_index_ofBI(true));
         putBI("seq_last_index_of", "seqLastIndexOf", new seq_index_ofBI(false));
+        putBI("sequence", new sequenceBI());
         putBI("short", new shortBI());
         putBI("size", new BuiltInsForMultipleTypes.sizeBI());
         putBI("sort_by", "sortBy", new sort_byBI());

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/2d0b4931/src/main/java/freemarker/core/BuiltInsForSequences.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/BuiltInsForSequences.java b/src/main/java/freemarker/core/BuiltInsForSequences.java
index 8430133..f2d1c6a 100644
--- a/src/main/java/freemarker/core/BuiltInsForSequences.java
+++ b/src/main/java/freemarker/core/BuiltInsForSequences.java
@@ -30,8 +30,10 @@ import java.util.List;
 import freemarker.ext.beans.CollectionModel;
 import freemarker.template.SimpleNumber;
 import freemarker.template.SimpleScalar;
+import freemarker.template.SimpleSequence;
 import freemarker.template.TemplateBooleanModel;
 import freemarker.template.TemplateCollectionModel;
+import freemarker.template.TemplateCollectionModelEx;
 import freemarker.template.TemplateDateModel;
 import freemarker.template.TemplateException;
 import freemarker.template.TemplateHashModel;
@@ -829,6 +831,33 @@ class BuiltInsForSequences {
         
     }
 
+    static class sequenceBI extends BuiltIn {
+
+        @Override
+        TemplateModel _eval(Environment env) throws TemplateException {
+            TemplateModel model = target.eval(env);
+            
+            if (model instanceof TemplateSequenceModel && !isBuggySeqButGoodCollection(model)) {
+                return model;
+            }
+            
+            if (!(model instanceof TemplateCollectionModel)) {
+                throw new NonSequenceOrCollectionException(target, model, env);
+            }
+            TemplateCollectionModel coll = (TemplateCollectionModel) model;
+            
+            SimpleSequence seq =
+                    coll instanceof TemplateCollectionModelEx
+                            ? new SimpleSequence(((TemplateCollectionModelEx) coll).size())
+                            : new SimpleSequence();
+            for (TemplateModelIterator iter = coll.iterator(); iter.hasNext(); ) {
+                seq.add(iter.next());
+            }
+            return seq;
+        }
+        
+    }
+    
     private static boolean isBuggySeqButGoodCollection(
             TemplateModel model) {
         return model instanceof CollectionModel

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/2d0b4931/src/main/java/freemarker/core/CollectionAndSequence.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/CollectionAndSequence.java b/src/main/java/freemarker/core/CollectionAndSequence.java
index 79e7576..33556ff 100644
--- a/src/main/java/freemarker/core/CollectionAndSequence.java
+++ b/src/main/java/freemarker/core/CollectionAndSequence.java
@@ -23,6 +23,7 @@ import java.io.Serializable;
 import java.util.ArrayList;
 
 import freemarker.template.TemplateCollectionModel;
+import freemarker.template.TemplateCollectionModelEx;
 import freemarker.template.TemplateModel;
 import freemarker.template.TemplateModelException;
 import freemarker.template.TemplateModelIterator;
@@ -30,13 +31,13 @@ import freemarker.template.TemplateSequenceModel;
 
 /**
  * Add sequence capabilities to an existing collection, or
- * vice versa. Used by ?keys and ?values built-ins.
+ * vice versa. Used by the ?keys and ?values built-ins.
  */
 final public class CollectionAndSequence
 implements TemplateCollectionModel, TemplateSequenceModel, Serializable {
     private TemplateCollectionModel collection;
     private TemplateSequenceModel sequence;
-    private ArrayList data;
+    private ArrayList<TemplateModel> data;
 
     public CollectionAndSequence(TemplateCollectionModel collection) {
         this.collection = collection;
@@ -59,13 +60,15 @@ implements TemplateCollectionModel, TemplateSequenceModel, Serializable {
             return sequence.get(i);
         } else {
             initSequence();
-            return (TemplateModel) data.get(i);
+            return data.get(i);
         }
     }
 
     public int size() throws TemplateModelException {
         if (sequence != null) {
             return sequence.size();
+        } if (collection instanceof TemplateCollectionModelEx) {
+            return ((TemplateCollectionModelEx) collection).size();
         } else {
             initSequence();
             return data.size();
@@ -74,7 +77,7 @@ implements TemplateCollectionModel, TemplateSequenceModel, Serializable {
 
     private void initSequence() throws TemplateModelException {
         if (data == null) {
-            data = new ArrayList();
+            data = new ArrayList<TemplateModel>();
             TemplateModelIterator it = collection.iterator();
             while (it.hasNext()) {
                 data.add(it.next());

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/2d0b4931/src/main/java/freemarker/core/NonSequenceException.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/NonSequenceException.java b/src/main/java/freemarker/core/NonSequenceException.java
index 2b85ab9..21440bc 100644
--- a/src/main/java/freemarker/core/NonSequenceException.java
+++ b/src/main/java/freemarker/core/NonSequenceException.java
@@ -21,6 +21,7 @@ package freemarker.core;
 
 import freemarker.template.TemplateModel;
 import freemarker.template.TemplateSequenceModel;
+import freemarker.template.utility.CollectionUtils;
 
 /**
  * Indicates that a {@link TemplateSequenceModel} value was expected, but the value had a different type.
@@ -46,19 +47,19 @@ public class NonSequenceException extends UnexpectedTypeException {
     NonSequenceException(
             Expression blamed, TemplateModel model, Environment env)
             throws InvalidReferenceException {
-        super(blamed, model, "sequence", EXPECTED_TYPES, env);
+        this(blamed, model, CollectionUtils.EMPTY_OBJECT_ARRAY, env);
     }
 
     NonSequenceException(
             Expression blamed, TemplateModel model, String tip,
             Environment env)
             throws InvalidReferenceException {
-        super(blamed, model, "sequence", EXPECTED_TYPES, tip, env);
+        this(blamed, model, new Object[] { tip }, env);
     }
 
     NonSequenceException(
-            Expression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException {
+            Expression blamed, TemplateModel model, Object[] tips, Environment env) throws InvalidReferenceException {
         super(blamed, model, "sequence", EXPECTED_TYPES, tips, env);
     }    
-
+    
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/2d0b4931/src/main/java/freemarker/core/UnexpectedTypeException.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/UnexpectedTypeException.java b/src/main/java/freemarker/core/UnexpectedTypeException.java
index 0878f81..61b0fe4 100644
--- a/src/main/java/freemarker/core/UnexpectedTypeException.java
+++ b/src/main/java/freemarker/core/UnexpectedTypeException.java
@@ -19,8 +19,13 @@
 
 package freemarker.core;
 
+import java.util.Arrays;
+
+import freemarker.template.TemplateCollectionModel;
+import freemarker.template.TemplateCollectionModelEx;
 import freemarker.template.TemplateException;
 import freemarker.template.TemplateModel;
+import freemarker.template.TemplateSequenceModel;
 
 /**
  * The type of a value differs from what was expected.
@@ -88,6 +93,13 @@ public class UnexpectedTypeException extends TemplateException {
                 errorDescBuilder.tip(tip);
             }
         }
+        if (model instanceof TemplateCollectionModel
+                && (Arrays.asList(expectedTypes).contains(TemplateSequenceModel.class)
+                        || Arrays.asList(expectedTypes).contains(TemplateCollectionModelEx.class))) {
+            errorDescBuilder.tip("As the problematic value contains a collection of items, you could convert it "
+                    + "to a sequence like someValue?sequence. Be sure though that you won't have a large number of "
+                    + "items, as all will be held in memory one the same time.");
+        }
         return errorDescBuilder;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/2d0b4931/src/manual/en_US/book.xml
----------------------------------------------------------------------
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index 21c3479..569cbac 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -12812,6 +12812,10 @@ grant codeBase "file:/path/to/freemarker.jar"
           </listitem>
 
           <listitem>
+            <para><link linkend="ref_builtin_sequence">sequence</link></para>
+          </listitem>
+
+          <listitem>
             <para><link linkend="ref_builtin_sort_by">sort_by</link></para>
           </listitem>
 
@@ -18439,6 +18443,61 @@ ${1305575275540?number_to_time}</programlisting>
 May 16, 2011
 3:47:55 PM</programlisting>
         </section>
+
+        <section xml:id="ref_builtin_sequence">
+          <title>sequence</title>
+
+          <indexterm>
+            <primary>seq_sequence built-in</primary>
+          </indexterm>
+
+          <para>This built-in is used to convert a listable value (one that
+          you can iterate through with the <link
+          linkend="ref.directive.list"><literal>list</literal>
+          directive</link>) to a more capable <link
+          linkend="dgui_datamodel_container">sequence</link> value. Sequences
+          support operations like <literal>xs[index]</literal> and
+          <literal>xs?size</literal>. Also, the resulting value is listable
+          for multiple times, even if the original value was backed by a
+          <literal>java.util.Iterator</literal>. This built-in is typically
+          used to work around data-model problems, in case you can't fix the
+          data-model itself. If you can, always fix the data-model instead
+          (give a <literal>java.util.List</literal> or array to the template
+          instead of a more restricted object, like a
+          non-<literal>List</literal> <literal>java.util.Collection</literal>,
+          or a <literal>java.util.Iterator</literal>).</para>
+
+          <para>If the value is already a sequence, then this built-in just
+          returns that as is. If the value is not something that the <link
+          linkend="ref.directive.list"><literal>list</literal>
+          directive</link> could list, then template processing will be
+          aborted with error. Otherwise, it fetches all the values, and stores
+          them into a sequence. Be careful if you can have a huge number of
+          items, as all of them will be held in memory on the same
+          time.</para>
+
+          <para>You should convert a value with <literal>sequence</literal>
+          only once. If you need the resulting sequence at multiple places,
+          always assign the result to a variable, because if the value you
+          convert is only listable once, converting it for the second time
+          will result in error or an empty sequence. Also the conversion is
+          somewhat costly for big collections, so it's better to do it only
+          once.</para>
+
+          <para>Example: Let's say you find that <literal>users</literal> is
+          only listable once (because it's a
+          <literal>java.util.Iterator</literal>), but you need to list it for
+          multiple times in the template, and you can't fix the data-model.
+          Then you could do this:</para>
+
+          <programlisting role="template">&lt;#-- Collect all the users into a sequence: --&gt;
+&lt;#assign usersSeq = users?sequence&gt;
+
+&lt;#list usersSeq as user&gt;...&lt;/#list&gt;
+Again:
+&lt;#list usersSeq as user&gt;...&lt;/#list&gt;
+</programlisting>
+        </section>
       </section>
     </chapter>
 
@@ -18494,6 +18553,11 @@ May 16, 2011
           </listitem>
 
           <listitem>
+            <para><link
+            linkend="ref.directive.list.continue">continue</link></para>
+          </listitem>
+
+          <listitem>
             <para><link linkend="ref.directive.default">default</link></para>
           </listitem>
 
@@ -26989,11 +27053,10 @@ TemplateModel x = env.getVariable("x");  // get variable x</programlisting>
 
           <itemizedlist>
             <listitem>
-              <para>Added the <link
-              linkend="ref.directive.list.continue"><literal>continue</literal>
-              directive</link>, which can be used inside a
-              <literal>list</literal> to skip to the next iteration (similarly
-              as in Java).</para>
+              <para>New directive: <literal>continue</literal>. This can be
+              used inside the <literal>list</literal> directive to skip to the
+              next iteration (similarly as in Java). <link
+              linkend="ref.directive.list.continue">See more...</link></para>
             </listitem>
 
             <listitem>
@@ -27010,6 +27073,15 @@ TemplateModel x = env.getVariable("x");  // get variable x</programlisting>
             </listitem>
 
             <listitem>
+              <para>New built-in, <literal>sequence</literal>. This can be
+              used to work around situations where a listable value lacks some
+              features that you need in the template (like it can't be listed
+              twice, it can't tell its size, etc.), and you can't modify the
+              data-model to fix the problem. <link
+              linkend="ref_builtin_sequence">See more...</link></para>
+            </listitem>
+
+            <listitem>
               <para>Bug fixed (<link
               xlink:href="https://issues.apache.org/jira/browse/FREEMARKER-70">FREEMARKER-70</link>):
               The usage of loop variable built-ins, like
@@ -27033,7 +27105,7 @@ TemplateModel x = env.getVariable("x");  // get variable x</programlisting>
               xlink:href="https://issues.apache.org/jira/browse/FREEMARKER-71">FREEMARKER-71</link>):
               When using
               <literal><replaceable>exp</replaceable>?eval</literal>, if the
-              expression inside evaluated string throws an exception, the
+              expression inside the evaluated string throws an exception, the
               cause exception of that exception was lost.</para>
             </listitem>
           </itemizedlist>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/2d0b4931/src/test/java/freemarker/core/SequenceBuiltInTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/SequenceBuiltInTest.java b/src/test/java/freemarker/core/SequenceBuiltInTest.java
new file mode 100644
index 0000000..77eecc5
--- /dev/null
+++ b/src/test/java/freemarker/core/SequenceBuiltInTest.java
@@ -0,0 +1,82 @@
+package freemarker.core;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+import freemarker.template.Configuration;
+import freemarker.template.DefaultIterableAdapter;
+import freemarker.template.DefaultNonListCollectionAdapter;
+import freemarker.template.TemplateCollectionModelEx;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateModel;
+import freemarker.template.TemplateSequenceModel;
+import freemarker.template.utility.ObjectWrapperWithAPISupport;
+import freemarker.test.TemplateTest;
+
+public class SequenceBuiltInTest extends TemplateTest {
+
+    @Test
+    public void testWithCollection() throws TemplateException, IOException {
+        ObjectWrapperWithAPISupport ow = (ObjectWrapperWithAPISupport) getConfiguration().getObjectWrapper();
+        
+        TemplateModel xs = DefaultIterableAdapter.adapt(ImmutableSet.of("a", "b"), ow);
+        assertThat(xs, not(instanceOf(TemplateCollectionModelEx.class)));
+        assertThat(xs, not(instanceOf(TemplateSequenceModel.class)));
+        addToDataModel("xs", xs);
+
+        try {
+            assertOutput("${xs[1]}", "b");
+            fail();
+        } catch (TemplateException e) {
+            System.out.println(e); //!!T
+            assertThat(e.getMessage(), containsString("?sequence")); // Contains tip to use ?sequence
+        }
+        assertOutput("${xs?sequence[1]}", "b");
+        
+        try {
+            assertOutput("${xs?size}", "2");
+            fail();
+        } catch (TemplateException e) {
+            System.out.println(e); //!!T
+            assertThat(e.getMessage(), containsString("?sequence")); // Contains tip to use ?sequence
+        }
+        assertOutput("${xs?sequence?size}", "2");
+    }
+
+    @Test
+    public void testWithCollectionEx() throws TemplateException, IOException {
+        ObjectWrapperWithAPISupport ow = (ObjectWrapperWithAPISupport) getConfiguration().getObjectWrapper();
+        
+        TemplateModel xs = DefaultNonListCollectionAdapter.adapt(ImmutableSet.of("a", "b"), ow);
+        assertThat(xs, not(instanceOf(TemplateSequenceModel.class)));
+        assertThat(xs, instanceOf(TemplateCollectionModelEx.class));
+        addToDataModel("xs", xs);
+
+        try {
+            assertOutput("${xs[1]}", "b");
+            fail();
+        } catch (TemplateException e) {
+            assertThat(e.getMessage(), containsString("?sequence")); // Contains tip to use ?sequence
+        }
+        assertOutput("${xs?sequence[1]}", "b");
+
+        assertOutput("${xs?size}", "2"); // No need for ?sequence
+    }
+
+    @Test
+    public void testWithSequence() throws TemplateException, IOException {
+        assertOutput("${[11, 12]?sequence[1]}", "12");
+        
+        
+        getConfiguration().setIncompatibleImprovements(Configuration.VERSION_2_3_23);
+        // As it returns the sequence as is, it works with an infinite sequence:
+        assertOutput("${(11..)?sequence[1]}", "12");
+    }
+
+}


[4/4] incubator-freemarker git commit: Merge remote-tracking branch 'origin/2.3-gae' into 2.3

Posted by dd...@apache.org.
Merge remote-tracking branch 'origin/2.3-gae' into 2.3


Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/bdc90250
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/bdc90250
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/bdc90250

Branch: refs/heads/2.3
Commit: bdc902508893c202a1cc370ef6c19a9b18abd16b
Parents: dd60aef b246de2
Author: ddekany <dd...@apache.org>
Authored: Mon Sep 25 17:15:38 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Mon Sep 25 17:15:38 2017 +0200

----------------------------------------------------------------------
 .../freemarker/core/ArithmeticExpression.java   |  38 +++---
 src/main/java/freemarker/core/BuiltIn.java      |   4 +-
 .../freemarker/core/BuiltInsForSequences.java   |  29 +++++
 .../freemarker/core/CollectionAndSequence.java  |  11 +-
 src/main/java/freemarker/core/Configurable.java |  26 ++--
 .../freemarker/core/NonSequenceException.java   |   9 +-
 .../core/UnexpectedTypeException.java           |  12 ++
 src/manual/en_US/book.xml                       | 123 ++++++++++++++++---
 .../freemarker/core/MiscErrorMessagesTest.java  |  11 ++
 .../freemarker/core/SequenceBuiltInTest.java    |  82 +++++++++++++
 .../core/StringLiteralInterpolationTest.java    |   2 +-
 .../freemarker/template/ConfigurationTest.java  |  24 ++++
 12 files changed, 322 insertions(+), 49 deletions(-)
----------------------------------------------------------------------



[3/4] incubator-freemarker git commit: Bug fixed (part of FREEMARKER-48): When an arithmetic exception has occurred in an expression (typically division by zero), the template processing has thrown the ArithmeticException as is, without packaging it into

Posted by dd...@apache.org.
Bug fixed (part of FREEMARKER-48): When an arithmetic exception has occurred in an expression (typically division by zero), the template processing has thrown the ArithmeticException as is, without packaging it into a TemplateException. Thus, the error location in the template wasn't visible in the exception.


Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/b246de24
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/b246de24
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/b246de24

Branch: refs/heads/2.3
Commit: b246de2403bac5b82c44a83cc3b7ab5b6617d0e8
Parents: ee1e922
Author: ddekany <dd...@apache.org>
Authored: Mon Sep 18 22:49:56 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Mon Sep 18 22:49:56 2017 +0200

----------------------------------------------------------------------
 .../freemarker/core/ArithmeticExpression.java   | 38 +++++++++++---------
 src/manual/en_US/book.xml                       | 11 ++++++
 .../freemarker/core/MiscErrorMessagesTest.java  | 11 ++++++
 .../core/StringLiteralInterpolationTest.java    |  2 +-
 4 files changed, 45 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b246de24/src/main/java/freemarker/core/ArithmeticExpression.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/ArithmeticExpression.java b/src/main/java/freemarker/core/ArithmeticExpression.java
index 4fbe778..2ed7890 100644
--- a/src/main/java/freemarker/core/ArithmeticExpression.java
+++ b/src/main/java/freemarker/core/ArithmeticExpression.java
@@ -54,22 +54,28 @@ final class ArithmeticExpression extends Expression {
     static TemplateModel _eval(Environment env, TemplateObject parent, Number lhoNumber, int operator, Number rhoNumber)
             throws TemplateException, _MiscTemplateException {
         ArithmeticEngine ae = EvalUtil.getArithmeticEngine(env, parent); 
-        switch (operator) {
-            case TYPE_SUBSTRACTION : 
-                return new SimpleNumber(ae.subtract(lhoNumber, rhoNumber));
-            case TYPE_MULTIPLICATION :
-                return new SimpleNumber(ae.multiply(lhoNumber, rhoNumber));
-            case TYPE_DIVISION :
-                return new SimpleNumber(ae.divide(lhoNumber, rhoNumber));
-            case TYPE_MODULO :
-                return new SimpleNumber(ae.modulus(lhoNumber, rhoNumber));
-            default:
-                if (parent instanceof Expression) {
-                    throw new _MiscTemplateException((Expression) parent,
-                            "Unknown operation: ", Integer.valueOf(operator));
-                } else {
-                    throw new _MiscTemplateException("Unknown operation: ", Integer.valueOf(operator));
-                }
+        try {
+            switch (operator) {
+                case TYPE_SUBSTRACTION : 
+                    return new SimpleNumber(ae.subtract(lhoNumber, rhoNumber));
+                case TYPE_MULTIPLICATION :
+                    return new SimpleNumber(ae.multiply(lhoNumber, rhoNumber));
+                case TYPE_DIVISION :
+                    return new SimpleNumber(ae.divide(lhoNumber, rhoNumber));
+                case TYPE_MODULO :
+                    return new SimpleNumber(ae.modulus(lhoNumber, rhoNumber));
+                default:
+                    if (parent instanceof Expression) {
+                        throw new _MiscTemplateException((Expression) parent,
+                                "Unknown operation: ", Integer.valueOf(operator));
+                    } else {
+                        throw new _MiscTemplateException("Unknown operation: ", Integer.valueOf(operator));
+                    }
+            }
+        } catch (ArithmeticException e) {
+            throw new _MiscTemplateException(e, env,
+                    "Arithmetic operation failed",
+                    (e.getMessage() != null ? new String[] { ": ", e.getMessage() } : " (see cause exception)"));
         }
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b246de24/src/manual/en_US/book.xml
----------------------------------------------------------------------
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index 210f674..edb158b 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -27179,6 +27179,17 @@ TemplateModel x = env.getVariable("x");  // get variable x</programlisting>
             </listitem>
 
             <listitem>
+              <para>Bug fixed (part of <link
+              xlink:href="https://issues.apache.org/jira/browse/FREEMARKER-48">FREEMARKER-48</link>):
+              When an arithmetic exception has occurred in an expression
+              (typically division by zero), the template processing has thrown
+              the <literal>ArithmeticException</literal> as is, without
+              packaging it into a <literal>TemplateException</literal>. Thus,
+              the error location in the template wasn't visible in the
+              exception.</para>
+            </listitem>
+
+            <listitem>
               <para>When logging error due to an error in an <link
               linkend="ref.directive.attempt"><literal>attempt</literal>
               directive</link> block, the log message now indicates that the

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b246de24/src/test/java/freemarker/core/MiscErrorMessagesTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/MiscErrorMessagesTest.java b/src/test/java/freemarker/core/MiscErrorMessagesTest.java
index 79ccb2c..5799c12 100644
--- a/src/test/java/freemarker/core/MiscErrorMessagesTest.java
+++ b/src/test/java/freemarker/core/MiscErrorMessagesTest.java
@@ -19,9 +19,13 @@
 
 package freemarker.core;
 
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
 import org.junit.Test;
 
 import freemarker.cache.TemplateNameFormat;
+import freemarker.template.TemplateException;
 import freemarker.test.TemplateTest;
 
 public class MiscErrorMessagesTest extends TemplateTest {
@@ -45,4 +49,11 @@ public class MiscErrorMessagesTest extends TemplateTest {
         assertErrorContains("${{}[10]}", "[]", "?api");
     }
     
+    @Test
+    public void aritheticException() {
+        Throwable e = assertErrorContains("<#assign x = 0>\n${1 / x}", "Arithmetic");
+        assertThat(e, instanceOf(TemplateException.class));
+        assertEquals((Integer) 2, ((TemplateException) e).getLineNumber());
+    }
+    
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b246de24/src/test/java/freemarker/core/StringLiteralInterpolationTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/StringLiteralInterpolationTest.java b/src/test/java/freemarker/core/StringLiteralInterpolationTest.java
index b0d0581..f655865 100644
--- a/src/test/java/freemarker/core/StringLiteralInterpolationTest.java
+++ b/src/test/java/freemarker/core/StringLiteralInterpolationTest.java
@@ -80,7 +80,7 @@ public class StringLiteralInterpolationTest extends TemplateTest {
     public void testErrors() {
         addToDataModel("x", 1);
         assertErrorContains("${'${noSuchVar}'}", InvalidReferenceException.class, "missing", "noSuchVar");
-        assertErrorContains("${'${x/0}'}", ArithmeticException.class, "zero");
+        assertErrorContains("${'${x/0}'}", "zero");
     }
 
     @Test


[2/4] incubator-freemarker git commit: Bug fixed: When setting the new_builtin_resolver from Properties or the setSetting(String, String) API, it didn't recognize the camel case form of the allowed_classes and trusted_templates keywords, and throw except

Posted by dd...@apache.org.
Bug fixed: When setting the new_builtin_resolver from Properties or the setSetting(String, String) API, it didn't recognize the camel case form of the allowed_classes and trusted_templates keywords, and throw exception for them. Now allowedClasses and trustedTemplates can be used as well.


Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/ee1e9221
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/ee1e9221
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/ee1e9221

Branch: refs/heads/2.3
Commit: ee1e9221a8c6ed41a07cb0df80c2cbad608ea5b6
Parents: 2d0b493
Author: ddekany <dd...@apache.org>
Authored: Mon Sep 18 13:15:33 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Mon Sep 18 13:15:33 2017 +0200

----------------------------------------------------------------------
 src/main/java/freemarker/core/Configurable.java | 26 +++++++++++-------
 src/manual/en_US/book.xml                       | 28 ++++++++++++++------
 .../freemarker/template/ConfigurationTest.java  | 24 +++++++++++++++++
 3 files changed, 61 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ee1e9221/src/main/java/freemarker/core/Configurable.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/Configurable.java b/src/main/java/freemarker/core/Configurable.java
index 492282a..0ae1be4 100644
--- a/src/main/java/freemarker/core/Configurable.java
+++ b/src/main/java/freemarker/core/Configurable.java
@@ -2033,8 +2033,10 @@ public class Configurable {
         }
     }
     
-    private static final String ALLOWED_CLASSES = "allowed_classes";
-    private static final String TRUSTED_TEMPLATES = "trusted_templates";
+    private static final String ALLOWED_CLASSES_SNAKE_CASE = "allowed_classes";
+    private static final String TRUSTED_TEMPLATES_SNAKE_CASE = "trusted_templates";
+    private static final String ALLOWED_CLASSES_CAMEL_CASE = "allowedClasses";
+    private static final String TRUSTED_TEMPLATES_CAMEL_CASE = "trustedTemplates";
     
     /**
      * Sets a FreeMarker setting by a name and string value. If you can configure FreeMarker directly with Java (or
@@ -2179,13 +2181,13 @@ public class Configurable {
      *             Use {@link TemplateClassResolver#UNRESTRICTED_RESOLVER}
      *         <li><p>{@code "safer"}:
      *             Use {@link TemplateClassResolver#SAFER_RESOLVER}
-     *         <li><p>{@code "allows_nothing"}:
+     *         <li><p>{@code "allows_nothing"} (or {@code "allowsNothing"}):
      *             Use {@link TemplateClassResolver#ALLOWS_NOTHING_RESOLVER}
      *         <li><p>Something that contains colon will use
      *             {@link OptInTemplateClassResolver} and is expected to
      *             store comma separated values (possibly quoted) segmented
-     *             with {@code "allowed_classes:"} and/or
-     *             {@code "trusted_templates:"}. Examples of valid values:
+     *             with {@code "allowed_classes:"} (or {@code "allowedClasses:"}) and/or
+     *             {@code "trusted_templates:"} (or {@code "trustedTemplates:"}). Examples of valid values:
      *             
      *             <table style="width: auto; border-collapse: collapse" border="1"
      *                  summary="trusted_template value examples">
@@ -2610,15 +2612,21 @@ public class Configurable {
                         KeyValuePair kv = (KeyValuePair) segments.get(i);
                         String segmentKey = (String) kv.getKey();
                         List segmentValue = (List) kv.getValue();
-                        if (segmentKey.equals(ALLOWED_CLASSES)) {
+                        if (segmentKey.equals(ALLOWED_CLASSES_SNAKE_CASE)
+                                || segmentKey.equals(ALLOWED_CLASSES_CAMEL_CASE)) {
                             allowedClasses = new HashSet(segmentValue); 
-                        } else if (segmentKey.equals(TRUSTED_TEMPLATES)) {
+                        } else if (segmentKey.equals(TRUSTED_TEMPLATES_SNAKE_CASE)
+                                || segmentKey.equals(TRUSTED_TEMPLATES_CAMEL_CASE)) {
                             trustedTemplates = segmentValue;
                         } else {
                             throw new ParseException(
                                     "Unrecognized list segment key: " + StringUtil.jQuote(segmentKey) +
-                                    ". Supported keys are: \"" + ALLOWED_CLASSES + "\", \"" +
-                                    TRUSTED_TEMPLATES + "\"", 0, 0);
+                                    ". Supported keys are: " +
+                                    "\"" + ALLOWED_CLASSES_SNAKE_CASE + "\", " +
+                                    "\"" + ALLOWED_CLASSES_CAMEL_CASE + "\", " +
+                                    "\"" + TRUSTED_TEMPLATES_SNAKE_CASE + "\", " +
+                                    "\"" + TRUSTED_TEMPLATES_CAMEL_CASE + "\". ",
+                                    0, 0);
                         }
                     }
                     setNewBuiltinClassResolver(

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ee1e9221/src/manual/en_US/book.xml
----------------------------------------------------------------------
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index 569cbac..210f674 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -27181,24 +27181,36 @@ TemplateModel x = env.getVariable("x");  // get variable x</programlisting>
             <listitem>
               <para>When logging error due to an error in an <link
               linkend="ref.directive.attempt"><literal>attempt</literal>
-              directive</link> block, the log message now indicates that error
-              was inside an <literal>attempt</literal> block.</para>
+              directive</link> block, the log message now indicates that the
+              error was inside an <literal>attempt</literal> block.</para>
             </listitem>
 
             <listitem>
               <para>Bug fixed (<link
               xlink:href="https://issues.apache.org/jira/browse/FREEMARKER-52">FREEMARKER-52</link>):
-              When setting the <literal>output_format</literal> with
-              <literal>Properties</literal>, the
-              <literal>XHTMLOutputFormat</literal> abbreviation wasn't
-              recognized (for example in a <literal>.properties</literal>
-              file, <literal>output_format=XHTMLOutputFormat</literal> didn't
-              work, only
+              When setting the <literal>output_format</literal> from
+              <literal>Properties</literal> or the <literal>setSetting(String,
+              String)</literal> API, the <literal>XHTMLOutputFormat</literal>
+              abbreviation wasn't recognized (for example in a
+              <literal>.properties</literal> file,
+              <literal>output_format=XHTMLOutputFormat</literal> didn't work,
+              only
               <literal>output_format=freemarker.core.XHTMLOutputFormat()</literal>
               did).</para>
             </listitem>
 
             <listitem>
+              <para>Bug fixed: When setting the
+              <literal>new_builtin_resolver</literal> from
+              <literal>Properties</literal> or the <literal>setSetting(String,
+              String)</literal> API, it didn't recognize the camel case form
+              of the <literal>allowed_classes</literal> and
+              <literal>trusted_templates</literal> keywords, and throw
+              exception for them. Now <literal>allowedClasses</literal> and
+              <literal>trustedTemplates</literal> can be used as well.</para>
+            </listitem>
+
+            <listitem>
               <para><literal>Constants.EMPTY_HASH</literal> and
               <literal>GeneralPurposeNothing</literal> (the value of
               <literal>missingVar!</literal>) now implements

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ee1e9221/src/test/java/freemarker/template/ConfigurationTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/template/ConfigurationTest.java b/src/test/java/freemarker/template/ConfigurationTest.java
index 226306f..69eaf3a 100644
--- a/src/test/java/freemarker/template/ConfigurationTest.java
+++ b/src/test/java/freemarker/template/ConfigurationTest.java
@@ -66,9 +66,11 @@ import freemarker.core.EpochMillisTemplateDateFormatFactory;
 import freemarker.core.HTMLOutputFormat;
 import freemarker.core.HexTemplateNumberFormatFactory;
 import freemarker.core.MarkupOutputFormat;
+import freemarker.core.OptInTemplateClassResolver;
 import freemarker.core.OutputFormat;
 import freemarker.core.ParseException;
 import freemarker.core.RTFOutputFormat;
+import freemarker.core.TemplateClassResolver;
 import freemarker.core.TemplateDateFormatFactory;
 import freemarker.core.TemplateNumberFormatFactory;
 import freemarker.core.UndefinedOutputFormat;
@@ -1694,6 +1696,28 @@ public class ConfigurationTest extends TestCase {
         assertTrue(cfg.isTimeZoneExplicitlySet());
     }
     
+    public void testNewBuiltinClassResolverSetting() throws TemplateException {
+        Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
+        assertSame(TemplateClassResolver.UNRESTRICTED_RESOLVER, cfg.getNewBuiltinClassResolver());
+        
+        cfg.setSetting(Configuration.NEW_BUILTIN_CLASS_RESOLVER_KEY_SNAKE_CASE,
+                "allowed_classes: com.example.C1, com.example.C2, trusted_templates: lib/*, safe.ftl");
+        assertThat(cfg.getNewBuiltinClassResolver(), instanceOf(OptInTemplateClassResolver.class));
+        
+        cfg.setSetting(Configuration.NEW_BUILTIN_CLASS_RESOLVER_KEY_SNAKE_CASE, "safer");
+        assertSame(TemplateClassResolver.SAFER_RESOLVER, cfg.getNewBuiltinClassResolver());
+        
+        cfg.setSetting(Configuration.NEW_BUILTIN_CLASS_RESOLVER_KEY_CAMEL_CASE,
+                "allowedClasses: com.example.C1, com.example.C2, trustedTemplates: lib/*, safe.ftl");
+        assertThat(cfg.getNewBuiltinClassResolver(), instanceOf(OptInTemplateClassResolver.class));
+        
+        cfg.setSetting(Configuration.NEW_BUILTIN_CLASS_RESOLVER_KEY_SNAKE_CASE, "allowsNothing");
+        assertSame(TemplateClassResolver.ALLOWS_NOTHING_RESOLVER, cfg.getNewBuiltinClassResolver());
+        
+        cfg.setSetting(Configuration.NEW_BUILTIN_CLASS_RESOLVER_KEY_SNAKE_CASE, "allows_nothing");
+        assertSame(TemplateClassResolver.ALLOWS_NOTHING_RESOLVER, cfg.getNewBuiltinClassResolver());
+    }
+    
     @Test
     public void testGetSettingNamesAreSorted() throws Exception {
         Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);