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 static org.fest.reflect.util.Accessibles.*;
018    import static org.fest.util.Arrays.array;
019    import static org.fest.util.Strings.*;
020    
021    import java.lang.reflect.Field;
022    import java.lang.reflect.Proxy;
023    
024    import org.fest.reflect.exception.ReflectionError;
025    import org.fest.reflect.field.decorator.DecoratorInvocationHandler;
026    import org.fest.reflect.field.decorator.PostDecorator;
027    import org.fest.reflect.field.decorator.PreDecorator;
028    import org.fest.reflect.reference.TypeRef;
029    
030    /**
031     * Understands the use of reflection to access a field from an object.
032     * <p>
033     * The following is an example of proper usage of this class:
034     * 
035     * <pre>
036     *   // Retrieves the value of the field "name"
037     *   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}();
038     * 
039     *   // Sets the value of the field "name" to "Yoda"
040     *   {@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");
041     * 
042     *   // Retrieves the value of the static field "count"
043     *   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}();
044     * 
045     *   // Sets the value of the static field "count" to 3
046     *   {@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);
047     * </pre>
048     * 
049     * </p>
050     * 
051     * @param <T> the declared type for the field to access.
052     * 
053     * @author Alex Ruiz
054     * @author Ivan Hristov
055     */
056    public final class Invoker<T> {
057    
058      private final Object target;
059      private final Field field;
060      private final boolean accessible;
061      private final Class<?> expectedType;
062    
063      static <T> Invoker<T> newInvoker(String fieldName, TypeRef<T> expectedType, Object target) {
064        return createInvoker(fieldName, expectedType.rawType(), target);
065      }
066    
067      static <T> Invoker<T> newInvoker(String fieldName, Class<T> expectedType, Object target) {
068        return createInvoker(fieldName, expectedType, target);
069      }
070    
071      private static <T> Invoker<T> createInvoker(String fieldName, Class<?> expectedType, Object target) {
072        if (target == null) { throw new NullPointerException("Target should not be null"); }
073        Field field = lookupInClassHierarchy(fieldName, typeOf(target));
074        verifyCorrectType(field, expectedType);
075        return new Invoker<T>(target, field, expectedType);
076      }
077    
078      private static Class<?> typeOf(Object target) {
079        if (target instanceof Class<?>) return (Class<?>) target;
080        return target.getClass();
081      }
082    
083      private static Field lookupInClassHierarchy(String fieldName, Class<?> declaringType) {
084        Field field = null;
085        Class<?> target = declaringType;
086        while (target != null) {
087          field = field(fieldName, target);
088          if (field != null) break;
089          target = target.getSuperclass();
090        }
091        if (field != null) return field;
092        throw new ReflectionError(concat("Unable to find field ", quote(fieldName), " in ", declaringType.getName()));
093      }
094    
095      private static void verifyCorrectType(Field field, Class<?> expectedType) {
096        boolean isAccessible = field.isAccessible();
097        try {
098          makeAccessible(field);
099          Class<?> actualType = field.getType();
100          if (!expectedType.isAssignableFrom(actualType)) throw incorrectFieldType(field, actualType, expectedType);
101        } finally {
102          setAccessibleIgnoringExceptions(field, isAccessible);
103        }
104      }
105    
106      private Invoker(Object target, Field field, Class<?> expectedType) {
107        this.target = target;
108        this.field = field;
109        this.expectedType = expectedType;
110        accessible = field.isAccessible();
111      }
112    
113      private static Field field(String fieldName, Class<?> declaringType) {
114        try {
115          return declaringType.getDeclaredField(fieldName);
116        } catch (NoSuchFieldException e) {
117          return null;
118        }
119      }
120    
121      private static ReflectionError incorrectFieldType(Field field, Class<?> actual, Class<?> expected) {
122        String fieldTypeName = field.getDeclaringClass().getName();
123        String message = concat("The type of the field ", quote(field.getName()), " in ", fieldTypeName, " should be <",
124            expected.getName(), "> but was <", actual.getName(), ">");
125        throw new ReflectionError(message);
126      }
127    
128      /**
129       * Sets a value in the field managed by this class.
130       * 
131       * @param value the value to set.
132       * @throws ReflectionError if the given value cannot be set.
133       */
134      public void set(T value) {
135        try {
136          setAccessible(field, true);
137          field.set(target, value);
138        } catch (Exception e) {
139          throw new ReflectionError(concat("Unable to update the value in field ", quote(field.getName())), e);
140        } finally {
141          setAccessibleIgnoringExceptions(field, accessible);
142        }
143      }
144    
145      /**
146       * <b>Pre</b>-decorates a targeted object's methods.
147       * <p>
148       * Each execution of a targeted object's method will be first performed on the same method of the {@code decorator}
149       * object. The result (if any) from the invocation of the targeted object's method will be returned but you can choose
150       * to return the decorator result if you want to.
151       * <p>
152       * Be aware:
153       * <li>The type of a targeted object should be an interface for this functionality to work</li>
154       * <li>Any exception caused by an invocation of a {@code decorator} object's method will result in disrupting the
155       * default program's flow</li>
156       * <p>
157       * Example: Assuming we have the following code:
158       * 
159       * <pre>
160       * interface IUploadFileService { 
161       *   boolean upload(String file, String destination); 
162       * }
163       * 
164       * public class FileManager {
165       *     
166       *   private IUploadFileService uploadFileService;
167       *   private static final String DEFAULT_DESTINATION = "http://example.org/default/destination/";
168       * 
169       *   public void manage(String fileName) {
170       *     if( uploadFileService.upload(fileName, DEFAULT_DESTINATION) ) {
171       *       System.out.println("File "+fileName+" sent to "+DEFAULT_DESTINATION);
172       *     } else {
173       *       System.out.println("Unable to sent "+fileName+" to "+DEFAULT_DESTINATION);
174       *     } 
175       *   }
176       * }
177       * </pre>
178       * 
179       * Let's say we want to decorate the uploadFileService.upload(...) part, so that additional functionality is executed
180       * <b>before</b> the actual uploadFileService.upload(...) logic, the following code will do the job:
181       * 
182       * <pre>
183       * IUploadFileService uploadFileServiceDecorator = ...; 
184       * FileManager fileManager = new FileManager();
185       * 
186       * field("uploadFileService").ofType(IUploadFileService.class)
187       *                           .in(fileManager)
188       *                           .preDecorateWith(uploadFileServiceDecorator);
189       * </pre>
190       * However, if there is an exception when calling
191       * <code>uploadFileServiceDecorator.upload(fileName, DEFAULT_DESTINATION)</code> the default program's flow will be
192       * interrupted and the <code>uploadFileService.upload(fileName, DEFAULT_DESTINATION)</code> will not be executed.
193       * <p>
194       * @param decorator which methods be called before the same targeted object methods
195       * @return the {@link DecoratedInvoker} pre decorating the target field interface with given decorator.
196       */
197      public DecoratedInvoker<T> preDecorateWith(T decorator) {
198        T target = get();
199        DecoratorInvocationHandler<T> handler = new PreDecorator<T>(target, decorator);
200        @SuppressWarnings("unchecked")
201        T field = (T) Proxy.newProxyInstance(decorator.getClass().getClassLoader(), array(expectedType), handler);
202        set(field);
203        return DecoratedInvoker.newInvoker(target, decorator, expectedType, this, handler);
204      }
205    
206      /**
207       * <b>Post</b>-decorates a targeted object's methods.
208       * <p>
209       * After each execution of a targeted object's method, the same method of the {@code decorator}
210       * object will be called. The result (if any) from the invocation of the targeted object's method will be returned but you can choose
211       * to return the decorator result if you want to.
212       * <p>
213       * Be aware:
214       * <li>The type of a targeted object should be an interface for this functionality to work</li>
215       * <li>Any exception caused by an invocation of a {@code decorator} object's method will result in disrupting the
216       * default program's flow</li>
217       * <p>
218       * Example: Assuming we have the following code:
219       * 
220       * <pre>
221       * interface IUploadFileService { 
222       *   boolean upload(String file, String destination); 
223       * }
224       * 
225       * public class FileManager {
226       *     
227       *   private IUploadFileService uploadFileService;
228       *   private static final String DEFAULT_DESTINATION = "http://example.org/default/destination/";
229       * 
230       *   public void manage(String fileName) {
231       *     if( uploadFileService.upload(fileName, DEFAULT_DESTINATION) ) {
232       *       System.out.println("File "+fileName+" sent to "+DEFAULT_DESTINATION);
233       *     } else {
234       *       System.out.println("Unable to sent "+fileName+" to "+DEFAULT_DESTINATION);
235       *     } 
236       *   }
237       * }
238       * </pre>
239       * 
240       * Let's say we want to decorate the uploadFileService.upload(...) part, so that additional functionality is executed
241       * <b>before</b> the actual uploadFileService.upload(...) logic, the following code will do the job:
242       * 
243       * <pre>
244       * IUploadFileService uploadFileServiceDecorator = ...; 
245       * FileManager fileManager = new FileManager();
246       * 
247       * field("uploadFileService").ofType(IUploadFileService.class)
248       *                           .in(fileManager)
249       *                           .postDecorateWith(uploadFileServiceDecorator);
250       * </pre>
251       * However, if there is an exception when calling
252       * <code>uploadFileServiceDecorator.upload(fileName, DEFAULT_DESTINATION)</code> the default program's flow will be
253       * interrupted and the <code>uploadFileService.upload(fileName, DEFAULT_DESTINATION)</code> will not be executed.
254       * <p>
255       * @param decorator which methods be called after the same targeted object methods
256       * @return the {@link DecoratedInvoker} post decorating the target field interface with given decorator.
257       */
258      public DecoratedInvoker<T> postDecorateWith(T decorator) {
259        T target = get();
260        DecoratorInvocationHandler<T> handler = new PostDecorator<T>(target, decorator);
261        @SuppressWarnings("unchecked")
262        T field = (T) Proxy.newProxyInstance(decorator.getClass().getClassLoader(), array(expectedType), handler);
263        set(field);
264        return DecoratedInvoker.newInvoker(target, decorator, expectedType, this, handler);
265      }
266    
267      /**
268       * Returns the value of the field managed by this class.
269       * 
270       * @return the value of the field managed by this class.
271       * @throws ReflectionError if the value of the field cannot be retrieved.
272       */
273      @SuppressWarnings("unchecked")
274      public T get() {
275        try {
276          setAccessible(field, true);
277          return (T) field.get(target);
278        } catch (Exception e) {
279          throw new ReflectionError(concat("Unable to obtain the value in field " + quote(field.getName())), e);
280        } finally {
281          setAccessibleIgnoringExceptions(field, accessible);
282        }
283      }
284    
285      /**
286       * Returns the "real" field managed by this class.
287       * 
288       * @return the "real" field managed by this class.
289       */
290      public Field info() {
291        return field;
292      }
293    }