Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.facebook.drift.idl.generator.ThriftIdlRenderer Maven / Gradle / Ivy
/*
* Copyright (C) 2017 Facebook, Inc.
*
* 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.facebook.drift.idl.generator;
import com.facebook.drift.annotations.ThriftField.Requiredness;
import com.facebook.drift.codec.ThriftProtocolType;
import com.facebook.drift.codec.metadata.ThriftEnumMetadata;
import com.facebook.drift.codec.metadata.ThriftFieldMetadata;
import com.facebook.drift.codec.metadata.ThriftMethodMetadata;
import com.facebook.drift.codec.metadata.ThriftServiceMetadata;
import com.facebook.drift.codec.metadata.ThriftStructMetadata;
import com.facebook.drift.codec.metadata.ThriftType;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import static com.google.common.base.Strings.emptyToNull;
import static com.google.common.collect.Streams.mapWithIndex;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
@SuppressFBWarnings("VA_FORMAT_STRING_USES_NEWLINE")
public final class ThriftIdlRenderer
{
private final ThriftTypeRenderer typeRenderer;
private ThriftIdlRenderer(ThriftTypeRenderer typeRenderer)
{
this.typeRenderer = requireNonNull(typeRenderer, "typeRenderer is null");
}
public static String renderThriftIdl(
Map namespaces,
Set includes,
List thriftTypes,
List services,
ThriftTypeRenderer typeRenderer)
{
List> enums = thriftTypes.stream()
.filter(type -> type.getProtocolType() == ThriftProtocolType.ENUM)
.map(ThriftType::getEnumMetadata)
.collect(toList());
List structs = thriftTypes.stream()
.filter(type -> type.getProtocolType() == ThriftProtocolType.STRUCT)
.map(ThriftType::getStructMetadata)
.collect(toList());
return new ThriftIdlRenderer(typeRenderer)
.render(namespaces, includes, enums, structs, services);
}
private String render(
Map namespaces,
Set includes,
List> enums,
List structs,
List services)
{
return Joiner.on("\n").skipNulls().join(
renderNamespaces(namespaces),
renderIncludes(includes),
renderEnums(enums),
renderStructs(structs),
renderServices(services));
}
private static String renderNamespaces(Map namespaces)
{
if (namespaces.isEmpty()) {
return null;
}
return namespaces.entrySet().stream()
.map(entry -> format("namespace %s %s", entry.getKey(), entry.getValue()))
.collect(joining("\n")) + "\n";
}
private static String renderIncludes(Set includes)
{
if (includes.isEmpty()) {
return null;
}
return includes.stream()
.map(include -> format("include \"%s\"", include))
.collect(joining("\n")) + "\n";
}
private static String renderEnums(List> enums)
{
return emptyToNull(enums.stream()
.map(ThriftIdlRenderer::renderEnum)
.collect(joining("\n")));
}
private static String renderEnum(ThriftEnumMetadata> metadata)
{
return documentation(metadata.getDocumentation(), "") +
format("enum %s {\n%s}\n", metadata.getEnumName(), renderEnumElements(metadata));
}
private static > String renderEnumElements(ThriftEnumMetadata metadata)
{
if (metadata.getElementsDocumentation().values().stream().allMatch(Collection::isEmpty) &&
(metadata.getByEnumConstant() == null)) {
return " " + metadata.getElementsDocumentation().keySet().stream()
.map(Enum::name)
.collect(joining(", ")) + "\n";
}
LineSeparator separator = new LineSeparator();
StringBuilder builder = new StringBuilder();
for (Entry> entry : metadata.getElementsDocumentation().entrySet()) {
builder.append(separator.getAndUpdate(!entry.getValue().isEmpty()))
.append(documentation(entry.getValue(), " "))
.append(" ").append(entry.getKey().name());
if (metadata.getByEnumConstant() != null) {
builder.append(" = ").append(metadata.getByEnumConstant().get(entry.getKey()));
}
builder.append(";\n");
}
return builder.toString();
}
private String renderStructs(List structs)
{
return emptyToNull(structs.stream()
.map(this::renderStruct)
.collect(joining("\n")));
}
private String renderStruct(ThriftStructMetadata struct)
{
StringBuilder builder = new StringBuilder()
.append(documentation(struct.getDocumentation(), ""))
.append(format("%s %s {\n", structKind(struct), struct.getStructName()));
LineSeparator separator = new LineSeparator();
for (ThriftFieldMetadata field : struct.getFields()) {
if (field.isInternal()) {
continue;
}
builder.append(separator.getAndUpdate(!field.getDocumentation().isEmpty()))
.append(documentation(field.getDocumentation(), " "))
.append(format(" %s: %s%s %s;\n",
field.getId(),
requiredness(field.getRequiredness()),
typeName(field.getThriftType()),
field.getName()));
}
return builder.append("}\n").toString();
}
private static String structKind(ThriftStructMetadata struct)
{
if (struct.isStruct()) {
return "struct";
}
if (struct.isException()) {
return "exception";
}
if (struct.isUnion()) {
return "union";
}
throw new IllegalArgumentException("Unknown type: " + struct);
}
private static String requiredness(Requiredness requiredness)
{
switch (requiredness) {
case REQUIRED:
return "required ";
case OPTIONAL:
return "optional ";
}
return "";
}
private String renderServices(List services)
{
return emptyToNull(services.stream()
.map(this::renderService)
.collect(joining("\n")));
}
private String renderService(ThriftServiceMetadata service)
{
return documentation(service.getDocumentation(), "") +
format("service %s {\n", service.getIdlName()) +
service.getMethods().stream()
.map(this::renderMethod)
.collect(joining("\n")) +
"}\n";
}
private String renderMethod(ThriftMethodMetadata method)
{
StringBuilder builder = new StringBuilder()
.append(documentation(method.getDocumentation(), " "));
String methodStart = format(" %s%s %s(",
method.getOneway() ? "oneway " : "",
typeName(method.getReturnType()),
method.getName());
List parameters = method.getParameters().stream()
.map(parameter -> format("%s: %s %s",
parameter.getId(),
typeName(parameter.getThriftType()),
parameter.getName()))
.collect(toList());
builder.append(renderParameters(methodStart, parameters));
if (!method.getExceptions().isEmpty()) {
List exceptions = mapWithIndex(method.getExceptions().entrySet().stream(),
(entry, index) -> format("%s: %s ex%s",
entry.getKey(),
typeName(entry.getValue()),
index + 1))
.collect(toList());
builder.append("\n")
.append(renderParameters(" throws (", exceptions));
}
return builder.append(";\n").toString();
}
private static String renderParameters(String start, List parameters)
{
String joined = Joiner.on(", ").join(parameters) + ")";
if ((start.length() + joined.length()) <= 80) {
return start + joined;
}
return start + "\n " + Joiner.on(",\n ").join(parameters) + ")";
}
private String typeName(ThriftType type)
{
return typeRenderer.toString(type);
}
private static String documentation(List documentation, String indent)
{
if (documentation.isEmpty()) {
return "";
}
StringBuilder builder = new StringBuilder()
.append(indent).append("/**\n");
for (String line : documentation) {
builder.append(indent).append(" *");
if (!line.isEmpty()) {
builder.append(" ").append(line);
}
builder.append("\n");
}
return builder.append(indent).append(" */\n").toString();
}
private static class LineSeparator
{
private boolean first = true;
private boolean wasDocumented;
public String getAndUpdate(boolean documented)
{
boolean needed = !first && (wasDocumented || documented);
first = false;
wasDocumented = documented;
return needed ? "\n" : "";
}
}
}