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

com.uchuhimo.konf.ConfigSpec.kt Maven / Gradle / Ivy

/*
 * Copyright 2017-2019 the original author or authors.
 *
 * 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.uchuhimo.konf

import com.fasterxml.jackson.module.kotlin.isKotlinClass

/**
 * The default implementation for [Spec].
 *
 * @param prefix common prefix for items in this config spec
 */
open class ConfigSpec @JvmOverloads constructor(
    prefix: String? = null,
    items: Set> = mutableSetOf(),
    innerSpecs: Set = mutableSetOf()
) : Spec {
    final override val prefix: String = prefix ?: {
        if (javaClass == ConfigSpec::class.java || javaClass.isAnonymousClass) {
            ""
        } else {
            javaClass.let { clazz ->
                if (this::class.isCompanion) clazz.declaringClass else clazz
            }.simpleName.let { name ->
                if (name == null || name.contains('$')) {
                    ""
                } else {
                    if (name.all { it.isUpperCase() }) {
                        name.toLowerCase()
                    } else {
                        val firstLowerCaseIndex = name.indexOfFirst { it.isLowerCase() }
                        when (firstLowerCaseIndex) {
                            0 -> name
                            1 -> name[0].toLowerCase() + name.drop(1)
                            else -> name.substring(0, firstLowerCaseIndex - 1).toLowerCase() +
                                name.substring(firstLowerCaseIndex - 1)
                        }
                    }
                }
            }.let { name ->
                if (name.endsWith("Spec")) {
                    name.removeSuffix("Spec")
                } else {
                    name
                }
            }
        }
    }()

    init {
        checkPath(this.prefix)
    }

    private val _items = items as? MutableSet> ?: items.toMutableSet()

    override val items: Set> = _items

    override fun addItem(item: Item<*>) {
        if (item !in _items) {
            _items += item
        } else {
            throw RepeatedItemException(item.name)
        }
    }

    private val _innerSpecs = innerSpecs as? MutableSet ?: innerSpecs.toMutableSet()

    override val innerSpecs: Set = _innerSpecs

    override fun addInnerSpec(spec: Spec) {
        if (spec !in _innerSpecs) {
            _innerSpecs += spec
        } else {
            throw RepeatedInnerSpecException(spec)
        }
    }

    init {
        if (javaClass.isKotlinClass()) {
            javaClass.kotlin.nestedClasses.map {
                it.objectInstance
            }.filterIsInstance().forEach { spec ->
                addInnerSpec(spec)
            }
        }
    }

    /**
     * Specify a required item in this config spec.
     *
     * @param name item name without prefix
     * @param description description for this item
     * @return a property of a required item with prefix of this config spec
     */
    inline fun  required(name: String? = null, description: String = "") =
        object : RequiredProperty(this, name, description, null is T) {}

    /**
     * Specify an optional item in this config spec.
     *
     * @param default default value returned before associating this item with specified value
     * @param name item name without prefix
     * @param description description for this item
     *
     * @return a property of an optional item with prefix of this config spec
     */
    inline fun  optional(default: T, name: String? = null, description: String = "") =
        object : OptionalProperty(this, default, name, description, null is T) {}

    /**
     * Specify a lazy item in this config spec.
     *
     * @param name item name without prefix
     * @param description description for this item
     * @param thunk thunk used to evaluate value for this item
     * @return a property of a lazy item with prefix of this config spec
     */
    inline fun  lazy(
        name: String? = null,
        description: String = "",
        noinline thunk: (config: ItemContainer) -> T
    ) =
        object : LazyProperty(this, thunk, name, description, null is T) {}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy