PropertyBase.java

package com.github.mygreen.sqlmapper.core.meta;

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

import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;

/**
 * JavaBeanのプロパティの基本的な機能を提供します。
 *
 * @since 0.3
 * @author T.TSUCHIE
 *
 */
@RequiredArgsConstructor
public abstract class PropertyBase {

    /**
     * プロパティ名
     */
    @Getter
    protected final String name;

    /**
     * プロパティタイプ
     */
    @Getter
    protected final Class<?> propertyType;

    /**
     * フィールド情報
     */
    protected Optional<Field> field = Optional.empty();

    /**
     * setterメソッド
     */
    protected Optional<Method> writeMethod = Optional.empty();

    /**
     * getterメソッド
     */
    protected Optional<Method> readMethod = Optional.empty();

    /**
     * アノテーションの情報
     */
    protected Map<Class<? extends Annotation>, Annotation> annotationMap = new HashMap<>();

    /**
     * プロパティが定義されているクラス情報を取得します。
     * @return プロパティが定義されているクラス情報
     * @throws IllegalStateException クラス情報を取得するための情報が不足している場合。
     */
    public Class<?> getDeclaringClass() {

        if(field.isPresent()) {
            return field.get().getDeclaringClass();
        }

        if(readMethod.isPresent()) {
            return readMethod.get().getDeclaringClass();
        }

        if(writeMethod.isPresent()) {
            return writeMethod.get().getDeclaringClass();
        }

        throw new IllegalStateException("not found availabeld info.");

    }

    /**
     * 読み込み可能なプロパティか判定する。
     * <p>getterメソッドまたはpublicなフィールドが存在する場合</p>
     * @return {@literal true}のとき読み込み可能。
     */
    public boolean isReadable() {

        if(readMethod.isPresent()) {
            return true;
        }

        if(field.isPresent()) {
            if(Modifier.isPublic(field.get().getModifiers())) {
                return true;
            }
        }

        return false;

    }

    /**
     * 書込み可能なプロパティか判定する。
     * <p>setterメソッドまたはpublicなフィールドが存在する場合</p>
     * @return {@literal true}のと書き込み可能。
     */
    public boolean isWritable() {

        if(writeMethod.isPresent()) {
            return true;
        }

        if(field.isPresent()) {
            if(Modifier.isPublic(field.get().getModifiers())) {
                return true;
            }
        }

        return false;

    }

    /**
     * プロパティに対するフィールドを設定します。
     * @param field フィールド(nullを許容します)
     */
    public void setField(Field field) {
        this.field = Optional.ofNullable(field);
    }

    /**
     * プロパティに対するフィールドを情報を取得します。
     * @return プロパティに対するフィールド情報
     */
    public Optional<Field> getField() {
        return field;
    }

    /**
     * プロパティに対するsetterメソッドを設定します。
     * @param method setterメソッド(nullを許容します)
     */
    public void setWriteMethod(Method method) {
        this.writeMethod = Optional.ofNullable(method);
    }

    /**
     * プロパティに対するsetterメソッドを取得します。
     * @return プロパティに対するsetterメソッド。
     */
    public Optional<Method> getWriteMethod() {
        return writeMethod;
    }

    /**
     * プロパティに対するgetterメソッドを設定します。
     * @param method getterメソッド(nullを許容します)
     */
    public void setReadMethod(Method method) {
        this.readMethod = Optional.ofNullable(method);
    }

    /**
     * プロパティに対するgetterメソッドを取得します。
     * @return プロパティに対するgetterメソッド
     */
    public Optional<Method> getReadMethod() {
        return readMethod;
    }

    /**
     * アノテーションを追加します。
     * @param annoClass アノテーションのタイプ
     * @param anno 追加するアノテーション
     */
    public void addAnnotation(@NonNull Class<? extends Annotation> annoClass, @NonNull Annotation anno) {
        this.annotationMap.put(annoClass, anno);
    }

    /**
     * 指定したアノテーションを持つか判定します。
     * @param <A> アノテーションのタイプ。
     * @param annoClass アノテーションのクラスタイプ。
     * @return trueの場合、アノテーションを持ちます。
     */
    public <A extends Annotation> boolean hasAnnotation(@NonNull Class<A> annoClass) {
        return annotationMap.containsKey(annoClass);
    }

    /**
     * タイプを指定して、アノテーションを取得する。
     * @param <A> アノテーションのタイプ。
     * @param annoClass アノテーションのクラスタイプ。
     * @return 存在しない場合、空を返します。
     */
    @SuppressWarnings("unchecked")
    public <A extends Annotation> Optional<A> getAnnotation(Class<A> annoClass) {
        return Optional.ofNullable((A)annotationMap.get(annoClass));
    }
}