From 1526c25a21be6812ccc4c76fd939964c3f74c2db Mon Sep 17 00:00:00 2001 From: Ihor Vasylenko Date: Tue, 8 Aug 2023 15:13:01 +0300 Subject: [PATCH 1/3] implemented task --- src/main/java/mate.academy/Main.java | 3 +- src/main/java/mate.academy/lib/Component.java | 7 +++ src/main/java/mate.academy/lib/Inject.java | 7 +++ src/main/java/mate.academy/lib/Injector.java | 62 ++++++++++++++++++- .../service/impl/FileReaderServiceImpl.java | 2 + .../service/impl/ProductParserImpl.java | 2 + .../service/impl/ProductServiceImpl.java | 5 ++ 7 files changed, 85 insertions(+), 3 deletions(-) diff --git a/src/main/java/mate.academy/Main.java b/src/main/java/mate.academy/Main.java index 9be740020..4afee2f36 100644 --- a/src/main/java/mate.academy/Main.java +++ b/src/main/java/mate.academy/Main.java @@ -8,9 +8,8 @@ public class Main { public static void main(String[] args) { - // Please test your Injector here. Feel free to push this class as a part of your solution Injector injector = Injector.getInjector(); - ProductService productService = null; + ProductService productService = (ProductService) injector.getInstance(ProductService.class); List products = productService.getAllFromFile("products.txt"); products.forEach(System.out::println); } diff --git a/src/main/java/mate.academy/lib/Component.java b/src/main/java/mate.academy/lib/Component.java index c7e830420..0cf5e1cf7 100644 --- a/src/main/java/mate.academy/lib/Component.java +++ b/src/main/java/mate.academy/lib/Component.java @@ -1,5 +1,12 @@ package mate.academy.lib; +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 Component { } diff --git a/src/main/java/mate.academy/lib/Inject.java b/src/main/java/mate.academy/lib/Inject.java index 1741f9df9..ad8411675 100644 --- a/src/main/java/mate.academy/lib/Inject.java +++ b/src/main/java/mate.academy/lib/Inject.java @@ -1,5 +1,12 @@ package mate.academy.lib; +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.FIELD) public @interface Inject { } diff --git a/src/main/java/mate.academy/lib/Injector.java b/src/main/java/mate.academy/lib/Injector.java index e87b914ad..883e56df7 100644 --- a/src/main/java/mate.academy/lib/Injector.java +++ b/src/main/java/mate.academy/lib/Injector.java @@ -1,13 +1,73 @@ package mate.academy.lib; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; +import mate.academy.service.FileReaderService; +import mate.academy.service.ProductParser; +import mate.academy.service.ProductService; +import mate.academy.service.impl.FileReaderServiceImpl; +import mate.academy.service.impl.ProductParserImpl; +import mate.academy.service.impl.ProductServiceImpl; + public class Injector { private static final Injector injector = new Injector(); + private final Map, Object> instances = new HashMap<>(); public static Injector getInjector() { return injector; } public Object getInstance(Class interfaceClazz) { - return null; + Class clazz = findImplementation(interfaceClazz); + Object clazzImplementationInstance = null; + Field[] declaredFields = clazz.getDeclaredFields(); + for (Field field : declaredFields) { + if (field.isAnnotationPresent(Inject.class)) { + Object fieldInstance = getInstance(field.getType()); + clazzImplementationInstance = createNewInstance(clazz); + try { + field.setAccessible(true); + field.set(clazzImplementationInstance, fieldInstance); + } catch (IllegalAccessException e) { + throw new RuntimeException("Can`t initialize field value. Class: " + + clazz.getName() + ". Field: " + field.getName()); + } + } + } + if (clazzImplementationInstance == null) { + clazzImplementationInstance = createNewInstance(clazz); + } + return clazzImplementationInstance; + } + + private Object createNewInstance(Class clazz) { + if (instances.containsKey(clazz)) { + return instances.get(clazz); + } + try { + Constructor constructor = clazz.getConstructor(); + Object instance = constructor.newInstance(); + instances.put(clazz, instance); + return instance; + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Can`t create a new instance of " + clazz.getName()); + } + } + + private Class findImplementation(Class interfaceClazz) { + Map, Class> interfaceImplementations = Map.of( + ProductParser.class, ProductParserImpl.class, + FileReaderService.class, FileReaderServiceImpl.class, + ProductService.class, ProductServiceImpl.class + ); + if (!interfaceImplementations.containsKey(interfaceClazz)) { + throw new RuntimeException("Unsupported class: " + interfaceClazz.getName()); + } + if (interfaceClazz.isInterface()) { + return interfaceImplementations.get(interfaceClazz); + } + return interfaceClazz; } } diff --git a/src/main/java/mate.academy/service/impl/FileReaderServiceImpl.java b/src/main/java/mate.academy/service/impl/FileReaderServiceImpl.java index 58540d301..752474e0d 100644 --- a/src/main/java/mate.academy/service/impl/FileReaderServiceImpl.java +++ b/src/main/java/mate.academy/service/impl/FileReaderServiceImpl.java @@ -4,8 +4,10 @@ import java.io.IOException; import java.nio.file.Files; import java.util.List; +import mate.academy.lib.Component; import mate.academy.service.FileReaderService; +@Component public class FileReaderServiceImpl implements FileReaderService { @Override public List readFile(String fileName) { diff --git a/src/main/java/mate.academy/service/impl/ProductParserImpl.java b/src/main/java/mate.academy/service/impl/ProductParserImpl.java index 00837f28d..03ea21a56 100644 --- a/src/main/java/mate.academy/service/impl/ProductParserImpl.java +++ b/src/main/java/mate.academy/service/impl/ProductParserImpl.java @@ -1,9 +1,11 @@ package mate.academy.service.impl; import java.math.BigDecimal; +import mate.academy.lib.Component; import mate.academy.model.Product; import mate.academy.service.ProductParser; +@Component public class ProductParserImpl implements ProductParser { public static final int ID_POSITION = 0; public static final int NAME_POSITION = 1; diff --git a/src/main/java/mate.academy/service/impl/ProductServiceImpl.java b/src/main/java/mate.academy/service/impl/ProductServiceImpl.java index 755304777..425ca5552 100644 --- a/src/main/java/mate.academy/service/impl/ProductServiceImpl.java +++ b/src/main/java/mate.academy/service/impl/ProductServiceImpl.java @@ -2,13 +2,18 @@ import java.util.List; import java.util.stream.Collectors; +import mate.academy.lib.Component; +import mate.academy.lib.Inject; import mate.academy.model.Product; import mate.academy.service.FileReaderService; import mate.academy.service.ProductParser; import mate.academy.service.ProductService; +@Component public class ProductServiceImpl implements ProductService { + @Inject private ProductParser productParser; + @Inject private FileReaderService fileReaderService; @Override From 72e808f7f443df1d99bf7eba43936f85e7245075 Mon Sep 17 00:00:00 2001 From: Ihor Vasylenko Date: Tue, 8 Aug 2023 15:15:06 +0300 Subject: [PATCH 2/3] implemented task --- src/main/java/mate.academy/lib/Component.java | 1 - src/main/java/mate.academy/lib/Inject.java | 1 - 2 files changed, 2 deletions(-) diff --git a/src/main/java/mate.academy/lib/Component.java b/src/main/java/mate.academy/lib/Component.java index 0cf5e1cf7..46e39cec8 100644 --- a/src/main/java/mate.academy/lib/Component.java +++ b/src/main/java/mate.academy/lib/Component.java @@ -8,5 +8,4 @@ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Component { - } diff --git a/src/main/java/mate.academy/lib/Inject.java b/src/main/java/mate.academy/lib/Inject.java index ad8411675..0730cb740 100644 --- a/src/main/java/mate.academy/lib/Inject.java +++ b/src/main/java/mate.academy/lib/Inject.java @@ -8,5 +8,4 @@ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Inject { - } From 9e25d9a3f60f14cec4b085329d65be2d4b918839 Mon Sep 17 00:00:00 2001 From: Ihor Vasylenko Date: Wed, 9 Aug 2023 11:23:54 +0300 Subject: [PATCH 3/3] improved solution --- src/main/java/mate.academy/lib/Injector.java | 30 +++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/main/java/mate.academy/lib/Injector.java b/src/main/java/mate.academy/lib/Injector.java index 883e56df7..6e084858a 100644 --- a/src/main/java/mate.academy/lib/Injector.java +++ b/src/main/java/mate.academy/lib/Injector.java @@ -13,6 +13,11 @@ public class Injector { private static final Injector injector = new Injector(); + private static final Map, Class> INTERFACE_IMPLEMENTATIONS = Map.of( + ProductParser.class, ProductParserImpl.class, + FileReaderService.class, FileReaderServiceImpl.class, + ProductService.class, ProductServiceImpl.class + ); private final Map, Object> instances = new HashMap<>(); public static Injector getInjector() { @@ -22,6 +27,10 @@ public static Injector getInjector() { public Object getInstance(Class interfaceClazz) { Class clazz = findImplementation(interfaceClazz); Object clazzImplementationInstance = null; + if (!clazz.isAnnotationPresent(Component.class)) { + throw new RuntimeException(clazz.getName() + + " - does not have an annotation @Component"); + } Field[] declaredFields = clazz.getDeclaredFields(); for (Field field : declaredFields) { if (field.isAnnotationPresent(Inject.class)) { @@ -36,10 +45,9 @@ public Object getInstance(Class interfaceClazz) { } } } - if (clazzImplementationInstance == null) { - clazzImplementationInstance = createNewInstance(clazz); - } - return clazzImplementationInstance; + return clazzImplementationInstance == null + ? createNewInstance(clazz) + : clazzImplementationInstance; } private Object createNewInstance(Class clazz) { @@ -57,17 +65,11 @@ private Object createNewInstance(Class clazz) { } private Class findImplementation(Class interfaceClazz) { - Map, Class> interfaceImplementations = Map.of( - ProductParser.class, ProductParserImpl.class, - FileReaderService.class, FileReaderServiceImpl.class, - ProductService.class, ProductServiceImpl.class - ); - if (!interfaceImplementations.containsKey(interfaceClazz)) { + if (!INTERFACE_IMPLEMENTATIONS.containsKey(interfaceClazz)) { throw new RuntimeException("Unsupported class: " + interfaceClazz.getName()); } - if (interfaceClazz.isInterface()) { - return interfaceImplementations.get(interfaceClazz); - } - return interfaceClazz; + return interfaceClazz.isInterface() + ? INTERFACE_IMPLEMENTATIONS.get(interfaceClazz) + : interfaceClazz; } }