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

com.facebook.drift.codec.metadata.ThriftEnumMetadata Maven / Gradle / Ivy

There is a newer version: 1.39
Show newest version
/*
 * 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.drift.codec.metadata;

import com.facebook.drift.annotations.ThriftEnumUnknownValue;
import com.facebook.drift.annotations.ThriftEnumValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;

import javax.annotation.concurrent.Immutable;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

import static com.google.common.base.MoreObjects.toStringHelper;
import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;

@Immutable
public class ThriftEnumMetadata>
{
    private final Class enumClass;
    private final Map byEnumValue;
    private final Map byEnumConstant;
    private final Optional unknownEnumConstant;
    private final String enumName;
    private final ImmutableList documentation;
    private final ImmutableMap> elementDocs;

    public ThriftEnumMetadata(
            String enumName,
            Class enumClass)
            throws RuntimeException
    {
        this.enumName = requireNonNull(enumName, "enumName must not be null");
        this.enumClass = requireNonNull(enumClass, "enumClass must not be null");

        Method enumValueMethod = null;
        for (Method method : enumClass.getMethods()) {
            if (method.isAnnotationPresent(ThriftEnumValue.class)) {
                checkArgument(
                        Modifier.isPublic(method.getModifiers()),
                        "Enum class %s @ThriftEnumValue method is not public: %s",
                        enumClass.getName(),
                        method);
                checkArgument(
                        !Modifier.isStatic(method.getModifiers()),
                        "Enum class %s @ThriftEnumValue method is static: %s",
                        enumClass.getName(),
                        method);
                checkArgument(
                        method.getTypeParameters().length == 0,
                        "Enum class %s @ThriftEnumValue method has parameters: %s",
                        enumClass.getName(),
                        method);
                Class returnType = method.getReturnType();
                checkArgument(
                        returnType == int.class || returnType == Integer.class,
                        "Enum class %s @ThriftEnumValue method does not return int or Integer: %s",
                        enumClass.getName(),
                        method);
                checkArgument(
                        enumValueMethod == null,
                        "Enum class %s has multiple methods annotated with @ThriftEnumValue",
                        enumClass.getName());
                enumValueMethod = method;
            }
        }
        checkArgument(
                enumValueMethod != null,
                "Enum class %s must have a method annotated with @ThriftEnumValue",
                enumClass.getName());

        Set values = new HashSet<>();
        ImmutableMap.Builder> elementDocs = ImmutableMap.builder();
        ImmutableMap.Builder byEnumValue = ImmutableMap.builder();
        ImmutableMap.Builder byEnumConstant = ImmutableMap.builder();
        Optional unknownEnumConstant = Optional.empty();
        for (T enumConstant : enumClass.getEnumConstants()) {
            Integer value;
            try {
                value = (Integer) enumValueMethod.invoke(enumConstant);
            }
            catch (Exception e) {
                throw new RuntimeException(format("Enum class %s element %s get value method threw an exception", enumClass.getName(), enumConstant), e);
            }
            checkArgument(value != null, "Enum class %s element %s returned null for enum value", enumClass.getName(), enumConstant);
            checkArgument(values.add(value), "Enum class %s returned duplicate enum values: %s", enumClass.getName(), value);

            byEnumValue.put(value, enumConstant);
            byEnumConstant.put(enumConstant, value);

            if (isThriftEnumUnknownValue(enumClass, enumConstant)) {
                checkArgument(!unknownEnumConstant.isPresent(), "Enum class %s has multiple constants annotated with @ThriftEnumUnknownValue", enumClass.getName());
                unknownEnumConstant = Optional.of(enumConstant);
            }
            elementDocs.put(enumConstant, ThriftCatalog.getThriftDocumentation(enumConstant));
        }
        this.byEnumValue = byEnumValue.build();
        this.byEnumConstant = byEnumConstant.build();
        this.unknownEnumConstant = unknownEnumConstant;
        this.elementDocs = elementDocs.build();
        this.documentation = ThriftCatalog.getThriftDocumentation(enumClass);
    }

    private static > boolean isThriftEnumUnknownValue(Class enumClass, T enumConstant)
    {
        try {
            return enumConstant.getClass().getField(enumConstant.name()).isAnnotationPresent(ThriftEnumUnknownValue.class);
        }
        catch (NoSuchFieldException ignored) {
            throw new IllegalArgumentException(format("Enum class %s does not have field for enum constant: %s", enumClass.getName(), enumConstant));
        }
    }

    public String getEnumName()
    {
        return enumName;
    }

    public Class getEnumClass()
    {
        return enumClass;
    }

    public Map getByEnumValue()
    {
        return byEnumValue;
    }

    public Map getByEnumConstant()
    {
        return byEnumConstant;
    }

    public Optional getUnknownEnumConstant()
    {
        return unknownEnumConstant;
    }

    public ImmutableList getDocumentation()
    {
        return documentation;
    }

    public Map> getElementsDocumentation()
    {
        return elementDocs;
    }

    @Override
    public boolean equals(Object o)
    {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        ThriftEnumMetadata that = (ThriftEnumMetadata) o;
        return Objects.equals(enumClass, that.enumClass);
    }

    @Override
    public int hashCode()
    {
        return Objects.hash(enumClass);
    }

    @Override
    public String toString()
    {
        return toStringHelper(this)
                .add("enumClass", enumClass)
                .add("byThriftValue", byEnumValue)
                .toString();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy