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]

Reply via email to