diff --git a/src/main/java/mate.academy/Main.java b/src/main/java/mate.academy/Main.java index 9be740020..e46903220 100644 --- a/src/main/java/mate.academy/Main.java +++ b/src/main/java/mate.academy/Main.java @@ -6,12 +6,13 @@ import mate.academy.service.ProductService; public class Main { + private static final String FILE_PATH = "src/main/java/resources/products.txt"; 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; - List products = productService.getAllFromFile("products.txt"); + ProductService productService = (ProductService) injector.getInstance(ProductService.class); + List products = productService.getAllFromFile(FILE_PATH); 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..76fb98e9c 100644 --- a/src/main/java/mate.academy/lib/Component.java +++ b/src/main/java/mate.academy/lib/Component.java @@ -1,5 +1,9 @@ package mate.academy.lib; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) 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..dfd2a0486 100644 --- a/src/main/java/mate.academy/lib/Inject.java +++ b/src/main/java/mate.academy/lib/Inject.java @@ -1,5 +1,8 @@ package mate.academy.lib; -public @interface Inject { +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +@Retention(RetentionPolicy.RUNTIME) +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..2afbac608 100644 --- a/src/main/java/mate.academy/lib/Injector.java +++ b/src/main/java/mate.academy/lib/Injector.java @@ -1,13 +1,74 @@ package mate.academy.lib; +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, Class> componentMap = Map.of( + ProductService.class, ProductServiceImpl.class, + ProductParser.class, ProductParserImpl.class, + FileReaderService.class, FileReaderServiceImpl.class); + private final Map, Object> instances = new HashMap<>(); public static Injector getInjector() { return injector; } public Object getInstance(Class interfaceClazz) { - return null; + if (instances.containsKey(interfaceClazz)) { + return instances.get(interfaceClazz); + } + validateImplementationPresent(interfaceClazz); + Class implementationClass = componentMap.get(interfaceClazz); + validateComponentPresent(implementationClass); + Object instance = newInstance(implementationClass); + Field[] fields = implementationClass.getDeclaredFields(); + for (Field field : fields) { + if (!field.isAnnotationPresent(Inject.class)) { + continue; + } + Object fieldValue = getInstance(field.getType()); + setFieldValue(instance, field, fieldValue); + } + return instance; + } + + private static void setFieldValue(Object instance, Field field, Object fieldValue) { + try { + field.setAccessible(true); + field.set(instance, fieldValue); + } catch (IllegalAccessException e) { + throw new RuntimeException("Failed to initialize field value", e); + } + } + + private static void validateComponentPresent(Class implementationClass) { + if (!implementationClass.isAnnotationPresent(Component.class)) { + throw new RuntimeException("Target class = [" + + implementationClass.getSimpleName() + "] should have @Component present"); + } + } + + private void validateImplementationPresent(Class interfaceClazz) { + if (!componentMap.containsKey(interfaceClazz)) { + throw new RuntimeException(String.format("No implementation for interface %s", + interfaceClazz.getName())); + } + } + + private Object newInstance(Class implementationClass) { + try { + return implementationClass.getDeclaredConstructor().newInstance(); + } catch (ReflectiveOperationException e) { + throw new RuntimeException("An error occurred while creating a new instance", e); + } } } 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 diff --git a/products.txt b/src/main/java/resources/products.txt similarity index 100% rename from products.txt rename to src/main/java/resources/products.txt