Coverage Report - org.apache.tapestry.listener.ListenerMethodInvokerImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
ListenerMethodInvokerImpl
0%
0/95
0%
0/80
7.857
 
 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 org.apache.hivemind.ApplicationRuntimeException;
 18  
 import org.apache.hivemind.util.Defense;
 19  
 import org.apache.tapestry.IPage;
 20  
 import org.apache.tapestry.IRequestCycle;
 21  
 import org.apache.tapestry.engine.ILink;
 22  
 import org.apache.tapestry.event.BrowserEvent;
 23  
 
 24  
 import java.lang.reflect.InvocationTargetException;
 25  
 import java.lang.reflect.Method;
 26  
 import java.util.ArrayList;
 27  
 import java.util.List;
 28  
 
 29  
 /**
 30  
  * Logic for mapping a listener method name to an actual method invocation; this
 31  
  * may require a little searching to find the correct version of the method,
 32  
  * based on the number of parameters to the method (there's a lot of flexibility
 33  
  * in terms of what methods may be considered a listener method).
 34  
  * 
 35  
  * @author Howard M. Lewis Ship
 36  
  * @since 4.0
 37  
  */
 38  
 public class ListenerMethodInvokerImpl implements ListenerMethodInvoker
 39  
 {
 40  
 
 41  
     /**
 42  
      * Used as default byte value in null method parameters for native types
 43  
      */
 44  
     private static final byte DEFAULT_BYTE = -1;
 45  
 
 46  
     /**
 47  
      * Used as default short value in null method parameters for native types
 48  
      */
 49  
     private static final short DEFAULT_SHORT = -1;
 50  
     
 51  
     /**
 52  
      * Methods with a name appropriate for this class, sorted into descending
 53  
      * order by number of parameters.
 54  
      */
 55  
 
 56  
     private final Method[] _methods;
 57  
 
 58  
     /**
 59  
      * The listener method name, used in some error messages.
 60  
      */
 61  
 
 62  
     private final String _name;
 63  
 
 64  
     public ListenerMethodInvokerImpl(String name, Method[] methods)
 65  0
     {
 66  0
         Defense.notNull(name, "name");
 67  0
         Defense.notNull(methods, "methods");
 68  
 
 69  0
         _name = name;
 70  0
         _methods = methods;
 71  0
     }
 72  
 
 73  
     public void invokeListenerMethod(Object target, IRequestCycle cycle)
 74  
     {
 75  0
         Object[] listenerParameters = cycle.getListenerParameters();
 76  
         
 77  0
         if (listenerParameters == null)
 78  0
             listenerParameters = new Object[0];
 79  
         
 80  0
         if (searchAndInvoke(target, cycle, listenerParameters))
 81  0
             return;
 82  
         
 83  0
         throw new ApplicationRuntimeException(ListenerMessages.noListenerMethodFound(_name, listenerParameters, target),
 84  
                 target, null, null);
 85  
     }
 86  
     
 87  
     private boolean searchAndInvoke(Object target, IRequestCycle cycle, Object[] listenerParameters)
 88  
     {
 89  0
         BrowserEvent event = null;
 90  0
         if (listenerParameters.length > 0 
 91  0
                 && BrowserEvent.class.isInstance(listenerParameters[listenerParameters.length - 1]))
 92  0
             event = (BrowserEvent)listenerParameters[listenerParameters.length - 1];
 93  
         
 94  0
         List invokeParms = new ArrayList();
 95  
 
 96  0
         Method possibleMethod = null;
 97  
 
 98  
         methods:
 99  0
             for (int i = 0; i < _methods.length; i++, invokeParms.clear()) {
 100  
                 
 101  0
                 if (!_methods[i].getName().equals(_name))
 102  0
                    continue;
 103  
                 
 104  0
                 Class[] parms = _methods[i].getParameterTypes();
 105  
                 
 106  
                 // impossible to call this
 107  
                 
 108  0
                 if (parms.length > (listenerParameters.length + 1) ) {
 109  
                     
 110  0
                     if (possibleMethod == null)
 111  0
                         possibleMethod = _methods[i];
 112  0
                     else if (parms.length < possibleMethod.getParameterTypes().length)
 113  0
                         possibleMethod = _methods[i];
 114  
                     
 115  
                     continue;
 116  
                 }
 117  
                 
 118  0
                 int listenerIndex = 0;
 119  0
                 for (int p = 0; p < parms.length && listenerIndex < (listenerParameters.length + 1); p++) {
 120  
                     
 121  
                     // special case for BrowserEvent
 122  0
                     if (BrowserEvent.class.isAssignableFrom(parms[p])) {
 123  0
                         if (event == null)
 124  0
                             continue methods;
 125  
                         
 126  0
                         if (!invokeParms.contains(event))
 127  0
                             invokeParms.add(event);
 128  
                         
 129  
                         continue;
 130  
                     }
 131  
                     
 132  
                     // special case for request cycle
 133  0
                     if (IRequestCycle.class.isAssignableFrom(parms[p])) {
 134  0
                         invokeParms.add(cycle);
 135  0
                         continue;
 136  
                     }
 137  
                     
 138  0
                     if (event != null && listenerIndex < (listenerParameters.length + 1)
 139  
                             || listenerIndex < listenerParameters.length) {
 140  0
                         invokeParms.add(listenerParameters[listenerIndex]);
 141  0
                         listenerIndex++;
 142  
                     }
 143  
                 }
 144  
                 
 145  0
                 if (invokeParms.size() != parms.length) {
 146  
 
 147  
                     // set possible method just in case
 148  
                     
 149  0
                     if (possibleMethod == null)
 150  0
                         possibleMethod = _methods[i];
 151  0
                     else if (parms.length < possibleMethod.getParameterTypes().length)
 152  0
                         possibleMethod = _methods[i];
 153  
 
 154  
                     continue;
 155  
                 }
 156  
                 
 157  0
                 invokeListenerMethod(_methods[i], target, cycle, invokeParms.toArray(new Object[invokeParms.size()]));
 158  
                 
 159  0
                 return true;
 160  
             }
 161  
 
 162  
         // if we didn't have enough parameters but still found a matching method name go ahead
 163  
         // and do your best to fill in the parameters and invoke it
 164  
 
 165  0
         if (possibleMethod != null) {
 166  
 
 167  0
             Class[] parms = possibleMethod.getParameterTypes();
 168  0
             Object[] args = new Object[parms.length];
 169  
             
 170  0
             for (int p=0; p < parms.length; p++) {
 171  
 
 172  
                 // setup primitive defaults
 173  
                 
 174  0
                 if (parms[p].isPrimitive()) {
 175  
 
 176  0
                     if (parms[p] == Boolean.TYPE) {
 177  
 
 178  0
                         args[p] = Boolean.FALSE;
 179  0
                     } else if (parms[p] == Byte.TYPE) {
 180  
 
 181  0
                         args[p] = new Byte(DEFAULT_BYTE);
 182  0
                     } else if (parms[p] == Short.TYPE) {
 183  
 
 184  0
                         args[p] = new Short(DEFAULT_SHORT);
 185  0
                     } else if (parms[p] == Integer.TYPE) {
 186  
 
 187  0
                         args[p] = new Integer(-1);
 188  0
                     } else if (parms[p] == Long.TYPE) {
 189  
 
 190  0
                         args[p] = new Long(-1);
 191  0
                     } else if (parms[p] == Float.TYPE) {
 192  
 
 193  0
                         args[p] = new Float(-1);
 194  0
                     } else if (parms[p] == Double.TYPE) {
 195  
 
 196  0
                         args[p] = new Double(-1);
 197  
                     }
 198  
                 }
 199  
 
 200  0
                 if (IRequestCycle.class.isAssignableFrom(parms[p])) {
 201  0
                     args[p] = cycle;
 202  
                 }
 203  
             }
 204  
             
 205  0
             invokeListenerMethod(possibleMethod, target, cycle, args);
 206  
             
 207  0
             return true;
 208  
         }
 209  
 
 210  0
         return false;
 211  
     }
 212  
 
 213  
     private void invokeListenerMethod(Method listenerMethod, Object target,
 214  
             IRequestCycle cycle, Object[] parameters)
 215  
     {
 216  
         
 217  0
         Object methodResult = null;
 218  
         
 219  
         try
 220  
         {
 221  0
             methodResult = invokeTargetMethod(target, listenerMethod, parameters);
 222  
         }
 223  0
         catch (InvocationTargetException ex)
 224  
         {
 225  0
             Throwable targetException = ex.getTargetException();
 226  
             
 227  0
             if (targetException instanceof ApplicationRuntimeException)
 228  0
                 throw (ApplicationRuntimeException) targetException;
 229  
 
 230  0
             throw new ApplicationRuntimeException(ListenerMessages.listenerMethodFailure(listenerMethod, target,
 231  
                             targetException), target, null, targetException);
 232  
         }
 233  0
         catch (Exception ex)
 234  
         {
 235  0
             throw new ApplicationRuntimeException(ListenerMessages.listenerMethodFailure(listenerMethod, target, ex), target,
 236  
                     null, ex);
 237  
 
 238  0
         }
 239  
         
 240  
         // void methods return null
 241  
         
 242  0
         if (methodResult == null) return;
 243  
         
 244  
         // The method scanner, inside ListenerMapSourceImpl,
 245  
         // ensures that only methods that return void, String,
 246  
         // or assignable to ILink or IPage are considered.
 247  
 
 248  0
         if (methodResult instanceof String)
 249  
         {
 250  0
             cycle.activate((String) methodResult);
 251  0
             return;
 252  
         }
 253  
         
 254  0
         if (methodResult instanceof ILink)
 255  
         {
 256  0
             ILink link = (ILink) methodResult;
 257  
 
 258  0
             String url = link.getAbsoluteURL();
 259  
 
 260  0
             cycle.sendRedirect(url);
 261  0
             return;
 262  
         }
 263  
 
 264  0
         cycle.activate((IPage) methodResult);
 265  0
     }
 266  
     
 267  
     /**
 268  
      * Provided as a hook so that subclasses can perform any additional work
 269  
      * before or after invoking the listener method.
 270  
      */
 271  
 
 272  
     protected Object invokeTargetMethod(Object target, Method listenerMethod,
 273  
             Object[] parameters)
 274  
         throws IllegalAccessException, InvocationTargetException
 275  
     {
 276  0
         return listenerMethod.invoke(target, parameters);
 277  
     }
 278  
 
 279  
 
 280  
     public String getMethodName()
 281  
     {
 282  0
         return _name;
 283  
     }
 284  
 
 285  
     public String toString()
 286  
     {
 287  0
         return "ListenerMethodInvokerImpl[" +
 288  
                "_name='" + _name + '\'' +
 289  
                ']';
 290  
     }
 291  
 }