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