001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *  
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *  
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License. 
018 *  
019 */
020package org.apache.directory.api.util;
021
022
023import org.slf4j.Logger;
024import org.slf4j.LoggerFactory;
025
026import java.lang.reflect.Method;
027import java.util.Arrays;
028
029
030/**
031 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
032 */
033public final class MethodUtils
034{
035    /** The logger. */
036    private static final Logger LOG = LoggerFactory.getLogger( MethodUtils.class );
037
038
039    /**
040     * Private constructor.
041     */
042    private MethodUtils()
043    {
044    }
045
046
047    /**
048     * A replacement for {@link java.lang.Class#getMethod} with extended capability.
049     * 
050     * <p>
051     * This method returns parameter-list assignment-compatible method as well as
052     * exact-signature matching method.
053     * 
054     * @param clazz The class which will be queried for the method.
055     * @param candidateMethodName Name of the method been looked for.
056     * @param candidateParameterTypes Types of the parameters in the signature of the method being loooked for.
057     * @return The Method found.
058     * @throws NoSuchMethodException when the method cannot be found
059     */
060    public static Method getAssignmentCompatibleMethod( Class<?> clazz,
061        String candidateMethodName,
062        Class<?>[] candidateParameterTypes
063        ) throws NoSuchMethodException
064    {
065        if ( LOG.isDebugEnabled() )
066        {
067            StringBuilder buf = new StringBuilder();
068            buf.append( "call to getAssignmentCompatibleMethod(): \n\tclazz = " );
069            buf.append( clazz.getName() );
070            buf.append( "\n\tcandidateMethodName = " );
071            buf.append( candidateMethodName );
072            buf.append( "\n\tcandidateParameterTypes = " );
073
074            for ( Class<?> argClass : candidateParameterTypes )
075            {
076                buf.append( "\n\t\t" );
077                buf.append( argClass.getName() );
078            }
079
080            LOG.debug( buf.toString() );
081        }
082
083        try
084        {
085            // Look for exactly the same signature.
086            Method exactMethod = clazz.getMethod( candidateMethodName, candidateParameterTypes );
087
088            if ( exactMethod != null )
089            {
090                return exactMethod;
091            }
092        }
093        catch ( Exception e )
094        {
095            LOG.info( "Could not find accessible exact match for candidateMethod {}", candidateMethodName, e );
096        }
097
098        /**
099         * Look for the assignment-compatible signature.
100         */
101
102        // Get all methods of the class.
103        Method[] methods = clazz.getMethods();
104
105        // For each method of the class...
106        for ( int mx = 0; mx < methods.length; mx++ )
107        {
108            // If the method name does not match...
109            if ( !candidateMethodName.equals( methods[mx].getName() ) )
110            {
111                // ... Go on with the next method.
112                continue;
113            }
114
115            // ... Get parameter types list.
116            Class<?>[] parameterTypes = methods[mx].getParameterTypes();
117
118            // If parameter types list length mismatch...
119            if ( parameterTypes.length != candidateParameterTypes.length )
120            {
121                // ... Go on with the next method.
122                continue;
123            }
124            // If parameter types list length is OK...
125            // ... For each parameter of the method...
126            for ( int px = 0; px < parameterTypes.length; px++ )
127            {
128                // ... If the parameter is not assignment-compatible with the candidate parameter type...
129                if ( !parameterTypes[px].isAssignableFrom( candidateParameterTypes[px] ) )
130                {
131                    // ... Go on with the next method.
132                    break;
133                }
134            }
135
136            // Return the only one possible and found method.
137            return methods[mx];
138        }
139
140        throw new NoSuchMethodException( clazz.getName() + "." + candidateMethodName
141            + "(" + Arrays.toString( candidateParameterTypes ) + ")" );
142    }
143}