AptType.java

package com.github.mygreen.sqlmapper.apt.model;

import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;

import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;

import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeName;

import lombok.RequiredArgsConstructor;
import lombok.Setter;

/**
 * APTで処理する際のクラスタイプに対するユーティリティクラス。
 *
 *
 * @author T.TSUCHIE
 *
 */
@RequiredArgsConstructor
public class AptType {

    /**
     * APT上のクラスタイプ
     */
    private final TypeMirror typeMirror;

    /**
     * APTのタイプ要素
     */
    private final Optional<Element> typeElement;

    /**
     * 継承しているクラス情報
     */
    @Setter
    private List<TypeMirror> superTypes = new ArrayList<TypeMirror>();

    /**
     * 静的なクラス(static class)か判定します。
     * @return 静的なクラス(static class)のとき{@literal true}を返します。
     */
    public boolean isStaticInnerClass() {

        if(typeElement.isEmpty()) {
            return false;
        }

        final Element element = typeElement.get();

        // クラスかどうか
        if(element.getKind() != ElementKind.CLASS) {
            return false;
        }

        // staticかどうか
        if(!element.getModifiers().contains(Modifier.STATIC)) {
            return false;
        }

        return true;

    }

    /**
     * 抽象クラス(static class)か判定します。
     * @return 抽象クラスのとき{@literal true}を返します。
     */
    public boolean isAbstract() {

        if(typeElement.isEmpty()) {
            return false;
        }

        final Element element = typeElement.get();

        // クラスかどうか
        if(element.getKind() != ElementKind.CLASS) {
            return false;
        }

        // abstractかどうか
        if(!element.getModifiers().contains(Modifier.ABSTRACT)) {
            return false;
        }

        return true;
    }

    /**
     * 配列型かどうか判定します。
     * @return 配列型のとき、{@literal true} を返す。
     */
    public boolean isArray() {
        return (typeMirror instanceof ArrayType);
    }

    /**
     * 列挙型かどうか判定します。
     * @return 列挙型のとき、{@literal true} を返す。
     */
    public boolean isEnum() {

        return typeElement
                .map(t -> t.getKind())
                .map(k -> k == ElementKind.ENUM)
                .orElse(false);
    }

    /**
     * プリミティブ型かどうか判定します。
     * @return プリミティブ型のとき、{@literal true} を返す。
     */
    public boolean isPrimitive() {
        return (typeMirror instanceof PrimitiveType);
    }

    /**
     * プリミティブ型の数値かどうか判定します。
     * @return プリミティブ型の数値とき、{@literal true} を返す。
     */
    public boolean isPrimitiveNumber() {
        if(!isPrimitive()) {
            return false;
        }

        switch(typeMirror.getKind()) {
            case SHORT:
            case INT:
            case LONG:
            case FLOAT:
            case DOUBLE:
                return true;
            default:
                return false;
        }
    }

    /**
     * プリミティブ型のbooleanかどうか判定します。
     * @return プリミティブ型のbooleanとき、{@literal true} を返す。
     */
    public boolean isPrimitiveBoolean() {
        if(!isPrimitive()) {
            return false;
        }

        return typeMirror.getKind() == TypeKind.BOOLEAN;
    }

    /**
     * このクラスが指定したクラスを継承しているかどうか判定します。
     * @param targetClass 継承するクラス情報
     * @return 指定したクラスを継承しているとき、{@literal true} を返す。
     */
    public boolean isInheritanceOf(final Class<?> targetClass) {

        final String targetClassName = targetClass.getCanonicalName();

        if(getCanonicalName().equals(targetClassName)) {
            return true;
        }

        for(TypeMirror superType : superTypes) {
            if(superType.toString().equals(targetClassName)) {
                return true;
            }
        }

        return false;

    }

    /**
     * FQNのクラス名を取得します。
     * @return パッケージ名付きのクラス名
     */
    public String getCanonicalName() {
        return typeMirror.toString();
    }

    /**
     * パッケージ名を除いたクラス名を取得します。
     * @return パッケージ名を除いたクラス
     */
    public String getSimpleName() {

        String name = typeMirror.toString();
        int index = name.lastIndexOf(".");
        if(index >= 0) {
            return name.substring(index+1);
        }

        return name;
    }

    /**
     * JavaPoetの{@link ClassName}に変換します。
     * @return クラス名情報。
     * @throws NoSuchElementException クラスタイプでない場合にスローされます。
     */
    public ClassName asClassName() {
        return ClassName.get((TypeElement)typeElement.orElseThrow());
    }

    /**
     * JavaPoetのタイプ情報を取得する。
     * ただし、プリミティブ型のときはラッパークラスを取得する。
     * @return JavaPoetのタイプ情報。
     */
    public TypeName getWrapperTypeName() {

        if(isPrimitive()) {
            // プリミティブ型のときはラッパー方に変える
            return TypeName.get(typeMirror).box();
        }

        return TypeName.get(typeMirror);

    }

    /**
     * JavaPoetのタイプ情報を取得する。
     * @return JavaPoetのタイプ情報。
     */
    public TypeName getTypeName() {
        return TypeName.get(typeMirror);
    }

}