PositionSetterFactory.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.slf4j.Logger;
  11. import org.slf4j.LoggerFactory;

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

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