Skip to content

Commit

Permalink
Check for possible supertype matches in AbstractArgumentFactory
Browse files Browse the repository at this point in the history
The AbstractArgumentFactory needs to be able to deal with supertypes when
being parameterized with Non-Classes (e.g. parameterized types).

We had no tests to actually try that. This fixes jdbi#2026.
  • Loading branch information
hgschmie committed Sep 30, 2022
1 parent f268e3a commit e8bda1c
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import static org.jdbi.v3.core.generic.GenericTypes.findGenericParameter;
import static org.jdbi.v3.core.generic.GenericTypes.getErasedType;
import static org.jdbi.v3.core.generic.GenericTypes.isSuperType;

/**
* An {@link ArgumentFactory} base class for arguments of type {@code T}. For values of type {@code T}, factories
Expand Down Expand Up @@ -71,7 +72,8 @@ protected AbstractArgumentFactory(int sqlType) {
this.isInstance = (type, value) ->
argumentClass.isAssignableFrom(getErasedType(type)) || argumentClass.isInstance(value);
} else {
this.isInstance = (type, value) -> argumentType.equals(type);
this.isInstance = (type, value) ->
argumentType.equals(type) || isSuperType(argumentType, type);
}
}

Expand Down
15 changes: 13 additions & 2 deletions core/src/main/java/org/jdbi/v3/core/generic/GenericTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -174,12 +174,23 @@ public static Type parameterizeClass(Class<?> clazz, Type... arguments) {
}

/**
* Perform boxing conversion on a {@code Type}, if it is a primitive type.
* Otherwise return the input argument.
* Perform boxing conversion on a {@code Type}, if it is a primitive type. Otherwise return the input argument.
*
* @param type the type to box
* @return the boxed type, or the input type if it is not a primitive
*/
public static Type box(Type type) {
return GenericTypeReflector.box(type);
}

/**
* Tests whether a given type is a supertype of another type
*
* @param superType The supertype to check.
* @param subType The subtype to check.
* @return True if supertype is a supertype of subtype.
*/
public static boolean isSuperType(Type superType, Type subType) {
return GenericTypeReflector.isSuperType(superType, subType);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Optional;
import java.util.function.Function;

import org.jdbi.v3.core.config.ConfigRegistry;
import org.jdbi.v3.core.generic.GenericType;
import org.jdbi.v3.core.qualifier.QualifiedType;
import org.jdbi.v3.core.statement.StatementContext;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
Expand Down Expand Up @@ -140,4 +143,54 @@ public void testNullOfExpectedGenericType() throws SQLException {
argument.apply(2, statement, ctx);
verify(statement).setNull(2, Types.VARCHAR);
}

static class StringBox extends Box<String> {

StringBox(String value) {
super(value);
}
}

static class IntegerBox extends Box<Integer> {

IntegerBox(Integer value) {
super(value);
}
}

@Test
public void testConcreteClassMatches() {
ArgumentFactory.Preparable preparable = new BoxOfStringArgumentFactory();
Optional<Function<Object, Argument>> argument = preparable.prepare(StringBox.class, new ConfigRegistry());
assertThat(argument).isPresent();

argument = preparable.prepare(IntegerBox.class, new ConfigRegistry());
assertThat(argument).isNotPresent();
}

@Test
public void testGenericMatches() {
ConfigRegistry registry = new ConfigRegistry();
Arguments arguments = new Arguments(registry);
arguments.register(new BoxOfStringArgumentFactory());
Box<String> genericBoxString = new Box<>("foo");
StringBox stringBox = new StringBox("bar");

assertThat(arguments.findFor(QualifiedType.of(new GenericType<Box<String>>() {}), genericBoxString)).isPresent();
assertThat(arguments.findFor(QualifiedType.of(new GenericType<Box<String>>() {}), stringBox)).isPresent();
assertThat(arguments.findFor(StringBox.class, stringBox)).isPresent();
}

@Test
public void testGenericNonMatches() {
ConfigRegistry registry = new ConfigRegistry();
Arguments arguments = new Arguments(registry);
arguments.register(new BoxOfStringArgumentFactory());
Box<Integer> genericBoxInteger = new Box<>(10);
IntegerBox integerBox = new IntegerBox(20);

assertThat(arguments.findFor(QualifiedType.of(new GenericType<Box<Integer>>() {}), genericBoxInteger)).isNotPresent();
assertThat(arguments.findFor(QualifiedType.of(new GenericType<Box<Integer>>() {}), integerBox)).isNotPresent();
assertThat(arguments.findFor(IntegerBox.class, integerBox)).isNotPresent();
}
}

0 comments on commit e8bda1c

Please sign in to comment.