This is an automated email from the ASF dual-hosted git repository.
chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git
The following commit(s) were added to refs/heads/main by this push:
new 827c62eb6 feat(java): bean encoder implemented interfaces honor
`@Ignore` (#2576)
827c62eb6 is described below
commit 827c62eb6477b613084df58a39cd223b3b6c2d59
Author: Steven Schlansker <[email protected]>
AuthorDate: Wed Sep 3 20:44:26 2025 -0700
feat(java): bean encoder implemented interfaces honor `@Ignore` (#2576)
this allows you to mark methods as not properties, for example if you
have a computed value with a `default` method
---
.../java/org/apache/fory/annotation/Ignore.java | 4 +-
.../main/java/org/apache/fory/type/Descriptor.java | 3 +-
.../fory/format/encoder/RowEncoderBuilder.java | 35 ++++++++++++-
.../format/encoder/ImplementInterfaceTest.java | 58 ++++++++++++++++++++++
4 files changed, 96 insertions(+), 4 deletions(-)
diff --git
a/java/fory-core/src/main/java/org/apache/fory/annotation/Ignore.java
b/java/fory-core/src/main/java/org/apache/fory/annotation/Ignore.java
index 1234e2055..25e308579 100644
--- a/java/fory-core/src/main/java/org/apache/fory/annotation/Ignore.java
+++ b/java/fory-core/src/main/java/org/apache/fory/annotation/Ignore.java
@@ -24,7 +24,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-/** Ignore fields just like transient. */
+/** Ignore properties just like transient. */
@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.FIELD)
+@Target({ElementType.FIELD, ElementType.METHOD})
public @interface Ignore {}
diff --git a/java/fory-core/src/main/java/org/apache/fory/type/Descriptor.java
b/java/fory-core/src/main/java/org/apache/fory/type/Descriptor.java
index 927ffda51..06d09a5d5 100644
--- a/java/fory-core/src/main/java/org/apache/fory/type/Descriptor.java
+++ b/java/fory-core/src/main/java/org/apache/fory/type/Descriptor.java
@@ -462,7 +462,8 @@ public class Descriptor {
for (Method method : clazz.getMethods()) {
if (method.getParameterCount() == 0
&& method.getReturnType() != void.class
- && !Modifier.isStatic(method.getModifiers())) {
+ && !Modifier.isStatic(method.getModifiers())
+ && !method.isAnnotationPresent(Ignore.class)) {
descriptorMap.put(method, new Descriptor(method));
}
}
diff --git
a/java/fory-format/src/main/java/org/apache/fory/format/encoder/RowEncoderBuilder.java
b/java/fory-format/src/main/java/org/apache/fory/format/encoder/RowEncoderBuilder.java
index f24da862d..6a3f59922 100644
---
a/java/fory-format/src/main/java/org/apache/fory/format/encoder/RowEncoderBuilder.java
+++
b/java/fory-format/src/main/java/org/apache/fory/format/encoder/RowEncoderBuilder.java
@@ -22,10 +22,14 @@ package org.apache.fory.format.encoder;
import static org.apache.fory.type.TypeUtils.CLASS_TYPE;
import static org.apache.fory.type.TypeUtils.getRawType;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.SortedMap;
import org.apache.arrow.vector.types.pojo.Field;
@@ -335,11 +339,19 @@ public class RowEncoderBuilder extends
BaseBinaryEncoderBuilder {
implClass.implementsInterfaces(implClass.type(beanClass));
implClass.addField(true, implClass.type(BinaryRow.class), "row", null);
+ Map<String, Map<MethodType, Method>> methodsNeedingImpl = new HashMap<>();
+ for (Method m : beanClass.getMethods()) {
+ methodsNeedingImpl
+ .computeIfAbsent(m.getName(), x -> new HashMap<>())
+ .put(MethodType.methodType(m.getReturnType(),
m.getParameterTypes()), m);
+ }
+
int numFields = schema.getFields().size();
for (int i = 0; i < numFields; i++) {
Literal ordinal = Literal.ofInt(i);
Descriptor d =
getDescriptorByFieldName(schema.getFields().get(i).getName());
TypeRef<?> fieldType = d.getTypeRef();
+ Class<?> rawFieldType = fieldType.getRawType();
Expression.Reference decodeValue =
new Expression.Reference(decodeMethodName(i) + "(row)", fieldType);
@@ -348,7 +360,6 @@ public class RowEncoderBuilder extends
BaseBinaryEncoderBuilder {
getterImpl = new Expression.Return(decodeValue);
} else {
String fieldName = "f" + i + "_" + d.getName();
- Class<?> rawFieldType = fieldType.getRawType();
implClass.addField(false, ctx.type(rawFieldType), fieldName,
nullValue(fieldType));
Expression fieldRef = new Expression.Reference(fieldName, fieldType,
true);
@@ -381,12 +392,34 @@ public class RowEncoderBuilder extends
BaseBinaryEncoderBuilder {
}
getterImpl = new Expression.ListExpression(assigner, new
Expression.Return(fieldRef));
}
+ methodsNeedingImpl
+ .getOrDefault(d.getName(), new HashMap<>())
+ .remove(MethodType.methodType(rawFieldType));
implClass.addMethod(
d.getName(), getterImpl.genCode(implClass).code(),
fieldType.getRawType());
}
// Note: adding constructor captures init code, so must happen after all
fields are collected
implClass.addConstructor("this.row = row;", BinaryRow.class, "row");
+ methodsNeedingImpl.forEach(
+ (methodName, signatures) ->
+ signatures.forEach(
+ (methodType, method) -> {
+ if (method.isDefault()) {
+ return;
+ }
+ Object[] params = new Object[methodType.parameterCount() *
2];
+ for (int i = 0; i < methodType.parameterCount(); i++) {
+ params[i * 2] = methodType.parameterType(i);
+ params[i * 2 + 1] = "unused" + i;
+ }
+ implClass.addMethod(
+ methodName,
+ "throw new UnsupportedOperationException();",
+ methodType.returnType(),
+ params);
+ }));
+
return implClass;
}
diff --git
a/java/fory-format/src/test/java/org/apache/fory/format/encoder/ImplementInterfaceTest.java
b/java/fory-format/src/test/java/org/apache/fory/format/encoder/ImplementInterfaceTest.java
index c054edeab..265a23982 100644
---
a/java/fory-format/src/test/java/org/apache/fory/format/encoder/ImplementInterfaceTest.java
+++
b/java/fory-format/src/test/java/org/apache/fory/format/encoder/ImplementInterfaceTest.java
@@ -29,6 +29,7 @@ import java.util.TreeSet;
import lombok.Data;
import org.apache.arrow.vector.types.pojo.Field;
import org.apache.fory.annotation.ForyField;
+import org.apache.fory.annotation.Ignore;
import org.apache.fory.format.row.binary.BinaryArray;
import org.apache.fory.format.row.binary.BinaryRow;
import org.apache.fory.format.type.DataTypes;
@@ -439,4 +440,61 @@ public class ImplementInterfaceTest {
Assert.assertEquals(deserializedBean.f1().get(2).f1(), 42);
Assert.assertEquals(deserializedBean.f1().get(3), null);
}
+
+ public interface IgnoredMethods {
+ int f1();
+
+ @Ignore
+ default int doubled() {
+ return doubled(f1());
+ }
+
+ IgnoredMethods withF1(int f1);
+
+ private int doubled(final int n) {
+ return n * 2;
+ }
+ }
+
+ static class IgnoredMethodsImpl implements IgnoredMethods {
+ private final int f1;
+
+ IgnoredMethodsImpl(final int f1) {
+ this.f1 = f1;
+ }
+
+ @Override
+ public int f1() {
+ return f1;
+ }
+
+ @Override
+ public int doubled() {
+ return f1() * 3;
+ }
+
+ @Override
+ public IgnoredMethods withF1(final int f1) {
+ return new IgnoredMethodsImpl(f1);
+ }
+ }
+
+ @Test
+ public void testIgnoredMethods() {
+ final IgnoredMethods bean1 = new IgnoredMethodsImpl(2112);
+ final RowEncoder<IgnoredMethods> encoder =
Encoders.bean(IgnoredMethods.class);
+ Assert.assertEquals(encoder.schema().getFields().size(), 1);
+ final BinaryRow row = encoder.toRow(bean1);
+ final MemoryBuffer buffer = MemoryUtils.wrap(row.toBytes());
+ row.pointTo(buffer, 0, buffer.size());
+ final IgnoredMethods deserializedBean = encoder.fromRow(row);
+ Assert.assertEquals(deserializedBean.f1(), 2112);
+ Assert.assertEquals(deserializedBean.doubled(), 2112 * 2);
+
+ try {
+ deserializedBean.withF1(100);
+ Assert.fail();
+ } catch (final UnsupportedOperationException expected) {
+ }
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]