com.comphenix.noxp.FieldUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.comphenix.noxp.FieldUtils.java

Source

package com.comphenix.noxp;

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import java.util.Iterator;

import org.apache.commons.lang.ClassUtils;

/**
 * Utilities for working with fields by reflection. Adapted and refactored from
 * the dormant [reflect] Commons sandbox component.
 * <p>
 * The ability is provided to break the scoping restrictions coded by the
 * programmer. This can allow fields to be changed that shouldn't be. This
 * facility should be used with care.
 * 
 * @author Apache Software Foundation
 * @author Matt Benson
 * @since 2.5
 * @version $Id: FieldUtils.java 1057009 2011-01-09 19:48:06Z niallp $
 */
@SuppressWarnings("rawtypes")
public class FieldUtils {

    /**
     * FieldUtils instances should NOT be constructed in standard programming.
     * <p>
     * This constructor is public to permit tools that require a JavaBean
     * instance to operate.
     */
    public FieldUtils() {
        super();
    }

    /**
     * Gets an accessible <code>Field</code> by name respecting scope.
     * Superclasses/interfaces will be considered.
     * 
     * @param cls the class to reflect, must not be null
     * @param fieldName the field name to obtain
     * @return the Field object
     * @throws IllegalArgumentException if the class or field name is null
     */
    public static Field getField(Class cls, String fieldName) {
        Field field = getField(cls, fieldName, false);
        MemberUtils.setAccessibleWorkaround(field);
        return field;
    }

    /**
     * Gets an accessible <code>Field</code> by name breaking scope if
     * requested. Superclasses/interfaces will be considered.
     * 
     * @param cls the class to reflect, must not be null
     * @param fieldName the field name to obtain
     * @param forceAccess whether to break scope restrictions using the
     *            <code>setAccessible</code> method. <code>False</code> will
     *            only match public fields.
     * @return the Field object
     * @throws IllegalArgumentException if the class or field name is null
     */
    public static Field getField(final Class cls, String fieldName, boolean forceAccess) {
        if (cls == null) {
            throw new IllegalArgumentException("The class must not be null");
        }
        if (fieldName == null) {
            throw new IllegalArgumentException("The field name must not be null");
        }
        // Sun Java 1.3 has a bugged implementation of getField hence we write
        // the
        // code ourselves

        // getField() will return the Field object with the declaring class
        // set correctly to the class that declares the field. Thus requesting
        // the
        // field on a subclass will return the field from the superclass.
        //
        // priority order for lookup:
        // searchclass private/protected/package/public
        // superclass protected/package/public
        // private/different package blocks access to further superclasses
        // implementedinterface public

        // check up the superclass hierarchy
        for (Class acls = cls; acls != null; acls = acls.getSuperclass()) {
            try {
                Field field = acls.getDeclaredField(fieldName);
                // getDeclaredField checks for non-public scopes as well
                // and it returns accurate results
                if (!Modifier.isPublic(field.getModifiers())) {
                    if (forceAccess) {
                        field.setAccessible(true);
                    } else {
                        continue;
                    }
                }
                return field;
            } catch (NoSuchFieldException ex) {
                // ignore
            }
        }
        // check the public interface case. This must be manually searched for
        // incase there is a public supersuperclass field hidden by a
        // private/package
        // superclass field.
        Field match = null;
        for (Iterator intf = ClassUtils.getAllInterfaces(cls).iterator(); intf.hasNext();) {
            try {
                Field test = ((Class) intf.next()).getField(fieldName);
                if (match != null) {
                    throw new IllegalArgumentException(
                            "Reference to field " + fieldName + " is ambiguous relative to " + cls
                                    + "; a matching field exists on two or more implemented interfaces.");
                }
                match = test;
            } catch (NoSuchFieldException ex) {
                // ignore
            }
        }
        return match;
    }

    /**
     * Read an accessible static Field.
     * 
     * @param field to read
     * @return the field value
     * @throws IllegalArgumentException if the field is null or not static
     * @throws IllegalAccessException if the field is not accessible
     */
    public static Object readStaticField(Field field) throws IllegalAccessException {
        return readStaticField(field, false);
    }

    /**
     * Read a static Field.
     * 
     * @param field to read
     * @param forceAccess whether to break scope restrictions using the
     *            <code>setAccessible</code> method.
     * @return the field value
     * @throws IllegalArgumentException if the field is null or not static
     * @throws IllegalAccessException if the field is not made accessible
     */
    public static Object readStaticField(Field field, boolean forceAccess) throws IllegalAccessException {
        if (field == null) {
            throw new IllegalArgumentException("The field must not be null");
        }
        if (!Modifier.isStatic(field.getModifiers())) {
            throw new IllegalArgumentException("The field '" + field.getName() + "' is not static");
        }
        return readField(field, (Object) null, forceAccess);
    }

    /**
     * Read the named public static field. Superclasses will be considered.
     * 
     * @param cls the class to reflect, must not be null
     * @param fieldName the field name to obtain
     * @return the value of the field
     * @throws IllegalArgumentException if the class or field name is null
     * @throws IllegalAccessException if the field is not accessible
     */
    public static Object readStaticField(Class cls, String fieldName) throws IllegalAccessException {
        return readStaticField(cls, fieldName, false);
    }

    /**
     * Read the named static field. Superclasses will be considered.
     * 
     * @param cls the class to reflect, must not be null
     * @param fieldName the field name to obtain
     * @param forceAccess whether to break scope restrictions using the
     *            <code>setAccessible</code> method. <code>False</code> will
     *            only match public fields.
     * @return the Field object
     * @throws IllegalArgumentException if the class or field name is null
     * @throws IllegalAccessException if the field is not made accessible
     */
    public static Object readStaticField(Class cls, String fieldName, boolean forceAccess)
            throws IllegalAccessException {
        Field field = getField(cls, fieldName, forceAccess);
        if (field == null) {
            throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls);
        }
        // already forced access above, don't repeat it here:
        return readStaticField(field, false);
    }

    /**
     * Read an accessible Field.
     * 
     * @param field the field to use
     * @param target the object to call on, may be null for static fields
     * @return the field value
     * @throws IllegalArgumentException if the field is null
     * @throws IllegalAccessException if the field is not accessible
     */
    public static Object readField(Field field, Object target) throws IllegalAccessException {
        return readField(field, target, false);
    }

    /**
     * Read a Field.
     * 
     * @param field the field to use
     * @param target the object to call on, may be null for static fields
     * @param forceAccess whether to break scope restrictions using the
     *            <code>setAccessible</code> method.
     * @return the field value
     * @throws IllegalArgumentException if the field is null
     * @throws IllegalAccessException if the field is not made accessible
     */
    public static Object readField(Field field, Object target, boolean forceAccess) throws IllegalAccessException {
        if (field == null) {
            throw new IllegalArgumentException("The field must not be null");
        }
        if (forceAccess && !field.isAccessible()) {
            field.setAccessible(true);
        } else {
            MemberUtils.setAccessibleWorkaround(field);
        }
        return field.get(target);
    }

    /**
     * Read the named public field. Superclasses will be considered.
     * 
     * @param target the object to reflect, must not be null
     * @param fieldName the field name to obtain
     * @return the value of the field
     * @throws IllegalArgumentException if the class or field name is null
     * @throws IllegalAccessException if the named field is not public
     */
    public static Object readField(Object target, String fieldName) throws IllegalAccessException {
        return readField(target, fieldName, false);
    }

    /**
     * Read the named field. Superclasses will be considered.
     * 
     * @param target the object to reflect, must not be null
     * @param fieldName the field name to obtain
     * @param forceAccess whether to break scope restrictions using the
     *            <code>setAccessible</code> method. <code>False</code> will
     *            only match public fields.
     * @return the field value
     * @throws IllegalArgumentException if the class or field name is null
     * @throws IllegalAccessException if the named field is not made accessible
     */
    public static Object readField(Object target, String fieldName, boolean forceAccess)
            throws IllegalAccessException {
        if (target == null) {
            throw new IllegalArgumentException("target object must not be null");
        }
        Class cls = target.getClass();
        Field field = getField(cls, fieldName, forceAccess);
        if (field == null) {
            throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls);
        }
        // already forced access above, don't repeat it here:
        return readField(field, target);
    }

    /**
     * Write a public static Field.
     * 
     * @param field to write
     * @param value to set
     * @throws IllegalArgumentException if the field is null or not static
     * @throws IllegalAccessException if the field is not public or is final
     */
    public static void writeStaticField(Field field, Object value) throws IllegalAccessException {
        writeStaticField(field, value, false);
    }

    /**
     * Write a static Field.
     * 
     * @param field to write
     * @param value to set
     * @param forceAccess whether to break scope restrictions using the
     *            <code>setAccessible</code> method. <code>False</code> will
     *            only match public fields.
     * @throws IllegalArgumentException if the field is null or not static
     * @throws IllegalAccessException if the field is not made accessible or is
     *             final
     */
    public static void writeStaticField(Field field, Object value, boolean forceAccess)
            throws IllegalAccessException {
        if (field == null) {
            throw new IllegalArgumentException("The field must not be null");
        }
        if (!Modifier.isStatic(field.getModifiers())) {
            throw new IllegalArgumentException("The field '" + field.getName() + "' is not static");
        }
        writeField(field, (Object) null, value, forceAccess);
    }

    /**
     * Write a named public static Field. Superclasses will be considered.
     * 
     * @param cls Class on which the Field is to be found
     * @param fieldName to write
     * @param value to set
     * @throws IllegalArgumentException if the field cannot be located or is not
     *             static
     * @throws IllegalAccessException if the field is not public or is final
     */
    public static void writeStaticField(Class cls, String fieldName, Object value) throws IllegalAccessException {
        writeStaticField(cls, fieldName, value, false);
    }

    /**
     * Write a named static Field. Superclasses will be considered.
     * 
     * @param cls Class on which the Field is to be found
     * @param fieldName to write
     * @param value to set
     * @param forceAccess whether to break scope restrictions using the
     *            <code>setAccessible</code> method. <code>False</code> will
     *            only match public fields.
     * @throws IllegalArgumentException if the field cannot be located or is not
     *             static
     * @throws IllegalAccessException if the field is not made accessible or is
     *             final
     */
    public static void writeStaticField(Class cls, String fieldName, Object value, boolean forceAccess)
            throws IllegalAccessException {
        Field field = getField(cls, fieldName, forceAccess);
        if (field == null) {
            throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls);
        }
        // already forced access above, don't repeat it here:
        writeStaticField(field, value);
    }

    /**
     * Write an accessible field.
     * 
     * @param field to write
     * @param target the object to call on, may be null for static fields
     * @param value to set
     * @throws IllegalArgumentException if the field is null
     * @throws IllegalAccessException if the field is not accessible or is final
     */
    public static void writeField(Field field, Object target, Object value) throws IllegalAccessException {
        writeField(field, target, value, false);
    }

    /**
     * Write a field.
     * 
     * @param field to write
     * @param target the object to call on, may be null for static fields
     * @param value to set
     * @param forceAccess whether to break scope restrictions using the
     *            <code>setAccessible</code> method. <code>False</code> will
     *            only match public fields.
     * @throws IllegalArgumentException if the field is null
     * @throws IllegalAccessException if the field is not made accessible or is
     *             final
     */
    public static void writeField(Field field, Object target, Object value, boolean forceAccess)
            throws IllegalAccessException {
        if (field == null) {
            throw new IllegalArgumentException("The field must not be null");
        }
        if (forceAccess && !field.isAccessible()) {
            field.setAccessible(true);
        } else {
            MemberUtils.setAccessibleWorkaround(field);
        }
        field.set(target, value);
    }

    /**
     * Write a public field. Superclasses will be considered.
     * 
     * @param target the object to reflect, must not be null
     * @param fieldName the field name to obtain
     * @param value to set
     * @throws IllegalArgumentException if <code>target</code> or
     *             <code>fieldName</code> is null
     * @throws IllegalAccessException if the field is not accessible
     */
    public static void writeField(Object target, String fieldName, Object value) throws IllegalAccessException {
        writeField(target, fieldName, value, false);
    }

    /**
     * Write a field. Superclasses will be considered.
     * 
     * @param target the object to reflect, must not be null
     * @param fieldName the field name to obtain
     * @param value to set
     * @param forceAccess whether to break scope restrictions using the
     *            <code>setAccessible</code> method. <code>False</code> will
     *            only match public fields.
     * @throws IllegalArgumentException if <code>target</code> or
     *             <code>fieldName</code> is null
     * @throws IllegalAccessException if the field is not made accessible
     */
    public static void writeField(Object target, String fieldName, Object value, boolean forceAccess)
            throws IllegalAccessException {
        if (target == null) {
            throw new IllegalArgumentException("target object must not be null");
        }
        Class cls = target.getClass();
        Field field = getField(cls, fieldName, forceAccess);
        if (field == null) {
            throw new IllegalArgumentException("Cannot locate declared field " + cls.getName() + "." + fieldName);
        }
        // already forced access above, don't repeat it here:
        writeField(field, target, value);
    }

    // Useful member methods
    private static class MemberUtils {

        private static final int ACCESS_TEST = Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE;

        public static void setAccessibleWorkaround(AccessibleObject o) {
            if (o == null || o.isAccessible()) {
                return;
            }
            Member m = (Member) o;
            if (Modifier.isPublic(m.getModifiers()) && isPackageAccess(m.getDeclaringClass().getModifiers())) {
                try {
                    o.setAccessible(true);
                } catch (SecurityException e) { // NOPMD
                    // ignore in favor of subsequent IllegalAccessException
                }
            }
        }

        /**
         * Returns whether a given set of modifiers implies package access.
         * 
         * @param modifiers to test
         * @return true unless package/protected/private modifier detected
         */
        public static boolean isPackageAccess(int modifiers) {
            return (modifiers & ACCESS_TEST) == 0;
        }
    }
}