You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2024/05/08 16:32:23 UTC
(freemarker) branch 2.3-gae updated (3b0de722 -> 68e95a39)
This is an automated email from the ASF dual-hosted git repository.
ddekany pushed a change to branch 2.3-gae
in repository https://gitbox.apache.org/repos/asf/freemarker.git
from 3b0de722 Fixed: Configuration.setServletContextForTemplateLoading did not support Jakarta
new 5afeb9a1 Improved JavaDoc related to ZeroArgumentNonVoidMethodPolicy (mostly).
new bf5e0b0a Renamed some ZeroArgumentNonVoidMethodPolicy-es to reflect the meaning more precisely (on the expense of having really long enum names): - BOTH_PROPERTY_AND_METHOD to BOTH_METHOD_AND_PROPERTY_UNLESS_BEAN_PROPERTY_READ_METHOD - PROPERTY_ONLY to PROPERTY_ONLY_UNLESS_BEAN_PROPERTY_READ_METHOD This way it's now also true that the ZeroArgumentNonVoidMethodPolicy just specifies "How to show 0 argument non-void public methods to templates", as not all the exceptions related to [...]
new 68e95a39 Updated README.md, mostly to describe Gradle build better.
The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
README.md | 121 ++++++++++-----------
.../java/freemarker/core/DotBeforeMethodCall.java | 4 +-
.../java/freemarker/ext/beans/BeansWrapper.java | 34 +++---
.../ext/beans/BeansWrapperConfiguration.java | 9 ++
.../freemarker/ext/beans/ClassIntrospector.java | 4 +-
.../ext/beans/ClassIntrospectorBuilder.java | 24 +++-
.../freemarker/ext/beans/MemberAccessPolicy.java | 7 +-
.../ext/beans/MethodAppearanceFineTuner.java | 61 +++++------
.../ext/beans/ZeroArgumentNonVoidMethodPolicy.java | 36 ++++--
.../template/MethodCallAwareTemplateHashModel.java | 47 +++++---
.../beans/TestZeroArgumentNonVoidMethodPolicy.java | 18 ++-
freemarker-manual/src/main/docgen/en_US/book.xml | 10 +-
12 files changed, 220 insertions(+), 155 deletions(-)
(freemarker) 02/03: Renamed some ZeroArgumentNonVoidMethodPolicy-es to reflect the meaning more precisely (on the expense of having really long enum names): - BOTH_PROPERTY_AND_METHOD to BOTH_METHOD_AND_PROPERTY_UNLESS_BEAN_PROPERTY_READ_METHOD - PROPERTY_ONLY to PROPERTY_ONLY_UNLESS_BEAN_PROPERTY_READ_METHOD This way it's now also true that the ZeroArgumentNonVoidMethodPolicy just specifies "How to show 0 argument non-void public methods to templates", as not all the exceptions related to Java Beans are desc [...]
Posted by dd...@apache.org.
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 bf5e0b0a60f5cfe2a5c3ab350beb96b1da785a23
Author: ddekany <dd...@apache.org>
AuthorDate: Wed May 8 18:23:34 2024 +0200
Renamed some ZeroArgumentNonVoidMethodPolicy-es to reflect the meaning more precisely (on the expense of having really long enum names):
- BOTH_PROPERTY_AND_METHOD to BOTH_METHOD_AND_PROPERTY_UNLESS_BEAN_PROPERTY_READ_METHOD
- PROPERTY_ONLY to PROPERTY_ONLY_UNLESS_BEAN_PROPERTY_READ_METHOD
This way it's now also true that the ZeroArgumentNonVoidMethodPolicy just specifies "How to show 0 argument non-void public methods to templates", as not all the exceptions related to Java Beans are described for the enum values.
---
.../main/java/freemarker/core/DotBeforeMethodCall.java | 2 +-
.../main/java/freemarker/ext/beans/BeansWrapper.java | 12 +++++++-----
.../freemarker/ext/beans/ClassIntrospectorBuilder.java | 5 +++--
.../ext/beans/ZeroArgumentNonVoidMethodPolicy.java | 6 +++---
.../template/MethodCallAwareTemplateHashModel.java | 3 ++-
.../ext/beans/TestZeroArgumentNonVoidMethodPolicy.java | 18 ++++++++++++------
freemarker-manual/src/main/docgen/en_US/book.xml | 10 +++++-----
7 files changed, 33 insertions(+), 23 deletions(-)
diff --git a/freemarker-core/src/main/java/freemarker/core/DotBeforeMethodCall.java b/freemarker-core/src/main/java/freemarker/core/DotBeforeMethodCall.java
index f40ebbd1..7fd3e1b7 100644
--- a/freemarker-core/src/main/java/freemarker/core/DotBeforeMethodCall.java
+++ b/freemarker-core/src/main/java/freemarker/core/DotBeforeMethodCall.java
@@ -30,7 +30,7 @@ import freemarker.template.TemplateModel;
* Like {@link Dot}, but when used before method call (but as of 2.3.33, before 0-argument calls only), as in
* {@code obj.key()}. The reason it's only used before 0-argument calls (as of 2.3.33 at least) is that it adds some
* overhead, and this {@link Dot} subclass was added to implement
- * {@link ZeroArgumentNonVoidMethodPolicy#BOTH_PROPERTY_AND_METHOD}
+ * {@link ZeroArgumentNonVoidMethodPolicy#BOTH_METHOD_AND_PROPERTY_UNLESS_BEAN_PROPERTY_READ_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.
*
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 542ac9d3..2a7f9125 100644
--- a/freemarker-core/src/main/java/freemarker/ext/beans/BeansWrapper.java
+++ b/freemarker-core/src/main/java/freemarker/ext/beans/BeansWrapper.java
@@ -266,7 +266,8 @@ public class BeansWrapper implements RichObjectWrapper, WriteProtectable {
* <li>
* <p>2.3.33 (or higher):
* The default of {@link BeansWrapper#setRecordZeroArgumentNonVoidMethodPolicy(ZeroArgumentNonVoidMethodPolicy)}
- * has changed to {@link ZeroArgumentNonVoidMethodPolicy#BOTH_PROPERTY_AND_METHOD}, from
+ * has changed to
+ * {@link ZeroArgumentNonVoidMethodPolicy#BOTH_METHOD_AND_PROPERTY_UNLESS_BEAN_PROPERTY_READ_METHOD}, from
* {@link ZeroArgumentNonVoidMethodPolicy#METHOD_ONLY}. This means that Java record 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
@@ -670,8 +671,9 @@ 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 this defaults to {@link ZeroArgumentNonVoidMethodPolicy#BOTH_PROPERTY_AND_METHOD}, otherwise this defaults
- * to {@link ZeroArgumentNonVoidMethodPolicy#METHOD_ONLY}.
+ * then this defaults to
+ * {@link ZeroArgumentNonVoidMethodPolicy#BOTH_METHOD_AND_PROPERTY_UNLESS_BEAN_PROPERTY_READ_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.
*
@@ -1945,12 +1947,12 @@ public class BeansWrapper implements RichObjectWrapper, WriteProtectable {
throw new BugException("Failed to create PropertyDescriptor for " + m, e);
}
methodInsteadOfPropertyValueBeforeCall = appliedZeroArgumentNonVoidMethodPolicy ==
- ZeroArgumentNonVoidMethodPolicy.BOTH_PROPERTY_AND_METHOD;
+ ZeroArgumentNonVoidMethodPolicy.BOTH_METHOD_AND_PROPERTY_UNLESS_BEAN_PROPERTY_READ_METHOD;
} else {
exposeAsProperty = null;
methodInsteadOfPropertyValueBeforeCall = false;
}
- exposeMethodAs = appliedZeroArgumentNonVoidMethodPolicy != ZeroArgumentNonVoidMethodPolicy.PROPERTY_ONLY
+ exposeMethodAs = appliedZeroArgumentNonVoidMethodPolicy != ZeroArgumentNonVoidMethodPolicy.PROPERTY_ONLY_UNLESS_BEAN_PROPERTY_READ_METHOD
? m.getName() : null;
methodShadowsProperty = true;
replaceExistingProperty = false;
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 a966f08d..7ff87072 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,7 @@ 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.BOTH_METHOD_AND_PROPERTY_UNLESS_BEAN_PROPERTY_READ_METHOD
: defaultZeroArgumentNonVoidMethodPolicy;
memberAccessPolicy = DefaultMemberAccessPolicy.getInstance(this.incompatibleImprovements);
}
@@ -198,7 +198,8 @@ final class ClassIntrospectorBuilder implements Cloneable {
/**
* Sets the {@link ZeroArgumentNonVoidMethodPolicy} used for records.
- * The default value is {@link ZeroArgumentNonVoidMethodPolicy#BOTH_PROPERTY_AND_METHOD} if
+ * The default value is
+ * {@link ZeroArgumentNonVoidMethodPolicy#BOTH_METHOD_AND_PROPERTY_UNLESS_BEAN_PROPERTY_READ_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}.
*
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 187d5bc7..607857ce 100644
--- a/freemarker-core/src/main/java/freemarker/ext/beans/ZeroArgumentNonVoidMethodPolicy.java
+++ b/freemarker-core/src/main/java/freemarker/ext/beans/ZeroArgumentNonVoidMethodPolicy.java
@@ -65,7 +65,7 @@ public enum ZeroArgumentNonVoidMethodPolicy {
* 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.
*/
- BOTH_PROPERTY_AND_METHOD,
+ BOTH_METHOD_AND_PROPERTY_UNLESS_BEAN_PROPERTY_READ_METHOD,
/**
* Only {@code obj.m()} gives back the value in a template, {@code obj.m} in a template just gives the method itself.
@@ -75,7 +75,7 @@ public enum ZeroArgumentNonVoidMethodPolicy {
/**
* {@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}.
+ * {@link #BOTH_METHOD_AND_PROPERTY_UNLESS_BEAN_PROPERTY_READ_METHOD}.
*/
- PROPERTY_ONLY
+ PROPERTY_ONLY_UNLESS_BEAN_PROPERTY_READ_METHOD
}
diff --git a/freemarker-core/src/main/java/freemarker/template/MethodCallAwareTemplateHashModel.java b/freemarker-core/src/main/java/freemarker/template/MethodCallAwareTemplateHashModel.java
index bac40768..97173f36 100644
--- a/freemarker-core/src/main/java/freemarker/template/MethodCallAwareTemplateHashModel.java
+++ b/freemarker-core/src/main/java/freemarker/template/MethodCallAwareTemplateHashModel.java
@@ -47,7 +47,8 @@ import freemarker.template.utility.NullArgumentException;
* 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}.
+ * which is needed to implement
+ * {@link ZeroArgumentNonVoidMethodPolicy#BOTH_METHOD_AND_PROPERTY_UNLESS_BEAN_PROPERTY_READ_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. This is a workaround we added to address the issue with accessing
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 ce61d01b..63f60482 100644
--- a/freemarker-core16/src/test/java/freemarker/ext/beans/TestZeroArgumentNonVoidMethodPolicy.java
+++ b/freemarker-core16/src/test/java/freemarker/ext/beans/TestZeroArgumentNonVoidMethodPolicy.java
@@ -75,7 +75,7 @@ public class TestZeroArgumentNonVoidMethodPolicy extends TemplateTest {
() -> {
DefaultObjectWrapper beansWrapper = new DefaultObjectWrapper(VERSION_2_3_32);
beansWrapper.setRecordZeroArgumentNonVoidMethodPolicy(
- ZeroArgumentNonVoidMethodPolicy.BOTH_PROPERTY_AND_METHOD);
+ ZeroArgumentNonVoidMethodPolicy.BOTH_METHOD_AND_PROPERTY_UNLESS_BEAN_PROPERTY_READ_METHOD);
return beansWrapper;
},
cacheTopLevelVars);
@@ -90,7 +90,8 @@ public class TestZeroArgumentNonVoidMethodPolicy extends TemplateTest {
setupDataModel(
() -> {
DefaultObjectWrapper beansWrapper = new DefaultObjectWrapper(VERSION_2_3_32);
- beansWrapper.setRecordZeroArgumentNonVoidMethodPolicy(ZeroArgumentNonVoidMethodPolicy.PROPERTY_ONLY);
+ beansWrapper.setRecordZeroArgumentNonVoidMethodPolicy(
+ ZeroArgumentNonVoidMethodPolicy.PROPERTY_ONLY_UNLESS_BEAN_PROPERTY_READ_METHOD);
return beansWrapper;
},
cacheTopLevelVars);
@@ -105,7 +106,8 @@ public class TestZeroArgumentNonVoidMethodPolicy extends TemplateTest {
setupDataModel(
() -> {
DefaultObjectWrapper beansWrapper = new DefaultObjectWrapper(VERSION_2_3_33);
- beansWrapper.setRecordZeroArgumentNonVoidMethodPolicy(ZeroArgumentNonVoidMethodPolicy.PROPERTY_ONLY);
+ beansWrapper.setRecordZeroArgumentNonVoidMethodPolicy(
+ ZeroArgumentNonVoidMethodPolicy.PROPERTY_ONLY_UNLESS_BEAN_PROPERTY_READ_METHOD);
return beansWrapper;
},
cacheTopLevelVars);
@@ -120,7 +122,8 @@ public class TestZeroArgumentNonVoidMethodPolicy extends TemplateTest {
setupDataModel(
() -> {
DefaultObjectWrapper beansWrapper = new DefaultObjectWrapper(VERSION_2_3_32);
- beansWrapper.setDefaultZeroArgumentNonVoidMethodPolicy(ZeroArgumentNonVoidMethodPolicy.PROPERTY_ONLY);
+ beansWrapper.setDefaultZeroArgumentNonVoidMethodPolicy(
+ ZeroArgumentNonVoidMethodPolicy.PROPERTY_ONLY_UNLESS_BEAN_PROPERTY_READ_METHOD);
return beansWrapper;
},
cacheTopLevelVars);
@@ -136,7 +139,7 @@ public class TestZeroArgumentNonVoidMethodPolicy extends TemplateTest {
() -> {
DefaultObjectWrapper beansWrapper = new DefaultObjectWrapper(VERSION_2_3_33);
beansWrapper.setDefaultZeroArgumentNonVoidMethodPolicy(
- ZeroArgumentNonVoidMethodPolicy.BOTH_PROPERTY_AND_METHOD);
+ ZeroArgumentNonVoidMethodPolicy.BOTH_METHOD_AND_PROPERTY_UNLESS_BEAN_PROPERTY_READ_METHOD);
return beansWrapper;
},
cacheTopLevelVars);
@@ -149,7 +152,10 @@ public class TestZeroArgumentNonVoidMethodPolicy extends TemplateTest {
public void testSettings() throws TemplateException, IOException {
getConfiguration().setSetting(
"objectWrapper",
- "DefaultObjectWrapper(2.3.33, defaultZeroArgumentNonVoidMethodPolicy=freemarker.ext.beans.ZeroArgumentNonVoidMethodPolicy.BOTH_PROPERTY_AND_METHOD)");
+ "DefaultObjectWrapper(2.3.33, "
+ + "defaultZeroArgumentNonVoidMethodPolicy="
+ + "freemarker.ext.beans.ZeroArgumentNonVoidMethodPolicy"
+ + ".BOTH_METHOD_AND_PROPERTY_UNLESS_BEAN_PROPERTY_READ_METHOD)");
setupDataModel(() -> getConfiguration().getObjectWrapper(), false);
assertRecIsBothPropertyAndMethod();
assertNrcIsBothPropertyAndMethod();
diff --git a/freemarker-manual/src/main/docgen/en_US/book.xml b/freemarker-manual/src/main/docgen/en_US/book.xml
index 918c9a8d..9e55a3d0 100644
--- a/freemarker-manual/src/main/docgen/en_US/book.xml
+++ b/freemarker-manual/src/main/docgen/en_US/book.xml
@@ -30269,7 +30269,7 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting>
<literal>ObjectWrapper</literal>, then set its
<literal>incompatible_improvements</literal>, or just its
<literal>recordZeroArgumentNonVoidMethodPolicy</literal>
- property to <literal>BOTH_PROPERTY_AND_METHOD</literal>). If in
+ property to <literal>BOTH_METHOD_AND_PROPERTY_UNLESS_BEAN_PROPERTY_READ_METHOD</literal>). If in
a Java record you have something like <literal>int
price()</literal>, earlier you could only read the value in
templates as <literal>obj.price()</literal>. With this
@@ -30305,8 +30305,8 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting>
Each has enum type
<literal>freemarker.ext.beans.ZeroArgumentNonVoidMethodPolicy</literal>,
that can be <literal>METHOD_ONLY</literal>,
- <literal>PROPERTY_ONLY</literal>, or
- <literal>BOTH_PROPERTY_AND_METHOD</literal>.
+ <literal>PROPERTY_ONLY_UNLESS_BEAN_PROPERTY_READ_METHOD</literal>, or
+ <literal>BOTH_METHOD_AND_PROPERTY_UNLESS_BEAN_PROPERTY_READ_METHOD</literal>.
Therefore:</para>
<itemizedlist>
@@ -30324,7 +30324,7 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting>
<para>For records, you can enforce proper style with
setting
<literal>recordZeroArgumentNonVoidMethodPolicy</literal>
- to <literal>PROPERTY_ONLY</literal>. The default with
+ to <literal>PROPERTY_ONLY_UNLESS_BEAN_PROPERTY_READ_METHOD</literal>. The default with
<literal>incompatible_improvements</literal> 2.3.33 is
more lenient, as there using <literal>()</literal> is
allowed (for backward compatibility, and because people
@@ -30345,7 +30345,7 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting>
instead of
<literal>TemplateHashModel.get("price")</literal>. This is
needed for
- <literal>ZeroArgumentNonVoidMethodPolicy.BOTH_PROPERTY_AND_METHOD</literal>
+ <literal>ZeroArgumentNonVoidMethodPolicy.BOTH_METHOD_AND_PROPERTY_UNLESS_BEAN_PROPERTY_READ_METHOD</literal>
to work.</para>
</listitem>
(freemarker) 01/03: Improved JavaDoc related to ZeroArgumentNonVoidMethodPolicy (mostly).
Posted by dd...@apache.org.
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 <dd...@apache.org>
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;
(freemarker) 03/03: Updated README.md, mostly to describe Gradle build better.
Posted by dd...@apache.org.
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 68e95a39d2794d27510e304077f6fb5e414b4ed6
Author: ddekany <dd...@apache.org>
AuthorDate: Wed May 8 18:31:05 2024 +0200
Updated README.md, mostly to describe Gradle build better.
---
README.md | 121 +++++++++++++++++++++++++++++++-------------------------------
1 file changed, 60 insertions(+), 61 deletions(-)
diff --git a/README.md b/README.md
index ea2fb9d6..f5878b6d 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-Apache FreeMarker {version}
-===========================
+Apache FreeMarkerâ„¢ {version}
+============================
[](https://github.com/apache/freemarker/actions/workflows/ci.yml)
@@ -19,11 +19,11 @@ For contributions that are judged to be non-trivial, you will be asked
to actually signing a Contributor License Agreement.
-What is Apache FreeMarker?
---------------------------
+What is Apache FreeMarkerâ„¢?
+---------------------------
-FreeMarker is a "template engine"; a generic tool to generate text
-output (anything from HTML to auto generated source code) based on
+Apache FreeMarkerâ„¢ is a "template engine"; a generic tool to generate
+text output (anything from HTML to auto generated source code) based on
templates. It's a Java package, a class library for Java programmers.
It's not an application for end-users in itself, but something that
programmers can embed into their products. FreeMarker is designed to
@@ -37,7 +37,7 @@ Licensing
FreeMarker is licensed under the Apache License, Version 2.0.
-See the LICENSE file for more details!
+See the `LICENSE` file for more details!
Documentation
@@ -68,9 +68,9 @@ If you are using Maven, just add this dependency:
```
Otherwise, simply copy `freemarker.jar` to a location where your Java
-application's ClassLoader will find it. For example, if you are using
+application's `ClassLoader` will find it. For example, if you are using
FreeMarker in a web application, you probably want to put
-freemarker.jar into the WEB-INF/lib directory of your web application.
+`freemarker.jar` into the `WEB-INF/lib` directory of your web application.
FreeMarker has no required dependencies. It has several optional
dependencies, but usually you don't have to deal with them, because if
@@ -79,13 +79,13 @@ application already uses the related library.
Attention: If you upgrade to OpenJDK 9 or later, and you are using
XPath queries in templates, you will need to add Apache Xalan as a
-dependency, as freemarker.ext.dom can't use the XPath support
+dependency, as `freemarker.ext.dom` can't use the XPath support
included in OpenJDK anymore. It's not needed on Oracle Java 9,
or if FreeMarker is configured to use Jaxen for XPath.
The minimum required Java version is currently Java SE 8. (The presence
-of a later version may be detected on runtime and utilized by
-FreeMarker.)
+of a later version is detected on runtime and utilized by FreeMarker
+automatically.)
Change log
@@ -95,7 +95,7 @@ Online (for stable releases only):
https://freemarker.apache.org/docs/app_versions.html
Offline:
-In the binary release, open documentation/index.html, and you will find the
+In the binary release, open `documentation/index.html`, and you will find the
link.
@@ -106,8 +106,10 @@ If you haven't yet, download the source release, or checkout FreeMarker from
the source code repository. See repository locations here:
https://freemarker.apache.org/sourcecode.html
-You need JDK 8, JDK 16 and JDK 17 (only for some tests) to be installed
+You need JDK 8, JDK 16, and JDK 17 (only for some tests) to be installed
(and [visible to Gradle](https://docs.gradle.org/current/userguide/toolchains.html)).
+That's because different parts of the source code target different Java versions,
+and Gradle requires the exact JDK version (not higher) for each.
Be sure that your default Java version (which Gradle should use automatically) is at
least 16!
@@ -118,40 +120,68 @@ have to add it yourself! You can download it
[from GitHub source code page](https://github.com/apache/freemarker/tree/2.3-gae/gradle/wrapper)!
(Or, use your own Gradle installation instead of `gradlew`.)
-To build `freemarker.jar`, just issue `./gradlew jar` in the project root directory,
-and it should download all dependencies automatically and build `freemarker.jar`.
+To build `freemarker.jar`, just issue `./gradlew jar` (`./gradlew.bat jar` on Windows) in the
+project root directory, and it should download all dependencies automatically, and build
+`freemarker.jar`.
To run all JUnit tests and some other checks, issue `./gradlew check`. (Avoid the
`test` task, as that will only run the tests of the `core` source set.)
To generate documentation, issue `./gradlew javadoc` and `./gradlew manualOffline`.
+To build the distribution artifacts (the `tgz`-s that people can download), run `./gradlew build`. However,
+for a stable (non-`SNAPSHOT`) version number, you must set up signing, or disable that verification;
+see `gradle.properties` in this project for those!
+
+Reproducible builds: If the resulting `freemarker.jar` is not identical with the official jar, see the build environment
+in the `.buildinfo` file packed into the official source distribution, and also into the Maven "sources" artifact! At
+least with identical Java versions, the resulting `freemarker.jar` meant to match exactly.
+
+
+Maven-related build tasks
+.........................
+
To see how the project would be deployed to Maven Central, issue
`./gradlew publishAllPublicationsToLocalRepository`,
and check the `build/local-deployment` directory.
-To build the distribution artifacts (the `tgz`-s that people can download), run `./gradlew build`.However,
-
-For a stable (non-SNAPSHOT) version number, you will need to set up signing, or disable that verification;
-see `gradle.properties` in this project for those!
+To publish to the Apache Maven Repository (from where you can also promote releases to the Maven Central Repository)
+issue `.\gradlew publish`. Note that for this the following Gradle properties must be properly set
+(in `gradle.properties`, or pass them via `-P<name>=<value>` arguments):
+`freemarker.signMethod`, `freemarker.deploy.apache.user`, `freemarker.deploy.apache.password`.
-See `gradle.properties` for some Gradle properties that you may what to set,
-especially if you are building a release.
-
-Reproducible builds: If the resulting `freemarker.jar` is not identical with the official jar, see the build environment
-in the `.buildinfo` file packed into the official source distribution, and also into the Maven "sources" artifact!
IDE setup
---------
+### IntelliJ IDEA
+
+Originally done on IntelliJ IDEA Community 2023.3.2:
+
+- "File" -> "Open": Select the "settings.gradle.kts" within the freemarker root directory.
+- If the project fails to load (or build), then adjust the following configuration
+ in "File" -> "Settings" -> "Build, Execution, Deployment" -> "Build Tools" -> "Gradle":
+ - Gradle JVM: JDK 16 (or higher)
+ - Build and run using: "Gradle"
+ - Run tests using: "Gradle"
+
+- "File" -> "Settings"
+ - Under "Editor" / "Code style", import and use
+ freemarker/src/ide-settings/IntelliJ-IDEA/Java-code-style-FreeMarker.xml
+ - Under "Editor" / "Inspections", import and use
+ freemarker/src/ide-settings/IntelliJ-IDEA/Editor-Inspections-FreeMarker.xml
+ - Copy the copyright header comment from some of the java files, then
+ under "Editor" / "Copyright" / "Copyright Profiles" click "+", enter "ASL2" as name,
+ then paste the copyright header. Delete the `/*` and ` */` lines, and the ` *`
+ prefixes (to select columns of text, hold Alt while selecting with the mouse.) Then
+ go back to "Copyright" in the tree, and set "Default project copyright" to "ASL2".
+
### Eclipse
-Below you find the step-by-step setup for Eclipse (originally done on Mars.1):
+This section wasn't updated long ago. But you should import the project as any other
+Gradle project. After that, it's recommended to set these preferences (based on Eclipse Mars):
-- Start Eclipse
-- You may prefer to start a new workspace (File -> "Switch workspace"), but
- it's optional.
- Window -> Preferences
- General -> Workspace, set the text file encoding
to "UTF-8". (Or, you can set the same later on project level instead.)
@@ -186,15 +216,6 @@ Below you find the step-by-step setup for Eclipse (originally done on Mars.1):
- Project -> Properties -> Java Compiler
- In Errors/Warnings, check in "Enable project specific settings", then set
"Forbidden reference (access rules)" from "Error" to "Warning".
-- You will still have errors on these java files (because different java
- files depend on different versions of the same library, and Eclipse can't
- handle that). Exclude those java files from the Build Path (in the Package
- Explorer, right-click on the problematic file -> "Build Path" -> "Exclude"):
- _Jython20*.java,
- _Jython22*.java,
- _FreeMarkerPageContext2.java,
- FreeMarkerJspFactory2.java,
- Also, close these files if they are open. Now you shouldn't have any errors.
- At Project -> Properties -> Java Code Style -> Formatter, check in "Enable
project specific settings", and then select "FreeMarker" as active profile.
- At Project -> Properties -> Java Editor -> Save Actions, check "Enable project
@@ -203,7 +224,7 @@ Below you find the step-by-step setup for Eclipse (originally done on Mars.1):
last should contain "Add missing @Override annotations",
"Add missing @Override annotations to implementations of interface methods",
"Add missing @Deprecated annotations", and "Remove unnecessary cast").
-- Right click on the project -> Run As -> JUnit Test
+- Right-click on the project -> Run As -> JUnit Test
It should run without problems (all green).
- It's highly recommended to use the Eclipse FindBugs plugin.
- Install it from Eclipse Marketplace (3.0.1 as of this writing)
@@ -213,25 +234,3 @@ Below you find the step-by-step setup for Eclipse (originally done on Mars.1):
- Project -> Properties -> FindBugs -> [x] Run Automatically
- There should 0 errors. But sometimes the plugin fails to take the
@SuppressFBWarnings annotations into account; then use Project -> Clean.
-
-### IntelliJ IDEA
-
-Originally done on IntelliJ IDEA Community 2023.3.2:
-
-- "File" -> "Open": Select the "settings.gradle.kts" within the freemarker root directory.
-- If the project fails to load (or build), then adjust the following configuration
- in "File" -> "Settings" -> "Build, Execution, Deployment" -> "Build Tools" -> "Gradle":
- - Gradle JVM: JDK 16 (or higher)
- - Build and run using: "Gradle"
- - Run tests using: "Gradle"
-
-- "File" -> "Settings"
- - Under "Editor" / "Code style", import and use
- freemarker/src/ide-settings/IntelliJ-IDEA/Java-code-style-FreeMarker.xml
- - Under "Editor" / "Inspections", import and use
- freemarker/src/ide-settings/IntelliJ-IDEA/Editor-Inspections-FreeMarker.xml
- - Copy the copyright header comment from some of the java files, then
- under "Editor" / "Copyright" / "Copyright Profiles" click "+", enter "ASL2" as name,
- then paste the copyright header. Delete the `/*` and ` */` lines, and the ` *`
- prefixes (to select columns of text, hold Alt while selecting with the mouse.) Then
- go back to "Copyright" in the tree, and set "Default project copyright" to "ASL2".