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

io.takari.swagger.ReflectionHelper Maven / Gradle / Ivy

The 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 io.takari.swagger;

import static com.google.common.base.Preconditions.checkNotNull;
import static java.lang.reflect.Modifier.isStatic;

import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
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.Arrays;
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 javax.annotation.Nullable;

import com.google.common.base.Function;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.reflect.TypeToken;
import com.thoughtworks.paranamer.AdaptiveParanamer;
import com.thoughtworks.paranamer.BytecodeReadingParanamer;
import com.thoughtworks.paranamer.CachingParanamer;
import com.thoughtworks.paranamer.Paranamer;

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 boolean isArray(Type type) {
    return TypeToken.of(type).getComponentType() != null;
  }

  public static Class getArrayOfType(Type componentType) {
    // this creates an extra object but is the simplest way to get an array class
    Class rawComponentType = TypeToken.of(componentType).getRawType();
    return Array.newInstance(rawComponentType, 0).getClass();
  }

  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  Set getEffectiveClassAnnotations(Class type, Class 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 builder = ImmutableSet.builder();
    addEffectiveClassAnnotations(type, annotation, builder);
    return builder.build();
  }

  private static  void addEffectiveClassAnnotations(Class type, Class annotation, ImmutableSet.Builder builder) {
    if (type.isAnnotationPresent(annotation)) {
      builder.add(type.getAnnotation(annotation));
      return;
    }
    if (type.getSuperclass() != null) {
      addEffectiveClassAnnotations(type.getSuperclass(), annotation, builder);
    }
    for (Class anInterface : type.getInterfaces()) {
      addEffectiveClassAnnotations(anInterface, annotation, builder);
    }
  }

  public static Iterable getAllDeclaredMethods(Class type) {
    ImmutableList.Builder 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 getAllDeclaredFields(Class type) {
    ImmutableList.Builder 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 findAnnotatedMethods(Class type, Class annotation) {
    List 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 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 findAnnotatedFields(Class type, Class annotation) {
    List result = new ArrayList<>();

    // gather all publicly available fields
    // 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 BytecodeReadingParanamer(), new GeneralParanamer()));

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

  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);
    }
  }

  public static String extractFieldName(Method method) {
    checkNotNull(method, "method is null");
    return extractFieldName(method.getName());
  }

  public static String extractFieldName(String methodName) {
    checkNotNull(methodName, "methodName is null");
    if ((methodName.startsWith("get") || methodName.startsWith("set")) && methodName.length() > 3) {
      String name = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
      return name;
    } else if (methodName.startsWith("is") && methodName.length() > 2) {
      String name = Character.toLowerCase(methodName.charAt(2)) + methodName.substring(3);
      return name;
    } else {
      return methodName;
    }
  }

  public static Type resolveFieldType(Type structType, Type genericType) {
    return TypeToken.of(structType).resolveType(genericType).getType();
  }

  public static Type[] resolveFieldTypes(final Type structType, Type[] genericTypes) {
    return Lists.transform(Arrays.asList(genericTypes), new Function() {
      @Nullable
      @Override
      public Type apply(@Nullable Type input) {
        return resolveFieldType(structType, input);
      }
    }).toArray(new Type[0]);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy