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

orbit.client.addressable.AddressableDefinitionDirectory.kt Maven / Gradle / Ivy

/*
 Copyright (C) 2015 - 2019 Electronic Arts Inc.  All rights reserved.
 This file is part of the Orbit Project .
 See license in LICENSE.
 */

package orbit.client.addressable

import mu.KotlinLogging
import orbit.client.util.DeferredWrappers
import orbit.shared.mesh.NodeCapabilities
import java.lang.reflect.Method
import java.util.concurrent.ConcurrentHashMap

internal class AddressableDefinitionDirectory {
    private val interfaceDefinitionMap = ConcurrentHashMap()
    private val implDefinitionMap = ConcurrentHashMap()
    private val interfaceImplComboImplDefinitionMap =
        ConcurrentHashMap, AddressableImplDefinition>()

    private val logger = KotlinLogging.logger {}

    fun setupDefinition(interfaceClasses: Iterable, impls: Map) {
        interfaceClasses.forEach {
            getOrCreateInterfaceDefinition(it)
        }

        impls.forEach { (interfaceClass, implClass) ->
            generateImplDefinition(interfaceClass, implClass).also {
                implDefinitionMap[interfaceClass] = it
            }
        }
    }

    fun getOrCreateInterfaceDefinition(interfaceClass: AddressableClass): AddressableInterfaceDefinition =
        interfaceDefinitionMap.getOrPut(interfaceClass) {
            generateInterfaceDefinition(interfaceClass)
        }

    @Suppress("UNCHECKED_CAST")
    fun getImplDefinition(name: String): AddressableImplDefinition =
        getImplDefinition(Class.forName(name) as AddressableClass)


    fun getImplDefinition(interfaceClass: AddressableClass): AddressableImplDefinition =
        implDefinitionMap[interfaceClass] ?: throw IllegalStateException("No implementation found for $interfaceClass")

    fun onDemandImplClass(interfaceClass: AddressableClass, implClass: AddressableClass): AddressableImplDefinition =
        interfaceImplComboImplDefinitionMap.getOrPut(interfaceClass to implClass) {
            generateImplDefinition(interfaceClass, implClass)
        }

    private fun generateInterfaceDefinition(interfaceClass: AddressableClass): AddressableInterfaceDefinition {
        if (!interfaceClass.isInterface) {
            throw IllegalArgumentException("${interfaceClass.name} is not an interface.")
        }
        if (interfaceClass.isAnnotationPresent(NonConcrete::class.java)) {
            throw IllegalArgumentException("${interfaceClass.name} is non-concrete and can not be directly addressed")
        }


        val methods = interfaceClass.methods
            .map { method ->
                method to generateInterfaceMethodDefinition(method)
            }.toMap()

        val definition = AddressableInterfaceDefinition(
            interfaceClass = interfaceClass,
            methods = methods
        )

        logger.debug { "Created interface definition: $definition" }

        return definition
    }

    private fun generateInterfaceMethodDefinition(
        method: Method
    ): AddressableInterfaceMethodDefinition {

        verifyMethodIsAsync(method)

        return AddressableInterfaceMethodDefinition(
            method = method
        )
    }

    private fun generateImplDefinition(
        interfaceClass: AddressableClass,
        implClass: AddressableClass
    ): AddressableImplDefinition {
        val interfaceDef = getOrCreateInterfaceDefinition(interfaceClass)

        val methods = implClass.methods
            .map { method ->
                method to generateImplMethodDefinition(method)
            }.toMap()

        val onActivateMethod = methods.values.singleOrNull { it.isOnActivate }
        val onDeactivateMethod = methods.values.singleOrNull { it.isOnDeactivate }

        val definition = AddressableImplDefinition(
            interfaceClass = interfaceClass,
            implClass = implClass,
            interfaceDefinition = interfaceDef,
            methods = methods,
            onActivateMethod = onActivateMethod,
            onDeactivateMethod = onDeactivateMethod
        )

        logger.debug { "Created implementation definition: $definition" }

        return definition
    }

    private fun generateImplMethodDefinition(
        method: Method
    ): AddressableImplMethodDefinition {

        val isOnActivate = method.isAnnotationPresent(OnActivate::class.java)
        val isOnDeactivate = method.isAnnotationPresent(OnDeactivate::class.java)

        if (isOnActivate || isOnDeactivate) {
            verifyMethodIsAsync(method)
        }

        return AddressableImplMethodDefinition(
            method = method,
            isOnActivate = isOnActivate,
            isOnDeactivate = isOnDeactivate
        )
    }

    private fun verifyMethodIsAsync(method: Method) {
        require(DeferredWrappers.canHandle(method.returnType)) { "Method $method does not return asynchronous type." }
    }

    fun generateCapabilities() = NodeCapabilities(
        addressableTypes = interfaceDefinitionMap.map { (key, _) -> key.name }
    )

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy