    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements.  See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache License, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License.  You may obtain a copy of the License at
    008     *
    009     *      http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the License for the specific language governing permissions and
    015     * limitations under the License.
    016     */
    017    package org.apache.commons.jexl2.internal.introspection;
    018    import java.util.List;
    019    import java.util.LinkedList;
    020    import java.util.Iterator;
    021    import java.lang.reflect.Constructor;
    022    import java.lang.reflect.Method;
    023    import java.util.Arrays;
    024
    025    /**
    026     * A method key usable by the introspector cache.
    027     * <p>
    028     * This stores a method (or class) name and parameters.
    029     * </p>
    030     * <p>
    031     * This replaces the original key scheme which used to build the key
    032     * by concatenating the method name and parameters class names as one string
    033     * with the exception that primitive types were converted to their object class equivalents.
    034     * </p>
    035     * <p>
    036     * The key is still based on the same information, it is just wrapped in an object instead.
    037     * Primitive type classes are converted to they object equivalent to make a key;
    038     * int foo(int) and int foo(Integer) do generate the same key.
    039     * </p>
    040     * A key can be constructed either from arguments (array of objects) or from parameters
    041     * (array of class).
    042     * Roughly 3x faster than string key to access the map & uses less memory.
    043     *
    044     * For the parameters methods:
    045     * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
    046     * @author <a href="mailto:bob@werken.com">Bob McWhirter</a>
    047     * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph Reck</a>
    048     * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
    049     * @author <a href="mailto:szegedia@freemail.hu">Attila Szegedi</a>
    050     * @author Nathan Bubna
    051     */
    052    public final class MethodKey {
    053        /** The hash code. */
    054        private final int hashCode;
    055        /** The method name. */
    056        private final String method;
    057        /** The parameters. */
    058        private final Class<?>[] params;
    059        /** A marker for empty parameter list. */
    060        private static final Class<?>[] NOARGS = new Class<?>[0];
    061        /** The hash code constants. */
    062        private static final int HASH = 37;
    063
    064        /**
    065         * Creates a key from a method name and a set of arguments.
    066         * @param aMethod the method to generate the key from
    067         * @param args the intended method arguments
    068         */
    069        public MethodKey(String aMethod, Object[] args) {
    070            super();
    071            // !! keep this in sync with the other ctor (hash code) !!
    072            this.method = aMethod;
    073            int hash = this.method.hashCode();
    074            final int size;
    075            // CSOFF: InnerAssignment
    076            if (args != null && (size = args.length) > 0) {
    077                this.params = new Class<?>[size];
    078                for (int p = 0; p < size; ++p) {
    079                    Object arg = args[p];
    080                    // null arguments use void as Void.class as marker
    081                    Class<?> parm = arg == null ? Void.class : arg.getClass();
    082                    hash = (HASH * hash) + parm.hashCode();
    083                    this.params[p] = parm;
    084                }
    085            } else {
    086                this.params = NOARGS;
    087            }
    088            this.hashCode = hash;
    089        }
    090
    091        /**
    092         * Creates a key from a method.
    093         * @param aMethod the method to generate the key from.
    094         */
    095        MethodKey(Method aMethod) {
    096            this(aMethod.getName(), aMethod.getParameterTypes());
    097        }
    098
    099        /**
    100         * Creates a key from a method name and a set of parameters.
    101         * @param aMethod the method to generate the key from
    102         * @param args the intended method parameters
    103         */
    104        MethodKey(String aMethod, Class<?>[] args) {
    105            super();
    106            // !! keep this in sync with the other ctor (hash code) !!
    107            this.method = aMethod.intern();
    108            int hash = this.method.hashCode();
    109            final int size;
    110            // CSOFF: InnerAssignment
    111            if (args != null && (size = args.length) > 0) {
    112                this.params = new Class<?>[size];
    113                for (int p = 0; p < size; ++p) {
    114                    Class<?> parm = ClassMap.MethodCache.primitiveClass(args[p]);
    115                    hash = (HASH * hash) + parm.hashCode();
    116                    this.params[p] = parm;
    117                }
    118            } else {
    119                this.params = NOARGS;
    120            }
    121            this.hashCode = hash;
    122        }
    123
    124        /**
    125         * Gets this key's method name.
    126         * @return the method name
    127         */
    128        String getMethod() {
    129            return method;
    130        }
    131
    132        /**
    133         * Gets this key's method parameter classes.
    134         * @return the parameters
    135         */
    136        Class<?>[] getParameters() {
    137            return params;
    138        }
    139
    140        /** {@inheritDoc} */
    141        @Override
    142        public int hashCode() {
    143            return hashCode;
    144        }
    145
    146        /** {@inheritDoc} */
    147        @Override
    148        public boolean equals(Object obj) {
    149            if (obj instanceof MethodKey) {
    150                MethodKey key = (MethodKey) obj;
    151                return method.equals(key.method) && Arrays.equals(params, key.params);
    152            }
    153            return false;
    154        }
    155
    156        /** {@inheritDoc} */
    157        @Override
    158        public String toString() {
    159            StringBuilder builder = new StringBuilder(method);
    160            for (Class<?> c : params) {
    161                builder.append(c == Void.class ? "null" : c.getName());
    162            }
    163            return builder.toString();
    164        }
    165
    166        /**
    167         * Outputs a human readable debug representation of this key.
    168         * @return method(p0, p1, ...)
    169         */
    170        public String debugString() {
    171            StringBuilder builder = new StringBuilder(method);
    172            builder.append('(');
    173            for (int i = 0; i < params.length; i++) {
    174                if (i > 0) {
    175                    builder.append(", ");
    176                }
    177                builder.append(Void.class == params[i] ? "null" : params[i].getName());
    178            }
    179            builder.append(')');
    180            return builder.toString();
    181        }
    182
    183        /**
    184         * Gets the most specific method that is applicable to the parameters of this key.
    185         * @param methods a list of methods.
    186         * @return the most specific method.
    187         * @throws MethodKey.AmbiguousException if there is more than one.
    188         */
    189        public Method getMostSpecificMethod(List<Method> methods) {
    190            return METHODS.getMostSpecific(methods, params);
    191        }
    192
    193        /**
    194         * Gets the most specific constructor that is applicable to the parameters of this key.
    195         * @param methods a list of constructors.
    196         * @return the most specific constructor.
    197         * @throws MethodKey.AmbiguousException if there is more than one.
    198         */
    199        public Constructor<?> getMostSpecificConstructor(List<Constructor<?>> methods) {
    200            return CONSTRUCTORS.getMostSpecific(methods, params);
    201        }
    202
    203        /**
    204         * Determines whether a type represented by a class object is
    205         * convertible to another type represented by a class object using a
    206         * method invocation conversion, treating object types of primitive
    207         * types as if they were primitive types (that is, a Boolean actual
    208         * parameter type matches boolean primitive formal type). This behavior
    209         * is because this method is used to determine applicable methods for
    210         * an actual parameter list, and primitive types are represented by
    211         * their object duals in reflective method calls.
    212         *
    213         * @param formal         the formal parameter type to which the actual
    214         *                       parameter type should be convertible
    215         * @param actual         the actual parameter type.
    216         * @param possibleVarArg whether or not we're dealing with the last parameter
    217         *                       in the method declaration
    218         * @return true if either formal type is assignable from actual type,
    219         *         or formal is a primitive type and actual is its corresponding object
    220         *         type or an object type of a primitive type that can be converted to
    221         *         the formal type.
    222         */
    223        public static boolean isInvocationConvertible(Class<?> formal,
    224                                                      Class<?> actual,
    225                                                      boolean possibleVarArg) {
    226            /* if it's a null, it means the arg was null */
    227            if (actual == null && !formal.isPrimitive()) {
    228                return true;
    229            }
    230
    231            /* Check for identity or widening reference conversion */
    232            if (actual != null && formal.isAssignableFrom(actual)) {
    233                return true;
    234            }
    235
    236            // CSOFF: NeedBraces
    237            /* Check for boxing with widening primitive conversion. Note that
    238             * actual parameters are never primitives. */
    239            if (formal.isPrimitive()) {
    240                if (formal == Boolean.TYPE && actual == Boolean.class)
    241                    return true;
    242                if (formal == Character.TYPE && actual == Character.class)
    243                    return true;
    244                if (formal == Byte.TYPE && actual == Byte.class)
    245                    return true;
    246                if (formal == Short.TYPE
    247                    && (actual == Short.class || actual == Byte.class))
    248                    return true;
    249                if (formal == Integer.TYPE
    250                    && (actual == Integer.class || actual == Short.class
    251                        || actual == Byte.class))
    252                    return true;
    253                if (formal == Long.TYPE
    254                    && (actual == Long.class || actual == Integer.class
    255                        || actual == Short.class || actual == Byte.class))
    256                    return true;
    257                if (formal == Float.TYPE
    258                    && (actual == Float.class || actual == Long.class
    259                        || actual == Integer.class || actual == Short.class
    260                        || actual == Byte.class))
    261                    return true;
    262                if (formal == Double.TYPE
    263                    && (actual == Double.class || actual == Float.class
    264                        || actual == Long.class || actual == Integer.class
    265                        || actual == Short.class || actual == Byte.class))
    266                    return true;
    267            }
    268            // CSON: NeedBraces
    269
    270            /* Check for vararg conversion. */
    271            if (possibleVarArg && formal.isArray()) {
    272                if (actual != null && actual.isArray()) {
    273                    actual = actual.getComponentType();
    274                }
    275                return isInvocationConvertible(formal.getComponentType(),
    276                        actual, false);
    277            }
    278            return false;
    279        }
    280
    281        /**
    282         * Determines whether a type represented by a class object is
    283         * convertible to another type represented by a class object using a
    284         * method invocation conversion, without matching object and primitive
    285         * types. This method is used to determine the more specific type when
    286         * comparing signatures of methods.
    287         *
    288         * @param formal         the formal parameter type to which the actual
    289         *                       parameter type should be convertible
    290         * @param actual         the actual parameter type.
    291         * @param possibleVarArg whether or not we're dealing with the last parameter
    292         *                       in the method declaration
    293         * @return true if either formal type is assignable from actual type,
    294         *         or formal and actual are both primitive types and actual can be
    295         *         subject to widening conversion to formal.
    296         */
    297        public static boolean isStrictInvocationConvertible(Class<?> formal,
    298                                                            Class<?> actual,
    299                                                            boolean possibleVarArg) {
    300            /* we shouldn't get a null into, but if so */
    301            if (actual == null && !formal.isPrimitive()) {
    302                return true;
    303            }
    304
    305            /* Check for identity or widening reference conversion */
    306            if (formal.isAssignableFrom(actual)) {
    307                return true;
    308            }
    309
    310            // CSOFF: NeedBraces
    311            /* Check for widening primitive conversion. */
    312            if (formal.isPrimitive()) {
    313                if (formal == Short.TYPE && (actual == Byte.TYPE))
    314                    return true;
    315                if (formal == Integer.TYPE
    316                    && (actual == Short.TYPE || actual == Byte.TYPE))
    317                    return true;
    318                if (formal == Long.TYPE
    319                    && (actual == Integer.TYPE || actual == Short.TYPE
    320                        || actual == Byte.TYPE))
    321                    return true;
    322                if (formal == Float.TYPE
    323                    && (actual == Long.TYPE || actual == Integer.TYPE
    324                        || actual == Short.TYPE || actual == Byte.TYPE))
    325                    return true;
    326                if (formal == Double.TYPE
    327                    && (actual == Float.TYPE || actual == Long.TYPE
    328                        || actual == Integer.TYPE || actual == Short.TYPE
    329                        || actual == Byte.TYPE))
    330                    return true;
    331            }
    332            // CSON: NeedBraces
    333
    334            /* Check for vararg conversion. */
    335            if (possibleVarArg && formal.isArray()) {
    336                if (actual != null && actual.isArray()) {
    337                    actual = actual.getComponentType();
    338                }
    339                return isStrictInvocationConvertible(formal.getComponentType(),
    340                        actual, false);
    341            }
    342            return false;
    343        }
    344
    345        /**
    346         * whether a method/ctor is more specific than a previously compared one.
    347         */
    348        private static final int MORE_SPECIFIC = 0;
    349        /**
    350         * whether a method/ctor is less specific than a previously compared one.
    351         */
    352        private static final int LESS_SPECIFIC = 1;
    353        /**
    354         * A method/ctor doesn't match a previously compared one.
    355         */
    356        private static final int INCOMPARABLE = 2;
    357
    358        /**
    359         * Simple distinguishable exception, used when
    360         * we run across ambiguous overloading.  Caught
    361         * by the introspector.
    362         */
    363        public static class AmbiguousException extends RuntimeException {
    364            /**
    365             * Version Id for serializable.
    366             */
    367            private static final long serialVersionUID = -2314636505414551664L;
    368        }
    369
    370        /**
    371         * Utility for parameters matching.
    372         * @param <T> Method or Constructor
    373         */
    374         private abstract static class Parameters<T> {
    375            /**
    376             * Extract the parameter types from its applicable argument.
    377             * @param app a method or constructor
    378             * @return the parameters
    379             */
    380            protected abstract Class<?>[] getParameterTypes(T app);
    381
    382            // CSOFF: RedundantThrows
    383            /**
    384             * Gets the most specific method that is applicable to actual argument types.
    385             * @param methods a list of methods.
    386             * @param classes list of argument types.
    387             * @return the most specific method.
    388             * @throws MethodKey.AmbiguousException if there is more than one.
    389             */
    390            private T getMostSpecific(List<T> methods, Class<?>[] classes) {
    391                LinkedList<T> applicables = getApplicables(methods, classes);
    392
    393                if (applicables.isEmpty()) {
    394                    return null;
    395                }
    396
    397                if (applicables.size() == 1) {
    398                    return applicables.getFirst();
    399                }
    400
    401                /*
    402                 * This list will contain the maximally specific methods. Hopefully at
    403                 * the end of the below loop, the list will contain exactly one method,
    404                 * (the most specific method) otherwise we have ambiguity.
    405                 */
    406
    407                LinkedList<T> maximals = new LinkedList<T>();
    408
    409                for (Iterator<T> applicable = applicables.iterator();
    410                        applicable.hasNext();) {
    411                    T app = applicable.next();
    412                    Class<?>[] appArgs = getParameterTypes(app);
    413
    414                    boolean lessSpecific = false;
    415
    416                    for (Iterator<T> maximal = maximals.iterator();
    417                            !lessSpecific && maximal.hasNext();) {
    418                        T max = maximal.next();
    419
    420                        // CSOFF: MissingSwitchDefault
    421                        switch (moreSpecific(appArgs, getParameterTypes(max))) {
    422                            case MORE_SPECIFIC:
    423                                /*
    424                                 * This method is more specific than the previously
    425                                 * known maximally specific, so remove the old maximum.
    426                                 */
    427                                maximal.remove();
    428                                break;
    429
    430                            case LESS_SPECIFIC:
    431                                /*
    432                                 * This method is less specific than some of the
    433                                 * currently known maximally specific methods, so we
    434                                 * won't add it into the set of maximally specific
    435                                 * methods
    436                                 */
    437
    438                                lessSpecific = true;
    439                                break;
    440                        }
    441                    } // CSON: MissingSwitchDefault
    442
    443                    if (!lessSpecific) {
    444                        maximals.addLast(app);
    445                    }
    446                }
    447
    448                if (maximals.size() > 1) {
    449                    // We have more than one maximally specific method
    450                    throw new AmbiguousException();
    451                }
    452
    453                return maximals.getFirst();
    454            } // CSON: RedundantThrows
    455
    456            /**
    457             * Determines which method signature (represented by a class array) is more
    458             * specific. This defines a partial ordering on the method signatures.
    459             *
    460             * @param c1 first signature to compare
    461             * @param c2 second signature to compare
    462             * @return MORE_SPECIFIC if c1 is more specific than c2, LESS_SPECIFIC if
    463             *         c1 is less specific than c2, INCOMPARABLE if they are incomparable.
    464             */
    465            private int moreSpecific(Class<?>[] c1, Class<?>[] c2) {
    466                boolean c1MoreSpecific = false;
    467                boolean c2MoreSpecific = false;
    468
    469                // compare lengths to handle comparisons where the size of the arrays
    470                // doesn't match, but the methods are both applicable due to the fact
    471                // that one is a varargs method
    472                if (c1.length > c2.length) {
    473                    return MORE_SPECIFIC;
    474                }
    475                if (c2.length > c1.length) {
    476                    return LESS_SPECIFIC;
    477                }
    478
    479                // ok, move on and compare those of equal lengths
    480                for (int i = 0; i < c1.length; ++i) {
    481                    if (c1[i] != c2[i]) {
    482                        boolean last = (i == c1.length - 1);
    483                        c1MoreSpecific = c1MoreSpecific || isStrictConvertible(c2[i], c1[i], last);
    484                        c2MoreSpecific = c2MoreSpecific || isStrictConvertible(c1[i], c2[i], last);
    485                    }
    486                }
    487
    488                if (c1MoreSpecific) {
    489                    if (c2MoreSpecific) {
    490                        /*
    491                         *  Incomparable due to cross-assignable arguments (i.e.
    492                         * foo(String, Object) vs. foo(Object, String))
    493                         */
    494
    495                        return INCOMPARABLE;
    496                    }
    497
    498                    return MORE_SPECIFIC;
    499                }
    500
    501                if (c2MoreSpecific) {
    502                    return LESS_SPECIFIC;
    503                }
    504
    505                /*
    506                 * Incomparable due to non-related arguments (i.e.
    507                 * foo(Runnable) vs. foo(Serializable))
    508                 */
    509
    510                return INCOMPARABLE;
    511            }
    512
    513            /**
    514             * Returns all methods that are applicable to actual argument types.
    515             *
    516             * @param methods list of all candidate methods
    517             * @param classes the actual types of the arguments
    518             * @return a list that contains only applicable methods (number of
    519             *         formal and actual arguments matches, and argument types are assignable
    520             *         to formal types through a method invocation conversion).
    521             */
    522            private LinkedList<T> getApplicables(List<T> methods, Class<?>[] classes) {
    523                LinkedList<T> list = new LinkedList<T>();
    524
    525                for (Iterator<T> imethod = methods.iterator(); imethod.hasNext();) {
    526                    T method = imethod.next();
    527                    if (isApplicable(method, classes)) {
    528                        list.add(method);
    529                    }
    530
    531                }
    532                return list;
    533            }
    534
    535            /**
    536             * Returns true if the supplied method is applicable to actual
    537             * argument types.
    538             *
    539             * @param method  method that will be called
    540             * @param classes arguments to method
    541             * @return true if method is applicable to arguments
    542             */
    543            private boolean isApplicable(T method, Class<?>[] classes) {
    544                Class<?>[] methodArgs = getParameterTypes(method);
    545
    546                if (methodArgs.length > classes.length) {
    547                    // if there's just one more methodArg than class arg
    548                    // and the last methodArg is an array, then treat it as a vararg
    549                    return methodArgs.length == classes.length + 1 && methodArgs[methodArgs.length - 1].isArray();
    550                }
    551                if (methodArgs.length == classes.length) {
    552                    // this will properly match when the last methodArg
    553                    // is an array/varargs and the last class is the type of array
    554                    // (e.g. String when the method is expecting String...)
    555                    for (int i = 0; i < classes.length; ++i) {
    556                        if (!isConvertible(methodArgs[i], classes[i], false)) {
    557                            // if we're on the last arg and the method expects an array
    558                            if (i == classes.length - 1 && methodArgs[i].isArray()) {
    559                                // check to see if the last arg is convertible
    560                                // to the array's component type
    561                                return isConvertible(methodArgs[i], classes[i], true);
    562                            }
    563                            return false;
    564                        }
    565                    }
    566                    return true;
    567                }
    568                // more arguments given than the method accepts; check for varargs
    569                if (methodArgs.length > 0) {
    570                    // check that the last methodArg is an array
    571                    Class<?> lastarg = methodArgs[methodArgs.length - 1];
    572                    if (!lastarg.isArray()) {
    573                        return false;
    574                    }
    575
    576                    // check that they all match up to the last method arg
    577                    for (int i = 0; i < methodArgs.length - 1; ++i) {
    578                        if (!isConvertible(methodArgs[i], classes[i], false)) {
    579                            return false;
    580                        }
    581                    }
    582
    583                    // check that all remaining arguments are convertible to the vararg type
    584                    Class<?> vararg = lastarg.getComponentType();
    585                    for (int i = methodArgs.length - 1; i < classes.length; ++i) {
    586                        if (!isConvertible(vararg, classes[i], false)) {
    587                            return false;
    588                        }
    589                    }
    590                    return true;
    591                }
    592                // no match
    593                return false;
    594            }
    595
    596            /**
    597             * @see #isInvocationConvertible(Class, Class, boolean)
    598             * @param formal         the formal parameter type to which the actual
    599             *                       parameter type should be convertible
    600             * @param actual         the actual parameter type.
    601             * @param possibleVarArg whether or not we're dealing with the last parameter
    602             *                       in the method declaration
    603             * @return see isMethodInvocationConvertible.
    604             */
    605            private boolean isConvertible(Class<?> formal, Class<?> actual,
    606                    boolean possibleVarArg) {
    607                // if we see Void.class, the argument was null
    608                return isInvocationConvertible(formal, actual.equals(Void.class)? null : actual, possibleVarArg);
    609            }
    610
    611            /**
    612             * @see #isStrictInvocationConvertible(Class, Class, boolean)
    613             * @param formal         the formal parameter type to which the actual
    614             *                       parameter type should be convertible
    615             * @param actual         the actual parameter type.
    616             * @param possibleVarArg whether or not we're dealing with the last parameter
    617             *                       in the method declaration
    618             * @return see isStrictMethodInvocationConvertible.
    619             */
    620            private boolean isStrictConvertible(Class<?> formal, Class<?> actual,
    621                    boolean possibleVarArg) {
    622                // if we see Void.class, the argument was null
    623                return isStrictInvocationConvertible(formal, actual.equals(Void.class)? null : actual, possibleVarArg);
    624            }
    625
    626        }
    627
    628        /**
    629         * The parameter matching service for methods.
    630         */
    631        private static final Parameters<Method> METHODS = new Parameters<Method>() {
    632            @Override
    633            protected Class<?>[] getParameterTypes(Method app) {
    634                return app.getParameterTypes();
    635            }
    636        };
    637
    638
    639        /**
    640         * The parameter matching service for constructors.
    641         */
    642        private static final Parameters<Constructor<?>> CONSTRUCTORS = new Parameters<Constructor<?>>() {
    643            @Override
    644            protected Class<?>[] getParameterTypes(Constructor<?> app) {
    645                return app.getParameterTypes();
    646            }
    647        };
    648    }