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 2018/03/12 06:04:24 UTC
[1/2] incubator-freemarker git commit: (JavaDoc typo)
Repository: incubator-freemarker
Updated Branches:
refs/heads/2.3-gae b996adaa2 -> 0c77097aa
(JavaDoc typo)
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/15e8ac35
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/15e8ac35
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/15e8ac35
Branch: refs/heads/2.3-gae
Commit: 15e8ac35bbcb652c49c55b88d7c7496834f03f71
Parents: b996ada
Author: ddekany <dd...@apache.org>
Authored: Mon Mar 12 06:38:29 2018 +0100
Committer: ddekany <dd...@apache.org>
Committed: Mon Mar 12 06:38:29 2018 +0100
----------------------------------------------------------------------
src/main/java/freemarker/template/DefaultIterableAdapter.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/15e8ac35/src/main/java/freemarker/template/DefaultIterableAdapter.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/template/DefaultIterableAdapter.java b/src/main/java/freemarker/template/DefaultIterableAdapter.java
index 824e2d7..7438ff9 100644
--- a/src/main/java/freemarker/template/DefaultIterableAdapter.java
+++ b/src/main/java/freemarker/template/DefaultIterableAdapter.java
@@ -33,7 +33,7 @@ import freemarker.template.utility.ObjectWrapperWithAPISupport;
*
* <p>
* Thread safety: A {@link DefaultIterableAdapter} is as thread-safe as the {@link Iterable} that it wraps is. Normally
- * you only have to consider read-only access, as the FreeMarker template language doesn't provide mean to call
+ * you only have to consider read-only access, as the FreeMarker template language doesn't provide means to call
* {@link Iterator} modifier methods (though of course, Java methods called from the template can violate this rule).
*
* <p>
[2/2] incubator-freemarker git commit: 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.
Posted by dd...@apache.org.
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/0c77097a
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/0c77097a
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/0c77097a
Branch: refs/heads/2.3-gae
Commit: 0c77097aac210ba6b89e957ccaa6d4ca160dee21
Parents: 15e8ac3
Author: ddekany <dd...@apache.org>
Authored: Mon Mar 12 07:02:51 2018 +0100
Committer: ddekany <dd...@apache.org>
Committed: Mon Mar 12 07:02:51 2018 +0100
----------------------------------------------------------------------
src/main/java/freemarker/core/BuiltIn.java | 4 +-
.../freemarker/core/BuiltInsForSequences.java | 68 +++++++++++++++
src/manual/en_US/book.xml | 52 ++++++++++++
src/test/java/freemarker/core/MinMaxBITest.java | 87 ++++++++++++++++++++
4 files changed, 210 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/0c77097a/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 78b9e45..6a6ad37 100644
--- a/src/main/java/freemarker/core/BuiltIn.java
+++ b/src/main/java/freemarker/core/BuiltIn.java
@@ -84,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 = 266;
+ static final int NUMBER_OF_BIS = 268;
static final HashMap<String, BuiltIn> BUILT_INS_BY_NAME = new HashMap(NUMBER_OF_BIS * 3 / 2 + 1, 1f);
static {
@@ -245,6 +245,8 @@ abstract class BuiltIn extends Expression implements Cloneable {
putBI("node_namespace", "nodeNamespace", new node_namespaceBI());
putBI("node_type", "nodeType", new node_typeBI());
putBI("no_esc", "noEsc", new no_escBI());
+ putBI("max", new BuiltInsForSequences.maxBI());
+ putBI("min", new BuiltInsForSequences.minBI());
putBI("number", new BuiltInsForStringsMisc.numberBI());
putBI("number_to_date", "numberToDate", new number_to_dateBI(TemplateDateModel.DATE));
putBI("number_to_time", "numberToTime", new number_to_dateBI(TemplateDateModel.TIME));
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/0c77097a/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 a58c7f6..9bc8d95 100644
--- a/src/main/java/freemarker/core/BuiltInsForSequences.java
+++ b/src/main/java/freemarker/core/BuiltInsForSequences.java
@@ -884,6 +884,74 @@ class BuiltInsForSequences {
}
}
+ private static abstract class MinOrMaxBI extends BuiltIn {
+
+ private final int comparatorOperator;
+
+ protected MinOrMaxBI(int comparatorOperator) {
+ this.comparatorOperator = comparatorOperator;
+ }
+
+ @Override
+ TemplateModel _eval(Environment env)
+ throws TemplateException {
+ TemplateModel model = target.eval(env);
+ if (model instanceof TemplateCollectionModel) {
+ return calculateResultForColletion((TemplateCollectionModel) model, env);
+ } else if (model instanceof TemplateSequenceModel) {
+ return calculateResultForSequence((TemplateSequenceModel) model, env);
+ } else {
+ throw new NonSequenceOrCollectionException(target, model, env);
+ }
+ }
+
+ private TemplateModel calculateResultForColletion(TemplateCollectionModel coll, Environment env)
+ throws TemplateException {
+ TemplateModel best = null;
+ TemplateModelIterator iter = coll.iterator();
+ while (iter.hasNext()) {
+ TemplateModel cur = iter.next();
+ if (cur != null
+ && (best == null || EvalUtil.compare(cur, null, comparatorOperator, null, best,
+ null, this, true, false, false, false, env))) {
+ best = cur;
+ }
+ }
+ return best;
+ }
+
+ private TemplateModel calculateResultForSequence(TemplateSequenceModel seq, Environment env)
+ throws TemplateException {
+ TemplateModel best = null;
+ for (int i = 0; i < seq.size(); i++) {
+ TemplateModel cur = seq.get(i);
+ if (cur != null
+ && (best == null || EvalUtil.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(EvalUtil.CMP_OP_GREATER_THAN);
+ }
+
+ }
+
+ static class minBI extends MinOrMaxBI {
+
+ public minBI() {
+ super(EvalUtil.CMP_OP_LESS_THAN);
+ }
+
+ }
+
// Can't be instantiated
private BuiltInsForSequences() { }
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/0c77097a/src/manual/en_US/book.xml
----------------------------------------------------------------------
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index db1d1f3..4b89a2f 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -12740,6 +12740,14 @@ grant codeBase "file:/path/to/freemarker.jar"
</listitem>
<listitem>
+ <para><link linkend="ref_builtin_min_max">max</link></para>
+ </listitem>
+
+ <listitem>
+ <para><link linkend="ref_builtin_min_max">min</link></para>
+ </listitem>
+
+ <listitem>
<para><link
linkend="ref_builtin_namespace">namespace</link></para>
</listitem>
@@ -16664,6 +16672,41 @@ red, green, blue.
die with error if the sequence is empty.</para>
</section>
+ <section xml:id="ref_builtin_min_max">
+ <title>min, max</title>
+
+ <indexterm>
+ <primary>min built-in</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>max built-in</primary>
+ </indexterm>
+
+ <para>Returns the smaller (<literal>min</literal>) or greatest
+ (<literal>max</literal>) item of the sequence (or collection). The
+ items must be either all numbers, or all date/time values of the
+ same kind (date-only, time-only, date-time), or else a comparison
+ error will occur. These are the same restrictions as for the <link
+ linkend="dgui_template_exp_comparison"><literal><</literal> and
+ <literal>></literal> operators</link>.</para>
+
+ <para>Missing items (i.e., Java <literal>null</literal>-s) will be
+ silently ignored. If the sequence is empty or it only contains
+ missing (Java <literal>null</literal>) items, the result itself will
+ be missing.</para>
+
+ <para>Example:</para>
+
+ <programlisting role="template">${[1, 2, 3]?min}
+${[1, 2, 3]?max}
+${[]?min!'-'}</programlisting>
+
+ <programlisting role="output">1
+3
+-</programlisting>
+ </section>
+
<section xml:id="ref_builtin_reverse">
<title>reverse</title>
@@ -27375,6 +27418,15 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting>
</listitem>
<listitem>
+ <para>Added new built-ins: <literal>sequence?min</literal> and
+ <literal>sequence?max</literal> (<link
+ xlink:href="https://issues.apache.org/jira/browse/FREEMARKER-86">FREEMARKER-86</link>),
+ which return the smallest and greatest item from a list of
+ numbers or date/time/date-times. <link
+ linkend="ref_builtin_min_max">See more here...</link></para>
+ </listitem>
+
+ <listitem>
<para>Bug fixed (<link
xlink:href="https://issues.apache.org/jira/browse/FREEMARKER-83">FREEMARKER-83</link>);
this fix is only active when <link
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/0c77097a/src/test/java/freemarker/core/MinMaxBITest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/MinMaxBITest.java b/src/test/java/freemarker/core/MinMaxBITest.java
new file mode 100644
index 0000000..3bff7ba
--- /dev/null
+++ b/src/test/java/freemarker/core/MinMaxBITest.java
@@ -0,0 +1,87 @@
+/*
+ * 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 freemarker.core;
+
+import java.sql.Time;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableList;
+
+import freemarker.template.DefaultIterableAdapter;
+import freemarker.template.utility.DateUtil;
+import freemarker.template.utility.ObjectWrapperWithAPISupport;
+import freemarker.test.TemplateTest;
+
+public class MinMaxBITest extends TemplateTest {
+
+ @Test
+ public void basicsTest() throws Exception {
+ getConfiguration().setTimeZone(DateUtil.UTC);
+ getConfiguration().setTimeFormat("HH:mm:ss");
+
+ 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");
+ }
+
+}