001 /* 002 * Created on Nov 23, 2009 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 005 * in compliance with 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 010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 011 * or implied. See the License for the specific language governing permissions and limitations under 012 * the License. 013 * 014 * Copyright @2009 the original author or authors. 015 */ 016 package org.fest.reflect.beanproperty; 017 018 import static org.fest.util.Strings.concat; 019 import static org.fest.util.Strings.quote; 020 021 import java.beans.*; 022 023 import org.fest.reflect.exception.ReflectionError; 024 import org.fest.reflect.field.StaticFieldName; 025 import org.fest.reflect.field.StaticFieldType; 026 import org.fest.reflect.reference.TypeRef; 027 028 /** 029 * Understands the use of instrospection to access a property from a JavaBean. 030 * <p> 031 * The following is an example of proper usage of this class: 032 * <pre> 033 * // Retrieves the value of the property "name" 034 * String name = {@link org.fest.reflect.core.Reflection#property(String) property}("name").{@link PropertyName#ofType(Class) ofType}(String.class).{@link PropertyType#in(Object) in}(person).{@link Invoker#get() get}(); 035 * 036 * // Sets the value of the property "name" to "Yoda" 037 * {@link org.fest.reflect.core.Reflection#property(String) property}("name").{@link PropertyName#ofType(Class) ofType}(String.class).{@link PropertyType#in(Object) in}(person).{@link Invoker#set(Object) set}("Yoda"); 038 * 039 * // Retrieves the value of the static property "count" 040 * 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}(); 041 * 042 * // Sets the value of the static property "count" to 3 043 * {@link org.fest.reflect.core.Reflection#staticField(String) property}("count").{@link StaticFieldName#ofType(Class) ofType}(int.class).{@link StaticFieldType#in(Class) in}(Person.class).{@link Invoker#set(Object) set}(3); 044 * </pre> 045 * </p> 046 * 047 * @param <T> the declared type for the property to access. 048 * 049 * @author Alex Ruiz 050 * 051 * @since 1.2 052 */ 053 public final class Invoker<T> { 054 055 static <T> Invoker<T> newInvoker(String name, TypeRef<T> expectedType, Object target) { 056 return createInvoker(name, expectedType.rawType(), target); 057 } 058 059 static <T> Invoker<T> newInvoker(String name, Class<T> expectedType, Object target) { 060 return createInvoker(name, expectedType, target); 061 } 062 063 private static <T> Invoker<T> createInvoker(String name, Class<?> expectedType, Object target) { 064 PropertyDescriptor descriptor = descriptorForProperty(name, target); 065 verifyCorrectType(name, target, expectedType, descriptor); 066 return new Invoker<T>(name, target, descriptor); 067 } 068 069 private static PropertyDescriptor descriptorForProperty(String propertyName, Object target) { 070 BeanInfo beanInfo = null; 071 Class<?> type = target.getClass(); 072 try { 073 beanInfo = Introspector.getBeanInfo(type, Object.class); 074 } catch (Exception e) { 075 throw new ReflectionError(concat("Unable to get BeanInfo for type ", type.getName()), e); 076 } 077 for (PropertyDescriptor d : beanInfo.getPropertyDescriptors()) 078 if (propertyName.equals(d.getName())) return d; 079 throw new ReflectionError(concat("Unable to find property ", quote(propertyName), " in " , type.getName())); 080 } 081 082 static void verifyCorrectType(String name, Object target, Class<?> expectedType, PropertyDescriptor descriptor) { 083 Class<?> actualType = descriptor.getPropertyType(); 084 if (!expectedType.isAssignableFrom(actualType)) throw incorrectPropertyType(name, target, actualType, expectedType); 085 } 086 087 private static ReflectionError incorrectPropertyType(String name, Object target, Class<?> actual, Class<?> expected) { 088 String typeName = target.getClass().getName(); 089 String msg = concat("The type of the property ", quote(name), " in ", typeName, " should be <", 090 expected.getName(), "> but was <", actual.getName(), ">"); 091 throw new ReflectionError(msg); 092 } 093 094 private final String propertyName; 095 private final Object target; 096 private final PropertyDescriptor descriptor; 097 098 private Invoker(String propertyName, Object target, PropertyDescriptor descriptor) { 099 this.propertyName = propertyName; 100 this.target = target; 101 this.descriptor = descriptor; 102 } 103 104 /** 105 * Sets a value in the property managed by this class. 106 * @param value the value to set. 107 * @throws ReflectionError if the given value cannot be set. 108 */ 109 public void set(T value) { 110 try { 111 descriptor.getWriteMethod().invoke(target, value); 112 } catch (Exception e) { 113 throw new ReflectionError(concat("Unable to update the value in property ", quote(propertyName)), e); 114 } 115 } 116 117 /** 118 * Returns the value of the property managed by this class. 119 * @return the value of the property managed by this class. 120 * @throws ReflectionError if the value of the property cannot be retrieved. 121 */ 122 @SuppressWarnings("unchecked") 123 public T get() { 124 try { 125 return (T) descriptor.getReadMethod().invoke(target); 126 } catch (Exception e) { 127 throw new ReflectionError(concat("Unable to obtain the value in property " + quote(propertyName)), e); 128 } 129 } 130 131 /** 132 * Returns the "real" property managed by this class. 133 * @return the "real" property managed by this class. 134 */ 135 public PropertyDescriptor info() { 136 return descriptor; 137 } 138 }