From 3f3917b4348f1ff7d71f48143167fb8ef93fc644 Mon Sep 17 00:00:00 2001 From: Graeme Rocher Date: Fri, 15 Nov 2024 18:34:32 +0100 Subject: [PATCH 1/2] fix combination of lombok.Builder with Introspected without builder --- .../visitor/BeanIntrospectionWriter.java | 3 +- .../IntrospectedTypeElementVisitor.java | 145 +++++++++++------ .../TypeElementVisitorProcessor.java | 11 ++ ...bstractInitializableBeanIntrospection.java | 146 +++++++++--------- test-suite/build.gradle | 1 + .../lombok/LombokIntrospectedBuilderTest.java | 22 ++- .../io/micronaut/test/lombok/MyEntity.java | 19 +++ 7 files changed, 218 insertions(+), 129 deletions(-) create mode 100644 test-suite/src/test/java/io/micronaut/test/lombok/MyEntity.java diff --git a/core-processor/src/main/java/io/micronaut/inject/beans/visitor/BeanIntrospectionWriter.java b/core-processor/src/main/java/io/micronaut/inject/beans/visitor/BeanIntrospectionWriter.java index 73190e38faf..6dedd1354a1 100644 --- a/core-processor/src/main/java/io/micronaut/inject/beans/visitor/BeanIntrospectionWriter.java +++ b/core-processor/src/main/java/io/micronaut/inject/beans/visitor/BeanIntrospectionWriter.java @@ -722,7 +722,8 @@ private void writeIntrospectionClass(ClassWriterOutputVisitor classWriterOutputV dispatchWriter.buildGetTargetMethodByIndex(classWriter); buildFindIndexedProperty(classWriter); buildGetIndexedProperties(classWriter); - boolean hasBuilder = annotationMetadata != null && annotationMetadata.isPresent(Introspected.class, "builder"); + boolean hasBuilder = annotationMetadata != null && + (annotationMetadata.isPresent(Introspected.class, "builder") || annotationMetadata.hasDeclaredAnnotation("lombok.Builder")); if (defaultConstructor != null) { writeInstantiateMethod(classWriter, defaultConstructor, "instantiate"); // in case invoked directly or via instantiateUnsafe diff --git a/core-processor/src/main/java/io/micronaut/inject/beans/visitor/IntrospectedTypeElementVisitor.java b/core-processor/src/main/java/io/micronaut/inject/beans/visitor/IntrospectedTypeElementVisitor.java index 44407f9be05..a7a0ff06c97 100644 --- a/core-processor/src/main/java/io/micronaut/inject/beans/visitor/IntrospectedTypeElementVisitor.java +++ b/core-processor/src/main/java/io/micronaut/inject/beans/visitor/IntrospectedTypeElementVisitor.java @@ -67,6 +67,7 @@ public class IntrospectedTypeElementVisitor implements TypeElementVisitor writers = new LinkedHashMap<>(10); @@ -180,62 +181,101 @@ private void processBuilderDefinition(ClassElement element, VisitorContext conte AnnotationClassValue builderClass = builder.annotationClassValue("builderClass").orElse(null); String[] writePrefixes = builder.getAnnotation("accessorStyle", AccessorsStyle.class) .map(a -> a.stringValues("writePrefixes")).orElse(new String[]{""}); - if (builderMethod != null) { - MethodElement methodElement = element - .getEnclosedElement(ElementQuery.ALL_METHODS.onlyStatic() - .filter(m -> m.getName().equals(builderMethod) && !m.getGenericReturnType().isVoid()) - .onlyAccessible(element)) - .orElse(null); - if (methodElement != null) { - ClassElement returnType = methodElement.getGenericReturnType(); - if (returnType.isPublic() || returnType.getPackageName().equals(element.getPackageName())) { - AnnotationValueBuilder replaceIntrospected = AnnotationValue.builder(introspected, RetentionPolicy.RUNTIME); - replaceIntrospected.member("builderClass", new AnnotationClassValue<>(returnType.getName())); - element.annotate(replaceIntrospected.build()); - AnnotationMetadata methodMetadata = methodElement.getMethodAnnotationMetadata().getTargetAnnotationMetadata(); - - handleBuilder( - element, - context, - creatorMethod, - writePrefixes, - methodElement, - null, - returnType, - methodMetadata, - index, - targetPackage - ); - } else { - context.fail("Builder return type is not public. The method must be static and accessible.", methodElement); - } - } else { - context.fail("Method specified by builderMethod not found. The method must be static and accessible.", element); - } - } else if (builderClass != null) { - ClassElement builderClassElement = context.getClassElement(builderClass.getName()).orElse(null); - if (builderClassElement != null) { + processBuilderDefinition( + element, + context, + introspected, + index, + targetPackage, + builderMethod, + creatorMethod, + writePrefixes, + builderClass + ); + } else if (element.hasDeclaredAnnotation(ANN_LOMBOK_BUILDER)) { + AnnotationValue lombokBuilder = element.getAnnotation(ANN_LOMBOK_BUILDER); + String builderMethod = lombokBuilder.stringValue("builderMethodName").orElse("builder"); + MethodElement methodElement = element + .getEnclosedElement(ElementQuery.ALL_METHODS.onlyStatic() + .filter(m -> m.getName().equals(builderMethod) && !m.getGenericReturnType().isVoid()) + .onlyAccessible(element)) + .orElse(null); + if (methodElement == null) { + // Lombok processing not done yet, try again in the next round. + throw new ElementPostponedToNextRoundException(element); + } + String creatorMethod = lombokBuilder.stringValue("buildMethodName").orElse("build"); + String[] writePrefixes = lombokBuilder.stringValue("setterPrefix").map(sp -> new String[] { sp }).orElse(new String[]{""}); + processBuilderDefinition( + element, + context, + introspected, + index, + targetPackage, + builderMethod, + creatorMethod, + writePrefixes, + null + ); + } + } + + private void processBuilderDefinition(ClassElement element, VisitorContext context, AnnotationValue introspected, int index, String targetPackage, String builderMethod, String creatorMethod, String[] writePrefixes, AnnotationClassValue builderClass) { + if (builderMethod != null) { + MethodElement methodElement = element + .getEnclosedElement(ElementQuery.ALL_METHODS.onlyStatic() + .filter(m -> m.getName().equals(builderMethod) && !m.getGenericReturnType().isVoid()) + .onlyAccessible(element)) + .orElse(null); + if (methodElement != null) { + ClassElement returnType = methodElement.getGenericReturnType(); + if (returnType.isPublic() || returnType.getPackageName().equals(element.getPackageName())) { AnnotationValueBuilder replaceIntrospected = AnnotationValue.builder(introspected, RetentionPolicy.RUNTIME); - replaceIntrospected.member("builderClass", new AnnotationClassValue<>(builderClassElement.getName())); + replaceIntrospected.member("builderClass", new AnnotationClassValue<>(returnType.getName())); element.annotate(replaceIntrospected.build()); + AnnotationMetadata methodMetadata = methodElement.getMethodAnnotationMetadata().getTargetAnnotationMetadata(); handleBuilder( element, context, creatorMethod, writePrefixes, - builderClassElement.getPrimaryConstructor().orElse(null), - builderClassElement.getDefaultConstructor().orElse(null), - builderClassElement, - builderClassElement.getTargetAnnotationMetadata(), + methodElement, + null, + returnType, + methodMetadata, index, - targetPackage); + targetPackage + ); } else { - context.fail("Builder class not found on compilation classpath: " + builderClass.getName(), element); + context.fail("Builder return type is not public. The method must be static and accessible.", methodElement); } } else { - context.fail("When specifying the 'builder' member of @Introspected you must supply either a builderClass or builderMethod", element); + context.fail("Method " + builderMethod + "() specified by builderMethod not found. The method must be static and accessible.", element); } + } else if (builderClass != null) { + ClassElement builderClassElement = context.getClassElement(builderClass.getName()).orElse(null); + if (builderClassElement != null) { + AnnotationValueBuilder replaceIntrospected = AnnotationValue.builder(introspected, RetentionPolicy.RUNTIME); + replaceIntrospected.member("builderClass", new AnnotationClassValue<>(builderClassElement.getName())); + element.annotate(replaceIntrospected.build()); + + handleBuilder( + element, + context, + creatorMethod, + writePrefixes, + builderClassElement.getPrimaryConstructor().orElse(null), + builderClassElement.getDefaultConstructor().orElse(null), + builderClassElement, + builderClassElement.getTargetAnnotationMetadata(), + index, + targetPackage); + } else { + context.fail("Builder class not found on compilation classpath: " + builderClass.getName(), element); + } + } else { + context.fail("When specifying the 'builder' member of @Introspected you must supply either a builderClass or builderMethod", element); } } @@ -380,13 +420,18 @@ private void processElement(boolean metadata, List beanProperties = ce.getBeanProperties(propertyElementQuery).stream() .filter(p -> !p.isExcluded()) .toList(); - Optional constructorElement = ce.getPrimaryConstructor(); - constructorElement.ifPresent(constructorEl -> { - if (ArrayUtils.isNotEmpty(constructorEl.getParameters())) { - writer.visitConstructor(constructorEl); - } - }); - ce.getDefaultConstructor().ifPresent(writer::visitDefaultConstructor); + // unfortunately sometimes we don't see the Lombok transformations + // so assume if the class is annotated with Lombok builder we cannot + // access the constructor. + if (!ce.hasDeclaredAnnotation(ANN_LOMBOK_BUILDER)) { + Optional constructorElement = ce.getPrimaryConstructor(); + constructorElement.ifPresent(constructorEl -> { + if (ArrayUtils.isNotEmpty(constructorEl.getParameters())) { + writer.visitConstructor(constructorEl); + } + }); + ce.getDefaultConstructor().ifPresent(writer::visitDefaultConstructor); + } for (PropertyElement beanProperty : beanProperties) { if (beanProperty.isExcluded()) { diff --git a/inject-java/src/main/java/io/micronaut/annotation/processing/TypeElementVisitorProcessor.java b/inject-java/src/main/java/io/micronaut/annotation/processing/TypeElementVisitorProcessor.java index 2d26416e059..e68fac8b282 100644 --- a/inject-java/src/main/java/io/micronaut/annotation/processing/TypeElementVisitorProcessor.java +++ b/inject-java/src/main/java/io/micronaut/annotation/processing/TypeElementVisitorProcessor.java @@ -35,6 +35,7 @@ import io.micronaut.inject.ast.MemberElement; import io.micronaut.inject.ast.MethodElement; import io.micronaut.inject.processing.ProcessingException; +import io.micronaut.inject.visitor.ElementPostponedToNextRoundException; import io.micronaut.inject.visitor.TypeElementVisitor; import io.micronaut.inject.visitor.VisitorContext; import io.micronaut.inject.writer.AbstractBeanDefinitionBuilder; @@ -42,6 +43,7 @@ import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedOptions; +import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; import java.io.IOException; @@ -276,6 +278,15 @@ public boolean process(Set annotations, RoundEnvironment error(originatingElement.element(), e.getMessage()); } catch (PostponeToNextRoundException e) { postponedTypes.put(javaClassElement.getCanonicalName(), e.getErrorElement()); + } catch (ElementPostponedToNextRoundException e) { + Object nativeType = e.getOriginatingElement().getNativeType(); + if (nativeType instanceof JavaNativeElement jne) { + Element element = jne.element(); + postponedTypes.put(javaClassElement.getCanonicalName(), element); + } else { + // should never happen. + throw e; + } } } } diff --git a/inject/src/main/java/io/micronaut/inject/beans/AbstractInitializableBeanIntrospection.java b/inject/src/main/java/io/micronaut/inject/beans/AbstractInitializableBeanIntrospection.java index 05fad119a7e..961614215f3 100644 --- a/inject/src/main/java/io/micronaut/inject/beans/AbstractInitializableBeanIntrospection.java +++ b/inject/src/main/java/io/micronaut/inject/beans/AbstractInitializableBeanIntrospection.java @@ -91,11 +91,11 @@ public abstract class AbstractInitializableBeanIntrospection implements Unsaf private IntrospectionBuilderData builderData; protected AbstractInitializableBeanIntrospection(Class beanType, - AnnotationMetadata annotationMetadata, - AnnotationMetadata constructorAnnotationMetadata, - Argument[] constructorArguments, - BeanPropertyRef[] propertiesRefs, - BeanMethodRef[] methodsRefs) { + AnnotationMetadata annotationMetadata, + AnnotationMetadata constructorAnnotationMetadata, + Argument[] constructorArguments, + BeanPropertyRef[] propertiesRefs, + BeanMethodRef[] methodsRefs) { this.beanType = beanType; this.annotationMetadata = annotationMetadata == null ? AnnotationMetadata.EMPTY_METADATA : EvaluatedAnnotationMetadata.wrapIfNecessary(annotationMetadata); this.constructorAnnotationMetadata = constructorAnnotationMetadata == null ? AnnotationMetadata.EMPTY_METADATA : EvaluatedAnnotationMetadata.wrapIfNecessary(constructorAnnotationMetadata); @@ -265,7 +265,7 @@ protected V dispatch(int index, @NonNull B target, @Nullable Object[] args) * @param index The method index * @param target The target * @param arg The argument - * @param The result type + * @param The result type * @return The result */ @Nullable @@ -472,67 +472,65 @@ private IntrospectionBuilderData getBuilderData() { AnnotationValue builderAnn = getAnnotationMetadata().findAnnotation(Introspected.class) .flatMap(a -> a.getAnnotation("builder", Introspected.IntrospectionBuilder.class)).orElse(null); - if (builderAnn != null) { - Class builderClass = getAnnotationMetadata().classValue(Introspected.class, "builderClass").orElse(null); - if (builderClass != null) { - BeanIntrospection builderIntrospection = (BeanIntrospection) BeanIntrospection.getIntrospection(builderClass); - Collection> beanMethods = builderIntrospection.getBeanMethods(); - - // find the creator method - BeanMethod constructorMethod = beanMethods.stream() - .filter(m -> m.getReturnType().getType().equals(getBeanType())) - .findFirst().orElse(null); - if (constructorMethod == null) { - throw new IntrospectionException("No build method found in builder: " + builderClass.getName()); - } else { - BeanMethod[] builderMethods = beanMethods.stream() - .filter(m -> - m.getReturnType().getType().isAssignableFrom(builderIntrospection.getBeanType()) - ) - .toArray(BeanMethod[]::new); - - List> arguments = new ArrayList<>(builderMethods.length); - Set properties = CollectionUtils.newHashSet(builderMethods.length); - Set> excludedMethods = new HashSet<>(); - for (BeanMethod builderMethod : builderMethods) { - Argument argument; - @NonNull Argument[] methodArgs = builderMethod.getArguments(); - if (ArrayUtils.isNotEmpty(methodArgs)) { - argument = toWrapperIfNecessary(methodArgs[0]); - } else { - argument = Argument.of(Boolean.class, builderMethod.getName()); - } - if (!properties.add(argument.getName())) { - excludedMethods.add(builderMethod); - } else { - arguments.add(argument); - } + Class builderClass = getAnnotationMetadata().classValue(Introspected.class, "builderClass").orElse(null); + if (builderAnn != null || builderClass != null) { + BeanIntrospection builderIntrospection = (BeanIntrospection) BeanIntrospection.getIntrospection(builderClass); + Collection> beanMethods = builderIntrospection.getBeanMethods(); + + // find the creator method + BeanMethod constructorMethod = beanMethods.stream() + .filter(m -> m.getReturnType().getType().equals(getBeanType())) + .findFirst().orElse(null); + if (constructorMethod == null) { + throw new IntrospectionException("No build method found in builder: " + builderClass.getName()); + } else { + BeanMethod[] builderMethods = beanMethods.stream() + .filter(m -> + m.getReturnType().getType().isAssignableFrom(builderIntrospection.getBeanType()) + ) + .toArray(BeanMethod[]::new); + + List> arguments = new ArrayList<>(builderMethods.length); + Set properties = CollectionUtils.newHashSet(builderMethods.length); + Set> excludedMethods = new HashSet<>(); + for (BeanMethod builderMethod : builderMethods) { + Argument argument; + @NonNull Argument[] methodArgs = builderMethod.getArguments(); + if (ArrayUtils.isNotEmpty(methodArgs)) { + argument = toWrapperIfNecessary(methodArgs[0]); + } else { + argument = Argument.of(Boolean.class, builderMethod.getName()); } - if (!excludedMethods.isEmpty()) { - builderMethods = Arrays.stream(builderMethods) - .filter(bm -> !excludedMethods.contains(bm)) - .toArray(BeanMethod[]::new); + if (!properties.add(argument.getName())) { + excludedMethods.add(builderMethod); + } else { + arguments.add(argument); } - this.builderData = new IntrospectionBuilderData( - builderIntrospection, - constructorMethod, - builderMethods, - arguments.toArray(Argument.ZERO_ARGUMENTS) - ); } - } else { - throw new IntrospectionException("Introspection defines invalid builder member for type: " + getBeanType()); + if (!excludedMethods.isEmpty()) { + builderMethods = Arrays.stream(builderMethods) + .filter(bm -> !excludedMethods.contains(bm)) + .toArray(BeanMethod[]::new); + } + this.builderData = new IntrospectionBuilderData( + builderIntrospection, + constructorMethod, + builderMethods, + arguments.toArray(Argument.ZERO_ARGUMENTS) + ); } } else { - int constructorLength = constructorArguments.length; - @NonNull UnsafeBeanProperty[] writeableProperties = resolveWriteableProperties(beanPropertiesList); - - this.builderData = new IntrospectionBuilderData( - constructorArguments, - constructorLength, - (UnsafeBeanProperty[]) writeableProperties - ); + throw new IntrospectionException("Introspection defines invalid builder member for type: " + getBeanType()); } + } else { + int constructorLength = constructorArguments.length; + @NonNull UnsafeBeanProperty[] writeableProperties = resolveWriteableProperties(beanPropertiesList); + + this.builderData = new IntrospectionBuilderData( + constructorArguments, + constructorLength, + (UnsafeBeanProperty[]) writeableProperties + ); } return builderData; } @@ -669,7 +667,7 @@ private void init(Argument[] arguments) { } private static final class IntrospectionBuilder implements Builder { - private static final Object[] NULL_ARG = { null }; + private static final Object[] NULL_ARG = {null}; private final Object[] params; private final IntrospectionBuilderData builderData; private final AbstractInitializableBeanIntrospection introspection; @@ -992,10 +990,10 @@ public boolean hasSetterOrConstructorArgument() { @Override public String toString() { return "BeanProperty{" + - "beanType=" + beanType + - ", type=" + ref.argument.getType() + - ", name='" + ref.argument.getName() + '\'' + - '}'; + "beanType=" + beanType + + ", type=" + ref.argument.getType() + + ", name='" + ref.argument.getName() + '\'' + + '}'; } } @@ -1089,10 +1087,10 @@ public B withValueUnsafe(B bean, P value) { @Override public String toString() { return "BeanWriteProperty{" + - "beanType=" + beanType + - ", type=" + argument.getType() + - ", name='" + argument.getName() + '\'' + - '}'; + "beanType=" + beanType + + ", type=" + argument.getType() + + ", name='" + argument.getName() + '\'' + + '}'; } } @@ -1160,10 +1158,10 @@ public P getUnsafe(B bean) { @Override public String toString() { return "BeanReadProperty{" + - "beanType=" + beanType + - ", type=" + argument.getType() + - ", name='" + argument.getName() + '\'' + - '}'; + "beanType=" + beanType + + ", type=" + argument.getType() + + ", name='" + argument.getName() + '\'' + + '}'; } } diff --git a/test-suite/build.gradle b/test-suite/build.gradle index 29c6095ecb4..690bf5429bb 100644 --- a/test-suite/build.gradle +++ b/test-suite/build.gradle @@ -118,3 +118,4 @@ test { // Prevent scanning classes with missing classes exclude '**/classnotfound/**' } + diff --git a/test-suite/src/test/java/io/micronaut/test/lombok/LombokIntrospectedBuilderTest.java b/test-suite/src/test/java/io/micronaut/test/lombok/LombokIntrospectedBuilderTest.java index 7424b20a457..76f1f54290f 100644 --- a/test-suite/src/test/java/io/micronaut/test/lombok/LombokIntrospectedBuilderTest.java +++ b/test-suite/src/test/java/io/micronaut/test/lombok/LombokIntrospectedBuilderTest.java @@ -1,5 +1,7 @@ package io.micronaut.test.lombok; +import static org.junit.jupiter.api.Assertions.assertEquals; + import io.micronaut.core.beans.BeanIntrospection; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -17,7 +19,19 @@ void testLombokBuilder() { RobotEntity robotEntity = builder.with("name", "foo") .build(); - Assertions.assertEquals("foo", robotEntity.getName()); + assertEquals("foo", robotEntity.getName()); + } + + @Test + void testLombokBuilder2() { + BeanIntrospection.Builder builder = BeanIntrospection.getIntrospection(MyEntity.class) + .builder(); + MyEntity.MyEntityBuilder builder1 = MyEntity.builder(); + builder.with("name", "foo"); + builder.with("id", "123"); + MyEntity myEntity = builder.build(); + assertEquals("foo", myEntity.getName()); + assertEquals("123", myEntity.getId()); } @Test @@ -29,7 +43,7 @@ void testLombokBuilderWithInnerClasses() { SimpleEntity simpleEntity = builder.with("id", id) .build(); - Assertions.assertEquals(id, simpleEntity.getId()); + assertEquals(id, simpleEntity.getId()); BeanIntrospection innerClassIntrospection = BeanIntrospection.getIntrospection(SimpleEntity.CompartmentCreationTimeIndexPrefix.class); @@ -42,7 +56,7 @@ void testLombokBuilderWithInnerClasses() { SimpleEntity.CompartmentCreationTimeIndexPrefix innerClassEntity = innerClassBuilder.with("compartmentId", "c1").with("timeCreated", current).build(); - Assertions.assertEquals("c1", innerClassEntity.getCompartmentId()); - Assertions.assertEquals(current, innerClassEntity.getTimeCreated()); + assertEquals("c1", innerClassEntity.getCompartmentId()); + assertEquals(current, innerClassEntity.getTimeCreated()); } } diff --git a/test-suite/src/test/java/io/micronaut/test/lombok/MyEntity.java b/test-suite/src/test/java/io/micronaut/test/lombok/MyEntity.java new file mode 100644 index 00000000000..a91af297fc3 --- /dev/null +++ b/test-suite/src/test/java/io/micronaut/test/lombok/MyEntity.java @@ -0,0 +1,19 @@ +package io.micronaut.test.lombok; + + +import io.micronaut.core.annotation.Introspected; +import lombok.Builder; +import lombok.Value; + +@Introspected +@Value +@Builder +public class MyEntity { + public static final String NAME_INDEX = "name"; + + @lombok.NonNull + String id; + + @lombok.NonNull + String name; +} From ed42c0ada2243c2149fcd73ab1a57f0bd9fc56b6 Mon Sep 17 00:00:00 2001 From: Graeme Rocher Date: Tue, 19 Nov 2024 09:41:46 +0100 Subject: [PATCH 2/2] fix tests --- ...bstractInitializableBeanIntrospection.java | 140 +++++++++--------- 1 file changed, 71 insertions(+), 69 deletions(-) diff --git a/inject/src/main/java/io/micronaut/inject/beans/AbstractInitializableBeanIntrospection.java b/inject/src/main/java/io/micronaut/inject/beans/AbstractInitializableBeanIntrospection.java index 961614215f3..cab98699d45 100644 --- a/inject/src/main/java/io/micronaut/inject/beans/AbstractInitializableBeanIntrospection.java +++ b/inject/src/main/java/io/micronaut/inject/beans/AbstractInitializableBeanIntrospection.java @@ -91,11 +91,11 @@ public abstract class AbstractInitializableBeanIntrospection implements Unsaf private IntrospectionBuilderData builderData; protected AbstractInitializableBeanIntrospection(Class beanType, - AnnotationMetadata annotationMetadata, - AnnotationMetadata constructorAnnotationMetadata, - Argument[] constructorArguments, - BeanPropertyRef[] propertiesRefs, - BeanMethodRef[] methodsRefs) { + AnnotationMetadata annotationMetadata, + AnnotationMetadata constructorAnnotationMetadata, + Argument[] constructorArguments, + BeanPropertyRef[] propertiesRefs, + BeanMethodRef[] methodsRefs) { this.beanType = beanType; this.annotationMetadata = annotationMetadata == null ? AnnotationMetadata.EMPTY_METADATA : EvaluatedAnnotationMetadata.wrapIfNecessary(annotationMetadata); this.constructorAnnotationMetadata = constructorAnnotationMetadata == null ? AnnotationMetadata.EMPTY_METADATA : EvaluatedAnnotationMetadata.wrapIfNecessary(constructorAnnotationMetadata); @@ -265,7 +265,7 @@ protected V dispatch(int index, @NonNull B target, @Nullable Object[] args) * @param index The method index * @param target The target * @param arg The argument - * @param The result type + * @param The result type * @return The result */ @Nullable @@ -474,63 +474,65 @@ private IntrospectionBuilderData getBuilderData() { .flatMap(a -> a.getAnnotation("builder", Introspected.IntrospectionBuilder.class)).orElse(null); Class builderClass = getAnnotationMetadata().classValue(Introspected.class, "builderClass").orElse(null); if (builderAnn != null || builderClass != null) { - BeanIntrospection builderIntrospection = (BeanIntrospection) BeanIntrospection.getIntrospection(builderClass); - Collection> beanMethods = builderIntrospection.getBeanMethods(); - - // find the creator method - BeanMethod constructorMethod = beanMethods.stream() - .filter(m -> m.getReturnType().getType().equals(getBeanType())) - .findFirst().orElse(null); - if (constructorMethod == null) { - throw new IntrospectionException("No build method found in builder: " + builderClass.getName()); + if (builderClass == null) { + throw new IntrospectionException("Introspection defines invalid builder member for type: " + getBeanType()); } else { - BeanMethod[] builderMethods = beanMethods.stream() - .filter(m -> - m.getReturnType().getType().isAssignableFrom(builderIntrospection.getBeanType()) - ) - .toArray(BeanMethod[]::new); - - List> arguments = new ArrayList<>(builderMethods.length); - Set properties = CollectionUtils.newHashSet(builderMethods.length); - Set> excludedMethods = new HashSet<>(); - for (BeanMethod builderMethod : builderMethods) { - Argument argument; - @NonNull Argument[] methodArgs = builderMethod.getArguments(); - if (ArrayUtils.isNotEmpty(methodArgs)) { - argument = toWrapperIfNecessary(methodArgs[0]); - } else { - argument = Argument.of(Boolean.class, builderMethod.getName()); + BeanIntrospection builderIntrospection = (BeanIntrospection) BeanIntrospection.getIntrospection(builderClass); + Collection> beanMethods = builderIntrospection.getBeanMethods(); + + // find the creator method + BeanMethod constructorMethod = beanMethods.stream() + .filter(m -> m.getReturnType().getType().equals(getBeanType())) + .findFirst().orElse(null); + if (constructorMethod == null) { + throw new IntrospectionException("No build method found in builder: " + builderClass.getName()); + } else { + BeanMethod[] builderMethods = beanMethods.stream() + .filter(m -> + m.getReturnType().getType().isAssignableFrom(builderIntrospection.getBeanType()) + ) + .toArray(BeanMethod[]::new); + + List> arguments = new ArrayList<>(builderMethods.length); + Set properties = CollectionUtils.newHashSet(builderMethods.length); + Set> excludedMethods = new HashSet<>(); + for (BeanMethod builderMethod : builderMethods) { + Argument argument; + @NonNull Argument[] methodArgs = builderMethod.getArguments(); + if (ArrayUtils.isNotEmpty(methodArgs)) { + argument = toWrapperIfNecessary(methodArgs[0]); + } else { + argument = Argument.of(Boolean.class, builderMethod.getName()); + } + if (!properties.add(argument.getName())) { + excludedMethods.add(builderMethod); + } else { + arguments.add(argument); + } } - if (!properties.add(argument.getName())) { - excludedMethods.add(builderMethod); - } else { - arguments.add(argument); + if (!excludedMethods.isEmpty()) { + builderMethods = Arrays.stream(builderMethods) + .filter(bm -> !excludedMethods.contains(bm)) + .toArray(BeanMethod[]::new); } + this.builderData = new IntrospectionBuilderData( + builderIntrospection, + constructorMethod, + builderMethods, + arguments.toArray(Argument.ZERO_ARGUMENTS) + ); } - if (!excludedMethods.isEmpty()) { - builderMethods = Arrays.stream(builderMethods) - .filter(bm -> !excludedMethods.contains(bm)) - .toArray(BeanMethod[]::new); - } - this.builderData = new IntrospectionBuilderData( - builderIntrospection, - constructorMethod, - builderMethods, - arguments.toArray(Argument.ZERO_ARGUMENTS) - ); } } else { - throw new IntrospectionException("Introspection defines invalid builder member for type: " + getBeanType()); + int constructorLength = constructorArguments.length; + @NonNull UnsafeBeanProperty[] writeableProperties = resolveWriteableProperties(beanPropertiesList); + + this.builderData = new IntrospectionBuilderData( + constructorArguments, + constructorLength, + (UnsafeBeanProperty[]) writeableProperties + ); } - } else { - int constructorLength = constructorArguments.length; - @NonNull UnsafeBeanProperty[] writeableProperties = resolveWriteableProperties(beanPropertiesList); - - this.builderData = new IntrospectionBuilderData( - constructorArguments, - constructorLength, - (UnsafeBeanProperty[]) writeableProperties - ); } return builderData; } @@ -667,7 +669,7 @@ private void init(Argument[] arguments) { } private static final class IntrospectionBuilder implements Builder { - private static final Object[] NULL_ARG = {null}; + private static final Object[] NULL_ARG = { null }; private final Object[] params; private final IntrospectionBuilderData builderData; private final AbstractInitializableBeanIntrospection introspection; @@ -990,10 +992,10 @@ public boolean hasSetterOrConstructorArgument() { @Override public String toString() { return "BeanProperty{" + - "beanType=" + beanType + - ", type=" + ref.argument.getType() + - ", name='" + ref.argument.getName() + '\'' + - '}'; + "beanType=" + beanType + + ", type=" + ref.argument.getType() + + ", name='" + ref.argument.getName() + '\'' + + '}'; } } @@ -1087,10 +1089,10 @@ public B withValueUnsafe(B bean, P value) { @Override public String toString() { return "BeanWriteProperty{" + - "beanType=" + beanType + - ", type=" + argument.getType() + - ", name='" + argument.getName() + '\'' + - '}'; + "beanType=" + beanType + + ", type=" + argument.getType() + + ", name='" + argument.getName() + '\'' + + '}'; } } @@ -1158,10 +1160,10 @@ public P getUnsafe(B bean) { @Override public String toString() { return "BeanReadProperty{" + - "beanType=" + beanType + - ", type=" + argument.getType() + - ", name='" + argument.getName() + '\'' + - '}'; + "beanType=" + beanType + + ", type=" + argument.getType() + + ", name='" + argument.getName() + '\'' + + '}'; } }