Forward ported from 2.3-gae: FREEMARKER-86: Added new built-ins: sequence?min 
and sequence?max (FREEMARKER-86), which return the smallest and greatest item 
from a list of numbers or date/time/date-times.


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

Branch: refs/heads/3
Commit: f22494599393a6ff49b5ccb34f88beb474cbf55f
Parents: 7f40cbe
Author: ddekany <ddek...@apache.org>
Authored: Mon Mar 12 07:04:00 2018 +0100
Committer: ddekany <ddek...@apache.org>
Committed: Mon Mar 12 07:04:00 2018 +0100

----------------------------------------------------------------------
 .../apache/freemarker/core/MinMaxBITest.java    | 89 ++++++++++++++++++++
 .../apache/freemarker/core/ASTExpBuiltIn.java   |  4 +-
 .../freemarker/core/BuiltInForIterable.java     |  4 +-
 .../freemarker/core/BuiltInsForSequences.java   | 52 ++++++++++--
 4 files changed, 141 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f2249459/freemarker-core-test/src/test/java/org/apache/freemarker/core/MinMaxBITest.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/MinMaxBITest.java
 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/MinMaxBITest.java
new file mode 100644
index 0000000..8a681d2
--- /dev/null
+++ 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/MinMaxBITest.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import java.sql.Time;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.freemarker.core.model.ObjectWrapperWithAPISupport;
+import org.apache.freemarker.core.model.impl.DefaultIterableAdapter;
+import org.apache.freemarker.core.util._DateUtils;
+import org.apache.freemarker.test.TemplateTest;
+import org.apache.freemarker.test.TestConfigurationBuilder;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableList;
+
+public class MinMaxBITest extends TemplateTest {
+    
+    @Override
+    protected Configuration createDefaultConfiguration() throws Exception {
+        return new 
TestConfigurationBuilder().sqlDateAndTimeTimeZone(_DateUtils.UTC).timeFormat("HH:mm:ss").build();
+    }
+
+    @Test
+    public void basicsTest() throws Exception {
+        ObjectWrapperWithAPISupport ow = (ObjectWrapperWithAPISupport) 
getConfiguration().getObjectWrapper();
+        for (boolean exposeAsSeq : new boolean[] { true, false }) { // Expose 
xs as SequenceTM or as CollectionTM
+            for (InputMinMax testParams : ImmutableList.of(
+                    // Test parameters:             List (xs), Expected result 
for `?min`, For `?max`
+                    new InputMinMax(ImmutableList.of(1, 2, 3), "1", "3"),
+                    new InputMinMax(ImmutableList.of(3, 2, 1), "1", "3"),
+                    new InputMinMax(ImmutableList.of(1, 3, 2), "1", "3"),
+                    new InputMinMax(ImmutableList.of(2, 1, 3), "1", "3"),
+                    new InputMinMax(ImmutableList.of(2), "2", "2"),
+                    new InputMinMax(Collections.emptyList(), "-", "-"),
+                    new InputMinMax(ImmutableList.of(1.5, -0.5, 1L, 2.25), 
"-0.5", "2.25"),
+                    new InputMinMax(ImmutableList.of(Double.NEGATIVE_INFINITY, 
1, Double.POSITIVE_INFINITY),
+                            "-\u221E", "\u221E"), // \u221E = ∞
+                    new InputMinMax(Arrays.asList(new Object[] { null, 1, 
null, 2, null }), "1", "2"),
+                    new InputMinMax(Arrays.asList(new Object[] { null, null, 
null }), "-", "-"),
+                    new InputMinMax(ImmutableList.of(new Time(2000), new 
Time(3000), new Time(1000)),
+                            "00:00:01", "00:00:03")
+                    )) {
+                addToDataModel("xs",
+                        exposeAsSeq ? testParams.input : 
DefaultIterableAdapter.adapt(testParams.input, ow));
+                assertOutput("${xs?min!'-'}", testParams.minExpected);
+                assertOutput("${xs?max!'-'}", testParams.maxExpected);
+            }
+        }
+    }
+    
+    private class InputMinMax {
+        private final List<?> input;
+        private final String minExpected;
+        private final String maxExpected;
+        
+        public InputMinMax(List<?> input, String minExpected, String 
maxExpected) {
+            this.input = input;
+            this.minExpected = minExpected;
+            this.maxExpected = maxExpected;
+        }
+    }
+
+    @Test
+    public void comparisonErrorTest() {
+        assertErrorContains("${['a', 'x']?min}", "less-than", "string");
+        assertErrorContains("${[0, true]?min}", "number", "boolean");
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f2249459/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltIn.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltIn.java 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltIn.java
index 37163da..3ff5c0a 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltIn.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltIn.java
@@ -75,7 +75,7 @@ abstract class ASTExpBuiltIn extends ASTExpression implements 
Cloneable {
     protected ASTExpression target;
     protected String key;
 
-    static final int NUMBER_OF_BIS = 263;
+    static final int NUMBER_OF_BIS = 265;
     static final HashMap<String, ASTExpBuiltIn> BUILT_INS_BY_NAME = new 
HashMap(NUMBER_OF_BIS * 3 / 2 + 1, 1f);
 
     static {
@@ -229,6 +229,8 @@ abstract class ASTExpBuiltIn extends ASTExpression 
implements Cloneable {
         putBI("nodeNamespace", new node_namespaceBI());
         putBI("nodeType", new node_typeBI());
         putBI("noEsc", new no_escBI());
+        putBI("max", new BuiltInsForSequences.maxBI());
+        putBI("min", new BuiltInsForSequences.minBI());
         putBI("number", new BuiltInsForStringsMisc.numberBI());
         putBI("numberToDate", new number_to_dateBI(TemplateDateModel.DATE));
         putBI("numberToTime", new number_to_dateBI(TemplateDateModel.TIME));

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f2249459/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForIterable.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForIterable.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForIterable.java
index 15556f8..ac3a56a 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForIterable.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForIterable.java
@@ -35,9 +35,9 @@ abstract class BuiltInForIterable extends ASTExpBuiltIn {
                     TemplateIterableModel.class,
                     null, env);
         }
-        return calculateResult((TemplateIterableModel) model);
+        return calculateResult((TemplateIterableModel) model, env);
     }
 
-    abstract TemplateModel calculateResult(TemplateIterableModel model) throws 
TemplateException;
+    abstract TemplateModel calculateResult(TemplateIterableModel model, 
Environment env) throws TemplateException;
 
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f2249459/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java
index ad50975..5df62f1 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java
@@ -168,7 +168,7 @@ class BuiltInsForSequences {
     static class firstBI extends BuiltInForIterable {
 
         @Override
-        TemplateModel calculateResult(TemplateIterableModel model) throws 
TemplateException {
+        TemplateModel calculateResult(TemplateIterableModel model, Environment 
env) throws TemplateException {
             TemplateModelIterator iter = model.iterator();
             if (!iter.hasNext()) {
                 return null;
@@ -237,7 +237,7 @@ class BuiltInsForSequences {
         }
 
         @Override
-        TemplateModel calculateResult(TemplateIterableModel model) throws 
TemplateException {
+        TemplateModel calculateResult(TemplateIterableModel model, Environment 
env) throws TemplateException {
             if (model instanceof RightUnboundedRangeModel) {
                 throw new TemplateException(
                         "The sequence to join was right-unbounded numerical 
range, thus it's infinitely long.");
@@ -329,7 +329,7 @@ class BuiltInsForSequences {
         }
 
         @Override
-        TemplateModel calculateResult(TemplateIterableModel model) throws 
TemplateException  {
+        TemplateModel calculateResult(TemplateIterableModel model, Environment 
env) throws TemplateException  {
             return new BIMethod(model);
         }
     
@@ -473,7 +473,7 @@ class BuiltInsForSequences {
         }
 
         @Override
-        TemplateModel calculateResult(TemplateIterableModel model) throws 
TemplateException {
+        TemplateModel calculateResult(TemplateIterableModel model, Environment 
env) throws TemplateException {
             return new BIMethod(model);
         }
     }
@@ -819,7 +819,7 @@ class BuiltInsForSequences {
     static class sequenceBI extends BuiltInForIterable {
 
         @Override
-        TemplateModel calculateResult(TemplateIterableModel model) throws 
TemplateException {
+        TemplateModel calculateResult(TemplateIterableModel model, Environment 
env) throws TemplateException {
             if (model instanceof TemplateSequenceModel) {
                 return model;
             }
@@ -835,6 +835,48 @@ class BuiltInsForSequences {
 
     }
 
+    private static abstract class MinOrMaxBI extends BuiltInForIterable {
+        
+        private final int comparatorOperator;
+        
+        protected MinOrMaxBI(int comparatorOperator) {
+            this.comparatorOperator = comparatorOperator;
+        }
+
+        @Override
+        TemplateModel calculateResult(TemplateIterableModel model, Environment 
env) throws TemplateException {
+            // TODO Auto-generated method stub
+            TemplateModel best = null;
+            TemplateModelIterator iter = model.iterator();
+            while (iter.hasNext()) {
+                TemplateModel cur = iter.next();
+                if (cur != null
+                        && (best == null || _EvalUtils.compare(cur, null, 
comparatorOperator, null, best,
+                                    null, this, true, false, false, false, 
env))) {
+                    best = cur;
+                }
+            }
+            return best;
+        }
+        
+    }    
+
+    static class maxBI extends MinOrMaxBI {
+
+        public maxBI() {
+            super(_EvalUtils.CMP_OP_GREATER_THAN);
+        }
+        
+    }
+
+    static class minBI extends MinOrMaxBI {
+
+        public minBI() {
+            super(_EvalUtils.CMP_OP_LESS_THAN);
+        }
+        
+    }
+    
     // Can't be instantiated
     private BuiltInsForSequences() { }
     

Reply via email to