1111import java .lang .reflect .Type ;
1212import java .util .Collection ;
1313import java .util .Collections ;
14+ import java .util .Deque ;
15+ import java .util .ArrayDeque ;
16+ import java .util .Objects ;
1417import java .util .stream .Collectors ;
1518import java .util .stream .IntStream ;
1619
1720public class InstanceGenerator {
21+
1822 private final ImplementationIndex implementationIndex ;
23+ private final ThreadLocal <Deque <Class <?>>> constructing = ThreadLocal .withInitial (ArrayDeque ::new );
1924
2025 public InstanceGenerator () {
2126 var discovered = ClassPathScanner .scanForAnnotatedClasses ();
2227 this .implementationIndex = new ImplementationIndex (discovered );
2328 }
2429
2530 public Object generateValueOf (Class <?> type ) {
31+ return generateValueOf (type , -1 );
32+ }
33+
34+ private Object generateValueOf (Class <?> type , int depth ) {
2635 var primitive = PrimitiveValueGenerator .getGenerator (type );
27- if (primitive .isPresent ()) return primitive .get ().get ();
36+ if (primitive .isPresent ()) {
37+ return primitive .get ().get ();
38+ }
2839
2940 if (type .isInterface ()) {
3041 var impl = implementationIndex .randomImplementationFor (type );
31- if (impl .isEmpty ()) throw new IllegalArgumentException ("Не найдены реализации интерфейса: " + type .getName ());
32- return generateSafely (impl .get ());
42+ if (impl .isEmpty ())
43+ throw new IllegalArgumentException ("Не найдены реализации интерфейса: " + type .getName ());
44+ return generateSafely (impl .get (), depth );
3345 }
3446
3547 if (Modifier .isAbstract (type .getModifiers ())) {
3648 throw new IllegalArgumentException ("Класс " + type .getName () + " является абстрактным, не удаётся создать экземпляр" );
3749 }
3850
39- if (! type . isAnnotationPresent ( CustomClassGenerator . class ) ) {
40- throw new IllegalArgumentException ( "Класс " + type . getName () + " нельзя сгенерировать. Требуется аннотация @CustomClassGenerator" ) ;
51+ if (depth == 0 ) {
52+ return null ;
4153 }
4254
43- var constructors = type .getDeclaredConstructors ();
44- if (constructors .length == 0 ) throw new IllegalArgumentException ("Класс " + type .getName () + " не имеет конструкторов" );
45-
55+ var stack = constructing .get ();
56+ if (stack .contains (type ) && !Objects .equals (stack .peek (), type )) {
57+ return null ;
58+ }
4659
47- Constructor <?> constructor = constructors [RandomProvider .RANDOM .nextInt (constructors .length )];
48- var args = buildArgsForConstructor (constructor );
60+ stack .push (type );
4961 try {
50- constructor .setAccessible (true );
51- return type .cast (constructor .newInstance (args ));
52- } catch (Throwable e ) {
53- throw new RuntimeException ("Не удалось создать экземпляр " + type .getName (), e );
62+ if (!type .isAnnotationPresent (CustomClassGenerator .class )) {
63+ throw new IllegalArgumentException ("Класс " + type .getName () + " нельзя сгенерировать. Требуется аннотация @CustomClassGenerator" );
64+ }
65+
66+ var constructors = type .getDeclaredConstructors ();
67+ if (constructors .length == 0 ) throw new IllegalArgumentException ("Класс " + type .getName () + " не имеет конструкторов" );
68+
69+ Constructor <?> constructor = constructors [RandomProvider .RANDOM .nextInt (constructors .length )];
70+
71+ int allowedForChildren = depth > 0 ? depth - 1 : -1 ;
72+ var args = buildArgsForConstructor (constructor , allowedForChildren );
73+ try {
74+ return type .cast (constructor .newInstance (args ));
75+ } catch (Throwable e ) {
76+ throw new RuntimeException ("Не удалось создать экземпляр " + type .getName (), e );
77+ }
78+ } finally {
79+ stack .pop ();
80+ if (stack .isEmpty ()) constructing .remove ();
5481 }
5582 }
5683
57- private Object generateSafely (Class <?> currentClass ) {
84+ private Object generateSafely (Class <?> currentClass , int depth ) {
5885 try {
59- return generateValueOf (currentClass );
86+ return generateValueOf (currentClass , depth );
6087 } catch (Exception e ) {
6188 throw new RuntimeException ("Не удается создать значение для " + currentClass .getName (), e );
6289 }
6390 }
6491
65- private Object [] buildArgsForConstructor (Constructor <?> constructor ) {
92+ private Object generateSafely (Class <?> currentClass ) {
93+ return generateSafely (currentClass , -1 );
94+ }
95+
96+ private Object [] buildArgsForConstructor (Constructor <?> constructor , int allowedSelfDepthForChildren ) {
6697 var parameters = constructor .getParameters ();
6798 var args = new Object [parameters .length ];
99+ Class <?> declaringClass = constructor .getDeclaringClass ();
100+
101+ final Integer [] perConstructorSelfDepth = new Integer [] {
102+ allowedSelfDepthForChildren >= 0 ? allowedSelfDepthForChildren : null
103+ };
68104
69105 IntStream .range (0 , parameters .length ).forEach (i -> {
70106 var parameter = parameters [i ];
@@ -119,6 +155,15 @@ private Object[] buildArgsForConstructor(Constructor<?> constructor) {
119155 return ;
120156 }
121157
158+ if (parameterType .equals (declaringClass )) {
159+ if (perConstructorSelfDepth [0 ] == null ) {
160+ perConstructorSelfDepth [0 ] = 1 + RandomProvider .RANDOM .nextInt (3 );
161+ }
162+ int depthToPass = perConstructorSelfDepth [0 ];
163+ args [i ] = generateSafely (parameterType , depthToPass );
164+ return ;
165+ }
166+
122167 args [i ] = generateSafely (parameterType );
123168 });
124169
@@ -141,4 +186,4 @@ private Class<?> resolveClassFromType(Type type) {
141186
142187 return null ;
143188 }
144- }
189+ }
0 commit comments