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.shared.util;
021
022import org.slf4j.Logger;
023import org.slf4j.LoggerFactory;
024
025import java.lang.reflect.Method;
026import java.util.Arrays;
027
028/**
029 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
030 */
031public final class MethodUtils
032{
033    /** The logger. */
034    private static final Logger LOG = LoggerFactory.getLogger( MethodUtils.class );
035
036
037    /**
038     * Private constructor.
039     */
040    private MethodUtils()
041    {
042    }
043
044
045    /**
046     * A replacement for {@link java.lang.Class#getMethod} with extended capability.
047     * 
048     * <p>
049     * This method returns parameter-list assignment-compatible method as well as
050     * exact-signature matching method.
051     * 
052     * @param clazz The class which will be queried for the method.
053     * @param candidateMethodName Name of the method been looked for.
054     * @param candidateParameterTypes Types of the parameters in the signature of the method being loooked for.
055     * @return The Method found.
056     * @throws NoSuchMethodException when the method cannot be found
057     */
058    public static Method getAssignmentCompatibleMethod( Class<?> clazz,
059                                                        String candidateMethodName,
060                                                        Class<?>[] candidateParameterTypes
061                                                      ) throws NoSuchMethodException
062    {
063        if ( LOG.isDebugEnabled() )
064        {
065            StringBuilder buf = new StringBuilder();
066            buf.append( "call to getAssignmentCompatibleMethod(): \n\tclazz = " );
067            buf.append( clazz.getName() );
068            buf.append( "\n\tcandidateMethodName = " );
069            buf.append( candidateMethodName );
070            buf.append( "\n\tcandidateParameterTypes = " );
071
072            for ( Class<?> argClass : candidateParameterTypes )
073            {
074                buf.append( "\n\t\t" );
075                buf.append( argClass.getName() );
076            }
077
078            LOG.debug( buf.toString() );
079        }
080
081        try
082        {
083            // Look for exactly the same signature.
084            Method exactMethod = clazz.getMethod( candidateMethodName, candidateParameterTypes );
085            
086            if ( exactMethod != null )
087            {
088                return exactMethod;
089            }
090        }
091        catch ( Exception e )
092        {
093            LOG.info( "Could not find accessible exact match for candidateMethod {}", candidateMethodName, e );
094        }
095
096
097        /**
098         * Look for the assignment-compatible signature.
099         */
100        
101        // Get all methods of the class.
102        Method[] methods = clazz.getMethods();
103        
104        // For each method of the class...
105        for ( int mx = 0; mx < methods.length; mx++ )
106        {
107            // If the method name does not match...
108            if ( !candidateMethodName.equals( methods[ mx ].getName() ) )
109            {
110                // ... Go on with the next method.
111                continue;
112            }
113            
114            // ... Get parameter types list.
115            Class<?>[] parameterTypes = methods[ mx ].getParameterTypes();
116            
117            // If parameter types list length mismatch...
118            if ( parameterTypes.length != candidateParameterTypes.length )
119            {
120                // ... Go on with the next method.
121                continue;
122            }
123            // If parameter types list length is OK...
124            // ... For each parameter of the method...
125            for ( int px = 0; px < parameterTypes.length; px++ )
126            {
127                // ... If the parameter is not assignment-compatible with the candidate parameter type...
128                if ( ! parameterTypes[ px ].isAssignableFrom( candidateParameterTypes[ px ] ) )
129                {
130                    // ... Go on with the next method.
131                    break;
132                }
133            }
134            
135            // Return the only one possible and found method.
136            return methods[ mx ];
137        }
138        
139        throw new NoSuchMethodException( clazz.getName() + "." + candidateMethodName
140            + "(" + Arrays.toString( candidateParameterTypes ) + ")" );
141    }
142}