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 }