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

org.jetbrains.kotlin.container.Storage.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-Beta1
Show newest version
/*
 * Copyright 2010-2015 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jetbrains.kotlin.container

import com.intellij.util.containers.MultiMap
import java.io.Closeable
import java.io.PrintStream
import java.lang.reflect.Modifier
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.util.ArrayList
import java.util.HashSet
import java.util.LinkedHashSet

enum class ComponentStorageState {
    Initial,
    Initialized,
    Disposing,
    Disposed
}

internal class InvalidCardinalityException(message: String, val descriptors: Collection) : Exception(message)

class ComponentStorage(val myId: String) : ValueResolver {
    var state = ComponentStorageState.Initial
    private val registry = ComponentRegistry()
    private val descriptors = LinkedHashSet()
    private val dependencies = MultiMap.createLinkedSet()

    override fun resolve(request: Type, context: ValueResolveContext): ValueDescriptor? {
        if (state == ComponentStorageState.Initial)
            throw ContainerConsistencyException("Container was not composed before resolving")

        val entry = registry.tryGetEntry(request)
        if (entry.isNotEmpty()) {
            registerDependency(request, context)

            if (entry.size > 1)
                throw InvalidCardinalityException("Request $request cannot be satisfied because there is more than one type registered", entry)
            return entry.singleOrNull()
        }
        return null
    }

    private fun registerDependency(request: Type, context: ValueResolveContext) {
        if (context is ComponentResolveContext) {
            val descriptor = context.requestingDescriptor
            if (descriptor is ComponentDescriptor) {
                dependencies.putValue(descriptor, request)
            }
        }
    }

    fun dump(printer: PrintStream): Unit = with (printer) {
        val heading = "Container: $myId"
        println(heading)
        println("=".repeat(heading.length))
        println()
        getDescriptorsInDisposeOrder().forEach { descriptor ->
            println(descriptor)
            dependencies[descriptor].forEach {
                print("   -> ")
                val typeName = it.toString()
                print(typeName.substringBefore(" ")) // interface, class
                print(" ")
                print(typeName.substringAfterLast(".")) // name
                val resolve = registry.tryGetEntry(it)
                print(" as ")
                print(resolve)
                println()
            }
            println()
        }
    }


    fun resolveMultiple(request: Type, context: ValueResolveContext): Iterable {
        registerDependency(request, context)
        return registry.tryGetEntry(request)
    }

    internal fun registerDescriptors(context: ComponentResolveContext, items: List) {
        if (state == ComponentStorageState.Disposed) {
            throw ContainerConsistencyException("Cannot register descriptors in $state state")
        }

        for (descriptor in items)
            descriptors.add(descriptor)

        if (state == ComponentStorageState.Initialized)
            composeDescriptors(context, items)

    }

    fun compose(context: ComponentResolveContext) {
        if (state != ComponentStorageState.Initial)
            throw ContainerConsistencyException("Container $myId was already composed.")

        state = ComponentStorageState.Initialized
        composeDescriptors(context, descriptors)
    }

    private fun composeDescriptors(context: ComponentResolveContext, descriptors: Collection) {
        if (descriptors.isEmpty()) return

        registry.addAll(descriptors)

        val implicits = inspectDependenciesAndRegisterAdhoc(context, descriptors)

        injectProperties(context, descriptors + implicits)
    }

    private fun injectProperties(context: ComponentResolveContext, components: Collection) {
        for (component in components) {
            if (component.shouldInjectProperties) {
                injectProperties(component.getValue(), context.container.createResolveContext(component))
            }
        }
    }

    private fun inspectDependenciesAndRegisterAdhoc(context: ComponentResolveContext, descriptors: Collection): LinkedHashSet {
        val adhoc = LinkedHashSet()
        val visitedTypes = HashSet()
        for (descriptor in descriptors) {
            collectAdhocComponents(context, descriptor, visitedTypes, adhoc)
        }
        registry.addAll(adhoc)
        return adhoc
    }

    private fun collectAdhocComponents(context: ComponentResolveContext, descriptor: ComponentDescriptor,
                                       visitedTypes: HashSet, adhocDescriptors: LinkedHashSet
    ) {
        val dependencies = descriptor.getDependencies(context)
        for (type in dependencies) {
            if (!visitedTypes.add(type))
                continue

            val entry = registry.tryGetEntry(type)
            if (entry.isEmpty()) {
                val rawType: Class<*>? = when (type) {
                    is Class<*> -> type
                    is ParameterizedType -> type.rawType as? Class<*>
                    else -> null
                }
                if (rawType == null)
                    continue

                if (!Modifier.isAbstract(rawType.modifiers) && !rawType.isPrimitive) {
                    val implicitDescriptor = ImplicitSingletonTypeComponentDescriptor(context.container, rawType)
                    adhocDescriptors.add(implicitDescriptor)
                    collectAdhocComponents(context, implicitDescriptor, visitedTypes, adhocDescriptors)
                }
            }
        }
    }

    private fun injectProperties(instance: Any, context: ValueResolveContext) {
        val classInfo = instance.javaClass.getInfo()

        classInfo.setterInfos.forEach { setterInfo ->
            val methodBinding = setterInfo.method.bindToMethod(context)
            methodBinding.invoke(instance)
        }
    }

    fun dispose() {
        if (state != ComponentStorageState.Initialized) {
            if (state == ComponentStorageState.Initial)
                return // it is valid to dispose container which was not initialized
            throw ContainerConsistencyException("Component container cannot be disposed in the $state state.")
        }

        state = ComponentStorageState.Disposing
        val disposeList = getDescriptorsInDisposeOrder()
        for (descriptor in disposeList)
            disposeDescriptor(descriptor)
        state = ComponentStorageState.Disposed
    }

    private fun getDescriptorsInDisposeOrder(): List {
        return topologicalSort(descriptors) {
            val dependent = ArrayList()
            for (interfaceType in dependencies[it]) {
                for (dependency in registry.tryGetEntry(interfaceType)) {
                    dependent.add(dependency)
                }
            }
            dependent
        }
    }

    private fun disposeDescriptor(descriptor: ComponentDescriptor) {
        if (descriptor is Closeable)
            descriptor.close()
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy