/* * Copyright 2008-2014 the original author or authors * * 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 org.kaleidofoundry.core.util; import static java.lang.reflect.Modifier.FINAL; import static java.lang.reflect.Modifier.PRIVATE; import static java.lang.reflect.Modifier.PROTECTED; import static java.lang.reflect.Modifier.PUBLIC; import static java.lang.reflect.Modifier.STATIC; import static java.lang.reflect.Modifier.TRANSIENT; import static java.lang.reflect.Modifier.VOLATILE; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import org.kaleidofoundry.core.lang.annotation.NotNull; import org.kaleidofoundry.core.lang.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Some helper methods for managing class introspection * * @author jraduget */ public abstract class ReflectionHelper { private static Logger LOGGER = LoggerFactory.getLogger(ReflectionHelper.class); /** * @param c Class that you want the "short name" * @return short name of the class in argument (for inner class too) <br/> * example : * <ul> * <li>org.kaleido.core.Configuration -> Configuration</li> * <li>org.kaleido.core.Configuration$InnerClass -> InnerClass</li> * </ul> */ @NotNull public static final String getShortClassName(@NotNull final Class<?> c) { final String name = c.getCanonicalName(); final int pos = name.lastIndexOf("."); if (pos > 0) { return name.substring(pos + 1, name.length()); } else { return name; } } /** * @param pClass class to introspect * @param name method name with no arg that we search * @return Method instance */ @Nullable public static final Method getMethodWithNoArgs(@NotNull final Class<?> pClass, final String name) { try { final Method method = pClass.getMethod(name, new Class[] {}); return method; } catch (final NoSuchMethodException nsme) { return null; } } /** * @param pClass * @return array of all classes, return by class declared methods (be careful, {@link Void} & {@link Void#TYPE} is a * type ;-) ) */ @NotNull public static final Set<Class<?>> getAllDeclaredMethodsReturns(@NotNull final Class<?> pClass) { final Set<Class<?>> result = new HashSet<Class<?>>(); final Collection<Method> methods = getAllDeclaredMethods(pClass); for (final Method method : methods) { if (method.getReturnType() != null) { result.add(method.getReturnType()); } } return result; } /** * @param c class which we seek all methods * @return Returns the full declared and inherited methods of a class. Look in the super class and implemented * interfaces to build its result */ @NotNull public static final Collection<Method> getAllDeclaredMethods(@NotNull final Class<?> c) { return getAllDeclaredMethodsByName(c).values(); } /** * @param c class which we seek all methods * @return Returns map by name of the full declared and inherited methods of a class. Look in the super class and * implemented * interfaces to build its result */ @NotNull public static final Map<String, Method> getAllDeclaredMethodsByName(@NotNull final Class<?> c) { final Map<String, Method> allMethods = new HashMap<String, Method>(); Class<?> superClass = c; // Super Class while (superClass != null && !superClass.equals(Object.class)) { for (final Method m : superClass.getDeclaredMethods()) { allMethods.put(m.getName(), m); } if (!superClass.equals(c.getSuperclass())) { superClass = c.getSuperclass(); } else { break; } } // Interfaces final Class<?>[] interfaces = c.getInterfaces(); if (interfaces != null) { for (final Class<?> interface1 : interfaces) { allMethods.putAll(getAllDeclaredMethodsByName(interface1)); } } return allMethods; } /** * @param c Class which we seek all interfaces * @return list of all interfaces implemented by the class as argument */ @NotNull public static final Set<Class<?>> getAllInterfaces(final Class<?> c) { final Set<Class<?>> set = new HashSet<Class<?>>(); Class<?> superClass = c; // Super Class while (superClass != null && !superClass.equals(Object.class)) { for (final Class<?> i : superClass.getInterfaces()) { set.add(i); } if (!superClass.equals(c.getSuperclass())) { superClass = c.getSuperclass(); } else { break; } } // Interfaces final Class<?>[] interfaces = c.getInterfaces(); if (interfaces != null) { for (final Class<?> interface1 : interfaces) { set.add(interface1); set.addAll(getAllInterfaces(interface1)); } } return set; } /** * @param c * @return Extract all super classes of a given class ({@link Object} is not included) */ @NotNull public static final Class<?>[] getSuperClasses(@NotNull final Class<?> c) { final Set<Class<?>> classSet = new LinkedHashSet<Class<?>>(); Class<?> superClass = c.getSuperclass(); // Super Class while (superClass != null && !superClass.equals(superClass.getSuperclass()) && !superClass.equals(Object.class)) { classSet.add(superClass); superClass = superClass.getSuperclass(); } return classSet.toArray(new Class<?>[classSet.size()]); } /** * @param c * @param fieldname * @return Extract the field named "fieldname" of the current class, or from the super classe<b>s</b> of the given class. */ public static final Field getDeclaredField(@NotNull final Class<?> c, @NotNull final String fieldname) { for (Field f : getAllDeclaredFields(c)) { if (fieldname.equalsIgnoreCase(f.getName())) { return f; } } return null; } /** * @param c * @return Extract all declared fields of the current class, and from the super classe<b>s</b> of the given class. */ @NotNull public static final Set<Field> getAllDeclaredFields(@NotNull final Class<?> c) { return getAllDeclaredFields(c, (Class<? extends Annotation>) null); } /** * @param c * @param annotation filter fields that are annotated by this annotation (is not null it is ignored) * @return Extract all declared fields of the current class, and from of the super classe<b>s</b> of the given class. */ @NotNull public static final Set<Field> getAllDeclaredFields(@NotNull final Class<?> c, final Class<? extends Annotation> annotation) { return getAllDeclaredFields(c, annotation, new int[0]); } /** * @param c * @param modifiers the field modifiers filter, see {@link Modifier} * @return Extract all declared fields of the current class, and from of the super classe<b>s</b> of the given class. */ public static final Set<Field> getAllDeclaredFields(@NotNull final Class<?> c, final int... modifiers) { return getAllDeclaredFields(c, null, modifiers); } /** * @param c * @param annotation filter fields that are annotated by this annotation (is not null it is ignored) * @param modifiers the field modifiers filter, see {@link Modifier} * @return Extract all declared fields of the current class and from the super classe<b>s</b> of the given class. * @see Modifier */ @NotNull public static final Set<Field> getAllDeclaredFields(@NotNull final Class<?> c, final Class<? extends Annotation> annotation, final int... modifiers) { final Set<Field> fieldSet = new LinkedHashSet<Field>(); // all fields (private, protected, public, package) for (Field f : c.getDeclaredFields()) { if (annotation == null || f.isAnnotationPresent(annotation)) { if (modifiers.length <= 0) { fieldSet.add(f); } else { for (int mod : modifiers) { if ((mod & PUBLIC) != 0 && Modifier.isPublic(f.getModifiers())) { fieldSet.add(f); } if ((mod & PROTECTED) != 0 && Modifier.isProtected(f.getModifiers())) { fieldSet.add(f); } if ((mod & PRIVATE) != 0 && Modifier.isPrivate(f.getModifiers())) { fieldSet.add(f); } if ((mod & STATIC) != 0 && Modifier.isStatic(f.getModifiers())) { fieldSet.add(f); } if ((mod & FINAL) != 0 && Modifier.isFinal(f.getModifiers())) { fieldSet.add(f); } if ((mod & TRANSIENT) != 0 && Modifier.isTransient(f.getModifiers())) { fieldSet.add(f); } if ((mod & VOLATILE) != 0 && Modifier.isVolatile(f.getModifiers())) { fieldSet.add(f); } } } } } for (Class<?> lc : getSuperClasses(c)) { // all accessible field (public, protected, package) for (Field f : lc.getDeclaredFields()) { if ((annotation == null || f.isAnnotationPresent(annotation)) && !"serialVersionUID".equals(f.getName())) { if (modifiers.length <= 0) { fieldSet.add(f); } else { for (int mod : modifiers) { if ((mod & PUBLIC) != 0 && Modifier.isPublic(f.getModifiers())) { fieldSet.add(f); } if ((mod & PROTECTED) != 0 && Modifier.isProtected(f.getModifiers())) { fieldSet.add(f); } if ((mod & PRIVATE) != 0 && Modifier.isPrivate(f.getModifiers())) { fieldSet.add(f); } if ((mod & STATIC) != 0 && Modifier.isStatic(f.getModifiers())) { fieldSet.add(f); } if ((mod & FINAL) != 0 && Modifier.isFinal(f.getModifiers())) { fieldSet.add(f); } if ((mod & TRANSIENT) != 0 && Modifier.isTransient(f.getModifiers())) { fieldSet.add(f); } if ((mod & VOLATILE) != 0 && Modifier.isVolatile(f.getModifiers())) { fieldSet.add(f); } } } } } } return fieldSet; } /** * Calling a static method on a class. <br/> * Note: The exceptions are silent. They are intercepted and ignored,but logged in! * * @param method Method name to invoke * @return static method result (or Void.TYPE if exception...) */ public static final Object invokeStaticMethodSilently(final Method method) { return invokeStaticMethodSilently(method, null); } /** * Calling a static method on a class with parameters. <br/> * Note: The exceptions are silent. They are intercepted and ignored,but logged in! * * @param method Method name to invoke * @param args method args * @return static method result (or Void.TYPE if exception...) */ public static final Object invokeStaticMethodSilently(@NotNull final Method method, final Object[] args) { try { return method.invoke(null, args != null ? args : new Object[] {}); } catch (final IllegalAccessException iae) { LOGGER.error(iae.getMessage(), iae); return Void.TYPE; } catch (final InvocationTargetException ite) { LOGGER.error(ite.getMessage(), ite.getTargetException()); return Void.TYPE; } } /** * Invocation property chained to an instance <br/> * Example : if an instance have a getter getName(), you could do : * <ul> * <li>person.name</li> * <li>person.name.length</li> * </ul> * * @param propertiesChain * @param instance * @param propertiesChainSeparator * @return method invoke result * @throws NoSuchMethodException If no method has been found (or getter method) for the property argument * @throws IllegalAccessException Attempted execution of the method due to a problem of access (private ,...) * @throws InvocationTargetException Exception during execution of the method body */ public static Object invokePropertyChain(final String propertiesChain, final Object instance, final String propertiesChainSeparator) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { int propertiesSeparatorIndex = -1; String firstProperty = null; propertiesSeparatorIndex = propertiesChain.indexOf(propertiesChainSeparator); if (propertiesSeparatorIndex >= 0) { firstProperty = propertiesChain.substring(0, propertiesSeparatorIndex); final Object result = invokePropertyWithNoArgs(instance, firstProperty); return invokePropertyChain(propertiesChain.substring(propertiesSeparatorIndex + 1), result, propertiesChainSeparator); } else { if (instance != null) { firstProperty = propertiesChain; return invokePropertyWithNoArgs(instance, firstProperty); } else { return null; } } } /** * @param instance * @param property * @return method invoke result * @throws NoSuchMethodException If no method has been found (or getter method) for the property argument * @throws IllegalAccessException Attempted execution of the method due to a problem of access (private ,...) * @throws InvocationTargetException Exception during execution of the method body * @see #invokePropertyChain(String, Object, String) */ public static Object invokePropertyWithNoArgs(final Object instance, final String property) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Method method = null; method = ReflectionHelper.getMethodWithNoArgs(instance.getClass(), "get" + StringHelper.upperCase1(property)); if (method == null) { method = ReflectionHelper.getMethodWithNoArgs(instance.getClass(), property); } if (method == null) { throw new NoSuchMethodException(property); } return method.invoke(instance, new Object[] {}); } }