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

com.microsoft.thrifty.schema.Program.kt Maven / Gradle / Ivy

/*
 * Thrifty
 *
 * Copyright (c) Microsoft Corporation
 *
 * All rights reserved.
 *
 * 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
 *
 * THIS CODE IS PROVIDED ON AN  *AS IS* BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
 * WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
 * FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
 *
 * See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
 */
package com.microsoft.thrifty.schema

import com.microsoft.thrifty.schema.parser.ThriftFileElement

/**
 * A Program is the set of elements declared in a Thrift file.  It
 * contains all types, namespaces, constants, and inclusions defined therein.
 */
class Program internal constructor(element: ThriftFileElement) {

    /**
     * All namespaces defined for this [Program].
     */
    val namespaces: Map = element.namespaces
            .map { it.scope to it.namespace }
            .toMap()

    /**
     * All `cpp_include` statements in this [Program].
     */
    val cppIncludes: List = element.includes
            .filter { it.isCpp }
            .map { it.path }

    private val thriftIncludes: List = element.includes
            .filter { !it.isCpp }
            .map { it.path }

    /**
     * All [constants][Constant] contained within this [Program]
     */
    val constants: List = element.constants.map { Constant(it, namespaces) }

    /**
     * All [enums][EnumType] contained within this [Program].
     */
    val enums: List = element.enums.map { EnumType(it, namespaces) }

    /**
     * All [structs][StructType] contained within this [Program].
     */
    val structs: List = element.structs.map { StructType(it, namespaces) }

    /**
     * All [unions][StructType] contained within this [Program].
     */
    val unions: List = element.unions.map { StructType(it, namespaces) }

    /**
     * All [exceptions][StructType] contained within this [Program].
     */
    val exceptions: List = element.exceptions.map { StructType(it, namespaces) }

    /**
     * All [typedefs][TypedefType] contained within this [Program].
     */
    val typedefs: List = element.typedefs.map { TypedefType(it, namespaces) }

    /**
     * All [services][ServiceType] contained within this [Program].
     */
    val services: List = element.services.map { ServiceType(it, namespaces) }

    /**
     * The location of this [Program], possibly relative (if it was loaded from the search path).
     */
    val location: Location = element.location

    private var includedPrograms: List? = null
    private var constSymbols: Map? = null

    /**
     * All other [programs][Program] included by this [Program].
     */
    val includes: List
        get() = includedPrograms ?: emptyList()

    /**
     * A map of constants in this program indexed by name.
     */
    val constantMap: Map
        get() = constSymbols ?: emptyMap()

    /**
     * Get all named types declared in this Program.
     *
     * Note that this does not include [constants], which are
     * not types.
     *
     * @return all user-defined types contained in this Program.
     */
    fun allUserTypes(): Iterable {
        return listOf(enums, structs, unions, exceptions, services, typedefs)
                .flatMapTo(mutableListOf()) { it }
    }

    /**
     * Loads this program's symbol table and list of included Programs.
     * @param loader
     * @param visited
     */
    internal fun loadIncludedPrograms(loader: Loader, visited: MutableSet) {
        if (!visited.add(this)) {
            if (includedPrograms == null) {
                loader.errorReporter().error(location, "Circular include; file includes itself transitively")
                throw IllegalStateException("Circular include: " + location.path
                        + " includes itself transitively")
            }
            return
        }

        check(this.includedPrograms == null) { "Included programs already resolved" }

        val includes = mutableListOf()
        for (thriftImport in thriftIncludes) {
            val included = loader.resolveIncludedProgram(location, thriftImport)
            included.loadIncludedPrograms(loader, visited)
            includes.add(included)
        }

        this.includedPrograms = includes

        val symbolMap = mutableMapOf()
        for (userType in allUserTypes()) {
            val oldValue = symbolMap.put(userType.name, userType)
            if (oldValue != null) {
                reportDuplicateSymbol(loader.errorReporter(), oldValue, userType)
            }
        }

        val constSymbolMap = mutableMapOf()
        for (constant in constants) {
            val oldValue = constSymbolMap.put(constant.name, constant)
            if (oldValue != null) {
                reportDuplicateSymbol(loader.errorReporter(), oldValue, constant)
            }
        }

        this.constSymbols = constSymbolMap
    }

    private fun reportDuplicateSymbol(
            reporter: ErrorReporter,
            oldValue: UserElement,
            newValue: UserElement) {
        val message = "Duplicate symbols: ${oldValue.name} defined at ${oldValue.location} and at ${newValue.location}"
        reporter.error(newValue.location, message)
    }

    /** @inheritdoc */
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is Program) return false

        // Programs are considered equal if they are derived from the same file.
        return location.base == other.location.base && location.path == other.location.path
    }

    /** @inheritdoc */
    override fun hashCode(): Int {
        var result = location.base.hashCode()
        result = 31 * result + location.path.hashCode()
        return result
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy