001    /*
002     * Created on Mar 18, 2012
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.Proxy;
018    
019    import org.fest.reflect.field.decorator.DecoratorInvocationHandler;
020    import org.fest.reflect.field.decorator.RuntimeExceptionShield;
021    
022    /**
023     * A decorated invoker allowing to ignore some exceptions or returning decorator result instead of field result.
024     * @author Ivan Hristov
025     */
026    public final class DecoratedInvoker<T> {
027    
028      private final T target;
029      private final T decorator;
030      private final Invoker<T> invoker;
031      private final Class<?> expectedType;
032      private final DecoratorInvocationHandler<T> decoratorInvocationHandler;
033    
034      static <T> DecoratedInvoker<T> newInvoker(T target, T decorator, Class<?> expectedType, Invoker<T> invoker,
035          DecoratorInvocationHandler<T> decoratorInvocationHandler) {
036        return new DecoratedInvoker<T>(target, decorator, expectedType, invoker, decoratorInvocationHandler);
037      }
038    
039      private DecoratedInvoker(T target, T decorator, Class<?> expectedType, Invoker<T> invoker,
040          DecoratorInvocationHandler<T> decoratorInvocationHandler) {
041        this.target = target;
042        this.decorator = decorator;
043        this.invoker = invoker;
044        this.expectedType = expectedType;
045        this.decoratorInvocationHandler = decoratorInvocationHandler;
046      }
047    
048      /**
049       * Ignores any {@link RuntimeException} which comes from the preceding decorator.
050       * @return the DecoratedResultInvoker ignoring exceptions.
051       */
052      public DecoratedInvoker<T> ignoringDecoratorExceptions() {
053        return ignoringDecoratorExceptionsOfType(RuntimeException.class);
054      }
055    
056      /**
057       * Ignores any exception of the {@code exceptionClass} type which comes from the preceding decorator.
058       * @param exceptionClass the exception to ignore - usually a checked exception of decorator method
059       * @return the DecoratedResultInvoker ignoring given exception type.
060       */
061      public DecoratedInvoker<T> ignoringDecoratorExceptionsOfType(Class<?> exceptionClass) {
062        RuntimeExceptionShield runtimeExceptionShield = new RuntimeExceptionShield(decorator, exceptionClass);
063        @SuppressWarnings("unchecked")
064        T exceptionSafeDecorator = (T) Proxy.newProxyInstance(decorator.getClass().getClassLoader(),//
065            new Class[] { expectedType }, runtimeExceptionShield);
066    
067        decoratorInvocationHandler.setDecorator(exceptionSafeDecorator);
068    
069        return newInvoker(target, exceptionSafeDecorator, expectedType, invoker, decoratorInvocationHandler);
070      }
071    
072      /**
073       * Specifies that the result from the decorator should be returned.
074       * <p>
075       * If {@link IgnoringDecoratedExceptionInvoker#ignoringDecoratorExceptions() ignoringDecoratorExceptions()} is used in
076       * combination with this method and an exception is thrown, the default value will be returned (as defined by JLS) for
077       * all primitives or null for all non-primitive.
078       * <p>
079       * Example :<br>
080       * If a {@link RuntimeException} is thrown while executing one of the decorated <code>IExampleService</code> field
081       * methods which returns primitive boolean value, the default value <i>false</i> will be returned.
082       * 
083       * <pre>
084       * field("fieldName").ofType(IExampleService.class).in(target)
085       *                   .postDecorateWith(postDecoratorService)
086       *                   .returningDecoratorResult()
087       *                   .ignoringDecoratorExceptions();
088       * </pre>
089       * In case of several decorators attached to a field, the result from the latest will be returned.
090       * <p>
091       * Example 1:<br>
092       * The result from the <b>pre</b>DecoratorService will be returned
093       * 
094       * <pre>
095       * field("fieldName").ofType(IExampleService.class).in(target)
096       *                   .preDecorateWith(preDecoratorService)
097       *                   .returningDecoratorResult();
098       * </pre>
099       * Example 2:<br>
100       * The result from the <b>post</b>DecoratorService will be returned
101       * 
102       * <pre>
103       * field("fieldName").ofType(IExampleService.class).in(target)
104       *                   .postDecorateWith(postDecoratorService)
105       *                   .returningDecoratorResult();
106       * </pre>
107       * Example 3:<br>
108       * The result from the <b>pre</b>DecoratorService will be returned, since it's the <b>latest</b> attached decorator.
109       * 
110       * <pre>
111       * field("fieldName").ofType(IExampleService.class).in(target)
112       *                   .postDecorateWith(postDecoratorService)
113       *                   .returningDecoratorResult()
114       *                   .<b>pre</b>DecorateWith(preDecoratorService)
115       *                   .returningDecoratorResult();
116       * </pre>
117       */
118      public DecoratedInvoker<T> returningDecoratorResult() {
119        decoratorInvocationHandler.setReturnDecoratorResult(true);
120        return newInvoker(target, decorator, expectedType, invoker, decoratorInvocationHandler);
121      }
122    
123      /**
124       * Adds a pre-decorator to an already decorated field.
125       * <p>
126       * Note that if there are more than one pre-decorators assigned to a field they will be executed starting from the
127       * last attached decorator.
128       * @param decorator which methods be called before the same targeted object methods
129       * @return the {@link DecoratedInvoker} pre decorating the target field interface with given decorator.
130       */
131      public DecoratedInvoker<T> preDecorateWith(T decorator) {
132        return invoker.preDecorateWith(decorator);
133      }
134    
135      /**
136       * Adds a post-decorator to an already decorated field
137       * <p>
138       * Note that if there are more than one post-decorators assigned to a field they will be executed starting from the
139       * first attached decorator.
140       * @param decorator which methods be called after the same targeted object methods
141       * @return the {@link DecoratedInvoker} post decorating the target field interface with given decorator.
142       */
143      public DecoratedInvoker<T> postDecorateWith(T decorator) {
144        return invoker.postDecorateWith(decorator);
145      }
146    }