ArrayPositionSetterFactory.java

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

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

  12. import org.apache.poi.ss.util.CellAddress;
  13. import org.slf4j.Logger;
  14. import org.slf4j.LoggerFactory;

  15. import com.gh.mygreen.xlsmapper.util.ArgUtils;
  16. import com.gh.mygreen.xlsmapper.util.CellPosition;
  17. import com.gh.mygreen.xlsmapper.util.Utils;

  18. /**
  19.  * {@link ArrayPositionSetter}のインスタンスを作成する
  20.  *
  21.  * @since 2.0
  22.  * @author T.TSUCHIE
  23.  *
  24.  */
  25. public class ArrayPositionSetterFactory {
  26.    
  27.     private static final Logger log = LoggerFactory.getLogger(ArrayPositionSetterFactory.class);
  28.    
  29.     /**
  30.      * フィールドの位置情報を設定するためのアクセッサを作成します。
  31.      * @param beanClass フィールドが定義されているクラス情報
  32.      * @param fieldName フィールドの名称
  33.      * @return 位置情報のsetterが存在しない場合は空を返す。
  34.      * @throws IllegalArgumentException {@literal beanClass == null or fieldName == null}
  35.      * @throws IllegalArgumentException {@literal fieldName.isEmpty() = true}
  36.      */
  37.     public Optional<ArrayPositionSetter> create(final Class<?> beanClass, final String fieldName) {
  38.        
  39.         ArgUtils.notNull(beanClass, "beanClass");
  40.         ArgUtils.notEmpty(fieldName, "fieldName");
  41.        
  42.         // フィールド Map positionsの場合
  43.         Optional<ArrayPositionSetter> arrayPositionSetter = createMapField(beanClass, fieldName);
  44.         if(arrayPositionSetter.isPresent()) {
  45.             return arrayPositionSetter;
  46.         }
  47.        
  48.         // setter メソッドの場合
  49.         arrayPositionSetter = createMethod(beanClass, fieldName);
  50.         if(arrayPositionSetter.isPresent()) {
  51.             return arrayPositionSetter;
  52.         }
  53.        
  54.         // フィールド + positionの場合
  55.         arrayPositionSetter = createField(beanClass, fieldName);
  56.         if(arrayPositionSetter.isPresent()) {
  57.             return arrayPositionSetter;
  58.         }
  59.        
  60.        
  61.         return Optional.empty();
  62.     }
  63.    
  64.     private String createMapKey(final String fieldName, final int index) {
  65.         return String.format("%s[%d]", fieldName, index);
  66.     }
  67.    
  68.     /**
  69.      * {@link Map}フィールドに位置情報が格納されている場合。
  70.      * <p>キーはフィールド名。</p>
  71.      * <p>マップの値は、{@link CellPosition}、{@link Point}、{@link CellAddress}をサポートする。</p>
  72.      *
  73.      * @param beanClass フィールドが定義してあるクラスのインスタンス
  74.      * @param fieldName フィールド名
  75.      * @return 位置情報の設定用クラス
  76.      */
  77.     private Optional<ArrayPositionSetter> createMapField(final Class<?> beanClass, final String fieldName) {
  78.        
  79.         final Field positionsField;
  80.         try {
  81.             positionsField = beanClass.getDeclaredField("positions");
  82.             positionsField.setAccessible(true);
  83.            
  84.         } catch (NoSuchFieldException | SecurityException e) {
  85.             // フィールドが見つからない場合は、何もしない。
  86.             return Optional.empty();
  87.         }
  88.        
  89.         if(!Map.class.isAssignableFrom(positionsField.getType())) {
  90.             return Optional.empty();
  91.         }
  92.        
  93.         final ParameterizedType type = (ParameterizedType) positionsField.getGenericType();
  94.         final Class<?> keyType = (Class<?>) type.getActualTypeArguments()[0];
  95.         final Class<?> valueType = (Class<?>) type.getActualTypeArguments()[1];
  96.        
  97.         if(keyType.equals(String.class) && valueType.equals(CellPosition.class)) {
  98.             return Optional.of(new ArrayPositionSetter() {
  99.                
  100.                 @SuppressWarnings("unchecked")
  101.                 @Override
  102.                 public void set(final Object beanObj, final CellPosition position, final int index) {
  103.                     ArgUtils.notNull(beanObj, "beanObj");
  104.                     ArgUtils.notNull(position, "position");
  105.                    
  106.                     try {
  107.                         Map<String, CellPosition> positionsMapObj = (Map<String, CellPosition>) positionsField.get(beanObj);
  108.                         if(positionsMapObj == null) {
  109.                             positionsMapObj = new LinkedHashMap<>();
  110.                             positionsField.set(beanObj, positionsMapObj);
  111.                         }
  112.                        
  113.                         final String mapKey = createMapKey(fieldName, index);
  114.                        
  115.                         positionsMapObj.put(mapKey, position);
  116.                        
  117.                     } catch (IllegalArgumentException | IllegalAccessException e) {
  118.                         throw new RuntimeException("fail access positions field.", e);
  119.                     }
  120.                 }
  121.             });
  122.            
  123.         } else if(keyType.equals(String.class) && valueType.equals(Point.class)) {
  124.            
  125.             return Optional.of(new ArrayPositionSetter() {
  126.                
  127.                 @SuppressWarnings("unchecked")
  128.                 @Override
  129.                 public void set(final Object beanObj, final CellPosition position, final int index) {
  130.                     ArgUtils.notNull(beanObj, "beanObj");
  131.                     ArgUtils.notNull(position, "position");
  132.                    
  133.                     try {
  134.                         Map<String, Point> positionsMapObj = (Map<String, Point>) positionsField.get(beanObj);
  135.                         if(positionsMapObj == null) {
  136.                             positionsMapObj = new LinkedHashMap<>();
  137.                             positionsField.set(beanObj, positionsMapObj);
  138.                         }
  139.                        
  140.                         final String mapKey = createMapKey(fieldName, index);
  141.                         positionsMapObj.put(mapKey, position.toPoint());
  142.                        
  143.                     } catch (IllegalArgumentException | IllegalAccessException e) {
  144.                         throw new RuntimeException("fail access positions field.", e);
  145.                     }
  146.                 }
  147.             });
  148.            
  149.            
  150.         } else if(keyType.equals(String.class) && valueType.equals(CellAddress.class)) {
  151.            
  152.             return Optional.of(new ArrayPositionSetter() {
  153.                
  154.                 @SuppressWarnings("unchecked")
  155.                 @Override
  156.                 public void set(final Object beanObj, final CellPosition position, final int index) {
  157.                     ArgUtils.notNull(beanObj, "beanObj");
  158.                     ArgUtils.notNull(position, "position");
  159.                    
  160.                     try {
  161.                         Map<String, CellAddress> positionsMapObj = (Map<String, CellAddress>) positionsField.get(beanObj);
  162.                         if(positionsMapObj == null) {
  163.                             positionsMapObj = new LinkedHashMap<>();
  164.                             positionsField.set(beanObj, positionsMapObj);
  165.                         }
  166.                        
  167.                         final String mapKey = createMapKey(fieldName, index);
  168.                         positionsMapObj.put(mapKey, position.toCellAddress());
  169.                        
  170.                     } catch (IllegalArgumentException | IllegalAccessException e) {
  171.                         throw new RuntimeException("fail access positions field.", e);
  172.                     }
  173.                 }
  174.             });
  175.            
  176.            
  177.         } else {
  178.             // タイプが一致しない場合
  179.             log.warn("not match generics type of positions. key type:{}, value type:{}.", keyType.getName(), valueType.getName());
  180.             return Optional.empty();
  181.         }
  182.        
  183.     }
  184.    
  185.     /**
  186.      * setterメソッドによる位置情報を格納する場合。
  187.      * <p>{@code set + <フィールド名> + Position}のメソッド名</p>
  188.      * <p>引数として、{@link CellPosition}、{@link Point}、{@code int(列番号), int(行番号)}、 {@link CellAddress}をサポートする。</p>
  189.      *
  190.      * @param beanClass フィールドが定義してあるクラスのインスタンス
  191.      * @param fieldName フィールド名
  192.      * @return 位置情報の設定用クラス
  193.      */
  194.     private Optional<ArrayPositionSetter> createMethod(final Class<?> beanClass, final String fieldName) {
  195.        
  196.         final String positionMethodName = "set" + Utils.capitalize(fieldName) + "Position";
  197.        
  198.         try {
  199.             final Method method = beanClass.getDeclaredMethod(positionMethodName, Integer.TYPE, CellPosition.class);
  200.             method.setAccessible(true);
  201.            
  202.             return Optional.of(new ArrayPositionSetter() {
  203.                
  204.                
  205.                 @Override
  206.                 public void set(final Object beanObj, final CellPosition position, final int index) {
  207.                     ArgUtils.notNull(beanObj, "beanObj");
  208.                     ArgUtils.notNull(position, "position");
  209.                    
  210.                     try {
  211.                         method.invoke(beanObj, index, position);
  212.                        
  213.                     } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
  214.                         throw new RuntimeException("fail access position field.", e);
  215.                     }
  216.                    
  217.                 }
  218.             });
  219.            
  220.         } catch (NoSuchMethodException | SecurityException e) {
  221.            
  222.         }
  223.        
  224.         try {
  225.             final Method method = beanClass.getDeclaredMethod(positionMethodName, Integer.TYPE, Point.class);
  226.             method.setAccessible(true);
  227.            
  228.             return Optional.of(new ArrayPositionSetter() {
  229.                
  230.                
  231.                 @Override
  232.                 public void set(final Object beanObj, final CellPosition position, final int index) {
  233.                     ArgUtils.notNull(beanObj, "beanObj");
  234.                     ArgUtils.notNull(position, "position");
  235.                    
  236.                     try {
  237.                         method.invoke(beanObj, index, position.toPoint());
  238.                        
  239.                     } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
  240.                         throw new RuntimeException("fail access position field.", e);
  241.                     }
  242.                    
  243.                 }
  244.             });
  245.            
  246.         } catch (NoSuchMethodException | SecurityException e) {
  247.            
  248.         }
  249.        
  250.         try {
  251.             final Method method = beanClass.getDeclaredMethod(positionMethodName, Integer.TYPE, CellAddress.class);
  252.             method.setAccessible(true);
  253.            
  254.             return Optional.of(new ArrayPositionSetter() {
  255.                
  256.                
  257.                 @Override
  258.                 public void set(final Object beanObj, final CellPosition position, final int index) {
  259.                     ArgUtils.notNull(beanObj, "beanObj");
  260.                     ArgUtils.notNull(position, "position");
  261.                    
  262.                     try {
  263.                         method.invoke(beanObj, index, position.toCellAddress());
  264.                        
  265.                     } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
  266.                         throw new RuntimeException("fail access position field.", e);
  267.                     }
  268.                    
  269.                 }
  270.             });
  271.            
  272.         } catch (NoSuchMethodException | SecurityException e) {
  273.            
  274.         }
  275.        
  276.         try {
  277.             final Method method = beanClass.getDeclaredMethod(positionMethodName, Integer.TYPE, Integer.TYPE, Integer.TYPE);
  278.             method.setAccessible(true);
  279.            
  280.             return Optional.of(new ArrayPositionSetter() {
  281.                
  282.                
  283.                 @Override
  284.                 public void set(final Object beanObj, final CellPosition position, final int index) {
  285.                     ArgUtils.notNull(beanObj, "beanObj");
  286.                     ArgUtils.notNull(position, "position");
  287.                    
  288.                     try {
  289.                         method.invoke(beanObj, index, position.getColumn(), position.getRow());
  290.                        
  291.                     } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
  292.                         throw new RuntimeException("fail access position field.", e);
  293.                     }
  294.                    
  295.                 }
  296.             });
  297.            
  298.         } catch (NoSuchMethodException | SecurityException e) {
  299.            
  300.         }
  301.        
  302.         return Optional.empty();
  303.        
  304.     }
  305.    
  306.     /**
  307.      * フィールドによる位置情報を格納する場合。
  308.      * <p>{@code <フィールド名> + Position}のメソッド名</p>
  309.      * <p>引数として、{@link CellPosition}、{@link Point}、 {@link CellAddress}をサポートする。</p>
  310.      *
  311.      * @param beanClass フィールドが定義してあるクラスのインスタンス
  312.      * @param fieldName フィールド名
  313.      * @return 位置情報の設定用クラス
  314.      */
  315.     private Optional<ArrayPositionSetter> createField(final Class<?> beanClass, final String fieldName) {
  316.        
  317.         final String positionFieldName = fieldName + "Position";
  318.        
  319.         final Field positionField;
  320.         try {
  321.             positionField = beanClass.getDeclaredField(positionFieldName);
  322.             positionField.setAccessible(true);
  323.            
  324.         } catch (NoSuchFieldException | SecurityException e) {
  325.             return Optional.empty();
  326.         }
  327.        
  328.         if(!List.class.isAssignableFrom(positionField.getType())) {
  329.             return Optional.empty();
  330.         }
  331.        
  332.         final ParameterizedType type = (ParameterizedType) positionField.getGenericType();
  333.         final Class<?> valueType = (Class<?>) type.getActualTypeArguments()[0];
  334.        
  335.         if(valueType.equals(CellPosition.class)) {
  336.            
  337.             return Optional.of(new ArrayPositionSetter() {
  338.                
  339.                 @SuppressWarnings("unchecked")
  340.                 @Override
  341.                 public void set(final Object beanObj, final CellPosition position, final int index) {
  342.                     ArgUtils.notNull(beanObj, "beanObj");
  343.                     ArgUtils.notNull(position, "position");
  344.                    
  345.                    try {
  346.                        List<CellPosition> positionListObj = (List<CellPosition>) positionField.get(beanObj);
  347.                        if(positionListObj == null) {
  348.                            positionListObj = new ArrayList<>();
  349.                            positionField.set(beanObj, positionListObj);
  350.                        }
  351.                        
  352.                        Utils.addListWithIndex(positionListObj, position, index);
  353.                        
  354.                     } catch (IllegalArgumentException | IllegalAccessException e) {
  355.                         throw new RuntimeException("fail access position field.", e);
  356.                     }
  357.                 }
  358.             });
  359.            
  360.         } else if(valueType.equals(Point.class)) {
  361.            
  362.             return Optional.of(new ArrayPositionSetter() {
  363.                
  364.                 @SuppressWarnings("unchecked")
  365.                 @Override
  366.                 public void set(final Object beanObj, final CellPosition position, final int index) {
  367.                     ArgUtils.notNull(beanObj, "beanObj");
  368.                     ArgUtils.notNull(position, "position");
  369.                    
  370.                     try {
  371.                         List<Point> positionListObj = (List<Point>) positionField.get(beanObj);
  372.                         if(positionListObj == null) {
  373.                             positionListObj = new ArrayList<>();
  374.                             positionField.set(beanObj, positionListObj);
  375.                         }
  376.                        
  377.                        
  378.                         Utils.addListWithIndex(positionListObj, position.toPoint(), index);
  379.                        
  380.                     } catch (IllegalArgumentException | IllegalAccessException e) {
  381.                         throw new RuntimeException("fail access position field.", e);
  382.                     }
  383.                 }
  384.             });
  385.            
  386.         } else if(valueType.equals(CellAddress.class)) {
  387.            
  388.             return Optional.of(new ArrayPositionSetter() {
  389.                
  390.                 @SuppressWarnings("unchecked")
  391.                 @Override
  392.                 public void set(final Object beanObj, final CellPosition position, final int index) {
  393.                     ArgUtils.notNull(beanObj, "beanObj");
  394.                     ArgUtils.notNull(position, "position");
  395.                    
  396.                     try {
  397.                         List<CellAddress> positionListObj = (List<CellAddress>) positionField.get(beanObj);
  398.                         if(positionListObj == null) {
  399.                             positionListObj = new ArrayList<>();
  400.                             positionField.set(beanObj, positionListObj);
  401.                         }
  402.                        
  403.                         Utils.addListWithIndex(positionListObj, position.toCellAddress(), index);
  404.                        
  405.                     } catch (IllegalArgumentException | IllegalAccessException e) {
  406.                         throw new RuntimeException("fail access position field.", e);
  407.                     }
  408.                 }
  409.             });
  410.        
  411.         }
  412.        
  413.         return Optional.empty();
  414.     }
  415.    
  416. }