All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gemstone.gemfire.cache.query.internal.MethodDispatch Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License. You
 * may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License. See accompanying
 * LICENSE file.
 */

package com.gemstone.gemfire.cache.query.internal;


import java.util.*;
import java.lang.reflect.*;

import com.gemstone.gemfire.cache.query.*;
import com.gemstone.gemfire.cache.query.internal.types.TypeUtils;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;


/**
 * Utility class for mapping operations in the query language to Java methods
 *
 * @author Eric Zoerner
 * @version $Revision: 1.1 $
 */

public class MethodDispatch
{
    private Class _targetClass;
    private String _methodName;
    private Class[] _argTypes;
    private Method _method; // remember the right method
    
    
    public MethodDispatch(Class targetClass, String methodName, List argTypes)
        throws NameResolutionException
    {
        _targetClass = targetClass;
        _methodName = methodName;
        _argTypes = (Class[])argTypes.toArray(new Class[argTypes.size()]);

        resolve();
            // override security in case this is a method on a nonpublic class
            // with a public method
        _method.setAccessible(true);
    }

    

    public Object invoke(Object target, List args)
        throws NameNotFoundException, QueryInvocationTargetException
    {
        Object[] argsArray = args.toArray();
        
       
        try
        {
            return _method.invoke(target, argsArray);
        }
        catch (IllegalAccessException e)
        {
            throw new NameNotFoundException(LocalizedStrings.MethodDispatch_METHOD_0_IN_CLASS_1_IS_NOT_ACCESSIBLE_TO_THE_QUERY_PROCESSOR.toLocalizedString(new Object[] {_method.getName(), target.getClass().getName()}), e);
        }
        catch(InvocationTargetException e)
        {
                // if targetException is Exception, wrap it, otherwise wrap the InvocationTargetException itself
            Throwable t = e.getTargetException();
            if (t instanceof Exception)
                throw new QueryInvocationTargetException(t);
            throw new QueryInvocationTargetException(e);
        }
    }
    
    
    private void resolve()
        throws NameResolutionException
    {
            // if argTypes contains a null, then go directly to resolveGeneral(),
            // otherwise try to resolve on the specific types first
            // (a null type gets passed in if the runtime value of the arg is null)
        for (int i = 0; i < _argTypes.length; i++)
            if (_argTypes[i] == null)
            {
                resolveGeneral();
                return;
            }
        
            // first try to get the method based on the exact parameter types
        try
        {
            _method = _targetClass.getMethod(_methodName, _argTypes);
        }
        catch (NoSuchMethodException e)
        {
            resolveGeneral();
        }
        
        
    }


    private void resolveGeneral()
        throws NameResolutionException
    {
        Method[] allMethods = _targetClass.getMethods();
            // keep only ones whose method names match and have the same number of args
        List candidates = new ArrayList();
        for (int i = 0; i < allMethods.length; i++)
        {
            Method meth = allMethods[i];
                /*if (Modifier.isStatic(meth.getModifiers()))
                  continue;*/
            if (!meth.getName().equals(_methodName))
                continue;
            if (meth.getParameterTypes().length != _argTypes.length)
                continue;
                // are the args all convertible to the parameter types?
            if (!TypeUtils.areTypesConvertible(_argTypes, meth.getParameterTypes()))
                continue;
            candidates.add(meth);
        }

        
        if (candidates.isEmpty())
        {
            throw new NameNotFoundException(LocalizedStrings.MethodDispatch_NO_APPLICABLE_AND_ACCESSIBLE_METHOD_NAMED_0_WAS_FOUND_IN_CLASS_1_FOR_THE_ARGUMENT_TYPES_2.toLocalizedString(new Object[] {_methodName, _targetClass.getName(), Arrays.asList(_argTypes)}));
        }
        

            // now we have a list of accessible and applicable method,
            // choose the most specific
        if (candidates.size() == 1)
        {
            _method = (Method)candidates.get(0);
            return;
        }

        
        sortByDecreasingSpecificity(candidates);
            // get the first two methods in the sorted list,
            // if they are equally specific, then throw AmbiguousMethodException
        Method meth1 = (Method)candidates.get(0);
        Method meth2 = (Method)candidates.get(1);
            // if meth1 cannot be type-converted to meth2, then meth1 is not more
            // specific than meth2 and the invocation is ambiguous.
            // special case a null argument type in this case, since there should
            // be not differentiation for those parameter types regarding specificity


        if (equalSpecificity(meth1, meth2, _argTypes))
            throw new AmbiguousNameException(LocalizedStrings.MethodDispatch_TWO_OR_MORE_MAXIMALLY_SPECIFIC_METHODS_WERE_FOUND_FOR_THE_METHOD_NAMED_0_IN_CLASS_1_FOR_THE_ARGUMENT_TYPES_2.toLocalizedString(new Object[] {meth1.getName(), _targetClass.getName(), Arrays.asList(_argTypes)}));

        _method = meth1;
    }
    
    
    
    private void sortByDecreasingSpecificity(List methods)
    {
        Collections.sort(methods, new Comparator() 
            {
                public int compare(Object o1, Object o2)
                {
                    Method m1 = (Method)o1;
                    Method m2 = (Method)o2;
                    if (m1.equals(m2))
                        return 0;
                    
                    boolean convertible1 = methodConvertible(m1, m2);
                    boolean convertible2 = methodConvertible(m2, m1);
                        // check to see if they are convertible both ways or neither way
                    if (convertible1 == convertible2)
                        return 0;
                    return convertible1 ? -1 : 1;
                }
            });
    }

    protected boolean methodConvertible(Method m1, Method m2)
    {
        boolean declaringClassesConvertible =
            TypeUtils.isTypeConvertible(m1.getDeclaringClass(),
                                        m2.getDeclaringClass());
        
        boolean paramsConvertible = true;
        Class[] p1 = m1.getParameterTypes();
        Class[] p2 = m2.getParameterTypes();
        for (int i = 0; i < p1.length; i++)
        {
            if (!TypeUtils.isTypeConvertible(p1[i], p2[i]))
            {
                paramsConvertible = false;
                break;
            }
        }
        return declaringClassesConvertible && paramsConvertible;
    }

    private boolean equalSpecificity(Method m1, Method m2, Class[] argTypes)
    {
            // if the m1 is not convertible to m2, then definitely equal specificity,
            // since this would be ambiguous even in Java
        if (!methodConvertible(m1, m2))
            return true;
        
        
            // if there is at least one param type that is more specific
            // ignoring parameters with a null argument, then
            // answer false, otherwise true.

        Class[] p1 = m1.getParameterTypes();
        Class[] p2 = m2.getParameterTypes();
        for (int i = 0; i < p1.length; i++)
        {
            if (argTypes[i] != null &&
                p1[i] != p2[i] &&
                TypeUtils.isTypeConvertible(p1[i], p2[i])) // assumes m1 is <= m2 in specificity
                return false;
        }
        return true;
    }
  
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy