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