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 extends Annotation> 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 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 findAnnotatedFields(Class> type, Class extends Annotation> 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