/*
 * Copyright (c) 2009 JSCAPE
 * 1147 S. 53rd Pl., Mesa, Arizona, 85206, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * JSCAPE. ("Confidential Information").  You shall not disclose such
 * Confidential Information and shall use it only in accordance with
 * the terms of the license agreement you entered into with JSCAPE.
 *
 */

package com.jscape.util.reflection;

import com.jscape.util.collections.Algorithms;
import com.jscape.util.functor.UnaryFunction;
import com.jscape.util.functor.UnaryPredicate;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Set;

import static com.jscape.util.CollectionTools.set;
import static com.jscape.util.Parameters.assertNotNull;
import static com.jscape.util.collections.Algorithms.copyIf;

/**
 * @author Alex Usun
 */
@SuppressWarnings("unchecked")
public class MethodFinder
        extends UnaryFunction<Class, Method> {

    private final UnaryPredicate<? super Method> predicate;

    public static Method find(Class aClass, UnaryPredicate<? super Method> predicate) {
        if (aClass == null) {
            return null;
        }

        Method ret = Algorithms.find(aClass.getDeclaredMethods(), predicate);

        return ret != null ? ret : find(aClass.getSuperclass(), predicate);
    }

    public static Method[] findSetters(Class aClass, UnaryPredicate<Method> predicate) {
        Collection<Method> methods = copyIf(getMethods(aClass), new MethodNameEquals("set.+").and(predicate));
        return methods.toArray(new Method[methods.size()]);
    }

    public static Method[] findGetters(Class aClass, UnaryPredicate<Method> predicate) {
        Collection<Method> methods = copyIf(getMethods(aClass), new MethodNameEquals("get.+").or(new MethodNameEquals("is.+")).and(predicate));
        return methods.toArray(new Method[methods.size()]);
    }

    private static Collection<Method> getMethods(Class aClass) {
        Set<Method> ret = set(aClass.getMethods());
        Set<Method> methods = set(aClass.getDeclaredMethods());
        ret.addAll(methods);
        return ret;
    }

    public MethodFinder(UnaryPredicate<? super Method> predicate) {
        assertNotNull(predicate);
        this.predicate = predicate;
    }

    public Method evaluate(Class value) {
        return find(value, this.predicate);
    }

    public String toString() {
        return "MethodFind{"
                + "predicate=" + this.predicate
                + '}';
    }
}