[
https://issues.apache.org/jira/browse/FREEMARKER-183?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17748976#comment-17748976
]
Simon edited comment on FREEMARKER-183 at 7/30/23 11:45 PM:
------------------------------------------------------------
It seems to work if I add this to the end of
ClassIntrospector.addBeanInfoToClassIntrospectionData:
{code:java}
if (isRecordType(clazz)) {
Method[] accessors = RecordAccessor.instance().getAccessors(clazz);
for (Method accessor : accessors) {
if (effClassMemberAccessPolicy.isMethodExposed(accessor)) {
introspData.put(accessor.getName(), new
FastPropertyDescriptor(accessor, null));
}
}
}{code}
We have this at the end so that it overwrites these having been found as
methods first.
Because they're now defined as properties, you can no longer invoke them as
methods.
isRecordType:
{code:java}
private static boolean isRecordType(Class<?> clazz) {
Class<?> parent = clazz.getSuperclass();
return (parent != null) && "java.lang.Record".equals(parent.getName());
}{code}
RecordAccessor:
{code:java}
public class RecordAccessor {
private final Method RECORD_GET_RECORD_COMPONENTS;
private final Method RECORD_COMPONENT_GET_ACCESSOR;
private final static RecordAccessor INSTANCE;
private final static RuntimeException PROBLEM;
static {
RuntimeException prob = null;
RecordAccessor inst = null;
try {
inst = new RecordAccessor();
} catch (RuntimeException e) {
prob = e;
}
INSTANCE = inst;
PROBLEM = prob;
}
private RecordAccessor() throws RuntimeException {
try {
RECORD_GET_RECORD_COMPONENTS =
Class.class.getMethod("getRecordComponents");
Class<?> c = Class.forName("java.lang.reflect.RecordComponent");
RECORD_COMPONENT_GET_ACCESSOR = c.getMethod("getAccessor");
} catch (Exception e) {
throw new RuntimeException(String.format(
"Failed to access Methods needed to support
`java.lang.Record`: (%s) %s",
e.getClass().getName(), e.getMessage()), e);
}
}
public static RecordAccessor instance() {
if (PROBLEM != null) {
throw PROBLEM;
}
return INSTANCE;
}
private Object[] recordComponents(Class<?> recordType) throws
IllegalArgumentException {
try {
return (Object[]) RECORD_GET_RECORD_COMPONENTS.invoke(recordType);
} catch (Exception e) {
throw new IllegalArgumentException("Failed to access
RecordComponents of type "
+ recordType.getName());
}
}
public Method[] getAccessors(Class<?> recordType) throws
IllegalArgumentException {
final Object[] components = recordComponents(recordType);
if (components == null) {
// not a record, or no reflective access on native image
return null;
}
final Method[] accessors = new Method[components.length];
for (int i = 0; i < components.length; i++) {
try {
accessors[i] = (Method)
RECORD_COMPONENT_GET_ACCESSOR.invoke(components[i]);
} catch (Exception e) {
throw new IllegalArgumentException(String.format(
"Failed to access name of field #%d (of %d) of Record
type %s",
i, components.length, recordType.getName()), e);
}
}
return accessors;
}
}{code}
P.S. This code is adapted from
[Jackson|https://github.com/FasterXML/jackson-databind] (see JDK14Util.java and
ClassUtil.java)
was (Author: JIRAUSER301336):
It seems to work if I add this to the end of
ClassIntrospector.addBeanInfoToClassIntrospectionData:
{code:java}
if (isRecordType(clazz)) {
Method[] accessors = RecordAccessor.instance().getAccessors(clazz);
for (Method accessor : accessors) {
if (effClassMemberAccessPolicy.isMethodExposed(accessor)) {
introspData.put(accessor.getName(), new
FastPropertyDescriptor(accessor, null));
}
}
}{code}
We have this at the end so that it overwrites these having been found as a
methods first.
Because they're now defined as properties, you can no longer invoke them as
methods.
isRecordType:
{code:java}
private static boolean isRecordType(Class<?> clazz) {
Class<?> parent = clazz.getSuperclass();
return (parent != null) && "java.lang.Record".equals(parent.getName());
}{code}
RecordAccessor:
{code:java}
public class RecordAccessor {
private final Method RECORD_GET_RECORD_COMPONENTS;
private final Method RECORD_COMPONENT_GET_ACCESSOR;
private final static RecordAccessor INSTANCE;
private final static RuntimeException PROBLEM;
static {
RuntimeException prob = null;
RecordAccessor inst = null;
try {
inst = new RecordAccessor();
} catch (RuntimeException e) {
prob = e;
}
INSTANCE = inst;
PROBLEM = prob;
}
private RecordAccessor() throws RuntimeException {
try {
RECORD_GET_RECORD_COMPONENTS =
Class.class.getMethod("getRecordComponents");
Class<?> c = Class.forName("java.lang.reflect.RecordComponent");
RECORD_COMPONENT_GET_ACCESSOR = c.getMethod("getAccessor");
} catch (Exception e) {
throw new RuntimeException(String.format(
"Failed to access Methods needed to support
`java.lang.Record`: (%s) %s",
e.getClass().getName(), e.getMessage()), e);
}
}
public static RecordAccessor instance() {
if (PROBLEM != null) {
throw PROBLEM;
}
return INSTANCE;
}
private Object[] recordComponents(Class<?> recordType) throws
IllegalArgumentException {
try {
return (Object[]) RECORD_GET_RECORD_COMPONENTS.invoke(recordType);
} catch (Exception e) {
throw new IllegalArgumentException("Failed to access
RecordComponents of type "
+ recordType.getName());
}
}
public Method[] getAccessors(Class<?> recordType) throws
IllegalArgumentException {
final Object[] components = recordComponents(recordType);
if (components == null) {
// not a record, or no reflective access on native image
return null;
}
final Method[] accessors = new Method[components.length];
for (int i = 0; i < components.length; i++) {
try {
accessors[i] = (Method)
RECORD_COMPONENT_GET_ACCESSOR.invoke(components[i]);
} catch (Exception e) {
throw new IllegalArgumentException(String.format(
"Failed to access name of field #%d (of %d) of Record
type %s",
i, components.length, recordType.getName()), e);
}
}
return accessors;
}
}{code}
P.S. This code is adapted from
[Jackson|https://github.com/FasterXML/jackson-databind] (see JDK14Util.java and
ClassUtil.java)
> Add support for Java records
> ----------------------------
>
> Key: FREEMARKER-183
> URL: https://issues.apache.org/jira/browse/FREEMARKER-183
> Project: Apache Freemarker
> Issue Type: Task
> Reporter: Dániel Dékány
> Assignee: Dániel Dékány
> Priority: Major
>
> Currently we don't support records (JEP 395), which was finalized in Java 16.
> Users can extend {{DefaultObjectWrapper}} for that of course, but it should
> be supported out of the box.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)