Clover coverage report - Code Coverage for tapestry release 4.0-beta-9
Coverage timestamp: Sat Oct 1 2005 08:36:20 EDT
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  1365 public ListenerMethodInvokerImpl(String name, Method[] methods)
 52    {
 53  1365 Defense.notNull(name, "name");
 54  1365 Defense.notNull(methods, "methods");
 55   
 56  1365 _name = name;
 57  1365 _methods = methods;
 58    }
 59   
 60  52 public void invokeListenerMethod(Object target, IRequestCycle cycle)
 61    {
 62  52 Object[] listenerParameters = cycle.getListenerParameters();
 63   
 64    // method(parameters)
 65  52 if (searchAndInvoke(target, false, true, cycle, listenerParameters))
 66  4 return;
 67   
 68    // method(IRequestCycle, parameters)
 69  47 if (searchAndInvoke(target, true, true, cycle, listenerParameters))
 70  36 return;
 71   
 72    // method()
 73  9 if (searchAndInvoke(target, false, false, cycle, listenerParameters))
 74  1 return;
 75   
 76    // method(IRequestCycle)
 77  6 if (searchAndInvoke(target, true, false, cycle, listenerParameters))
 78  5 return;
 79   
 80  1 throw new ApplicationRuntimeException(ListenerMessages.noListenerMethodFound(
 81    _name,
 82    listenerParameters,
 83    target), target, null, null);
 84    }
 85   
 86  114 private boolean searchAndInvoke(Object target, boolean includeCycle, boolean includeParameters,
 87    IRequestCycle cycle, Object[] listenerParameters)
 88    {
 89  114 int listenerParameterCount = Tapestry.size(listenerParameters);
 90  114 int methodParameterCount = includeParameters ? listenerParameterCount : 0;
 91   
 92  114 if (includeCycle)
 93  53 methodParameterCount++;
 94   
 95  114 for (int i = 0; i < _methods.length; i++)
 96    {
 97  115 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  115 Class[] parameterTypes = m.getParameterTypes();
 103   
 104  115 if (parameterTypes.length < methodParameterCount)
 105  16 break;
 106   
 107  99 if (parameterTypes.length != methodParameterCount)
 108  47 continue;
 109   
 110  52 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  52 if (includeCycle != firstIsCycle)
 122  1 continue;
 123   
 124  51 invokeListenerMethod(
 125    m,
 126    target,
 127    includeCycle,
 128    includeParameters,
 129    cycle,
 130    listenerParameters);
 131   
 132  46 return true;
 133    }
 134   
 135  63 return false;
 136    }
 137   
 138  51 private void invokeListenerMethod(Method listenerMethod, Object target, boolean includeCycle,
 139    boolean includeParameters, IRequestCycle cycle, Object[] listenerParameters)
 140    {
 141  51 Object[] parameters = new Object[listenerMethod.getParameterTypes().length];
 142  51 int cursor = 0;
 143   
 144  51 if (includeCycle)
 145  43 parameters[cursor++] = cycle;
 146   
 147  51 if (includeParameters)
 148  43 for (int i = 0; i < Tapestry.size(listenerParameters); i++)
 149  26 parameters[cursor++] = listenerParameters[i];
 150   
 151  51 Object methodResult = null;
 152   
 153  51 try
 154    {
 155  51 methodResult = invokeTargetMethod(target, listenerMethod, parameters);
 156    }
 157    catch (InvocationTargetException ex)
 158    {
 159  4 Throwable targetException = ex.getTargetException();
 160   
 161  4 if (targetException instanceof ApplicationRuntimeException)
 162  3 throw (ApplicationRuntimeException) targetException;
 163   
 164  1 throw new ApplicationRuntimeException(ListenerMessages.listenerMethodFailure(
 165    listenerMethod,
 166    target,
 167    targetException), target, null, targetException);
 168    }
 169    catch (Exception ex)
 170    {
 171  1 throw new ApplicationRuntimeException(ListenerMessages.listenerMethodFailure(
 172    listenerMethod,
 173    target,
 174    ex), target, null, ex);
 175   
 176    }
 177   
 178    // void methods return null
 179   
 180  46 if (methodResult == null)
 181  43 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  3 if (methodResult instanceof String)
 188    {
 189  1 cycle.activate((String) methodResult);
 190  1 return;
 191    }
 192   
 193  2 if (methodResult instanceof ILink)
 194    {
 195  1 ILink link = (ILink) methodResult;
 196   
 197  1 String url = link.getAbsoluteURL();
 198   
 199  1 cycle.sendRedirect(url);
 200  1 return;
 201    }
 202   
 203  1 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  51 protected Object invokeTargetMethod(Object target, Method listenerMethod, Object[] parameters)
 212    throws IllegalAccessException, InvocationTargetException
 213    {
 214  51 return listenerMethod.invoke(target, parameters);
 215    }
 216    }