All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jvmAndroidMain.korlibs.inject.AsyncInjectorJvmExt.kt Maven / Gradle / Ivy

There is a newer version: 4.0.10
Show newest version
package korlibs.inject

import java.lang.reflect.*
import kotlin.reflect.*

@Target(AnnotationTarget.CLASS)
annotation class Prototype

@Target(AnnotationTarget.CLASS)
annotation class Singleton

@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.FIELD)
annotation class Optional

fun AsyncInjector.jvmAutomapping(): AsyncInjector = this.apply {
	this.fallbackProvider = { kclazz, ctx -> AsyncInjector.jvmFallback(this, kclazz, ctx) }
}

suspend fun AsyncInjector.Companion.jvmFallback(
    injector: AsyncInjector,
    kclazz: KClass<*>,
    ctx: AsyncInjector.RequestContext
): AsyncObjectProvider<*> {
    return fallback(injector, kclazz, ctx)
}

fun AsyncInjector.jvmRemoveMappingsByClassName(classNames: Set) {
    val classes = providersByClass.keys.filter { it.qualifiedName in classNames }
    for (clazz in classes) providersByClass.remove(clazz)
    parent?.jvmRemoveMappingsByClassName(classNames)
}

private suspend fun fallback(
	injector: AsyncInjector,
	kclazz: KClass<*>,
	ctx: AsyncInjector.RequestContext
): AsyncObjectProvider<*> {
	val clazz = (kclazz as kotlin.reflect.KClass<*>).java

    //println("Requested $clazz")

	val isPrototype = clazz.getAnnotation(Prototype::class.java) != null
	val isSingleton = clazz.getAnnotation(Singleton::class.java) != null
	val isAsyncFactoryClass = clazz.getAnnotation(AsyncFactoryClass::class.java) != null

    //println("isPrototype=$isPrototype, isSingleton=$isSingleton, isAsyncFactoryClass=$isAsyncFactoryClass")
    //println(clazz.declaredAnnotations.toList())

	val generator: suspend AsyncInjector.() -> Any? = {
		try {
			// @TODO: Performance: Cache all this!
			// Use: ClassFactory and stuff

			val loaderClass = clazz.getAnnotation(AsyncFactoryClass::class.java)
			val actualClass = loaderClass?.clazz?.java ?: clazz
			if (actualClass.isInterface || Modifier.isAbstract(actualClass.modifiers)) throw IllegalArgumentException("Can't instantiate abstract or interface: $actualClass in $ctx")
			val constructor = actualClass.declaredConstructors.firstOrNull()
					?: throw IllegalArgumentException("No available constructor for $clazz")
			val out = arrayListOf()
			val allInstances = arrayListOf()

			for ((paramType, annotations) in constructor.parameterTypes.zip(constructor.parameterAnnotations)) {
				var isOptional = false

				val i = if (annotations.isNotEmpty()) {
					val i = this.child()
					for (annotation in annotations) {
						when (annotation) {
							is Optional -> isOptional = true
							else -> i.mapInstance(annotation.annotationClass as KClass, annotation as Any)
						}
					}
					i
				} else {
					this
				}
				if (isOptional) {
					out.add(if (i.has(paramType.kotlin)) i.getOrNull(paramType.kotlin, ctx) else null)
				} else {
					out.add(
						i.getOrNull(paramType.kotlin, ctx) ?: throw AsyncInjector.NotMappedException(
							paramType.kotlin,
							actualClass.kotlin,
							ctx
						)
					)
				}
			}
			allInstances.addAll(out)
			constructor.isAccessible = true
			val instance = constructor.newInstance(*out.toTypedArray())

			// @TODO: Cache this!
            val allDeclaredFields = clazz.allDeclaredFields
			//for (field in allDeclaredFields.filter { it.getAnnotation(Inject::class.java) != null }) {
			//	if (Modifier.isStatic(field.modifiers)) continue
			//	var isOptional = false
			//	val i = if (field.annotations.isNotEmpty()) {
			//		val i = this.child()
			//		for (annotation in field.annotations) {
			//			when (annotation) {
			//				is Optional -> isOptional = true
			//				else -> i.mapInstance(annotation.annotationClass as KClass, annotation as Any)
			//			}
			//		}
			//		i
			//	} else {
			//		this
			//	}
			//	field.isAccessible = true
			//	val res = if (isOptional) {
			//		if (i.has(field.type.kotlin)) i.get(field.type.kotlin, ctx) else null
			//	} else {
			//		i.get(field.type.kotlin, ctx)
			//	}
			//	allInstances.add(res)
			//	field.set(instance, res)
			//}

			if (instance is AsyncDependency) instance.init()

			for (createdInstance in allInstances) {
				if (createdInstance is InjectedHandler) {
					createdInstance.injectedInto(instance)
				}
			}

			if (loaderClass != null) {
				(instance as AsyncFactory).create()
			} else {
				instance
			}
		} catch (e: Throwable) {
			println("$this error while creating '$clazz': (${e.message}):")
			e.printStackTrace()
			throw e
		}
	}

	return when {
		isPrototype -> PrototypeAsyncObjectProvider(generator)
		isSingleton -> SingletonAsyncObjectProvider(generator)
		isAsyncFactoryClass -> FactoryAsyncObjectProvider(generator as suspend AsyncInjector.() -> AsyncFactory)
	//else -> invalidOp("Unmapped jvmAutomapping: $clazz")
		else -> PrototypeAsyncObjectProvider(generator)
	}
}

private val Class<*>.allDeclaredFields: List
	get() = this.declaredFields.toList() + (this.superclass?.allDeclaredFields?.toList() ?: listOf())




© 2015 - 2025 Weber Informatics LLC | Privacy Policy