diff --git a/kilo-client/src/main/java/org/httprpc/kilo/beans/BeanAdapter.java b/kilo-client/src/main/java/org/httprpc/kilo/beans/BeanAdapter.java index f0eb311a..9ccb5255 100644 --- a/kilo-client/src/main/java/org/httprpc/kilo/beans/BeanAdapter.java +++ b/kilo-client/src/main/java/org/httprpc/kilo/beans/BeanAdapter.java @@ -46,6 +46,7 @@ import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; @@ -53,7 +54,6 @@ import java.util.TreeMap; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; import static org.httprpc.kilo.util.Optionals.*; @@ -65,10 +65,12 @@ public class BeanAdapter extends AbstractMap { * Represents a bean property or record component. */ public static class Property { - private Method accessor = null; - private Method mutator = null; + private Method accessor; + private Method mutator; - private Property() { + private Property(Method accessor, Method mutator) { + this.accessor = accessor; + this.mutator = mutator; } /** @@ -1189,7 +1191,8 @@ public static Map getProperties(Class type) { } private static Map computeProperties(Class type) { - var properties = new HashMap(); + var accessors = new HashMap(); + var mutatorMap = new HashMap>(); if (type.isRecord()) { var recordComponents = type.getRecordComponents(); @@ -1197,11 +1200,7 @@ private static Map computeProperties(Class type) { for (var i = 0; i < recordComponents.length; i++) { var recordComponent = recordComponents[i]; - var property = new Property(); - - property.accessor = recordComponent.getAccessor(); - - properties.put(recordComponent.getName(), property); + accessors.put(recordComponent.getName(), recordComponent.getAccessor()); } } else { var methods = type.getMethods(); @@ -1219,39 +1218,42 @@ private static Map computeProperties(Class type) { continue; } - var property = properties.computeIfAbsent(propertyName, key -> new Property()); - if (method.getParameterCount() == 0) { - property.accessor = method; - } else if (property.mutator == null) { - property.mutator = method; + accessors.put(propertyName, method); } else { - throw new UnsupportedOperationException("Duplicate mutator."); + mutatorMap.computeIfAbsent(propertyName, key -> new LinkedList<>()).add(method); } } } - return java.util.Collections.unmodifiableMap(properties.entrySet().stream().peek(entry -> { - var value = entry.getValue(); + var properties = new TreeMap(); + + for (var entry : accessors.entrySet()) { + var propertyName = entry.getKey(); - var accessor = value.getAccessor(); + var accessor = entry.getValue(); - if (accessor == null) { - throw new UnsupportedOperationException("Missing accessor."); + var propertyType = accessor.getReturnType(); + + var mutatorList = mutatorMap.get(propertyName); + + Method mutator; + if (mutatorList != null) { + mutator = mutatorList.stream() + .filter(method -> method.getParameterTypes()[0] == propertyType) + .findFirst().orElse(null); + } else { + mutator = null; } - var mutator = value.getMutator(); + var key = getKey(accessor, propertyName); - if (mutator != null && !accessor.getGenericReturnType().equals(mutator.getGenericParameterTypes()[0])) { - throw new UnsupportedOperationException("Property type mismatch."); + if (properties.put(key, new Property(accessor, mutator)) != null) { + throw new UnsupportedOperationException("Duplicate name."); } - }).collect(Collectors.toMap(entry -> { - var accessor = entry.getValue().getAccessor(); + } - return getKey(accessor, entry.getKey()); - }, Map.Entry::getValue, (v1, v2) -> { - throw new UnsupportedOperationException("Duplicate name."); - }, TreeMap::new))); + return java.util.Collections.unmodifiableMap(properties); } private static String getPropertyName(Method method) { diff --git a/kilo-client/src/test/java/org/httprpc/kilo/beans/BeanAdapterTest.java b/kilo-client/src/test/java/org/httprpc/kilo/beans/BeanAdapterTest.java index 37e74790..00505462 100644 --- a/kilo-client/src/test/java/org/httprpc/kilo/beans/BeanAdapterTest.java +++ b/kilo-client/src/test/java/org/httprpc/kilo/beans/BeanAdapterTest.java @@ -59,28 +59,6 @@ public int getY() { } } - public static class MissingAccessor { - public void setX(int value) { - // No-op - } - } - - public static class DuplicateMutator { - private double x; - - public double getX() { - return x; - } - - public void setX(double x) { - this.x = x; - } - - public void setX(String x) { - // No-op - } - } - public static class DuplicateName { @Name("x") public String getFoo() { @@ -93,16 +71,6 @@ public String getBar() { } } - public static class PropertyTypeMismatch { - public int getX() { - return 0; - } - - public void setX(String x) { - // No-op - } - } - public interface DefaultMethod { int getX(); @@ -498,19 +466,6 @@ public String toString() { assertEquals(map2.toString(), nestedBean2.toString()); } - @Test - public void testDefaultMethod() { - var defaultMethod = BeanAdapter.coerce(mapOf( - entry("x", 1) - ), DefaultMethod.class); - - assertEquals(2, defaultMethod.getY()); - - var beanAdapter = new BeanAdapter(defaultMethod); - - assertEquals(2, beanAdapter.get("z")); - } - @Test @SuppressWarnings("unchecked") public void testRequired() { @@ -630,23 +585,21 @@ public void testReadOnly() { assertEquals(100, readOnly.getY()); } - @Test - public void testMissingAccessor() { - assertThrows(UnsupportedOperationException.class, () -> new BeanAdapter(new MissingAccessor())); - } - - @Test - public void testDuplicateMutator() { - assertThrows(UnsupportedOperationException.class, () -> new BeanAdapter(new DuplicateMutator())); - } - @Test public void testDuplicateName() { assertThrows(UnsupportedOperationException.class, () -> new BeanAdapter(new DuplicateName())); } @Test - public void testPropertyTypeMismatch() { - assertThrows(UnsupportedOperationException.class, () -> new BeanAdapter(new PropertyTypeMismatch())); + public void testDefaultMethod() { + var defaultMethod = BeanAdapter.coerce(mapOf( + entry("x", 1) + ), DefaultMethod.class); + + assertEquals(2, defaultMethod.getY()); + + var beanAdapter = new BeanAdapter(defaultMethod); + + assertEquals(2, beanAdapter.get("z")); } }