Using Reflection you can:
- Invoke
static newBuilder method to create builder.
- Find all
fields/setters for given POJO class. If in all cases setters on builder and setters on POJO have the same name like in your example it should be easy to convert.
- Invoke
build on builder instance to create new POJO.
Using basic Java Reflection and Stream API-s it could look like below:
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Properties;
import java.util.StringJoiner;
import java.util.function.Function;
import java.util.stream.Stream;
public class ReflectionApp {
public static void main(String[] args) throws Exception {
Properties properties = new Properties();
properties.put("name", "John");
properties.put("date", "today");
AvroPropertie manualInstance = AvroPropertie.newBuilder()
.setName(properties.getProperty("name"))
.setDate(properties.getProperty("date"))
.build();
Object dynamicInstance = AvroAutoCoder.createAndSet(AvroPropertie.class, properties::getProperty);
System.out.println(manualInstance);
System.out.println(dynamicInstance);
System.out.println("manualInstance == dynamicInstance => " + manualInstance.equals(dynamicInstance));
}
}
class AvroAutoCoder {
public static Object createAndSet(Class clazz, Function<String, String> dataSupplier) throws Exception {
Object builderInstance = findMethod(clazz, "newBuilder")
.invoke(null);
Class<?> builderClass = builderInstance.getClass();
getSetters(clazz).forEach(setter -> {
try {
String fieldName = setter.getName().substring(3).toLowerCase();
findMethod(builderClass, setter.getName())
.invoke(builderInstance, dataSupplier.apply(fieldName));
} catch (Exception e) {
throw new IllegalStateException(e);
}
});
return findMethod(builderClass, "build")
.invoke(builderInstance);
}
private static Method findMethod(Class clazz, String methodName) {
return Arrays.stream(clazz.getDeclaredMethods())
.filter(method -> method.getName().equals(methodName))
.findFirst()
.orElseThrow(IllegalArgumentException::new);
}
private static Stream<Method> getSetters(Class clazz) {
return Arrays.stream(clazz.getDeclaredMethods())
.filter(method -> method.getParameterCount() == 1 && method.getReturnType() == Void.TYPE)
.filter(method -> method.getName().startsWith("set"));
}
}
Above code prints:
AvroPropertie[name='John', date='today']
AvroPropertie[name='John', date='today']
manualInstance == dynamicInstance => true
Comment added by the Author of question:
In this specific example class APropertie has a static inner class named Builder which holds the wanted setters. In this case the getSetters() methods needs to be changed a little bit:
// Find all setter of class
private static Stream<Method> getSetters2(Class clazz) {
Optional<Class> first = Arrays.stream(clazz.getDeclaredClasses())
.findFirst();
return Arrays.stream(first.get().getDeclaredMethods())
.filter(method -> method.getName().startsWith("set"));
}