Clover coverage report - Code Coverage for tapestry release 4.0-beta-2
Coverage timestamp: Sat Jul 9 2005 22:02:17 EDT
file stats: LOC: 203   Methods: 5
NCLOC: 110   Classes: 1
30 day Evaluation License registered to hlship@comcast.net Your 30 day evaluation period has expired. Please visit http://www.cenqua.com to obtain a licensed version of Clover
 
 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   
 26    /**
 27    * Logic for mapping a listener method name to an actual method invocation; this may require a
 28    * little searching to find the correct version of the method, based on the number of parameters to
 29    * the method (there's a lot of flexibility in terms of what methods may be considered a listener
 30    * method).
 31    *
 32    * @author Howard M. Lewis Ship
 33    * @since 4.0
 34    */
 35    public class ListenerMethodInvokerImpl implements ListenerMethodInvoker
 36    {
 37    /**
 38    * Methods with a name appropriate for this class, sorted into descending order by number of
 39    * parameters.
 40    */
 41   
 42    private final Method[] _methods;
 43   
 44    /**
 45    * The listener method name, used in some error messages.
 46    */
 47   
 48    private final String _name;
 49   
 50  1316 public ListenerMethodInvokerImpl(String name, Method[] methods)
 51    {
 52  1316 Defense.notNull(name, "name");
 53  1316 Defense.notNull(methods, "methods");
 54   
 55  1316 _name = name;
 56  1316 _methods = methods;
 57    }
 58   
 59  51 public void invokeListenerMethod(Object target, IRequestCycle cycle)
 60    {
 61  51 Object[] listenerParameters = cycle.getListenerParameters();
 62   
 63    // method(parameters)
 64  51 if (searchAndInvoke(target, false, true, cycle, listenerParameters))
 65  3 return;
 66   
 67    // method(IRequestCycle, parameters)
 68  47 if (searchAndInvoke(target, true, true, cycle, listenerParameters))
 69  32 return;
 70   
 71    // method()
 72  13 if (searchAndInvoke(target, false, false, cycle, listenerParameters))
 73  1 return;
 74   
 75    // method(IRequestCycle)
 76  10 if (searchAndInvoke(target, true, false, cycle, listenerParameters))
 77  9 return;
 78   
 79  1 throw new ApplicationRuntimeException(ListenerMessages.noListenerMethodFound(
 80    _name,
 81    listenerParameters,
 82    target), target, null, null);
 83    }
 84   
 85  121 private boolean searchAndInvoke(Object target, boolean includeCycle, boolean includeParameters,
 86    IRequestCycle cycle, Object[] listenerParameters)
 87    {
 88  121 int listenerParameterCount = Tapestry.size(listenerParameters);
 89  121 int methodParameterCount = includeParameters ? listenerParameterCount : 0;
 90   
 91  121 if (includeCycle)
 92  57 methodParameterCount++;
 93   
 94  121 for (int i = 0; i < _methods.length; i++)
 95    {
 96  122 Method m = _methods[i];
 97   
 98    // Since the methods are sorted, descending, by parameter count,
 99    // there's no point in searching past that point.
 100   
 101  122 Class[] parameterTypes = m.getParameterTypes();
 102   
 103  122 if (parameterTypes.length < methodParameterCount)
 104  20 break;
 105   
 106  102 if (parameterTypes.length != methodParameterCount)
 107  47 continue;
 108   
 109  55 boolean firstIsCycle = parameterTypes.length > 0
 110    && parameterTypes[0] == IRequestCycle.class;
 111   
 112    // When we're searching for a "traditional" style listener method,
 113    // one which takes the request cycle as its first parameter,
 114    // then check that first parameter is *exactly* IRequestCycle
 115    // On the other hand, if we're looking for new style
 116    // listener methods (includeCycle is false), then ignore
 117    // any methods whose first parameter is the request cycle
 118    // (we'll catch those in a later search).
 119   
 120  55 if (includeCycle != firstIsCycle)
 121  5 continue;
 122   
 123  50 invokeListenerMethod(
 124    m,
 125    target,
 126    includeCycle,
 127    includeParameters,
 128    cycle,
 129    listenerParameters);
 130   
 131  45 return true;
 132    }
 133   
 134  71 return false;
 135    }
 136   
 137  50 private void invokeListenerMethod(Method listenerMethod, Object target, boolean includeCycle,
 138    boolean includeParameters, IRequestCycle cycle, Object[] listenerParameters)
 139    {
 140  50 Object[] parameters = new Object[listenerMethod.getParameterTypes().length];
 141  50 int cursor = 0;
 142   
 143  50 if (includeCycle)
 144  43 parameters[cursor++] = cycle;
 145   
 146  50 if (includeParameters)
 147  38 for (int i = 0; i < Tapestry.size(listenerParameters); i++)
 148  26 parameters[cursor++] = listenerParameters[i];
 149   
 150  50 try
 151    {
 152  50 Object methodResult = invokeTargetMethod(target, listenerMethod, parameters);
 153   
 154    // void methods return null
 155   
 156  45 if (methodResult == null)
 157  43 return;
 158   
 159    // The method scanner, inside ListenerMapSourceImpl,
 160    // ensures that only methods that return void, String,
 161    // or assignable to IPage are considered.
 162   
 163  2 if (methodResult instanceof String)
 164    {
 165  1 cycle.activate((String) methodResult);
 166  1 return;
 167    }
 168   
 169  1 cycle.activate((IPage) methodResult);
 170    }
 171    catch (InvocationTargetException ex)
 172    {
 173  4 Throwable targetException = ex.getTargetException();
 174   
 175  4 if (targetException instanceof ApplicationRuntimeException)
 176  3 throw (ApplicationRuntimeException) targetException;
 177   
 178  1 throw new ApplicationRuntimeException(ListenerMessages.listenerMethodFailure(
 179    listenerMethod,
 180    target,
 181    targetException), target, null, targetException);
 182    }
 183    catch (Exception ex)
 184    {
 185  1 throw new ApplicationRuntimeException(ListenerMessages.listenerMethodFailure(
 186    listenerMethod,
 187    target,
 188    ex), target, null, ex);
 189   
 190    }
 191    }
 192   
 193    /**
 194    * Provided as a hook so that subclasses can perform any additional work before or after
 195    * invoking the listener method.
 196    */
 197   
 198  50 protected Object invokeTargetMethod(Object target, Method listenerMethod, Object[] parameters)
 199    throws IllegalAccessException, InvocationTargetException
 200    {
 201  50 return listenerMethod.invoke(target, parameters);
 202    }
 203    }