Clover coverage report - Code Coverage for tapestry release 4.0.1
Coverage timestamp: Fri Mar 31 2006 09:12:14 EST
file stats: LOC: 216   Methods: 5
NCLOC: 119   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
ListenerMethodInvokerImpl.java 100% 100% 100% 100%
coverage
 1    // Copyright 2005 The Apache Software Foundation
 2    //
 3    // Licensed under the Apache License, Version 2.0 (the "License");
 4    // you may not use this file except in compliance with the License.
 5    // You may obtain a copy of the License at
 6    //
 7    // http://www.apache.org/licenses/LICENSE-2.0
 8    //
 9    // Unless required by applicable law or agreed to in writing, software
 10    // distributed under the License is distributed on an "AS IS" BASIS,
 11    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12    // See the License for the specific language governing permissions and
 13    // limitations under the License.
 14   
 15    package org.apache.tapestry.listener;
 16   
 17    import java.lang.reflect.InvocationTargetException;
 18    import java.lang.reflect.Method;
 19   
 20    import org.apache.hivemind.ApplicationRuntimeException;
 21    import org.apache.hivemind.util.Defense;
 22    import org.apache.tapestry.IPage;
 23    import org.apache.tapestry.IRequestCycle;
 24    import org.apache.tapestry.Tapestry;
 25    import org.apache.tapestry.engine.ILink;
 26   
 27    /**
 28    * Logic for mapping a listener method name to an actual method invocation; this may require a
 29    * little searching to find the correct version of the method, based on the number of parameters to
 30    * the method (there's a lot of flexibility in terms of what methods may be considered a listener
 31    * method).
 32    *
 33    * @author Howard M. Lewis Ship
 34    * @since 4.0
 35    */
 36    public class ListenerMethodInvokerImpl implements ListenerMethodInvoker
 37    {
 38    /**
 39    * Methods with a name appropriate for this class, sorted into descending order by number of
 40    * parameters.
 41    */
 42   
 43    private final Method[] _methods;
 44   
 45    /**
 46    * The listener method name, used in some error messages.
 47    */
 48   
 49    private final String _name;
 50   
 51  2772 public ListenerMethodInvokerImpl(String name, Method[] methods)
 52    {
 53  2772 Defense.notNull(name, "name");
 54  2772 Defense.notNull(methods, "methods");
 55   
 56  2772 _name = name;
 57  2772 _methods = methods;
 58    }
 59   
 60  104 public void invokeListenerMethod(Object target, IRequestCycle cycle)
 61    {
 62  104 Object[] listenerParameters = cycle.getListenerParameters();
 63   
 64    // method(parameters)
 65  104 if (searchAndInvoke(target, false, true, cycle, listenerParameters))
 66  8 return;
 67   
 68    // method(IRequestCycle, parameters)
 69  94 if (searchAndInvoke(target, true, true, cycle, listenerParameters))
 70  72 return;
 71   
 72    // method()
 73  18 if (searchAndInvoke(target, false, false, cycle, listenerParameters))
 74  2 return;
 75   
 76    // method(IRequestCycle)
 77  12 if (searchAndInvoke(target, true, false, cycle, listenerParameters))
 78  10 return;
 79   
 80  2 throw new ApplicationRuntimeException(ListenerMessages.noListenerMethodFound(
 81    _name,
 82    listenerParameters,
 83    target), target, null, null);
 84    }
 85   
 86  228 private boolean searchAndInvoke(Object target, boolean includeCycle, boolean includeParameters,
 87    IRequestCycle cycle, Object[] listenerParameters)
 88    {
 89  228 int listenerParameterCount = Tapestry.size(listenerParameters);
 90  228 int methodParameterCount = includeParameters ? listenerParameterCount : 0;
 91   
 92  228 if (includeCycle)
 93  106 methodParameterCount++;
 94   
 95  228 for (int i = 0; i < _methods.length; i++)
 96    {
 97  230 Method m = _methods[i];
 98   
 99    // Since the methods are sorted, descending, by parameter count,
 100    // there's no point in searching past that point.
 101   
 102  230 Class[] parameterTypes = m.getParameterTypes();
 103   
 104  230 if (parameterTypes.length < methodParameterCount)
 105  32 break;
 106   
 107  198 if (parameterTypes.length != methodParameterCount)
 108  94 continue;
 109   
 110  104 boolean firstIsCycle = parameterTypes.length > 0
 111    && parameterTypes[0] == IRequestCycle.class;
 112   
 113    // When we're searching for a "traditional" style listener method,
 114    // one which takes the request cycle as its first parameter,
 115    // then check that first parameter is *exactly* IRequestCycle
 116    // On the other hand, if we're looking for new style
 117    // listener methods (includeCycle is false), then ignore
 118    // any methods whose first parameter is the request cycle
 119    // (we'll catch those in a later search).
 120   
 121  104 if (includeCycle != firstIsCycle)
 122  2 continue;
 123   
 124  102 invokeListenerMethod(
 125    m,
 126    target,
 127    includeCycle,
 128    includeParameters,
 129    cycle,
 130    listenerParameters);
 131   
 132  92 return true;
 133    }
 134   
 135  126 return false;
 136    }
 137   
 138  102 private void invokeListenerMethod(Method listenerMethod, Object target, boolean includeCycle,
 139    boolean includeParameters, IRequestCycle cycle, Object[] listenerParameters)
 140    {
 141  102 Object[] parameters = new Object[listenerMethod.getParameterTypes().length];
 142  102 int cursor = 0;
 143   
 144  102 if (includeCycle)
 145  86 parameters[cursor++] = cycle;
 146   
 147  102 if (includeParameters)
 148  86 for (int i = 0; i < Tapestry.size(listenerParameters); i++)
 149  52 parameters[cursor++] = listenerParameters[i];
 150   
 151  102 Object methodResult = null;
 152   
 153  102 try
 154    {
 155  102 methodResult = invokeTargetMethod(target, listenerMethod, parameters);
 156    }
 157    catch (InvocationTargetException ex)
 158    {
 159  8 Throwable targetException = ex.getTargetException();
 160   
 161  8 if (targetException instanceof ApplicationRuntimeException)
 162  6 throw (ApplicationRuntimeException) targetException;
 163   
 164  2 throw new ApplicationRuntimeException(ListenerMessages.listenerMethodFailure(
 165    listenerMethod,
 166    target,
 167    targetException), target, null, targetException);
 168    }
 169    catch (Exception ex)
 170    {
 171  2 throw new ApplicationRuntimeException(ListenerMessages.listenerMethodFailure(
 172    listenerMethod,
 173    target,
 174    ex), target, null, ex);
 175   
 176    }
 177   
 178    // void methods return null
 179   
 180  92 if (methodResult == null)
 181  86 return;
 182   
 183    // The method scanner, inside ListenerMapSourceImpl,
 184    // ensures that only methods that return void, String,
 185    // or assignable to ILink or IPage are considered.
 186   
 187  6 if (methodResult instanceof String)
 188    {
 189  2 cycle.activate((String) methodResult);
 190  2 return;
 191    }
 192   
 193  4 if (methodResult instanceof ILink)
 194    {
 195  2 ILink link = (ILink) methodResult;
 196   
 197  2 String url = link.getAbsoluteURL();
 198   
 199  2 cycle.sendRedirect(url);
 200  2 return;
 201    }
 202   
 203  2 cycle.activate((IPage) methodResult);
 204    }
 205   
 206    /**
 207    * Provided as a hook so that subclasses can perform any additional work before or after
 208    * invoking the listener method.
 209    */
 210   
 211  102 protected Object invokeTargetMethod(Object target, Method listenerMethod, Object[] parameters)
 212    throws IllegalAccessException, InvocationTargetException
 213    {
 214  102 return listenerMethod.invoke(target, parameters);
 215    }
 216    }