From af6cf5817c63ded4ed0bd3a65b62b3acf0f3022b Mon Sep 17 00:00:00 2001 From: James Baker Date: Mon, 11 Jan 2021 10:50:03 +0000 Subject: [PATCH 1/2] Fixes #467 by looping through all interfaces and superclasses, rather than just direct parents Signed-off-by: James Baker --- .../yasson/internal/ComponentMatcher.java | 61 +++++++++++++++++-- .../yasson/internal/ComponentMatcherTest.java | 40 ++++++++++++ 2 files changed, 95 insertions(+), 6 deletions(-) create mode 100644 src/test/java/org/eclipse/yasson/internal/ComponentMatcherTest.java diff --git a/src/main/java/org/eclipse/yasson/internal/ComponentMatcher.java b/src/main/java/org/eclipse/yasson/internal/ComponentMatcher.java index 1db2f5e57..afd84c80e 100644 --- a/src/main/java/org/eclipse/yasson/internal/ComponentMatcher.java +++ b/src/main/java/org/eclipse/yasson/internal/ComponentMatcher.java @@ -15,11 +15,15 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.Function; +import java.util.stream.Collectors; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.adapter.JsonbAdapter; @@ -206,7 +210,7 @@ private Optional searchComponentBinding( if (runtimeType instanceof Class) { Class runtimeClass = (Class) runtimeType; // Check if any interfaces have a match - for (Class ifc : runtimeClass.getInterfaces()) { + for (Class ifc : getInterfaces(runtimeClass)) { ComponentBindings ifcBinding = userComponents.get(ifc); if (ifcBinding != null) { Optional match = getMatchingBinding(ifc, ifcBinding, supplier); @@ -217,17 +221,62 @@ private Optional searchComponentBinding( } // check if the superclass has a match - Class superClass = runtimeClass.getSuperclass(); - if (superClass != null && superClass != Object.class) { - Optional superBinding = searchComponentBinding(superClass, supplier); - if (superBinding.isPresent()) { - return superBinding; + for (Class superClass : getSuperclasses(runtimeClass)) { + if (superClass != Object.class) { + Optional superBinding = searchComponentBinding(superClass, supplier); + if (superBinding.isPresent()) { + return superBinding; + } } } } return Optional.empty(); } + + /** + * List all interfaces and super-interfaces (recursively) of a class. + * Interfaces are listed with direct parents first, then parents of those parents, then parents of those, and so on + * + * @param runtimeClass + * The class to return interfaces of + * @return + * List of Interfaces, or an empty list if there are no interfaces + */ + static List> getInterfaces(Class runtimeClass){ + List> interfaces = new ArrayList<>(Arrays.asList(runtimeClass.getInterfaces())); + + interfaces.addAll(interfaces.stream() + .flatMap(c -> getInterfaces(c).stream()) + .collect(Collectors.toList())); + + return interfaces; + } + + /** + * List all superclasses of a class (recursively). + * Classes are listed in order starting with the direct parent + * + * @param runtimeClass + * The class to return superclasses of + * @return + * List of Superclasses + */ + static List> getSuperclasses(Class runtimeClass){ + List> superclasses = new ArrayList<>(); + + Class superclass = runtimeClass; + while (true) { + superclass = superclass.getSuperclass(); + if (superclass == null) { + break; + } + + superclasses.add(superclass); + } + + return superclasses; + } private Optional getMatchingBinding(Type runtimeType, ComponentBindings binding, Function supplier) { final T component = supplier.apply(binding); diff --git a/src/test/java/org/eclipse/yasson/internal/ComponentMatcherTest.java b/src/test/java/org/eclipse/yasson/internal/ComponentMatcherTest.java new file mode 100644 index 000000000..c7cbec246 --- /dev/null +++ b/src/test/java/org/eclipse/yasson/internal/ComponentMatcherTest.java @@ -0,0 +1,40 @@ +package org.eclipse.yasson.internal; + +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ComponentMatcherTest { + @Test + public void testGetInterfaces(){ + assertEquals(Collections.emptyList(), ComponentMatcher.getInterfaces(TestClass.class)); + + assertEquals(List.of(TestInterfaceA.class), ComponentMatcher.getInterfaces(TestClassA.class)); + assertEquals(List.of(TestInterfaceB.class, TestInterfaceA.class), ComponentMatcher.getInterfaces(TestClassB.class)); + assertEquals(List.of(TestInterfaceC.class, TestInterfaceB.class, TestInterfaceA.class), ComponentMatcher.getInterfaces(TestClassC.class)); + assertEquals(List.of(TestInterfaceB.class, TestInterfaceD.class, TestInterfaceA.class), ComponentMatcher.getInterfaces(TestClassBD.class)); + } + + @Test + public void testGetSuperclasses(){ + assertEquals(List.of(Object.class), ComponentMatcher.getSuperclasses(TestClass.class)); + assertEquals(List.of(TestClass.class, Object.class), ComponentMatcher.getSuperclasses(TestSubclass.class)); + assertEquals(List.of(TestSubclass.class, TestClass.class, Object.class), ComponentMatcher.getSuperclasses(TestSubSubclass.class)); + } + + interface TestInterfaceA {} + interface TestInterfaceB extends TestInterfaceA {} + interface TestInterfaceC extends TestInterfaceB {} + interface TestInterfaceD {} + + class TestClass {} + class TestSubclass extends TestClass {} + class TestSubSubclass extends TestSubclass {} + class TestClassA implements TestInterfaceA {} + class TestClassB implements TestInterfaceB {} + class TestClassC implements TestInterfaceC {} + class TestClassBD implements TestInterfaceB, TestInterfaceD {} +} From d6949eb36d3fbb8834c7fdb3159b3482d1449d0f Mon Sep 17 00:00:00 2001 From: James Baker Date: Mon, 11 Jan 2021 11:06:34 +0000 Subject: [PATCH 2/2] Add copyright statements Signed-off-by: James Baker --- .../eclipse/yasson/internal/ComponentMatcher.java | 2 +- .../yasson/internal/ComponentMatcherTest.java | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/eclipse/yasson/internal/ComponentMatcher.java b/src/main/java/org/eclipse/yasson/internal/ComponentMatcher.java index afd84c80e..28337853d 100644 --- a/src/main/java/org/eclipse/yasson/internal/ComponentMatcher.java +++ b/src/main/java/org/eclipse/yasson/internal/ComponentMatcher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/test/java/org/eclipse/yasson/internal/ComponentMatcherTest.java b/src/test/java/org/eclipse/yasson/internal/ComponentMatcherTest.java index c7cbec246..0a5117534 100644 --- a/src/test/java/org/eclipse/yasson/internal/ComponentMatcherTest.java +++ b/src/test/java/org/eclipse/yasson/internal/ComponentMatcherTest.java @@ -1,3 +1,15 @@ +/* + * Copyright (c) 2021 Dstl. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + package org.eclipse.yasson.internal; import org.junit.jupiter.api.Test;