Coverage Report - org.mule.util.generics.MethodParameter
 
Classes in this File Line Coverage Branch Coverage Complexity
MethodParameter
0%
0/76
0%
0/42
0
 
 1  
 /*
 2  
  * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 3  
  * The software in this package is published under the terms of the CPAL v1.0
 4  
  * license, a copy of which has been included with this distribution in the
 5  
  * LICENSE.txt file.
 6  
  */
 7  
 package org.mule.util.generics;
 8  
 
 9  
 import java.lang.annotation.Annotation;
 10  
 import java.lang.reflect.Constructor;
 11  
 import java.lang.reflect.Method;
 12  
 import java.lang.reflect.Type;
 13  
 import java.lang.reflect.TypeVariable;
 14  
 import java.util.HashMap;
 15  
 import java.util.Map;
 16  
 
 17  
 /**
 18  
  * Helper class that encapsulates the specification of a method parameter, i.e.
 19  
  * a Method or Constructor plus a parameter index and a nested type index for
 20  
  * a declared generic type. Useful as a specification object to pass along.
 21  
  * <p/>
 22  
  * author: Spring
 23  
  */
 24  
 public class MethodParameter
 25  
 {
 26  
 
 27  
     private Method method;
 28  
 
 29  
     private Constructor constructor;
 30  
 
 31  
     private final int parameterIndex;
 32  
 
 33  
     private Class<?> parameterType;
 34  
 
 35  
     private Type genericParameterType;
 36  
 
 37  
     private Annotation[] parameterAnnotations;
 38  
 
 39  
     private ParameterNameDiscoverer parameterNameDiscoverer;
 40  
 
 41  
     private String parameterName;
 42  
 
 43  0
     private int nestingLevel = 1;
 44  
 
 45  
     /**
 46  
      * Map from Integer level to Integer type index
 47  
      */
 48  
     private Map<Integer, Integer> typeIndexesPerLevel;
 49  
 
 50  
     Map<TypeVariable, Type> typeVariableMap;
 51  
 
 52  
 
 53  
     /**
 54  
      * Create a new MethodParameter for the given method, with nesting level 1.
 55  
      *
 56  
      * @param method         the Method to specify a parameter for
 57  
      * @param parameterIndex the index of the parameter
 58  
      */
 59  
     public MethodParameter(Method method, int parameterIndex)
 60  
     {
 61  0
         this(method, parameterIndex, 1);
 62  0
     }
 63  
 
 64  
     /**
 65  
      * Create a new MethodParameter for the given method.
 66  
      *
 67  
      * @param method         the Method to specify a parameter for
 68  
      * @param parameterIndex the index of the parameter
 69  
      *                       (-1 for the method return type; 0 for the first method parameter,
 70  
      *                       1 for the second method parameter, etc)
 71  
      * @param nestingLevel   the nesting level of the target type
 72  
      *                       (typically 1; e.g. in case of a List of Lists, 1 would indicate the
 73  
      *                       nested List, whereas 2 would indicate the element of the nested List)
 74  
      */
 75  
     public MethodParameter(Method method, int parameterIndex, int nestingLevel)
 76  0
     {
 77  0
         this.method = method;
 78  0
         this.parameterIndex = parameterIndex;
 79  0
         this.nestingLevel = nestingLevel;
 80  0
     }
 81  
 
 82  
     /**
 83  
      * Create a new MethodParameter for the given constructor, with nesting level 1.
 84  
      *
 85  
      * @param constructor    the Constructor to specify a parameter for
 86  
      * @param parameterIndex the index of the parameter
 87  
      */
 88  
     public MethodParameter(Constructor constructor, int parameterIndex)
 89  
     {
 90  0
         this(constructor, parameterIndex, 1);
 91  0
     }
 92  
 
 93  
     /**
 94  
      * Create a new MethodParameter for the given constructor.
 95  
      *
 96  
      * @param constructor    the Constructor to specify a parameter for
 97  
      * @param parameterIndex the index of the parameter
 98  
      * @param nestingLevel   the nesting level of the target type
 99  
      *                       (typically 1; e.g. in case of a List of Lists, 1 would indicate the
 100  
      *                       nested List, whereas 2 would indicate the element of the nested List)
 101  
      */
 102  
     public MethodParameter(Constructor constructor, int parameterIndex, int nestingLevel)
 103  0
     {
 104  0
         this.constructor = constructor;
 105  0
         this.parameterIndex = parameterIndex;
 106  0
         this.nestingLevel = nestingLevel;
 107  0
     }
 108  
 
 109  
     /**
 110  
      * Copy constructor, resulting in an independent MethodParameter object
 111  
      * based on the same metadata and cache state that the original object was in.
 112  
      *
 113  
      * @param original the original MethodParameter object to copy from
 114  
      */
 115  
     public MethodParameter(MethodParameter original)
 116  0
     {
 117  0
         this.method = original.method;
 118  0
         this.constructor = original.constructor;
 119  0
         this.parameterIndex = original.parameterIndex;
 120  0
         this.parameterType = original.parameterType;
 121  0
         this.parameterAnnotations = original.parameterAnnotations;
 122  0
         this.typeVariableMap = original.typeVariableMap;
 123  0
     }
 124  
 
 125  
 
 126  
     /**
 127  
      * Return the wrapped Method, if any.
 128  
      * <p>Note: Either Method or Constructor is available.
 129  
      *
 130  
      * @return the Method, or <code>null</code> if none
 131  
      */
 132  
     public Method getMethod()
 133  
     {
 134  0
         return this.method;
 135  
     }
 136  
 
 137  
     /**
 138  
      * Return the wrapped Constructor, if any.
 139  
      * <p>Note: Either Method or Constructor is available.
 140  
      *
 141  
      * @return the Constructor, or <code>null</code> if none
 142  
      */
 143  
     public Constructor getConstructor()
 144  
     {
 145  0
         return this.constructor;
 146  
     }
 147  
 
 148  
     /**
 149  
      * Return the class that declares the underlying Method or Constructor.
 150  
      */
 151  
     public Class getDeclaringClass()
 152  
     {
 153  0
         return (this.method != null ? this.method.getDeclaringClass() : this.constructor.getDeclaringClass());
 154  
     }
 155  
 
 156  
     /**
 157  
      * Return the index of the method/constructor parameter.
 158  
      *
 159  
      * @return the parameter index (never negative)
 160  
      */
 161  
     public int getParameterIndex()
 162  
     {
 163  0
         return this.parameterIndex;
 164  
     }
 165  
 
 166  
     /**
 167  
      * Set a resolved (generic) parameter type.
 168  
      */
 169  
     void setParameterType(Class<?> parameterType)
 170  
     {
 171  0
         this.parameterType = parameterType;
 172  0
     }
 173  
 
 174  
     /**
 175  
      * Return the type of the method/constructor parameter.
 176  
      *
 177  
      * @return the parameter type (never <code>null</code>)
 178  
      */
 179  
     public Class<?> getParameterType()
 180  
     {
 181  0
         if (this.parameterType == null)
 182  
         {
 183  0
             if (this.parameterIndex < 0)
 184  
             {
 185  0
                 this.parameterType = (this.method != null ? this.method.getReturnType() : null);
 186  
             }
 187  
             else
 188  
             {
 189  0
                 this.parameterType = (this.method != null ?
 190  
                         this.method.getParameterTypes()[this.parameterIndex] :
 191  
                         this.constructor.getParameterTypes()[this.parameterIndex]);
 192  
             }
 193  
         }
 194  0
         return this.parameterType;
 195  
     }
 196  
 
 197  
     /**
 198  
      * Return the generic type of the method/constructor parameter.
 199  
      *
 200  
      * @return the parameter type (never <code>null</code>)
 201  
      */
 202  
     public Type getGenericParameterType()
 203  
     {
 204  0
         if (this.genericParameterType == null)
 205  
         {
 206  0
             if (this.parameterIndex < 0)
 207  
             {
 208  0
                 this.genericParameterType = (this.method != null ? this.method.getGenericReturnType() : null);
 209  
             }
 210  
             else
 211  
             {
 212  0
                 this.genericParameterType = (this.method != null ?
 213  
                         this.method.getGenericParameterTypes()[this.parameterIndex] :
 214  
                         this.constructor.getGenericParameterTypes()[this.parameterIndex]);
 215  
             }
 216  
         }
 217  0
         return this.genericParameterType;
 218  
     }
 219  
 
 220  
     /**
 221  
      * Return the annotations associated with the target method/constructor itself.
 222  
      */
 223  
     public Annotation[] getMethodAnnotations()
 224  
     {
 225  0
         return (this.method != null ? this.method.getAnnotations() : this.constructor.getAnnotations());
 226  
     }
 227  
 
 228  
     /**
 229  
      * Return the method/constructor annotation of the given type, if available.
 230  
      *
 231  
      * @param annotationType the annotation type to look for
 232  
      * @return the annotation object, or <code>null</code> if not found
 233  
      */
 234  
     @SuppressWarnings("unchecked")
 235  
     public <T extends Annotation> T getMethodAnnotation(Class<T> annotationType)
 236  
     {
 237  0
         return (this.method != null ? this.method.getAnnotation(annotationType) :
 238  
                 (T) this.constructor.getAnnotation(annotationType));
 239  
     }
 240  
 
 241  
     /**
 242  
      * Return the annotations associated with the specific method/constructor parameter.
 243  
      */
 244  
     public Annotation[] getParameterAnnotations()
 245  
     {
 246  0
         if (this.parameterAnnotations == null)
 247  
         {
 248  0
             Annotation[][] annotationArray = (this.method != null ?
 249  
                     this.method.getParameterAnnotations() : this.constructor.getParameterAnnotations());
 250  0
             this.parameterAnnotations = annotationArray[this.parameterIndex];
 251  
         }
 252  0
         return this.parameterAnnotations;
 253  
     }
 254  
 
 255  
     /**
 256  
      * Return the parameter annotation of the given type, if available.
 257  
      *
 258  
      * @param annotationType the annotation type to look for
 259  
      * @return the annotation object, or <code>null</code> if not found
 260  
      */
 261  
     @SuppressWarnings("unchecked")
 262  
     public <T extends Annotation> T getParameterAnnotation(Class<T> annotationType)
 263  
     {
 264  0
         Annotation[] anns = getParameterAnnotations();
 265  0
         for (Annotation ann : anns)
 266  
         {
 267  0
             if (annotationType.isInstance(ann))
 268  
             {
 269  0
                 return (T) ann;
 270  
             }
 271  
         }
 272  0
         return null;
 273  
     }
 274  
 
 275  
     /**
 276  
      * Initialize parameter name discovery for this method parameter.
 277  
      * <p>This method does not actually try to retrieve the parameter name at
 278  
      * this point; it just allows discovery to happen when the application calls
 279  
      * {@link #getParameterName()} (if ever).
 280  
      */
 281  
     public void initParameterNameDiscovery(ParameterNameDiscoverer parameterNameDiscoverer)
 282  
     {
 283  0
         this.parameterNameDiscoverer = parameterNameDiscoverer;
 284  0
     }
 285  
 
 286  
     /**
 287  
      * Return the name of the method/constructor parameter.
 288  
      *
 289  
      * @return the parameter name (may be <code>null</code> if no
 290  
      *         parameter name metadata is contained in the class file or no
 291  
      *         {@link #initParameterNameDiscovery ParameterNameDiscoverer}
 292  
      *         has been set to begin with)
 293  
      */
 294  
     public String getParameterName()
 295  
     {
 296  0
         if (this.parameterNameDiscoverer != null)
 297  
         {
 298  0
             String[] parameterNames = (this.method != null ?
 299  
                     this.parameterNameDiscoverer.getParameterNames(this.method) :
 300  
                     this.parameterNameDiscoverer.getParameterNames(this.constructor));
 301  0
             if (parameterNames != null)
 302  
             {
 303  0
                 this.parameterName = parameterNames[this.parameterIndex];
 304  
             }
 305  0
             this.parameterNameDiscoverer = null;
 306  
         }
 307  0
         return this.parameterName;
 308  
     }
 309  
 
 310  
     /**
 311  
      * Increase this parameter's nesting level.
 312  
      *
 313  
      * @see #getNestingLevel()
 314  
      */
 315  
     public void increaseNestingLevel()
 316  
     {
 317  0
         this.nestingLevel++;
 318  0
     }
 319  
 
 320  
     /**
 321  
      * Decrease this parameter's nesting level.
 322  
      *
 323  
      * @see #getNestingLevel()
 324  
      */
 325  
     public void decreaseNestingLevel()
 326  
     {
 327  0
         getTypeIndexesPerLevel().remove(this.nestingLevel);
 328  0
         this.nestingLevel--;
 329  0
     }
 330  
 
 331  
     /**
 332  
      * Return the nesting level of the target type
 333  
      * (typically 1; e.g. in case of a List of Lists, 1 would indicate the
 334  
      * nested List, whereas 2 would indicate the element of the nested List).
 335  
      */
 336  
     public int getNestingLevel()
 337  
     {
 338  0
         return this.nestingLevel;
 339  
     }
 340  
 
 341  
     /**
 342  
      * Set the type index for the current nesting level.
 343  
      *
 344  
      * @param typeIndex the corresponding type index
 345  
      *                  (or <code>null</code> for the default type index)
 346  
      * @see #getNestingLevel()
 347  
      */
 348  
     public void setTypeIndexForCurrentLevel(int typeIndex)
 349  
     {
 350  0
         getTypeIndexesPerLevel().put(this.nestingLevel, typeIndex);
 351  0
     }
 352  
 
 353  
     /**
 354  
      * Return the type index for the current nesting level.
 355  
      *
 356  
      * @return the corresponding type index, or <code>null</code>
 357  
      *         if none specified (indicating the default type index)
 358  
      * @see #getNestingLevel()
 359  
      */
 360  
     public Integer getTypeIndexForCurrentLevel()
 361  
     {
 362  0
         return getTypeIndexForLevel(this.nestingLevel);
 363  
     }
 364  
 
 365  
     /**
 366  
      * Return the type index for the specified nesting level.
 367  
      *
 368  
      * @param nestingLevel the nesting level to check
 369  
      * @return the corresponding type index, or <code>null</code>
 370  
      *         if none specified (indicating the default type index)
 371  
      */
 372  
     public Integer getTypeIndexForLevel(int nestingLevel)
 373  
     {
 374  0
         return getTypeIndexesPerLevel().get(nestingLevel);
 375  
     }
 376  
 
 377  
     /**
 378  
      * Obtain the (lazily constructed) type-indexes-per-level Map.
 379  
      */
 380  
     private Map<Integer, Integer> getTypeIndexesPerLevel()
 381  
     {
 382  0
         if (this.typeIndexesPerLevel == null)
 383  
         {
 384  0
             this.typeIndexesPerLevel = new HashMap<Integer, Integer>(4);
 385  
         }
 386  0
         return this.typeIndexesPerLevel;
 387  
     }
 388  
 
 389  
 
 390  
     /**
 391  
      * Create a new MethodParameter for the given method or constructor.
 392  
      * <p>This is a convenience constructor for scenarios where a
 393  
      * Method or Constructor reference is treated in a generic fashion.
 394  
      *
 395  
      * @param methodOrConstructor the Method or Constructor to specify a parameter for
 396  
      * @param parameterIndex      the index of the parameter
 397  
      * @return the corresponding MethodParameter instance
 398  
      */
 399  
     public static MethodParameter forMethodOrConstructor(Object methodOrConstructor, int parameterIndex)
 400  
     {
 401  0
         if (methodOrConstructor instanceof Method)
 402  
         {
 403  0
             return new MethodParameter((Method) methodOrConstructor, parameterIndex);
 404  
         }
 405  0
         else if (methodOrConstructor instanceof Constructor) {
 406  0
             return new MethodParameter((Constructor) methodOrConstructor, parameterIndex);
 407  
         }
 408  
         else {
 409  0
             throw new IllegalArgumentException(
 410  
                     "Given object [" + methodOrConstructor + "] is neither a Method nor a Constructor");
 411  
         }
 412  
     }
 413  
 
 414  
 }