com.facebook.swift.codec.metadata.ReflectionHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.swift.codec.metadata.ReflectionHelper.java

Source

/*
 * 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.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.TypeToken;
import com.thoughtworks.paranamer.AdaptiveParanamer;
import com.thoughtworks.paranamer.AnnotationParanamer;
import com.thoughtworks.paranamer.BytecodeReadingParanamer;
import com.thoughtworks.paranamer.CachingParanamer;
import com.thoughtworks.paranamer.Paranamer;

import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Future;

import static java.lang.reflect.Modifier.isStatic;

public final class ReflectionHelper {
    private ReflectionHelper() {
    }

    private static final Type MAP_KEY_TYPE;
    private static final Type MAP_VALUE_TYPE;
    private static final Type ITERATOR_TYPE;
    private static final Type ITERATOR_ELEMENT_TYPE;
    private static final Type FUTURE_RETURN_TYPE;

    static {
        try {
            Method mapPutMethod = Map.class.getMethod("put", Object.class, Object.class);
            MAP_KEY_TYPE = mapPutMethod.getGenericParameterTypes()[0];
            MAP_VALUE_TYPE = mapPutMethod.getGenericParameterTypes()[1];

            ITERATOR_TYPE = Iterable.class.getMethod("iterator").getGenericReturnType();
            ITERATOR_ELEMENT_TYPE = Iterator.class.getMethod("next").getGenericReturnType();

            Method futureGetMethod = Future.class.getMethod("get");
            FUTURE_RETURN_TYPE = futureGetMethod.getGenericReturnType();
        } catch (Exception e) {
            throw Throwables.propagate(e);
        }
    }

    public static Type getMapKeyType(Type type) {
        return TypeToken.of(type).resolveType(MAP_KEY_TYPE).getType();
    }

    public static Type getMapValueType(Type type) {
        return TypeToken.of(type).resolveType(MAP_VALUE_TYPE).getType();
    }

    public static Type getIterableType(Type type) {
        return TypeToken.of(type).resolveType(ITERATOR_TYPE).resolveType(ITERATOR_ELEMENT_TYPE).getType();
    }

    public static Type getFutureReturnType(Type type) {
        return TypeToken.of(type).resolveType(FUTURE_RETURN_TYPE).getType();
    }

    public static <T extends Annotation> Set<T> getAllClassAnnotations(Class<?> type, Class<T> annotation) {
        // if the class is directly annotated, it is considered the only annotation
        if (type.isAnnotationPresent(annotation)) {
            return ImmutableSet.of(type.getAnnotation(annotation));
        }

        // otherwise find all annotations from all super classes and interfaces
        ImmutableSet.Builder<T> builder = ImmutableSet.builder();
        addAllClassAnnotations(type, annotation, builder);
        return builder.build();
    }

    private static <T extends Annotation> void addAllClassAnnotations(Class<?> type, Class<T> annotation,
            ImmutableSet.Builder<T> builder) {
        if (type.isAnnotationPresent(annotation)) {
            builder.add(type.getAnnotation(annotation));
        }
        if (type.getSuperclass() != null) {
            addAllClassAnnotations(type.getSuperclass(), annotation, builder);
        }
        for (Class<?> anInterface : type.getInterfaces()) {
            addAllClassAnnotations(anInterface, annotation, builder);
        }
    }

    public static Iterable<Method> getAllDeclaredMethods(Class<?> type) {
        ImmutableList.Builder<Method> methods = ImmutableList.builder();

        for (Class<?> clazz = type; (clazz != null) && !clazz.equals(Object.class); clazz = clazz.getSuperclass()) {
            methods.addAll(ImmutableList.copyOf(clazz.getDeclaredMethods()));
        }
        return methods.build();
    }

    public static Iterable<Field> getAllDeclaredFields(Class<?> type) {
        ImmutableList.Builder<Field> fields = ImmutableList.builder();
        for (Class<?> clazz = type; (clazz != null) && !clazz.equals(Object.class); clazz = clazz.getSuperclass()) {
            fields.addAll(ImmutableList.copyOf(clazz.getDeclaredFields()));
        }
        return fields.build();
    }

    /**
     * Find methods that are tagged with a given annotation somewhere in the hierarchy
     */
    public static Collection<Method> findAnnotatedMethods(Class<?> type, Class<? extends Annotation> annotation) {
        List<Method> result = new ArrayList<>();

        // gather all publicly available methods
        // this returns everything, even if it's declared in a parent
        for (Method method : type.getMethods()) {
            // skip methods that are used internally by the vm for implementing covariance, etc
            if (method.isSynthetic() || method.isBridge() || isStatic(method.getModifiers())) {
                continue;
            }

            // look for annotations recursively in super-classes or interfaces
            Method managedMethod = findAnnotatedMethod(type, annotation, method.getName(),
                    method.getParameterTypes());
            if (managedMethod != null) {
                result.add(managedMethod);
            }
        }

        return result;
    }

    @SuppressWarnings("PMD.EmptyCatchBlock")
    public static Method findAnnotatedMethod(Class<?> configClass, Class<? extends Annotation> annotation,
            String methodName, Class<?>... paramTypes) {
        try {
            Method method = configClass.getDeclaredMethod(methodName, paramTypes);
            if (method != null && method.isAnnotationPresent(annotation)) {
                return method;
            }
        } catch (NoSuchMethodException e) {
            // ignore
        }

        if (configClass.getSuperclass() != null) {
            Method managedMethod = findAnnotatedMethod(configClass.getSuperclass(), annotation, methodName,
                    paramTypes);
            if (managedMethod != null) {
                return managedMethod;
            }
        }

        for (Class<?> iface : configClass.getInterfaces()) {
            Method managedMethod = findAnnotatedMethod(iface, annotation, methodName, paramTypes);
            if (managedMethod != null) {
                return managedMethod;
            }
        }

        return null;
    }

    public static Collection<Field> findAnnotatedFields(Class<?> type, Class<? extends Annotation> annotation) {
        List<Field> result = new ArrayList<>();

        // gather all publicly available methods
        // this returns everything, even if it's declared in a parent
        for (Field field : type.getFields()) {
            if (field.isSynthetic() || isStatic(field.getModifiers())) {
                continue;
            }

            if (field.isAnnotationPresent(annotation)) {
                result.add(field);
            }
        }

        return result;
    }

    private static final Paranamer PARANAMER = new CachingParanamer(new AdaptiveParanamer(
            new ThriftFieldParanamer(), new BytecodeReadingParanamer(), new GeneralParanamer()));

    public static String[] extractParameterNames(AccessibleObject methodOrConstructor) {
        String[] names = PARANAMER.lookupParameterNames(methodOrConstructor);
        return names;
    }

    private static class ThriftFieldParanamer extends AnnotationParanamer {
        @Override
        protected String getNamedValue(Annotation annotation) {
            if (annotation instanceof ThriftField) {
                String name = ((ThriftField) annotation).name();
                if (!name.isEmpty()) {
                    return name;
                }
            }
            return super.getNamedValue(annotation);
        }

        @Override
        protected boolean isNamed(Annotation annotation) {
            return (annotation instanceof ThriftField) || super.isNamed(annotation);
        }
    }

    private static class GeneralParanamer implements Paranamer {
        @Override
        public String[] lookupParameterNames(AccessibleObject methodOrConstructor) {
            String[] names;
            if (methodOrConstructor instanceof Method) {
                Method method = (Method) methodOrConstructor;
                names = new String[method.getParameterTypes().length];
            } else if (methodOrConstructor instanceof Constructor<?>) {
                Constructor<?> constructor = (Constructor<?>) methodOrConstructor;
                names = new String[constructor.getParameterTypes().length];
            } else {
                throw new IllegalArgumentException(
                        "methodOrConstructor is not an instance of Method or Constructor but is "
                                + methodOrConstructor.getClass().getName());
            }
            for (int i = 0; i < names.length; i++) {
                names[i] = "arg" + i;
            }
            return names;
        }

        @Override
        public String[] lookupParameterNames(AccessibleObject methodOrConstructor,
                boolean throwExceptionIfMissing) {
            return lookupParameterNames(methodOrConstructor);
        }
    }
}