4141
4242@ API (since = "0.4.0" , status = API .Status .MAINTAINED )
4343public final class BuilderArbitraryIntrospector implements ArbitraryIntrospector {
44+
4445 public static final BuilderArbitraryIntrospector INSTANCE = new BuilderArbitraryIntrospector ();
4546
4647 private static final Logger LOGGER = LoggerFactory .getLogger (BuilderArbitraryIntrospector .class );
@@ -76,20 +77,17 @@ public ArbitraryIntrospectorResult introspect(ArbitraryGeneratorContext context)
7677 Method buildMethod ;
7778 try {
7879 builderType = this .getBuilderType (type );
79- buildMethod = BUILD_METHOD_CACHE .computeIfAbsent (
80- builderType ,
81- t -> {
82- String buildMethodName = typedBuildMethodName .getOrDefault (t , defaultBuildMethodName );
83- Method method = Reflections .findMethod (builderType , buildMethodName );
84- if (method == null ) {
85- throw new IllegalStateException (
86- "Can not retrieve a build method. type: " + type + " buildMethodName: " + buildMethodName
87- );
88- }
89- method .setAccessible (true );
90- return method ;
80+ buildMethod = BUILD_METHOD_CACHE .computeIfAbsent (builderType , t -> {
81+ String buildMethodName = typedBuildMethodName .getOrDefault (t , defaultBuildMethodName );
82+ Method method = Reflections .findMethod (builderType , buildMethodName );
83+ if (method == null ) {
84+ throw new IllegalStateException (
85+ "Can not retrieve a build method. type: " + type + " buildMethodName: " + buildMethodName
86+ );
9187 }
92- );
88+ method .setAccessible (true );
89+ return method ;
90+ });
9391 } catch (Exception ex ) {
9492 ArbitraryGeneratorLoggingContext loggingContext = context .getLoggingContext ();
9593 if (loggingContext .isEnableLoggingFail ()) {
@@ -99,38 +97,34 @@ public ArbitraryIntrospectorResult introspect(ArbitraryGeneratorContext context)
9997 }
10098 Method builderMethod = BUILDER_CACHE .get (type );
10199
102- LazyArbitrary <Object > generateArbitrary = LazyArbitrary .lazy (
103- () -> {
104- Object builder = Reflections .invokeMethod (builderMethod , null );
105-
106- for (ArbitraryProperty arbitraryProperty : childrenProperties ) {
107- String methodName = getFieldName (arbitraryProperty .getObjectProperty ().getProperty ());
108- Class <?> actualType = getActualType (arbitraryProperty .getObjectProperty ().getProperty ());
109- String buildFieldMethodName = builderType .getName () + "#" + methodName ;
110-
111- String resolvePropertyName =
112- arbitraryProperty .getObjectProperty ().getResolvedPropertyName ();
113- CombinableArbitrary <?> combinableArbitrary =
114- arbitrariesByResolvedName .get (resolvePropertyName );
115-
116- Method method = BUILD_FIELD_METHOD_CACHE .computeIfAbsent (buildFieldMethodName , f -> {
117- Method buildFieldMethod = Reflections .findMethod (builderType , methodName , actualType );
118- if (buildFieldMethod != null ) {
119- buildFieldMethod .setAccessible (true );
120- }
121- return buildFieldMethod ;
122- });
123- if (method != null ) {
124- Object child = combinableArbitrary .combined ();
125- if (child != null ) {
126- Reflections .invokeMethod (method , builder , child );
127- }
100+ LazyArbitrary <Object > generateArbitrary = LazyArbitrary .lazy (() -> {
101+ Object builder = Reflections .invokeMethod (builderMethod , null );
102+
103+ for (ArbitraryProperty arbitraryProperty : childrenProperties ) {
104+ String methodName = getFieldName (arbitraryProperty .getObjectProperty ().getProperty ());
105+ Class <?> actualType = getActualType (arbitraryProperty .getObjectProperty ().getProperty ());
106+ String buildFieldMethodName = builderType .getName () + "#" + methodName ;
107+
108+ String resolvePropertyName = arbitraryProperty .getObjectProperty ().getResolvedPropertyName ();
109+ CombinableArbitrary <?> combinableArbitrary = arbitrariesByResolvedName .get (resolvePropertyName );
110+
111+ Method method = BUILD_FIELD_METHOD_CACHE .computeIfAbsent (buildFieldMethodName , f -> {
112+ Method buildFieldMethod = Reflections .findMethod (builderType , methodName , actualType );
113+ if (buildFieldMethod != null ) {
114+ buildFieldMethod .setAccessible (true );
115+ }
116+ return buildFieldMethod ;
117+ });
118+ if (method != null ) {
119+ Object child = combinableArbitrary .combined ();
120+ if (child != null ) {
121+ Reflections .invokeMethod (method , builder , child );
128122 }
129123 }
130-
131- return Reflections .invokeMethod (buildMethod , builder );
132124 }
133- );
125+
126+ return Reflections .invokeMethod (buildMethod , builder );
127+ });
134128 return new ArbitraryIntrospectorResult (CombinableArbitrary .from (generateArbitrary ));
135129 }
136130
@@ -166,8 +160,8 @@ private Class<?> getBuilderType(Class<?> objectType) {
166160
167161 if (builderMethod == null ) {
168162 throw new IllegalArgumentException (
169- "Class has no builder class. "
170- + "type: " + objectType . getName () + " builderMethodName: " + builderMethodName
163+ "Class has no builder class. " + "type: " + objectType . getName ()
164+ + " builderMethodName: " + builderMethodName
171165 );
172166 }
173167
@@ -182,7 +176,20 @@ private String getFieldName(Property property) {
182176 }
183177
184178 private Class <?> getActualType (Property property ) {
185- return Types .getActualType (getActualProperty (property ).getType ());
179+ Property actualProperty = getActualProperty (property );
180+
181+ // The adapter may resolve a FieldProperty's type to a concrete type (e.g. ArrayList),
182+ // but builder methods accept the field's declared type (e.g. List) as their parameter.
183+ // If the concrete type is a subtype of the declared type, use the declared type instead.
184+ if (actualProperty instanceof FieldProperty ) {
185+ Class <?> fieldDeclaredType = ((FieldProperty )actualProperty ).getField ().getType ();
186+ Class <?> propertyType = Types .getActualType (actualProperty .getType ());
187+ if (fieldDeclaredType != propertyType && fieldDeclaredType .isAssignableFrom (propertyType )) {
188+ return fieldDeclaredType ;
189+ }
190+ }
191+
192+ return Types .getActualType (actualProperty .getType ());
186193 }
187194
188195 private Property getActualProperty (Property property ) {
0 commit comments