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 @FunctionalInterface 039 public interface FieldCallback { 040 041 /** 042 * Perform an operation using the given field. 043 * 044 * @param field the field to operate on 045 */ 046 void doWith(Field field) throws IllegalArgumentException, IllegalAccessException; 047 } 048 049 /** 050 * Action to take on each method. 051 */ 052 @FunctionalInterface 053 public interface MethodCallback { 054 055 /** 056 * Perform an operation using the given method. 057 * 058 * @param method the method to operate on 059 */ 060 void doWith(Method method) throws IllegalArgumentException, IllegalAccessException; 061 } 062 063 /** 064 * Action to take on each class. 065 */ 066 @FunctionalInterface 067 public interface ClassCallback { 068 069 /** 070 * Perform an operation using the given class. 071 * 072 * @param clazz the class to operate on 073 */ 074 void doWith(Class<?> clazz) throws IllegalArgumentException, IllegalAccessException; 075 } 076 077 /** 078 * Perform the given callback operation on the nested (inner) classes. 079 * 080 * @param clazz class to start looking at 081 * @param cc the callback to invoke for each inner class (excluding the class itself) 082 */ 083 public static void doWithClasses(Class<?> clazz, ClassCallback cc) throws IllegalArgumentException { 084 // and then nested classes 085 Class<?>[] classes = clazz.getDeclaredClasses(); 086 for (Class<?> aClazz : classes) { 087 try { 088 cc.doWith(aClazz); 089 } catch (IllegalAccessException ex) { 090 throw new IllegalStateException("Shouldn't be illegal to access class '" + aClazz.getName() + "': " + ex); 091 } 092 } 093 } 094 095 /** 096 * Invoke the given callback on all fields in the target class, going up the class hierarchy to get all declared 097 * fields. 098 * 099 * @param clazz the target class to analyze 100 * @param fc the callback to invoke for each field 101 */ 102 public static void doWithFields(Class<?> clazz, FieldCallback fc) throws IllegalArgumentException { 103 // Keep backing up the inheritance hierarchy. 104 Class<?> targetClass = clazz; 105 do { 106 Field[] fields = targetClass.getDeclaredFields(); 107 for (Field field : fields) { 108 try { 109 fc.doWith(field); 110 } catch (IllegalAccessException ex) { 111 throw new IllegalStateException("Shouldn't be illegal to access field '" + field.getName() + "': " + ex); 112 } 113 } 114 targetClass = targetClass.getSuperclass(); 115 } while (targetClass != null && targetClass != Object.class); 116 } 117 118 /** 119 * Perform the given callback operation on all matching methods of the given class and superclasses (or given 120 * interface and super-interfaces). 121 * <p/> 122 * <b>Important:</b> This method does not take the {@link java.lang.reflect.Method#isBridge() bridge methods} into 123 * account. 124 * 125 * @param clazz class to start looking at 126 * @param mc the callback to invoke for each method 127 */ 128 public static void doWithMethods(Class<?> clazz, MethodCallback mc) throws IllegalArgumentException { 129 // Keep backing up the inheritance hierarchy. 130 Method[] methods = clazz.getDeclaredMethods(); 131 for (Method method : methods) { 132 if (method.isBridge()) { 133 // skip the bridge methods which in Java 8 leads to problems with inheritance 134 // see https://bugs.openjdk.java.net/browse/JDK-6695379 135 continue; 136 } 137 try { 138 mc.doWith(method); 139 } catch (IllegalAccessException ex) { 140 throw new IllegalStateException("Shouldn't be illegal to access method '" + method.getName() + "': " + ex); 141 } 142 } 143 if (clazz.getSuperclass() != null) { 144 doWithMethods(clazz.getSuperclass(), mc); 145 } else if (clazz.isInterface()) { 146 for (Class<?> superIfc : clazz.getInterfaces()) { 147 doWithMethods(superIfc, mc); 148 } 149 } 150 } 151 152 /** 153 * Attempt to find a {@link Method} on the supplied class with the supplied name and parameter types. Searches all 154 * superclasses up to {@code Object}. 155 * <p> 156 * Returns {@code null} if no {@link Method} can be found. 157 * 158 * @param clazz the class to introspect 159 * @param name the name of the method 160 * @param paramTypes the parameter types of the method (may be {@code null} to indicate any signature) 161 * @return the Method object, or {@code null} if none found 162 */ 163 public static Method findMethod(Class<?> clazz, String name, Class<?>... paramTypes) { 164 ObjectHelper.notNull(clazz, "Class must not be null"); 165 ObjectHelper.notNull(name, "Method name must not be null"); 166 Class<?> searchType = clazz; 167 while (searchType != null) { 168 Method[] methods = searchType.isInterface() ? searchType.getMethods() : searchType.getDeclaredMethods(); 169 for (Method method : methods) { 170 if (name.equals(method.getName()) 171 && (paramTypes == null || Arrays.equals(paramTypes, method.getParameterTypes()))) { 172 return method; 173 } 174 } 175 searchType = searchType.getSuperclass(); 176 } 177 return null; 178 } 179 180 public static void setField(Field f, Object instance, Object value) { 181 try { 182 if (!Modifier.isPublic(f.getModifiers()) && !f.canAccess(instance)) { 183 f.setAccessible(true); 184 } 185 // must use fine-grained for the correct type when setting a field value via reflection 186 Class<?> type = f.getType(); 187 if (boolean.class == type) { 188 boolean val; 189 if (value instanceof Boolean) { 190 val = (boolean) value; 191 } else { 192 val = Boolean.parseBoolean(value.toString()); 193 } 194 f.setBoolean(instance, val); 195 } else if (byte.class == type) { 196 byte val; 197 if (value instanceof Byte) { 198 val = (byte) value; 199 } else { 200 val = Byte.parseByte(value.toString()); 201 } 202 f.setByte(instance, val); 203 } else if (int.class == type) { 204 int val; 205 if (value instanceof Integer) { 206 val = (int) value; 207 } else { 208 val = Integer.parseInt(value.toString()); 209 } 210 f.setInt(instance, val); 211 } else if (long.class == type) { 212 long val; 213 if (value instanceof Long) { 214 val = (long) value; 215 } else { 216 val = Long.parseLong(value.toString()); 217 } 218 f.setLong(instance, val); 219 } else if (float.class == type) { 220 float val; 221 if (value instanceof Float) { 222 val = (float) value; 223 } else { 224 val = Float.parseFloat(value.toString()); 225 } 226 f.setFloat(instance, val); 227 } else if (double.class == type) { 228 double val; 229 if (value instanceof Double) { 230 val = (double) value; 231 } else { 232 val = Double.parseDouble(value.toString()); 233 } 234 f.setDouble(instance, val); 235 } else { 236 f.set(instance, value); 237 } 238 } catch (Exception ex) { 239 throw new UnsupportedOperationException("Cannot inject value of class: " + value.getClass() + " into: " + f); 240 } 241 } 242 243 public static Object getField(Field f, Object instance) { 244 try { 245 if (!Modifier.isPublic(f.getModifiers()) && !f.canAccess(instance)) { 246 f.setAccessible(true); 247 } 248 return f.get(instance); 249 } catch (Exception ex) { 250 // ignore 251 } 252 return null; 253 } 254 255}