-
Notifications
You must be signed in to change notification settings - Fork 34
Lab2 Solution #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| 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 {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,18 +1,189 @@ | ||
| package org.example.generator; | ||
|
|
||
| import java.lang.reflect.Constructor; | ||
| import java.lang.reflect.Field; | ||
| import java.lang.reflect.InvocationTargetException; | ||
| import java.util.Random; | ||
| import java.lang.reflect.ParameterizedType; | ||
| import java.util.*; | ||
| import java.util.function.Supplier; | ||
|
|
||
| import static org.example.generator.PackageUtils.getAnnotatedClass; | ||
|
|
||
| public class Generator { | ||
| private final Random random; | ||
| private final int maxDepth; | ||
|
|
||
| private final Map<Class<?>, Supplier<?>> primitiveTypesGenerators; | ||
|
|
||
| private static final Set<Class<?>> IMMUTABLE_KEY_TYPES = Set.of( | ||
| int.class, Integer.class, long.class, Long.class, double.class, Double.class, | ||
| float.class, Float.class, boolean.class, Boolean.class, char.class, Character.class, | ||
| String.class | ||
| ); | ||
|
|
||
| public Generator() { | ||
| this(new Random(), 4); | ||
| } | ||
|
|
||
| public Generator(Random random, int maxDepth) { | ||
| this.random = random; | ||
| this.maxDepth = maxDepth; | ||
| this.primitiveTypesGenerators = initializePrimitiveGenerators(); | ||
| } | ||
|
|
||
| public Object generateByType(Class<?> clazz, | ||
| String packageName | ||
| ) | ||
| throws IllegalArgumentException, | ||
| InvocationTargetException, | ||
| InstantiationException, | ||
| IllegalAccessException | ||
| { | ||
| var annotatedClass = getAnnotatedClass(clazz, Generatable.class, packageName); | ||
|
|
||
| if (annotatedClass.isEmpty()) { | ||
| throw new IllegalArgumentException(clazz.getCanonicalName() + " is not annotated with @" + Generatable.class.getCanonicalName()); | ||
| } | ||
|
|
||
| return createInstance(annotatedClass.get(), 0); | ||
| } | ||
|
|
||
| private Object createInstance(Class<?> clazz, int depth) | ||
| throws InvocationTargetException, InstantiationException, IllegalAccessException { | ||
|
|
||
| if (primitiveTypesGenerators.containsKey(clazz)) { | ||
| return primitiveTypesGenerators.get(clazz); | ||
| } | ||
|
|
||
| if (depth > maxDepth) { | ||
| return null; | ||
| } | ||
|
|
||
| if (Collection.class.isAssignableFrom(clazz)) { | ||
| return new ArrayList<>(); | ||
| } | ||
|
|
||
| if (Map.class.isAssignableFrom(clazz)) { | ||
| return new HashMap<>(); | ||
| } | ||
|
|
||
| var constructors = clazz.getDeclaredConstructors(); | ||
| var randomConstructorIndex = constructors.length == 0 ? 0 : random.nextInt(constructors.length); | ||
| var constructor = constructors[randomConstructorIndex]; | ||
| constructor.setAccessible(true); | ||
|
|
||
| var paramTypes = constructor.getParameterTypes(); | ||
| var params = new Object[paramTypes.length]; | ||
|
|
||
| public Object generateValueOfType(Class<?> clazz) throws InvocationTargetException, InstantiationException, IllegalAccessException { | ||
| Constructor<?>[] constructors = clazz.getDeclaredConstructors(); | ||
| for (int i = 0; i < paramTypes.length; i++) { | ||
| params[i] = createInstance(paramTypes[i], depth + 1); | ||
| } | ||
|
|
||
| int randomConstructorIndex = new Random().nextInt(constructors.length); | ||
| Constructor<?> randomConstructor = constructors[randomConstructorIndex]; | ||
| return randomConstructor.newInstance(111); | ||
| var instance = constructor.newInstance(params); | ||
|
|
||
| // Заполняем поля объекта | ||
| for (var field : clazz.getDeclaredFields()) { | ||
| // Пропускаем статические поля | ||
| if (Modifier.isStatic(field.getModifiers())) { | ||
| continue; | ||
| } | ||
|
|
||
| field.setAccessible(true); | ||
| var fieldType = field.getType(); | ||
| Object value; | ||
|
|
||
| if (Collection.class.isAssignableFrom(fieldType)) { | ||
| value = generateCollection(field, depth + 1); | ||
| } else if (Map.class.isAssignableFrom(fieldType)) { | ||
| value = generateMap(field, depth + 1); | ||
| } else if (primitiveTypesGenerators.containsKey(fieldType)) { | ||
| value = primitiveTypesGenerators.get(fieldType); | ||
| } else { | ||
| value = createInstance(fieldType, depth + 1); | ||
| } | ||
|
|
||
| field.set(instance, value); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. а если оно статик? нет ли нюансов с остальными модификаторами?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. да, это может привести к ошибкам. добавил исключение изменения статических полей в коде выше |
||
| } | ||
|
|
||
| return instance; | ||
| } | ||
|
|
||
| private Map<Class<?>, Supplier<?>> initializePrimitiveGenerators() { | ||
| Map<Class<?>, Supplier<?>> generators = new HashMap<>(); | ||
|
|
||
| generators.put(int.class, () -> random.nextInt(100)); | ||
| generators.put(Integer.class, () -> random.nextInt(100)); | ||
| generators.put(long.class, () -> random.nextLong(100)); | ||
| generators.put(Long.class, () -> random.nextLong(100)); | ||
| generators.put(double.class, () -> random.nextDouble(100)); | ||
| generators.put(Double.class, () -> random.nextDouble(100)); | ||
| generators.put(float.class, () -> random.nextFloat(100)); | ||
| generators.put(Float.class, () -> random.nextFloat(100)); | ||
| generators.put(boolean.class, random::nextBoolean); | ||
| generators.put(Boolean.class, random::nextBoolean); | ||
| generators.put(char.class, () -> (char) (random.nextInt(26) + 'a')); | ||
| generators.put(Character.class, () -> (char) (random.nextInt(26) + 'a')); | ||
| generators.put(String.class, getRandomString()); | ||
|
|
||
| return generators; | ||
| } | ||
|
|
||
| private Object generateCollection(Field field, int depth) | ||
| throws InvocationTargetException, InstantiationException, IllegalAccessException { | ||
| var collection = new ArrayList<>(); | ||
|
|
||
| } | ||
| var genericType = field.getGenericType(); | ||
| if (genericType instanceof ParameterizedType parameterizedType) { | ||
| var typeArgs = parameterizedType.getActualTypeArguments(); | ||
| if (typeArgs.length == 1 && typeArgs[0] instanceof Class<?> elementClass) { | ||
| int size = random.nextInt(3) + 1; // Используем переданный Random | ||
| for (int i = 0; i < size; i++) { | ||
| collection.add(createInstance(elementClass, depth + 1)); | ||
| } | ||
| } | ||
| } | ||
| return collection; | ||
| } | ||
|
|
||
| private Object generateMap(Field field, int depth) | ||
| throws InvocationTargetException, InstantiationException, IllegalAccessException | ||
| { | ||
| var map = new HashMap<>(); | ||
|
|
||
| var genericType = field.getGenericType(); | ||
| if (genericType instanceof ParameterizedType parameterizedType) { | ||
| var typeArgs = parameterizedType.getActualTypeArguments(); | ||
| if (typeArgs.length == 2 && typeArgs[0] instanceof Class<?> keyClass && | ||
| typeArgs[1] instanceof Class<?> valueClass) { | ||
| int size = random.nextInt(3) + 1; // Используем переданный Random | ||
| for (int i = 0; i < size; i++) { | ||
| Object key; | ||
| if (keyClass.isEnum() || IMMUTABLE_KEY_TYPES.contains(keyClass)) { | ||
| key = createInstance(keyClass, depth + 1); | ||
| } else { | ||
| throw new IllegalArgumentException("key for Map is not immutable"); | ||
| } | ||
| Object value = createInstance(valueClass, depth + 1); | ||
| map.put(key, value); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return map; | ||
| } | ||
|
|
||
| private Supplier<String> getRandomString() { | ||
| return () -> { | ||
| int length = 10; | ||
| char[] chars = new char[length]; | ||
|
|
||
| for (int i = 0; i < length; i++) { | ||
| boolean lower = random.nextBoolean(); | ||
| char left = lower ? 'a' : 'A'; | ||
| char right = lower ? 'z' : 'Z'; | ||
| chars[i] = (char) (random.nextInt(right - left + 1) + left); | ||
| } | ||
|
|
||
| return new String(chars); | ||
| }; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Кажется вы забыли тестики. В связи с этим вопрос -- а если я на вход вашему главному методу дам коллекцию какую нибудь типа списка, оно будет работать?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Хотя по условиям этого особо не требуется конечно
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Если коллекция не помечена аннотацией, то главный метод выбросит исключение. Если коллекция помечена аннотацией, то метод просто создаст пустую коллекцию