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

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

There is a newer version: 3.1.0
Show newest version
/*
 * 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.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.microsoft.thrifty.schema.parser.ConstElement;
import com.microsoft.thrifty.schema.parser.EnumElement;
import com.microsoft.thrifty.schema.parser.IncludeElement;
import com.microsoft.thrifty.schema.parser.NamespaceElement;
import com.microsoft.thrifty.schema.parser.ServiceElement;
import com.microsoft.thrifty.schema.parser.StructElement;
import com.microsoft.thrifty.schema.parser.ThriftFileElement;
import com.microsoft.thrifty.schema.parser.TypedefElement;

import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Set;

/**
 * A Program is the set of elements declared in a Thrift file.  It
 * contains all types, namespaces, constants, and inclusions defined therein.
 */
public class Program {
    private final ThriftFileElement element;
    private final ImmutableMap namespaces;
    private final ImmutableList cppIncludes;
    private final ImmutableList thriftIncludes;
    private final ImmutableList typedefs;
    private final ImmutableList constants;
    private final ImmutableList enums;
    private final ImmutableList structs;
    private final ImmutableList unions;
    private final ImmutableList exceptions;
    private final ImmutableList services;

    private ImmutableList includedPrograms;
    private ImmutableMap symbols;
    private ImmutableMap constSymbols;

    Program(ThriftFileElement element) {
        this.element = element;

        ImmutableMap.Builder ns = ImmutableMap.builder();
        for (NamespaceElement namespaceElement : element.namespaces()) {
            ns.put(namespaceElement.scope(), namespaceElement.namespace());
        }
        namespaces = ns.build();

        ImmutableList.Builder cppIncludes = ImmutableList.builder();
        ImmutableList.Builder thriftIncludes = ImmutableList.builder();
        for (IncludeElement includeElement : element.includes()) {
            if (includeElement.isCpp()) {
                cppIncludes.add(includeElement.path());
            } else {
                thriftIncludes.add(includeElement.path());
            }
        }
        this.cppIncludes = cppIncludes.build();
        this.thriftIncludes = thriftIncludes.build();

        ImmutableList.Builder structs = ImmutableList.builder();
        for (StructElement structElement : element.structs()) {
            structs.add(new StructType(this, structElement));
        }
        this.structs = structs.build();

        ImmutableList.Builder typedefs = ImmutableList.builder();
        for (TypedefElement typedefElement : element.typedefs()) {
            typedefs.add(new TypedefType(this, typedefElement));
        }
        this.typedefs = typedefs.build();

        ImmutableList.Builder constants = ImmutableList.builder();
        for (ConstElement constElement : element.constants()) {
            constants.add(new Constant(constElement, namespaces));
        }
        this.constants = constants.build();

        ImmutableList.Builder enums = ImmutableList.builder();
        for (EnumElement enumElement : element.enums()) {
            enums.add(new EnumType(this, enumElement));
        }
        this.enums = enums.build();

        ImmutableList.Builder unions = ImmutableList.builder();
        for (StructElement structElement : element.unions()) {
            unions.add(new StructType(this, structElement));
        }
        this.unions = unions.build();

        ImmutableList.Builder exceptions = ImmutableList.builder();
        for (StructElement structElement : element.exceptions()) {
            exceptions.add(new StructType(this, structElement));
        }
        this.exceptions = exceptions.build();

        ImmutableList.Builder services = ImmutableList.builder();
        for (ServiceElement serviceElement : element.services()) {
            services.add(new ServiceType(this, serviceElement));
        }
        this.services = services.build();
    }

    public Location location() {
        return element.location();
    }

    public ImmutableMap namespaces() {
        return this.namespaces;
    }

    public ImmutableList cppIncludes() {
        return this.cppIncludes;
    }

    public ImmutableList includes() {
        return this.includedPrograms;
    }

    public ImmutableList constants() {
        return this.constants;
    }

    public ImmutableList enums() {
        return this.enums;
    }

    public ImmutableList structs() {
        return this.structs;
    }

    public ImmutableList unions() {
        return this.unions;
    }

    public ImmutableList exceptions() {
        return this.exceptions;
    }

    public ImmutableList services() {
        return this.services;
    }

    public ImmutableList typedefs() {
        return this.typedefs;
    }

    public ImmutableMap symbols() {
        return this.symbols;
    }

    public ImmutableMap constantMap() {
        return this.constSymbols;
    }

    /**
     * Get all named types declared in this Program.
     *
     * Note that this does not include {@link #constants()}, which are
     * not types.
     *
     * @return all user-defined types contained in this Program.
     */
    public Iterable allUserTypes() {
        // Some type-resolution subtlety eludes me.  I'd have thought that
        // Iterable is castable to Iterable (covariance),
        // but the IDE claims otherwise.  So, instead of FluentIterable.from(enums),
        // we work around by making one from an empty UserType array and appending.
        FluentIterable iter = FluentIterable.of(new UserType[0]);
        return iter
                .append(enums)
                .append(structs)
                .append(unions)
                .append(exceptions)
                .append(services)
                .append(typedefs);
    }

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

        Preconditions.checkState(this.includedPrograms == null, "Included programs already resolved");

        ImmutableList.Builder includes = ImmutableList.builder();
        for (String thriftImport : thriftIncludes) {
            Program included = loader.resolveIncludedProgram(location(), thriftImport);
            included.loadIncludedPrograms(loader, visited);
            includes.add(included);
        }

        this.includedPrograms = includes.build();

        LinkedHashMap symbolMap = new LinkedHashMap<>();
        for (UserType userType : allUserTypes()) {
            UserType oldValue = symbolMap.put(userType.name(), userType);
            if (oldValue != null) {
                throw duplicateSymbol(userType.name(), oldValue, userType);
            }
        }

        this.symbols = ImmutableMap.copyOf(symbolMap);

        LinkedHashMap constSymbolMap = new LinkedHashMap<>();
        for (Constant constant : constants()) {
            Constant oldValue = constSymbolMap.put(constant.name(), constant);
            if (oldValue != null) {
                throw duplicateSymbol(constant.name(), oldValue, constant);
            }
        }

        this.constSymbols = ImmutableMap.copyOf(constSymbolMap);
    }

    private IllegalStateException duplicateSymbol(String symbol, UserElement oldValue, UserElement newValue) {
        throw new IllegalStateException(
                "Duplicate symbols: '" + symbol + "' defined at "
                + oldValue.location() + " and at " + newValue.location());
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null) return false;
        if (!(obj instanceof Program)) return false;

        // Programs are considered equal if they are derived from the same file.
        Location mine = location();
        Location other = ((Program) obj).location();
        return mine.base().equals(other.base()) && mine.path().equals(other.path());
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(element.location().base(), element.location().path());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy