Repository: incubator-freemarker Updated Branches: refs/heads/2.3-gae 1ecf10a28 -> d8d070a1c
Further tests for <#list xs as k ,v> and <#items as k ,v>. Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/d8d070a1 Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/d8d070a1 Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/d8d070a1 Branch: refs/heads/2.3-gae Commit: d8d070a1c24efc3c0573b7a51f3e45b8ec9112db Parents: 1ecf10a Author: ddekany <[email protected]> Authored: Mon May 30 23:53:32 2016 +0200 Committer: ddekany <[email protected]> Committed: Mon May 30 23:53:32 2016 +0200 ---------------------------------------------------------------------- .../java/freemarker/core/ListErrorsTest.java | 137 ++++++++++++++ .../freemarker/core/ListValidationsTest.java | 125 ------------- .../test/templatesuite/TemplateTestCase.java | 5 +- .../test/templatesuite/models/Listables.java | 104 +++++++++++ .../test/templatesuite/expected/listhash.txt | 183 +++++++++++++++++++ .../test/templatesuite/templates/listhash.ftl | 52 ++++++ .../freemarker/test/templatesuite/testcases.xml | 1 + 7 files changed, 480 insertions(+), 127 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d8d070a1/src/test/java/freemarker/core/ListErrorsTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/freemarker/core/ListErrorsTest.java b/src/test/java/freemarker/core/ListErrorsTest.java new file mode 100644 index 0000000..0c322c6 --- /dev/null +++ b/src/test/java/freemarker/core/ListErrorsTest.java @@ -0,0 +1,137 @@ +/* + * 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.io.IOException; + +import org.junit.Test; + +import com.google.common.collect.ImmutableMap; + +import freemarker.template.DefaultObjectWrapper; +import freemarker.template.TemplateException; +import freemarker.test.TemplateTest; +import freemarker.test.templatesuite.models.Listables; + +public class ListErrorsTest extends TemplateTest { + + @Test + public void testValid() throws IOException, TemplateException { + assertOutput("<#list 1..2 as x><#list 3..4>${x}:<#items as x>${x}</#items></#list>;</#list>", "1:34;2:34;"); + assertOutput("<#list [] as x>${x}<#else><#list 1..2 as x>${x}<#sep>, </#list></#list>", "1, 2"); + assertOutput("<#macro m>[<#nested 3>]</#macro>" + + "<#list 1..2 as x>" + + "${x}@${x?index}" + + "<@m ; x>" + + "${x}," + + "<#list 4..4 as x>${x}@${x?index}</#list>" + + "</@>" + + "${x}@${x?index}; " + + "</#list>", + "1@0[3,4@0]1@0; 2@1[3,4@0]2@1; "); + } + + @Test + public void testInvalidItemsParseTime() throws IOException, TemplateException { + assertErrorContains("<#items as x>${x}</#items>", + "#items", "must be inside", "#list"); + assertErrorContains("<#list xs><#macro m><#items as x></#items></#macro></#list>", + "#items", "must be inside", "#list"); + assertErrorContains("<#list xs><#forEach x in xs><#items as x></#items></#forEach></#list>", + "#forEach", "doesn't support", "#items"); + assertErrorContains("<#list xs as x><#items as x>${x}</#items></#list>", + "#list", "must not have", "#items", "as loopVar"); + assertErrorContains("<#list xs><#list xs as x><#items as x>${x}</#items></#list></#list>", + "#list", "must not have", "#items", "as loopVar"); + assertErrorContains("<#list xs></#list>", + "#list", "must have", "#items", "as loopVar"); + assertErrorContains("<#forEach x in xs><#items as x></#items></#forEach>", + "#forEach", "doesn't support", "#items"); + assertErrorContains("<#list xs><#forEach x in xs><#items as x></#items></#forEach></#list>", + "#forEach", "doesn't support", "#items"); + } + + @Test + public void testInvalidSepParseTime() throws IOException, TemplateException { + assertErrorContains("<#sep>, </#sep>", + "#sep", "must be inside", "#list", "#foreach"); + assertErrorContains("<#sep>, ", + "#sep", "must be inside", "#list", "#foreach"); + assertErrorContains("<#list xs as x><#else><#sep>, </#list>", + "#sep", "must be inside", "#list", "#foreach"); + assertErrorContains("<#list xs as x><#macro m><#sep>, </#macro></#list>", + "#sep", "must be inside", "#list", "#foreach"); + } + + @Test + public void testInvalidItemsRuntime() throws IOException, TemplateException { + assertErrorContains("<#list 1..1><#items as x></#items><#items as x></#items></#list>", + "#items", "already entered earlier"); + assertErrorContains("<#list 1..1><#items as x><#items as y>${x}/${y}</#items></#items></#list>", + "#items", "Can't nest #items into each other"); + } + + @Test + public void testInvalidLoopVarBuiltinLHO() { + assertErrorContains("<#list foos>${foo?index}</#list>", + "?index", "foo", "no loop variable"); + assertErrorContains("<#list foos as foo></#list>${foo?index}", + "?index", "foo" , "no loop variable"); + assertErrorContains("<#list foos as foo><#macro m>${foo?index}</#macro></#list>", + "?index", "foo" , "no loop variable"); + assertErrorContains("<#list foos as foo><#function f>${foo?index}</#function></#list>", + "?index", "foo" , "no loop variable"); + assertErrorContains("<#list xs as x>${foo?index}</#list>", + "?index", "foo" , "no loop variable"); + assertErrorContains("<#list foos as foo><@m; foo>${foo?index}</@></#list>", + "?index", "foo" , "user defined directive"); + assertErrorContains( + "<#list foos as foo><@m; foo><@m; foo>${foo?index}</@></@></#list>", + "?index", "foo" , "user defined directive"); + assertErrorContains( + "<#list foos as foo><@m; foo>" + + "<#list foos as foo><@m; foo>${foo?index}</@></#list>" + + "</@></#list>", + "?index", "foo" , "user defined directive"); + } + + @Test + public void testKeyValueSameName() { + assertErrorContains("<#list {} as foo, foo></#list>", + "key", "value", "both" , "foo"); + } + + @Test + public void testCollectionVersusHash() { + assertErrorContains("<#list {} as i></#list>", + "as k, v"); + assertErrorContains("<#list [] as k, v></#list>", + "only one loop variable"); + } + + @Test + public void testNonEx2NonStringKey() throws IOException, TemplateException { + addToDataModel("m", new Listables.NonEx2MapAdapter(ImmutableMap.of("k1", "v1", 2, "v2"), new DefaultObjectWrapper())); + assertOutput("<#list m?keys as k>${k};</#list>", "k1;2;"); + assertErrorContains("<#list m as k, v></#list>", + "string", "number", ".TemplateHashModelEx2"); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d8d070a1/src/test/java/freemarker/core/ListValidationsTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/freemarker/core/ListValidationsTest.java b/src/test/java/freemarker/core/ListValidationsTest.java deleted file mode 100644 index eed74ce..0000000 --- a/src/test/java/freemarker/core/ListValidationsTest.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * 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.io.IOException; - -import org.junit.Test; - -import freemarker.template.TemplateException; -import freemarker.test.TemplateTest; - -public class ListValidationsTest extends TemplateTest { - - @Test - public void testValid() throws IOException, TemplateException { - assertOutput("<#list 1..2 as x><#list 3..4>${x}:<#items as x>${x}</#items></#list>;</#list>", "1:34;2:34;"); - assertOutput("<#list [] as x>${x}<#else><#list 1..2 as x>${x}<#sep>, </#list></#list>", "1, 2"); - assertOutput("<#macro m>[<#nested 3>]</#macro>" - + "<#list 1..2 as x>" - + "${x}@${x?index}" - + "<@m ; x>" - + "${x}," - + "<#list 4..4 as x>${x}@${x?index}</#list>" - + "</@>" - + "${x}@${x?index}; " - + "</#list>", - "1@0[3,4@0]1@0; 2@1[3,4@0]2@1; "); - } - - @Test - public void testInvalidItemsParseTime() throws IOException, TemplateException { - assertErrorContains("<#items as x>${x}</#items>", - "#items", "must be inside", "#list"); - assertErrorContains("<#list xs><#macro m><#items as x></#items></#macro></#list>", - "#items", "must be inside", "#list"); - assertErrorContains("<#list xs><#forEach x in xs><#items as x></#items></#forEach></#list>", - "#forEach", "doesn't support", "#items"); - assertErrorContains("<#list xs as x><#items as x>${x}</#items></#list>", - "#list", "must not have", "#items", "as loopVar"); - assertErrorContains("<#list xs><#list xs as x><#items as x>${x}</#items></#list></#list>", - "#list", "must not have", "#items", "as loopVar"); - assertErrorContains("<#list xs></#list>", - "#list", "must have", "#items", "as loopVar"); - assertErrorContains("<#forEach x in xs><#items as x></#items></#forEach>", - "#forEach", "doesn't support", "#items"); - assertErrorContains("<#list xs><#forEach x in xs><#items as x></#items></#forEach></#list>", - "#forEach", "doesn't support", "#items"); - } - - @Test - public void testInvalidSepParseTime() throws IOException, TemplateException { - assertErrorContains("<#sep>, </#sep>", - "#sep", "must be inside", "#list", "#foreach"); - assertErrorContains("<#sep>, ", - "#sep", "must be inside", "#list", "#foreach"); - assertErrorContains("<#list xs as x><#else><#sep>, </#list>", - "#sep", "must be inside", "#list", "#foreach"); - assertErrorContains("<#list xs as x><#macro m><#sep>, </#macro></#list>", - "#sep", "must be inside", "#list", "#foreach"); - } - - @Test - public void testInvalidItemsRuntime() throws IOException, TemplateException { - assertErrorContains("<#list 1..1><#items as x></#items><#items as x></#items></#list>", - "#items", "already entered earlier"); - assertErrorContains("<#list 1..1><#items as x><#items as y>${x}/${y}</#items></#items></#list>", - "#items", "Can't nest #items into each other"); - } - - @Test - public void testInvalidLoopVarBuiltinLHO() { - assertErrorContains("<#list foos>${foo?index}</#list>", - "?index", "foo", "no loop variable"); - assertErrorContains("<#list foos as foo></#list>${foo?index}", - "?index", "foo" , "no loop variable"); - assertErrorContains("<#list foos as foo><#macro m>${foo?index}</#macro></#list>", - "?index", "foo" , "no loop variable"); - assertErrorContains("<#list foos as foo><#function f>${foo?index}</#function></#list>", - "?index", "foo" , "no loop variable"); - assertErrorContains("<#list xs as x>${foo?index}</#list>", - "?index", "foo" , "no loop variable"); - assertErrorContains("<#list foos as foo><@m; foo>${foo?index}</@></#list>", - "?index", "foo" , "user defined directive"); - assertErrorContains( - "<#list foos as foo><@m; foo><@m; foo>${foo?index}</@></@></#list>", - "?index", "foo" , "user defined directive"); - assertErrorContains( - "<#list foos as foo><@m; foo>" - + "<#list foos as foo><@m; foo>${foo?index}</@></#list>" - + "</@></#list>", - "?index", "foo" , "user defined directive"); - } - - @Test - public void testKeyValueSameName() { - assertErrorContains("<#list {} as foo, foo></#list>", - "key", "value", "both" , "foo"); - } - - @Test - public void testCollectionVersusHash() { - assertErrorContains("<#list {} as i></#list>", - "as k, v"); - assertErrorContains("<#list [] as k, v></#list>", - "only one loop variable"); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d8d070a1/src/test/java/freemarker/test/templatesuite/TemplateTestCase.java ---------------------------------------------------------------------- diff --git a/src/test/java/freemarker/test/templatesuite/TemplateTestCase.java b/src/test/java/freemarker/test/templatesuite/TemplateTestCase.java index 9ccf93f..9e9b4ff 100644 --- a/src/test/java/freemarker/test/templatesuite/TemplateTestCase.java +++ b/src/test/java/freemarker/test/templatesuite/TemplateTestCase.java @@ -277,8 +277,9 @@ public class TemplateTestCase extends FileTestCase { }); dataModel.put("sqlDate", new java.sql.Date(1273955885023L)); dataModel.put("sqlTime", new java.sql.Time(74285023L)); - } else if (templateName.equals("list.ftl") - || templateName.equals("list2.ftl") || templateName.equals("list3.ftl")) { + } else if ( + templateName.equals("list.ftl") || templateName.equals("list2.ftl") || templateName.equals("list3.ftl") + || simpleTestName.equals("listhash")) { dataModel.put("listables", new Listables()); } else if (simpleTestName.startsWith("number-format")) { dataModel.put("int", new SimpleNumber(Integer.valueOf(1))); http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d8d070a1/src/test/java/freemarker/test/templatesuite/models/Listables.java ---------------------------------------------------------------------- diff --git a/src/test/java/freemarker/test/templatesuite/models/Listables.java b/src/test/java/freemarker/test/templatesuite/models/Listables.java index 42d43dc..d607a7e 100644 --- a/src/test/java/freemarker/test/templatesuite/models/Listables.java +++ b/src/test/java/freemarker/test/templatesuite/models/Listables.java @@ -18,14 +18,44 @@ */ package freemarker.test.templatesuite.models; +import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.SortedMap; import java.util.TreeSet; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +import freemarker.core._DelayedJQuote; +import freemarker.core._TemplateModelException; +import freemarker.ext.beans.BeansWrapperBuilder; +import freemarker.ext.util.WrapperTemplateModel; +import freemarker.template.AdapterTemplateModel; +import freemarker.template.Configuration; +import freemarker.template.DefaultMapAdapter; +import freemarker.template.DefaultObjectWrapperBuilder; +import freemarker.template.MapKeyValuePairIterator; +import freemarker.template.ObjectWrapper; +import freemarker.template.SimpleCollection; +import freemarker.template.SimpleHash; +import freemarker.template.TemplateCollectionModel; +import freemarker.template.TemplateHashModelEx; +import freemarker.template.TemplateHashModelEx2; +import freemarker.template.TemplateModel; +import freemarker.template.TemplateModelException; +import freemarker.template.TemplateModelWithAPISupport; +import freemarker.template.WrappingTemplateModel; +import freemarker.template.TemplateHashModelEx2.KeyValuePairIterator; +import freemarker.template.utility.ObjectWrapperWithAPISupport; + @SuppressWarnings("boxing") public class Listables { @@ -94,4 +124,78 @@ public class Listables { return Collections.<Integer>emptySet().iterator(); } + public List<TemplateHashModelEx2> getHashEx2s() throws TemplateModelException { + Map<Object, Object> map; + map = new LinkedHashMap<Object, Object>(); + map.put("k1", "v1"); + map.put(2, "v2"); + map.put("k3", "v3"); + map.put(null, "v4"); + map.put(true, "v5"); + map.put(false, null); + + return getMapsWrappedAsEx2(map); + } + + public List<? extends TemplateHashModelEx> getEmptyHashes() throws TemplateModelException { + List<TemplateHashModelEx> emptyMaps = new ArrayList<TemplateHashModelEx>(); + emptyMaps.addAll(getMapsWrappedAsEx2(Collections.emptyMap())); + emptyMaps.add((TemplateHashModelEx) new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_24).build() + .wrap(Collections.emptyMap())); + return emptyMaps; + } + + /** + * Returns the map wrapped on various ways. + */ + private List<TemplateHashModelEx2> getMapsWrappedAsEx2(Map<?, ?> map) throws TemplateModelException { + List<TemplateHashModelEx2> maps = new ArrayList<TemplateHashModelEx2>(); + + maps.add((SimpleHash) new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_0).build().wrap(map)); + + maps.add((DefaultMapAdapter) new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_24).build().wrap(map)); + + BeansWrapperBuilder bwb = new BeansWrapperBuilder(Configuration.VERSION_2_3_24); + bwb.setSimpleMapWrapper(true); + maps.add((TemplateHashModelEx2) bwb.build().wrap(map)); + + return maps; + } + + public TemplateHashModelEx getHashNonEx2() { + return new NonEx2MapAdapter(ImmutableMap.of("k1", 11, "k2", 22), + new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_24).build()); + } + + public static class NonEx2MapAdapter extends WrappingTemplateModel implements TemplateHashModelEx { + + private final Map<?, ?> map; + + public NonEx2MapAdapter(Map<?, ?> map, ObjectWrapper wrapper) { + super(wrapper); + this.map = map; + } + + public TemplateModel get(String key) throws TemplateModelException { + return wrap(map.get(key)); + } + + public boolean isEmpty() { + return map.isEmpty(); + } + + public int size() { + return map.size(); + } + + public TemplateCollectionModel keys() { + return new SimpleCollection(map.keySet(), getObjectWrapper()); + } + + public TemplateCollectionModel values() { + return new SimpleCollection(map.values(), getObjectWrapper()); + } + + } + } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d8d070a1/src/test/resources/freemarker/test/templatesuite/expected/listhash.txt ---------------------------------------------------------------------- diff --git a/src/test/resources/freemarker/test/templatesuite/expected/listhash.txt b/src/test/resources/freemarker/test/templatesuite/expected/listhash.txt new file mode 100644 index 0000000..c603890 --- /dev/null +++ b/src/test/resources/freemarker/test/templatesuite/expected/listhash.txt @@ -0,0 +1,183 @@ + +Non-empty maps: + + Map: + + [ + k1 = v1 + 2 = v2 + k3 = v3 + null = v4 + Y = v5 + N = null + ] + + [ + k1 = v1; // @0=@0; odd=odd; Y=Y + 2 = v2; // @1=@1; even=even; Y=Y + k3 = v3; // @2=@2; odd=odd; Y=Y + null = v4; // @3=@3; even=even; Y=Y + Y = v5; // @4=@4; odd=odd; Y=Y + N = null // @5=@5; even=even; N=N + ] + + { + [ + k1 = v1; // @0=@0; odd=odd; Y=Y + 2 = v2; // @1=@1; even=even; Y=Y + k3 = v3; // @2=@2; odd=odd; Y=Y + null = v4; // @3=@3; even=even; Y=Y + Y = v5; // @4=@4; odd=odd; Y=Y + N = null // @5=@5; even=even; N=N + ] + } + + Map: + + [ + k1 = v1 + 2 = v2 + k3 = v3 + null = v4 + Y = v5 + N = null + ] + + [ + k1 = v1; // @0=@0; odd=odd; Y=Y + 2 = v2; // @1=@1; even=even; Y=Y + k3 = v3; // @2=@2; odd=odd; Y=Y + null = v4; // @3=@3; even=even; Y=Y + Y = v5; // @4=@4; odd=odd; Y=Y + N = null // @5=@5; even=even; N=N + ] + + { + [ + k1 = v1; // @0=@0; odd=odd; Y=Y + 2 = v2; // @1=@1; even=even; Y=Y + k3 = v3; // @2=@2; odd=odd; Y=Y + null = v4; // @3=@3; even=even; Y=Y + Y = v5; // @4=@4; odd=odd; Y=Y + N = null // @5=@5; even=even; N=N + ] + } + + Map: + + [ + k1 = v1 + 2 = v2 + k3 = v3 + null = v4 + Y = v5 + N = null + ] + + [ + k1 = v1; // @0=@0; odd=odd; Y=Y + 2 = v2; // @1=@1; even=even; Y=Y + k3 = v3; // @2=@2; odd=odd; Y=Y + null = v4; // @3=@3; even=even; Y=Y + Y = v5; // @4=@4; odd=odd; Y=Y + N = null // @5=@5; even=even; N=N + ] + + { + [ + k1 = v1; // @0=@0; odd=odd; Y=Y + 2 = v2; // @1=@1; even=even; Y=Y + k3 = v3; // @2=@2; odd=odd; Y=Y + null = v4; // @3=@3; even=even; Y=Y + Y = v5; // @4=@4; odd=odd; Y=Y + N = null // @5=@5; even=even; N=N + ] + } + + Map: + + [ + k1 = 11 + k2 = 22 + ] + + [ + k1 = 11; // @0=@0; odd=odd; Y=Y + k2 = 22 // @1=@1; even=even; N=N + ] + + { + [ + k1 = 11; // @0=@0; odd=odd; Y=Y + k2 = 22 // @1=@1; even=even; N=N + ] + } + + +Empty maps: + + Map: + + [ + ] + + [ + Empty + ] + + { + Empty + } + + Map: + + [ + ] + + [ + Empty + ] + + { + Empty + } + + Map: + + [ + ] + + [ + Empty + ] + + { + Empty + } + + Map: + + [ + ] + + [ + Empty + ] + + { + Empty + } + + + a @ 0, 1 + aa = 11 @ 0 // inside a @ 0, 1 + a @ 0, 1 + -- + b @ 1, 2 + ba = 21 @ 0 // inside b @ 1, 2 + bb = 22 @ 1 // inside b @ 1, 2 + b @ 1, 2 + -- + c @ 2, 0 + c @ 2, 0 + -- http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d8d070a1/src/test/resources/freemarker/test/templatesuite/templates/listhash.ftl ---------------------------------------------------------------------- diff --git a/src/test/resources/freemarker/test/templatesuite/templates/listhash.ftl b/src/test/resources/freemarker/test/templatesuite/templates/listhash.ftl new file mode 100644 index 0000000..3668f89 --- /dev/null +++ b/src/test/resources/freemarker/test/templatesuite/templates/listhash.ftl @@ -0,0 +1,52 @@ +<#setting booleanFormat='Y,N'> + +<#macro listings maps> + <#list maps as m> + Map: + + [ + <#list m as k, v> + ${k!'null'} = ${v!'null'} + </#list> + ] + + [ + <#list m as k, v> + ${k!'null'} = ${v!'null'}<#sep>;</#sep> // @${k?index}=@${v?index}; ${k?itemParity}=${v?itemParity}; ${k?hasNext}=${v?hasNext} + <#else> + Empty + </#list> + ] + + { + <#list m> + [ + <#items as k, v> + ${k!'null'} = ${v!'null'}<#sep>;</#sep> // @${k?index}=@${v?index}; ${k?itemParity}=${v?itemParity}; ${k?hasNext}=${v?hasNext} + </#items> + ] + <#else> + Empty + </#list> + } + + </#list> +</#macro> + +Non-empty maps: + +<@listings listables.hashEx2s /> +<@listings [ listables.hashNonEx2 ] /> + +Empty maps: + +<@listings listables.emptyHashes /> + +<#list { 'a': { 'aa': 11 }, 'b': { 'ba': 21, 'bb': 22 }, 'c': {} } as k1, v1> + ${k1} @ ${k1?index}, ${v1?size} + <#list v1 as k2, v2> + ${k2} = ${v2} @ ${k2?index} // inside ${k1} @ ${k1?index}, ${v1?size} + </#list> + ${k1} @ ${k1?index}, ${v1?size} + -- +</#list> http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d8d070a1/src/test/resources/freemarker/test/templatesuite/testcases.xml ---------------------------------------------------------------------- diff --git a/src/test/resources/freemarker/test/templatesuite/testcases.xml b/src/test/resources/freemarker/test/templatesuite/testcases.xml index cc42796..8df64bd 100644 --- a/src/test/resources/freemarker/test/templatesuite/testcases.xml +++ b/src/test/resources/freemarker/test/templatesuite/testcases.xml @@ -133,6 +133,7 @@ <testCase name="list-bis[#endTN]-collectionAdapter" expected="list-bis.txt"> <setting object_wrapper="DefaultObjectWrapper(2.3.22, forceLegacyNonListCollections=false)" /> </testCase> + <testCase name="listhash" /> <testCase name="listliteral" /> <testCase name="localization" > <setting locale="en_AU"/>
