Package com.facebook.swift.codec.metadata

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

/*
* 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.ThriftField;
import com.facebook.swift.codec.ThriftUnion;
import com.facebook.swift.codec.ThriftUnionId;
import com.facebook.swift.codec.metadata.ThriftStructMetadata.MetadataType;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;

import javax.annotation.concurrent.NotThreadSafe;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;

import static com.facebook.swift.codec.ThriftField.Requiredness;
import static com.facebook.swift.codec.metadata.FieldKind.THRIFT_UNION_ID;
import static com.facebook.swift.codec.metadata.ReflectionHelper.findAnnotatedMethods;

@NotThreadSafe
public class ThriftUnionMetadataBuilder
    extends AbstractThriftMetadataBuilder
{
    public ThriftUnionMetadataBuilder(ThriftCatalog catalog, Type structType)
    {
        super(catalog, structType);

        // verify the class is public and has the correct annotations
        verifyClass(ThriftUnion.class);

        // extract the @ThriftUnionId fields
        extractThriftUnionId();

        // finally normalize the field metadata using things like
        normalizeThriftFields(catalog);
    }

    @Override
    protected String extractName()
    {
        ThriftUnion annotation = getStructClass().getAnnotation(ThriftUnion.class);
        if (annotation == null) {
            return getStructClass().getSimpleName();
        }
        else if (!annotation.value().isEmpty()) {
            return annotation.value();
        }
        else {
            return getStructClass().getSimpleName();
        }
    }

    @Override
    protected Class<?> extractBuilderClass()
    {
        ThriftUnion annotation = getStructClass().getAnnotation(ThriftUnion.class);
        if (annotation != null && !annotation.builder().equals(void.class)) {
            return annotation.builder();
        }
        else {
            return null;
        }
    }

    private void extractThriftUnionId()
    {
        Collection<Field> idFields = ReflectionHelper.findAnnotatedFields(getStructClass(), ThriftUnionId.class);
        Collection<Method> idMethods = findAnnotatedMethods(getStructClass(), ThriftUnionId.class);

        if (idFields.size() + idMethods.size() != 1) {
            if (idFields.size() + idMethods.size() == 0) {
                metadataErrors.addError("Neither a field nor a method is annotated with @ThriftUnionId");
            }
            else  if (idFields.size() > 1) {
                metadataErrors.addError("More than one @ThriftUnionId field present");
            }
            else if (idMethods.size() > 1) {
                metadataErrors.addError("More than one @ThriftUnionId method present");
            }
            else {
                metadataErrors.addError("Both fields and methods annotated with @ThriftUnionId");
            }
            return;
        }

        for (Field idField : idFields) {
            FieldExtractor fieldExtractor = new FieldExtractor(structType, idField, null, THRIFT_UNION_ID);
            fields.add(fieldExtractor);
            extractors.add(fieldExtractor);

            FieldInjection fieldInjection = new FieldInjection(structType, idField, null, THRIFT_UNION_ID);
            fields.add(fieldInjection);
            fieldInjections.add(fieldInjection);
        }

        for (Method idMethod: idMethods) {
            if (!Modifier.isPublic(idMethod.getModifiers())) {
                metadataErrors.addError("@ThriftUnionId method [%s] is not public", idMethod.toGenericString());
                continue;
            }
            if (Modifier.isStatic(idMethod.getModifiers())) {
                metadataErrors.addError("@ThriftUnionId method [%s] is static", idMethod.toGenericString());
                continue;
            }

            if (isValidateGetter(idMethod)) {
                MethodExtractor methodExtractor = new MethodExtractor(structType, idMethod, null, THRIFT_UNION_ID);
                fields.add(methodExtractor);
                extractors.add(methodExtractor);
            }
        }
    }

    @Override
    protected void validateConstructors()
    {
        for (ConstructorInjection constructorInjection : constructorInjections) {
            if (constructorInjection.getParameters().size() > 1) {
                metadataErrors.addError("@ThriftConstructor [%s] takes %d arguments, this is illegal for an union",
                                        constructorInjection.getConstructor().toGenericString(),
                                        constructorInjection.getParameters().size());
            }
        }
    }

    @Override
    protected boolean isValidateSetter(Method method)
    {
        // Unions only allow setters with exactly one parameters
        return method.getParameterTypes().length == 1;
    }

    //
    // Build final metadata
    //
    @Override
    public ThriftStructMetadata build()
    {
        // this code assumes that metadata is clean
        metadataErrors.throwIfHasErrors();

        // builder constructor injection
        ThriftMethodInjection builderMethodInjection = buildBuilderConstructorInjections();

        // constructor injection (or factory method for builder)
        ThriftConstructorInjection constructorInjection = buildConstructorInjection();

        // fields injections
        Iterable<ThriftFieldMetadata> fieldsMetadata = buildFieldInjections();

        // methods injections
        List<ThriftMethodInjection> methodInjections = buildMethodInjections();

        return new ThriftStructMetadata(
                structName,
                structType,
                builderType,
                MetadataType.UNION,
                Optional.fromNullable(builderMethodInjection),
                ImmutableList.copyOf(documentation),
                ImmutableList.copyOf(fieldsMetadata),
                Optional.fromNullable(constructorInjection),
                methodInjections
        );
    }

    private ThriftConstructorInjection buildConstructorInjection()
    {
        for (ConstructorInjection constructorInjection : constructorInjections) {
            if (constructorInjection.getParameters().size() == 0) {
                return new ThriftConstructorInjection(constructorInjection.getConstructor(), buildParameterInjections(constructorInjection.getParameters()));
            }
        }

        // This is actually legal for a ThriftUnion, all c'tors available take arguments and are associated with the FieldMetadata...
        return null;
    }

    @Override
    protected ThriftFieldMetadata buildField(Collection<FieldMetadata> input)
    {
        short id = -1;
        String name = null;
        Requiredness requiredness = Requiredness.UNSPECIFIED;
        FieldKind fieldType = FieldKind.THRIFT_FIELD;
        ThriftType thriftType = null;
        ThriftConstructorInjection thriftConstructorInjection = null;
        ThriftMethodInjection thriftMethodInjection = null;

        // process field injections and extractions
        ImmutableList.Builder<ThriftInjection> injections = ImmutableList.builder();
        ThriftExtraction extraction = null;
        for (FieldMetadata fieldMetadata : input) {
            id = fieldMetadata.getId();
            name = fieldMetadata.getName();
            requiredness = fieldMetadata.getRequiredness();
            fieldType = fieldMetadata.getType();
            thriftType = catalog.getThriftType(fieldMetadata.getJavaType());

            switch (requiredness) {
                case REQUIRED:
                case OPTIONAL:
                    metadataErrors.addError(
                            "Thrift union '%s' field '%s(%s)' should not be marked required or optional",
                            structName,
                            name,
                            id);
                    break;

                default:
                    break;
            }

            if (fieldMetadata instanceof FieldInjection) {
                FieldInjection fieldInjection = (FieldInjection) fieldMetadata;
                injections.add(new ThriftFieldInjection(fieldInjection.getId(), fieldInjection.getName(), fieldInjection.getField(), fieldInjection.getType()));
            }
            else if (fieldMetadata instanceof ParameterInjection) {
                ParameterInjection parameterInjection = (ParameterInjection) fieldMetadata;
                ThriftParameterInjection thriftParameterInjection =  new ThriftParameterInjection(
                        parameterInjection.getId(),
                        parameterInjection.getName(),
                        parameterInjection.getParameterIndex(),
                        fieldMetadata.getJavaType()
                );
                injections.add(thriftParameterInjection);

                for (ConstructorInjection constructorInjection : constructorInjections) {
                    if (constructorInjection.getParameters().size() == 1 && constructorInjection.getParameters().get(0).equals(parameterInjection)) {
                        thriftConstructorInjection = new ThriftConstructorInjection(constructorInjection.getConstructor(), thriftParameterInjection);
                        break;
                    }
                }

                for (MethodInjection methodInjection : methodInjections) {
                    if (methodInjection.getParameters().size() == 1 && methodInjection.getParameters().get(0).equals(parameterInjection)) {
                        thriftMethodInjection = new ThriftMethodInjection(methodInjection.getMethod(), thriftParameterInjection);
                    }
                }
            }
            else if (fieldMetadata instanceof FieldExtractor) {
                FieldExtractor fieldExtractor = (FieldExtractor) fieldMetadata;
                extraction = new ThriftFieldExtractor(fieldExtractor.getId(), fieldExtractor.getName(), fieldExtractor.getType(), fieldExtractor.getField(), fieldExtractor.getJavaType());
            }
            else if (fieldMetadata instanceof MethodExtractor) {
                MethodExtractor methodExtractor = (MethodExtractor) fieldMetadata;
                extraction = new ThriftMethodExtractor(methodExtractor.getId(), methodExtractor.getName(), methodExtractor.getType(), methodExtractor.getMethod(), methodExtractor.getJavaType());
            }
        }

        // add type coercion
        TypeCoercion coercion = null;
        if (thriftType.isCoerced()) {
            coercion = catalog.getDefaultCoercion(thriftType.getJavaType());
        }

        ThriftFieldMetadata thriftFieldMetadata = new ThriftFieldMetadata(
                id,
                requiredness,
                thriftType,
                name,
                fieldType,
                injections.build(),
                Optional.fromNullable(thriftConstructorInjection),
                Optional.fromNullable(thriftMethodInjection),
                Optional.fromNullable(extraction),
                Optional.fromNullable(coercion)
        );
        return thriftFieldMetadata;
    }
}
TOP

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

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.