From 74eb63619d9c48ce0d3c09bc7146f4bac95484dc Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 15:05:18 +0000 Subject: [PATCH] add deadline --- README.md | 1 + build.gradle.kts | 21 +++ .../java/org/example/GenerateExample.java | 4 +- .../org/example/classes/BinaryTreeNode.java | 12 ++ src/main/java/org/example/classes/Cart.java | 10 ++ .../java/org/example/classes/Example.java | 3 + .../java/org/example/classes/Product.java | 9 +- .../java/org/example/classes/Rectangle.java | 11 ++ src/main/java/org/example/classes/Shape.java | 3 + .../java/org/example/classes/Triangle.java | 12 ++ .../org/example/generator/Generatable.java | 8 + .../example/generator/GeneratableIndex.java | 37 +++++ .../generator/GeneratableProcessor.java | 51 +++++++ .../java/org/example/generator/Generator.java | 141 +++++++++++++++++- .../javax.annotation.processing.Processor | 1 + .../org/example/generator/GeneratorTest.java | 73 +++++++++ 16 files changed, 387 insertions(+), 10 deletions(-) create mode 100644 src/main/java/org/example/generator/Generatable.java create mode 100644 src/main/java/org/example/generator/GeneratableIndex.java create mode 100644 src/main/java/org/example/generator/GeneratableProcessor.java create mode 100644 src/main/resources/META-INF/services/javax.annotation.processing.Processor create mode 100644 src/test/java/org/example/generator/GeneratorTest.java 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** Ваша задача --- написать генератор экземпляров произвольных классов. diff --git a/build.gradle.kts b/build.gradle.kts index b4738ae..26bf0a1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -17,4 +17,25 @@ dependencies { tasks.test { useJUnitPlatform() +} + +sourceSets { + create("processor") { + java.setSrcDirs(listOf("src/main/java")) + resources.setSrcDirs(listOf("src/main/resources")) + } +} + +tasks.named("compileProcessorJava").configure { + source = fileTree("src/main/java") { + include("org/example/generator/GeneratableProcessor.java") + } +} + +tasks.named("compileJava").configure { + dependsOn("compileProcessorJava", "processProcessorResources") + options.annotationProcessorPath = files( + sourceSets.getByName("processor").output, + configurations.getByName("processorCompileClasspath") + ) } \ No newline at end of file diff --git a/src/main/java/org/example/GenerateExample.java b/src/main/java/org/example/GenerateExample.java index 47679a9..0c40f49 100644 --- a/src/main/java/org/example/GenerateExample.java +++ b/src/main/java/org/example/GenerateExample.java @@ -1,14 +1,16 @@ package org.example; +import org.example.classes.Cart; import org.example.classes.Example; +import org.example.classes.Shape; import org.example.generator.Generator; public class GenerateExample { public static void main(String[] args) { var gen = new Generator(); try { - Object generated = gen.generateValueOfType(Example.class); + Object generated = gen.generateValueOfType(Shape.class); System.out.println(generated); } catch (Throwable e) { throw new RuntimeException(e); 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..84c4e75 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..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..58f286c 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,9 @@ public boolean equals(Object obj) { @Override public String toString() { - return super.toString(); + return "Product{" + + "name='" + name.substring(0, Math.min(name.length(), 3)) + "...'" + + ", 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..7a6f41f 100644 --- a/src/main/java/org/example/classes/Shape.java +++ b/src/main/java/org/example/classes/Shape.java @@ -1,5 +1,8 @@ package org.example.classes; +import org.example.generator.Generatable; + +@Generatable public interface Shape { double getArea(); double getPerimeter(); 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..7a13e8b --- /dev/null +++ b/src/main/java/org/example/generator/Generatable.java @@ -0,0 +1,8 @@ +package org.example.generator; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Generatable { +} diff --git a/src/main/java/org/example/generator/GeneratableIndex.java b/src/main/java/org/example/generator/GeneratableIndex.java new file mode 100644 index 0000000..08020e6 --- /dev/null +++ b/src/main/java/org/example/generator/GeneratableIndex.java @@ -0,0 +1,37 @@ +package org.example.generator; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.lang.reflect.Modifier; +import java.nio.charset.StandardCharsets; +import java.util.LinkedHashSet; +import java.util.Set; + +public class GeneratableIndex { + private static final String INDEX_PATH = "META-INF/org/example/generator/GeneratableIndex"; + + public static Set> loadAnnotatedClasses() { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + var url = cl.getResource(INDEX_PATH); + if (url == null) { + return Set.of(); + } + + Set> result = new LinkedHashSet<>(); + try (var in = url.openStream(); + BufferedReader r = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)) + ) { + String line; + while ((line = r.readLine()) != null) { + String cn = line.trim(); + if (cn.isEmpty()) continue; + try { + Class c = Class.forName(cn, false, cl); + result.add(c); + } catch (Throwable ignored) { + } + } + } catch (Exception ignored) { + } + return result; + } +} diff --git a/src/main/java/org/example/generator/GeneratableProcessor.java b/src/main/java/org/example/generator/GeneratableProcessor.java new file mode 100644 index 0000000..7215590 --- /dev/null +++ b/src/main/java/org/example/generator/GeneratableProcessor.java @@ -0,0 +1,51 @@ +package org.example.generator; + +import javax.annotation.processing.*; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.*; +import javax.tools.Diagnostic; +import javax.tools.FileObject; +import javax.tools.StandardLocation; +import java.io.IOException; +import java.io.Writer; +import java.util.LinkedHashSet; +import java.util.Set; + +@SupportedSourceVersion(SourceVersion.RELEASE_23) +@SupportedAnnotationTypes("org.example.generator.Generatable") +public class GeneratableProcessor extends AbstractProcessor { + private static final String INDEX_PATH = "META-INF/org/example/generator/GeneratableIndex"; + private final Set collected = new LinkedHashSet<>(); + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + for (Element e : roundEnv.getElementsAnnotatedWith( + processingEnv.getElementUtils().getTypeElement("org.example.generator.Generatable"))) { + if (e.getKind() == ElementKind.CLASS || e.getKind() == ElementKind.INTERFACE) { + String fqcn = ((TypeElement) e).getQualifiedName().toString(); + collected.add(fqcn); + } + } + + if (roundEnv.processingOver()) { + try { + writeIndex(); + } catch (IOException ex) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, + "Failed to write index: " + ex.getMessage()); + } + } + return true; + } + + private void writeIndex() throws IOException { + FileObject fo = processingEnv.getFiler() + .createResource(StandardLocation.CLASS_OUTPUT, "", INDEX_PATH); + try (Writer w = fo.openWriter()) { + for (String cn : collected) { + w.write(cn); + w.write('\n'); + } + } + } +} diff --git a/src/main/java/org/example/generator/Generator.java b/src/main/java/org/example/generator/Generator.java index 9d86bfb..afd49c2 100644 --- a/src/main/java/org/example/generator/Generator.java +++ b/src/main/java/org/example/generator/Generator.java @@ -1,18 +1,145 @@ 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 Set> generatable = GeneratableIndex.loadAnnotatedClasses(); + private final Map, Integer> classCount = new HashMap<>(); public Object generateValueOfType(Class clazz) throws InvocationTargetException, InstantiationException, IllegalAccessException { - Constructor[] constructors = clazz.getDeclaredConstructors(); + return generate(clazz); + } + + private double getCycleBreakProbability(Class clazz) { + int count = classCount.getOrDefault(clazz, 0); + if (count == 0) { + return 0.0; + } + return 1.0 - Math.pow(0.5, count); + } + + private Object generate(Type type) throws InvocationTargetException, InstantiationException, IllegalAccessException { + if (type instanceof Class clazz) { + return generate(clazz); + } else if (type instanceof ParameterizedType parameterizedType) { + return generate(parameterizedType); + } + return null; + } + + private Object generate(ParameterizedType parameterizedType) throws InvocationTargetException, InstantiationException, IllegalAccessException { + Type rawType = parameterizedType.getRawType(); + if (rawType instanceof Class rawClass) { + if (rawClass.isAssignableFrom(List.class)) { + return generateList(parameterizedType.getActualTypeArguments()[0]); + } + } + return null; + } + + private Object generate(Class clazz) throws InvocationTargetException, InstantiationException, IllegalAccessException { + if (clazz.isArray()) { + return generateArray(clazz.componentType()); + } + if (clazz.isInterface()) { + return generateForInterface(clazz); + } + if (clazz.isPrimitive()) { + return generatePrimitive(clazz); + } + if (clazz.isAssignableFrom(String.class)) { + return generateRandomString(); + } + if (clazz.isAssignableFrom(Integer.class)) { + return random.nextInt(); + } + if (clazz.isAssignableFrom(Double.class)) { + return random.nextDouble(); + } + if (clazz.isAssignableFrom(Long.class)) { + return random.nextLong(); + } + if (clazz.isAssignableFrom(Boolean.class)) { + return random.nextBoolean(); + } + + int currentCount = classCount.getOrDefault(clazz, 0); + double breakProbability = getCycleBreakProbability(clazz); + + if (currentCount > 0 && random.nextDouble() < breakProbability) { + return null; + } + + classCount.put(clazz, currentCount + 1); + + try { + Constructor[] constructors = clazz.getDeclaredConstructors(); + int randomConstructorIndex = new Random().nextInt(constructors.length); + Constructor randomConstructor = constructors[randomConstructorIndex]; + Type[] params = randomConstructor.getGenericParameterTypes(); + Object[] objects = new Object[params.length]; + for (int i = 0; i < params.length; i++) { + objects[i] = generate(params[i]); + } + return randomConstructor.newInstance(objects); + } finally { + // Уменьшаем счетчик после создания объекта + classCount.put(clazz, Math.max(0, classCount.get(clazz) - 1)); + } + } + + private String generateRandomString() { + int length = random.nextInt(256); + StringBuilder sb = new StringBuilder(length); + for (int i = 0; i < length; i++) { + sb.append((char) random.nextInt(32, 127)); + } + return sb.toString(); + } - int randomConstructorIndex = new Random().nextInt(constructors.length); - Constructor randomConstructor = constructors[randomConstructorIndex]; - return randomConstructor.newInstance(111); + private List generateList(Type actualTypeArgument) throws InvocationTargetException, InstantiationException, IllegalAccessException { + int length = random.nextInt(256); + List list = new ArrayList<>(length); + for (int i = 0; i < length; i++) { + list.add(generate(actualTypeArgument)); + } + return list; } + private Object generatePrimitive(Class clazz) { + if (clazz.isAssignableFrom(Integer.TYPE)) { + return random.nextInt(); + } else if (clazz.isAssignableFrom(Boolean.TYPE)) { + return random.nextBoolean(); + } else if (clazz.isAssignableFrom(Long.TYPE)) { + return random.nextLong(); + } else if (clazz.isAssignableFrom(Double.TYPE)) { + return random.nextDouble(); + } + return null; + } + private Object generateForInterface(Class clazz) throws InvocationTargetException, InstantiationException, IllegalAccessException { + if (clazz.isAssignableFrom(List.class)) { + return generateList(clazz.getGenericInterfaces()[0]); + } + List> assignable = new LinkedList<>(); + for (Class classes: generatable) { + if (clazz.isAssignableFrom(classes) && !classes.isInterface()) { + assignable.add(classes); + } + } + return generateValueOfType(assignable.get(random.nextInt(assignable.size()))); + } + + private Object[] generateArray(Class clazz) throws InvocationTargetException, InstantiationException, IllegalAccessException { + int length = random.nextInt(256); + Object[] ans = (Object[]) Array.newInstance(clazz, length); + for (int i = 0; i < length; i++) { + ans[i] = generateValueOfType(clazz); + } + return ans; + } } diff --git a/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/src/main/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 0000000..542a31f --- /dev/null +++ b/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +org.example.generator.GeneratableProcessor \ 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..3e43c9b --- /dev/null +++ b/src/test/java/org/example/generator/GeneratorTest.java @@ -0,0 +1,73 @@ +package org.example.generator; + +import org.example.classes.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class GeneratorTest { + + private Generator generator; + + @BeforeEach + void setUp() { + generator = new Generator(); + } + + @Test + void testGenerateRectangle() throws Exception { + Object result = generator.generateValueOfType(Rectangle.class); + + assertNotNull(result); + assertTrue(result instanceof Rectangle); + } + + @Test + void testGenerateTriangle() throws Exception { + Object result = generator.generateValueOfType(Triangle.class); + + assertNotNull(result); + assertTrue(result instanceof Triangle); + } + + @Test + void testGenerateProduct() throws Exception { + Object result = generator.generateValueOfType(Product.class); + + assertNotNull(result); + assertTrue(result instanceof Product); + } + + @Test + void testGenerateCart() throws Exception { + Object result = generator.generateValueOfType(Cart.class); + + assertNotNull(result); + assertTrue(result instanceof Cart); + } + + @Test + void testGenerateBinaryTreeNode() throws Exception { + Object result = generator.generateValueOfType(BinaryTreeNode.class); + + assertNotNull(result); + assertTrue(result instanceof BinaryTreeNode); + } + + @Test + void testGenerateShape() throws Exception { + Object result = generator.generateValueOfType(Shape.class); + + assertNotNull(result); + assertTrue(result instanceof Shape); + } + + @Test + void testGenerateExample() throws Exception { + Object result = generator.generateValueOfType(Example.class); + + assertNotNull(result); + assertTrue(result instanceof Example); + } +}