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

ddekany pushed a commit to branch 2.3-gae
in repository https://gitbox.apache.org/repos/asf/freemarker.git

commit 5afeb9a12b874cf51fbf7723797244e183d545c5
Author: ddekany <[email protected]>
AuthorDate: Wed May 8 17:30:42 2024 +0200

    Improved JavaDoc related to ZeroArgumentNonVoidMethodPolicy (mostly).
---
 .../java/freemarker/core/DotBeforeMethodCall.java  |  2 +
 .../java/freemarker/ext/beans/BeansWrapper.java    | 22 ++++----
 .../ext/beans/BeansWrapperConfiguration.java       |  9 ++++
 .../freemarker/ext/beans/ClassIntrospector.java    |  4 +-
 .../ext/beans/ClassIntrospectorBuilder.java        | 23 +++++++-
 .../freemarker/ext/beans/MemberAccessPolicy.java   |  7 ++-
 .../ext/beans/MethodAppearanceFineTuner.java       | 61 ++++++++++------------
 .../ext/beans/ZeroArgumentNonVoidMethodPolicy.java | 32 +++++++++---
 .../template/MethodCallAwareTemplateHashModel.java | 44 ++++++++++------
 9 files changed, 130 insertions(+), 74 deletions(-)

diff --git 
a/freemarker-core/src/main/java/freemarker/core/DotBeforeMethodCall.java 
b/freemarker-core/src/main/java/freemarker/core/DotBeforeMethodCall.java
index 17ae026c..f40ebbd1 100644
--- a/freemarker-core/src/main/java/freemarker/core/DotBeforeMethodCall.java
+++ b/freemarker-core/src/main/java/freemarker/core/DotBeforeMethodCall.java
@@ -33,6 +33,8 @@ import freemarker.template.TemplateModel;
  * {@link ZeroArgumentNonVoidMethodPolicy#BOTH_PROPERTY_AND_METHOD}
  * (via {@link 
BeansWrapper.MethodAppearanceDecision#setMethodInsteadOfPropertyValueBeforeCall(boolean)}).
 We don't
  * necessarily want to go beyond that hack, as we don't have separate method 
namespace in the template language.
+ *
+ * @since 2.3.33
  */
 class DotBeforeMethodCall extends Dot {
     public DotBeforeMethodCall(Dot dot) {
diff --git 
a/freemarker-core/src/main/java/freemarker/ext/beans/BeansWrapper.java 
b/freemarker-core/src/main/java/freemarker/ext/beans/BeansWrapper.java
index f7993f19..542ac9d3 100644
--- a/freemarker-core/src/main/java/freemarker/ext/beans/BeansWrapper.java
+++ b/freemarker-core/src/main/java/freemarker/ext/beans/BeansWrapper.java
@@ -107,13 +107,7 @@ public class BeansWrapper implements RichObjectWrapper, 
WriteProtectable {
     /**
      * At this level of exposure, all methods and properties of the wrapped
      * objects are exposed to the template except methods that are deemed
-     * not safe. The not safe methods are java.lang.Object methods wait() and
-     * notify(), java.lang.Class methods getClassLoader() and newInstance(),
-     * java.lang.reflect.Method and java.lang.reflect.Constructor invoke() and
-     * newInstance() methods, all java.lang.reflect.Field set methods, all 
-     * java.lang.Thread and java.lang.ThreadGroup methods that can change its 
-     * state, as well as the usual suspects in java.lang.System and
-     * java.lang.Runtime.
+     * not safe by the {@link MemberAccessPolicy}.
      *
      * <p>Note that the {@link MemberAccessPolicy} will further restrict 
what's visible. That mechanism was introduced
      * much later than "exposure levels", and it's the primary place to look 
at if you are concerned with safety.
@@ -121,19 +115,21 @@ public class BeansWrapper implements RichObjectWrapper, 
WriteProtectable {
     public static final int EXPOSE_SAFE = 1;
     
     /**
-     * At this level of exposure, only property getters are exposed.
-     * Additionally, property getters that map to unsafe methods are not
-     * exposed (i.e. Class.classLoader and Thread.contextClassLoader).
+     * At this level of exposure, only Java Bean properties are exposed. For 
example, if you have
+     * {@code public int getX()} in a public class, then you can access that 
in templates like {@code obj.x} (but
+     * not as {@code obj.getX()}).
      *
-     * <p>Note that the {@link MemberAccessPolicy} will further restrict 
what's visible.
+     * <p>Note that the {@link MemberAccessPolicy} will further restricts 
what's visible.
+     * Java Bean properties (like {@code obj.x} earlier) whose read method 
(like {@code getX()} earlier) is not
+     * accessible according the policy will not be visible.
      */
     public static final int EXPOSE_PROPERTIES_ONLY = 2;
 
     /**
-     * At this level of exposure, no bean properties and methods are exposed.
+     * At this level of exposure, no Java Bean properties, and no methods are 
exposed.
      * Only map items, resource bundle items, and objects retrieved through
      * the generic get method (on objects of classes that have a generic get
-     * method) can be retrieved through the hash interface. You might want to 
+     * method) can be retrieved through the {@link TemplateHashModel} 
interface. You might want to
      * call {@link #setMethodsShadowItems(boolean)} with {@code false} value to
      * speed up map item retrieval.
      */
diff --git 
a/freemarker-core/src/main/java/freemarker/ext/beans/BeansWrapperConfiguration.java
 
b/freemarker-core/src/main/java/freemarker/ext/beans/BeansWrapperConfiguration.java
index e69cf7a3..3d8337c4 100644
--- 
a/freemarker-core/src/main/java/freemarker/ext/beans/BeansWrapperConfiguration.java
+++ 
b/freemarker-core/src/main/java/freemarker/ext/beans/BeansWrapperConfiguration.java
@@ -252,6 +252,8 @@ public abstract class BeansWrapperConfiguration implements 
Cloneable {
     }
 
     /**
+     * Getter pair of {@link #setDefaultZeroArgumentNonVoidMethodPolicy}.
+     *
      * @since 2.3.33
      */
     public ZeroArgumentNonVoidMethodPolicy 
getDefaultZeroArgumentNonVoidMethodPolicy() {
@@ -270,6 +272,8 @@ public abstract class BeansWrapperConfiguration implements 
Cloneable {
     }
 
     /**
+     * Getter pair of {@link #setRecordZeroArgumentNonVoidMethodPolicy}.
+     *
      * @since 2.3.33
      */
     public ZeroArgumentNonVoidMethodPolicy 
getRecordZeroArgumentNonVoidMethodPolicy() {
@@ -287,6 +291,9 @@ public abstract class BeansWrapperConfiguration implements 
Cloneable {
         
classIntrospectorBuilder.setRecordZeroArgumentNonVoidMethodPolicy(recordZeroArgumentNonVoidMethodPolicy);
     }
 
+    /**
+     * Getter pair of {@link #setMethodAppearanceFineTuner}
+     */
     public MethodAppearanceFineTuner getMethodAppearanceFineTuner() {
         return classIntrospectorBuilder.getMethodAppearanceFineTuner();
     }
@@ -295,6 +302,8 @@ public abstract class BeansWrapperConfiguration implements 
Cloneable {
      * See {@link 
BeansWrapper#setMethodAppearanceFineTuner(MethodAppearanceFineTuner)}; 
additionally,
      * note that currently setting this to non-{@code null} will disable class 
introspection cache sharing, unless
      * the value implements {@link SingletonCustomizer}.
+     *
+     * <p>Note that methods in this class are inherited by {@link 
DefaultObjectWrapperBuilder}, which is what you normally use.
      */
     public void setMethodAppearanceFineTuner(MethodAppearanceFineTuner 
methodAppearanceFineTuner) {
         
classIntrospectorBuilder.setMethodAppearanceFineTuner(methodAppearanceFineTuner);
diff --git 
a/freemarker-core/src/main/java/freemarker/ext/beans/ClassIntrospector.java 
b/freemarker-core/src/main/java/freemarker/ext/beans/ClassIntrospector.java
index b223868e..45f76de7 100644
--- a/freemarker-core/src/main/java/freemarker/ext/beans/ClassIntrospector.java
+++ b/freemarker-core/src/main/java/freemarker/ext/beans/ClassIntrospector.java
@@ -203,7 +203,7 @@ class ClassIntrospector {
         if (recordAware && _JavaVersions.JAVA_16 == null) {
             throw new IllegalArgumentException(
                     "defaultZeroArgumentNonVoidMethodPolicy != 
recordZeroArgumentNonVoidMethodPolicy, " +
-                    "but Java 16 support is not available.");
+                    "but record support is not available (as Java 16 support 
is not available).");
         }
         this.incompatibleImprovements = builder.getIncompatibleImprovements();
 
@@ -316,7 +316,7 @@ class ClassIntrospector {
 
         if (introspData.size() > 1) {
             return introspData;
-        } else if (introspData.size() == 0) {
+        } else if (introspData.isEmpty()) {
             return Collections.emptyMap();
         } else { // map.size() == 1
             Entry<Object, Object> e = introspData.entrySet().iterator().next();
diff --git 
a/freemarker-core/src/main/java/freemarker/ext/beans/ClassIntrospectorBuilder.java
 
b/freemarker-core/src/main/java/freemarker/ext/beans/ClassIntrospectorBuilder.java
index ea75987d..a966f08d 100644
--- 
a/freemarker-core/src/main/java/freemarker/ext/beans/ClassIntrospectorBuilder.java
+++ 
b/freemarker-core/src/main/java/freemarker/ext/beans/ClassIntrospectorBuilder.java
@@ -77,7 +77,8 @@ final class ClassIntrospectorBuilder implements Cloneable {
         treatDefaultMethodsAsBeanMembers = incompatibleImprovements.intValue() 
>= _VersionInts.V_2_3_26;
         defaultZeroArgumentNonVoidMethodPolicy = 
ZeroArgumentNonVoidMethodPolicy.METHOD_ONLY;
         recordZeroArgumentNonVoidMethodPolicy = 
incompatibleImprovements.intValue() >= _VersionInts.V_2_3_33 && 
_JavaVersions.JAVA_16 != null
-                ? ZeroArgumentNonVoidMethodPolicy.BOTH_PROPERTY_AND_METHOD : 
ZeroArgumentNonVoidMethodPolicy.METHOD_ONLY;
+                ? ZeroArgumentNonVoidMethodPolicy.BOTH_PROPERTY_AND_METHOD
+                : defaultZeroArgumentNonVoidMethodPolicy;
         memberAccessPolicy = 
DefaultMemberAccessPolicy.getInstance(this.incompatibleImprovements);
     }
 
@@ -166,6 +167,8 @@ final class ClassIntrospectorBuilder implements Cloneable {
     }
 
     /**
+     * The getter pair of {@link 
#setDefaultZeroArgumentNonVoidMethodPolicy(ZeroArgumentNonVoidMethodPolicy)}.
+     *
      * @since 2.3.33
      */
     public ZeroArgumentNonVoidMethodPolicy 
getDefaultZeroArgumentNonVoidMethodPolicy() {
@@ -173,6 +176,10 @@ final class ClassIntrospectorBuilder implements Cloneable {
     }
 
     /**
+     * Sets the {@link ZeroArgumentNonVoidMethodPolicy} used for classes that 
are not records (or any other special
+     * cases we add support for later).
+     * The default value is {@link 
ZeroArgumentNonVoidMethodPolicy#METHOD_ONLY}.
+     *
      * @since 2.3.33
      */
     public void 
setDefaultZeroArgumentNonVoidMethodPolicy(ZeroArgumentNonVoidMethodPolicy 
defaultZeroArgumentNonVoidMethodPolicy) {
@@ -181,6 +188,8 @@ final class ClassIntrospectorBuilder implements Cloneable {
     }
 
     /**
+     * The getter pair of {@link 
#setRecordZeroArgumentNonVoidMethodPolicy(ZeroArgumentNonVoidMethodPolicy)}.
+     *
      * @since 2.3.33
      */
     public ZeroArgumentNonVoidMethodPolicy 
getRecordZeroArgumentNonVoidMethodPolicy() {
@@ -188,6 +197,13 @@ final class ClassIntrospectorBuilder implements Cloneable {
     }
 
     /**
+     * Sets the {@link ZeroArgumentNonVoidMethodPolicy} used for records.
+     * The default value is {@link 
ZeroArgumentNonVoidMethodPolicy#BOTH_PROPERTY_AND_METHOD} if
+     * {@link #getIncompatibleImprovements()} is at least 2.3.33, and we are 
on Java 16 or later, otherwise
+     * it's {@link ZeroArgumentNonVoidMethodPolicy#METHOD_ONLY}.
+     *
+     * @see 
#setDefaultZeroArgumentNonVoidMethodPolicy(ZeroArgumentNonVoidMethodPolicy)  
+     *
      * @since 2.3.33
      */
     public void 
setRecordZeroArgumentNonVoidMethodPolicy(ZeroArgumentNonVoidMethodPolicy 
recordZeroArgumentNonVoidMethodPolicy) {
@@ -195,6 +211,11 @@ final class ClassIntrospectorBuilder implements Cloneable {
         this.recordZeroArgumentNonVoidMethodPolicy = 
recordZeroArgumentNonVoidMethodPolicy;
     }
 
+    /**
+     * Get getter pair of {@link #setMemberAccessPolicy(MemberAccessPolicy)}
+     *
+     * @since 2.3.30
+     */
     public MemberAccessPolicy getMemberAccessPolicy() {
         return memberAccessPolicy;
     }
diff --git 
a/freemarker-core/src/main/java/freemarker/ext/beans/MemberAccessPolicy.java 
b/freemarker-core/src/main/java/freemarker/ext/beans/MemberAccessPolicy.java
index 6d7abdbf..e4e3a093 100644
--- a/freemarker-core/src/main/java/freemarker/ext/beans/MemberAccessPolicy.java
+++ b/freemarker-core/src/main/java/freemarker/ext/beans/MemberAccessPolicy.java
@@ -36,11 +36,16 @@ import freemarker.template.TemplateModel;
  * {@link BeansWrapperBuilder#setMemberAccessPolicy(MemberAccessPolicy)} (or 
if you use {@link DefaultObjectWrapper},
  * with {@link 
DefaultObjectWrapperBuilder#setMemberAccessPolicy(MemberAccessPolicy)}).
  *
- * <p>As {@link BeansWrapper}, and its subclasses like {@link 
DefaultObjectWrapper}, only discover public
+ * <p>As {@link BeansWrapper}, and its subclasses, like {@link 
DefaultObjectWrapper}, only discover public
  * members, it's pointless to whitelist non-public members. (Also, while 
public members declared in non-public classes
  * are discovered by {@link BeansWrapper}, Java reflection will not allow 
accessing those normally, so generally it's
  * not useful to whitelist those either.)
  *
+ * <p>Note {@link BeansWrapper}, and its subclasses, like {@link 
DefaultObjectWrapper}, also have an
+ * {@link BeansWrapper#setExposureLevel(int) exposureLevel} a setting that's 
applied before the
+ * {@link MemberAccessPolicy}, also, with {@link BeansWrapper#EXPOSE_ALL} the 
{@link MemberAccessPolicy} will be
+ * ignored.
+ *
  * <p>Note that if you add {@link TemplateModel}-s directly to the data-model, 
those are not wrapped by the
  * {@link ObjectWrapper} (from {@link Environment#getObjectWrapper()}), and so 
the {@link MemberAccessPolicy} won't
  * affect those.
diff --git 
a/freemarker-core/src/main/java/freemarker/ext/beans/MethodAppearanceFineTuner.java
 
b/freemarker-core/src/main/java/freemarker/ext/beans/MethodAppearanceFineTuner.java
index 8d0ccd8d..88d16827 100644
--- 
a/freemarker-core/src/main/java/freemarker/ext/beans/MethodAppearanceFineTuner.java
+++ 
b/freemarker-core/src/main/java/freemarker/ext/beans/MethodAppearanceFineTuner.java
@@ -24,11 +24,13 @@ import java.beans.PropertyDescriptor;
 
 import freemarker.ext.beans.BeansWrapper.MethodAppearanceDecision;
 import freemarker.ext.beans.BeansWrapper.MethodAppearanceDecisionInput;
+import freemarker.template.MethodCallAwareTemplateHashModel;
 
 /**
- * Used for customizing how the methods are visible from templates, via
+ * Used for customizing how the Java methods are visible from templates, via
  * {@link 
BeansWrapper#setMethodAppearanceFineTuner(MethodAppearanceFineTuner)}.
- * The object that implements this should also implement {@link 
SingletonCustomizer} whenever possible.
+ * The object that implements this should also implement {@link 
SingletonCustomizer} whenever possible, to allow reusing
+ * the class introspection cache in more situations.
  * 
  * @since 2.3.21
  */
@@ -52,24 +54,24 @@ public interface MethodAppearanceFineTuner {
      *     {@link #process} is not called for those.</li>
      *   <li>Show the method with a different name in the data-model than its
      *     real name by calling
-     *     {@link MethodAppearanceDecision#setExposeMethodAs(String)}
-     *     with non-{@code null} parameter. Also, if set to {@code null}, the 
method won't be exposed.
-     *     The default is the name of the method. Note that if {@code 
methodInsteadOfPropertyValueBeforeCall} is
-     *     {@code true}, the method is not exposed if the method name set here 
is the same as the name of the property
-     *     set for this method with {@link 
MethodAppearanceDecision#setExposeAsProperty(PropertyDescriptor)}.
+     *     {@link MethodAppearanceDecision#setExposeMethodAs(String)} with 
non-{@code null} parameter. (If set to
+     *     {@code null}, then the method won't be exposed.) The default is the 
real name of the method.
+     *     Note that if {@code methodInsteadOfPropertyValueBeforeCall} is 
{@code true}, the method is not exposed if the
+     *     method name set here is the same as the name of the property set 
for this method with
+     *     {@link 
MethodAppearanceDecision#setExposeAsProperty(PropertyDescriptor)}.
      *   <li>Create a fake JavaBean property for this method by calling
      *     {@link 
MethodAppearanceDecision#setExposeAsProperty(PropertyDescriptor)}.
      *     For example, if you have {@code int size()} in a class, but you
-     *     want it to be accessed from the templates as {@code obj.size},
-     *     rather than as {@code obj.size()}, you can do that with this
-     *     (but remember calling
-     *     {@link MethodAppearanceDecision#setMethodShadowsProperty(boolean)
-     *     setMethodShadowsProperty(false)} as well, if the method name is 
exactly
-     *     the same as the property name).
+     *     want it to be accessed from the templates as {@code obj.size} (like 
a JavaBean property),
+     *     rather than as {@code obj.size()}, you can do that with this (but 
remember calling
+     *     {@link MethodAppearanceDecision#setMethodShadowsProperty(boolean) 
setMethodShadowsProperty(false)} as well,
+     *     if the method name is exactly the same as the property name).
      *     The default is {@code null}, which means that no fake property is
      *     created for the method. You need not, and shouldn't set this
      *     to non-{@code null} for the property read (get/is) methods of real 
JavaBeans
-     *     properties, as bean properties are not seen as methods, and are 
exposed independently of this mechanism.
+     *     properties, as bean properties are not treated as methods (hence 
the {@link MethodAppearanceFineTuner} is
+     *     irrelevant), and are exposed or not regardless of this mechanism 
(based on
+     *     {@link BeansWrapperBuilder#setExposureLevel(int)}).
      *     The property name in the {@link PropertyDescriptor} can be anything,
      *     but the method (or methods) in it must belong to the class that
      *     is given as the {@code clazz} parameter, or it must be inherited 
from
@@ -88,29 +90,22 @@ public interface MethodAppearanceFineTuner {
      *     exposed that as a property via {@link 
MethodAppearanceDecision#setExposeAsProperty(PropertyDescriptor)}. So
      *     far, you can access the property value from templates as {@code 
user.name}, but {@code user.name()} will
      *     fail, saying that you try to call a {@code String} (because you 
apply the {@code ()} operator on the result
-     *     of {@code user.name}). But with
+     *     of {@code user.name}, which is a {@code String}). But with
      *     {@link 
MethodAppearanceDecision#setMethodInsteadOfPropertyValueBeforeCall(boolean)} 
{@code true},
-     *     both {@code user.name}, and {@code user.name()} will do the same.
-     *     The default of this is influenced by
+     *     both {@code user.name}, and {@code user.name()} will do the same 
(which is possible because if {@code user}
+     *     is a {@link MethodCallAwareTemplateHashModel}, "name" can be 
resoled to different values in the two cases).
+     *     The default (initial) value of {@code 
methodInsteadOfPropertyValueBeforeCall} depends on
      *     {@link 
BeansWrapperConfiguration#setDefaultZeroArgumentNonVoidMethodPolicy(ZeroArgumentNonVoidMethodPolicy)},
-     *     {@link 
BeansWrapperConfiguration#setRecordZeroArgumentNonVoidMethodPolicy(ZeroArgumentNonVoidMethodPolicy)}.
-     *   <li>Prevent the method to hide a JavaBeans property (fake or real) of
-     *     the same name by calling
-     *     {@link MethodAppearanceDecision#setMethodShadowsProperty(boolean)}
-     *     with {@code false}. The default is {@code true}, so if you have
-     *     both a property and a method called "foo", then in the template
-     *     {@code myObject.foo} will return the method itself instead
-     *     of the property value, which is often undesirable.
+     *     and {@link 
BeansWrapperConfiguration#setRecordZeroArgumentNonVoidMethodPolicy(ZeroArgumentNonVoidMethodPolicy)}.
+     *   <li>Prevent the method to hide a JavaBeans property (fake or real) of 
the same name by calling
+     *     {@link MethodAppearanceDecision#setMethodShadowsProperty(boolean)} 
with {@code false}.
+     *     The default is {@code true}, so if you have both a property and a 
method called "foo", then in the template
+     *     {@code myObject.foo} will return the method itself instead of the 
property value, which is often undesirable.
      * </ul>
-     * 
-     * <p>Note that you can expose a Java method both as a method, and as a
-     * JavaBeans property on the same time, however you have to chose different
-     * names for them to prevent shadowing. 
-     * 
-     * @param in Describes the method about which the decision will have to be 
made.
+     *
+     * @param in Describes the method about which the decision will be made.
      *  
-     * @param out Stores how the method will be exposed in the
-     *   data-model after {@link #process} returns.
+     * @param out Stores how the method will be exposed in the data-model 
after {@link #process} returns.
      *   This is initialized so that it reflects the default
      *   behavior of {@link BeansWrapper}, so you don't have to do anything 
with this
      *   when you don't want to change the default behavior.
diff --git 
a/freemarker-core/src/main/java/freemarker/ext/beans/ZeroArgumentNonVoidMethodPolicy.java
 
b/freemarker-core/src/main/java/freemarker/ext/beans/ZeroArgumentNonVoidMethodPolicy.java
index 2864e1ca..187d5bc7 100644
--- 
a/freemarker-core/src/main/java/freemarker/ext/beans/ZeroArgumentNonVoidMethodPolicy.java
+++ 
b/freemarker-core/src/main/java/freemarker/ext/beans/ZeroArgumentNonVoidMethodPolicy.java
@@ -20,9 +20,12 @@
 package freemarker.ext.beans;
 
 import freemarker.template.DefaultObjectWrapper;
+import freemarker.template.MethodCallAwareTemplateHashModel;
+import freemarker.template.ObjectWrapper;
+import freemarker.template.TemplateHashModel;
 
 /**
- * How to show 0 argument non-void public methods to templates, which are not 
standard Java Beans read methods.
+ * How to show 0 argument non-void public methods to templates.
  * Used in {@link BeansWrapper}, and therefore in {@link DefaultObjectWrapper}.
  * This policy doesn't apply to methods that Java Beans introspector discovers 
as a property read method (which
  * typically look like {@code getSomething()}, or {@code isSomething()}). It's 
only applicable to methods like
@@ -31,6 +34,7 @@ import freemarker.template.DefaultObjectWrapper;
  * @see 
BeansWrapperConfiguration#setDefaultZeroArgumentNonVoidMethodPolicy(ZeroArgumentNonVoidMethodPolicy)
  * @see 
BeansWrapperConfiguration#setRecordZeroArgumentNonVoidMethodPolicy(ZeroArgumentNonVoidMethodPolicy)
  * @see 
BeansWrapper.MethodAppearanceDecision#setMethodInsteadOfPropertyValueBeforeCall(boolean)
+ * @see MethodCallAwareTemplateHashModel
  *
  * @since 2.3.33
  */
@@ -38,12 +42,22 @@ public enum ZeroArgumentNonVoidMethodPolicy {
 
     /**
      * Both {@code obj.m}, and {@code obj.m()} gives back the value that the 
{@code m} Java method returns, and it's
-     * not possible to get the method itself.
+     * not possible to get the method itself. But, it's not applicable for 
Java Bean property read methods
+     * (like {@code int getX()}), which remain just simple methods (because 
the Java Bean property is visible regardless
+     * of the {@link ZeroArgumentNonVoidMethodPolicy}, like {@code obj.x}, for 
which the Java Bean property read method
+     * is {@link int getX()}).
      *
-     * <p>This is a parse-time trick that only works when the result of the 
dot operator is called immediately in a
-     * template (and therefore the dot operator knows that you will call the 
result of it). The practical reason for
-     * this feature is that the convention of having {@code SomeType 
something()} instead of
-     * {@code SomeType getSomething()} spreads in the Java ecosystem (and is a 
standard in some other JVM languages),
+     * <p>This is a parse-time trick that only works when the result of the 
dot operator (like {@code obj.m}), or of the
+     * square bracket key operator (like {@code obj["m"]}) is called 
immediately in a template (like {@code obj.m()}, or
+     * like {@code obj["m"]()}), and therefore the dot, or square bracket key 
operator knows that you will call the
+     * result of it. In such case, if the {@linkplain ObjectWrapper wrapped} 
{@code obj} implements
+     * {@link MethodCallAwareTemplateHashModel}, the operator will call
+     * {@link MethodCallAwareTemplateHashModel#getBeforeMethodCall(String)} 
instead of
+     * {@link TemplateHashModel#get(String)}. Also note that at least in 
2.3.33 it's only done if the method call has
+     * 0 arguments.
+     *
+     * <p>The practical reason for this feature is that the convention of 
using {@code SomeType something()} instead
+     * of {@code SomeType getSomething()} spreads in the Java ecosystem (and 
is a standard in some other JVM languages),
      * and thus we can't tell anymore if {@code SomeType something()} just 
reads a value, and hence should be accessed
      * like {@code obj.something}, or it's more like an operation that has 
side effect, and therefore should be
      * accessed like {@code obj.something()}. So with allowing both, the 
template author is free to decide which is
@@ -54,12 +68,14 @@ public enum ZeroArgumentNonVoidMethodPolicy {
     BOTH_PROPERTY_AND_METHOD,
 
     /**
-     * Only {@code obj.m()} gives back the value, {@code obj.m} just gives the 
method itself.
+     * Only {@code obj.m()} gives back the value in a template, {@code obj.m} 
in a template just gives the method itself.
      */
     METHOD_ONLY,
 
     /**
-     * {@code obj.m} in gives back the value, and the method itself can't be 
get.
+     * {@code obj.m} in a template gives back the value, and you can't get the 
method itself. But, it's not applicable
+     * for Java Bean property read methods, which will remain normals methods, 
just like with
+     * {@link #BOTH_PROPERTY_AND_METHOD}.
      */
     PROPERTY_ONLY
 }
diff --git 
a/freemarker-core/src/main/java/freemarker/template/MethodCallAwareTemplateHashModel.java
 
b/freemarker-core/src/main/java/freemarker/template/MethodCallAwareTemplateHashModel.java
index a3e80560..bac40768 100644
--- 
a/freemarker-core/src/main/java/freemarker/template/MethodCallAwareTemplateHashModel.java
+++ 
b/freemarker-core/src/main/java/freemarker/template/MethodCallAwareTemplateHashModel.java
@@ -30,23 +30,32 @@ import freemarker.template.utility.NullArgumentException;
 
 /**
  * Adds an extra getter method to {@link TemplateHashModel} that can return 
different result than {@link #get(String)},
- * knowing that the result of it will be called as a method. At least as of 
2.3.33, this is only utilized by the
- * template language for 0-argument non-void method calls directly after the 
dot operator and the key (like
- * {@code obj.m()}), or for the equivalent with square brackets ({@code 
obj["m"]()}). For example, if in the
- * template you have {@code someRecord.someComponent()}, and there {@code 
someRecord} was wrapped by the
- * {@link ObjectWrapper} into a {@link TemplateHashModel} that also implements 
this interface, then the dot operator
- * will call {@link #getBeforeMethodCall(String) 
getBeforeMethodCall("someComponent")}, rather than
+ * knowing that the result of it will be called as a method. At least as of 
2.3.33, this is only used by the
+ * template language for 0-argument method calls that are <em>directly</em> 
after the dot operator and the key (like in
+ * {@code obj.m()}, where the "()" is directly after the key "m"), or for the 
equivalent of that with square brackets
+ * ({@code obj["m"]()}).
+ *
+ * <p>Background knowledge needed to understand this: In the FreeMarker 
template language, methods/functions are
+ * first class values (just like strings, numbers,etc.). Also, unlike in Java, 
there's no separate namespace for
+ * methods, and for the other field-like members. When you have {@code 
obj.m()} in a template, first, the dot operator
+ * gets the value for the key "m", and after that, and independently of that, 
the method call operator tries to call
+ * that value (and if it's not a method or function, that will fail). The dot 
operator, before 2.3.33, was never aware
+ * of what the value it gets will be used for (like will it be called, will it 
be printed, etc.). Now it can be.
+ *
+ * For example, if in the template you have {@code 
someRecord.someComponent()}, and there {@code someRecord} was wrapped
+ * by the {@link ObjectWrapper} into a {@link TemplateHashModel} that also 
implements this interface, then the dot
+ * operator will call {@link #getBeforeMethodCall(String) 
getBeforeMethodCall("someComponent")}, rather than
  * {@link #get(String) get("someComponent")}. This is needed to implement 
subtle features like
  * {@link 
BeansWrapper.MethodAppearanceDecision#setMethodInsteadOfPropertyValueBeforeCall(boolean)},
  * which is needed to implement {@link 
ZeroArgumentNonVoidMethodPolicy#BOTH_PROPERTY_AND_METHOD}.
  *
  * <p>While technically we could do the same for method calls with more the 0 
arguments, as of 2.3.33 at least, we
- * don't want to generalize this to that case. The FreeMarker 2.x template 
language doesn't have separated namespace for
- * methods, so this is already a hack as is, but we had to address the issue 
with Java records (see that at
+ * don't want to generalize this to that case. This is a workaround we added 
to address the issue with accessing
+ * components in Java records, which are 0-argument methods (see that at
  * {@link 
BeansWrapper.MethodAppearanceDecision#setMethodInsteadOfPropertyValueBeforeCall(boolean)}).
  *
  * <p>Objects wrapped with {@link BeansWrapper}, and hence with {@link 
DefaultObjectWrapper}, will implement this
- * interface, when they are "generic" objects (that is, when they are not 
classes with special wrapper, like
+ * interface when they are "generic" objects (that is, when they are not 
classes with special wrapper, like
  * {@link Map}-s, {@link Collection}-s, {@link Number}-s, etc.).
  *
  * @since 2.3.33
@@ -54,22 +63,23 @@ import freemarker.template.utility.NullArgumentException;
 public interface MethodCallAwareTemplateHashModel extends TemplateHashModel {
 
     /**
-     * This is called instead of {@link #get(String)}, if we know that the 
return value should be callable like a
-     * method. The advantage of this is that we can coerce the value to a 
method when desirable, and otherwise can give
+     * This is called instead of {@link #get(String)} if we know that the 
return value should be callable like a method.
+     * The advantage of this is that we can coerce the value to a method when 
desirable, and otherwise can give
      * a more specific error message in the resulting exception than the 
standard {@link NonMethodException} would.
      *
      * @param key
      *      Same as for {@link #get(String)}
      *
      * @return
-     *      Same as for just like {@link #get(String)}, except it should 
return a
-     *      {@link TemplateMethodModelEx}, or a {@link TemplateMethodModel}, 
or in very rare case a {@link Macro}
-     *      that was created with the {@code function} directive. Or, {@code 
null} in the same case as
+     *      Same as for {@link #get(String)}, except it should return a {@link 
TemplateMethodModel}
+     *      (a {@link TemplateMethodModelEx} if possible), or in very rare 
case a {@link Macro} that was created with
+     *      the {@code function} directive. Or, {@code null} in the same case 
as
      *      {@link #get(String)}. The method should never return something 
that's not callable in the template language
-     *      as a method or function.
+     *      as a method or function; the return type is {@link TemplateModel} 
because that's the most specific common
+     *      super-interface of {@link TemplateMethodModel}, and {@link Macro}.
      *
      * @throws ShouldNotBeGetAsMethodException
-     *      If the value for the given key exists, but it shouldn't be coerced 
something callable as a method. This will
+     *      If the value for the given key exists, but shouldn't be coerced to 
something callable as a method. This will
      *      be converted to {@link NonMethodException} by the engine, but in 
this exception you can optionally give a
      *      more specific explanation, and that will be added to the resulting 
{@link NonMethodException} as a hint to
      *      the user.
@@ -79,6 +89,8 @@ public interface MethodCallAwareTemplateHashModel extends 
TemplateHashModel {
 
     /**
      * Thrown by {@link #getBeforeMethodCall(String)}; see there.
+     *
+     * @since 2.3.33
      */
     final class ShouldNotBeGetAsMethodException extends Exception {
         private final TemplateModel actualValue;

Reply via email to