com.jtransc.injector.Injector.kt Maven / Gradle / Ivy
package com.jtransc.injector
import com.jtransc.annotation.JTranscKeep
import com.jtransc.annotation.JTranscKeepConstructors
import com.jtransc.error.invalidOp
import java.lang.reflect.InvocationTargetException
@Suppress("UNCHECKED_CAST")
class Injector() {
val maps = hashMapOf, () -> Any>(
Injector::class.java to { this@Injector }
)
inline fun get(): T = getInstance(T::class.java)
inline fun getOrNull(): T? = getInstanceOrNull(T::class.java)
inline fun get(default: () -> T): T = if (T::class.java in maps) getInstance(T::class.java) else default()
fun getInstance(clazz: Class): T {
return getInstanceOrNull(clazz)
?: invalidOp("Cannot automap '$clazz' not @Singleton or @Prototype")
}
fun getInstanceOrNull(clazz: Class): T? {
if (clazz !in maps) {
val allAnnotations = getAllAnnotations(clazz)
val isSingleton = allAnnotations.filterIsInstance().isNotEmpty()
val isPrototype = allAnnotations.filterIsInstance().isNotEmpty()
if (!isSingleton && !isPrototype) {
return null
}
mapImplementation(clazz, clazz)
}
return this.maps[clazz]!!() as T
}
internal fun createInstance(clazz: Class): T {
val c = clazz.constructors.firstOrNull() ?: invalidOp("No constructors for $clazz")
try {
return c.newInstance(*(c.parameterTypes.map { this.getInstance(it) }).toTypedArray()) as T
} catch (e: InvocationTargetException) {
throw InvocationTargetException(e, "Can't construct class $clazz : $c")
}
}
private fun getAllAnnotations(clazz: Class<*>): List {
return if (clazz.superclass == null) {
clazz.annotations.toList()
} else {
clazz.annotations.toList() + getAllAnnotations(clazz.superclass)
} + clazz.interfaces.flatMap { getAllAnnotations(it) }
}
fun mapImplementation(classInterface: Class<*>, classImpl: Class<*>) {
val allAnnotations = getAllAnnotations(classImpl)
val isSingleton = allAnnotations.filterIsInstance().isNotEmpty()
var cached: Any? = null
this.maps[classInterface] = {
if (isSingleton) {
if (cached == null) cached = createInstance(classImpl)
cached!!
} else {
createInstance(classImpl)
}
}
}
fun mapInstances(vararg objs: Any) = run { for (obj in objs) mapInstance(obj) }
fun mapInstance(obj: T): T = mapInstance(obj, obj.javaClass)
fun mapInstance(obj: T, type: Class): T = obj.apply { maps[type] = { obj } }
inline fun mapImpl() = mapImplementation(TInt::class.java, TImpl::class.java)
}
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
@JTranscKeepConstructors
annotation class Singleton()
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
@JTranscKeepConstructors
annotation class Prototype()