/* * Copyright [2014] [Christian Loehnert, krampenschiesser@gmail.com] * 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 de.ks.reflection; import de.ks.util.Predicates; import javafx.util.Pair; import org.objenesis.ObjenesisStd; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.*; import java.util.function.Predicate; import java.util.stream.Collectors; /** * */ public class ReflectionUtil { private static final Logger log = LoggerFactory.getLogger(ReflectionUtil.class); /** * Returns a List of all methods of this class and its supertypes without the methods of Object. * The list is ordered by hierarchy level (clazz first, then clazz.getSuperClass etc.) * In a hierarchy level the methods are ordered by their name. * * @param clazz * @param predicates combined with AND * @return */ @SafeVarargs @SuppressWarnings("unchecked") public static List<Method> getAllMethods(Class<?> clazz, Predicate<Method>... predicates) { ArrayList<Method> methods = new ArrayList<>(100); Map<Pair<String, List<Class<?>>>, Method> collected = new HashMap<>(); List<Class<?>> hierarchy = getClassHierarchy(clazz, true); for (Class<?> current : hierarchy) { List<Method> declaredMethods = Arrays.asList(current.getDeclaredMethods()); Collections.sort(declaredMethods, getMethodComparator()); for (Method declaredMethod : declaredMethods) { String methodName = declaredMethod.getName(); Pair<String, List<Class<?>>> key = new Pair<>(methodName, Arrays.asList(declaredMethod.getParameterTypes())); if (!collected.containsKey(key)) { collected.put(new Pair<>(methodName, Arrays.asList(declaredMethod.getParameterTypes())), declaredMethod); methods.add(declaredMethod); } } } Predicate<Method> combinedPredicate = Predicates.combineAnd(predicates); if (combinedPredicate != null) { return methods.stream().distinct().filter(combinedPredicate).collect(Collectors.toList()); } else { return methods.stream().distinct().collect(Collectors.toList()); } } @SafeVarargs @SuppressWarnings("unchecked") public static List<Field> getAllFields(Class<?> clazz, Predicate<Field>... predicates) { ArrayList<Field> fields = new ArrayList<>(100); List<Class<?>> hierarchy = getClassHierarchy(clazz, false); Collections.reverse(hierarchy); for (Class<?> current : hierarchy) { List<Field> declaredFields = Arrays.asList(current.getDeclaredFields()); Collections.sort(declaredFields, getFieldComparator()); fields.addAll(declaredFields); } Predicate<Field> combinedPredicate = Predicates.combineAnd(predicates); if (combinedPredicate != null) { return fields.stream().filter(combinedPredicate).collect(Collectors.toList()); } else { return fields; } } public static List<Class<?>> getClassHierarchy(Class<?> clazz, boolean includeInterfaces) { Predicate<Class<?>> filter = new Predicate<Class<?>>() { @Override public boolean test(Class<?> clazz) { boolean retval = !Object.class.equals(clazz); retval = retval && (includeInterfaces ? true : !clazz.isInterface()); return retval; } }; ArrayList<Class<?>> classes = new ArrayList<>(25); try { for (Class<?> current = clazz; filter.test(current); current = current.getSuperclass()) { classes.add(current); } } catch (NullPointerException e) { e.printStackTrace(); } return classes; } public static Method getMethod(Class<?> clazz, String methodName) { try { Method method = clazz.getDeclaredMethod(methodName); method.setAccessible(true); return method; } catch (NoSuchMethodException e) { List<Method> allMethods = getAllMethods(clazz); Optional<Method> method = allMethods.parallelStream().filter((Method curr) -> curr.getName().equals(methodName)).findFirst(); if (method.isPresent()) { method.get().setAccessible(true); return method.get(); } else { log.error("Could not find method {} in {}", methodName, clazz.getName()); throw new RuntimeException(e); } } } protected static Comparator<Method> getMethodComparator() { return (o1, o2) -> o1.getName().compareTo(o2.getName()); } protected static Comparator<Field> getFieldComparator() { return (o1, o2) -> o1.getName().compareTo(o2.getName()); } public static Object invokeMethod(Method method, Object target, Object... parameters) { try { method.setAccessible(true); return method.invoke(target, parameters); } catch (IllegalAccessException | InvocationTargetException e) { log.error("Could not invoke method {} of {}", method.getName(), target.getClass().getName(), e); throw new RuntimeException(e); } } public static void setField(Field field, Object instance, Object value) { try { field.setAccessible(true); field.set(instance, value); } catch (IllegalAccessException e) { log.error("Could not set field {}", field, e); throw new RuntimeException(e); } } @SuppressWarnings("unchecked") public static <T> T newInstance(Class<T> clazz) { return newInstance(clazz, true); } @SuppressWarnings("unchecked") public static <T> T newInstance(Class<T> clazz, boolean useObjenesis) { Exception caught = null; try { Constructor<?>[] constructors = clazz.getDeclaredConstructors(); for (Constructor<?> constructor : constructors) { if (constructor.getParameterTypes().length == 0) { constructor.setAccessible(true); return (T) constructor.newInstance(); } } } catch (Exception e) { log.trace("Could not create a new instance of {} by using the default constructor.", clazz.getName(), e); caught = e; } if (useObjenesis) { return new ObjenesisStd().newInstance(clazz); } else { throw new RuntimeException(caught); } } public static Object getFieldValue(Object object, String fieldName) { Field field = getField(object, fieldName); return getFieldValue(object, field); } public static Object getFieldValue(Object object, Field field) { if (field == null) { return null; } field.setAccessible(true); try { return field.get(object); } catch (IllegalAccessException e) { log.debug("Could nto get field {} from {}", field, object.getClass().getSimpleName(), e); return null; } } public static Field getField(Object object, String fieldName) { Class<?> clazz = object.getClass(); return getField(clazz, fieldName); } public static Field getField(Class<?> clazz, String fieldName) { List<Field> allFields = getAllFields(clazz, f -> f.getName().equals(fieldName)); Field field = null; if (allFields.size() == 1) { field = allFields.get(0); } return field; } }