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

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


The following commit(s) were added to refs/heads/FREEMARKER-183 by this push:
     new 44b8c4a6 FREEMARKER-183 continued: Some code and javadoc cleanup
44b8c4a6 is described below

commit 44b8c4a68567a426bff6a1dce69412b4e9eb27a7
Author: ddekany <[email protected]>
AuthorDate: Sun Feb 18 01:53:55 2024 +0100

    FREEMARKER-183 continued: Some code and javadoc cleanup
---
 .../src/main/java/freemarker/core/Dot.java         |   6 +-
 .../java/freemarker/core/DotBeforeMethodCall.java  |   4 +-
 .../main/java/freemarker/ext/beans/BeanModel.java  |  46 +++---
 .../java/freemarker/ext/beans/BeansWrapper.java    |   6 +-
 .../freemarker/ext/beans/ClassIntrospector.java    |  10 +-
 .../ext/beans/FastPropertyDescriptor.java          |   3 +-
 .../ext/beans/ZeroArgumentNonVoidMethodPolicy.java |   4 +-
 .../template/MethodCallAwareTemplateHashModel.java |   7 +-
 .../beans/TestZeroArgumentNonVoidMethodPolicy.java | 164 ++++++++++++---------
 9 files changed, 129 insertions(+), 121 deletions(-)

diff --git a/freemarker-core/src/main/java/freemarker/core/Dot.java 
b/freemarker-core/src/main/java/freemarker/core/Dot.java
index f360a955..a2d2f27b 100644
--- a/freemarker-core/src/main/java/freemarker/core/Dot.java
+++ b/freemarker-core/src/main/java/freemarker/core/Dot.java
@@ -29,7 +29,7 @@ import freemarker.template.TemplateModel;
  */
 class Dot extends Expression {
     private final Expression target;
-    private final String key;
+    protected final String key;
 
     Dot(Expression target, String key) {
         this.target = target;
@@ -61,10 +61,6 @@ class Dot extends Expression {
         return leftModel.get(key);
     }
 
-    String getKey() {
-        return key;
-    }
-
     @Override
     public String getCanonicalForm() {
         return target.getCanonicalForm() + getNodeTypeSymbol() + 
_CoreStringUtils.toFTLIdentifierReferenceAfterDot(key);
diff --git 
a/freemarker-core/src/main/java/freemarker/core/DotBeforeMethodCall.java 
b/freemarker-core/src/main/java/freemarker/core/DotBeforeMethodCall.java
index c842c7d6..17ae026c 100644
--- a/freemarker-core/src/main/java/freemarker/core/DotBeforeMethodCall.java
+++ b/freemarker-core/src/main/java/freemarker/core/DotBeforeMethodCall.java
@@ -20,8 +20,8 @@
 package freemarker.core;
 
 import freemarker.ext.beans.BeansWrapper;
-import freemarker.template.MethodCallAwareTemplateHashModel;
 import freemarker.ext.beans.ZeroArgumentNonVoidMethodPolicy;
+import freemarker.template.MethodCallAwareTemplateHashModel;
 import freemarker.template.TemplateException;
 import freemarker.template.TemplateHashModel;
 import freemarker.template.TemplateModel;
@@ -43,7 +43,7 @@ class DotBeforeMethodCall extends Dot {
     protected TemplateModel evalOnHash(TemplateHashModel leftModel) throws 
TemplateException {
         if (leftModel instanceof MethodCallAwareTemplateHashModel) {
             try {
-                return ((MethodCallAwareTemplateHashModel) 
leftModel).getBeforeMethodCall(getKey());
+                return ((MethodCallAwareTemplateHashModel) 
leftModel).getBeforeMethodCall(key);
             } catch 
(MethodCallAwareTemplateHashModel.ShouldNotBeGetAsMethodException e) {
                 String hint = e.getHint();
                 throw new NonMethodException(
diff --git a/freemarker-core/src/main/java/freemarker/ext/beans/BeanModel.java 
b/freemarker-core/src/main/java/freemarker/ext/beans/BeanModel.java
index fd92bab8..c983ae03 100644
--- a/freemarker-core/src/main/java/freemarker/ext/beans/BeanModel.java
+++ b/freemarker-core/src/main/java/freemarker/ext/beans/BeanModel.java
@@ -158,27 +158,6 @@ implements TemplateHashModelEx, AdapterTemplateModel, 
WrapperTemplateModel, Temp
         }
     }
 
-    /**
-     * Can be overridden to be public, to implement {@link 
MethodCallAwareTemplateHashModel}. We don't implement that
-     * in {@link BeanModel} for backward compatibility, but the functionality 
is present. If you expose this method by
-     * implementing {@link MethodCallAwareTemplateHashModel}, then be sure 
that {@link #get(String)} is
-     * not overridden in custom subclasses; if it is, then those subclasses 
should be modernized to override
-     * {@link #get(String, boolean)} instead.
-     *
-     * @since 2.3.33
-     */
-    protected TemplateModel getBeforeMethodCall(String key)
-            throws TemplateModelException, 
MethodCallAwareTemplateHashModel.ShouldNotBeGetAsMethodException {
-        TemplateModel result = get(key, true);
-        if (result instanceof  TemplateMethodModelEx) {
-            return (TemplateMethodModelEx) result;
-        }
-        if (result == null) {
-            return null;
-        }
-        throw new 
MethodCallAwareTemplateHashModel.ShouldNotBeGetAsMethodException(result, null);
-    }
-
     /**
      * Override this if you want to customize the behavior of {@link 
#get(String)}.
      * In standard implementations at least, this is what {@link 
#get(String)}, and
@@ -200,11 +179,11 @@ implements TemplateHashModelEx, AdapterTemplateModel, 
WrapperTemplateModel, Temp
     // instead, as this class didn't exist before 2.3.33. So with 
incompatibleImprovements before that, that should be
     // the only place where this gets called, or else the behavior of the 
model will be inconsistent.
     protected TemplateModel get(String key, boolean beforeMethodCall)
-        throws TemplateModelException, 
MethodCallAwareTemplateHashModel.ShouldNotBeGetAsMethodException {
+            throws TemplateModelException, 
MethodCallAwareTemplateHashModel.ShouldNotBeGetAsMethodException {
         Class<?> clazz = object.getClass();
         Map<Object, Object> classInfo = 
wrapper.getClassIntrospector().get(clazz);
         TemplateModel retval = null;
-        
+
         try {
             if (wrapper.isMethodsShadowItems()) {
                 Object fd = classInfo.get(key);
@@ -242,9 +221,6 @@ implements TemplateHashModelEx, AdapterTemplateModel, 
WrapperTemplateModel, Temp
         } catch (TemplateModelException | 
MethodCallAwareTemplateHashModel.ShouldNotBeGetAsMethodException e) {
             throw e;
         } catch (Exception e) {
-            if (beforeMethodCall && e instanceof 
MethodCallAwareTemplateHashModel.ShouldNotBeGetAsMethodException) {
-                throw 
(MethodCallAwareTemplateHashModel.ShouldNotBeGetAsMethodException) e;
-            }
             throw new _TemplateModelException(e,
                     "An error has occurred when reading existing sub-variable 
", new _DelayedJQuote(key),
                     "; see cause exception! The type of the containing value 
was: ",
@@ -253,6 +229,24 @@ implements TemplateHashModelEx, AdapterTemplateModel, 
WrapperTemplateModel, Temp
         }
     }
 
+    /**
+     * Can be overridden to be public, to implement {@link 
MethodCallAwareTemplateHashModel}. We don't implement that
+     * in {@link BeanModel} for backward compatibility, but the functionality 
is present. If you expose this method by
+     * implementing {@link MethodCallAwareTemplateHashModel}, then be sure 
that {@link #get(String)} is
+     * not overridden in custom subclasses; if it is, then those subclasses 
should be modernized to override
+     * {@link #get(String, boolean)} instead.
+     *
+     * @since 2.3.33
+     */
+    protected TemplateModel getBeforeMethodCall(String key)
+            throws TemplateModelException, 
MethodCallAwareTemplateHashModel.ShouldNotBeGetAsMethodException {
+        TemplateModel result = get(key, true);
+        if (result instanceof  TemplateMethodModelEx || result == null) {
+            return result;
+        }
+        throw new 
MethodCallAwareTemplateHashModel.ShouldNotBeGetAsMethodException(result, null);
+    }
+
     private void logNoSuchKey(String key, Map<?, ?> keyMap) {
         LOG.debug("Key " + StringUtil.jQuoteNoXSS(key) + " was not found on 
instance of " + 
             object.getClass().getName() + ". Introspection information for " +
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 0fbf3561..0e202662 100644
--- a/freemarker-core/src/main/java/freemarker/ext/beans/BeansWrapper.java
+++ b/freemarker-core/src/main/java/freemarker/ext/beans/BeansWrapper.java
@@ -267,7 +267,7 @@ public class BeansWrapper implements RichObjectWrapper, 
WriteProtectable {
      *     <li>
      *       <p>2.3.33 (or higher):
      *       The default of {@link 
BeansWrapper#setRecordZeroArgumentNonVoidMethodPolicy(ZeroArgumentNonVoidMethodPolicy)}
-     *       has changes to {@link 
ZeroArgumentNonVoidMethodPolicy#BOTH_PROPERTY_AND_METHOD}, from
+     *       has changed to {@link 
ZeroArgumentNonVoidMethodPolicy#BOTH_PROPERTY_AND_METHOD}, from
      *       {@link ZeroArgumentNonVoidMethodPolicy#METHOD_ONLY}. This means 
that Java records public methods with
      *       0-arguments and non-void return type are now exposed both as 
properties, and as methods, while earlier they
      *       were only exposed as methods. That is, if in a record you have 
{@code public String name()}, now in
@@ -667,8 +667,8 @@ public class BeansWrapper implements RichObjectWrapper, 
WriteProtectable {
     /**
      * Sets the {@link ZeroArgumentNonVoidMethodPolicy} for classes that are 
Java records; if the
      * {@code BeansWrapper#BeansWrapper(Version) incompatibleImprovements} of 
the object wrapper is at least 2.3.33,
-     * then it defaults to {@link 
ZeroArgumentNonVoidMethodPolicy#BOTH_PROPERTY_AND_METHOD}, otherwise it 
defaults to
-     * {@link ZeroArgumentNonVoidMethodPolicy#METHOD_ONLY}.
+     * then this defaults to {@link 
ZeroArgumentNonVoidMethodPolicy#BOTH_PROPERTY_AND_METHOD}, otherwise this 
defaults
+     * to {@link ZeroArgumentNonVoidMethodPolicy#METHOD_ONLY}.
      *
      * <p>Note that methods in this class are inherited by {@link 
DefaultObjectWrapper}, which is what you normally use.
      *
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 81f4ffe0..bc4a5ddd 100644
--- a/freemarker-core/src/main/java/freemarker/ext/beans/ClassIntrospector.java
+++ b/freemarker-core/src/main/java/freemarker/ext/beans/ClassIntrospector.java
@@ -350,7 +350,7 @@ class ClassIntrospector {
 
         // For real Java Beans properties only, used to exclude them from 
creating fake properties based on ZeroArgumentNonVoidMethod.
         Set<String> beanPropertyReadMethodNameCollector = 
zeroArgumentNonVoidMethodPolicy != ZeroArgumentNonVoidMethodPolicy.METHOD_ONLY
-                ? new HashSet<String>()
+                ? new HashSet<>()
                 : null;
 
         List<PropertyDescriptor> pdas = getPropertyDescriptors(beanInfo, 
clazz);
@@ -443,11 +443,9 @@ class ClassIntrospector {
 
     private static ZeroArgumentNonVoidMethodPolicy 
getAppliedZeroArgumentNonVoidMethodPolicy(Method method, Set<String> 
beanPropertyReadMethodNameCollector, ZeroArgumentNonVoidMethodPolicy 
zeroArgumentNonVoidMethodPolicy) {
         if (method.getParameterCount() == 0 && method.getReturnType() != 
void.class) {
-            if (beanPropertyReadMethodNameCollector != null && 
beanPropertyReadMethodNameCollector.contains(method.getName())) {
-                return ZeroArgumentNonVoidMethodPolicy.METHOD_ONLY;
-            } else {
-                return zeroArgumentNonVoidMethodPolicy;
-            }
+            return beanPropertyReadMethodNameCollector != null && 
beanPropertyReadMethodNameCollector.contains(method.getName())
+                    ? ZeroArgumentNonVoidMethodPolicy.METHOD_ONLY
+                    : zeroArgumentNonVoidMethodPolicy;
         } else {
             return null;
         }
diff --git 
a/freemarker-core/src/main/java/freemarker/ext/beans/FastPropertyDescriptor.java
 
b/freemarker-core/src/main/java/freemarker/ext/beans/FastPropertyDescriptor.java
index 7ae8b674..d66f06d2 100644
--- 
a/freemarker-core/src/main/java/freemarker/ext/beans/FastPropertyDescriptor.java
+++ 
b/freemarker-core/src/main/java/freemarker/ext/beans/FastPropertyDescriptor.java
@@ -23,6 +23,7 @@ import java.lang.reflect.Method;
 
 /**
  * Used instead of {@link PropertyDescriptor}, because the methods of that are 
synchronized.
+ * Also, we use this for "fake" Java Beans properties too (see {@link 
BeansWrapper.MethodAppearanceDecision}).
  * 
  * @since 2.3.27
  */
@@ -48,7 +49,7 @@ final class FastPropertyDescriptor {
 
     /**
      * If this is true, and the property value is referred directly before 
it's called in a template, then
-     * the instead of the property value, it the value should be the read 
method (which therefore will be called).
+     * instead of the property value, the value should be the read method.
      *
      * @since 2.3.33
      */
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 85f2390a..add78f0b 100644
--- 
a/freemarker-core/src/main/java/freemarker/ext/beans/ZeroArgumentNonVoidMethodPolicy.java
+++ 
b/freemarker-core/src/main/java/freemarker/ext/beans/ZeroArgumentNonVoidMethodPolicy.java
@@ -25,7 +25,7 @@ import freemarker.template.DefaultObjectWrapper;
  * How to show 0 argument non-void public methods to templates, which are not 
standard Java Beans read methods.
  * 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()}/{@code getSomething()}). It's 
only applicable to methods like
+ * typically look like {@code getSomething()}, or {@code isSomething()}). It's 
only applicable to methods like
  * {@code something()}, including the component read methods of Java records.
  *
  * @see 
BeansWrapperConfiguration#setNonRecordZeroArgumentNonVoidMethodPolicy(ZeroArgumentNonVoidMethodPolicy)
@@ -46,7 +46,7 @@ public enum ZeroArgumentNonVoidMethodPolicy {
      * {@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 be allowing both, the 
template author is free to decide which is
+     * accessed like {@code obj.something()}. So with allowing both, the 
template author is free to decide which is
      * the more fitting. Also, for accessing Java records components, the 
proper way is {@code obj.something}, but
      * before FreeMarker was aware of records (and hence that those methods 
are like property read methods), the
      * only way that worked was {@code obj.something()}, so to be more 
backward compatible, we have to support both.
diff --git 
a/freemarker-core/src/main/java/freemarker/template/MethodCallAwareTemplateHashModel.java
 
b/freemarker-core/src/main/java/freemarker/template/MethodCallAwareTemplateHashModel.java
index 6c4912e2..a3e80560 100644
--- 
a/freemarker-core/src/main/java/freemarker/template/MethodCallAwareTemplateHashModel.java
+++ 
b/freemarker-core/src/main/java/freemarker/template/MethodCallAwareTemplateHashModel.java
@@ -29,9 +29,10 @@ import freemarker.ext.beans.ZeroArgumentNonVoidMethodPolicy;
 import freemarker.template.utility.NullArgumentException;
 
 /**
- * Adds a getter method to {@link TemplateHashModel}, that can return 
different result than {@link #get(String)},
+ * 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 method calls directly after the dot 
operator and the key. For example, if in 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
@@ -39,7 +40,7 @@ import freemarker.template.utility.NullArgumentException;
  * {@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
+ * <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
  * {@link 
BeansWrapper.MethodAppearanceDecision#setMethodInsteadOfPropertyValueBeforeCall(boolean)}).
diff --git 
a/freemarker-core16/src/test/java/freemarker/ext/beans/TestZeroArgumentNonVoidMethodPolicy.java
 
b/freemarker-core16/src/test/java/freemarker/ext/beans/TestZeroArgumentNonVoidMethodPolicy.java
index 56b80d53..ee834f88 100644
--- 
a/freemarker-core16/src/test/java/freemarker/ext/beans/TestZeroArgumentNonVoidMethodPolicy.java
+++ 
b/freemarker-core16/src/test/java/freemarker/ext/beans/TestZeroArgumentNonVoidMethodPolicy.java
@@ -37,20 +37,12 @@ import freemarker.template.TemplateException;
 import freemarker.test.TemplateTest;
 
 public class TestZeroArgumentNonVoidMethodPolicy extends TemplateTest {
-    private static final Pattern DOT_REPLACE_PATTERN = 
Pattern.compile("\\.(\\w+)");
-
-    private static String withDotOrSquareBracket(String s, boolean dot) {
-        if (dot) {
-            return s;
-        }
-        return DOT_REPLACE_PATTERN.matcher(s).replaceFirst(key -> "['" + 
key.group(1) + "']");
-    }
-
     @Override
     protected Configuration createConfiguration() throws Exception {
         Configuration cfg = super.createConfiguration();
         // Don't use default, as then the object wrapper is a shared static 
mutable object:
         cfg.setIncompatibleImprovements(Configuration.VERSION_2_3_32);
+        cfg.setAPIBuiltinEnabled(true);
         return cfg;
     }
 
@@ -174,104 +166,104 @@ public class TestZeroArgumentNonVoidMethodPolicy 
extends TemplateTest {
     }
 
     private void assertRecIsBothPropertyAndMethod() throws IOException, 
TemplateException {
-        for (boolean dot : List.of(true, false)) {
-            assertOutput(withDotOrSquareBracket("${rec.x}", dot), "1");
-            assertOutput(withDotOrSquareBracket("${rec.x()}", dot), "1");
-            assertOutput(withDotOrSquareBracket("${rec.s}", dot), "S");
-            assertOutput(withDotOrSquareBracket("${rec.s()}", dot), "S");
-            assertOutput(withDotOrSquareBracket("${rec.y}", dot), "2");
-            assertOutput(withDotOrSquareBracket("${rec.y()}", dot), "2");
-            assertOutput(withDotOrSquareBracket("${rec.tenX}", dot), "10");
-            assertOutput(withDotOrSquareBracket("${rec.tenX()}", dot), "10");
+        for (TemplateModifications tempMods : TemplateModifications.values()) {
+            assertOutput(modifyTemplate("${rec.x}", tempMods), "1");
+            assertOutput(modifyTemplate("${rec.x()}", tempMods), "1");
+            assertOutput(modifyTemplate("${rec.s}", tempMods), "S");
+            assertOutput(modifyTemplate("${rec.s()}", tempMods), "S");
+            assertOutput(modifyTemplate("${rec.y}", tempMods), "2");
+            assertOutput(modifyTemplate("${rec.y()}", tempMods), "2");
+            assertOutput(modifyTemplate("${rec.tenX}", tempMods), "10");
+            assertOutput(modifyTemplate("${rec.tenX()}", tempMods), "10");
         }
         assertRecPolicyIndependentMembers();
     }
 
     private void assertRecIsMethodOnly() throws IOException, TemplateException 
{
-        for (boolean dot : List.of(true, false)) {
-            assertErrorContains(withDotOrSquareBracket("${rec.x}", dot), 
"SimpleMethodModel");
-            assertOutput(withDotOrSquareBracket("${rec.x()}", dot), "1");
-            assertErrorContains(withDotOrSquareBracket("${rec.s}", dot), 
"SimpleMethodModel");
-            assertOutput(withDotOrSquareBracket("${rec.s()}", dot), "S");
-            assertErrorContains(withDotOrSquareBracket("${rec.y}", dot), 
"SimpleMethodModel");
-            assertOutput(withDotOrSquareBracket("${rec.y()}", dot), "2");
-            assertErrorContains(withDotOrSquareBracket("${rec.tenX}", dot), 
"SimpleMethodModel");
-            assertOutput(withDotOrSquareBracket("${rec.tenX()}", dot), "10");
+        for (TemplateModifications tempMods : TemplateModifications.values()) {
+            assertErrorContains(modifyTemplate("${rec.x}", tempMods), 
"SimpleMethodModel");
+            assertOutput(modifyTemplate("${rec.x()}", tempMods), "1");
+            assertErrorContains(modifyTemplate("${rec.s}", tempMods), 
"SimpleMethodModel");
+            assertOutput(modifyTemplate("${rec.s()}", tempMods), "S");
+            assertErrorContains(modifyTemplate("${rec.y}", tempMods), 
"SimpleMethodModel");
+            assertOutput(modifyTemplate("${rec.y()}", tempMods), "2");
+            assertErrorContains(modifyTemplate("${rec.tenX}", tempMods), 
"SimpleMethodModel");
+            assertOutput(modifyTemplate("${rec.tenX()}", tempMods), "10");
         }
         assertRecPolicyIndependentMembers();
     }
 
     private void assertRecIsPropertyOnly() throws IOException, 
TemplateException {
-        for (boolean dot : List.of(true, false)) {
-            assertOutput(withDotOrSquareBracket("${rec.x}", dot), "1");
-            assertErrorContains(withDotOrSquareBracket("${rec.x()}", dot), 
"SimpleNumber", "must not be called as a method");
-            assertOutput(withDotOrSquareBracket("${rec.s}", dot), "S");
-            assertErrorContains(withDotOrSquareBracket("${rec.s()}", dot), 
"SimpleScalar");
-            assertOutput(withDotOrSquareBracket("${rec.y}", dot), "2");
-            assertErrorContains(withDotOrSquareBracket("${rec.y()}", dot), 
"SimpleNumber");
-            assertOutput(withDotOrSquareBracket("${rec.tenX}", dot), "10");
-            assertErrorContains(withDotOrSquareBracket("${rec.tenX()}", dot), 
"SimpleNumber");
+        for (TemplateModifications tempMods : TemplateModifications.values()) {
+            assertOutput(modifyTemplate("${rec.x}", tempMods), "1");
+            assertErrorContains(modifyTemplate("${rec.x()}", tempMods), 
"SimpleNumber", "must not be called as a method");
+            assertOutput(modifyTemplate("${rec.s}", tempMods), "S");
+            assertErrorContains(modifyTemplate("${rec.s()}", tempMods), 
"SimpleScalar");
+            assertOutput(modifyTemplate("${rec.y}", tempMods), "2");
+            assertErrorContains(modifyTemplate("${rec.y()}", tempMods), 
"SimpleNumber");
+            assertOutput(modifyTemplate("${rec.tenX}", tempMods), "10");
+            assertErrorContains(modifyTemplate("${rec.tenX()}", tempMods), 
"SimpleNumber");
         }
         assertRecPolicyIndependentMembers();
     }
 
     private void assertRecPolicyIndependentMembers() throws IOException, 
TemplateException {
-        for (boolean dot : List.of(true, false)) {
-            assertOutput(withDotOrSquareBracket("${rec.z}", dot), "3");
-            assertErrorContains(withDotOrSquareBracket("${rec.z()}", dot), 
"SimpleNumber");
-            assertOutput(withDotOrSquareBracket("${rec.getZ()}", dot), "3");
-            assertOutput(withDotOrSquareBracket("${rec.xTimes(5)}", dot), "5");
-            assertErrorContains(withDotOrSquareBracket("${rec.xTimes}", dot), 
"SimpleMethodModel");
-            assertOutput(withDotOrSquareBracket("${rec.voidMethod()}", dot), 
"");
-            assertErrorContains(withDotOrSquareBracket("${rec.voidMethod}", 
dot), "SimpleMethodModel");
+        for (TemplateModifications tempMods : TemplateModifications.values()) {
+            assertOutput(modifyTemplate("${rec.z}", tempMods), "3");
+            assertErrorContains(modifyTemplate("${rec.z()}", tempMods), 
"SimpleNumber");
+            assertOutput(modifyTemplate("${rec.getZ()}", tempMods), "3");
+            assertOutput(modifyTemplate("${rec.xTimes(5)}", tempMods), "5");
+            assertErrorContains(modifyTemplate("${rec.xTimes}", tempMods), 
"SimpleMethodModel");
+            assertOutput(modifyTemplate("${rec.voidMethod()}", tempMods), "");
+            assertErrorContains(modifyTemplate("${rec.voidMethod}", tempMods), 
"SimpleMethodModel");
         }
     }
 
     private void assertNrcIsMethodOnly() throws IOException, TemplateException 
{
-        for (boolean dot : List.of(true, false)) {
-            assertErrorContains(withDotOrSquareBracket("${nrc.x}", dot), 
"SimpleMethodModel");
-            assertOutput(withDotOrSquareBracket("${nrc.x()}", dot), "1");
-            assertErrorContains(withDotOrSquareBracket("${nrc.y}", dot), 
"SimpleMethodModel");
-            assertOutput(withDotOrSquareBracket("${nrc.y()}", dot), "2");
-            assertErrorContains(withDotOrSquareBracket("${nrc.tenX}", dot), 
"SimpleMethodModel");
-            assertOutput(withDotOrSquareBracket("${nrc.tenX()}", dot), "10");
+        for (TemplateModifications tempMods : TemplateModifications.values()) {
+            assertErrorContains(modifyTemplate("${nrc.x}", tempMods), 
"SimpleMethodModel");
+            assertOutput(modifyTemplate("${nrc.x()}", tempMods), "1");
+            assertErrorContains(modifyTemplate("${nrc.y}", tempMods), 
"SimpleMethodModel");
+            assertOutput(modifyTemplate("${nrc.y()}", tempMods), "2");
+            assertErrorContains(modifyTemplate("${nrc.tenX}", tempMods), 
"SimpleMethodModel");
+            assertOutput(modifyTemplate("${nrc.tenX()}", tempMods), "10");
         }
         assertNrcPolicyIndependentMembers();
     }
 
     private void assertNrcIsBothPropertyAndMethod() throws IOException, 
TemplateException {
-        for (boolean dot : List.of(true, false)) {
-            assertOutput(withDotOrSquareBracket("${nrc.x}", dot), "1");
-            assertOutput(withDotOrSquareBracket("${nrc.x()}", dot), "1");
-            assertOutput(withDotOrSquareBracket("${nrc.y}", dot), "2");
-            assertOutput(withDotOrSquareBracket("${nrc.y()}", dot), "2");
-            assertOutput(withDotOrSquareBracket("${nrc.tenX}", dot), "10");
-            assertOutput(withDotOrSquareBracket("${nrc.tenX()}", dot), "10");
+        for (TemplateModifications tempMods : TemplateModifications.values()) {
+            assertOutput(modifyTemplate("${nrc.x}", tempMods), "1");
+            assertOutput(modifyTemplate("${nrc.x()}", tempMods), "1");
+            assertOutput(modifyTemplate("${nrc.y}", tempMods), "2");
+            assertOutput(modifyTemplate("${nrc.y()}", tempMods), "2");
+            assertOutput(modifyTemplate("${nrc.tenX}", tempMods), "10");
+            assertOutput(modifyTemplate("${nrc.tenX()}", tempMods), "10");
         }
         assertNrcPolicyIndependentMembers();
     }
 
     private void assertNrcIsPropertyOnly() throws IOException, 
TemplateException {
-        for (boolean dot : List.of(true, false)) {
-            assertOutput(withDotOrSquareBracket("${nrc.x}", dot), "1");
-            assertErrorContains(withDotOrSquareBracket("${nrc.x()}", dot), 
"SimpleNumber", "must not be called as a method");
-            assertOutput(withDotOrSquareBracket("${nrc.y}", dot), "2");
-            assertErrorContains(withDotOrSquareBracket("${nrc.y()}", dot), 
"SimpleNumber");
-            assertOutput(withDotOrSquareBracket("${nrc.tenX}", dot), "10");
-            assertErrorContains(withDotOrSquareBracket("${nrc.tenX()}", dot), 
"SimpleNumber");
+        for (TemplateModifications tempMods : TemplateModifications.values()) {
+            assertOutput(modifyTemplate("${nrc.x}", tempMods), "1");
+            assertErrorContains(modifyTemplate("${nrc.x()}", tempMods), 
"SimpleNumber", "must not be called as a method");
+            assertOutput(modifyTemplate("${nrc.y}", tempMods), "2");
+            assertErrorContains(modifyTemplate("${nrc.y()}", tempMods), 
"SimpleNumber");
+            assertOutput(modifyTemplate("${nrc.tenX}", tempMods), "10");
+            assertErrorContains(modifyTemplate("${nrc.tenX()}", tempMods), 
"SimpleNumber");
         }
         assertNrcPolicyIndependentMembers();
     }
 
     private void assertNrcPolicyIndependentMembers() throws IOException, 
TemplateException {
-        for (boolean dot : List.of(true, false)) {
-            assertOutput(withDotOrSquareBracket("${nrc.z}", dot), "3");
-            assertErrorContains(withDotOrSquareBracket("${nrc.z()}", dot), 
"SimpleNumber");
-            assertOutput(withDotOrSquareBracket("${nrc.getZ()}", dot), "3");
-            assertOutput(withDotOrSquareBracket("${nrc.xTimes(5)}", dot), "5");
-            assertErrorContains(withDotOrSquareBracket("${nrc.xTimes}", dot), 
"SimpleMethodModel");
-            assertOutput(withDotOrSquareBracket("${nrc.voidMethod()}", dot), 
"");
-            assertErrorContains(withDotOrSquareBracket("${nrc.voidMethod}", 
dot), "SimpleMethodModel");
+        for (TemplateModifications tempMods : TemplateModifications.values()) {
+            assertOutput(modifyTemplate("${nrc.z}", tempMods), "3");
+            assertErrorContains(modifyTemplate("${nrc.z()}", tempMods), 
"SimpleNumber");
+            assertOutput(modifyTemplate("${nrc.getZ()}", tempMods), "3");
+            assertOutput(modifyTemplate("${nrc.xTimes(5)}", tempMods), "5");
+            assertErrorContains(modifyTemplate("${nrc.xTimes}", tempMods), 
"SimpleMethodModel");
+            assertOutput(modifyTemplate("${nrc.voidMethod()}", tempMods), "");
+            assertErrorContains(modifyTemplate("${nrc.voidMethod}", tempMods), 
"SimpleMethodModel");
         }
     }
 
@@ -349,4 +341,30 @@ public class TestZeroArgumentNonVoidMethodPolicy extends 
TemplateTest {
         }
     }
 
+    private static final Pattern DOT_TO_SQUARE_BRACKETS_REPLACEMENT_PATTERN = 
Pattern.compile("\\.(\\w+)");
+    
+    private static String modifyTemplate(String s, TemplateModifications 
tempMods) {
+        if (tempMods.useApi) {
+            s = s.replace(".", "?api.");
+        }
+        if (tempMods.doToSquareBrackets) {
+            s = 
DOT_TO_SQUARE_BRACKETS_REPLACEMENT_PATTERN.matcher(s).replaceFirst(key -> "['" 
+ key.group(1) + "']");
+        }
+        return s;
+    }
+
+    enum TemplateModifications {
+        DOT(true, false), SQUARE_BRACKETS(false, false),
+        API_DOT(true, true), API_SQUARE_BRACKETS(false, true);
+
+        private final boolean doToSquareBrackets;
+        private final boolean useApi;
+
+        TemplateModifications(boolean doToSquareBrackets, boolean useApi) {
+            this.doToSquareBrackets = doToSquareBrackets;
+            this.useApi = useApi;
+        }
+    }
+
+
 }

Reply via email to