diff --git a/pom.xml b/pom.xml index a1613eb..1dfc8fa 100644 --- a/pom.xml +++ b/pom.xml @@ -59,8 +59,8 @@ maven-compiler-plugin 3.3 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/src/main/java/ru/spbau/mit/Injector.java b/src/main/java/ru/spbau/mit/Injector.java index c54168d..dcd296a 100644 --- a/src/main/java/ru/spbau/mit/Injector.java +++ b/src/main/java/ru/spbau/mit/Injector.java @@ -1,9 +1,16 @@ package ru.spbau.mit; -import java.util.List; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.*; +import java.util.function.Predicate; +import java.util.stream.Collectors; public final class Injector { + private static Map alreadyCreatedObjects = new HashMap<>(); + private static Set enqueued = new HashSet<>(); + private Injector() { } @@ -12,6 +19,71 @@ private Injector() { * `implementationClassNames` for concrete dependencies. */ public static Object initialize(String rootClassName, List implementationClassNames) throws Exception { - throw new IllegalStateException(); + try { + return initialize0(rootClassName, implementationClassNames); + } finally { + deinitializeStructures(); + } + } + + private static void deinitializeStructures() { + alreadyCreatedObjects.clear(); + enqueued.clear(); + } + + private static Object initialize0(String rootClassName, List implementationClassNames) + throws ImplementationNotFoundException, + ClassNotFoundException, + AmbiguousImplementationException, + InjectionCycleException { + if (alreadyCreatedObjects.containsKey(rootClassName)) { + return alreadyCreatedObjects.get(rootClassName); + } + if (enqueued.contains(rootClassName)) { + return new InjectionCycleException(); + } + enqueued.add(rootClassName); + + Class rootClass = Class.forName(rootClassName); + Constructor rootConstructor = rootClass.getConstructors()[0]; + List args = new ArrayList<>(); + for (Class dependencyClass : rootConstructor.getParameterTypes()) { + Predicate predicate = (implName) -> { + try { + Class implementationClass = Class.forName(implName); + return dependencyClass.isAssignableFrom(implementationClass); + } catch (ClassNotFoundException e) { + throw new RuntimeException(); + } + }; + List allowedClasses = implementationClassNames + .stream() + .filter(predicate) + .collect(Collectors.toList()); + + if (allowedClasses.size() == 1) { + String allowedClass = allowedClasses.get(0); + Object arg = initialize0(allowedClass, implementationClassNames); + args.add(arg); + } else if (allowedClasses.size() > 1) { + throw new AmbiguousImplementationException(); + } else { + if (enqueued.stream().anyMatch(predicate)) { + throw new InjectionCycleException(); + } else { + throw new ImplementationNotFoundException(); + } + } + } + + Object rootObject; + try { + rootObject = rootConstructor.newInstance(args.toArray()); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(); + } + enqueued.remove(rootClassName); + alreadyCreatedObjects.put(rootClassName, rootObject); + return rootObject; } }