From e5bd3d9bdc278bd76cc23c4c3e98df26d5c211cd Mon Sep 17 00:00:00 2001 From: egorsv21 Date: Thu, 25 Dec 2025 00:37:59 +0300 Subject: [PATCH] Implemented lab2 --- .../java/org/example/GenerateExample.java | 26 ++- .../org/example/classes/BinaryTreeNode.java | 12 + src/main/java/org/example/classes/Cart.java | 12 +- .../java/org/example/classes/Example.java | 11 +- .../org/example/classes/PrimitiveExample.java | 71 ++++++ .../java/org/example/classes/Product.java | 8 +- .../java/org/example/classes/Rectangle.java | 11 + src/main/java/org/example/classes/Shape.java | 5 +- .../java/org/example/classes/Triangle.java | 12 + .../org/example/generator/Generatable.java | 12 + .../java/org/example/generator/Generator.java | 208 +++++++++++++++++- .../NotGeneratableClassException.java | 7 + 12 files changed, 383 insertions(+), 12 deletions(-) create mode 100644 src/main/java/org/example/classes/PrimitiveExample.java create mode 100644 src/main/java/org/example/generator/Generatable.java create mode 100644 src/main/java/org/example/generator/NotGeneratableClassException.java diff --git a/src/main/java/org/example/GenerateExample.java b/src/main/java/org/example/GenerateExample.java index 47679a9..74dd78f 100644 --- a/src/main/java/org/example/GenerateExample.java +++ b/src/main/java/org/example/GenerateExample.java @@ -1,17 +1,37 @@ package org.example; +import org.example.classes.BinaryTreeNode; +import org.example.classes.Cart; import org.example.classes.Example; +import org.example.classes.PrimitiveExample; +import org.example.classes.Product; +import org.example.classes.Rectangle; +import org.example.classes.Shape; +import org.example.classes.Triangle; import org.example.generator.Generator; +import org.example.generator.NotGeneratableClassException; public class GenerateExample { public static void main(String[] args) { var gen = new Generator(); try { - Object generated = gen.generateValueOfType(Example.class); - System.out.println(generated); + System.out.println(gen.generateValueOfType(Example.class)); + System.out.println(gen.generateValueOfType(Cart.class)); + System.out.println(gen.generateValueOfType(BinaryTreeNode.class)); + System.out.println(gen.generateValueOfType(Product.class)); + System.out.println(gen.generateValueOfType(Rectangle.class)); + System.out.println(gen.generateValueOfType(Shape.class)); + System.out.println(gen.generateValueOfType(Triangle.class)); + System.out.println(gen.generateValueOfType(PrimitiveExample.class)); + + try { + System.out.println(gen.generateValueOfType(GenerateExample.class)); + } catch (NotGeneratableClassException e) { + System.err.println(e.getMessage()); + } } catch (Throwable e) { throw new RuntimeException(e); } } -} \ No newline at end of file +} diff --git a/src/main/java/org/example/classes/BinaryTreeNode.java b/src/main/java/org/example/classes/BinaryTreeNode.java index 046ff56..be25504 100644 --- a/src/main/java/org/example/classes/BinaryTreeNode.java +++ b/src/main/java/org/example/classes/BinaryTreeNode.java @@ -1,5 +1,8 @@ package org.example.classes; +import org.example.generator.Generatable; + +@Generatable public class BinaryTreeNode { private Integer data; private BinaryTreeNode left; @@ -30,4 +33,13 @@ public void setLeft(BinaryTreeNode left) { public void setRight(BinaryTreeNode right) { this.right = right; } + + @Override + public String toString() { + return "BinaryTreeNode{" + + "data=" + data + + ", left=" + left + + ", right=" + right + + '}'; + } } diff --git a/src/main/java/org/example/classes/Cart.java b/src/main/java/org/example/classes/Cart.java index 965237d..d2987a4 100644 --- a/src/main/java/org/example/classes/Cart.java +++ b/src/main/java/org/example/classes/Cart.java @@ -1,7 +1,10 @@ package org.example.classes; +import org.example.generator.Generatable; + import java.util.List; +@Generatable public class Cart { private List items; @@ -17,5 +20,12 @@ public void setItems(List items) { this.items = items; } + @Override + public String toString() { + return "Cart{" + + "items=" + items + + '}'; + } + // Конструктор, методы добавления и удаления товаров, геттеры и другие методы -} \ No newline at end of file +} diff --git a/src/main/java/org/example/classes/Example.java b/src/main/java/org/example/classes/Example.java index eac9463..f758e17 100644 --- a/src/main/java/org/example/classes/Example.java +++ b/src/main/java/org/example/classes/Example.java @@ -1,14 +1,21 @@ package org.example.classes; +import org.example.generator.Generatable; + +import java.util.Arrays; + +@Generatable public class Example { int i; + int[] ii; - public Example(int i) { + public Example(int i, int[] ii) { this.i = i; + this.ii = ii; } @Override public String toString() { - return "Example(" + i + ")"; + return "Example(" + i + ", " + Arrays.toString(ii) + ")"; } } diff --git a/src/main/java/org/example/classes/PrimitiveExample.java b/src/main/java/org/example/classes/PrimitiveExample.java new file mode 100644 index 0000000..18f08ee --- /dev/null +++ b/src/main/java/org/example/classes/PrimitiveExample.java @@ -0,0 +1,71 @@ +package org.example.classes; + +import org.example.generator.Generatable; + +@Generatable +public class PrimitiveExample { + // Primitive types + private byte byteValue; + private short shortValue; + private int intValue; + private long longValue; + private float floatValue; + private double doubleValue; + private char charValue; + private boolean booleanValue; + + // Primitive wrapper types + private Byte byteWrapper; + private Short shortWrapper; + private Integer intWrapper; + private Long longWrapper; + private Float floatWrapper; + private Double doubleWrapper; + private Character charWrapper; + private Boolean booleanWrapper; + + public PrimitiveExample(byte byteValue, short shortValue, int intValue, long longValue, + float floatValue, double doubleValue, char charValue, boolean booleanValue, + Byte byteWrapper, Short shortWrapper, Integer intWrapper, Long longWrapper, + Float floatWrapper, Double doubleWrapper, Character charWrapper, Boolean booleanWrapper) { + this.byteValue = byteValue; + this.shortValue = shortValue; + this.intValue = intValue; + this.longValue = longValue; + this.floatValue = floatValue; + this.doubleValue = doubleValue; + this.charValue = charValue; + this.booleanValue = booleanValue; + this.byteWrapper = byteWrapper; + this.shortWrapper = shortWrapper; + this.intWrapper = intWrapper; + this.longWrapper = longWrapper; + this.floatWrapper = floatWrapper; + this.doubleWrapper = doubleWrapper; + this.charWrapper = charWrapper; + this.booleanWrapper = booleanWrapper; + } + + @Override + public String toString() { + return "PrimitiveExample{" + + "byteValue=" + byteValue + + ", shortValue=" + shortValue + + ", intValue=" + intValue + + ", longValue=" + longValue + + ", floatValue=" + floatValue + + ", doubleValue=" + doubleValue + + ", charValue=" + charValue + + ", booleanValue=" + booleanValue + + ", byteWrapper=" + byteWrapper + + ", shortWrapper=" + shortWrapper + + ", intWrapper=" + intWrapper + + ", longWrapper=" + longWrapper + + ", floatWrapper=" + floatWrapper + + ", doubleWrapper=" + doubleWrapper + + ", charWrapper=" + charWrapper + + ", booleanWrapper=" + booleanWrapper + + '}'; + } +} + diff --git a/src/main/java/org/example/classes/Product.java b/src/main/java/org/example/classes/Product.java index e7dcc89..23c3873 100644 --- a/src/main/java/org/example/classes/Product.java +++ b/src/main/java/org/example/classes/Product.java @@ -1,5 +1,8 @@ package org.example.classes; +import org.example.generator.Generatable; + +@Generatable public class Product { private String name; private double price; @@ -42,7 +45,10 @@ public boolean equals(Object obj) { @Override public String toString() { - return super.toString(); + return "Product{" + + "name='" + name + '\'' + + ", price=" + price + + '}'; } } diff --git a/src/main/java/org/example/classes/Rectangle.java b/src/main/java/org/example/classes/Rectangle.java index 90b0886..d4e2320 100644 --- a/src/main/java/org/example/classes/Rectangle.java +++ b/src/main/java/org/example/classes/Rectangle.java @@ -1,5 +1,8 @@ package org.example.classes; +import org.example.generator.Generatable; + +@Generatable public class Rectangle implements Shape { private double length; private double width; @@ -18,4 +21,12 @@ public double getArea() { public double getPerimeter() { return 2 * (length + width); } + + @Override + public String toString() { + return "Rectangle{" + + "length=" + length + + ", width=" + width + + '}'; + } } \ No newline at end of file diff --git a/src/main/java/org/example/classes/Shape.java b/src/main/java/org/example/classes/Shape.java index c20a851..dac60d7 100644 --- a/src/main/java/org/example/classes/Shape.java +++ b/src/main/java/org/example/classes/Shape.java @@ -1,6 +1,9 @@ package org.example.classes; +import org.example.generator.Generatable; + +@Generatable(implementations = {Rectangle.class, Triangle.class}) public interface Shape { double getArea(); double getPerimeter(); -} \ No newline at end of file +} diff --git a/src/main/java/org/example/classes/Triangle.java b/src/main/java/org/example/classes/Triangle.java index 011e96f..1f6a5df 100644 --- a/src/main/java/org/example/classes/Triangle.java +++ b/src/main/java/org/example/classes/Triangle.java @@ -1,5 +1,8 @@ package org.example.classes; +import org.example.generator.Generatable; + +@Generatable public class Triangle implements Shape { private double sideA; private double sideB; @@ -21,4 +24,13 @@ public double getArea() { public double getPerimeter() { return sideA + sideB + sideC; } + + @Override + public String toString() { + return "Triangle{" + + "sideA=" + sideA + + ", sideB=" + sideB + + ", sideC=" + sideC + + '}'; + } } \ No newline at end of file diff --git a/src/main/java/org/example/generator/Generatable.java b/src/main/java/org/example/generator/Generatable.java new file mode 100644 index 0000000..26c4ed6 --- /dev/null +++ b/src/main/java/org/example/generator/Generatable.java @@ -0,0 +1,12 @@ +package org.example.generator; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface Generatable { + Class[] implementations() default {}; +} diff --git a/src/main/java/org/example/generator/Generator.java b/src/main/java/org/example/generator/Generator.java index 9d86bfb..773bd02 100644 --- a/src/main/java/org/example/generator/Generator.java +++ b/src/main/java/org/example/generator/Generator.java @@ -1,18 +1,218 @@ package org.example.generator; + +import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; +import java.lang.reflect.Parameter; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; import java.util.Random; +import java.util.Set; +import java.util.stream.IntStream; + public class Generator { + private static final int NULL_CHANCE = 0; + private static final int MAX_DEPTH = 4; + private static final int ARRAY_MAX_SIZE = 12; + + private static final Set> WRAPPERS = Set.of( + Boolean.class, + Byte.class, + Character.class, + Double.class, + Float.class, + Integer.class, + Long.class, + Short.class + ); + + private final Random random = new Random(); + + public T generateValueOfType(Class clazz) throws InvocationTargetException, InstantiationException, IllegalAccessException { + return (T) generateValueOfType(clazz, 0); + } + + public Object generateValueOfType(Class clazz, int depth) throws InvocationTargetException, InstantiationException, IllegalAccessException { + return generateValueOfType(clazz, depth, null); + } + + public Object generateValueOfType(Class clazz, int depth, Type genericType) throws InvocationTargetException, InstantiationException, IllegalAccessException { + if (!clazz.isPrimitive() && random.nextInt(100) < NULL_CHANCE) { + return null; + } + + if (WRAPPERS.contains(clazz) || clazz.isPrimitive()) { + return nextPrimitive(clazz); + } + if (clazz == String.class) { + return nextString(); + } + if (clazz.isEnum()) { + return nextEnum(clazz); + } + + // Start of potential recursion + if (depth > MAX_DEPTH) { + return null; + } + + if (clazz.isArray()) { + return nextArray(clazz.getComponentType(), depth); + } + + if (clazz.isAssignableFrom(List.class) || clazz == ArrayList.class) { + return nextList(depth, genericType); + } + + if (clazz == Set.class || clazz == HashSet.class) { + return nextSet(depth, genericType); + } + + return nextGeneratable(clazz, depth); + } + + private Object nextPrimitive(Class type) { + if (type == boolean.class || type == Boolean.class) { + return random.nextBoolean(); + } + if (type == byte.class || type == Byte.class) { + return (byte) random.nextInt(); + } + if (type == short.class || type == Short.class) { + return (short) random.nextInt(); + } + if (type == int.class || type == Integer.class) { + return random.nextInt(); + } + if (type == char.class || type == Character.class) { + return nextChar(); + } + if (type == double.class || type == Double.class) { + return random.nextDouble(); + } + if (type == float.class || type == Float.class) { + return random.nextFloat(); + } + if (type == long.class || type == Long.class) { + return random.nextLong(); + } + + throw new IllegalArgumentException("Not a primitive or wrapper"); + } + + + private String nextString() { + StringBuilder sb = new StringBuilder(); + IntStream.range(0, random.nextInt(1, ARRAY_MAX_SIZE + 1)) + .forEach(i -> sb.append(nextChar())); + return sb.toString(); + } + + + private char nextChar() { + return (char) random.nextInt('A', 128); + } + + private Object nextEnum(Class clazz) { + Object[] constants = clazz.getEnumConstants(); + if (constants == null || constants.length == 0) { + return null; + } + return constants[random.nextInt(constants.length)]; + } + + private Object nextArray(Class clazz, int depth) throws InvocationTargetException, InstantiationException, IllegalAccessException { + int length = random.nextInt(1, ARRAY_MAX_SIZE + 1); + Object array = Array.newInstance(clazz, length); + for (int i = 0; i < length; i++) { + Array.set(array, i, generateValueOfType(clazz, depth + 1)); + } + return array; + } + + private Object nextList(int depth, Type genericType) throws InvocationTargetException, InstantiationException, IllegalAccessException { + if (genericType instanceof ParameterizedType parameterizedType) { + Type[] types = parameterizedType.getActualTypeArguments(); + if (types.length == 1) { + Type type = types[0]; + if (type instanceof Class componentClass) { + int size = random.nextInt(1, MAX_DEPTH + 1); + List list = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + list.add(generateValueOfType(componentClass, depth + 1)); + } + return list; + } + } + } + return new ArrayList<>(); + } + + private Object nextSet(int depth, Type genericType) throws InvocationTargetException, InstantiationException, IllegalAccessException { + if (genericType instanceof ParameterizedType parameterizedType) { + Type[] types = parameterizedType.getActualTypeArguments(); + if (types.length == 1) { + Type type = types[0]; + if (type instanceof Class componentClass) { + int size = random.nextInt(1, MAX_DEPTH + 1); + Set set = new HashSet<>(size); + for (int i = 0; i < size; i++) { + set.add(generateValueOfType(componentClass, depth + 1)); + } + return set; + } + } + } + return new HashSet<>(); + } + + private Object nextGeneratable(Class clazz, int depth) throws InvocationTargetException, InstantiationException, IllegalAccessException { + Generatable annotation = clazz.getAnnotation(Generatable.class); + if (annotation == null) { + throw new NotGeneratableClassException(clazz.getSimpleName()); + } + + if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) { + return nextImplementation(annotation, depth); + } - public Object generateValueOfType(Class clazz) throws InvocationTargetException, InstantiationException, IllegalAccessException { Constructor[] constructors = clazz.getDeclaredConstructors(); - int randomConstructorIndex = new Random().nextInt(constructors.length); - Constructor randomConstructor = constructors[randomConstructorIndex]; - return randomConstructor.newInstance(111); + Constructor constructor = Arrays.stream(constructors) + .filter(c -> c.canAccess(null)) + .findAny() + .orElse(null); + + if (constructor == null) { + return null; + } + + int paramCnt = constructor.getParameterCount(); + Object[] initArgs = new Object[paramCnt]; + for (int i = 0; i < paramCnt; i++) { + Parameter parameter = constructor.getParameters()[i]; + initArgs[i] = generateValueOfType( + parameter.getType(), + depth + 1, + parameter.getParameterizedType() + ); + } + return constructor.newInstance(initArgs); } + private Object nextImplementation(Generatable annotation, int depth) throws InvocationTargetException, InstantiationException, IllegalAccessException { + Class[] implementations = annotation.implementations(); + if (implementations.length == 0) { + return null; + } + return generateValueOfType(implementations[random.nextInt(implementations.length)], depth + 1); + } } diff --git a/src/main/java/org/example/generator/NotGeneratableClassException.java b/src/main/java/org/example/generator/NotGeneratableClassException.java new file mode 100644 index 0000000..61e9b16 --- /dev/null +++ b/src/main/java/org/example/generator/NotGeneratableClassException.java @@ -0,0 +1,7 @@ +package org.example.generator; + +public class NotGeneratableClassException extends RuntimeException { + public NotGeneratableClassException(String clazz) { + super("Class " + clazz + " is not generatable"); + } +}