001 /* 002 * Created on Oct 31, 2006 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 005 * the License. You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 010 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 011 * specific language governing permissions and limitations under the License. 012 * 013 * Copyright @2006-2009 the original author or authors. 014 */ 015 package org.fest.reflect.field; 016 017 import java.lang.reflect.Field; 018 019 import org.fest.reflect.exception.ReflectionError; 020 import org.fest.reflect.reference.TypeRef; 021 022 import static org.fest.reflect.util.Accessibles.*; 023 import static org.fest.util.Strings.*; 024 025 /** 026 * Understands the use of reflection to access a field from an object. 027 * <p> 028 * The following is an example of proper usage of this class: 029 * <pre> 030 * // Retrieves the value of the field "name" 031 * String name = {@link org.fest.reflect.core.Reflection#field(String) field}("name").{@link FieldName#ofType(Class) ofType}(String.class).{@link FieldType#in(Object) in}(person).{@link Invoker#get() get}(); 032 * 033 * // Sets the value of the field "name" to "Yoda" 034 * {@link org.fest.reflect.core.Reflection#field(String) field}("name").{@link FieldName#ofType(Class) ofType}(String.class).{@link FieldType#in(Object) in}(person).{@link Invoker#set(Object) set}("Yoda"); 035 * 036 * // Retrieves the value of the static field "count" 037 * int count = {@link org.fest.reflect.core.Reflection#staticField(String) staticField}("count").{@link StaticFieldName#ofType(Class) ofType}(int.class).{@link StaticFieldType#in(Class) in}(Person.class).{@link Invoker#get() get}(); 038 * 039 * // Sets the value of the static field "count" to 3 040 * {@link org.fest.reflect.core.Reflection#staticField(String) field}("count").{@link StaticFieldName#ofType(Class) ofType}(int.class).{@link StaticFieldType#in(Class) in}(Person.class).{@link Invoker#set(Object) set}(3); 041 * </pre> 042 * </p> 043 * 044 * @param <T> the declared type for the field to access. 045 * 046 * @author Alex Ruiz 047 */ 048 public final class Invoker<T> { 049 050 private final Object target; 051 private final Field field; 052 private final boolean accessible; 053 054 static <T> Invoker<T> newInvoker(String fieldName, TypeRef<T> expectedType, Object target) { 055 return createInvoker(fieldName, expectedType.rawType(), target); 056 } 057 058 static <T> Invoker<T> newInvoker(String fieldName, Class<T> expectedType, Object target) { 059 return createInvoker(fieldName, expectedType, target); 060 } 061 062 private static <T> Invoker<T> createInvoker(String fieldName, Class<?> expectedType, Object target) { 063 if (target == null) throw new NullPointerException("Target should not be null"); 064 Field field = lookupInClassHierarchy(fieldName, typeOf(target)); 065 verifyCorrectType(field, expectedType); 066 return new Invoker<T>(target, field); 067 } 068 069 private static Class<?> typeOf(Object target) { 070 if (target instanceof Class<?>) return (Class<?>)target; 071 return target.getClass(); 072 } 073 074 private static Field lookupInClassHierarchy(String fieldName, Class<?> declaringType) { 075 Field field = null; 076 Class<?> target = declaringType; 077 while (target != null) { 078 field = field(fieldName, target); 079 if (field != null) break; 080 target = target.getSuperclass(); 081 } 082 if (field != null) return field; 083 throw new ReflectionError(concat("Unable to find field ", quote(fieldName), " in ", declaringType.getName())); 084 } 085 086 private static void verifyCorrectType(Field field, Class<?> expectedType) { 087 boolean isAccessible = field.isAccessible(); 088 try { 089 makeAccessible(field); 090 Class<?> actualType = field.getType(); 091 if (!expectedType.isAssignableFrom(actualType)) throw incorrectFieldType(field, actualType, expectedType); 092 } finally { 093 setAccessibleIgnoringExceptions(field, isAccessible); 094 } 095 } 096 097 private Invoker(Object target, Field field) { 098 this.target = target; 099 this.field = field; 100 accessible = field.isAccessible(); 101 } 102 103 private static Field field(String fieldName, Class<?> declaringType) { 104 try { 105 return declaringType.getDeclaredField(fieldName); 106 } catch (NoSuchFieldException e) { 107 return null; 108 } 109 } 110 111 private static ReflectionError incorrectFieldType(Field field, Class<?> actual, Class<?> expected) { 112 String fieldTypeName = field.getDeclaringClass().getName(); 113 String message = concat("The type of the field ", quote(field.getName()), " in ", fieldTypeName, " should be <", 114 expected.getName(), "> but was <", actual.getName(), ">"); 115 throw new ReflectionError(message); 116 } 117 118 /** 119 * Sets a value in the field managed by this class. 120 * @param value the value to set. 121 * @throws ReflectionError if the given value cannot be set. 122 */ 123 public void set(T value) { 124 try { 125 setAccessible(field, true); 126 field.set(target, value); 127 } catch (Exception e) { 128 throw new ReflectionError(concat("Unable to update the value in field ", quote(field.getName())), e); 129 } finally { 130 setAccessibleIgnoringExceptions(field, accessible); 131 } 132 } 133 134 /** 135 * Returns the value of the field managed by this class. 136 * @return the value of the field managed by this class. 137 * @throws ReflectionError if the value of the field cannot be retrieved. 138 */ 139 @SuppressWarnings("unchecked") 140 public T get() { 141 try { 142 setAccessible(field, true); 143 return (T) field.get(target); 144 } catch (Exception e) { 145 throw new ReflectionError(concat("Unable to obtain the value in field " + quote(field.getName())), e); 146 } finally { 147 setAccessibleIgnoringExceptions(field, accessible); 148 } 149 } 150 151 /** 152 * Returns the "real" field managed by this class. 153 * @return the "real" field managed by this class. 154 */ 155 public Field info() { 156 return field; 157 } 158 }