This is an automated email from the ASF dual-hosted git repository.

mridulpathak pushed a commit to branch release24.09
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git


The following commit(s) were added to refs/heads/release24.09 by this push:
     new e332d3d170 Fixed: HashMaps are not properly rendered in FTL with 
current FTL integration (OFBIZ-13164) (#1616)
e332d3d170 is described below

commit e332d3d1700aa739ea5768c5c2e1aa140931985f
Author: Mridul Pathak <[email protected]>
AuthorDate: Mon Aug 10 19:04:12 2026 +0530

    Fixed: HashMaps are not properly rendered in FTL with current FTL 
integration (OFBIZ-13164) (#1616)
    
    Backported from trunk (#1615). OfbizBeansWrapper substitutes a MapModel 
whose key set is the map's own, instead of FreeMarker's stock union of the 
map's keys and its bean property names, so ?keys, ?size, and <#list map as key, 
value> stop seeing accessors like getClass or entrySet mixed in with the real 
entries; HtmlWidget.ExtendedWrapper picks up the same fix for screen rendering. 
Cherry-picked cleanly onto release24.09 with no conflicts and no adaptation 
needed.
    
    Thanks: Aditi Patel (author of the trunk fix) and Carsten Schinzer 
(original reporter).
    
    Co-authored-by: toaditi <[email protected]>
---
 .../ofbiz/base/util/template/FreeMarkerWorker.java |  3 +-
 .../base/util/template/OfbizBeansWrapper.java      | 79 ++++++++++++++++++++++
 .../org/apache/ofbiz/widget/model/HtmlWidget.java  |  3 +-
 3 files changed, 82 insertions(+), 3 deletions(-)

diff --git 
a/framework/base/src/main/java/org/apache/ofbiz/base/util/template/FreeMarkerWorker.java
 
b/framework/base/src/main/java/org/apache/ofbiz/base/util/template/FreeMarkerWorker.java
index 61fa33b0a6..09deeea2ee 100644
--- 
a/framework/base/src/main/java/org/apache/ofbiz/base/util/template/FreeMarkerWorker.java
+++ 
b/framework/base/src/main/java/org/apache/ofbiz/base/util/template/FreeMarkerWorker.java
@@ -54,7 +54,6 @@ import freemarker.core.Environment;
 import freemarker.core.TemplateClassResolver;
 import freemarker.ext.beans.BeanModel;
 import freemarker.ext.beans.BeansWrapper;
-import freemarker.ext.beans.BeansWrapperBuilder;
 import freemarker.template.Configuration;
 import freemarker.template.SimpleHash;
 import freemarker.template.SimpleScalar;
@@ -80,7 +79,7 @@ public final class FreeMarkerWorker {
     // or maybe not for performance reasons... hmmm, leave to config file...
     private static final UtilCache<String, Template> CACHED_TEMPLATES =
             UtilCache.createUtilCache("template.ftl.general", 0, 0, false);
-    private static final BeansWrapper DEFAULT_OFBIZ_WRAPPER = new 
BeansWrapperBuilder(VERSION).build();
+    private static final BeansWrapper DEFAULT_OFBIZ_WRAPPER = new 
OfbizBeansWrapper(VERSION);
     private static final TemplateHashModel DEFAULT_STATIC_MODELS =
             getConfiguredStaticModel(getDefaultOfbizWrapper());
     private static final Configuration DEFAULT_OFBIZ_CONFIG = 
makeConfiguration(DEFAULT_OFBIZ_WRAPPER);
diff --git 
a/framework/base/src/main/java/org/apache/ofbiz/base/util/template/OfbizBeansWrapper.java
 
b/framework/base/src/main/java/org/apache/ofbiz/base/util/template/OfbizBeansWrapper.java
new file mode 100644
index 0000000000..a440721e74
--- /dev/null
+++ 
b/framework/base/src/main/java/org/apache/ofbiz/base/util/template/OfbizBeansWrapper.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * 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.ofbiz.base.util.template;
+
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import freemarker.ext.beans.BeansWrapper;
+import freemarker.ext.beans.MapModel;
+import freemarker.template.TemplateModel;
+import freemarker.template.TemplateModelException;
+import freemarker.template.Version;
+
+/**
+ * The {@link BeansWrapper} that exposes Java objects to OFBiz FreeMarker 
templates.
+ *
+ * <p>It behaves like {@code BeansWrapper} in every respect but one: for 
{@link Map} values it
+ * enumerates only the map's own keys. FreeMarker's stock {@link MapModel} 
reports the union of the
+ * map's keys and the bean property names of the map object, so {@code ?keys}, 
{@code ?values},
+ * {@code ?size} and {@code <#list aMap as key, value>} all see accessors such 
as {@code getClass} or
+ * {@code entrySet} mixed in with the real entries (OFBIZ-13164).
+ *
+ * <p>Only key enumeration changes. Member lookup still falls back to the bean 
model, so templates
+ * can keep calling methods on maps — including on {@code GenericValue}, which 
implements {@code Map}.
+ */
+public class OfbizBeansWrapper extends BeansWrapper {
+
+    public OfbizBeansWrapper(Version version) {
+        super(version);
+    }
+
+    @Override
+    public TemplateModel wrap(Object object) throws TemplateModelException {
+        // A TemplateModel is left to the superclass, which passes it through 
untouched even when it
+        // also happens to be a Map.
+        if (object instanceof Map && !(object instanceof TemplateModel)) {
+            return new MapEntryKeysModel((Map<?, ?>) object, this);
+        }
+        return super.wrap(object);
+    }
+
+    /**
+     * A {@link MapModel} that enumerates the map's own keys rather than the 
union of those keys and
+     * the bean property names of the map object.
+     */
+    private static final class MapEntryKeysModel extends MapModel {
+        private final Map<?, ?> map;
+
+        MapEntryKeysModel(Map<?, ?> map, BeansWrapper wrapper) {
+            super(map, wrapper);
+            this.map = map;
+        }
+
+        @Override
+        protected Set<Object> keySet() {
+            // A modifiable copy: MapModel's own implementation adds to the 
set it gets from BeanModel,
+            // while maps such as GenericEntity and MapContext return an 
unmodifiable key set. Copying
+            // also preserves the map's iteration order.
+            return new LinkedHashSet<>(map.keySet());
+        }
+    }
+}
diff --git 
a/framework/widget/src/main/java/org/apache/ofbiz/widget/model/HtmlWidget.java 
b/framework/widget/src/main/java/org/apache/ofbiz/widget/model/HtmlWidget.java
index a4965df51b..a413311d38 100644
--- 
a/framework/widget/src/main/java/org/apache/ofbiz/widget/model/HtmlWidget.java
+++ 
b/framework/widget/src/main/java/org/apache/ofbiz/widget/model/HtmlWidget.java
@@ -42,6 +42,7 @@ import org.apache.ofbiz.base.util.cache.UtilCache;
 import org.apache.ofbiz.base.util.collections.MapStack;
 import org.apache.ofbiz.base.util.string.FlexibleStringExpander;
 import org.apache.ofbiz.base.util.template.FreeMarkerWorker;
+import org.apache.ofbiz.base.util.template.OfbizBeansWrapper;
 import org.apache.ofbiz.widget.renderer.ScreenRenderer;
 import org.apache.ofbiz.widget.renderer.ScreenStringRenderer;
 import org.apache.ofbiz.widget.renderer.html.HtmlWidgetRenderer;
@@ -77,7 +78,7 @@ public class HtmlWidget extends ModelScreenWidget {
         
SPECIAL_CONFIG_SQUARE_INTERPOLATION.setInterpolationSyntax(Configuration.SQUARE_BRACKET_INTERPOLATION_SYNTAX);
     }
     // not sure if this is the best way to get FTL to use my fancy MapModel 
derivative, but should work at least...
-    public static class ExtendedWrapper extends BeansWrapper {
+    public static class ExtendedWrapper extends OfbizBeansWrapper {
         public ExtendedWrapper(Version version) {
             super(version);
         }

Reply via email to