package br.com.citframework.util; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.UndeclaredThrowableException; import java.util.Arrays; /** * Utilit�rios para reflection utilizados * * @author bruno.ribeiro - <a href="mailto:bruno.ribeiro@centrait.com.br">bruno.ribeiro@centrait.com.br</a> * @since 18/08/2014 * */ public final class ReflectionUtils { private ReflectionUtils() {} /** * Attempt to find a {@link Field field} on the supplied {@link Class} with the * supplied <code>name</code>. Searches all superclasses up to {@link Object}. * * @param clazz * the class to introspect * @param name * the name of the field * @return the corresponding Field object, or <code>null</code> if not found */ public static Field findField(final Class<?> clazz, final String name) { return findField(clazz, name, null); } /** * Attempt to find a {@link Field field} on the supplied {@link Class} with the * supplied <code>name</code> and/or {@link Class type}. Searches all superclasses * up to {@link Object}. * * @param clazz * the class to introspect * @param name * the name of the field (may be <code>null</code> if type is specified) * @param type * the type of the field (may be <code>null</code> if name is specified) * @return the corresponding Field object, or <code>null</code> if not found */ public static Field findField(final Class<?> clazz, final String name, final Class<?> type) { Class<?> searchType = clazz; while (!Object.class.equals(searchType) && searchType != null) { final Field[] fields = searchType.getDeclaredFields(); for (final Field field : fields) { if ((name == null || name.equals(field.getName())) && (type == null || type.equals(field.getType()))) { return field; } } searchType = searchType.getSuperclass(); } return null; } /** * Set the field represented by the supplied {@link Field field object} on the * specified {@link Object target object} to the specified <code>value</code>. * In accordance with {@link Field#set(Object, Object)} semantics, the new value * is automatically unwrapped if the underlying field has a primitive type. * <p> * Thrown exceptions are handled via a call to {@link #handleReflectionException(Exception)}. * * @param field * the field to set * @param target * the target object on which to set the field * @param value * the value to set; may be <code>null</code> */ public static void setField(final Field field, final Object target, final Object value) { try { field.set(target, value); } catch (final IllegalAccessException ex) { handleReflectionException(ex); throw new IllegalStateException("Unexpected reflection exception - " + ex.getClass().getName() + ": " + ex.getMessage()); } } /** * Seta em um atributo de uma classe o atributo com nome '{@code name}' o valor '{@code value}' no objeto desta inst�ncia * * @param target * objeto a ter o campo e valores setados * @param name * nome do atributo a ser setado o conte�do * @param value * conte�do a ser setado * @see ReflectionUtils#findField(Class, String) * @see ReflectionUtils#makeAccessible(Field) * @see ReflectionUtils#setField(Field, Object, Object) */ public static void setField(final Object target, final String name, final Object value) { final Field field = ReflectionUtils.findField(target.getClass(), name); ReflectionUtils.makeAccessible(field); ReflectionUtils.setField(field, target, value); } /** * Seta um atributo em um m�todo est�tico com um novo valor * * @param field * atributo a ter o valor alterado * @param newValue * novo valor a ser setado * @author bruno.ribeiro - <a href="mailto:bruno.ribeiro@centrait.com.br">bruno.ribeiro@centrait.com.br</a> * @since 20/08/2014 */ public static void setFieldOnStatic(final Field field, final Object newValue) { try { final Field modifiersField = Field.class.getDeclaredField("modifiers"); modifiersField.setAccessible(true); modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); field.set(null, newValue); } catch (final IllegalAccessException ex) { handleReflectionException(ex); throw new IllegalStateException("Unexpected reflection exception - " + ex.getClass().getName() + ": " + ex.getMessage()); } catch (final NoSuchFieldException ex) { throw new IllegalStateException("Unexpected reflection exception - " + ex.getClass().getName() + ": " + ex.getMessage()); } } /** * Seta um atributo em um m�todo est�tico com um novo valor * * @param clazz * classe a ser refletida * @param name * nome do atributo a ser setado o conte�do * @param newValue * novo valor a ser setado * @author bruno.ribeiro - <a href="mailto:bruno.ribeiro@centrait.com.br">bruno.ribeiro@centrait.com.br</a> * @since 20/08/2014 */ public static void setFieldOnStatic(final Class<?> clazz, final String name, final Object newValue) { final Field field = ReflectionUtils.findField(clazz, name); field.setAccessible(true); setFieldOnStatic(field, newValue); } /** * Get the field represented by the supplied {@link Field field object} on the * specified {@link Object target object}. In accordance with {@link Field#get(Object)} semantics, the returned value is automatically wrapped if the underlying field * has a primitive type. * <p> * Thrown exceptions are handled via a call to {@link #handleReflectionException(Exception)}. * * @param field * the field to get * @param target * the target object from which to get the field * @return the field's current value */ public static Object getField(final Field field, final Object target) { try { return field.get(target); } catch (final IllegalAccessException ex) { handleReflectionException(ex); throw new IllegalStateException("Unexpected reflection exception - " + ex.getClass().getName() + ": " + ex.getMessage()); } } /** * Attempt to find a {@link Method} on the supplied class with the supplied name * and parameter types. Searches all superclasses up to <code>Object</code>. * <p> * Returns <code>null</code> if no {@link Method} can be found. * * @param clazz * the class to introspect * @param name * the name of the method * @param paramTypes * the parameter types of the method * (may be <code>null</code> to indicate any signature) * @return the Method object, or <code>null</code> if none found */ public static Method findMethod(final Class<?> clazz, final String name, final Class<?>... paramTypes) { Class<?> searchType = clazz; while (searchType != null) { final Method[] methods = searchType.isInterface() ? searchType.getMethods() : searchType.getDeclaredMethods(); for (final Method method : methods) { if (name.equals(method.getName()) && (paramTypes == null || Arrays.equals(paramTypes, method.getParameterTypes()))) { return method; } } searchType = searchType.getSuperclass(); } throw new IllegalStateException(String.format("Should never get here. Method '%s' not found.", name)); } /** * Invoca o m�todo especificado {@link Method} no objeto com os argumentos informados * O objeto alvo pode ser nulo quando invocando um m�todo est�tico. * <p> * Exce��es lan�adas s�o tratadas por {@link #handleReflectionException}. * * @param method * o m�todo a ser executado * @param target * objeto alvo para ser executado o m�todo * @param args * par�metros do m�todo a ser executado (pode ser <code>null</code>) * @return resultado da execu��o, se existir */ public static Object invokeMethod(final Method method, final Object target, final Object... args) { try { return method.invoke(target, args); } catch (final Exception ex) { handleReflectionException(ex); } throw new IllegalStateException("Should never get here"); } /** * Trata a exce��o lan�ada na opera��o de reflex�o. Deve ser chamada apenas se se nenhuma exce��o "checked" seja lan�ada no m�todo alvo. * * @param ex * exce��o de reflex�o a ser tratada */ public static void handleReflectionException(final Exception ex) { if (ex instanceof NoSuchMethodException) { throw new IllegalStateException("Method not found: " + ex.getMessage()); } if (ex instanceof IllegalAccessException) { throw new IllegalStateException("Could not access method: " + ex.getMessage()); } if (ex instanceof InvocationTargetException) { handleInvocationTargetException((InvocationTargetException) ex); } if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } throw new UndeclaredThrowableException(ex); } /** * Trata exece��o {@link InvocationTargetException}. Deve ser chamada apenas se se nenhuma exce��o "checked" seja lan�ada no m�todo alvo. * * @param ex * a {@link InvocationTargetException} a ser tratada */ public static void handleInvocationTargetException(final InvocationTargetException ex) { rethrowRuntimeException(ex.getTargetException()); } /** * Relan�a {@link Throwable}, que � presumivelmente a <em>exception alvo</em> de uma {@link InvocationTargetException}. Deve ser chamada apenas se se nenhuma exce��o "checked" * seja lan�ada no m�todo alvo. * <p> * Rethrows the underlying exception cast to an {@link RuntimeException} or {@link Error} if appropriate; otherwise, throws an {@link IllegalStateException}. * * @param ex * the exception to rethrow * @throws RuntimeException * the rethrown exception */ public static void rethrowRuntimeException(final Throwable ex) { if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } if (ex instanceof Error) { throw (Error) ex; } throw new UndeclaredThrowableException(ex); } /** * Torna o campo explicitamente acess�vel, caso necess�rio. {@code setAccessible(true)} � chamado apenas quando necess�rio, evitando conflitos com a JVM {@link SecurityManager} * (se ativo) * * @param field * campo a ser tornado acess�vel * @see java.lang.reflect.Field#setAccessible */ public static void makeAccessible(final Field field) { if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) { field.setAccessible(true); } } /** * Torna o m�todo explicitamente acess�vel, caso necess�rio. {@code setAccessible(true)} � chamado apenas quando necess�rio, evitando conflitos com a JVM * {@link SecurityManager} (se ativo) * * @param method * m�todo a ser tornado acess�vel * @see java.lang.reflect.Method#setAccessible */ public static void makeAccessible(final Method method) { if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !method.isAccessible()) { method.setAccessible(true); } } /** * Recupera um array de par�metros, de acordo com os '{@code types}' informados. '{@code types}' sempre deve ser menor ou igual a '{@code values}' * * @param types * tipos a serem procurandos em '{@code values}' * @param values * '{@code }' para filtro * @return array de par�metros * @author bruno.ribeiro - <a href="mailto:bruno.ribeiro@centrait.com.br">bruno.ribeiro@centrait.com.br</a> * @since 23/09/2014 */ public static Object[] getListParameterForTypes(final Class<?>[] types, final Object... values) { if (types == null || types.length <= 0 || values == null || values.length <= 0) { return null; } final int typesLength = types.length; final int valuesLength = values.length; Assert.isTrue(typesLength > 0, "types can't be less or equal zero "); Assert.isTrue(valuesLength > 0, "values can't be less or equal zero "); Assert.isTrue(typesLength <= valuesLength, "values lenght can't be less than types length."); final Object[] result = new Object[typesLength]; for (int i = 0; i < typesLength; i++) { final Class<?> type = types[i]; for (int j = 0; j < valuesLength; j++) { final Object value = values[j]; if (value.getClass().isAssignableFrom(type)) { result[i] = value; } } } return result; } }