From 0bc721fd77472cd4e0a7119dea9e725d67998069 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Tue, 2 Dec 2014 17:53:14 +0300 Subject: [PATCH] Singletons supported in Injector Generator --- .../org/jetbrains/jet/di/Dependencies.java | 71 ++++++++++++++----- ...anceFieldAccess.java => GetSingleton.java} | 40 +++++++---- 2 files changed, 79 insertions(+), 32 deletions(-) rename injector-generator/src/org/jetbrains/jet/di/{ObjectInstanceFieldAccess.java => GetSingleton.java} (50%) diff --git a/injector-generator/src/org/jetbrains/jet/di/Dependencies.java b/injector-generator/src/org/jetbrains/jet/di/Dependencies.java index 5eb148d2c07..4de993466fb 100644 --- a/injector-generator/src/org/jetbrains/jet/di/Dependencies.java +++ b/injector-generator/src/org/jetbrains/jet/di/Dependencies.java @@ -128,36 +128,37 @@ public class Dependencies { Class clazz = type.getClazz(); if (clazz.isInterface()) { + if (initializeAsSingleton(field, type)) return; throw new IllegalArgumentException("cannot instantiate interface: " + clazz.getName() + " needed for " + neededFor); } if (Modifier.isAbstract(clazz.getModifiers())) { + if (initializeAsSingleton(field, type)) return; throw new IllegalArgumentException("cannot instantiate abstract class: " + clazz.getName() + " needed for " + neededFor); } // Note: projections are not computed here - if (isKotlinSingletonObject(clazz)) { - field.setInitialization(new ObjectInstanceFieldAccess(clazz)); - return; + // Look for constructor + List> publicConstructors = findPublicConstructors(clazz.getConstructors()); + if (publicConstructors.size() != 1) { + if (initializeAsSingleton(field, type)) return; + if (publicConstructors.size() == 0) { + throw new IllegalArgumentException("No public constructor: " + clazz.getName() + " needed for " + neededFor); + } + else { + throw new IllegalArgumentException("Too many public constructors in " + clazz.getName() + " needed for " + neededFor); + } } - // Look for constructor - Constructor[] constructors = clazz.getConstructors(); - if (constructors.length == 0 || !Modifier.isPublic(constructors[0].getModifiers())) { - throw new IllegalArgumentException("No constructor: " + clazz.getName() + " needed for " + neededFor); - } - if (constructors.length > 1) { - throw new IllegalArgumentException("Too many constructors in " + clazz.getName() + " needed for " + neededFor); - } - Constructor constructor = constructors[0]; + Constructor publicConstructor = publicConstructors.get(0); // Find arguments - ConstructorCall dependency = new ConstructorCall(constructor); - Type[] parameterTypes = constructor.getGenericParameterTypes(); + ConstructorCall dependency = new ConstructorCall(publicConstructor); + Type[] parameterTypes = publicConstructor.getGenericParameterTypes(); for (Type parameterType : parameterTypes) { Field fieldForParameter = findDependencyOfType( DiType.fromReflectionType(parameterType), - "constructor: " + constructor + ", parameter: " + parameterType, + "constructor: " + publicConstructor + ", parameter: " + parameterType, neededFor.prepend(field) ); used.add(fieldForParameter); @@ -167,14 +168,48 @@ public class Dependencies { field.setInitialization(dependency); } - private static boolean isKotlinSingletonObject(Class clazz) { + @NotNull + private static List> findPublicConstructors(Constructor[] constructors) { + List> result = new ArrayList>(); + for (Constructor constructor : constructors) { + if (Modifier.isPublic(constructor.getModifiers())) { + result.add(constructor); + } + } + return result; + } + + private static boolean initializeAsSingleton(Field field, DiType type) { + Class clazz = type.getClazz(); + + return initializeBySingletonMethod(field, clazz, "getInstance") + || initializeBySingletonField(field, clazz, "INSTANCE") + || initializeBySingletonField(field, clazz, "INSTANCE$"); + } + + private static boolean initializeBySingletonMethod(Field field, Class clazz, String name) { try { - clazz.getDeclaredField("INSTANCE$"); + clazz.getMethod(name); + field.setInitialization(GetSingleton.byMethod(clazz, name)); + return true; + } + catch (NoSuchMethodException e) { + // Ignored + } + return false; + } + + private static boolean initializeBySingletonField(Field field, Class clazz, String name) { + try { + clazz.getField(name); + field.setInitialization(GetSingleton.byField(clazz, name)); return true; } catch (NoSuchFieldException e) { - return false; + // Ignored } + + return false; } public Collection satisfyDependencies() { diff --git a/injector-generator/src/org/jetbrains/jet/di/ObjectInstanceFieldAccess.java b/injector-generator/src/org/jetbrains/jet/di/GetSingleton.java similarity index 50% rename from injector-generator/src/org/jetbrains/jet/di/ObjectInstanceFieldAccess.java rename to injector-generator/src/org/jetbrains/jet/di/GetSingleton.java index 15f1244e7a6..202482fb46a 100644 --- a/injector-generator/src/org/jetbrains/jet/di/ObjectInstanceFieldAccess.java +++ b/injector-generator/src/org/jetbrains/jet/di/GetSingleton.java @@ -17,38 +17,50 @@ package org.jetbrains.jet.di; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.Collections; -public class ObjectInstanceFieldAccess implements Expression { - private final Class clazz; +public class GetSingleton implements Expression { - public ObjectInstanceFieldAccess(@NotNull Class clazz) { - this.clazz = clazz; + public static GetSingleton byMethod(@NotNull Class singletonClass, @NotNull String methodName) { + return new GetSingleton(singletonClass, methodName, "()"); + } + + public static GetSingleton byField(@NotNull Class singletonClass, @NotNull String fieldName) { + return new GetSingleton(singletonClass, fieldName, ""); + } + + private final Class singletonClass; + private final String memberName; + private final String callSuffix; + + private GetSingleton(@NotNull Class singletonClass, @NotNull String name, String callSuffix) { + this.singletonClass = singletonClass; + this.memberName = name; + this.callSuffix = callSuffix; + } + + @Override + public String toString() { + return renderAsCode(); } @NotNull @Override public String renderAsCode() { - return clazz.getSimpleName() + ".INSTANCE$"; + return singletonClass.getSimpleName() + "." + memberName + callSuffix; } @NotNull @Override public Collection getTypesToImport() { - return Collections.singleton(new DiType(clazz)); + return Collections.singletonList(new DiType(singletonClass)); } - @Nullable + @NotNull @Override public DiType getType() { - return new DiType(clazz); - } - - @Override - public String toString() { - return clazz.toString(); + return new DiType(singletonClass); } }