MapPositionSetterFactory.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.LinkedHashMap;
  8. import java.util.Map;
  9. import java.util.Optional;

  10. import org.apache.poi.ss.util.CellAddress;
  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.CellPosition;
  15. import com.gh.mygreen.xlsmapper.util.Utils;

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