ClassUtils.java

package com.gh.mygreen.xlsmapper.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Optional;

import com.github.mygreen.cellformatter.lang.ArgUtils;

/**
 * クラスやメソッドに関するユーティリティクラス。
 * また、リフレクションについても処理する。
 * 
 * @since 2.0
 * @author T.TSUCHIE
 *
 */
public class ClassUtils {
    
    /**
     * メソッドがアクセッサメソッド(getter/setter)か判定します。
     * 
     * @param method メソッド情報
     * @return trueの場合、アクセッサメソッド。
     */
    public static boolean isAccessorMethod(final Method method) {
        
        return isGetterMethod(method)
                || isBooleanGetterMethod(method)
                || isSetterMethod(method);
        
    }
    
    /**
     * メソッドがgetterの書式かどうか判定する。
     * ただし、boolean型にたいするisは{@link #isBooleanGetterMethod(Method)}で判定すること。
     * <ol>
     *  <li>メソッド名が'get'か始まっていること。</li>
     *  <li>メソッド名が4文字以上であること。</li>
     *  <li>引数がないこと。</li>
     *  <li>戻り値が存在すること。</li>
     * </ol>
     * @param method メソッド情報
     * @return trueの場合はgetterメソッドである。
     */
    public static boolean isGetterMethod(final Method method) {
        
        final String methodName = method.getName();
        if(!methodName.startsWith("get")) {
            return false;
            
        } else if(methodName.length() <= 3) {
            return false;
        }
        
        if(method.getParameterCount() > 0) {
            return false;
        }
        
        if(method.getReturnType().equals(Void.class)) {
            return false;
        }
        
        return true;
        
    }
    
    /**
     * メソッドがsetterの書式かどうか判定する。
     * <ol>
     *  <li>メソッド名が'set'か始まっていること。</li>
     *  <li>メソッド名が4文字以上であること。</li>
     *  <li>引数が1つのみ存在すること</li>
     *  <li>戻り値は、検証しません。</li>
     * </ol>
     * @param method メソッド情報
     * @return trueの場合はsetterメソッドである。
     */
    public static boolean isSetterMethod(final Method method) {
        
        final String methodName = method.getName();
        if(!methodName.startsWith("set")) {
            return false;
            
        } else if(methodName.length() <= 3) {
            return false;
        }
        
        if(method.getParameterCount() != 1) {
            return false;
        }
        
        return true;
        
    }
    
    /**
     * メソッドがプリミティブ型のbooleanに対するgetterの書式かどうか判定する。
     * <ol>
     *  <li>メソッド名が'is'か始まっていること。</li>
     *  <li>メソッド名が3文字以上であること。</li>
     *  <li>引数がないこと。</li>
     *  <li>戻り値がプリミティブのboolean型であること。</li>
     * </ol>
     * 
     * @param method メソッド情報
     * @return trueの場合はboolean型のgetterメソッドである。
     */
    public static boolean isBooleanGetterMethod(final Method method) {
        
        final String methodName = method.getName();
        if(!methodName.startsWith("is")) {
            return false;
            
        } else if(methodName.length() <= 2) {
            return false;
        }
        
        if(method.getParameterCount() > 0) {
            return false;
        }
        
        if(!isPrimitiveBoolean(method.getReturnType())) {
            return false;
        }
        
        return true;
    }
    
    /**
     * タイプがプリミティブのboolean型かどうか判定する。
     * @param type 判定対象のクラスタイプ。
     * @return trueの場合、プリミティブのboolean型。
     * @throws IllegalArgumentException {@literal type == null.}
     */
    public static boolean isPrimitiveBoolean(final Class<?> type) {
        ArgUtils.notNull(type, "type");
        
        if(type.isPrimitive() && boolean.class.isAssignableFrom(type)) {
            return true;
        }
        
        return false;
    }
    
    /**
     * クラスから、指定したフィールド情報を取得します。
     * @param targetClass フィールドが定義されているクラス
     * @param propertyName プロパティ名
     * @param propertyType プロパティタイプ
     * @return 存在しない場合、空を返します。
     */
    public static Optional<Field> extractField(final Class<?> targetClass, final String propertyName, final Class<?> propertyType) {
        
        final Field field;
        try {
            field = targetClass.getDeclaredField(propertyName);
        } catch (NoSuchFieldException | SecurityException e) {
            return Optional.empty();
        }
        
        field.setAccessible(true);
        
        if(!field.getType().equals(propertyType)) {
            return Optional.empty();
            
        }
        
        return Optional.of(field);
        
    }
    
    /**
     * クラスから、指定したgetterメソッド情報を取得します。
     * @param targetClass し抽出先のクラス
     * @param propertyName プロパティ名
     * @param propertyType プロパティタイプ
     * @return 存在しない場合、空を返します。
     */
    public static Optional<Method> extractGetter(final Class<?> targetClass, final String propertyName, final Class<?> propertyType) {
        
        final String methodName = "get" + Utils.capitalize(propertyName);
        
        Method method;
        try {
            method = targetClass.getMethod(methodName);
            
        } catch (NoSuchMethodException | SecurityException e) {
            return Optional.empty();
        }
        
        method.setAccessible(true);
        
        if(method.getParameterCount() > 0) {
            return Optional.empty();
        }
        
        if(!method.getReturnType().equals(propertyType)) {
            return Optional.empty();
        }
        
        return Optional.of(method);
    }
    
    /**
     * クラスから、指定したboolean型のgetterメソッド情報を取得します。
     * @param targetClass 抽出先のクラス
     * @param propertyName プロパティ名
     * @return 存在しない場合、空を返します。
     */
    public static Optional<Method> extractBooleanGetter(final Class<?> targetClass, final String propertyName) {
        
        final String methodName = "is" + Utils.capitalize(propertyName);
        
        Method method;
        try {
            method = targetClass.getMethod(methodName);
            
        } catch (NoSuchMethodException | SecurityException e) {
            return Optional.empty();
        }
        
        method.setAccessible(true);
        
        if(!method.getReturnType().equals(boolean.class)) {
            return Optional.empty();
        }
        
        return Optional.of(method);
    }
    
    /**
     * クラスから、指定したsetterメソッド情報を取得します。
     * @param targetClass 抽出先のクラス
     * @param propertyName プロパティ名
     * @param propertyType プロパティタイプ
     * @return 存在しない場合、空を返します。
     */
    public static Optional<Method> extractSetter(final Class<?> targetClass, final String propertyName, final Class<?> propertyType) {
        
        final String methodName = "set" + Utils.capitalize(propertyName);
        
        Method method;
        try {
            method = targetClass.getMethod(methodName, propertyType);
        } catch (NoSuchMethodException | SecurityException e) {
            return Optional.empty();
        }
        
        method.setAccessible(true);
        
        return Optional.of(method);
    }
    
    /**
     * アノテーションの指定した属性値を取得する。
     * <p>アノテーションの修飾子はpublicである必要があります。</p>
     * @param anno アノテーションのインスタンス
     * @param attrName 属性名
     * @param attrType 属性のタイプ。
     * @return 属性を持たない場合、空を返す。
     */
    @SuppressWarnings("unchecked")
    public static <T> Optional<T> getAnnotationAttribute(final Annotation anno, final String attrName, final Class<T> attrType) {
        
        try {
            final Method method = anno.annotationType().getMethod(attrName);
            method.setAccessible(true);
            if(!attrType.equals(method.getReturnType())) {
                return Optional.empty();
            }
            
            final Object value = method.invoke(anno);
            return Optional.of((T)value);
            
        } catch (Exception e) {
            return Optional.empty();
        }
        
    }
    
    /**
     * アノテーションの指定した属性値を持つかどうか判定する。
     * <p>アノテーションの修飾子はpublicである必要があります。</p>
     * @param anno アノテーションのインスタンス
     * @param attrName 属性名
     * @param attrType 属性のタイプ。
     * @return 属性を持つ場合trueを返す。
     */
    public static <T>  boolean hasAnnotationAttribute(final Annotation anno, final String attrName, final Class<T> attrType) {
        
        return getAnnotationAttribute(anno, attrName, attrType).isPresent();
        
    }
    
}