/*
 * Copyright 2011-2015 B2i Healthcare Pte Ltd, http://b2i.sg
 * 
 * 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.
 */
package com.b2international.commons;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;

/**
 * Collection of utility functions related to classes and interfaces.
 *  
 */
public class ClassUtils {
	
	/**
	 * Returns true if the class with the specified name is either the same as, or is a superclass or 
	 * superinterface of, the class represented by the specified class parameter.
	 * 
	 * @param clazz the class to test
	 * @param className the class name to test against
	 * @return 	true if the class with the specified name is either the same as, or is a superclass or 
	 * 			superinterface of, the class represented by the specified class parameter, false otherwise
	 */
	public static boolean isClassAssignableFrom(Class<?> clazz, String className) {
		boolean match = false;
		while (clazz != null) {
			if (clazz.getName().equals(className)) {
				return true;
			}
			Class<?>[] interfaces = clazz.getInterfaces();
			for (int i = 0; i < interfaces.length; i++) {
				if (isClassAssignableFrom(interfaces[i], className)) {
					return true;
				}
			}
			clazz = clazz.getSuperclass();
		}
		return match;
	}
	
	public static <T> T checkAndCast(final Object object, final Class<T> targetClass) {
		checkNotNull(object, "Object argument cannot be null.");
		checkNotNull(targetClass, "Target class argument cannot be null.");
		checkArgument(targetClass.isInstance(object), "Object must be an instance of %s but was a %s.", targetClass.getSimpleName(), object.getClass().getSimpleName());
		return targetClass.cast(object);
	}
	
    /**
     * Finds the type parameter for the given class.
     *
     * @param klass    a parameterized class
     * @return the class's type parameter
     */
    public static Class<?> getTypeParameter(Class<?> klass) {
        return getTypeParameter(klass, Object.class);
    }

    /**
     * Finds the type parameter for the given class which is assignable to the bound class.
     *
     * @param klass    a parameterized class
     * @param bound    the type bound
     * @param <T>      the type bound
     * @return the class's type parameter
     */
    public static <T> Class<T> getTypeParameter(Class<?> klass, Class<? super T> bound) {
        Type t = checkNotNull(klass);
        while (t instanceof Class<?>) {
            t = ((Class<?>) t).getGenericSuperclass();
        }
        /* This is not guaranteed to work for all cases with convoluted piping
         * of type parameters: but it can at least resolve straight-forward
         * extension with single type parameter.
         * And when it fails to do that, will indicate with specific exception.
         */
        if (t instanceof ParameterizedType) {
            // should typically have one of type parameters (first one) that matches:
            for (Type param : ((ParameterizedType) t).getActualTypeArguments()) {
                if (param instanceof Class<?>) {
                    final Class<T> cls = determineClass(bound, param);
                    if (cls != null) { return cls; }
                }
                else if (param instanceof TypeVariable) {
                    for (Type paramBound : ((TypeVariable<?>) param).getBounds()) {
                        if (paramBound instanceof Class<?>) {
                            final Class<T> cls = determineClass(bound, paramBound);
                            if (cls != null) { return cls; }
                        }
                    }
                }
            }
        }
        throw new IllegalStateException("Cannot figure out type parameterization for " + klass.getName());
    }

    @SuppressWarnings("unchecked")
    private static <T> Class<T> determineClass(Class<? super T> bound, Type candidate) {
        if (candidate instanceof Class<?>) {
            final Class<?> cls = (Class<?>) candidate;
            if (bound.isAssignableFrom(cls)) {
                return (Class<T>) cls;
            }
        }
        return null;
    }
}
@@ -795,123 +795,8 @@
 l;%0A%0A
-import java.lang.reflect.ParameterizedType;%0Aimport java.lang.reflect.Type;%0Aimport java.lang.reflect.TypeVariable;%0A%0A
 /**%0A
@@ -2247,2417 +2247,5 @@
 %7D%0A%09%0A
-    /**%0A     * Finds the type parameter for the given class.%0A     *%0A     * @param klass    a parameterized class%0A     * @return the class's type parameter%0A     */%0A    public static Class%3C?%3E getTypeParameter(Class%3C?%3E klass) %7B%0A        return getTypeParameter(klass, Object.class);%0A    %7D%0A%0A    /**%0A     * Finds the type parameter for the given class which is assignable to the bound class.%0A     *%0A     * @param klass    a parameterized class%0A     * @param bound    the type bound%0A     * @param %3CT%3E      the type bound%0A     * @return the class's type parameter%0A     */%0A    public static %3CT%3E Class%3CT%3E getTypeParameter(Class%3C?%3E klass, Class%3C? super T%3E bound) %7B%0A        Type t = checkNotNull(klass);%0A        while (t instanceof Class%3C?%3E) %7B%0A            t = ((Class%3C?%3E) t).getGenericSuperclass();%0A        %7D%0A        /* This is not guaranteed to work for all cases with convoluted piping%0A         * of type parameters: but it can at least resolve straight-forward%0A         * extension with single type parameter.%0A         * And when it fails to do that, will indicate with specific exception.%0A         */%0A        if (t instanceof ParameterizedType) %7B%0A            // should typically have one of type parameters (first one) that matches:%0A            for (Type param : ((ParameterizedType) t).getActualTypeArguments()) %7B%0A                if (param instanceof Class%3C?%3E) %7B%0A                    final Class%3CT%3E cls = determineClass(bound, param);%0A                    if (cls != null) %7B return cls; %7D%0A                %7D%0A                else if (param instanceof TypeVariable) %7B%0A                    for (Type paramBound : ((TypeVariable%3C?%3E) param).getBounds()) %7B%0A                        if (paramBound instanceof Class%3C?%3E) %7B%0A                            final Class%3CT%3E cls = determineClass(bound, paramBound);%0A                            if (cls != null) %7B return cls; %7D%0A                        %7D%0A                    %7D%0A                %7D%0A            %7D%0A        %7D%0A        throw new IllegalStateException(%22Cannot figure out type parameterization for %22 + klass.getName());%0A    %7D%0A%0A    @SuppressWarnings(%22unchecked%22)%0A    private static %3CT%3E Class%3CT%3E determineClass(Class%3C? super T%3E bound, Type candidate) %7B%0A        if (candidate instanceof Class%3C?%3E) %7B%0A            final Class%3C?%3E cls = (Class%3C?%3E) candidate;%0A            if (bound.isAssignableFrom(cls)) %7B%0A                return (Class%3CT%3E) cls;%0A            %7D%0A        %7D%0A        return null;%0A    %7D%0A
 %7D

remove unused ClassUtils.getTypeParameter() methods