FieldAccessorFactory.java

  1. package com.gh.mygreen.xlsmapper.fieldaccessor;

  2. import java.lang.annotation.Annotation;
  3. import java.lang.reflect.Field;
  4. import java.lang.reflect.Method;
  5. import java.lang.reflect.ParameterizedType;
  6. import java.util.Collection;
  7. import java.util.Map;
  8. import java.util.Optional;

  9. import org.slf4j.Logger;
  10. import org.slf4j.LoggerFactory;

  11. import com.gh.mygreen.xlsmapper.annotation.XlsArrayCells;
  12. import com.gh.mygreen.xlsmapper.annotation.XlsArrayColumns;
  13. import com.gh.mygreen.xlsmapper.annotation.XlsIterateTables;
  14. import com.gh.mygreen.xlsmapper.annotation.XlsLabelledArrayCells;
  15. import com.gh.mygreen.xlsmapper.annotation.XlsMapColumns;
  16. import com.gh.mygreen.xlsmapper.localization.MessageBuilder;
  17. import com.gh.mygreen.xlsmapper.util.ArgUtils;
  18. import com.gh.mygreen.xlsmapper.util.ClassUtils;
  19. import com.gh.mygreen.xlsmapper.util.Utils;
  20. import com.gh.mygreen.xlsmapper.xml.AnnotationReader;

  21. /**
  22.  * {@link FieldAccessor}のインスタンスを作成するクラス。
  23.  *
  24.  * @version 2.1
  25.  * @since 2.0
  26.  * @author T.TSUCHIE
  27.  *
  28.  */
  29. public class FieldAccessorFactory {

  30.     private static Logger log = LoggerFactory.getLogger(FieldAccessorFactory.class);

  31.     private final AnnotationReader annoReader;

  32.     private PositionSetterFactory positionSetterFactory = new PositionSetterFactory();
  33.     private PositionGetterFactory positionGetterFactory = new PositionGetterFactory();
  34.     private MapPositionSetterFactory mapPositionSetterFactory = new MapPositionSetterFactory();
  35.     private ArrayPositionSetterFactory arrayPositionSetterFactory = new ArrayPositionSetterFactory();

  36.     private LabelSetterFactory labelSetterFactory = new LabelSetterFactory();
  37.     private LabelGetterFactory labelGetterFactory = new LabelGetterFactory();
  38.     private MapLabelSetterFactory mapLabelSetterFactory = new MapLabelSetterFactory();
  39.     private ArrayLabelSetterFactory arrayLabelSetterFactory = new ArrayLabelSetterFactory();
  40.    
  41.     private CommentSetterFactory commentSetterFactory = new CommentSetterFactory();
  42.     private CommentGetterFactory commentGetterFactory = new CommentGetterFactory();
  43.    
  44.     private MapCommentSetterFactory mapCommentSetterFactory = new MapCommentSetterFactory();
  45.     private MapCommentGetterFactory mapCommentGetterFactory = new MapCommentGetterFactory();
  46.    
  47.     private ArrayCommentSetterFactory arrayCommentSetterFactory = new ArrayCommentSetterFactory();
  48.     private ArrayCommentGetterFactory arrayCommentGetterFactory = new ArrayCommentGetterFactory();

  49.    
  50.     /**
  51.      * コンストラクタ
  52.      * @param annoReader XMLで定義したアノテーション情報を提供するクラス。
  53.      * @throws IllegalArgumentException {@literal annoReader == null.}
  54.      */
  55.     public FieldAccessorFactory(final AnnotationReader annoReader) {
  56.         ArgUtils.notNull(annoReader, "annoReader");

  57.         this.annoReader = annoReader;
  58.     }

  59.     /**
  60.      * フィールド情報を元にインスタンスを作成する。
  61.      * @param field フィールド
  62.      * @return フィールド情報を元に組み立てられたインスタンス。
  63.      * @throws IllegalArgumentException {@literal field == null.}
  64.      */
  65.     public FieldAccessor create(final Field field) {

  66.         ArgUtils.notNull(field, "field");

  67.         final FieldAccessor accessor = new FieldAccessor();

  68.         // 共通情報の設定
  69.         accessor.name = field.getName();
  70.         accessor.targetType = field.getType();
  71.         accessor.declaringClass = field.getDeclaringClass();

  72.         // フィールド情報の設定
  73.         setupWithFiled(accessor, field);

  74.         // getter情報の設定
  75.         if(ClassUtils.isPrimitiveBoolean(accessor.getType())) {
  76.             ClassUtils.extractBooleanGetter(accessor.getDeclaringClass(), accessor.getName())
  77.                     .ifPresent(getter -> setupWithGetter(accessor, getter));

  78.         } else {
  79.             ClassUtils.extractGetter(accessor.getDeclaringClass(), accessor.getName(), accessor.getType())
  80.                     .ifPresent(getter -> setupWithGetter(accessor, getter));
  81.         }

  82.         // setter情報の設定
  83.         ClassUtils.extractSetter(accessor.getDeclaringClass(), accessor.getName(), accessor.getType())
  84.                 .ifPresent(setter -> setupWithSetter(accessor, setter));

  85.         // コンポーネントタイプの設定
  86.         if(Collection.class.isAssignableFrom(accessor.getType())) {
  87.             ParameterizedType type = (ParameterizedType) field.getGenericType();
  88.             accessor.componentType = Optional.of((Class<?>) type.getActualTypeArguments()[0]);

  89.         } else if(accessor.getType().isArray()) {
  90.             accessor.componentType = Optional.of(accessor.getType().getComponentType());

  91.         } else if(Map.class.isAssignableFrom(accessor.getType())) {
  92.             ParameterizedType type = (ParameterizedType) field.getGenericType();
  93.             accessor.componentType = Optional.of((Class<?>) type.getActualTypeArguments()[1]);
  94.         }

  95.         // 位置・ラベル情報のアクセッサの設定
  96.         if(accessor.hasAnnotation(XlsMapColumns.class)) {
  97.             // マップ形式の場合
  98.             accessor.mapPositionSetter = mapPositionSetterFactory.create(accessor.getDeclaringClass(), accessor.getName());
  99.             accessor.mapLabelSetter = mapLabelSetterFactory.create(accessor.getDeclaringClass(), accessor.getName());
  100.            
  101.             accessor.mapCommentSetter = mapCommentSetterFactory.create(accessor.getDeclaringClass(), accessor.getName());
  102.             accessor.mapCommentGetter = mapCommentGetterFactory.create(accessor.getDeclaringClass(), accessor.getName());
  103.            

  104.         } else if(accessor.hasAnnotation(XlsArrayColumns.class)
  105.                 || accessor.hasAnnotation(XlsArrayCells.class)
  106.                 || accessor.hasAnnotation(XlsLabelledArrayCells.class)
  107.                 || accessor.hasAnnotation(XlsIterateTables.class)){
  108.             // リストや配列形式の場合
  109.             accessor.arrayPositionSetter = arrayPositionSetterFactory.create(accessor.getDeclaringClass(), accessor.getName());
  110.             accessor.arrayLabelSetter = arrayLabelSetterFactory.create(accessor.getDeclaringClass(), accessor.getName());
  111.            
  112.             accessor.arrayCommentSetter = arrayCommentSetterFactory.create(accessor.getDeclaringClass(), accessor.getName());
  113.             accessor.arrayCommentGetter = arrayCommentGetterFactory.create(accessor.getDeclaringClass(), accessor.getName());

  114.             if(accessor.hasAnnotation(XlsArrayColumns.class)
  115.                     || accessor.hasAnnotation(XlsLabelledArrayCells.class)) {
  116.                 // インデックスが付いていないラベルの設定
  117.                 accessor.labelSetter = labelSetterFactory.create(accessor.getDeclaringClass(), accessor.getName());
  118.             }

  119.         } else {
  120.             // リストやMapではない通常のプロパティの場合
  121.             accessor.positionSetter = positionSetterFactory.create(accessor.getDeclaringClass(), accessor.getName());
  122.             accessor.positionGetter = positionGetterFactory.create(accessor.getDeclaringClass(), accessor.getName());

  123.             accessor.labelSetter = labelSetterFactory.create(accessor.getDeclaringClass(), accessor.getName());
  124.             accessor.labelGetter = labelGetterFactory.create(accessor.getDeclaringClass(), accessor.getName());
  125.            
  126.             accessor.commentSetter = commentSetterFactory.create(accessor.getDeclaringClass(), accessor.getName());
  127.             accessor.commentGetter = commentGetterFactory.create(accessor.getDeclaringClass(), accessor.getName());
  128.            

  129.         }

  130.         return accessor;
  131.     }

  132.     /**
  133.      * メソッド情報を元にインスタンスを作成する。
  134.      * @param method メソッド情報
  135.      * @return メソッド情報を元に組み立てられたインスタンス。
  136.      * @throws IllegalArgumentException {@literal method == null.}
  137.      * @throws IllegalArgumentException {@literal methodの名称がsetterまたはgetterの書式でない場合。}
  138.      */
  139.     public FieldAccessor create(final Method method) {

  140.         ArgUtils.notNull(method, "method");

  141.         final FieldAccessor accessor = new FieldAccessor();

  142.         final String methodName = method.getName();
  143.         if(ClassUtils.isGetterMethod(method) || ClassUtils.isBooleanGetterMethod(method)) {
  144.             final String propertyName;
  145.             if(methodName.startsWith("get")) {
  146.                 propertyName = Utils.uncapitalize(methodName.substring(3));
  147.             } else {
  148.                 propertyName = Utils.uncapitalize(methodName.substring(2));
  149.             }

  150.             // 共通情報の設定
  151.             accessor.name = propertyName;
  152.             accessor.targetType = method.getReturnType();
  153.             accessor.declaringClass = method.getDeclaringClass();

  154.             // getter情報の設定
  155.             setupWithGetter(accessor, method);

  156.             // フィールド情報の設定
  157.             ClassUtils.extractField(accessor.getDeclaringClass(), accessor.getName(), accessor.getType())
  158.                     .ifPresent(field -> setupWithFiled(accessor, field));

  159.             // setter情報の設定
  160.             ClassUtils.extractSetter(accessor.getDeclaringClass(), accessor.getName(), accessor.getType())
  161.                     .ifPresent(setter -> setupWithSetter(accessor, setter));

  162.             // コンポーネントタイプの設定
  163.             if(Collection.class.isAssignableFrom(accessor.getType())) {
  164.                 ParameterizedType type = (ParameterizedType) method.getGenericReturnType();
  165.                 accessor.componentType = Optional.of((Class<?>) type.getActualTypeArguments()[0]);

  166.             } else if(accessor.getType().isArray()) {
  167.                 accessor.componentType = Optional.of(accessor.getType().getComponentType());

  168.             } else if(Map.class.isAssignableFrom(accessor.getType())) {
  169.                 ParameterizedType type = (ParameterizedType) method.getGenericReturnType();
  170.                 accessor.componentType = Optional.of((Class<?>) type.getActualTypeArguments()[1]);
  171.             }

  172.         } else if(ClassUtils.isSetterMethod(method)) {
  173.             final String propertyName = Utils.uncapitalize(methodName.substring(3));

  174.             // 共通情報の設定
  175.             accessor.name = propertyName;
  176.             accessor.targetType = method.getParameterTypes()[0];
  177.             accessor.declaringClass = method.getDeclaringClass();

  178.             // setter情報の設定
  179.             setupWithSetter(accessor, method);

  180.             // フィールド情報の設定
  181.             ClassUtils.extractField(accessor.getDeclaringClass(), accessor.getName(), accessor.getType())
  182.                     .ifPresent(field -> setupWithFiled(accessor, field));

  183.             // getter情報の設定
  184.             if(ClassUtils.isPrimitiveBoolean(accessor.getType())) {
  185.                 ClassUtils.extractBooleanGetter(accessor.getDeclaringClass(), accessor.getName())
  186.                         .ifPresent(getter -> setupWithGetter(accessor, getter));

  187.             } else {
  188.                 ClassUtils.extractGetter(accessor.getDeclaringClass(), accessor.getName(), accessor.getType())
  189.                         .ifPresent(getter -> setupWithGetter(accessor, getter));
  190.             }

  191.             // コンポーネントタイプの設定
  192.             if(Collection.class.isAssignableFrom(accessor.getType())) {
  193.                 ParameterizedType type = (ParameterizedType) method.getGenericParameterTypes()[0];
  194.                 accessor.componentType = Optional.of((Class<?>) type.getActualTypeArguments()[0]);

  195.             } else if(accessor.getType().isArray()) {
  196.                 accessor.componentType = Optional.of(accessor.getType().getComponentType());

  197.             } else if(Map.class.isAssignableFrom(accessor.getType())) {
  198.                 ParameterizedType type = (ParameterizedType) method.getGenericParameterTypes()[0];
  199.                 accessor.componentType = Optional.of((Class<?>) type.getActualTypeArguments()[1]);
  200.             }

  201.         } else {
  202.             throw new IllegalArgumentException(MessageBuilder.create("method.noAccessor")
  203.                     .varWithClass("className", method.getDeclaringClass())
  204.                     .var("methodName", methodName)
  205.                     .format());
  206.         }

  207.         // 位置・ラベル情報のアクセッサの設定
  208.         if(accessor.hasAnnotation(XlsMapColumns.class)) {
  209.             accessor.mapPositionSetter = mapPositionSetterFactory.create(accessor.getDeclaringClass(), accessor.getName());
  210.             accessor.mapLabelSetter = mapLabelSetterFactory.create(accessor.getDeclaringClass(), accessor.getName());

  211.         } else if(accessor.hasAnnotation(XlsArrayColumns.class)
  212.                 || accessor.hasAnnotation(XlsArrayCells.class)
  213.                 || accessor.hasAnnotation(XlsLabelledArrayCells.class)
  214.                 || accessor.hasAnnotation(XlsIterateTables.class)){
  215.             // リストや配列形式の場合
  216.             accessor.arrayPositionSetter = arrayPositionSetterFactory.create(accessor.getDeclaringClass(), accessor.getName());
  217.             accessor.arrayLabelSetter = arrayLabelSetterFactory.create(accessor.getDeclaringClass(), accessor.getName());

  218.             if(accessor.hasAnnotation(XlsArrayColumns.class)
  219.                     || accessor.hasAnnotation(XlsLabelledArrayCells.class)) {
  220.                 // インデックスが付いていないラベルの設定
  221.                 accessor.labelSetter = labelSetterFactory.create(accessor.getDeclaringClass(), accessor.getName());
  222.             }

  223.         } else {
  224.             // XlsMapColumnsを持たない通常のプロパティの場合
  225.             accessor.positionSetter = positionSetterFactory.create(accessor.getDeclaringClass(), accessor.getName());
  226.             accessor.positionGetter = positionGetterFactory.create(accessor.getDeclaringClass(), accessor.getName());

  227.             accessor.labelSetter = labelSetterFactory.create(accessor.getDeclaringClass(), accessor.getName());
  228.             accessor.labelGetter = labelGetterFactory.create(accessor.getDeclaringClass(), accessor.getName());
  229.            
  230.             accessor.commentSetter = commentSetterFactory.create(accessor.getDeclaringClass(), accessor.getName());
  231.             accessor.commentGetter = commentGetterFactory.create(accessor.getDeclaringClass(), accessor.getName());

  232.         }


  233.         return accessor;
  234.     }

  235.     private void setupWithFiled(final FieldAccessor accessor, final Field field) {

  236.         accessor.targetField = Optional.of(field);

  237.         final Annotation[] annos = annoReader.getAnnotations(field);
  238.         for(Annotation anno : annos) {
  239.             if(!isSupportedAnnotation(anno)) {
  240.                 continue;
  241.             }

  242.             final Class<? extends Annotation> annoClass = anno.annotationType();

  243.             if(accessor.annotationMap.containsKey(annoClass)) {
  244.                 final String message = MessageBuilder.create("anno.duplicated")
  245.                         .varWithClass("classType", accessor.getDeclaringClass())
  246.                         .var("property", field.getName())
  247.                         .varWithAnno("anno", annoClass)
  248.                         .format();
  249.                 log.warn(message);
  250.             }

  251.             accessor.annotationMap.put(annoClass, anno);
  252.         }


  253.     }

  254.     private void setupWithGetter(final FieldAccessor accessor, final Method method) {

  255.         accessor.targetGetter = Optional.of(method);

  256.         final Annotation[] annos = annoReader.getAnnotations(method);
  257.         for(Annotation anno : annos) {
  258.             if(!isSupportedAnnotation(anno)) {
  259.                 continue;
  260.             }

  261.             final Class<? extends Annotation> annoClass = anno.annotationType();

  262.             if(accessor.annotationMap.containsKey(annoClass)) {
  263.                 final String message = MessageBuilder.create("anno.duplicated")
  264.                         .varWithClass("classType", accessor.getDeclaringClass())
  265.                         .var("property", method.getName() + "()")
  266.                         .varWithAnno("anno", annoClass)
  267.                         .format();
  268.                 log.warn(message);
  269.             }

  270.             accessor.annotationMap.put(annoClass, anno);
  271.         }
  272.     }

  273.     private void setupWithSetter(final FieldAccessor accessor, final Method method) {

  274.         accessor.targetSetter = Optional.of(method);

  275.         final Annotation[] annos = annoReader.getAnnotations(method);
  276.         for(Annotation anno : annos) {
  277.             if(!isSupportedAnnotation(anno)) {
  278.                 continue;
  279.             }

  280.             final Class<? extends Annotation> annoClass = anno.annotationType();

  281.             if(accessor.annotationMap.containsKey(annoClass)) {
  282.                 final String message = MessageBuilder.create("anno.duplicated")
  283.                         .varWithClass("classType", accessor.getDeclaringClass())
  284.                         .var("property", method.getName() + "(...)")
  285.                         .varWithAnno("anno", annoClass)
  286.                         .format();
  287.                 log.warn(message);
  288.             }

  289.             accessor.annotationMap.put(annoClass, anno);
  290.         }
  291.     }

  292.     /**
  293.      * サポートするアノテーションか判定する。
  294.      * <p>確実に重複するJava標準のアノテーションは除外するようにします。</p>
  295.      *
  296.      * @param anno 判定対象のアノテーション
  297.      * @return tureの場合、サポートします。
  298.      */
  299.     private boolean isSupportedAnnotation(final Annotation anno) {

  300.         final String name = anno.annotationType().getName();
  301.         if(name.startsWith("java.lang.annotation.")) {
  302.             return false;
  303.         }

  304.         return true;
  305.     }

  306. }