    1   /*
    2    * Copyright 1999-2004 The Apache Software Foundation
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *     http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  package org.apache.commons.jxpath.util;
    17
    18  import java.lang.reflect.Constructor;
    19  import java.lang.reflect.Method;
    20  import java.lang.reflect.Modifier;
    21  import java.util.Arrays;
    22
    23  import org.apache.commons.jxpath.ExpressionContext;
    24  import org.apache.commons.jxpath.JXPathException;
    25
    26  /***
    27   * Method lookup utilities, which find static and non-static methods as well
    28   * as constructors based on a name and list of parameters.
    29   *
    30   * @author Dmitri Plotnikov
    31   * @version $Revision: 1.7 $ $Date: 2004/02/29 14:17:43 $
    32   */
    33  public class MethodLookupUtils {
    34
    35      private static final int NO_MATCH = 0;
    36      private static final int APPROXIMATE_MATCH = 1;
    37      private static final int EXACT_MATCH = 2;
    38      private static final Object[] EMPTY_ARRAY = new Object[0];
    39
    40       public static Constructor lookupConstructor(
    41          Class targetClass,
    42          Object[] parameters)
    43       {
    44          boolean tryExact = true;
    45          int count = parameters == null ? 0 : parameters.length;
    46          Class types[] = new Class[count];
    47          for (int i = 0; i < count; i++) {
    48              Object param = parameters[i];
    49              if (param != null) {
    50                  types[i] = param.getClass();
    51              }
    52              else {
    53                  types[i] = null;
    54                  tryExact = false;
    55              }
    56          }
    57
    58          Constructor constructor = null;
    59
    60          if (tryExact) {
    61              // First - without type conversion
    62              try {
    63                  constructor = targetClass.getConstructor(types);
    64                  if (constructor != null) {
    65                      return constructor;
    66                  }
    67              }
    68              catch (NoSuchMethodException ex) {
    69                  // Ignore
    70              }
    71          }
    72
    73          int currentMatch = 0;
    74          boolean ambiguous = false;
    75
    76          // Then - with type conversion
    77          Constructor[] constructors = targetClass.getConstructors();
    78          for (int i = 0; i < constructors.length; i++) {
    79              int match =
    80                  matchParameterTypes(
    81                      constructors[i].getParameterTypes(),
    82                      parameters);
    83              if (match != NO_MATCH) {
    84                  if (match > currentMatch) {
    85                      constructor = constructors[i];
    86                      currentMatch = match;
    87                      ambiguous = false;
    88                  }
    89                  else if (match == currentMatch) {
    90                      ambiguous = true;
    91                  }
    92              }
    93          }
    94          if (ambiguous) {
    95              throw new JXPathException(
    96                  "Ambigous constructor " + Arrays.asList(parameters));
    97          }
    98          return constructor;
    99      }
    100
    101     public static Method lookupStaticMethod(
    102         Class targetClass,
    103         String name,
    104         Object[] parameters)
    105     {
    106         boolean tryExact = true;
    107         int count = parameters == null ? 0 : parameters.length;
    108         Class types[] = new Class[count];
    109         for (int i = 0; i < count; i++) {
    110             Object param = parameters[i];
    111             if (param != null) {
    112                 types[i] = param.getClass();
    113             }
    114             else {
    115                 types[i] = null;
    116                 tryExact = false;
    117             }
    118         }
    119
    120         Method method = null;
    121
    122         if (tryExact) {
    123             // First - without type conversion
    124             try {
    125                 method = targetClass.getMethod(name, types);
    126                 if (method != null
    127                     && Modifier.isStatic(method.getModifiers())) {
    128                     return method;
    129                 }
    130             }
    131             catch (NoSuchMethodException ex) {
    132                 // Ignore
    133             }
    134         }
    135
    136         int currentMatch = 0;
    137         boolean ambiguous = false;
    138
    139         // Then - with type conversion
    140         Method[] methods = targetClass.getMethods();
    141         for (int i = 0; i < methods.length; i++) {
    142             if (Modifier.isStatic(methods[i].getModifiers())
    143                 && methods[i].getName().equals(name)) {
    144                 int match =
    145                     matchParameterTypes(
    146                         methods[i].getParameterTypes(),
    147                         parameters);
    148                 if (match != NO_MATCH) {
    149                     if (match > currentMatch) {
    150                         method = methods[i];
    151                         currentMatch = match;
    152                         ambiguous = false;
    153                     }
    154                     else if (match == currentMatch) {
    155                         ambiguous = true;
    156                     }
    157                 }
    158             }
    159         }
    160         if (ambiguous) {
    161             throw new JXPathException("Ambigous method call: " + name);
    162         }
    163         return method;
    164     }
    165
    166     public static Method lookupMethod(
    167         Class targetClass,
    168         String name,
    169         Object[] parameters)
    170     {
    171         if (parameters == null
    172             || parameters.length < 1
    173             || parameters[0] == null) {
    174             return null;
    175         }
    176
    177         if (matchType(targetClass, parameters[0]) == NO_MATCH) {
    178             return null;
    179         }
    180
    181         targetClass = TypeUtils.convert(parameters[0], targetClass).getClass();
    182
    183         boolean tryExact = true;
    184         int count = parameters.length - 1;
    185         Class types[] = new Class[count];
    186         Object arguments[] = new Object[count];
    187         for (int i = 0; i < count; i++) {
    188             Object param = parameters[i + 1];
    189             arguments[i] = param;
    190             if (param != null) {
    191                 types[i] = param.getClass();
    192             }
    193             else {
    194                 types[i] = null;
    195                 tryExact = false;
    196             }
    197         }
    198
    199         Method method = null;
    200
    201         if (tryExact) {
    202             // First - without type conversion
    203             try {
    204                 method = targetClass.getMethod(name, types);
    205                 if (method != null
    206                     && !Modifier.isStatic(method.getModifiers())) {
    207                     return method;
    208                 }
    209             }
    210             catch (NoSuchMethodException ex) {
    211                 // Ignore
    212             }
    213         }
    214
    215         int currentMatch = 0;
    216         boolean ambiguous = false;
    217
    218         // Then - with type conversion
    219         Method[] methods = targetClass.getMethods();
    220         for (int i = 0; i < methods.length; i++) {
    221             if (!Modifier.isStatic(methods[i].getModifiers())
    222                 && methods[i].getName().equals(name)) {
    223                 int match =
    224                     matchParameterTypes(
    225                         methods[i].getParameterTypes(),
    226                         arguments);
    227                 if (match != NO_MATCH) {
    228                     if (match > currentMatch) {
    229                         method = methods[i];
    230                         currentMatch = match;
    231                         ambiguous = false;
    232                     }
    233                     else if (match == currentMatch) {
    234                         ambiguous = true;
    235                     }
    236                 }
    237             }
    238         }
    239         if (ambiguous) {
    240             throw new JXPathException("Ambigous method call: " + name);
    241         }
    242         return method;
    243     }
    244
    245     private static int matchParameterTypes(
    246         Class types[],
    247         Object parameters[])
    248     {
    249         int pi = 0;
    250         if (types.length >= 1
    251             && ExpressionContext.class.isAssignableFrom(types[0])) {
    252             pi++;
    253         }
    254         int length = parameters == null ? 0 : parameters.length;
    255         if (types.length != length + pi) {
    256             return NO_MATCH;
    257         }
    258         int totalMatch = EXACT_MATCH;
    259         for (int i = 0; i < length; i++) {
    260             int match = matchType(types[i + pi], parameters[i]);
    261             if (match == NO_MATCH) {
    262                 return NO_MATCH;
    263             }
    264             if (match < totalMatch) {
    265                 totalMatch = match;
    266             }
    267         }
    268         return totalMatch;
    269     }
    270
    271     private static int matchType(Class expected, Object object) {
    272         if (object == null) {
    273             return APPROXIMATE_MATCH;
    274         }
    275
    276         Class actual = object.getClass();
    277
    278         if (expected.equals(actual)) {
    279             return EXACT_MATCH;
    280         }
    281         if (expected.isAssignableFrom(actual)) {
    282             return EXACT_MATCH;
    283         }
    284
    285         if (TypeUtils.canConvert(object, expected)) {
    286             return APPROXIMATE_MATCH;
    287         }
    288
    289         return NO_MATCH;
    290     }
    291 }