From f611068a036807b27f15d07209236d0ca7eedf59 Mon Sep 17 00:00:00 2001 From: rob bygrave Date: Thu, 3 Sep 2020 14:45:27 +1200 Subject: [PATCH] #2050 - Refactor internals - remove dependency on java.beans --- .../server/dto/DtoMetaBuilder.java | 51 +++++--- .../server/dto/DtoMetaProperty.java | 16 +-- .../server/dto/DtoMetaBuilderTest.java | 120 ++++++++++++++++++ 3 files changed, 155 insertions(+), 32 deletions(-) create mode 100644 src/test/java/io/ebeaninternal/server/dto/DtoMetaBuilderTest.java diff --git a/src/main/java/io/ebeaninternal/server/dto/DtoMetaBuilder.java b/src/main/java/io/ebeaninternal/server/dto/DtoMetaBuilder.java index d1c249f85..bc5f900ed 100644 --- a/src/main/java/io/ebeaninternal/server/dto/DtoMetaBuilder.java +++ b/src/main/java/io/ebeaninternal/server/dto/DtoMetaBuilder.java @@ -4,11 +4,9 @@ import io.ebeaninternal.server.type.TypeManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.beans.BeanInfo; -import java.beans.IntrospectionException; -import java.beans.Introspector; -import java.beans.PropertyDescriptor; import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; @@ -34,32 +32,49 @@ class DtoMetaBuilder { this.typeManager = typeManager; } - public DtoMeta build() throws IntrospectionException { - + DtoMeta build() { readConstructors(); readProperties(); - return new DtoMeta(dtoType, constructorList, properties); } - private void readProperties() throws IntrospectionException { - - BeanInfo beanInfo = Introspector.getBeanInfo(dtoType); - for (PropertyDescriptor propertyDescriptor : beanInfo.getPropertyDescriptors()) { - if (include(propertyDescriptor)) { + private void readProperties() { + for (Method method : dtoType.getMethods()) { + if (includeMethod(method)) { try { - properties.add(new DtoMetaProperty(typeManager, propertyDescriptor, dtoType)); + final String name = propertyName(method.getName()); + final Class propertyType = propertyType(method); + properties.add(new DtoMetaProperty(typeManager, dtoType, method, name, propertyType)); } catch (Exception e) { - log.debug("exclude on " + dtoType + " property " + propertyDescriptor.getName(), e); + log.debug("exclude on " + dtoType + " method " + method, e); } } } } + static Class propertyType(Method method) { + return method.getParameterTypes()[0]; + } + + static String propertyName(String methodName) { + final String name = methodName.substring(3); + return Character.toLowerCase(name.charAt(0)) + name.substring(1); + } + + /** + * Include a public "setter" method - 1 argument, returns void. + */ + static boolean includeMethod(Method method) { + final int modifiers = method.getModifiers(); + return Modifier.isPublic(modifiers) + && !Modifier.isStatic(modifiers) + && Void.TYPE.equals(method.getReturnType()) + && method.getParameterTypes().length == 1 + && method.getName().startsWith("set") && method.getName().length() > 3; + } + private void readConstructors() { - Constructor[] constructors = dtoType.getConstructors(); - for (Constructor constructor : constructors) { try { constructorList.add(new DtoMetaConstructor(typeManager, constructor, dtoType)); @@ -70,8 +85,4 @@ class DtoMetaBuilder { } } - private boolean include(PropertyDescriptor property) { - return !property.getName().equals("class"); - } - } diff --git a/src/main/java/io/ebeaninternal/server/dto/DtoMetaProperty.java b/src/main/java/io/ebeaninternal/server/dto/DtoMetaProperty.java index 69182e08b..d177f22d3 100644 --- a/src/main/java/io/ebeaninternal/server/dto/DtoMetaProperty.java +++ b/src/main/java/io/ebeaninternal/server/dto/DtoMetaProperty.java @@ -4,7 +4,6 @@ import io.ebeaninternal.server.type.DataReader; import io.ebeaninternal.server.type.ScalarType; import io.ebeaninternal.server.type.TypeManager; -import java.beans.PropertyDescriptor; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -20,19 +19,12 @@ class DtoMetaProperty implements DtoReadSet { private final MethodHandle setter; private final ScalarType scalarType; - DtoMetaProperty(TypeManager typeManager, PropertyDescriptor descriptor, Class dtoType) throws IllegalAccessException, NoSuchMethodException { - + DtoMetaProperty(TypeManager typeManager, Class dtoType, Method writeMethod, String name, Class propertyType) throws IllegalAccessException, NoSuchMethodException { this.dtoType = dtoType; - this.name = descriptor.getName(); - - Method writeMethod = descriptor.getWriteMethod(); + this.name = name; if (writeMethod != null) { - - Class propertyType = descriptor.getPropertyType(); - this.setter = LOOKUP.findVirtual(dtoType, writeMethod.getName(), MethodType.methodType(void.class, propertyType)); this.scalarType = typeManager.getScalarType(propertyType); - } else { this.scalarType = null; this.setter = null; @@ -58,11 +50,11 @@ class DtoMetaProperty implements DtoReadSet { try { setter.invoke(instance, arg); } catch (Throwable e) { - throw new RuntimeException("Error calling setter for property " + fullname() + " with arg: " + arg, e); + throw new RuntimeException("Error calling setter for property " + fullName() + " with arg: " + arg, e); } } - private String fullname() { + private String fullName() { return dtoType.getName() + "." + name; } diff --git a/src/test/java/io/ebeaninternal/server/dto/DtoMetaBuilderTest.java b/src/test/java/io/ebeaninternal/server/dto/DtoMetaBuilderTest.java new file mode 100644 index 000000000..2cf97ca85 --- /dev/null +++ b/src/test/java/io/ebeaninternal/server/dto/DtoMetaBuilderTest.java @@ -0,0 +1,120 @@ +package io.ebeaninternal.server.dto; + +import org.junit.Test; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DtoMetaBuilderTest { + + @Test + public void includeMethod() { + Map methods = getIncludedMethodsFor(D0.class); + + assertThat(methods).hasSize(2); + assertThat(methods.get("setName")).isNotNull(); + assertThat(methods.get("setId")).isNotNull(); + } + + @Test + public void includeMethod_when_notStrictlySetters() { + Map methods = getIncludedMethodsFor(D1.class); + + assertThat(methods).hasSize(3); + assertThat(methods.get("setNameThen")).isNotNull(); + assertThat(methods.get("setIdFor")).isNotNull(); + assertThat(methods.get("setI")).isNotNull(); + } + + @Test + public void propertyType() { + Map methods = getIncludedMethodsFor(D0.class); + + assertThat(methods).hasSize(2); + assertThat(DtoMetaBuilder.propertyType(methods.get("setName"))).isEqualTo(String.class); + assertThat(DtoMetaBuilder.propertyType(methods.get("setId"))).isEqualTo(long.class); + } + + @Test + public void propertyName() { + + assertThat(DtoMetaBuilder.propertyName("setName")).isEqualTo("name"); + assertThat(DtoMetaBuilder.propertyName("setId")).isEqualTo("id"); + assertThat(DtoMetaBuilder.propertyName("setI")).isEqualTo("i"); + assertThat(DtoMetaBuilder.propertyName("setfoo")).isEqualTo("foo"); + } + + + private Map getIncludedMethodsFor(Class cls) { + Map included = new HashMap<>(); + for (Method method : cls.getMethods()) { + if (DtoMetaBuilder.includeMethod(method)) { + included.put(method.getName(), method); + } + } + return included; + } + + @SuppressWarnings("unused") + static class D0 { + private String name; + private long id; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public void setId(long id) { + this.id = id; + } + + public void setNamePlus(String name, long id) { + this.name = name; + this.id = id; + } + + public static void setFoo(String foo) { + } + + protected void setProtected(String foo) { + } + + private void setPrivate(String foo) { + } + + private void setPackage(String foo) { + } + } + + + @SuppressWarnings("unused") + static class D1 { + + public void setNameThen(String name) { + + } + + public void setIdFor(long id) { + + } + + public void setI(long val) { + + } + + public void set(long val) { + + } + + public D1 setA(long val) { + return this; + } + } +}