ArrayCommentSetterFactory.java

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

  2. import java.lang.reflect.Field;
  3. import java.lang.reflect.InvocationTargetException;
  4. import java.lang.reflect.Method;
  5. import java.lang.reflect.ParameterizedType;
  6. import java.util.ArrayList;
  7. import java.util.LinkedHashMap;
  8. import java.util.List;
  9. import java.util.Map;
  10. import java.util.Optional;

  11. import org.slf4j.Logger;
  12. import org.slf4j.LoggerFactory;

  13. import com.gh.mygreen.xlsmapper.util.ArgUtils;
  14. import com.gh.mygreen.xlsmapper.util.Utils;

  15. /**
  16.  * {@link ArrayCommentSetter}のインスタンスを作成する
  17.  *
  18.  * @since 2.1
  19.  * @author T.TSUCHIE
  20.  *
  21.  */
  22. public class ArrayCommentSetterFactory {
  23.    
  24.     private static final Logger log = LoggerFactory.getLogger(ArrayCommentSetterFactory.class);
  25.    
  26.     /**
  27.      * フィールドの位置情報を設定するためのアクセッサを作成します。
  28.      * @param beanClass フィールドが定義されているクラス情報
  29.      * @param fieldName フィールドの名称
  30.      * @return ラベル情報のsetterが存在しない場合は空を返す。
  31.      * @throws IllegalArgumentException {@literal beanClass == null or fieldName == null}
  32.      * @throws IllegalArgumentException {@literal fieldName.isEmpty() = true}
  33.      */
  34.     public Optional<ArrayCommentSetter> create(final Class<?> beanClass, final String fieldName) {
  35.        
  36.         ArgUtils.notNull(beanClass, "beanClass");
  37.         ArgUtils.notEmpty(fieldName, "fieldName");
  38.        
  39.         // フィールド Map commentsの場合
  40.         Optional<ArrayCommentSetter> arrayCommentSetter = createMapField(beanClass, fieldName);
  41.         if(arrayCommentSetter.isPresent()) {
  42.             return arrayCommentSetter;
  43.         }
  44.        
  45.         // setter メソッドの場合
  46.         arrayCommentSetter = createMethod(beanClass, fieldName);
  47.         if(arrayCommentSetter.isPresent()) {
  48.             return arrayCommentSetter;
  49.         }
  50.        
  51.         // フィールド + commentの場合
  52.         arrayCommentSetter = createField(beanClass, fieldName);
  53.         if(arrayCommentSetter.isPresent()) {
  54.             return arrayCommentSetter;
  55.         }
  56.        
  57.        
  58.         return Optional.empty();
  59.     }
  60.    
  61.     private String createMapKey(final String fieldName, final int index) {
  62.         return String.format("%s[%d]", fieldName, index);
  63.     }
  64.    
  65.     /**
  66.      * {@link Map}フィールドにラベル情報が格納されている場合。
  67.      * <p>キーはフィールド名。</p>
  68.      *
  69.      * @param beanClass フィールドが定義してあるクラスのインスタンス
  70.      * @param fieldName フィールド名
  71.      * @return ラベル情報の設定用クラス
  72.      */
  73.     private Optional<ArrayCommentSetter> createMapField(final Class<?> beanClass, final String fieldName) {
  74.        
  75.         final Field commentsField;
  76.         try {
  77.             commentsField = beanClass.getDeclaredField("comments");
  78.             commentsField.setAccessible(true);
  79.            
  80.         } catch (NoSuchFieldException | SecurityException e) {
  81.             // フィールドが見つからない場合は、何もしない。
  82.             return Optional.empty();
  83.         }
  84.        
  85.         if(!Map.class.isAssignableFrom(commentsField.getType())) {
  86.             return Optional.empty();
  87.         }
  88.        
  89.         final ParameterizedType type = (ParameterizedType) commentsField.getGenericType();
  90.         final Class<?> keyType = (Class<?>) type.getActualTypeArguments()[0];
  91.         final Class<?> valueType = (Class<?>) type.getActualTypeArguments()[1];
  92.        
  93.         if(keyType.equals(String.class) && valueType.equals(String.class)) {
  94.             return Optional.of(new ArrayCommentSetter() {
  95.                
  96.                 @SuppressWarnings("unchecked")
  97.                 @Override
  98.                 public void set(final Object beanObj, final String comment, final int index) {
  99.                     ArgUtils.notNull(beanObj, "beanObj");
  100.                     ArgUtils.notEmpty(comment, "comment");
  101.                    
  102.                     try {
  103.                         Map<String, String> commentsMapObj = (Map<String, String>) commentsField.get(beanObj);
  104.                         if(commentsMapObj == null) {
  105.                             commentsMapObj = new LinkedHashMap<>();
  106.                             commentsField.set(beanObj, commentsMapObj);
  107.                         }
  108.                        
  109.                         final String mapKey = createMapKey(fieldName, index);
  110.                        
  111.                         commentsMapObj.put(mapKey, comment);
  112.                        
  113.                     } catch (IllegalArgumentException | IllegalAccessException e) {
  114.                         throw new RuntimeException("fail access comments field.", e);
  115.                     }
  116.                 }
  117.             });
  118.            
  119.         } else {
  120.             // タイプが一致しない場合
  121.             log.warn("not match generics type of comments. key type:{}, value type:{}.", keyType.getName(), valueType.getName());
  122.             return Optional.empty();
  123.         }
  124.        
  125.     }
  126.    
  127.     /**
  128.      * setterメソッドによるラベル情報を格納する場合。
  129.      * <p>{@code set + <フィールド名> + Comments}のメソッド名</p>
  130.      *
  131.      * @param beanClass フィールドが定義してあるクラスのインスタンス
  132.      * @param fieldName フィールド名
  133.      * @return ラベル情報の設定用クラス
  134.      */
  135.     private Optional<ArrayCommentSetter> createMethod(final Class<?> beanClass, final String fieldName) {
  136.        
  137.         final String commentMethodName = "set" + Utils.capitalize(fieldName) + "Comment";
  138.        
  139.         try {
  140.             final Method method = beanClass.getDeclaredMethod(commentMethodName, Integer.TYPE, String.class);
  141.             method.setAccessible(true);
  142.            
  143.             return Optional.of(new ArrayCommentSetter() {
  144.                
  145.                
  146.                 @Override
  147.                 public void set(final Object beanObj, final String comment, final int index) {
  148.                     ArgUtils.notNull(beanObj, "beanObj");
  149.                     ArgUtils.notEmpty(comment, "comment");
  150.                    
  151.                     try {
  152.                         method.invoke(beanObj, index, comment);
  153.                        
  154.                     } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
  155.                         throw new RuntimeException("fail access comment field.", e);
  156.                     }
  157.                    
  158.                 }
  159.             });
  160.            
  161.         } catch (NoSuchMethodException | SecurityException e) {
  162.            
  163.         }
  164.        
  165.         return Optional.empty();
  166.        
  167.     }
  168.    
  169.     /**
  170.      * フィールドによるラベル情報を格納する場合。
  171.      * <p>{@code <フィールド名> + Comment}のメソッド名</p>
  172.      *
  173.      * @param beanClass フィールドが定義してあるクラスのインスタンス
  174.      * @param fieldName フィールド名
  175.      * @return ラベル情報の設定用クラス
  176.      */
  177.     private Optional<ArrayCommentSetter> createField(final Class<?> beanClass, final String fieldName) {
  178.        
  179.         final String commentFieldName = fieldName + "Comment";
  180.        
  181.         final Field commentField;
  182.         try {
  183.             commentField = beanClass.getDeclaredField(commentFieldName);
  184.             commentField.setAccessible(true);
  185.            
  186.         } catch (NoSuchFieldException | SecurityException e) {
  187.             return Optional.empty();
  188.         }
  189.        
  190.         if(!List.class.isAssignableFrom(commentField.getType())) {
  191.             return Optional.empty();
  192.         }
  193.        
  194.         final ParameterizedType type = (ParameterizedType) commentField.getGenericType();
  195.         final Class<?> valueType = (Class<?>) type.getActualTypeArguments()[0];
  196.        
  197.         if(valueType.equals(String.class)) {
  198.            
  199.             return Optional.of(new ArrayCommentSetter() {
  200.                
  201.                 @SuppressWarnings("unchecked")
  202.                 @Override
  203.                 public void set(final Object beanObj, final String comment, final int index) {
  204.                     ArgUtils.notNull(beanObj, "beanObj");
  205.                     ArgUtils.notEmpty(comment, "comment");
  206.                    
  207.                    try {
  208.                        List<String> commentListObj = (List<String>) commentField.get(beanObj);
  209.                        if(commentListObj == null) {
  210.                            commentListObj = new ArrayList<>();
  211.                            commentField.set(beanObj, commentListObj);
  212.                        }
  213.                        
  214.                        Utils.addListWithIndex(commentListObj, comment, index);
  215.                        
  216.                     } catch (IllegalArgumentException | IllegalAccessException e) {
  217.                         throw new RuntimeException("fail access comment field.", e);
  218.                     }
  219.                 }
  220.             });
  221.            
  222.         }
  223.        
  224.         return Optional.empty();
  225.     }
  226. }