Repository: incubator-freemarker Updated Branches: refs/heads/2.3-gae ee52c77f3 -> 0df2c1878
freemarker.template.utility.DeepUnwrap (a rarely used utility) now utilizes when the unwrapped TemplateModel implements TemplateHashModelEx2.getKeyValuePairIterator(), and thus can unwrap such a hash value even if it has non-string keys. Also, it nows keeps the iteration order of the hashes, as it unwraps into a LinkedHashMap instead of into a plain HashMap. Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/0df2c187 Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/0df2c187 Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/0df2c187 Branch: refs/heads/2.3-gae Commit: 0df2c18781d0d86696df834a7ac8960feda3c408 Parents: ee52c77 Author: ddekany <ddek...@apache.org> Authored: Thu Mar 1 18:03:27 2018 +0100 Committer: ddekany <ddek...@apache.org> Committed: Thu Mar 1 18:03:27 2018 +0100 ---------------------------------------------------------------------- .../freemarker/template/utility/DeepUnwrap.java | 26 ++++-- src/manual/en_US/book.xml | 11 +++ .../template/utility/DeepUnwrapTest.java | 92 ++++++++++++++++++++ 3 files changed, 123 insertions(+), 6 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/0df2c187/src/main/java/freemarker/template/utility/DeepUnwrap.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/template/utility/DeepUnwrap.java b/src/main/java/freemarker/template/utility/DeepUnwrap.java index 85b032c..aa893a2 100644 --- a/src/main/java/freemarker/template/utility/DeepUnwrap.java +++ b/src/main/java/freemarker/template/utility/DeepUnwrap.java @@ -20,7 +20,8 @@ package freemarker.template.utility; import java.util.ArrayList; -import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; import freemarker.core.Environment; import freemarker.ext.util.WrapperTemplateModel; @@ -30,6 +31,9 @@ import freemarker.template.TemplateBooleanModel; import freemarker.template.TemplateCollectionModel; import freemarker.template.TemplateDateModel; import freemarker.template.TemplateHashModelEx; +import freemarker.template.TemplateHashModelEx2; +import freemarker.template.TemplateHashModelEx2.KeyValuePair; +import freemarker.template.TemplateHashModelEx2.KeyValuePairIterator; import freemarker.template.TemplateModel; import freemarker.template.TemplateModelException; import freemarker.template.TemplateModelIterator; @@ -147,11 +151,21 @@ public class DeepUnwrap { } if (model instanceof TemplateHashModelEx) { TemplateHashModelEx hash = (TemplateHashModelEx) model; - HashMap map = new HashMap(); - TemplateModelIterator keys = hash.keys().iterator(); - while (keys.hasNext()) { - String key = (String) unwrap(keys.next(), nullModel, permissive); - map.put(key, unwrap(hash.get(key), nullModel, permissive)); + Map<Object, Object> map = new LinkedHashMap<Object, Object>(); + if (model instanceof TemplateHashModelEx2) { + KeyValuePairIterator kvps = ((TemplateHashModelEx2) model).keyValuePairIterator(); + while (kvps.hasNext()) { + KeyValuePair kvp = kvps.next(); + map.put( + unwrap(kvp.getKey(), nullModel, permissive), + unwrap(kvp.getValue(), nullModel, permissive)); + } + } else { + TemplateModelIterator keys = hash.keys().iterator(); + while (keys.hasNext()) { + String key = (String) unwrap(keys.next(), nullModel, permissive); + map.put(key, unwrap(hash.get(key), nullModel, permissive)); + } } return map; } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/0df2c187/src/manual/en_US/book.xml ---------------------------------------------------------------------- diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml index ce5f4da..572af9c 100644 --- a/src/manual/en_US/book.xml +++ b/src/manual/en_US/book.xml @@ -27318,6 +27318,17 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting> This simplifies Java code that iterates through key-value pairs.</para> </listitem> + + <listitem> + <para><literal>freemarker.template.utility.DeepUnwrap</literal> + (a rarely used utility) now utilizes when the unwrapped + <literal>TemplateModel</literal> implements + <literal>TemplateHashModelEx2.getKeyValuePairIterator()</literal>, + and thus can unwrap such a hash value even if it has non-string + keys. Also, it nows keeps the iteration order of the hashes, as + it unwraps into a <literal>LinkedHashMap</literal> instead of + into a plain <literal>HashMap</literal>.</para> + </listitem> </itemizedlist> </section> </section> http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/0df2c187/src/test/java/freemarker/template/utility/DeepUnwrapTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/freemarker/template/utility/DeepUnwrapTest.java b/src/test/java/freemarker/template/utility/DeepUnwrapTest.java new file mode 100644 index 0000000..6b43431 --- /dev/null +++ b/src/test/java/freemarker/template/utility/DeepUnwrapTest.java @@ -0,0 +1,92 @@ +/* + * 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.template.utility; + +import static org.junit.Assert.*; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.junit.Test; + +import freemarker.template.Configuration; +import freemarker.template.DefaultObjectWrapper; +import freemarker.template.DefaultObjectWrapperBuilder; +import freemarker.template.TemplateCollectionModel; +import freemarker.template.TemplateHashModelEx2; +import freemarker.template.TemplateModel; +import freemarker.template.TemplateModelException; + +public class DeepUnwrapTest { + + @SuppressWarnings("rawtypes") + @Test + public void testHashEx2Unwrapping() throws Exception { + Map<Object, Object> map = new LinkedHashMap<Object, Object>(); + map.put("k1", "v1"); + map.put("k2", null); + map.put(3, "v3"); + map.put(null, "v4"); + + DefaultObjectWrapper dow = new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_27).build(); + TemplateModel model = dow.wrap(map); + + assertSame(map, DeepUnwrap.unwrap(model)); + + Object unwrapped = DeepUnwrap.unwrap(new PurelyTemplateHashModelEx2((TemplateHashModelEx2) model)); + assertNotSame(map, unwrapped); + assertEquals(map, unwrapped); + // Order is kept: + assertArrayEquals(new Object[] { "k1", "k2", 3, null }, ((Map) unwrapped).keySet().toArray()); + } + + public static class PurelyTemplateHashModelEx2 implements TemplateHashModelEx2 { + private final TemplateHashModelEx2 delegate; + + public PurelyTemplateHashModelEx2(TemplateHashModelEx2 delegate) { + this.delegate = delegate; + } + + public TemplateModel get(String key) throws TemplateModelException { + return delegate.get(key); + } + + public int size() throws TemplateModelException { + return delegate.size(); + } + + public KeyValuePairIterator keyValuePairIterator() throws TemplateModelException { + return delegate.keyValuePairIterator(); + } + + public TemplateCollectionModel keys() throws TemplateModelException { + return delegate.keys(); + } + + public boolean isEmpty() throws TemplateModelException { + return delegate.isEmpty(); + } + + public TemplateCollectionModel values() throws TemplateModelException { + return delegate.values(); + } + } + +}