From c90f4c8661ebb6c82b775a7500070424afe156db Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 12:44:42 +0000 Subject: [PATCH 1/5] add deadline --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 87ffcbb..1a6a1a9 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/4ISSpVK4) **Java reflection** Ваша задача --- написать генератор экземпляров произвольных классов. From c79a77b0c5738f9447ba59f410e7cbd605698d93 Mon Sep 17 00:00:00 2001 From: railolog Date: Wed, 5 Nov 2025 23:25:22 +0300 Subject: [PATCH 2/5] implementation and tests --- build.gradle.kts | 3 + .../java/org/example/GenerateExample.java | 75 +++++++++- .../org/example/classes/BinaryTreeNode.java | 29 ++-- src/main/java/org/example/classes/Cart.java | 5 + .../java/org/example/classes/Example.java | 3 + .../java/org/example/classes/Product.java | 10 +- .../java/org/example/classes/Rectangle.java | 5 + .../java/org/example/classes/Triangle.java | 5 + .../org/example/generator/Generatable.java | 11 ++ .../java/org/example/generator/Generator.java | 128 +++++++++++++++++- .../example/generator/InterfaceRegistry.java | 103 ++++++++++++++ .../org/example/generator/GeneratorTest.java | 64 +++++++++ 12 files changed, 403 insertions(+), 38 deletions(-) create mode 100644 src/main/java/org/example/generator/Generatable.java create mode 100644 src/main/java/org/example/generator/InterfaceRegistry.java create mode 100644 src/test/java/org/example/generator/GeneratorTest.java diff --git a/build.gradle.kts b/build.gradle.kts index b4738ae..b5970f3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -10,6 +10,9 @@ repositories { } dependencies { + compileOnly("org.projectlombok:lombok:1.18.42") + annotationProcessor("org.projectlombok:lombok:1.18.42") + testImplementation(platform("org.junit:junit-bom:5.10.0")) testImplementation("org.junit.jupiter:junit-jupiter") testRuntimeOnly("org.junit.platform:junit-platform-launcher") diff --git a/src/main/java/org/example/GenerateExample.java b/src/main/java/org/example/GenerateExample.java index 47679a9..3aae693 100644 --- a/src/main/java/org/example/GenerateExample.java +++ b/src/main/java/org/example/GenerateExample.java @@ -1,17 +1,80 @@ package org.example; - -import org.example.classes.Example; +import org.example.classes.*; import org.example.generator.Generator; public class GenerateExample { public static void main(String[] args) { var gen = new Generator(); + + System.out.println("=== Testing Generator ==="); + + try { + System.out.println("\n1. Generating Example:"); + Object example = gen.generateValueOfType(Example.class); + System.out.println("Generated: " + example); + } catch (Exception e) { + System.err.println("Error generating Example: " + e.getMessage()); + } + + try { + System.out.println("\n2. Generating Shape:"); + Object shape = gen.generateValueOfType(Shape.class); + System.out.println("Generated: " + shape); + if (shape instanceof Shape s) { + System.out.println("Area: " + s.getArea()); + System.out.println("Perimeter: " + s.getPerimeter()); + } + } catch (Exception e) { + System.err.println("Error generating Shape: " + e.getMessage()); + } + + try { + System.out.println("\n3. Generating Product:"); + Object product = gen.generateValueOfType(Product.class); + System.out.println("Generated: " + product); + } catch (Exception e) { + System.err.println("Error generating Product: " + e.getMessage()); + } + + try { + System.out.println("\n4. Generating Rectangle:"); + Object rectangle = gen.generateValueOfType(Rectangle.class); + System.out.println("Generated: " + rectangle); + if (rectangle instanceof Rectangle r) { + System.out.println("Area: " + r.getArea()); + System.out.println("Perimeter: " + r.getPerimeter()); + } + } catch (Exception e) { + System.err.println("Error generating Rectangle: " + e.getMessage()); + } + + try { + System.out.println("\n5. Generating Triangle:"); + Object triangle = gen.generateValueOfType(Triangle.class); + System.out.println("Generated: " + triangle); + if (triangle instanceof Triangle t) { + System.out.println("Area: " + t.getArea()); + System.out.println("Perimeter: " + t.getPerimeter()); + } + } catch (Exception e) { + System.err.println("Error generating Triangle: " + e.getMessage()); + } + + try { + System.out.println("\n6. Generating Cart:"); + Object cart = gen.generateValueOfType(Cart.class); + System.out.println("Generated: " + cart); + } catch (Exception e) { + System.err.println("Error generating Cart: " + e.getMessage()); + } + try { - Object generated = gen.generateValueOfType(Example.class); - System.out.println(generated); - } catch (Throwable e) { - throw new RuntimeException(e); + System.out.println("\n7. Generating BinaryTreeNode:"); + Object node = gen.generateValueOfType(BinaryTreeNode.class); + System.out.println("Generated: " + node); + } catch (Exception e) { + System.err.println("Error generating BinaryTreeNode: " + e.getMessage()); } } } \ 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..e6af71a 100644 --- a/src/main/java/org/example/classes/BinaryTreeNode.java +++ b/src/main/java/org/example/classes/BinaryTreeNode.java @@ -1,5 +1,14 @@ package org.example.classes; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.example.generator.Generatable; + +@Getter +@Setter +@ToString +@Generatable public class BinaryTreeNode { private Integer data; private BinaryTreeNode left; @@ -10,24 +19,4 @@ public BinaryTreeNode(Integer data, BinaryTreeNode left, BinaryTreeNode right) { this.left = left; this.right = right; } - - public Integer getData() { - return data; - } - - public BinaryTreeNode getLeft() { - return left; - } - - public BinaryTreeNode getRight() { - return right; - } - - public void setLeft(BinaryTreeNode left) { - this.left = left; - } - - public void setRight(BinaryTreeNode right) { - this.right = right; - } } diff --git a/src/main/java/org/example/classes/Cart.java b/src/main/java/org/example/classes/Cart.java index 965237d..2a08d1d 100644 --- a/src/main/java/org/example/classes/Cart.java +++ b/src/main/java/org/example/classes/Cart.java @@ -2,6 +2,11 @@ import java.util.List; +import lombok.ToString; +import org.example.generator.Generatable; + +@ToString +@Generatable public class Cart { private List items; diff --git a/src/main/java/org/example/classes/Example.java b/src/main/java/org/example/classes/Example.java index eac9463..e0c8d1c 100644 --- a/src/main/java/org/example/classes/Example.java +++ b/src/main/java/org/example/classes/Example.java @@ -1,5 +1,8 @@ package org.example.classes; +import org.example.generator.Generatable; + +@Generatable public class Example { int i; diff --git a/src/main/java/org/example/classes/Product.java b/src/main/java/org/example/classes/Product.java index e7dcc89..d8d0923 100644 --- a/src/main/java/org/example/classes/Product.java +++ b/src/main/java/org/example/classes/Product.java @@ -1,5 +1,10 @@ package org.example.classes; +import lombok.ToString; +import org.example.generator.Generatable; + +@ToString +@Generatable public class Product { private String name; private double price; @@ -40,9 +45,4 @@ public boolean equals(Object obj) { return super.equals(obj); } - @Override - public String toString() { - return super.toString(); - } - } diff --git a/src/main/java/org/example/classes/Rectangle.java b/src/main/java/org/example/classes/Rectangle.java index 90b0886..4018fc8 100644 --- a/src/main/java/org/example/classes/Rectangle.java +++ b/src/main/java/org/example/classes/Rectangle.java @@ -1,5 +1,10 @@ package org.example.classes; +import lombok.ToString; +import org.example.generator.Generatable; + +@ToString +@Generatable public class Rectangle implements Shape { private double length; private double width; diff --git a/src/main/java/org/example/classes/Triangle.java b/src/main/java/org/example/classes/Triangle.java index 011e96f..a0a9c85 100644 --- a/src/main/java/org/example/classes/Triangle.java +++ b/src/main/java/org/example/classes/Triangle.java @@ -1,5 +1,10 @@ package org.example.classes; +import lombok.ToString; +import org.example.generator.Generatable; + +@ToString +@Generatable public class Triangle implements Shape { private double sideA; private double sideB; 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..5d02ef2 --- /dev/null +++ b/src/main/java/org/example/generator/Generatable.java @@ -0,0 +1,11 @@ +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 { +} \ No newline at end of file diff --git a/src/main/java/org/example/generator/Generator.java b/src/main/java/org/example/generator/Generator.java index 9d86bfb..4b68453 100644 --- a/src/main/java/org/example/generator/Generator.java +++ b/src/main/java/org/example/generator/Generator.java @@ -1,18 +1,132 @@ package org.example.generator; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.util.Random; +import java.lang.reflect.*; +import java.util.*; public class Generator { + private final Random random = new Random(); + private final int maxDepth = 3; + private final int maxCollectionSize = 5; + private final InterfaceRegistry interfaceRegistry = new InterfaceRegistry(); + + public Object generateValueOfType(Class clazz) throws Exception { + return generateValueOfType(clazz, 0); + } + + private Object generateValueOfType(Class clazz, int currentDepth) throws Exception { + if (currentDepth > maxDepth) { + return null; + } + + if (clazz == int.class || clazz == Integer.class) { + return random.nextInt(1000); + } + if (clazz == double.class || clazz == Double.class) { + return random.nextDouble() * 100; + } + if (clazz == float.class || clazz == Float.class) { + return random.nextFloat() * 100; + } + if (clazz == long.class || clazz == Long.class) { + return random.nextLong(); + } + if (clazz == boolean.class || clazz == Boolean.class) { + return random.nextBoolean(); + } + if (clazz == byte.class || clazz == Byte.class) { + return (byte) random.nextInt(256); + } + if (clazz == short.class || clazz == Short.class) { + return (short) random.nextInt(32768); + } + if (clazz == char.class || clazz == Character.class) { + return (char) (random.nextInt(26) + 'a'); + } + + if (clazz == String.class) { + return generateRandomString(); + } + + if (List.class.isAssignableFrom(clazz)) { + return generateList(currentDepth); + } + if (Set.class.isAssignableFrom(clazz)) { + return generateSet(currentDepth); + } + + if (clazz.isInterface()) { + return generateInterfaceImplementation(clazz, currentDepth); + } + + return generateObject(clazz, currentDepth); + } + + private Object generateObject(Class clazz, int currentDepth) throws Exception { + if (!clazz.isAnnotationPresent(Generatable.class)) { + return null; + } + - public Object generateValueOfType(Class clazz) throws InvocationTargetException, InstantiationException, IllegalAccessException { Constructor[] constructors = clazz.getDeclaredConstructors(); + if (constructors.length == 0) { + return null; + } + + Constructor selectedConstructor = selectRandomConstructor(constructors); + selectedConstructor.setAccessible(true); + + Class[] paramTypes = selectedConstructor.getParameterTypes(); + Object[] params = new Object[paramTypes.length]; - int randomConstructorIndex = new Random().nextInt(constructors.length); - Constructor randomConstructor = constructors[randomConstructorIndex]; - return randomConstructor.newInstance(111); + for (int i = 0; i < paramTypes.length; i++) { + params[i] = generateValueOfType(paramTypes[i], currentDepth + 1); + } + + return selectedConstructor.newInstance(params); + } + + private Constructor selectRandomConstructor(Constructor[] constructors) { + int idx = random.nextInt(constructors.length); + return constructors[idx]; + } + + private String generateRandomString() { + String[] words = {"apple", "banana", "cherry", "dog", "elephant", "forest", "guitar", "house", "ice", "jungle"}; + return words[random.nextInt(words.length)] + random.nextInt(100); + } + + private List generateList(int depth) throws Exception { + List list = new ArrayList<>(); + int size = random.nextInt(maxCollectionSize); + + Class[] possibleTypes = {String.class, Integer.class, Double.class}; + for (int i = 0; i < size; i++) { + Class randomType = possibleTypes[random.nextInt(possibleTypes.length)]; + list.add(generateValueOfType(randomType, depth + 1)); + } + return list; + } + + private Set generateSet(int depth) throws Exception { + Set set = new HashSet<>(); + int size = random.nextInt(maxCollectionSize); + + Class[] possibleTypes = {String.class, Integer.class, Double.class}; + for (int i = 0; i < size; i++) { + Class randomType = possibleTypes[random.nextInt(possibleTypes.length)]; + set.add(generateValueOfType(randomType, depth + 1)); + } + return set; } + private Object generateInterfaceImplementation(Class interfaceClass, int depth) throws Exception { + List> implementations = interfaceRegistry.getImplementations(interfaceClass); + + if (implementations.isEmpty()) { + throw new IllegalArgumentException("No @Generatable implementations found for interface: " + interfaceClass.getName()); + } + Class selectedImpl = implementations.get(random.nextInt(implementations.size())); + return generateValueOfType(selectedImpl, depth); + } } diff --git a/src/main/java/org/example/generator/InterfaceRegistry.java b/src/main/java/org/example/generator/InterfaceRegistry.java new file mode 100644 index 0000000..f9edf66 --- /dev/null +++ b/src/main/java/org/example/generator/InterfaceRegistry.java @@ -0,0 +1,103 @@ +package org.example.generator; + +import java.io.File; +import java.net.URL; +import java.util.*; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +public class InterfaceRegistry { + private static final String PACKAGE_NAME = "org.example.classes"; + + private final Map, List>> interfaceToImplementations = new HashMap<>(); + private boolean initialized = false; + + public List> getImplementations(Class interfaceClass) { + if (!initialized) { + scanForGeneratableClasses(); + } + return interfaceToImplementations.getOrDefault(interfaceClass, Collections.emptyList()); + } + + private void registerClass(Class clazz) { + if (!clazz.isAnnotationPresent(Generatable.class)) { + return; + } + + Class[] interfaces = clazz.getInterfaces(); + for (Class interfaceClass : interfaces) { + interfaceToImplementations + .computeIfAbsent(interfaceClass, k -> new ArrayList<>()) + .add(clazz); + } + } + + private void scanForGeneratableClasses() { + try { + String packagePath = PACKAGE_NAME.replace('.', '/'); + + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + Enumeration resources = classLoader.getResources(packagePath); + + while (resources.hasMoreElements()) { + URL resource = resources.nextElement(); + if (resource.getProtocol().equals("file")) { + scanDirectory(new File(resource.getFile()), PACKAGE_NAME); + } else if (resource.getProtocol().equals("jar")) { + scanJarFile(resource.getFile(), packagePath); + } + } + + initialized = true; + } catch (Exception e) { + System.err.println("Error scanning for @Generatable classes: " + e.getMessage()); + } + } + + private void scanDirectory(File directory, String packageName) { + if (!directory.exists()) { + return; + } + + File[] files = directory.listFiles(); + if (files == null) { + return; + } + + for (File file : files) { + if (file.isDirectory()) { + scanDirectory(file, packageName + "." + file.getName()); + } else if (file.getName().endsWith(".class")) { + String className = packageName + "." + file.getName().substring(0, file.getName().length() - 6); + try { + Class clazz = Class.forName(className); + registerClass(clazz); + } catch (ClassNotFoundException ignored) { + } + } + } + } + + private void scanJarFile(String jarPath, String packagePath) { + try { + String jarFilePath = jarPath.substring(jarPath.indexOf(":") + 1, jarPath.indexOf("!")); + try (JarFile jarFile = new JarFile(jarFilePath)) { + Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + String entryName = entry.getName(); + if (entryName.startsWith(packagePath) && entryName.endsWith(".class")) { + String className = entryName.replace('/', '.').substring(0, entryName.length() - 6); + try { + Class clazz = Class.forName(className); + registerClass(clazz); + } catch (ClassNotFoundException ignored) { + } + } + } + } + } catch (Exception e) { + System.err.println("Error scanning JAR file: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/test/java/org/example/generator/GeneratorTest.java b/src/test/java/org/example/generator/GeneratorTest.java new file mode 100644 index 0000000..6281518 --- /dev/null +++ b/src/test/java/org/example/generator/GeneratorTest.java @@ -0,0 +1,64 @@ +package org.example.generator; + +import org.example.classes.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.RepeatedTest; +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; + +public class GeneratorTest { + + private Generator generator; + + @BeforeEach + void setUp() { + generator = new Generator(); + } + + @Test + void testGenerateExample_ShouldCreateObject() throws Exception { + Object result = generator.generateValueOfType(Example.class); + + assertNotNull(result, "Generated object should not be null"); + assertInstanceOf(Example.class, result, "Generated object should be instance of Example"); + } + + @Test + void testGenerateProduct_ShouldCreateObject() throws Exception { + Object result = generator.generateValueOfType(Product.class); + + assertNotNull(result, "Generated Product should not be null"); + assertInstanceOf(Product.class, result, "Generated object should be instance of Product"); + + Product product = (Product) result; + assertNotNull(product.getName(), "Product name should not be null"); + assertTrue(product.getPrice() != 0 || product.getPrice() == 0, "Product price should be set"); + } + + @Test + void testGenerateShape_ShouldCreateShapeImplementation() throws Exception { + Object result = generator.generateValueOfType(Shape.class); + + assertNotNull(result, "Generated Shape should not be null"); + assertInstanceOf(Shape.class, result, "Generated object should implement Shape interface"); + + assertTrue(result instanceof Rectangle || result instanceof Triangle, + "Shape implementation should be either Rectangle or Triangle"); + } + + @Test + void testGenerateCart_ShouldCreateCartWithNonEmptyCollection() throws Exception { + Object result = generator.generateValueOfType(Cart.class); + + assertNotNull(result, "Generated Cart should not be null"); + assertInstanceOf(Cart.class, result, "Generated object should be instance of Cart"); + + Cart cart = (Cart) result; + List items = cart.getItems(); + + assertNotNull(items, "Cart items should not be null"); + assertFalse(items.isEmpty(), "Cart should have non-empty collection of items"); + } +} \ No newline at end of file From fb43f3446a02c2463abe61af0a5188e2bd6447b3 Mon Sep 17 00:00:00 2001 From: railolog Date: Thu, 6 Nov 2025 16:29:09 +0300 Subject: [PATCH 3/5] remove jar processing --- .../example/generator/InterfaceRegistry.java | 41 ++++--------------- .../org/example/generator/GeneratorTest.java | 28 ++++++------- 2 files changed, 21 insertions(+), 48 deletions(-) diff --git a/src/main/java/org/example/generator/InterfaceRegistry.java b/src/main/java/org/example/generator/InterfaceRegistry.java index f9edf66..74bf989 100644 --- a/src/main/java/org/example/generator/InterfaceRegistry.java +++ b/src/main/java/org/example/generator/InterfaceRegistry.java @@ -3,8 +3,6 @@ import java.io.File; import java.net.URL; import java.util.*; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; public class InterfaceRegistry { private static final String PACKAGE_NAME = "org.example.classes"; @@ -27,27 +25,25 @@ private void registerClass(Class clazz) { Class[] interfaces = clazz.getInterfaces(); for (Class interfaceClass : interfaces) { interfaceToImplementations - .computeIfAbsent(interfaceClass, k -> new ArrayList<>()) - .add(clazz); + .computeIfAbsent(interfaceClass, k -> new ArrayList<>()) + .add(clazz); } } private void scanForGeneratableClasses() { try { String packagePath = PACKAGE_NAME.replace('.', '/'); - + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); Enumeration resources = classLoader.getResources(packagePath); - + while (resources.hasMoreElements()) { URL resource = resources.nextElement(); if (resource.getProtocol().equals("file")) { scanDirectory(new File(resource.getFile()), PACKAGE_NAME); - } else if (resource.getProtocol().equals("jar")) { - scanJarFile(resource.getFile(), packagePath); } } - + initialized = true; } catch (Exception e) { System.err.println("Error scanning for @Generatable classes: " + e.getMessage()); @@ -58,12 +54,12 @@ private void scanDirectory(File directory, String packageName) { if (!directory.exists()) { return; } - + File[] files = directory.listFiles(); if (files == null) { return; } - + for (File file : files) { if (file.isDirectory()) { scanDirectory(file, packageName + "." + file.getName()); @@ -77,27 +73,4 @@ private void scanDirectory(File directory, String packageName) { } } } - - private void scanJarFile(String jarPath, String packagePath) { - try { - String jarFilePath = jarPath.substring(jarPath.indexOf(":") + 1, jarPath.indexOf("!")); - try (JarFile jarFile = new JarFile(jarFilePath)) { - Enumeration entries = jarFile.entries(); - while (entries.hasMoreElements()) { - JarEntry entry = entries.nextElement(); - String entryName = entry.getName(); - if (entryName.startsWith(packagePath) && entryName.endsWith(".class")) { - String className = entryName.replace('/', '.').substring(0, entryName.length() - 6); - try { - Class clazz = Class.forName(className); - registerClass(clazz); - } catch (ClassNotFoundException ignored) { - } - } - } - } - } catch (Exception e) { - System.err.println("Error scanning JAR file: " + e.getMessage()); - } - } } \ No newline at end of file diff --git a/src/test/java/org/example/generator/GeneratorTest.java b/src/test/java/org/example/generator/GeneratorTest.java index 6281518..91fa07a 100644 --- a/src/test/java/org/example/generator/GeneratorTest.java +++ b/src/test/java/org/example/generator/GeneratorTest.java @@ -3,61 +3,61 @@ import org.example.classes.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.RepeatedTest; + import static org.junit.jupiter.api.Assertions.*; import java.util.List; public class GeneratorTest { - + private Generator generator; - + @BeforeEach void setUp() { generator = new Generator(); } - + @Test void testGenerateExample_ShouldCreateObject() throws Exception { Object result = generator.generateValueOfType(Example.class); - + assertNotNull(result, "Generated object should not be null"); assertInstanceOf(Example.class, result, "Generated object should be instance of Example"); } - + @Test void testGenerateProduct_ShouldCreateObject() throws Exception { Object result = generator.generateValueOfType(Product.class); - + assertNotNull(result, "Generated Product should not be null"); assertInstanceOf(Product.class, result, "Generated object should be instance of Product"); - + Product product = (Product) result; assertNotNull(product.getName(), "Product name should not be null"); assertTrue(product.getPrice() != 0 || product.getPrice() == 0, "Product price should be set"); } - + @Test void testGenerateShape_ShouldCreateShapeImplementation() throws Exception { Object result = generator.generateValueOfType(Shape.class); - + assertNotNull(result, "Generated Shape should not be null"); assertInstanceOf(Shape.class, result, "Generated object should implement Shape interface"); assertTrue(result instanceof Rectangle || result instanceof Triangle, "Shape implementation should be either Rectangle or Triangle"); } - + @Test void testGenerateCart_ShouldCreateCartWithNonEmptyCollection() throws Exception { Object result = generator.generateValueOfType(Cart.class); - + assertNotNull(result, "Generated Cart should not be null"); assertInstanceOf(Cart.class, result, "Generated object should be instance of Cart"); - + Cart cart = (Cart) result; List items = cart.getItems(); - + assertNotNull(items, "Cart items should not be null"); assertFalse(items.isEmpty(), "Cart should have non-empty collection of items"); } From 260326036b08b0dc38be6bf3169664c9db9569a5 Mon Sep 17 00:00:00 2001 From: railolog Date: Wed, 24 Dec 2025 23:22:19 +0300 Subject: [PATCH 4/5] add processing generic types --- .../java/org/example/GenerateExample.java | 18 ++- .../java/org/example/classes/Example.java | 10 +- .../java/org/example/generator/Generator.java | 107 ++++++++++++++---- 3 files changed, 105 insertions(+), 30 deletions(-) diff --git a/src/main/java/org/example/GenerateExample.java b/src/main/java/org/example/GenerateExample.java index 3aae693..af2223c 100644 --- a/src/main/java/org/example/GenerateExample.java +++ b/src/main/java/org/example/GenerateExample.java @@ -1,10 +1,14 @@ package org.example; +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + import org.example.classes.*; import org.example.generator.Generator; public class GenerateExample { - public static void main(String[] args) { + public static void main(String[] args) throws NoSuchFieldException { var gen = new Generator(); System.out.println("=== Testing Generator ==="); @@ -76,5 +80,17 @@ public static void main(String[] args) { } catch (Exception e) { System.err.println("Error generating BinaryTreeNode: " + e.getMessage()); } + + Field items = Cart.class.getDeclaredField("items"); + Type genericType = items.getGenericType(); + if (genericType instanceof ParameterizedType pType) { + Type[] actualTypeArguments = pType.getActualTypeArguments(); + for (Type type : actualTypeArguments) { + if (type instanceof Class elementClass) { + System.out.println("Declared element type: " + elementClass.getName()); + // Output: Declared element type: java.lang.String + } + } + } } } \ 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 e0c8d1c..8080791 100644 --- a/src/main/java/org/example/classes/Example.java +++ b/src/main/java/org/example/classes/Example.java @@ -1,17 +1,17 @@ package org.example.classes; +import java.util.Map; + +import lombok.ToString; import org.example.generator.Generatable; +@ToString @Generatable public class Example { int i; + public Map myMap; public Example(int i) { this.i = i; } - - @Override - public String toString() { - return "Example(" + i + ")"; - } } diff --git a/src/main/java/org/example/generator/Generator.java b/src/main/java/org/example/generator/Generator.java index 4b68453..b67c4b8 100644 --- a/src/main/java/org/example/generator/Generator.java +++ b/src/main/java/org/example/generator/Generator.java @@ -4,9 +4,11 @@ import java.util.*; public class Generator { + + private static final int MAX_COLLECTION_SIZE = 10; + private static final int MAX_DEPTH = 10; + private final Random random = new Random(); - private final int maxDepth = 3; - private final int maxCollectionSize = 5; private final InterfaceRegistry interfaceRegistry = new InterfaceRegistry(); public Object generateValueOfType(Class clazz) throws Exception { @@ -14,7 +16,11 @@ public Object generateValueOfType(Class clazz) throws Exception { } private Object generateValueOfType(Class clazz, int currentDepth) throws Exception { - if (currentDepth > maxDepth) { + return generateValueOfType(clazz, currentDepth, null); + } + + private Object generateValueOfType(Class clazz, int currentDepth, Field field) throws Exception { + if (currentDepth > MAX_DEPTH) { return null; } @@ -48,10 +54,13 @@ private Object generateValueOfType(Class clazz, int currentDepth) throws Exce } if (List.class.isAssignableFrom(clazz)) { - return generateList(currentDepth); + return field == null ? new ArrayList<>() : generateList(field, currentDepth); } if (Set.class.isAssignableFrom(clazz)) { - return generateSet(currentDepth); + return field == null ? new HashSet<>() : generateSet(field, currentDepth); + } + if (Map.class.isAssignableFrom(clazz)) { + return field == null ? new HashMap<>() : generateMap(field, currentDepth); } if (clazz.isInterface()) { @@ -82,46 +91,96 @@ private Object generateObject(Class clazz, int currentDepth) throws Exception params[i] = generateValueOfType(paramTypes[i], currentDepth + 1); } - return selectedConstructor.newInstance(params); + Object instance = selectedConstructor.newInstance(params); + populateFields(instance, clazz, currentDepth); + return instance; } - private Constructor selectRandomConstructor(Constructor[] constructors) { - int idx = random.nextInt(constructors.length); - return constructors[idx]; - } + public void populateFields(Object instance, Class clazz, int currentDepth) { + for (Field field : getAllFields(clazz)) { + if (Modifier.isFinal(field.getModifiers())) { + continue; + } - private String generateRandomString() { - String[] words = {"apple", "banana", "cherry", "dog", "elephant", "forest", "guitar", "house", "ice", "jungle"}; - return words[random.nextInt(words.length)] + random.nextInt(100); + try { + field.setAccessible(true); + Object fieldValue = generateValueOfType(field.getType(), currentDepth + 1, field); + field.set(instance, fieldValue); + } catch (Exception ignored) { + } + } } - private List generateList(int depth) throws Exception { + private List generateList(Field field, int currentDepth) throws Exception { List list = new ArrayList<>(); - int size = random.nextInt(maxCollectionSize); + Class elementClass = resolveSingleTypeArgument(field, 0); + int size = random.nextInt(MAX_COLLECTION_SIZE); - Class[] possibleTypes = {String.class, Integer.class, Double.class}; for (int i = 0; i < size; i++) { - Class randomType = possibleTypes[random.nextInt(possibleTypes.length)]; - list.add(generateValueOfType(randomType, depth + 1)); + list.add(elementClass != null ? generateValueOfType(elementClass, currentDepth + 1) : null); } return list; } - private Set generateSet(int depth) throws Exception { + private Set generateSet(Field field, int currentDepth) throws Exception { Set set = new HashSet<>(); - int size = random.nextInt(maxCollectionSize); + Class elementClass = resolveSingleTypeArgument(field, 0); + int size = random.nextInt(MAX_COLLECTION_SIZE); - Class[] possibleTypes = {String.class, Integer.class, Double.class}; for (int i = 0; i < size; i++) { - Class randomType = possibleTypes[random.nextInt(possibleTypes.length)]; - set.add(generateValueOfType(randomType, depth + 1)); + set.add(elementClass != null ? generateValueOfType(elementClass, currentDepth + 1) : null); } return set; } + private Map generateMap(Field field, int currentDepth) throws Exception { + Map map = new HashMap<>(); + Class keyClass = resolveSingleTypeArgument(field, 0); + Class valueClass = resolveSingleTypeArgument(field, 1); + int size = random.nextInt(MAX_COLLECTION_SIZE); + + for (int i = 0; i < size; i++) { + Object key = keyClass != null ? generateValueOfType(keyClass, currentDepth + 1) : null; + Object value = valueClass != null ? generateValueOfType(valueClass, currentDepth + 1) : null; + map.put(key, value); + } + return map; + } + + public static Class resolveSingleTypeArgument(Field field, int idx) { + Type genericType = field.getGenericType(); + if (genericType instanceof ParameterizedType p) { + Type[] args = p.getActualTypeArguments(); + if (idx >= 0 && idx < args.length && args[idx] instanceof Class clazz) { + return clazz; + } + } + return null; + } + + public static List getAllFields(Class type) { + List result = new ArrayList<>(); + Class cur = type; + while (cur != null && cur != Object.class) { + Collections.addAll(result, cur.getDeclaredFields()); + cur = cur.getSuperclass(); + } + return result; + } + + private Constructor selectRandomConstructor(Constructor[] constructors) { + int idx = random.nextInt(constructors.length); + return constructors[idx]; + } + + private String generateRandomString() { + String[] words = {"apple", "banana", "cherry", "dog", "elephant", "forest", "guitar", "house", "ice", "jungle"}; + return words[random.nextInt(words.length)] + random.nextInt(100); + } + private Object generateInterfaceImplementation(Class interfaceClass, int depth) throws Exception { List> implementations = interfaceRegistry.getImplementations(interfaceClass); - + if (implementations.isEmpty()) { throw new IllegalArgumentException("No @Generatable implementations found for interface: " + interfaceClass.getName()); } From b8aa442f034a427d9283971153d404658a1e2640 Mon Sep 17 00:00:00 2001 From: railolog Date: Wed, 24 Dec 2025 23:28:10 +0300 Subject: [PATCH 5/5] handle fields not from constructor and exceptions when trying to instantiate --- .../java/org/example/generator/Generator.java | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/example/generator/Generator.java b/src/main/java/org/example/generator/Generator.java index b67c4b8..f98c30b 100644 --- a/src/main/java/org/example/generator/Generator.java +++ b/src/main/java/org/example/generator/Generator.java @@ -81,17 +81,26 @@ private Object generateObject(Class clazz, int currentDepth) throws Exception return null; } - Constructor selectedConstructor = selectRandomConstructor(constructors); - selectedConstructor.setAccessible(true); - - Class[] paramTypes = selectedConstructor.getParameterTypes(); - Object[] params = new Object[paramTypes.length]; + Object instance = null; + for (Constructor constructor : constructors) { + constructor.setAccessible(true); + Class[] paramTypes = constructor.getParameterTypes(); + Object[] params = new Object[paramTypes.length]; + + for (int i = 0; i < paramTypes.length; i++) { + params[i] = generateValueOfType(paramTypes[i], currentDepth + 1); + } - for (int i = 0; i < paramTypes.length; i++) { - params[i] = generateValueOfType(paramTypes[i], currentDepth + 1); + try { + instance = constructor.newInstance(params); + } catch (Exception e) { + System.out.println("Error generating object: " + e.getMessage()); + } + } + if (instance == null) { + return null; } - Object instance = selectedConstructor.newInstance(params); populateFields(instance, clazz, currentDepth); return instance; } @@ -168,11 +177,6 @@ public static List getAllFields(Class type) { return result; } - private Constructor selectRandomConstructor(Constructor[] constructors) { - int idx = random.nextInt(constructors.length); - return constructors[idx]; - } - private String generateRandomString() { String[] words = {"apple", "banana", "cherry", "dog", "elephant", "forest", "guitar", "house", "ice", "jungle"}; return words[random.nextInt(words.length)] + random.nextInt(100);