Clover coverage report - Code Coverage for tapestry release 3.1-alpha-1
Coverage timestamp: Mon Feb 21 2005 09:16:14 EST
file stats: LOC: 262   Methods: 9
NCLOC: 131   Classes: 1
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover
 
 Source file Conditionals Statements Methods TOTAL
ListenerMap.java 83.3% 91.4% 100% 89.7%
coverage coverage
 1   
 // Copyright 2004, 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   
 import java.lang.reflect.Modifier;
 20   
 import java.util.Collection;
 21   
 import java.util.Collections;
 22   
 import java.util.HashMap;
 23   
 import java.util.Map;
 24   
 
 25   
 import org.apache.commons.logging.Log;
 26   
 import org.apache.commons.logging.LogFactory;
 27   
 import org.apache.hivemind.ApplicationRuntimeException;
 28   
 import org.apache.tapestry.IActionListener;
 29   
 import org.apache.tapestry.IRequestCycle;
 30   
 
 31   
 /**
 32   
  * Maps a class to a set of listeners based on the public methods of the class.
 33   
  * {@link org.apache.tapestry.listener.ListenerMapPropertyAccessor}is setup to provide these
 34   
  * methods as named properties of the ListenerMap.
 35   
  * 
 36   
  * @author Howard Ship
 37   
  * @since 1.0.2
 38   
  */
 39   
 
 40   
 public class ListenerMap
 41   
 {
 42   
     private static final Log LOG = LogFactory.getLog(ListenerMap.class);
 43   
 
 44   
     private Object _target;
 45   
 
 46   
     /**
 47   
      * A {@link Map}of relevant {@link Method}s, keyed on method name. This is just the public
 48   
      * void methods that take an {@link IRequestCycle}and throw nothing or just
 49   
      * {@link ApplicationRuntimeException}.
 50   
      */
 51   
 
 52   
     private Map _methodMap;
 53   
 
 54   
     /**
 55   
      * A {@link Map}of cached listener instances, keyed on method name
 56   
      */
 57   
 
 58   
     private Map _listenerCache = new HashMap();
 59   
 
 60   
     /**
 61   
      * A {@link Map}, keyed on Class, of {@link Map}... the method map for any particular instance
 62   
      * of the given class.
 63   
      */
 64   
 
 65   
     private static Map _classMap = new HashMap();
 66   
 
 67  39
     public ListenerMap(Object target)
 68   
     {
 69  39
         _target = target;
 70   
     }
 71   
 
 72   
     /**
 73   
      * Gets a listener for the given name (which is both a property name and a method name). The
 74   
      * listener is created as needed, but is also cached for later use.
 75   
      * 
 76   
      * @throws ApplicationRuntimeException
 77   
      *             if the listener can not be created.
 78   
      */
 79   
 
 80  52
     public synchronized Object getListener(String name)
 81   
     {
 82  52
         Object listener = null;
 83   
 
 84  52
         listener = _listenerCache.get(name);
 85   
 
 86  52
         if (listener == null)
 87   
         {
 88  48
             listener = createListener(name);
 89   
 
 90  48
             _listenerCache.put(name, listener);
 91   
         }
 92   
 
 93  52
         return listener;
 94   
     }
 95   
 
 96   
     /**
 97   
      * Returns an object that implements {@link IActionListener}. This involves looking up the
 98   
      * method by name and determining which inner class to create.
 99   
      */
 100   
 
 101  48
     private synchronized Object createListener(String name)
 102   
     {
 103  48
         if (_methodMap == null)
 104  32
             getMethodMap();
 105   
 
 106  48
         Method method = (Method) _methodMap.get(name);
 107   
 
 108  48
         if (method == null)
 109  0
             throw new ApplicationRuntimeException(ListenerMessages.objectMissingMethod(
 110   
                     _target,
 111   
                     name));
 112   
 
 113  48
         return new SyntheticListener(_target, method);
 114   
     }
 115   
 
 116   
     /**
 117   
      * Gets the method map for the current instance. If necessary, it is constructed and cached (for
 118   
      * other instances of the same class).
 119   
      */
 120   
 
 121  41
     private synchronized Map getMethodMap()
 122   
     {
 123  41
         if (_methodMap != null)
 124  3
             return _methodMap;
 125   
 
 126  38
         Class beanClass = _target.getClass();
 127   
 
 128  38
         synchronized (_classMap)
 129   
         {
 130  38
             _methodMap = (Map) _classMap.get(beanClass);
 131   
 
 132  38
             if (_methodMap == null)
 133   
             {
 134  29
                 _methodMap = buildMethodMap(beanClass);
 135   
 
 136  29
                 _classMap.put(beanClass, _methodMap);
 137   
             }
 138   
         }
 139   
 
 140  38
         return _methodMap;
 141   
     }
 142   
 
 143  29
     private static Map buildMethodMap(Class beanClass)
 144   
     {
 145  29
         if (LOG.isDebugEnabled())
 146  0
             LOG.debug("Building method map for class " + beanClass.getName());
 147   
 
 148  29
         Map result = new HashMap();
 149  29
         Method[] methods = beanClass.getMethods();
 150   
 
 151  29
         for (int i = 0; i < methods.length; i++)
 152   
         {
 153  2292
             Method m = methods[i];
 154  2292
             int mods = m.getModifiers();
 155   
 
 156  2292
             if (Modifier.isStatic(mods))
 157  2
                 continue;
 158   
 
 159   
             // Probably not necessary, getMethods() returns only public
 160   
             // methods.
 161   
 
 162  2290
             if (!Modifier.isPublic(mods))
 163  0
                 continue;
 164   
 
 165   
             // Must return void
 166   
 
 167  2290
             if (m.getReturnType() != Void.TYPE)
 168  1027
                 continue;
 169   
 
 170  1263
             Class[] parmTypes = m.getParameterTypes();
 171   
 
 172  1263
             if (parmTypes.length != 1)
 173  463
                 continue;
 174   
 
 175   
             // parm must be IRequestCycle
 176   
 
 177  800
             if (!parmTypes[0].equals(IRequestCycle.class))
 178  727
                 continue;
 179   
 
 180   
             // Ha! Passed all tests.
 181   
 
 182  73
             result.put(m.getName(), m);
 183   
         }
 184   
 
 185  29
         return result;
 186   
 
 187   
     }
 188   
 
 189   
     /**
 190   
      * Invoked by the inner listener/adaptor classes to invoke the method.
 191   
      */
 192   
 
 193  65
     static void invokeTargetMethod(Object target, Method method, Object[] args)
 194   
     {
 195  65
         if (LOG.isDebugEnabled())
 196  0
             LOG.debug("Invoking listener method " + method + " on " + target);
 197   
 
 198  65
         try
 199   
         {
 200  65
             try
 201   
             {
 202  65
                 method.invoke(target, args);
 203   
             }
 204   
             catch (InvocationTargetException ex)
 205   
             {
 206  11
                 Throwable inner = ex.getTargetException();
 207   
 
 208  11
                 if (inner instanceof ApplicationRuntimeException)
 209  10
                     throw (ApplicationRuntimeException) inner;
 210   
 
 211   
                 // Edit out the InvocationTargetException, if possible.
 212   
 
 213  1
                 if (inner instanceof RuntimeException)
 214  1
                     throw (RuntimeException) inner;
 215   
 
 216  0
                 throw ex;
 217   
             }
 218   
         }
 219   
         catch (ApplicationRuntimeException ex)
 220   
         {
 221  10
             throw ex;
 222   
         }
 223   
         catch (Exception ex)
 224   
         {
 225   
             // Catch InvocationTargetException or, preferrably,
 226   
             // the inner exception here (if its a runtime exception).
 227   
 
 228  1
             throw new ApplicationRuntimeException(ListenerMessages.unableToInvokeMethod(
 229   
                     method,
 230   
                     target,
 231   
                     ex), ex);
 232   
         }
 233   
     }
 234   
 
 235   
     /**
 236   
      * Returns an unmodifiable collection of the names of the listeners implemented by the target
 237   
      * class.
 238   
      * 
 239   
      * @since 1.0.6
 240   
      */
 241   
 
 242  2
     public synchronized Collection getListenerNames()
 243   
     {
 244  2
         return Collections.unmodifiableCollection(getMethodMap().keySet());
 245   
     }
 246   
 
 247   
     /**
 248   
      * Returns true if this ListenerMap can provide a listener with the given name.
 249   
      * 
 250   
      * @since 2.2
 251   
      */
 252   
 
 253  7
     public synchronized boolean canProvideListener(String name)
 254   
     {
 255  7
         return getMethodMap().containsKey(name);
 256   
     }
 257   
 
 258  1
     public String toString()
 259   
     {
 260  1
         return "ListenerMap[" + _target + "]";
 261   
     }
 262   
 }