Package com.facebook.swift.codec.metadata

Source Code of com.facebook.swift.codec.metadata.AbstractThriftMetadataBuilder

/*
* Copyright (C) 2012 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.swift.codec.metadata;

import com.facebook.swift.codec.ThriftConstructor;
import com.facebook.swift.codec.ThriftField;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.reflect.TypeToken;
import com.google.inject.internal.MoreTypes;

import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

import static com.facebook.swift.codec.ThriftField.Requiredness;
import static com.facebook.swift.codec.metadata.FieldMetadata.extractThriftFieldName;
import static com.facebook.swift.codec.metadata.FieldMetadata.getOrExtractThriftFieldName;
import static com.facebook.swift.codec.metadata.FieldMetadata.getThriftFieldId;
import static com.facebook.swift.codec.metadata.FieldMetadata.getThriftFieldName;
import static com.facebook.swift.codec.metadata.FieldKind.THRIFT_FIELD;
import static com.facebook.swift.codec.metadata.FieldMetadata.*;
import static com.facebook.swift.codec.metadata.ReflectionHelper.extractParameterNames;
import static com.facebook.swift.codec.metadata.ReflectionHelper.findAnnotatedMethods;
import static com.facebook.swift.codec.metadata.ReflectionHelper.getAllDeclaredFields;
import static com.facebook.swift.codec.metadata.ReflectionHelper.getAllDeclaredMethods;
import static com.facebook.swift.codec.metadata.ReflectionHelper.resolveFieldTypes;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Predicates.notNull;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Lists.newArrayListWithCapacity;
import static com.google.common.collect.Sets.newTreeSet;

import static java.util.Arrays.asList;

@NotThreadSafe
public abstract class AbstractThriftMetadataBuilder
{
    protected final String structName;
    protected final Type structType;
    protected final Type builderType;

    protected final List<String> documentation;
    protected final List<FieldMetadata> fields = newArrayList();

    // readers
    protected final List<Extractor> extractors = newArrayList();

    // writers
    protected final List<MethodInjection> builderMethodInjections = newArrayList();
    protected final List<ConstructorInjection> constructorInjections = newArrayList();
    protected final List<FieldInjection> fieldInjections = newArrayList();
    protected final List<MethodInjection> methodInjections = newArrayList();

    protected final ThriftCatalog catalog;
    protected final MetadataErrors metadataErrors;

    protected AbstractThriftMetadataBuilder(ThriftCatalog catalog, Type structType)
    {
        this.catalog = checkNotNull(catalog, "catalog is null");
        this.structType = checkNotNull(structType, "structType is null");
        this.metadataErrors = new MetadataErrors(catalog.getMonitor());

        // assign the struct name from the annotation or from the Java class
        structName = extractName();
        // get the builder type from the annotation or from the Java class
        builderType = extractBuilderType();
        // grab any documentation from the annotation or saved JavaDocs
        documentation = ThriftCatalog.getThriftDocumentation(getStructClass());
        // extract all of the annotated constructor and report an error if
        // there is more than one or none
        // also extract thrift fields from the annotated parameters and verify
        extractFromConstructors();
        // extract thrift fields from the annotated fields and verify
        extractFromFields();
        // extract thrift fields from the annotated methods (and parameters) and verify
        extractFromMethods();
    }

    protected abstract String extractName();

    protected abstract Class<?> extractBuilderClass();

    protected abstract void validateConstructors();

    protected abstract boolean isValidateSetter(Method method);

    protected abstract ThriftFieldMetadata buildField(Collection<FieldMetadata> input);

    public abstract ThriftStructMetadata build();

    public MetadataErrors getMetadataErrors()
    {
        return metadataErrors;
    }

    public Class<?> getStructClass()
    {
        return TypeToken.of(structType).getRawType();
    }

    public Class<?> getBuilderClass()
    {
        return TypeToken.of(builderType).getRawType();
    }

    private Type extractBuilderType()
    {
        Class<?> builderClass = extractBuilderClass();

        if (builderClass == null) {
            return null;
        }

        if (builderClass.getTypeParameters().length == 0) {
            return builderClass;
        }

        if (!(structType instanceof ParameterizedType)) {
            metadataErrors.addError("Builder class '%s' may only be generic if the type it builds ('%s') is also generic", builderClass.getName(), getStructClass().getName());
            return builderClass;
        }

        if (builderClass.getTypeParameters().length != getStructClass().getTypeParameters().length) {
            metadataErrors.addError("Generic builder class '%s' must have the same number of type parameters as the type it builds ('%s')", builderClass.getName(), getStructClass().getName());
            return builderClass;
        }

        ParameterizedType parameterizedStructType = (ParameterizedType) structType;

        return new MoreTypes.ParameterizedTypeImpl(builderClass.getEnclosingClass(), builderClass, parameterizedStructType.getActualTypeArguments());
    }


    protected final void verifyClass(Class<? extends Annotation> annotation)
    {
        String annotationName = annotation.getSimpleName();
        String structClassName = getStructClass().getName();

        // Verify struct class is public and final
        if (!Modifier.isPublic(getStructClass().getModifiers())) {
            metadataErrors.addError("%s class '%s' is not public", annotationName, structClassName);
        }
        if (!Modifier.isFinal(getStructClass().getModifiers())) {
            metadataErrors.addError("%s class '%s' is not final (thrift does not support polymorphic data types)", annotationName, structClassName);
        }

        if (!getStructClass().isAnnotationPresent(annotation)) {
            metadataErrors.addError("%s class '%s' does not have a @%s annotation", annotationName, structClassName, annotationName);
        }
    }

    protected final void extractFromConstructors()
    {
        if (builderType == null) {
            // struct class must have a valid constructor
            addConstructors(structType);
        }
        else {
            // builder class must have a valid constructor
            addConstructors(builderType);

            // builder class must have a build method annotated with @ThriftConstructor
            addBuilderMethods();

            // verify struct class does not have @ThriftConstructors
            for (Constructor<?> constructor : getStructClass().getConstructors()) {
                if (constructor.isAnnotationPresent(ThriftConstructor.class)) {
                    metadataErrors.addWarning("Thrift class '%s' has a builder class, but constructor '%s' annotated with @ThriftConstructor", getStructClass().getName(), constructor);
                }
            }
        }
    }

    protected final void addConstructors(Type type)
    {
        Class<?> clazz = TypeToken.of(type).getRawType();

        for (Constructor<?> constructor : clazz.getConstructors()) {
            if (constructor.isSynthetic()) {
                continue;
            }
            if (!constructor.isAnnotationPresent(ThriftConstructor.class)) {
                continue;
            }

            if (!Modifier.isPublic(constructor.getModifiers())) {
                metadataErrors.addError("@ThriftConstructor '%s' is not public", constructor.toGenericString());
                continue;
            }

            List<ParameterInjection> parameters = getParameterInjections(
                    type,
                    constructor.getParameterAnnotations(),
                    resolveFieldTypes(structType, constructor.getGenericParameterTypes()),
                    extractParameterNames(constructor));
            if (parameters != null) {
                fields.addAll(parameters);
                constructorInjections.add(new ConstructorInjection(constructor, parameters));
            }
        }

        // add the default constructor
        if (constructorInjections.isEmpty()) {
            try {
                Constructor<?> constructor = clazz.getDeclaredConstructor();
                if (!Modifier.isPublic(constructor.getModifiers())) {
                    metadataErrors.addError("Default constructor '%s' is not public", constructor.toGenericString());
                }
                constructorInjections.add(new ConstructorInjection(constructor));
            }
            catch (NoSuchMethodException e) {
                metadataErrors.addError("Struct class '%s' does not have a public no-arg constructor", clazz.getName());
            }
        }

        validateConstructors();
    }

    protected final void addBuilderMethods()
    {
        for (Method method : findAnnotatedMethods(getBuilderClass(), ThriftConstructor.class)) {
            List<ParameterInjection> parameters = getParameterInjections(
                    builderType,
                    method.getParameterAnnotations(),
                    resolveFieldTypes(builderType, method.getGenericParameterTypes()),
                    extractParameterNames(method));

            // parameters are null if the method is misconfigured
            if (parameters != null) {
                fields.addAll(parameters);
                builderMethodInjections.add(new MethodInjection(method, parameters));
            }

            if (!getStructClass().isAssignableFrom(method.getReturnType())) {
                metadataErrors.addError(
                        "'%s' says that '%s' is its builder class, but @ThriftConstructor method '%s' in the builder does not build an instance assignable to that type",
                        structType,
                        builderType,
                        method.getName());
            }
        }

        // find invalid methods not skipped by findAnnotatedMethods()
        for (Method method : getAllDeclaredMethods(getBuilderClass())) {
            if (method.isAnnotationPresent(ThriftConstructor.class) || hasThriftFieldAnnotation(method)) {
                if (!Modifier.isPublic(method.getModifiers())) {
                    metadataErrors.addError("@ThriftConstructor method '%s' is not public", method.toGenericString());
                }
                if (Modifier.isStatic(method.getModifiers())) {
                    metadataErrors.addError("@ThriftConstructor method '%s' is static", method.toGenericString());
                }
            }
        }

        if (builderMethodInjections.isEmpty()) {
            metadataErrors.addError("Struct builder class '%s' does not have a public builder method annotated with @ThriftConstructor", getBuilderClass().getName());
        }
        if (builderMethodInjections.size() > 1) {
            metadataErrors.addError("Multiple builder methods are annotated with @ThriftConstructor ", builderMethodInjections);
        }
    }

    protected final void extractFromFields()
    {
        if (builderType == null) {
            // struct fields are readable and writable
            addFields(getStructClass(), true, true);
        }
        else {
            // builder fields are writable
            addFields(getBuilderClass(), false, true);
            // struct fields are readable
            addFields(getStructClass(), true, false);
        }
    }

    protected final void addFields(Class<?> clazz, boolean allowReaders, boolean allowWriters)
    {
        for (Field fieldField : ReflectionHelper.findAnnotatedFields(clazz, ThriftField.class)) {
            addField(fieldField, allowReaders, allowWriters);
        }

        // find invalid fields not skipped by findAnnotatedFields()
        for (Field field : getAllDeclaredFields(clazz)) {
            if (field.isAnnotationPresent(ThriftField.class)) {
                if (!Modifier.isPublic(field.getModifiers())) {
                    metadataErrors.addError("@ThriftField field '%s' is not public", field.toGenericString());
                }
                if (Modifier.isStatic(field.getModifiers())) {
                    metadataErrors.addError("@ThriftField field '%s' is static", field.toGenericString());
                }
            }
        }
    }

    protected final void addField(Field fieldField, boolean allowReaders, boolean allowWriters)
    {
        checkArgument(fieldField.isAnnotationPresent(ThriftField.class));

        ThriftField annotation = fieldField.getAnnotation(ThriftField.class);
        if (allowReaders) {
            FieldExtractor fieldExtractor = new FieldExtractor(structType, fieldField, annotation, THRIFT_FIELD);
            fields.add(fieldExtractor);
            extractors.add(fieldExtractor);
        }
        if (allowWriters) {
            FieldInjection fieldInjection = new FieldInjection(structType, fieldField, annotation, THRIFT_FIELD);
            fields.add(fieldInjection);
            fieldInjections.add(fieldInjection);
        }
    }

    protected final void extractFromMethods()
    {
        if (builderType != null) {
            // builder methods are writable
            addMethods(builderType, false, true);
            // struct methods are readable
            addMethods(structType, true, false);
        }
        else {
            // struct methods are readable and writable
            addMethods(structType, true, true);
        }
    }

    protected final void addMethods(Type type, boolean allowReaders, boolean allowWriters)
    {
        Class<?> clazz = TypeToken.of(type).getRawType();

        for (Method fieldMethod : findAnnotatedMethods(clazz, ThriftField.class)) {
            addMethod(type, fieldMethod, allowReaders, allowWriters);
        }

        // find invalid methods not skipped by findAnnotatedMethods()
        for (Method method : getAllDeclaredMethods(clazz)) {
            if (method.isAnnotationPresent(ThriftField.class) || hasThriftFieldAnnotation(method)) {
                if (!Modifier.isPublic(method.getModifiers())) {
                    metadataErrors.addError("@ThriftField method '%s' is not public", method.toGenericString());
                }
                if (Modifier.isStatic(method.getModifiers())) {
                    metadataErrors.addError("@ThriftField method '%s' is static", method.toGenericString());
                }
            }
        }
    }

    protected final void addMethod(Type type, Method method, boolean allowReaders, boolean allowWriters)
    {
        checkArgument(method.isAnnotationPresent(ThriftField.class));

        ThriftField annotation = method.getAnnotation(ThriftField.class);
        Class<?> clazz = TypeToken.of(type).getRawType();

        // verify parameters
        if (isValidateGetter(method)) {
            if (allowReaders) {
                MethodExtractor methodExtractor = new MethodExtractor(type, method, annotation, THRIFT_FIELD);
                fields.add(methodExtractor);
                extractors.add(methodExtractor);
            }
            else {
                metadataErrors.addError("Reader method %s.%s is not allowed on a builder class", clazz.getName(), method.getName());
            }
        }
        else if (isValidateSetter(method)) {
            if (allowWriters) {
                List<ParameterInjection> parameters;
                if (method.getParameterTypes().length > 1 || Iterables.any(asList(method.getParameterAnnotations()[0]), Predicates.instanceOf(ThriftField.class))) {
                    parameters = getParameterInjections(
                            type,
                            method.getParameterAnnotations(),
                            resolveFieldTypes(type, method.getGenericParameterTypes()),
                            extractParameterNames(method));
                    if (annotation.value() != Short.MIN_VALUE) {
                        metadataErrors.addError("A method with annotated parameters can not have a field id specified: %s.%s ", clazz.getName(), method.getName());
                    }
                    if (!annotation.name().isEmpty()) {
                        metadataErrors.addError("A method with annotated parameters can not have a field name specified: %s.%s ", clazz.getName(), method.getName());
                    }
                    if (annotation.requiredness() == Requiredness.REQUIRED) {
                        metadataErrors.addError("A method with annotated parameters can not be marked as required: %s.%s ", clazz.getName(), method.getName());
                    }
                }
                else {
                    Type parameterType = resolveFieldTypes(type, method.getGenericParameterTypes())[0];
                    parameters = ImmutableList.of(new ParameterInjection(type, 0, annotation, ReflectionHelper.extractFieldName(method), parameterType));
                }
                fields.addAll(parameters);
                methodInjections.add(new MethodInjection(method, parameters));
            }
            else {
                metadataErrors.addError("Inject method %s.%s is not allowed on struct class, since struct has a builder", clazz.getName(), method.getName());
            }
        }
        else {
            metadataErrors.addError("Method %s.%s is not a supported getter or setter", clazz.getName(), method.getName());
        }
    }

    protected final boolean hasThriftFieldAnnotation(Method method)
    {
        for (Annotation[] parameterAnnotations : method.getParameterAnnotations()) {
            for (Annotation parameterAnnotation : parameterAnnotations) {
                if (parameterAnnotation instanceof ThriftField) {
                    return true;
                }
            }
        }
        return false;
    }

    protected final boolean isValidateGetter(Method method)
    {
        return method.getParameterTypes().length == 0 && method.getReturnType() != void.class;
    }

    protected final List<ParameterInjection> getParameterInjections(Type type, Annotation[][] parameterAnnotations, Type[] parameterTypes, String[] parameterNames)
    {
        List<ParameterInjection> parameters = newArrayListWithCapacity(parameterAnnotations.length);
        for (int parameterIndex = 0; parameterIndex < parameterAnnotations.length; parameterIndex++) {
            Annotation[] annotations = parameterAnnotations[parameterIndex];
            Type parameterType = parameterTypes[parameterIndex];

            ThriftField thriftField = null;
            for (Annotation annotation : annotations) {
                if (annotation instanceof ThriftField) {
                    thriftField = (ThriftField) annotation;
                }
            }

            ParameterInjection parameterInjection = new ParameterInjection(
                    type,
                    parameterIndex,
                    thriftField,
                    parameterNames[parameterIndex],
                    parameterType
            );

            parameters.add(parameterInjection);
        }
        return parameters;
    }


    protected final void normalizeThriftFields(ThriftCatalog catalog)
    {
        // assign all fields an id (if possible)
        Set<String> fieldsWithConflictingIds = inferThriftFieldIds();

        // group fields by id
        Multimap<Optional<Short>, FieldMetadata> fieldsById = Multimaps.index(fields, getThriftFieldId());
        for (Entry<Optional<Short>, Collection<FieldMetadata>> entry : fieldsById.asMap().entrySet()) {
            Collection<FieldMetadata> fields = entry.getValue();

            // fields must have an id
            if (!entry.getKey().isPresent()) {
                for (String fieldName : newTreeSet(transform(fields, getOrExtractThriftFieldName()))) {
                    // only report errors for fields that don't have conflicting ids
                    if (!fieldsWithConflictingIds.contains(fieldName)) {
                        metadataErrors.addError("Thrift class '%s' fields %s do not have an id", structName, newTreeSet(transform(fields, getOrExtractThriftFieldName())));
                    }
                }
                continue;
            }

            short fieldId = entry.getKey().get();

            // assure all fields for this ID have the same name
            String fieldName = extractFieldName(fieldId, fields);
            for (FieldMetadata field : fields) {
                field.setName(fieldName);
            }

            Requiredness requiredness = extractFieldRequiredness(fieldId, fieldName, fields);
            for (FieldMetadata field : fields) {
                field.setRequiredness(requiredness);
            }

            // verify fields have a supported java type and all fields
            // for this ID have the same thrift type
            verifyFieldType(fieldId, fieldName, fields, catalog);
        }
    }

    /**
     * Assigns all fields an id if possible.  Fields are grouped by name and for each group, if there
     * is a single id, all fields in the group are assigned this id.  If the group has multiple ids,
     * an error is reported.
     */
    protected final Set<String> inferThriftFieldIds()
    {
        Set<String> fieldsWithConflictingIds = new HashSet<>();

        // group fields by explicit name or by name extracted from field, method or property
        Multimap<String, FieldMetadata> fieldsByExplicitOrExtractedName = Multimaps.index(fields, getOrExtractThriftFieldName());
        inferThriftFieldIds(fieldsByExplicitOrExtractedName, fieldsWithConflictingIds);

        // group fields by name extracted from field, method or property
        // this allows thrift name to be set explicitly without having to duplicate the name on getters and setters
        // todo should this be the only way this works?
        Multimap<String, FieldMetadata> fieldsByExtractedName = Multimaps.index(fields, extractThriftFieldName());
        inferThriftFieldIds(fieldsByExtractedName, fieldsWithConflictingIds);

        return fieldsWithConflictingIds;
    }

    protected final void inferThriftFieldIds(Multimap<String, FieldMetadata> fieldsByName, Set<String> fieldsWithConflictingIds)
    {
        // for each name group, set the ids on the fields without ids
        for (Entry<String, Collection<FieldMetadata>> entry : fieldsByName.asMap().entrySet()) {
            Collection<FieldMetadata> fields = entry.getValue();

            // skip all entries without a name or singleton groups... we'll deal with these later
            if (fields.size() <= 1) {
                continue;
            }

            // all ids used by this named field
            Set<Short> ids = ImmutableSet.copyOf(Optional.presentInstances(transform(fields, getThriftFieldId())));

            // multiple conflicting ids
            if (ids.size() > 1) {
                String fieldName = entry.getKey();
                if (!fieldsWithConflictingIds.contains(fieldName)) {
                    metadataErrors.addError("Thrift class '%s' field '%s' has multiple ids: %s", structName, fieldName, ids.toString());
                    fieldsWithConflictingIds.add(fieldName);
                }
                continue;
            }

            // single id, so set on all fields in this group (groups with no id are handled later)
            if (ids.size() == 1) {
                // propagate the id to all fields in this group
                short id = Iterables.getOnlyElement(ids);
                for (FieldMetadata field : fields) {
                    field.setId(id);
                }
            }
        }
    }

    protected final String extractFieldName(short id, Collection<FieldMetadata> fields)
    {
        // get the names used by these fields
        Set<String> names = ImmutableSet.copyOf(filter(transform(fields, getThriftFieldName()), notNull()));

        String name;
        if (!names.isEmpty()) {
            if (names.size() > 1) {
                metadataErrors.addWarning("Thrift class %s field %s has multiple names %s", structName, id, names);
            }
            name = names.iterator().next();
        }
        else {
            // pick a name for this field
            name = Iterables.find(transform(fields, extractThriftFieldName()), notNull());
        }
        return name;
    }

    protected final Requiredness extractFieldRequiredness(short fieldId, String fieldName, Collection<FieldMetadata> fields)
    {
        Predicate<Requiredness> specificRequiredness = new Predicate<Requiredness>()
        {
            @Override
            public boolean apply(@Nullable Requiredness input)
            {
                return (input != null) && (input != Requiredness.UNSPECIFIED);
            }
        };

        Set<Requiredness> requirednessValues = ImmutableSet.copyOf(filter(transform(fields, getThriftFieldRequiredness()), specificRequiredness));

        if (requirednessValues.size() > 1) {
            metadataErrors.addError("Thrift class '%s' field '%s(%d)' has multiple requiredness values: %s", structName, fieldName, fieldId, requirednessValues.toString());
        }

        Requiredness resolvedRequiredness;
        if (requirednessValues.isEmpty()) {
            resolvedRequiredness = Requiredness.NONE;
        }
        else {
            resolvedRequiredness = requirednessValues.iterator().next();
        }

        return resolvedRequiredness;
    }

    /**
     * Verifies that the the fields all have a supported Java type and that all fields map to the
     * exact same ThriftType.
     */
    protected final void verifyFieldType(short id, String name, Collection<FieldMetadata> fields, ThriftCatalog catalog)
    {
        boolean isSupportedType = true;
        for (FieldMetadata field : fields) {
            if (!catalog.isSupportedStructFieldType(field.getJavaType())) {
                metadataErrors.addError("Thrift class '%s' field '%s(%s)' type '%s' is not a supported Java type", structName, name, id, TypeToken.of(field.getJavaType()));
                isSupportedType = false;
                // only report the error once
                break;
            }
        }

        // fields must have the same type
        if (isSupportedType) {
            Set<ThriftType> types = new HashSet<>();
            for (FieldMetadata field : fields) {
                types.add(catalog.getThriftType(field.getJavaType()));
            }
            if (types.size() > 1) {
                metadataErrors.addError("Thrift class '%s' field '%s(%s)' has multiple types: %s", structName, name, id, types);
            }
        }
    }


    protected final ThriftMethodInjection buildBuilderConstructorInjections()
    {
        ThriftMethodInjection builderMethodInjection = null;
        if (builderType != null) {
            MethodInjection builderMethod = builderMethodInjections.get(0);
            builderMethodInjection = new ThriftMethodInjection(builderMethod.getMethod(), buildParameterInjections(builderMethod.getParameters()));
        }
        return builderMethodInjection;
    }

    protected final Iterable<ThriftFieldMetadata> buildFieldInjections()
    {
        Multimap<Optional<Short>, FieldMetadata> fieldsById = Multimaps.index(fields, getThriftFieldId());
        return Iterables.transform(fieldsById.asMap().values(), new Function<Collection<FieldMetadata>, ThriftFieldMetadata>()
        {
            @Override
            public ThriftFieldMetadata apply(Collection<FieldMetadata> input)
            {
                checkArgument(!input.isEmpty(), "input is empty");
                return buildField(input);
            }
        });
    }

    protected final List<ThriftMethodInjection> buildMethodInjections()
    {
        return Lists.transform(methodInjections, new Function<MethodInjection, ThriftMethodInjection>()
        {
            @Override
            public ThriftMethodInjection apply(MethodInjection injection)
            {
                return new ThriftMethodInjection(injection.getMethod(), buildParameterInjections(injection.getParameters()));
            }
        });
    }

    protected final List<ThriftParameterInjection> buildParameterInjections(List<ParameterInjection> parameters)
    {
        return Lists.transform(parameters, new Function<ParameterInjection, ThriftParameterInjection>()
        {
            @Override
            public ThriftParameterInjection apply(ParameterInjection injection)
            {
                return new ThriftParameterInjection(
                        injection.getId(),
                        injection.getName(),
                        injection.getParameterIndex(),
                        injection.getJavaType()
                );
            }
        });
    }
}
TOP

Related Classes of com.facebook.swift.codec.metadata.AbstractThriftMetadataBuilder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.