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);
}