4242
4343@ API (since = "0.4.0" , status = API .Status .MAINTAINED )
4444public final class BuilderArbitraryIntrospector implements ArbitraryIntrospector {
45+
4546 public static final BuilderArbitraryIntrospector INSTANCE = new BuilderArbitraryIntrospector ();
4647
4748 private static final Logger LOGGER = LoggerFactory .getLogger (BuilderArbitraryIntrospector .class );
@@ -78,20 +79,17 @@ public ArbitraryIntrospectorResult introspect(ArbitraryGeneratorContext context)
7879 Method buildMethod ;
7980 try {
8081 builderType = this .getBuilderType (type );
81- buildMethod = BUILD_METHOD_CACHE .computeIfAbsent (
82- builderType ,
83- t -> {
84- String buildMethodName = typedBuildMethodName .getOrDefault (t , defaultBuildMethodName );
85- Method method = Reflections .findMethod (builderType , buildMethodName );
86- if (method == null ) {
87- throw new IllegalStateException (
88- "Can not retrieve a build method. type: " + type + " buildMethodName: " + buildMethodName
89- );
90- }
91- method .setAccessible (true );
92- return method ;
82+ buildMethod = BUILD_METHOD_CACHE .computeIfAbsent (builderType , t -> {
83+ String buildMethodName = typedBuildMethodName .getOrDefault (t , defaultBuildMethodName );
84+ Method method = Reflections .findMethod (builderType , buildMethodName );
85+ if (method == null ) {
86+ throw new IllegalStateException (
87+ "Can not retrieve a build method. type: " + type + " buildMethodName: " + buildMethodName
88+ );
9389 }
94- );
90+ method .setAccessible (true );
91+ return method ;
92+ });
9593 } catch (Exception ex ) {
9694 ArbitraryGeneratorLoggingContext loggingContext = context .getLoggingContext ();
9795 if (loggingContext .isEnableLoggingFail ()) {
@@ -101,38 +99,34 @@ public ArbitraryIntrospectorResult introspect(ArbitraryGeneratorContext context)
10199 }
102100 Method builderMethod = BUILDER_CACHE .get (type );
103101
104- LazyArbitrary <Object > generateArbitrary = LazyArbitrary .lazy (
105- () -> {
106- Object builder = Reflections .invokeMethod (builderMethod , null );
107-
108- for (ArbitraryProperty arbitraryProperty : childrenProperties ) {
109- String methodName = getFieldName (arbitraryProperty .getObjectProperty ().getProperty ());
110- Class <?> actualType = getActualType (arbitraryProperty .getObjectProperty ().getProperty ());
111- String buildFieldMethodName = builderType .getName () + "#" + methodName ;
112-
113- String resolvePropertyName =
114- arbitraryProperty .getObjectProperty ().getResolvedPropertyName ();
115- CombinableArbitrary <?> combinableArbitrary =
116- arbitrariesByResolvedName .get (resolvePropertyName );
117-
118- Method method = BUILD_FIELD_METHOD_CACHE .computeIfAbsent (buildFieldMethodName , f -> {
119- Method buildFieldMethod = Reflections .findMethod (builderType , methodName , actualType );
120- if (buildFieldMethod != null ) {
121- buildFieldMethod .setAccessible (true );
122- }
123- return buildFieldMethod ;
124- });
125- if (method != null ) {
126- Object child = combinableArbitrary .combined ();
127- if (child != null ) {
128- Reflections .invokeMethod (method , builder , child );
129- }
102+ LazyArbitrary <Object > generateArbitrary = LazyArbitrary .lazy (() -> {
103+ Object builder = Reflections .invokeMethod (builderMethod , null );
104+
105+ for (ArbitraryProperty arbitraryProperty : childrenProperties ) {
106+ String methodName = getFieldName (arbitraryProperty .getObjectProperty ().getProperty ());
107+ Class <?> actualType = getActualType (arbitraryProperty .getObjectProperty ().getProperty ());
108+ String buildFieldMethodName = builderType .getName () + "#" + methodName ;
109+
110+ String resolvePropertyName = arbitraryProperty .getObjectProperty ().getResolvedPropertyName ();
111+ CombinableArbitrary <?> combinableArbitrary = arbitrariesByResolvedName .get (resolvePropertyName );
112+
113+ Method method = BUILD_FIELD_METHOD_CACHE .computeIfAbsent (buildFieldMethodName , f -> {
114+ Method buildFieldMethod = Reflections .findMethod (builderType , methodName , actualType );
115+ if (buildFieldMethod != null ) {
116+ buildFieldMethod .setAccessible (true );
117+ }
118+ return buildFieldMethod ;
119+ });
120+ if (method != null ) {
121+ Object child = combinableArbitrary .combined ();
122+ if (child != null ) {
123+ Reflections .invokeMethod (method , builder , child );
130124 }
131125 }
132-
133- return Reflections .invokeMethod (buildMethod , builder );
134126 }
135- );
127+
128+ return Reflections .invokeMethod (buildMethod , builder );
129+ });
136130 return new ArbitraryIntrospectorResult (CombinableArbitrary .from (generateArbitrary ));
137131 }
138132
@@ -169,8 +163,8 @@ public void setBuildMethodName(Class<?> type, String buildMethodName) {
169163
170164 if (builderMethod == null ) {
171165 throw new IllegalArgumentException (
172- "Class has no builder class. "
173- + "type: " + objectType . getName () + " builderMethodName: " + builderMethodName
166+ "Class has no builder class. " + "type: " + objectType . getName ()
167+ + " builderMethodName: " + builderMethodName
174168 );
175169 }
176170
@@ -186,7 +180,20 @@ private String getFieldName(Property property) {
186180 }
187181
188182 private Class <?> getActualType (Property property ) {
189- return Types .getActualType (getActualProperty (property ).getType ());
183+ Property actualProperty = getActualProperty (property );
184+
185+ // The adapter may resolve a FieldProperty's type to a concrete type (e.g. ArrayList),
186+ // but builder methods accept the field's declared type (e.g. List) as their parameter.
187+ // If the concrete type is a subtype of the declared type, use the declared type instead.
188+ if (actualProperty instanceof FieldProperty ) {
189+ Class <?> fieldDeclaredType = ((FieldProperty )actualProperty ).getField ().getType ();
190+ Class <?> propertyType = Types .getActualType (actualProperty .getType ());
191+ if (fieldDeclaredType != propertyType && fieldDeclaredType .isAssignableFrom (propertyType )) {
192+ return fieldDeclaredType ;
193+ }
194+ }
195+
196+ return Types .getActualType (actualProperty .getType ());
190197 }
191198
192199 private Property getActualProperty (Property property ) {
0 commit comments