Skip to content

Commit

Permalink
fix(core): handle primitive classes, like byte[]
Browse files Browse the repository at this point in the history
  • Loading branch information
timonback committed Jun 7, 2024
1 parent 4e056c9 commit 3a10d41
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.HashMap;
import java.util.Map;

@Slf4j
Expand All @@ -23,37 +24,45 @@ public TypeToClassConverter(SpringwolfConfigProperties properties) {
}

public Class<?> extractClass(Type parameterType) {
// this uses the error boundary pattern
try {
if (parameterType instanceof ParameterizedType) {
Type rawParameterType = ((ParameterizedType) parameterType).getRawType();
String rawParameterTypeName = rawParameterType.getTypeName();

Class<?> actualPayloadClass =
extractActualGenericClass((ParameterizedType) parameterType, rawParameterTypeName);
if (actualPayloadClass != Void.class) {
return actualPayloadClass;
}
return extractClassUnsafe(parameterType);
} catch (Exception ex) {
log.debug("Unable to extract class for type {}", parameterType.getTypeName(), ex);
}

return Void.class;
}

public Class<?> extractClassUnsafe(Type parameterType) {
if (parameterType instanceof ParameterizedType parameterTypeParameterized) {
Type rawParameterType = parameterTypeParameterized.getRawType();

// nested generic class - fallback to most outer container
return Class.forName(rawParameterTypeName);
Class<?> actualPayloadClass =
extractActualGenericClass(parameterTypeParameterized, rawParameterType.getTypeName());
if (actualPayloadClass != Void.class) {
return actualPayloadClass;
}

// no generics used - just a normal type
return Class.forName(parameterType.getTypeName());
} catch (Exception ex) {
log.info("Unable to extract generic data type of {}", parameterType, ex);
// nested generic class - fallback to most outer container
String typeName = getTypeName(rawParameterType, rawParameterType.getTypeName());
return loadClass(typeName);
}
return Void.class;

// no generics used - just a normal type
String typeName = getTypeName(parameterType, parameterType.getTypeName());
return loadClass(typeName);
}

private Class<?> extractActualGenericClass(ParameterizedType parameterType, String rawParameterTypeName) {
Type type = parameterType;
String typeName = rawParameterTypeName;

while (type instanceof ParameterizedType && extractableClassToArgumentIndex.containsKey(typeName)) {
while (type instanceof ParameterizedType typeParameterized
&& extractableClassToArgumentIndex.containsKey(typeName)) {
Integer index = extractableClassToArgumentIndex.get(typeName);

type = ((ParameterizedType) type).getActualTypeArguments()[index];
type = typeParameterized.getActualTypeArguments()[index];
typeName = type.getTypeName();

if (type instanceof WildcardType) {
Expand All @@ -72,14 +81,62 @@ private Class<?> extractActualGenericClass(ParameterizedType parameterType, Stri
if (type instanceof ParameterizedType) {
typeName = ((ParameterizedType) type).getRawType().getTypeName();
}

typeName = getTypeName(type, typeName);
}

return loadClass(typeName);
}

private Class<?> loadClass(String typeName) {
try {
return Class.forName(typeName);
return ClassForNameUtil.forName(typeName);
} catch (ClassNotFoundException ex) {
log.debug("Unable to find class for type {}", typeName, ex);
log.debug("Unable to load class for type {}", typeName, ex);
}

return Void.class;
}

private String getTypeName(Type type, String fallback) {
if (type instanceof Class && ((Class<?>) type).getComponentType() != null) {
return ((Class<?>) type).componentType().getTypeName();
}
return fallback;
}

public static final class ClassForNameUtil {
private ClassForNameUtil() {}

private static final Map<String, Class<?>> PRIMITIVE_CLASSES;

static {
Class<?>[] classes = {
void.class,
boolean.class,
char.class,
byte.class,
short.class,
int.class,
long.class,
float.class,
double.class
};

PRIMITIVE_CLASSES = new HashMap<>();
for (Class<?> cls : classes) {
PRIMITIVE_CLASSES.put(cls.getName(), cls);
}
}

public static Class<?> forName(final String name) throws ClassNotFoundException {
Class<?> clazz = PRIMITIVE_CLASSES.get(name);

if (clazz != null) {
return clazz;
}

return Class.forName(name);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,33 @@ void getPayloadTypeWithCustomMessagePairSuperInterface() throws NoSuchMethodExce
assertThat(result).isEqualTo(Double.class);
}

@Test
void getPayloadTypeWithPrimitive() throws NoSuchMethodException {
Method m = TestClass.class.getDeclaredMethod("consumeWithPrimitive", int.class);

Class<?> result = typeToClassConverter.extractClass(extractFrom(m));

assertThat(result).isEqualTo(int.class);
}

@Test
void getPayloadTypeWithPrimitiveArray() throws NoSuchMethodException {
Method m = TestClass.class.getDeclaredMethod("consumeWithPrimitiveArray", int[].class);

Class<?> result = typeToClassConverter.extractClass(extractFrom(m));

assertThat(result).isEqualTo(int.class);
}

@Test
void getPayloadTypeWithListOfPrimitiveArray() throws NoSuchMethodException {
Method m = TestClass.class.getDeclaredMethod("consumeWithCollectionOfPrimitiveArray", List.class);

Class<?> result = typeToClassConverter.extractClass(extractFrom(m));

assertThat(result).isEqualTo(int.class);
}

@Test
void getPayloadTypeWithCustomType() throws NoSuchMethodException {
Method m = TestClass.class.getDeclaredMethod("consumeWithCustomType", TestClass.MyType.class);
Expand Down Expand Up @@ -165,6 +192,12 @@ public void consumeWithCustomMessageSuperInterface(List<? super CustomMessage> v

public void consumeWithCustomMessagePairSuperInterface(List<? super CustomPair<Integer, Double>> value) {}

public void consumeWithPrimitive(int value) {}

public void consumeWithPrimitiveArray(int[] value) {}

public void consumeWithCollectionOfPrimitiveArray(List<int[]> value) {}

public void consumeWithCustomType(MyType value) {}

public static class MyType extends GenericMessage<String> {
Expand Down

0 comments on commit 3a10d41

Please sign in to comment.