alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (AbstractFinder.java)

This example Java source code file (AbstractFinder.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

abstractfinder, ambiguous, class, executable, hashmap, nosuchmethodexception, reflection, util

The AbstractFinder.java Java example source code

/*
 * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.sun.beans.finder;

import java.lang.reflect.Executable;
import java.lang.reflect.Modifier;

import java.util.HashMap;
import java.util.Map;

/**
 * This abstract class provides functionality
 * to find a public method or constructor
 * with specified parameter types.
 * It supports a variable number of parameters.
 *
 * @since 1.7
 *
 * @author Sergey A. Malenkov
 */
abstract class AbstractFinder<T extends Executable> {
    private final Class<?>[] args;

    /**
     * Creates finder for array of classes of arguments.
     * If a particular element of array equals {@code null},
     * than the appropriate pair of classes
     * does not take into consideration.
     *
     * @param args  array of classes of arguments
     */
    protected AbstractFinder(Class<?>[] args) {
        this.args = args;
    }

    /**
     * Checks validness of the method.
     * At least the valid method should be public.
     *
     * @param method  the object that represents method
     * @return {@code true} if the method is valid,
     *         {@code false} otherwise
     */
    protected boolean isValid(T method) {
        return Modifier.isPublic(method.getModifiers());
    }

    /**
     * Performs a search in the {@code methods} array.
     * The one method is selected from the array of the valid methods.
     * The list of parameters of the selected method shows
     * the best correlation with the list of arguments
     * specified at class initialization.
     * If more than one method is both accessible and applicable
     * to a method invocation, it is necessary to choose one
     * to provide the descriptor for the run-time method dispatch.
     * The most specific method should be chosen.
     *
     * @param methods  the array of methods to search within
     * @return the object that represents found method
     * @throws NoSuchMethodException if no method was found or several
     *                               methods meet the search criteria
     * @see #isAssignable
     */
    final T find(T[] methods) throws NoSuchMethodException {
        Map<T, Class map = new HashMap[]>();

        T oldMethod = null;
        Class<?>[] oldParams = null;
        boolean ambiguous = false;

        for (T newMethod : methods) {
            if (isValid(newMethod)) {
                Class<?>[] newParams = newMethod.getParameterTypes();
                if (newParams.length == this.args.length) {
                    PrimitiveWrapperMap.replacePrimitivesWithWrappers(newParams);
                    if (isAssignable(newParams, this.args)) {
                        if (oldMethod == null) {
                            oldMethod = newMethod;
                            oldParams = newParams;
                        } else {
                            boolean useNew = isAssignable(oldParams, newParams);
                            boolean useOld = isAssignable(newParams, oldParams);

                            if (useOld && useNew) {
                                // only if parameters are equal
                                useNew = !newMethod.isSynthetic();
                                useOld = !oldMethod.isSynthetic();
                            }
                            if (useOld == useNew) {
                                ambiguous = true;
                            } else if (useNew) {
                                oldMethod = newMethod;
                                oldParams = newParams;
                                ambiguous = false;
                            }
                        }
                    }
                }
                if (newMethod.isVarArgs()) {
                    int length = newParams.length - 1;
                    if (length <= this.args.length) {
                        Class<?>[] array = new Class[this.args.length];
                        System.arraycopy(newParams, 0, array, 0, length);
                        if (length < this.args.length) {
                            Class<?> type = newParams[length].getComponentType();
                            if (type.isPrimitive()) {
                                type = PrimitiveWrapperMap.getType(type.getName());
                            }
                            for (int i = length; i < this.args.length; i++) {
                                array[i] = type;
                            }
                        }
                        map.put(newMethod, array);
                    }
                }
            }
        }
        for (T newMethod : methods) {
            Class<?>[] newParams = map.get(newMethod);
            if (newParams != null) {
                if (isAssignable(newParams, this.args)) {
                    if (oldMethod == null) {
                        oldMethod = newMethod;
                        oldParams = newParams;
                    } else {
                        boolean useNew = isAssignable(oldParams, newParams);
                        boolean useOld = isAssignable(newParams, oldParams);

                        if (useOld && useNew) {
                            // only if parameters are equal
                            useNew = !newMethod.isSynthetic();
                            useOld = !oldMethod.isSynthetic();
                        }
                        if (useOld == useNew) {
                            if (oldParams == map.get(oldMethod)) {
                                ambiguous = true;
                            }
                        } else if (useNew) {
                            oldMethod = newMethod;
                            oldParams = newParams;
                            ambiguous = false;
                        }
                    }
                }
            }
        }

        if (ambiguous) {
            throw new NoSuchMethodException("Ambiguous methods are found");
        }
        if (oldMethod == null) {
            throw new NoSuchMethodException("Method is not found");
        }
        return oldMethod;
    }

    /**
     * Determines if every class in {@code min} array is either the same as,
     * or is a superclass of, the corresponding class in {@code max} array.
     * The length of every array must equal the number of arguments.
     * This comparison is performed in the {@link #find} method
     * before the first call of the isAssignable method.
     * If an argument equals {@code null}
     * the appropriate pair of classes does not take into consideration.
     *
     * @param min  the array of classes to be checked
     * @param max  the array of classes that is used to check
     * @return {@code true} if all classes in {@code min} array
     *         are assignable from corresponding classes in {@code max} array,
     *         {@code false} otherwise
     *
     * @see Class#isAssignableFrom
     */
    private boolean isAssignable(Class<?>[] min, Class[] max) {
        for (int i = 0; i < this.args.length; i++) {
            if (null != this.args[i]) {
                if (!min[i].isAssignableFrom(max[i])) {
                    return false;
                }
            }
        }
        return true;
    }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java AbstractFinder.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.