) =
21 | instances.forEach { assist(it) }
22 |
23 | fun assist(instance: P, type: KClass, environment: String? = null) =
24 | assisted.add(instance, type, environment)
25 |
26 | fun assist(instance: Any, environment: String? = null) =
27 | assisted.add(instance, instance::class, environment)
28 |
29 | fun holder(holder: Any) {
30 | this.holder = holder
31 | }
32 |
33 | internal fun build() = InjectorConfig(assisted.build(), holder)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/popkorn/src/commonTest/kotlin/PopKornTest.kt:
--------------------------------------------------------------------------------
1 | package cc.popkorn
2 |
3 | import cc.popkorn.core.Injector
4 | import kotlin.random.Random
5 | import kotlin.reflect.KClass
6 | import kotlin.test.assertEquals
7 |
8 | /**
9 | * Parent class of all injectable tests
10 | *
11 | * @author Pau Corbella
12 | * @since 1.0.0
13 | */
14 | internal abstract class PopKornTest {
15 | private val alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmptwxyz".toList()
16 |
17 | fun randEnvironment(): String {
18 | val len = Random.nextInt(10)
19 | val sb = StringBuilder(len)
20 | for (i in 0 until len) {
21 | val loc = Random.nextInt(alphabet.size)
22 | sb.append(alphabet[loc])
23 | }
24 | return sb.toString()
25 | }
26 |
27 | fun Injector.assertNumberInstances(numberOfProviders: Int) {
28 | assertEquals(instances.size, numberOfProviders)
29 | }
30 |
31 | fun Injector.assertNumberInstancesForClass(clazz: KClass, numberOfInstances: Int) {
32 | val size = instances[clazz]?.size() ?: 0
33 | assertEquals(size, numberOfInstances)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/popkorn/src/jvmMain/kotlin/pools/ReflectionResolverPool.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("UNCHECKED_CAST")
2 |
3 | package cc.popkorn.pools
4 |
5 | import cc.popkorn.RESOLVER_SUFFIX
6 | import cc.popkorn.core.exceptions.ResolverNotFoundException
7 | import cc.popkorn.normalizeQualifiedName
8 | import cc.popkorn.resolvers.Resolver
9 | import kotlin.reflect.KClass
10 |
11 | /**
12 | * Implementation to get resolvers via reflection
13 | *
14 | * @author Pau Corbella
15 | * @since 1.3.0
16 | */
17 | internal class ReflectionResolverPool : ResolverPool {
18 |
19 | override fun isPresent(clazz: KClass): Boolean {
20 | val name = transform(clazz)
21 | return existClass(name)
22 | }
23 |
24 | override fun create(clazz: KClass): Resolver {
25 | val name = transform(clazz)
26 | return createClass(name)
27 | ?.let { it as? Resolver }
28 | ?: throw ResolverNotFoundException(clazz)
29 | }
30 |
31 | private fun transform(original: KClass<*>): String {
32 | return "${normalizeQualifiedName(original.getHierarchyName())}_$RESOLVER_SUFFIX"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/popkorn/src/iosMain/kotlin/ReferenceClass.kt:
--------------------------------------------------------------------------------
1 | package cc.popkorn
2 |
3 | import kotlinx.cinterop.ObjCClass
4 | import kotlinx.cinterop.ObjCProtocol
5 | import platform.Foundation.NSStringFromClass
6 | import kotlin.reflect.KClass
7 |
8 | /**
9 | * Fake KClass to hold platform-specific types
10 | *
11 | * @author Pau Corbella
12 | * @since 2.0.0
13 | */
14 | internal class ReferenceClass private constructor(
15 | override val qualifiedName: String,
16 | override val simpleName: String
17 | ) : KClass {
18 |
19 | constructor(objClass: ObjCClass) : this(NSStringFromClass(objClass), NSStringFromClass(objClass))
20 |
21 | // TODO should be the name of the protocol
22 | constructor(objProtocol: ObjCProtocol) : this(objProtocol.hashCode().toString(), objProtocol.hashCode().toString())
23 |
24 | override fun equals(other: Any?): Boolean {
25 | return other is ReferenceClass<*> && qualifiedName == other.qualifiedName
26 | }
27 |
28 | override fun hashCode() = qualifiedName.hashCode()
29 |
30 | override fun isInstance(value: Any?) = false
31 |
32 | override fun toString() = "class $qualifiedName"
33 | }
34 |
--------------------------------------------------------------------------------
/popkorn/src/jvmMain/kotlin/pools/ReflectionProviderPool.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("UNCHECKED_CAST")
2 |
3 | package cc.popkorn.pools
4 |
5 | import cc.popkorn.PROVIDER_SUFFIX
6 | import cc.popkorn.core.exceptions.ProviderNotFoundException
7 | import cc.popkorn.getName
8 | import cc.popkorn.normalizeQualifiedName
9 | import cc.popkorn.providers.Provider
10 | import kotlin.reflect.KClass
11 |
12 | /**
13 | * Implementation to get providers via reflection
14 | *
15 | * @author Pau Corbella
16 | * @since 1.3.0
17 | */
18 | internal class ReflectionProviderPool : ProviderPool {
19 |
20 | override fun isPresent(clazz: KClass): Boolean {
21 | val name = transform(clazz)
22 | return existClass(name)
23 | }
24 |
25 | override fun create(clazz: KClass): Provider {
26 | val name = transform(clazz)
27 | return createClass(name)
28 | ?.let { it as? Provider }
29 | ?: throw ProviderNotFoundException(clazz)
30 | }
31 |
32 | private fun transform(original: KClass<*>): String {
33 | return "${normalizeQualifiedName(original.getName())}_$PROVIDER_SUFFIX"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/samples/androidx-viewmodel/src/main/res/layout/fragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
21 |
22 |
27 |
28 |
--------------------------------------------------------------------------------
/popkorn/src/commonMain/kotlin/instances/VolatileInstances.kt:
--------------------------------------------------------------------------------
1 | package cc.popkorn.instances
2 |
3 | import cc.popkorn.InjectorManager
4 | import cc.popkorn.WeakReference
5 | import cc.popkorn.core.config.Parameters
6 | import cc.popkorn.providers.Provider
7 |
8 | /**
9 | * Instances implementation for Scope.BY_USE
10 | * Calling get() for the same environment and T::class will return the same instance as long as
11 | * this instance is being used by others. If no other object is using it then it will create a new one
12 | *
13 | * @author Pau Corbella
14 | * @since 1.0.0
15 | */
16 | internal class VolatileInstances(private val injector: InjectorManager, private val provider: Provider) : Instances, Purgeable {
17 | private val instances = hashMapOf>()
18 |
19 | fun get(environment: String?): T {
20 | return instances[environment]?.get() ?: provider.create(injector, Parameters.EMPTY, environment)
21 | .also { instances[environment] = WeakReference(it) }
22 | }
23 |
24 | override fun size() = instances.size
25 |
26 | override fun purge() = instances.filter { it.value.get() == null }.forEach { instances.remove(it.key) }
27 | }
28 |
--------------------------------------------------------------------------------
/popkorn/src/commonMain/kotlin/core/InjectorWithPreference.kt:
--------------------------------------------------------------------------------
1 | package cc.popkorn.core
2 |
3 | import cc.popkorn.InjectorManager
4 | import cc.popkorn.core.config.InjectorConfig
5 | import cc.popkorn.core.config.Parameters
6 | import kotlin.reflect.KClass
7 |
8 | /**
9 | * Define an Injector Manager that firstly resolves dependencies based on the ones passed ('overridden').
10 | * If not, will resolve them with the base injector
11 | *
12 | * @author Pau Corbella
13 | * @since 2.1.0
14 | */
15 | internal class InjectorWithPreference(
16 | private val baseInjector: InjectorManager,
17 | private val overridden: Parameters?
18 | ) : InjectorManager {
19 |
20 | override fun inject(clazz: KClass, environment: String?, config: (InjectorConfig.Builder.() -> Unit)?): T {
21 | return overridden?.getOrNull(clazz, environment)
22 | ?: baseInjector.inject(clazz, environment, config)
23 | }
24 |
25 | override fun injectOrNull(clazz: KClass, environment: String?, config: (InjectorConfig.Builder.() -> Unit)?): T? {
26 | return overridden?.getOrNull(clazz, environment)
27 | ?: baseInjector.injectOrNull(clazz, environment, config)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/samples/androidx-viewmodel/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
26 |
27 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/samples/androidx-viewmodel/src/main/res/layout/main_activity.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
22 |
23 |
29 |
30 |
--------------------------------------------------------------------------------
/popkorn/src/iosMain/kotlin/PopKornCompat.kt:
--------------------------------------------------------------------------------
1 | package cc.popkorn
2 |
3 | import cc.popkorn.mapping.Mapping
4 | import kotlinx.cinterop.ObjCClass
5 |
6 | /**
7 | * Compatibility class to use PopKorn from ios code
8 | *
9 | * @author Pau Corbella
10 | * @since 2.0.0
11 | */
12 |
13 | internal lateinit var classCreator: (ObjCClass) -> Mapping
14 | internal lateinit var resolverMappings: Set
15 | internal lateinit var providerMappings: Set
16 |
17 | /**
18 | * This method needs to be called on IOS platform before using PopKorn. This is because from Kotlin
19 | * cannot instantiate a class with "class_createInstance(ObjCClass, 0) as? Mapping". So we need
20 | * the ios to provide a lambda of how to create an object
21 | *
22 | * @param creator Lambda defining how to create a certain ObjCClass
23 | */
24 | fun setup(creator: (ObjCClass) -> Mapping) {
25 | if (::classCreator.isInitialized) return
26 | classCreator = creator
27 | }
28 |
29 | /**
30 | * Secondary method to initialize popkorn from IOS in case the first doesn't work
31 | *
32 | * @param resolvers All the resolver mappings that PopKorn generated
33 | * @param providers All the provider mappings that PopKorn generated
34 | */
35 | fun setup(resolvers: Set, providers: Set) {
36 | if (::resolverMappings.isInitialized || ::providerMappings.isInitialized) return
37 | resolverMappings = resolvers
38 | providerMappings = providers
39 | }
40 |
--------------------------------------------------------------------------------
/popkorn/src/commonMain/kotlin/instances/HolderInstances.kt:
--------------------------------------------------------------------------------
1 | package cc.popkorn.instances
2 |
3 | import cc.popkorn.InjectorManager
4 | import cc.popkorn.WeakReference
5 | import cc.popkorn.core.config.Parameters
6 | import cc.popkorn.providers.Provider
7 |
8 | /**
9 | * Instances implementation for Scope.BY_HOLDER
10 | * Calling get() for the same holder, environment and T::class will return the same instance as long as
11 | * the holder still exist. If holder not existing, will create a new instance
12 | *
13 | * @author Pau Corbella
14 | * @since 2.1.0
15 | */
16 | internal class HolderInstances(private val injector: InjectorManager, private val provider: Provider) : Instances, Purgeable {
17 | private val instances = hashMapOf, HashMap>()
18 |
19 | fun get(holder: Any, environment: String?): T {
20 | val map = instances.mapKeys { it.key.get() }[holder]
21 | return if (map == null) {
22 | val instance = provider.create(injector, Parameters.EMPTY, environment)
23 | instances[WeakReference(holder)] = hashMapOf(environment to instance)
24 | instance
25 | } else {
26 | map[environment] ?: provider.create(injector, Parameters.EMPTY, environment).also { map[environment] = it }
27 | }
28 | }
29 |
30 | override fun size() = instances.size
31 |
32 | override fun purge() = instances.filter { it.key.get() == null }.forEach { instances.remove(it.key) }
33 | }
34 |
--------------------------------------------------------------------------------
/popkorn/src/commonMain/kotlin/core/config/CreatorConfig.kt:
--------------------------------------------------------------------------------
1 | package cc.popkorn.core.config
2 |
3 | import kotlin.reflect.KClass
4 |
5 | /**
6 | * Extra configurations to be used when creating an instance
7 | *
8 | * @author Pau Corbella
9 | * @since 2.1.0
10 | */
11 | class CreatorConfig private constructor(
12 | val assisted: Parameters,
13 | val overridden: Parameters
14 | ) {
15 |
16 | class Builder internal constructor() {
17 | private val assisted = Parameters.Builder()
18 | private val overridden = Parameters.Builder()
19 |
20 | fun assistAll(instances: List) =
21 | instances.forEach { assisted.add(it) }
22 |
23 | fun assist(instance: P, type: KClass, environment: String? = null) =
24 | assisted.add(instance, type, environment)
25 |
26 | fun assist(instance: Any, environment: String? = null) =
27 | assisted.add(instance, instance::class, environment)
28 |
29 | fun overrideAll(instances: List) =
30 | instances.forEach { overridden.add(it) }
31 |
32 | fun override(instance: P, type: KClass, environment: String? = null) =
33 | overridden.add(instance, type, environment)
34 |
35 | fun override(instance: Any, environment: String? = null) =
36 | overridden.add(instance, instance::class, environment)
37 |
38 | internal fun build() = CreatorConfig(assisted.build(), overridden.build())
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/popkorn/src/commonTest/kotlin/ClientTests.kt:
--------------------------------------------------------------------------------
1 | package cc.popkorn
2 |
3 | import cc.popkorn.core.Injector
4 | import cc.popkorn.data.*
5 | import kotlin.test.Test
6 | import kotlin.test.assertNotNull
7 | import kotlin.test.assertNotSame
8 | import kotlin.test.assertSame
9 |
10 | /**
11 | * Class to test injectable classes end to end
12 | *
13 | * @author Pau Corbella
14 | * @since 1.0.0
15 | */
16 | internal class ClientTests : PopKornTest() {
17 |
18 | @Test
19 | fun testWhole() {
20 | val tmp = TestClassNoProvider(randEnvironment())
21 | val injector = Injector(TestResolverPool(), TestProviderPool())
22 | injector.addInjectable(tmp)
23 |
24 | val classApp = injector.inject(TestClassByApp::class, "app")
25 | val classUse = injector.inject(TestClassByUse::class, "use")
26 | val classNew = injector.inject(TestClassByNew::class, "new")
27 | val classManual = injector.inject(TestClassNoProvider::class, "some")
28 |
29 | val interfaceApp = injector.inject(TestInterface::class, "app")
30 | val interfaceUse = injector.inject(TestInterface::class, "use")
31 | val interfaceNew = injector.injectOrNull(TestInterface::class, "new")
32 |
33 | assertSame(classApp, interfaceApp)
34 | assertSame(classUse, interfaceUse)
35 | assertNotSame(classNew, interfaceNew)
36 | assertNotNull(classManual)
37 |
38 | injector.removeInjectable(tmp::class)
39 |
40 | injector.reset()
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/popkorn/src/nativeMain/kotlin/Platform.kt:
--------------------------------------------------------------------------------
1 | package cc.popkorn
2 |
3 | import cc.popkorn.core.Injector
4 | import cc.popkorn.core.exceptions.NonExistingClassException
5 | import cc.popkorn.core.exceptions.PopKornNotInitializedException
6 | import cc.popkorn.pools.MappingProviderPool
7 | import cc.popkorn.pools.MappingResolverPool
8 | import cc.popkorn.pools.ProviderPool
9 | import cc.popkorn.pools.ResolverPool
10 | import kotlin.reflect.KClass
11 |
12 | /**
13 | * Implementation for Native of the methods/classes that are Platform-dependent
14 | *
15 | * @author Pau Corbella
16 | * @since 2.0.0
17 | */
18 |
19 | actual typealias WeakReference = kotlin.native.ref.WeakReference
20 |
21 | internal actual fun KClass.getName() = this.qualifiedName ?: throw NonExistingClassException(this)
22 |
23 | internal actual fun KClass.needsResolver(resolverPool: ResolverPool) = resolverPool.isPresent(this)
24 |
25 | internal actual fun createDefaultInjector() = Injector(nativeResolverPool(), nativeProviderPool())
26 |
27 | private fun nativeResolverPool(): ResolverPool {
28 | if (!::resolverMappings.isInitialized || !::providerMappings.isInitialized) throw PopKornNotInitializedException()
29 | return MappingResolverPool(resolverMappings)
30 | }
31 |
32 | private fun nativeProviderPool(): ProviderPool {
33 | if (!::resolverMappings.isInitialized || !::providerMappings.isInitialized) throw PopKornNotInitializedException()
34 | return MappingProviderPool(providerMappings)
35 | }
36 |
--------------------------------------------------------------------------------
/popkorn/src/jsMain/kotlin/Platform.kt:
--------------------------------------------------------------------------------
1 | package cc.popkorn
2 |
3 | import cc.popkorn.core.Injector
4 | import cc.popkorn.core.exceptions.PopKornNotInitializedException
5 | import cc.popkorn.pools.MappingProviderPool
6 | import cc.popkorn.pools.MappingResolverPool
7 | import cc.popkorn.pools.ProviderPool
8 | import cc.popkorn.pools.ResolverPool
9 | import kotlin.reflect.KClass
10 |
11 | /**
12 | * Implementation for JS of the methods/classes that are Platform-dependent
13 | *
14 | * @author Pau Corbella
15 | * @since 2.0.0
16 | */
17 |
18 | internal fun JsClass.kotlinClass() = kotlin
19 |
20 | actual class WeakReference actual constructor(referred: T) {
21 | private var pointer: T? = referred
22 |
23 | actual fun clear() {
24 | pointer = null
25 | }
26 |
27 | actual fun get() = pointer
28 | }
29 |
30 | internal actual fun KClass.getName() = js.name
31 |
32 | internal actual fun KClass.needsResolver(resolverPool: ResolverPool) = resolverPool.isPresent(this)
33 |
34 | internal actual fun createDefaultInjector() = Injector(jsResolverPool(), jsProviderPool())
35 |
36 | private fun jsResolverPool(): ResolverPool {
37 | if (!::resolverMappings.isInitialized || !::providerMappings.isInitialized) throw PopKornNotInitializedException()
38 | return MappingResolverPool(resolverMappings)
39 | }
40 |
41 | private fun jsProviderPool(): ProviderPool {
42 | if (!::resolverMappings.isInitialized || !::providerMappings.isInitialized) throw PopKornNotInitializedException()
43 | return MappingProviderPool(providerMappings)
44 | }
45 |
--------------------------------------------------------------------------------
/popkorn-example/src/main/java/cc/popkorn/example/ExampleJava.java:
--------------------------------------------------------------------------------
1 | package cc.popkorn.example;
2 |
3 | import cc.popkorn.PopKornCompat;
4 | import cc.popkorn.config.CreatorConfigBuilder;
5 | import cc.popkorn.example.model.*;
6 |
7 | public class ExampleJava {
8 |
9 | void execute() {
10 | D10 d10 = new D10();
11 | PopKornCompat.addInjectable(d10);
12 |
13 | PopKornCompat.inject(String.class);
14 | PopKornCompat.inject(Integer.class);
15 |
16 | PopKornCompat.inject(R1i.class);
17 | PopKornCompat.inject(R2i.class);
18 | PopKornCompat.inject(R3i.class);
19 | PopKornCompat.inject(R4i.class, "envX");
20 | PopKornCompat.inject(R5i.class);
21 |
22 | System.gc();
23 | PopKornCompat.purge();
24 |
25 | PopKornCompat.inject(R6i.class);
26 | PopKornCompat.inject(R7i.class);
27 | PopKornCompat.inject(R8i.class);
28 | PopKornCompat.inject(R8i.class, "env1");
29 | PopKornCompat.inject(R8i.class, "env2");
30 | PopKornCompat.inject(R8i.class, "env3");
31 | PopKornCompat.inject(R8i.class, "env4");
32 | PopKornCompat.inject(R9i.class);
33 |
34 | PopKornCompat.create(R10i.class, new CreatorConfigBuilder().assist(10L).assist(new R9()));
35 | PopKornCompat.create(R10i.class, "env2", new CreatorConfigBuilder().assist(10L).assist(20L, "second").assist(new R10()));
36 | PopKornCompat.create(R8i.class);
37 | PopKornCompat.create(R8i.class, "env1");
38 |
39 | PopKornCompat.removeInjectable(D10.class);
40 | PopKornCompat.reset();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/popkorn-compiler/build.gradle.kts:
--------------------------------------------------------------------------------
1 | val kotlinPoetVersion = "1.14.2"
2 | val kotlinxMetadataVersion = "0.5.0"
3 | val apacheVersion = "2.13.0"
4 | val compileTestVersion = "0.21.0"
5 |
6 | plugins {
7 | kotlin("jvm")
8 | id("org.jetbrains.dokka")
9 | id("pk-publish")
10 | id("org.jlleitschuh.gradle.ktlint")
11 | }
12 |
13 | tasks.dokkaHtml {
14 | outputDirectory.set(buildDir.resolve("javadoc"))
15 | }
16 |
17 | kotlin {
18 | jvmToolchain(8)
19 | }
20 |
21 | val dokkaJar by tasks.creating(Jar::class) {
22 | group = JavaBasePlugin.DOCUMENTATION_GROUP
23 | description = "Assembles Kotlin docs with Dokka"
24 | archiveClassifier.set("javadoc")
25 | from(tasks.dokkaHtml)
26 | }
27 |
28 | val sourcesJar by tasks.creating(Jar::class) {
29 | archiveClassifier.set("sources")
30 | from(sourceSets.main.get().allSource)
31 | }
32 |
33 | publishing {
34 | publications {
35 | create("compiler") {
36 | from(components["kotlin"])
37 | artifact(dokkaJar)
38 | artifact(sourcesJar)
39 | }
40 | }
41 | }
42 |
43 | dependencies {
44 | implementation(project(":popkorn"))
45 | implementation(kotlin("stdlib"))
46 | implementation(kotlin("reflect"))
47 | implementation("com.squareup:kotlinpoet:$kotlinPoetVersion")
48 | implementation("org.jetbrains.kotlinx:kotlinx-metadata-jvm:$kotlinxMetadataVersion")
49 |
50 | testImplementation(kotlin("test"))
51 | testImplementation(kotlin("test-junit"))
52 | testImplementation("com.google.testing.compile:compile-testing:$compileTestVersion")
53 | testImplementation("commons-io:commons-io:$apacheVersion")
54 | }
55 |
--------------------------------------------------------------------------------
/popkorn/src/iosMain/kotlin/config/InjectorConfigBuilder.kt:
--------------------------------------------------------------------------------
1 | package cc.popkorn.config
2 |
3 | import cc.popkorn.core.config.InjectorConfig
4 | import cc.popkorn.core.model.Instance
5 | import cc.popkorn.kotlinClass
6 | import kotlinx.cinterop.ObjCClass
7 | import kotlinx.cinterop.ObjCProtocol
8 |
9 | /**
10 | * Extra configurations to be used when injecting an instance from ObjectiveC
11 | *
12 | * @author Pau Corbella
13 | * @since 2.1.0
14 | */
15 | class InjectorConfigBuilder {
16 | private val assisted = arrayListOf>()
17 | private var holder: Any? = null
18 |
19 | fun assist(instance: Any, type: ObjCClass, environment: String): InjectorConfigBuilder {
20 | assisted.add(Instance(instance, type.kotlinClass(), environment))
21 | return this
22 | }
23 |
24 | fun assist(instance: Any, type: ObjCClass): InjectorConfigBuilder {
25 | assisted.add(Instance(instance, type.kotlinClass()))
26 | return this
27 | }
28 |
29 | fun assist(instance: Any, protocol: ObjCProtocol, environment: String): InjectorConfigBuilder {
30 | assisted.add(Instance(instance, protocol.kotlinClass(), environment))
31 | return this
32 | }
33 |
34 | fun assist(instance: Any, protocol: ObjCProtocol): InjectorConfigBuilder {
35 | assisted.add(Instance(instance, protocol.kotlinClass()))
36 | return this
37 | }
38 |
39 | fun holder(holder: Any): InjectorConfigBuilder {
40 | this.holder = holder
41 | return this
42 | }
43 |
44 | internal fun apply(builder: InjectorConfig.Builder) {
45 | assisted.forEach { builder.assist(it.instance, it.type, it.environment) }
46 | holder?.let { builder.holder(it) }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/popkorn/src/commonMain/kotlin/core/config/Parameters.kt:
--------------------------------------------------------------------------------
1 | package cc.popkorn.core.config
2 |
3 | import cc.popkorn.core.exceptions.AssistedNotFoundException
4 | import cc.popkorn.core.model.Instance
5 | import kotlin.reflect.KClass
6 |
7 | /**
8 | * Class to define a list of parameters. This class is not meant to be created by users.
9 | *
10 | * @author Pau Corbella
11 | * @since 2.1.0
12 | */
13 | class Parameters private constructor(private val params: List>) {
14 |
15 | internal class Builder {
16 |
17 | private val parameters = arrayListOf>()
18 |
19 | fun add(instance: T, type: KClass, environment: String? = null) = parameters.add(Instance(instance, type, environment))
20 |
21 | fun add(instance: T, environment: String? = null) = parameters.add(Instance(instance, instance::class, environment))
22 |
23 | fun build() = Parameters(parameters)
24 | }
25 |
26 | fun get(clazz: KClass, environment: String? = null): T {
27 | return params.filter { it.type == clazz }
28 | .let { list ->
29 | list.singleOrNull { it.environment == environment } ?: list.singleOrNull { it.environment == null }
30 | }
31 | ?.instance
32 | ?.let { it as T }
33 | ?: throw AssistedNotFoundException(clazz, environment)
34 | }
35 |
36 | fun getOrNull(clazz: KClass, environment: String? = null): T? {
37 | return try {
38 | get(clazz, environment)
39 | } catch (e: AssistedNotFoundException) {
40 | null
41 | }
42 | }
43 |
44 | companion object {
45 | internal val EMPTY = Parameters(listOf())
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/popkorn/src/commonTest/kotlin/data/TestProviderPool.kt:
--------------------------------------------------------------------------------
1 | package cc.popkorn.data
2 |
3 | import cc.popkorn.core.exceptions.ProviderNotFoundException
4 | import cc.popkorn.pools.ProviderPool
5 | import cc.popkorn.providers.Provider
6 | import kotlin.reflect.KClass
7 |
8 | /**
9 | * ResolverPool that define how test classes are created
10 | *
11 | * @author Pau Corbella
12 | * @since 2.0.0
13 | */
14 | class TestProviderPool : ProviderPool {
15 |
16 | override fun isPresent(clazz: KClass): Boolean {
17 | return when (clazz) {
18 | TestClassByApp::class -> true
19 | TestClassByUse::class -> true
20 | TestClassByHolder::class -> true
21 | TestClassByNew::class -> true
22 | TestClassByNewAssisted::class -> true
23 | TestClassByNewAssisted2::class -> true
24 | TestCascadeClass::class -> true
25 | else -> false
26 | }
27 | }
28 |
29 | override fun create(clazz: KClass): Provider {
30 | return when (clazz) {
31 | TestClassByApp::class -> TestClassByAppProvider() as Provider
32 | TestClassByUse::class -> TestClassByUseProvider() as Provider
33 | TestClassByHolder::class -> TestClassByHolderProvider() as Provider
34 | TestClassByNew::class -> TestClassByNewProvider() as Provider
35 | TestClassByNewAssisted::class -> TestClassByNewAssistedProvider() as Provider
36 | TestClassByNewAssisted2::class -> TestClassByNewAssisted2Provider() as Provider
37 | TestCascadeClass::class -> TestCascadeClassProvider() as Provider
38 | else -> throw ProviderNotFoundException(clazz)
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/popkorn/src/jvmMain/kotlin/config/InjectorConfigBuilder.kt:
--------------------------------------------------------------------------------
1 | package cc.popkorn.config
2 |
3 | import cc.popkorn.core.config.InjectorConfig
4 | import cc.popkorn.core.model.Instance
5 | import cc.popkorn.kotlinClass
6 |
7 | /**
8 | * Extra configurations to be used when injecting an instance from JVM
9 | *
10 | * @author Pau Corbella
11 | * @since 2.1.0
12 | */
13 | class InjectorConfigBuilder {
14 | private val assisted = arrayListOf>()
15 | private var holder: Any? = null
16 |
17 | fun assistAll(instances: List): InjectorConfigBuilder {
18 | instances.forEach { assisted.add(Instance(it)) }
19 | return this
20 | }
21 |
22 | fun assist(instance: T, type: Class, environment: String): InjectorConfigBuilder {
23 | assisted.add(Instance(instance, type.kotlinClass(), environment))
24 | return this
25 | }
26 |
27 | fun assist(instance: T, type: Class): InjectorConfigBuilder {
28 | assisted.add(Instance(instance, type.kotlinClass()))
29 | return this
30 | }
31 |
32 | fun assist(instance: T, environment: String): InjectorConfigBuilder {
33 | assisted.add(Instance(instance, environment = environment))
34 | return this
35 | }
36 |
37 | fun assist(instance: T): InjectorConfigBuilder {
38 | assisted.add(Instance(instance))
39 | return this
40 | }
41 |
42 | fun holder(holder: Any): InjectorConfigBuilder {
43 | this.holder = holder
44 | return this
45 | }
46 |
47 | internal fun apply(builder: InjectorConfig.Builder) {
48 | assisted.forEach { builder.assist(it.instance, it.type, it.environment) }
49 | holder?.let { builder.holder(it) }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/popkorn/src/jsMain/kotlin/config/InjectorConfigBuilder.kt:
--------------------------------------------------------------------------------
1 | package cc.popkorn.config
2 |
3 | import cc.popkorn.core.config.InjectorConfig
4 | import cc.popkorn.core.model.Instance
5 | import cc.popkorn.kotlinClass
6 |
7 | /**
8 | * Extra configurations to be used when injecting an instance from JS
9 | *
10 | * @author Pau Corbella
11 | * @since 2.1.0
12 | */
13 | class InjectorConfigBuilder {
14 | private val assisted = arrayListOf>()
15 | private var holder: Any? = null
16 |
17 | fun assistAll(instances: List): InjectorConfigBuilder {
18 | instances.forEach { assisted.add(Instance(it)) }
19 | return this
20 | }
21 |
22 | fun assist(instance: T, type: JsClass, environment: String): InjectorConfigBuilder {
23 | assisted.add(Instance(instance, type.kotlinClass(), environment))
24 | return this
25 | }
26 |
27 | fun assist(instance: T, type: JsClass): InjectorConfigBuilder {
28 | assisted.add(Instance(instance, type.kotlinClass()))
29 | return this
30 | }
31 |
32 | fun assist(instance: T, environment: String): InjectorConfigBuilder {
33 | assisted.add(Instance(instance, environment = environment))
34 | return this
35 | }
36 |
37 | fun assist(instance: T): InjectorConfigBuilder {
38 | assisted.add(Instance(instance))
39 | return this
40 | }
41 |
42 | fun holder(holder: Any): InjectorConfigBuilder {
43 | this.holder = holder
44 | return this
45 | }
46 |
47 | internal fun apply(builder: InjectorConfig.Builder) {
48 | assisted.forEach { builder.assist(it.instance, it.type, it.environment) }
49 | holder?.let { builder.holder(it) }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/popkorn-androidx-viewmodel/build.gradle.kts:
--------------------------------------------------------------------------------
1 | val lifecycleVersion = "2.3.1"
2 | val fragmentVersion = "1.3.2"
3 |
4 | plugins {
5 | id("com.android.library")
6 | kotlin("multiplatform")
7 | id("org.jetbrains.dokka")
8 | id("pk-publish")
9 | id("org.jlleitschuh.gradle.ktlint")
10 | }
11 |
12 | tasks.dokkaHtml {
13 | outputDirectory.set(buildDir.resolve("javadoc"))
14 | }
15 |
16 | val dokkaJar by tasks.creating(Jar::class) {
17 | group = JavaBasePlugin.DOCUMENTATION_GROUP
18 | description = "Assembles Kotlin docs with Dokka"
19 | archiveClassifier.set("javadoc")
20 | from(tasks.dokkaHtml)
21 | }
22 |
23 | publishing {
24 | publications {
25 | publications.configureEach {
26 | if (this is MavenPublication) {
27 | artifact(dokkaJar)
28 | }
29 | }
30 | }
31 | }
32 |
33 | repositories {
34 | mavenCentral()
35 | google()
36 | }
37 |
38 | android {
39 | namespace = "cc.popkorn.androidx.viewmodel"
40 | }
41 |
42 | android.androidConfig()
43 |
44 | kotlin {
45 | jvmToolchain(8)
46 |
47 | android {
48 | publishLibraryVariants("release", "debug")
49 | }
50 |
51 | sourceSets {
52 | commonTest {
53 | dependencies {
54 | implementation(kotlin("test"))
55 | implementation(kotlin("test-junit"))
56 | }
57 | }
58 |
59 | named("androidMain") {
60 | dependencies {
61 | implementation(project(":popkorn"))
62 | implementation("androidx.fragment:fragment:$fragmentVersion")
63 | implementation("androidx.lifecycle:lifecycle-viewmodel:$lifecycleVersion")
64 | }
65 | }
66 |
67 | named("androidTest") { }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/samples/androidx-viewmodel/src/main/java/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package cc.popkorn.samples.androidx.viewmodel
2 |
3 | import android.content.Intent
4 | import android.os.Bundle
5 | import android.util.Log
6 | import android.widget.Button
7 | import android.widget.EditText
8 | import android.widget.TextView
9 | import androidx.appcompat.app.AppCompatActivity
10 | import androidx.lifecycle.LiveData
11 | import androidx.lifecycle.MutableLiveData
12 | import androidx.lifecycle.ViewModel
13 | import cc.popkorn.androidx.viewModel.viewModel
14 | import cc.popkorn.annotations.Injectable
15 | import cc.popkorn.core.Propagation
16 | import cc.popkorn.core.Scope
17 |
18 | class MainActivity : AppCompatActivity(R.layout.main_activity) {
19 |
20 | private val viewModel: FirstViewModel by viewModel()
21 |
22 | override fun onCreate(savedInstanceState: Bundle?) {
23 | super.onCreate(savedInstanceState)
24 |
25 | viewModel.greeting.observe(this) { greeting ->
26 | findViewById(R.id.textViewGreeting).text = greeting
27 | }
28 |
29 | findViewById