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

tagan.codegen-impl.1.5.1.source-code.Namespace.kt Maven / Gradle / Ivy

Go to download

Yatagan is a Dependency Injection framework, specializing on runtime performance and build speed. Supports code generation (apt/kapt/ksp) or reflection.

The newest version!
/*
 * Copyright 2022 Yandex LLC
 *
 * 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 com.yandex.yatagan.codegen.impl

import com.yandex.yatagan.lang.Annotation
import com.yandex.yatagan.lang.compiled.ArrayNameModel
import com.yandex.yatagan.lang.compiled.ClassNameModel
import com.yandex.yatagan.lang.compiled.CtTypeNameModel
import com.yandex.yatagan.lang.compiled.InvalidNameModel
import com.yandex.yatagan.lang.compiled.KeywordTypeNameModel
import com.yandex.yatagan.lang.compiled.ParameterizedNameModel
import com.yandex.yatagan.lang.compiled.WildcardNameModel
import java.util.Locale

private fun String.capitalize() = replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.US) else it.toString() }
private fun String.decapitalize() = replaceFirstChar { it.lowercase(Locale.US) }

private fun Iterable.joinWithCamelCase(firstCapital: Boolean? = null): String {
    val joined = joinToString(separator = "", transform = String::capitalize)
    return when (firstCapital) {
        null -> joined
        true -> joined.capitalize()
        false -> joined.decapitalize()
    }
}

private val NonIdentifierCharacters = "[^a-zA-Z$0-9]".toRegex()

private fun  singleValueIterator(value: T) = object : Iterator {
    var expired = false

    override fun hasNext() = !expired

    override fun next(): T {
        if (expired) throw NoSuchElementException()
        expired = true
        return value
    }
}

internal class Namespace(
    private val prefix: String = "",
) {
    private val names = hashMapOf().withDefault { 0 }

    private fun obtainNameImpl(
        nameModel: CtTypeNameModel,
    ): Iterator {
        return when (nameModel) {
            is ClassNameModel -> iterator {
                yield(nameModel.simpleNames.joinWithCamelCase())
                val fullyQualified = nameModel.packageName.split(".") + nameModel.simpleNames
                yield(fullyQualified.joinWithCamelCase())
            }
            is ParameterizedNameModel -> iterator {
                val nameGenerators = mutableListOf(obtainNameImpl(nameModel.raw))
                nameModel.typeArguments.mapTo(nameGenerators, ::obtainNameImpl)
                val variants = nameGenerators.mapTo(arrayListOf(), Iterator::next)
                while (true) {
                    yield(variants.joinWithCamelCase())
                    val available = nameGenerators.withIndex().find { it.value.hasNext() }
                    if (available != null) {
                        variants[available.index] = available.value.next()
                    } else break
                }
            }
            is WildcardNameModel ->
                nameModel.lowerBound?.let(::obtainNameImpl)
                    ?: nameModel.upperBound?.let(::obtainNameImpl)
                    ?: singleValueIterator("Any")
            is ArrayNameModel -> iterator {
                for (name in obtainNameImpl(nameModel.elementType)) {
                    yield("arrayOf$name")
                }
            }
            is KeywordTypeNameModel -> singleValueIterator(nameModel.name)
            is InvalidNameModel -> singleValueIterator(nameModel.toString())
        }
    }

    fun name(
        string: String,
        firstCapital: Boolean = false,
    ): String {
        val name = sequenceOf(this.prefix, string).asIterable().joinWithCamelCase(firstCapital = firstCapital)
        if (name !in names) {
            names[name] = 0
            return name
        }

        // Fallback to duplicate count of the last yielded name
        val count = names.merge(name, 0) { old, _ -> old + 1 }
        return name + count
    }

    fun name(
        nameModel: CtTypeNameModel,
        qualifier: Annotation? = null,
        prefix: String = "",
        suffix: String = "",
        firstCapital: Boolean = false,
    ): String {
        val qualifierString = qualifier?.toString()?.split(NonIdentifierCharacters)?.joinWithCamelCase() ?: ""
        val variants: Iterator = obtainNameImpl(nameModel)
        var name: String? = null
        for (nameVariant in variants) {
            name = sequenceOf(this.prefix, prefix, qualifierString, nameVariant, suffix).asIterable()
                .joinWithCamelCase(firstCapital = firstCapital)
            if (name !in names) {
                names[name] = 0
                return name
            }
        }

        // Fallback to duplicate count of the last yielded name
        val count = names.merge(name!!, 0) { old, _ -> old + 1 }
        return name + count
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy