001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.camel.util; 018 019import java.lang.reflect.Field; 020import java.lang.reflect.Method; 021import java.lang.reflect.Modifier; 022import java.util.Arrays; 023 024/** 025 * Helper for working with reflection on classes. 026 * <p/> 027 * This code is based on org.apache.camel.spring.util.ReflectionUtils class. 028 */ 029public final class ReflectionHelper { 030 031 private ReflectionHelper() { 032 // utility class 033 } 034 035 /** 036 * Callback interface invoked on each field in the hierarchy. 037 */ 038 public interface FieldCallback { 039 040 /** 041 * Perform an operation using the given field. 042 * 043 * @param field the field to operate on 044 */ 045 void doWith(Field field) throws IllegalArgumentException, IllegalAccessException; 046 } 047 048 /** 049 * Action to take on each method. 050 */ 051 public interface MethodCallback { 052 053 /** 054 * Perform an operation using the given method. 055 * 056 * @param method the method to operate on 057 */ 058 void doWith(Method method) throws IllegalArgumentException, IllegalAccessException; 059 } 060 061 /** 062 * Action to take on each class. 063 */ 064 public interface ClassCallback { 065 066 /** 067 * Perform an operation using the given class. 068 * 069 * @param clazz the class to operate on 070 */ 071 void doWith(Class clazz) throws IllegalArgumentException, IllegalAccessException; 072 } 073 074 /** 075 * Perform the given callback operation on the nested (inner) classes. 076 * 077 * @param clazz class to start looking at 078 * @param cc the callback to invoke for each inner class (excluding the class itself) 079 */ 080 public static void doWithClasses(Class<?> clazz, ClassCallback cc) throws IllegalArgumentException { 081 // and then nested classes 082 Class[] classes = clazz.getDeclaredClasses(); 083 for (Class aClazz : classes) { 084 try { 085 cc.doWith(aClazz); 086 } catch (IllegalAccessException ex) { 087 throw new IllegalStateException("Shouldn't be illegal to access class '" + aClazz.getName() + "': " + ex); 088 } 089 } 090 } 091 092 /** 093 * Invoke the given callback on all fields in the target class, going up the 094 * class hierarchy to get all declared fields. 095 * @param clazz the target class to analyze 096 * @param fc the callback to invoke for each field 097 */ 098 public static void doWithFields(Class<?> clazz, FieldCallback fc) throws IllegalArgumentException { 099 // Keep backing up the inheritance hierarchy. 100 Class<?> targetClass = clazz; 101 do { 102 Field[] fields = targetClass.getDeclaredFields(); 103 for (Field field : fields) { 104 try { 105 fc.doWith(field); 106 } catch (IllegalAccessException ex) { 107 throw new IllegalStateException("Shouldn't be illegal to access field '" + field.getName() + "': " + ex); 108 } 109 } 110 targetClass = targetClass.getSuperclass(); 111 } 112 while (targetClass != null && targetClass != Object.class); 113 } 114 115 /** 116 * Perform the given callback operation on all matching methods of the given 117 * class and superclasses (or given interface and super-interfaces). 118 * <p/> 119 * <b>Important:</b> This method does not take the 120 * {@link java.lang.reflect.Method#isBridge() bridge methods} into account. 121 * 122 * @param clazz class to start looking at 123 * @param mc the callback to invoke for each method 124 */ 125 public static void doWithMethods(Class<?> clazz, MethodCallback mc) throws IllegalArgumentException { 126 // Keep backing up the inheritance hierarchy. 127 Method[] methods = clazz.getDeclaredMethods(); 128 for (Method method : methods) { 129 if (method.isBridge()) { 130 // skip the bridge methods which in Java 8 leads to problems with inheritance 131 // see https://bugs.openjdk.java.net/browse/JDK-6695379 132 continue; 133 } 134 try { 135 mc.doWith(method); 136 } catch (IllegalAccessException ex) { 137 throw new IllegalStateException("Shouldn't be illegal to access method '" + method.getName() + "': " + ex); 138 } 139 } 140 if (clazz.getSuperclass() != null) { 141 doWithMethods(clazz.getSuperclass(), mc); 142 } else if (clazz.isInterface()) { 143 for (Class<?> superIfc : clazz.getInterfaces()) { 144 doWithMethods(superIfc, mc); 145 } 146 } 147 } 148 149 /** 150 * Attempt to find a {@link Method} on the supplied class with the supplied name 151 * and parameter types. Searches all superclasses up to {@code Object}. 152 * <p>Returns {@code null} if no {@link Method} can be found. 153 * @param clazz the class to introspect 154 * @param name the name of the method 155 * @param paramTypes the parameter types of the method 156 * (may be {@code null} to indicate any signature) 157 * @return the Method object, or {@code null} if none found 158 */ 159 public static Method findMethod(Class<?> clazz, String name, Class<?>... paramTypes) { 160 ObjectHelper.notNull(clazz, "Class must not be null"); 161 ObjectHelper.notNull(name, "Method name must not be null"); 162 Class<?> searchType = clazz; 163 while (searchType != null) { 164 Method[] methods = searchType.isInterface() ? searchType.getMethods() : searchType.getDeclaredMethods(); 165 for (Method method : methods) { 166 if (name.equals(method.getName()) && (paramTypes == null || Arrays.equals(paramTypes, method.getParameterTypes()))) { 167 return method; 168 } 169 } 170 searchType = searchType.getSuperclass(); 171 } 172 return null; 173 } 174 175 public static void setField(Field f, Object instance, Object value) { 176 try { 177 boolean oldAccessible = f.isAccessible(); 178 boolean shouldSetAccessible = !Modifier.isPublic(f.getModifiers()) && !oldAccessible; 179 if (shouldSetAccessible) { 180 f.setAccessible(true); 181 } 182 f.set(instance, value); 183 if (shouldSetAccessible) { 184 f.setAccessible(oldAccessible); 185 } 186 } catch (Exception ex) { 187 throw new UnsupportedOperationException("Cannot inject value of class: " + value.getClass() + " into: " + f); 188 } 189 } 190 191 public static Object getField(Field f, Object instance) { 192 try { 193 boolean oldAccessible = f.isAccessible(); 194 boolean shouldSetAccessible = !Modifier.isPublic(f.getModifiers()) && !oldAccessible; 195 if (shouldSetAccessible) { 196 f.setAccessible(true); 197 } 198 Object answer = f.get(instance); 199 if (shouldSetAccessible) { 200 f.setAccessible(oldAccessible); 201 } 202 return answer; 203 } catch (Exception ex) { 204 // ignore 205 } 206 return null; 207 } 208 209}