From 125ea4ece3948eb8c0f03192d3418310b2880977 Mon Sep 17 00:00:00 2001 From: Andrey Kravtsun Date: Fri, 19 May 2017 11:44:56 +0300 Subject: [PATCH 1/5] Initial commit. --- pom.xml | 4 +- src/main/java/ru/spbau/mit/Injector.java | 81 +++++++++++++++++++++++- 2 files changed, 81 insertions(+), 4 deletions(-) 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..09960b6 100644 --- a/src/main/java/ru/spbau/mit/Injector.java +++ b/src/main/java/ru/spbau/mit/Injector.java @@ -1,9 +1,18 @@ package ru.spbau.mit; -import java.util.List; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.*; +import java.util.stream.Collectors; public final class Injector { + private static Map alreadyCreatedObjects; + private static Set enqueued; +// private static Map usedConstructors; +// private static Set initialized; + +// private static Set alreadyCreatedObjects; private Injector() { } @@ -12,6 +21,74 @@ private Injector() { * `implementationClassNames` for concrete dependencies. */ public static Object initialize(String rootClassName, List implementationClassNames) throws Exception { - throw new IllegalStateException(); + initializeStructures(); + Object object = initialize0(rootClassName, implementationClassNames); + deinitializeStructures(); + return object; + } + + private static void initializeStructures() { + if (Objects.isNull(alreadyCreatedObjects)) { + assert Objects.isNull(enqueued); + alreadyCreatedObjects = new HashMap<>(); + enqueued = new HashSet<>(); + } + if (!alreadyCreatedObjects.isEmpty() || !enqueued.isEmpty()) { + throw new RuntimeException(); + } + } + + private static void deinitializeStructures() { + alreadyCreatedObjects.clear(); + enqueued.clear(); + } + + private static Object initialize0(String rootClassName, List implementationClassNames) + throws ImplementationNotFoundException, + ClassNotFoundException, + AmbiguousImplementationException { + 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()) { + List allowedClasses = implementationClassNames + .stream() + .filter((implName) -> { + try { + Class implementationClass = Class.forName(implName); + return dependencyClass.isAssignableFrom(implementationClass); + } catch (ClassNotFoundException e) { + throw new RuntimeException(); + } + }) + .collect(Collectors.toList()); + if (allowedClasses.size() > 1) { + throw new AmbiguousImplementationException(); + } else if (allowedClasses.isEmpty()) { + throw new ImplementationNotFoundException(); + } else { + String allowedClass = allowedClasses.get(0); + Object arg = initialize0(allowedClass, implementationClassNames); + args.add(arg); + } + } + + 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; } } From 57c0468400a14b65f9ed97387223a9f52e739846 Mon Sep 17 00:00:00 2001 From: Andrey Kravtsun Date: Fri, 19 May 2017 11:49:02 +0300 Subject: [PATCH 2/5] Some cleaning. --- src/main/java/ru/spbau/mit/Injector.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/ru/spbau/mit/Injector.java b/src/main/java/ru/spbau/mit/Injector.java index 09960b6..61e2617 100644 --- a/src/main/java/ru/spbau/mit/Injector.java +++ b/src/main/java/ru/spbau/mit/Injector.java @@ -9,10 +9,7 @@ public final class Injector { private static Map alreadyCreatedObjects; private static Set enqueued; -// private static Map usedConstructors; -// private static Set initialized; -// private static Set alreadyCreatedObjects; private Injector() { } @@ -58,7 +55,7 @@ private static Object initialize0(String rootClassName, List implementat Class rootClass = Class.forName(rootClassName); Constructor rootConstructor = rootClass.getConstructors()[0]; List args = new ArrayList<>(); - for (Class dependencyClass : rootConstructor.getParameterTypes()) { + for (Class dependencyClass : rootConstructor.getParameterTypes()) { List allowedClasses = implementationClassNames .stream() .filter((implName) -> { From 0966bf6363babafe8113d365c118d158e31bfce4 Mon Sep 17 00:00:00 2001 From: Andrey Kravtsun Date: Fri, 19 May 2017 11:55:01 +0300 Subject: [PATCH 3/5] Reenterability. --- src/main/java/ru/spbau/mit/Injector.java | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/src/main/java/ru/spbau/mit/Injector.java b/src/main/java/ru/spbau/mit/Injector.java index 61e2617..ffbb989 100644 --- a/src/main/java/ru/spbau/mit/Injector.java +++ b/src/main/java/ru/spbau/mit/Injector.java @@ -7,8 +7,8 @@ public final class Injector { - private static Map alreadyCreatedObjects; - private static Set enqueued; + private static Map alreadyCreatedObjects = new HashMap<>(); + private static Set enqueued = new HashSet<>(); private Injector() { } @@ -18,20 +18,11 @@ private Injector() { * `implementationClassNames` for concrete dependencies. */ public static Object initialize(String rootClassName, List implementationClassNames) throws Exception { - initializeStructures(); - Object object = initialize0(rootClassName, implementationClassNames); - deinitializeStructures(); - return object; - } - - private static void initializeStructures() { - if (Objects.isNull(alreadyCreatedObjects)) { - assert Objects.isNull(enqueued); - alreadyCreatedObjects = new HashMap<>(); - enqueued = new HashSet<>(); + try { + return initialize0(rootClassName, implementationClassNames); } - if (!alreadyCreatedObjects.isEmpty() || !enqueued.isEmpty()) { - throw new RuntimeException(); + finally { + deinitializeStructures(); } } From ad88c014d22e50a09f707f6bfc408757b683ad34 Mon Sep 17 00:00:00 2001 From: Andrey Kravtsun Date: Fri, 19 May 2017 11:56:35 +0300 Subject: [PATCH 4/5] Checkstyle fix. --- src/main/java/ru/spbau/mit/Injector.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/ru/spbau/mit/Injector.java b/src/main/java/ru/spbau/mit/Injector.java index ffbb989..8126564 100644 --- a/src/main/java/ru/spbau/mit/Injector.java +++ b/src/main/java/ru/spbau/mit/Injector.java @@ -20,8 +20,7 @@ private Injector() { public static Object initialize(String rootClassName, List implementationClassNames) throws Exception { try { return initialize0(rootClassName, implementationClassNames); - } - finally { + } finally { deinitializeStructures(); } } From fe6ac425e14aadf357bbbc5cfe0438c53b15485f Mon Sep 17 00:00:00 2001 From: Andrey Kravtsun Date: Fri, 19 May 2017 12:02:00 +0300 Subject: [PATCH 5/5] Trying to fix 'certain' cases with cycles. --- src/main/java/ru/spbau/mit/Injector.java | 36 +++++++++++++++--------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/main/java/ru/spbau/mit/Injector.java b/src/main/java/ru/spbau/mit/Injector.java index 8126564..dcd296a 100644 --- a/src/main/java/ru/spbau/mit/Injector.java +++ b/src/main/java/ru/spbau/mit/Injector.java @@ -3,6 +3,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.*; +import java.util.function.Predicate; import java.util.stream.Collectors; @@ -33,7 +34,8 @@ private static void deinitializeStructures() { private static Object initialize0(String rootClassName, List implementationClassNames) throws ImplementationNotFoundException, ClassNotFoundException, - AmbiguousImplementationException { + AmbiguousImplementationException, + InjectionCycleException { if (alreadyCreatedObjects.containsKey(rootClassName)) { return alreadyCreatedObjects.get(rootClassName); } @@ -46,25 +48,31 @@ private static Object initialize0(String rootClassName, List implementat 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((implName) -> { - try { - Class implementationClass = Class.forName(implName); - return dependencyClass.isAssignableFrom(implementationClass); - } catch (ClassNotFoundException e) { - throw new RuntimeException(); - } - }) + .filter(predicate) .collect(Collectors.toList()); - if (allowedClasses.size() > 1) { - throw new AmbiguousImplementationException(); - } else if (allowedClasses.isEmpty()) { - throw new ImplementationNotFoundException(); - } else { + + 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(); + } } }