Provide more context when the code of a value cannot be generated

Closes gh-29118
This commit is contained in:
Stephane Nicoll 2022-09-09 11:33:48 +02:00
parent c72c2ffc26
commit 2d4f308cc1
2 changed files with 77 additions and 10 deletions

View File

@ -85,7 +85,30 @@ class BeanDefinitionPropertyValueCodeGenerator {
CodeBlock generateCode(@Nullable Object value) {
ResolvableType type = (value != null) ? ResolvableType.forInstance(value)
: ResolvableType.NONE;
return generateCode(value, type);
try {
return generateCode(value, type);
}
catch (Exception ex) {
throw new IllegalArgumentException(buildErrorMessage(value, type), ex);
}
}
private CodeBlock generateCodeForElement(@Nullable Object value, ResolvableType type) {
try {
return generateCode(value, type);
}
catch (Exception ex) {
throw new IllegalArgumentException(buildErrorMessage(value, type), ex);
}
}
private static String buildErrorMessage(@Nullable Object value, ResolvableType type) {
StringBuilder message = new StringBuilder("Failed to generate code for '");
message.append(value).append("'");
if (type != ResolvableType.NONE) {
message.append(" with type ").append(type);
}
return message.toString();
}
private CodeBlock generateCode(@Nullable Object value, ResolvableType type) {
@ -98,8 +121,7 @@ class BeanDefinitionPropertyValueCodeGenerator {
return code;
}
}
throw new IllegalArgumentException(
"'type' " + type + " must be supported for instance code generation");
throw new IllegalArgumentException("Code generation does not support " + type);
}
@ -304,7 +326,7 @@ class BeanDefinitionPropertyValueCodeGenerator {
while (iterator.hasNext()) {
Object element = iterator.next();
code.add("$L", BeanDefinitionPropertyValueCodeGenerator.this
.generateCode(element, elementType));
.generateCodeForElement(element, elementType));
if (iterator.hasNext()) {
code.add(", ");
}
@ -371,9 +393,9 @@ class BeanDefinitionPropertyValueCodeGenerator {
Entry<?, ?> entry = iterator.next();
code.add("$T.entry($L,$L)", Map.class,
BeanDefinitionPropertyValueCodeGenerator.this
.generateCode(entry.getKey(), keyType),
.generateCodeForElement(entry.getKey(), keyType),
BeanDefinitionPropertyValueCodeGenerator.this
.generateCode(entry.getValue(), valueType));
.generateCodeForElement(entry.getValue(), valueType));
if (iterator.hasNext()) {
code.add(", ");
}
@ -457,9 +479,9 @@ class BeanDefinitionPropertyValueCodeGenerator {
while (iterator.hasNext()) {
Entry<K, V> entry = iterator.next();
CodeBlock keyCode = BeanDefinitionPropertyValueCodeGenerator.this
.generateCode(entry.getKey(), keyType);
.generateCodeForElement(entry.getKey(), keyType);
CodeBlock valueCode = BeanDefinitionPropertyValueCodeGenerator.this
.generateCode(entry.getValue(), valueType);
.generateCodeForElement(entry.getValue(), valueType);
if (!useOfEntries) {
code.add("$L, $L", keyCode, valueCode);
}
@ -490,9 +512,9 @@ class BeanDefinitionPropertyValueCodeGenerator {
LinkedHashMap.class, map.size());
map.forEach((key, value) -> method.addStatement("map.put($L, $L)",
BeanDefinitionPropertyValueCodeGenerator.this
.generateCode(key, keyType),
.generateCodeForElement(key, keyType),
BeanDefinitionPropertyValueCodeGenerator.this
.generateCode(value, valueType)));
.generateCodeForElement(value, valueType)));
method.addStatement("return map");
});
return CodeBlock.of("$L()", generatedMethod.getName());

View File

@ -50,6 +50,7 @@ import org.springframework.javapoet.MethodSpec;
import org.springframework.javapoet.ParameterizedTypeName;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link BeanDefinitionPropertyValueCodeGenerator}.
@ -492,4 +493,48 @@ class BeanDefinitionPropertyValueCodeGeneratorTests {
}
@Nested
static class ExceptionTests {
@Test
void generateWhenUnsupportedDataTypeThrowsException() {
SampleValue sampleValue = new SampleValue("one");
assertThatIllegalArgumentException().isThrownBy(() -> generateCode(sampleValue))
.withMessageContaining("Failed to generate code for")
.withMessageContaining(sampleValue.toString())
.withMessageContaining(SampleValue.class.getName())
.havingCause()
.withMessageContaining("Code generation does not support")
.withMessageContaining(SampleValue.class.getName());
}
@Test
void generateWhenListOfUnsupportedElement() {
SampleValue one = new SampleValue("one");
SampleValue two = new SampleValue("two");
List<SampleValue> list = List.of(one, two);
assertThatIllegalArgumentException().isThrownBy(() -> generateCode(list))
.withMessageContaining("Failed to generate code for")
.withMessageContaining(list.toString())
.withMessageContaining(list.getClass().getName())
.havingCause()
.withMessageContaining("Failed to generate code for")
.withMessageContaining(one.toString())
.withMessageContaining("?")
.havingCause()
.withMessageContaining("Code generation does not support ?");
}
private void generateCode(Object value) {
TestGenerationContext context = new TestGenerationContext();
GeneratedClass generatedClass = context.getGeneratedClasses()
.addForFeature("Test", type -> {});
new BeanDefinitionPropertyValueCodeGenerator(generatedClass.getMethods())
.generateCode(value);
}
record SampleValue(String name) {}
}
}