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;
022
023/**
024 * Helper for working with reflection on classes.
025 * <p/>
026 * This code is based on org.apache.camel.spring.util.ReflectionUtils class.
027 */
028public final class ReflectionHelper {
029
030    private ReflectionHelper() {
031        // utility class
032    }
033
034    /**
035     * Callback interface invoked on each field in the hierarchy.
036     */
037    public interface FieldCallback {
038
039        /**
040         * Perform an operation using the given field.
041         *
042         * @param field the field to operate on
043         */
044        void doWith(Field field) throws IllegalArgumentException, IllegalAccessException;
045    }
046
047    /**
048     * Action to take on each method.
049     */
050    public interface MethodCallback {
051
052        /**
053         * Perform an operation using the given method.
054         *
055         * @param method the method to operate on
056         */
057        void doWith(Method method) throws IllegalArgumentException, IllegalAccessException;
058    }
059
060    /**
061     * Invoke the given callback on all fields in the target class, going up the
062     * class hierarchy to get all declared fields.
063     * @param clazz the target class to analyze
064     * @param fc the callback to invoke for each field
065     */
066    public static void doWithFields(Class<?> clazz, FieldCallback fc) throws IllegalArgumentException {
067        // Keep backing up the inheritance hierarchy.
068        Class<?> targetClass = clazz;
069        do {
070            Field[] fields = targetClass.getDeclaredFields();
071            for (Field field : fields) {
072                try {
073                    fc.doWith(field);
074                } catch (IllegalAccessException ex) {
075                    throw new IllegalStateException("Shouldn't be illegal to access field '" + field.getName() + "': " + ex);
076                }
077            }
078            targetClass = targetClass.getSuperclass();
079        }
080        while (targetClass != null && targetClass != Object.class);
081    }
082
083    /**
084     * Perform the given callback operation on all matching methods of the given
085     * class and superclasses (or given interface and super-interfaces).
086     * <p/>
087     * <b>Important:</b> This method does not take the
088     * {@link java.lang.reflect.Method#isBridge() bridge methods} into account.
089     *
090     * @param clazz class to start looking at
091     * @param mc the callback to invoke for each method
092     */
093    public static void doWithMethods(Class<?> clazz, MethodCallback mc) throws IllegalArgumentException {
094        // Keep backing up the inheritance hierarchy.
095        Method[] methods = clazz.getDeclaredMethods();
096        for (Method method : methods) {
097            if (method.isBridge()) {
098                // skip the bridge methods which in Java 8 leads to problems with inheritance
099                // see https://bugs.openjdk.java.net/browse/JDK-6695379
100                continue;
101            }
102            try {
103                mc.doWith(method);
104            } catch (IllegalAccessException ex) {
105                throw new IllegalStateException("Shouldn't be illegal to access method '" + method.getName() + "': " + ex);
106            }
107        }
108        if (clazz.getSuperclass() != null) {
109            doWithMethods(clazz.getSuperclass(), mc);
110        } else if (clazz.isInterface()) {
111            for (Class<?> superIfc : clazz.getInterfaces()) {
112                doWithMethods(superIfc, mc);
113            }
114        }
115    }
116
117    public static void setField(Field f, Object instance, Object value) {
118        try {
119            boolean oldAccessible = f.isAccessible();
120            boolean shouldSetAccessible = !Modifier.isPublic(f.getModifiers()) && !oldAccessible;
121            if (shouldSetAccessible) {
122                f.setAccessible(true);
123            }
124            f.set(instance, value);
125            if (shouldSetAccessible) {
126                f.setAccessible(oldAccessible);
127            }
128        } catch (Exception ex) {
129            throw new UnsupportedOperationException("Cannot inject value of class: " + value.getClass() + " into: " + f);
130        }
131    }
132
133}