Skip to content

Commit 771dffd

Browse files
committed
LAB-2 Реализация защиты от зацикливания при генерации параметров того же типа в кейсе с циклическими/перекрестными зависимостями
1 parent f7067d0 commit 771dffd

File tree

2 files changed

+64
-19
lines changed

2 files changed

+64
-19
lines changed

src/main/java/org/example/GenerateExample.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
public class GenerateExample {
1313
public static void main(String[] args) {
14-
InstanceGenerator instanceGenerator = new InstanceGenerator();
14+
var instanceGenerator = new InstanceGenerator();
1515
try {
1616
var generated1 = instanceGenerator.generateValueOf(Example.class);
1717
System.out.println(generated1);

src/main/java/org/example/generator/InstanceGenerator.java

Lines changed: 63 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,60 +11,96 @@
1111
import java.lang.reflect.Type;
1212
import java.util.Collection;
1313
import java.util.Collections;
14+
import java.util.Deque;
15+
import java.util.ArrayDeque;
16+
import java.util.Objects;
1417
import java.util.stream.Collectors;
1518
import java.util.stream.IntStream;
1619

1720
public 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

Comments
 (0)