Skip to content

Fixes #467 by looping through all interfaces and superclasses, rather… #469

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 56 additions & 7 deletions src/main/java/org/eclipse/yasson/internal/ComponentMatcher.java
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -206,7 +210,7 @@ private <T extends AbstractComponentBinding> Optional<T> 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<T> match = getMatchingBinding(ifc, ifcBinding, supplier);
Expand All @@ -217,17 +221,62 @@ private <T extends AbstractComponentBinding> Optional<T> searchComponentBinding(
}

// check if the superclass has a match
Class<?> superClass = runtimeClass.getSuperclass();
if (superClass != null && superClass != Object.class) {
Optional<T> superBinding = searchComponentBinding(superClass, supplier);
if (superBinding.isPresent()) {
return superBinding;
for (Class<?> superClass : getSuperclasses(runtimeClass)) {
if (superClass != Object.class) {
Optional<T> 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<Class<?>> getInterfaces(Class<?> runtimeClass){
List<Class<?>> 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<Class<?>> getSuperclasses(Class<?> runtimeClass){
List<Class<?>> superclasses = new ArrayList<>();

Class<?> superclass = runtimeClass;
while (true) {
superclass = superclass.getSuperclass();
if (superclass == null) {
break;
}

superclasses.add(superclass);
}

return superclasses;
}

private <T> Optional<T> getMatchingBinding(Type runtimeType, ComponentBindings binding, Function<ComponentBindings, T> supplier) {
final T component = supplier.apply(binding);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* 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;

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 {}
}