com.microsoft.thrifty.schema.Program Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of thrifty-schema Show documentation
Show all versions of thrifty-schema Show documentation
A simple Thrift IDL parser and validator
/*
* 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());
}
}