CellFormulaHandler.java

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

  2. import java.awt.Point;
  3. import java.lang.reflect.InvocationTargetException;
  4. import java.lang.reflect.Method;
  5. import java.util.HashMap;
  6. import java.util.Map;
  7. import java.util.Optional;

  8. import org.apache.poi.ss.formula.FormulaParseException;
  9. import org.apache.poi.ss.usermodel.Cell;
  10. import org.apache.poi.ss.usermodel.Sheet;
  11. import org.apache.poi.ss.util.CellReference;

  12. import com.gh.mygreen.xlsmapper.Configuration;
  13. import com.gh.mygreen.xlsmapper.XlsMapperException;
  14. import com.gh.mygreen.xlsmapper.fieldaccessor.FieldAccessor;
  15. import com.gh.mygreen.xlsmapper.localization.MessageBuilder;
  16. import com.gh.mygreen.xlsmapper.util.ArgUtils;
  17. import com.gh.mygreen.xlsmapper.util.CellPosition;
  18. import com.gh.mygreen.xlsmapper.util.Utils;

  19. /**
  20.  * セルの数式を処理する
  21.  *
  22.  * @since 2.0
  23.  * @author T.TSUCHIE
  24.  *
  25.  */
  26. public class CellFormulaHandler {
  27.    
  28.     /**
  29.      * 数式を直接指定している場合
  30.      */
  31.     private Optional<String> formula = Optional.empty();
  32.    
  33.     /**
  34.      * 数式を取得するメソッドを指定している場合
  35.      */
  36.     private Optional<Method> method = Optional.empty();
  37.    
  38.     /**
  39.      * セルの値が設定済みの時に、数式の設定を優先するかどうか。
  40.      */
  41.     private boolean primaryFormula;
  42.    
  43.     /**
  44.      * 数式を直接指定する場合
  45.      * @param formula 数式。EL式で評価可能な形式。
  46.      * @throws IllegalArgumentException {@literal formula == null or empty.}
  47.      */
  48.     public CellFormulaHandler(final String formula) {
  49.         ArgUtils.notEmpty(formula, "formula");
  50.        
  51.         this.formula = Optional.of(formula);
  52.     }
  53.    
  54.     /**
  55.      * 数式を取得するメソッドを指定する場合
  56.      * @param method 数式を取得するためのメソッド
  57.      * @throws IllegalArgumentException {@literal method == null.}
  58.      */
  59.     public CellFormulaHandler(final Method method) {
  60.         ArgUtils.notNull(method, "method");
  61.        
  62.         this.method = Optional.of(method);
  63.        
  64.     }
  65.    
  66.     /**
  67.      * セルに数式を設定する
  68.      * @param field フィールド情報
  69.      * @param config システム情報
  70.      * @param cell セル情報
  71.      * @param targetBean 処理対象のフィールドが定義されているクラスのインスタンス。
  72.      * @throws ConversionException 数式の解析に失敗した場合。
  73.      */
  74.     public void handleFormula(final FieldAccessor field, final Configuration config, final Cell cell, final Object targetBean) {
  75.        
  76.         ArgUtils.notNull(field, "field");
  77.         ArgUtils.notNull(config, "config");
  78.         ArgUtils.notNull(cell, "cell");
  79.        
  80.         final String evaluatedFormula = createFormulaValue(config, cell, targetBean);
  81.         if(Utils.isEmpty(evaluatedFormula)) {
  82.             cell.setBlank();
  83.             return;
  84.         }
  85.        
  86.         try {
  87.             cell.setCellFormula(evaluatedFormula);
  88.            
  89.         } catch(FormulaParseException e) {
  90.             // 数式の解析に失敗した場合
  91.             String message = MessageBuilder.create("cell.failParseFormula")
  92.                     .var("property", field.getNameWithClass())
  93.                     .var("cellAddress", CellPosition.of(cell).toString())
  94.                     .var("formula", evaluatedFormula)
  95.                     .format();
  96.            
  97.             throw new ConversionException(message, e, field.getType());
  98.         }
  99.        
  100.     }
  101.    
  102.     /**
  103.      * Excelの式を組み立てる。
  104.      * @param config システム情報設定
  105.      * @param cell セル情報
  106.      * @param targetBean 処理対象のフィールドが定義されているクラスのインスタンス。
  107.      * @return 組み立てた数式
  108.      */
  109.     public String createFormulaValue(final Configuration config, final Cell cell, final Object targetBean) {
  110.        
  111.         if(formula.isPresent()) {
  112.             final Map<String, Object> vars = new HashMap<>();
  113.             vars.put("rowIndex", cell.getRowIndex());
  114.             vars.put("columnIndex", cell.getColumnIndex());
  115.             vars.put("rowNumber", cell.getRowIndex()+1);
  116.             vars.put("columnNumber", cell.getColumnIndex()+1);
  117.             vars.put("columnAlpha", CellReference.convertNumToColString(cell.getColumnIndex()));
  118.             vars.put("address", CellPosition.of(cell).formatAsString());
  119.             vars.put("targetBean", targetBean);
  120.             vars.put("cell", cell);
  121.            
  122.             return config.getFormulaFormatter().interpolate(formula.get(), vars);
  123.            
  124.         } else if(method.isPresent()) {
  125.            
  126.             // メソッドの引数の組み立て
  127.             final Class<?>[] paramTypes = method.get().getParameterTypes();
  128.             final Object[] paramValues = new Object[paramTypes.length];
  129.            
  130.             for(int i=0; i < paramTypes.length; i++) {
  131.                 if(Cell.class.isAssignableFrom(paramTypes[i])) {
  132.                     paramValues[i] = cell;
  133.                    
  134.                 } else if(CellPosition.class.isAssignableFrom(paramTypes[i])) {
  135.                     paramValues[i] = CellPosition.of(cell);
  136.                    
  137.                 } else if(Point.class.isAssignableFrom(paramTypes[i])) {
  138.                     paramValues[i] = CellPosition.of(cell).toPoint();
  139.                    
  140.                 } else if(org.apache.poi.ss.util.CellAddress.class.isAssignableFrom(paramTypes[i])) {
  141.                     paramValues[i] = CellPosition.of(cell).toCellAddress();
  142.                    
  143.                 } else if(Sheet.class.isAssignableFrom(paramTypes[i])) {
  144.                     paramValues[i] = cell.getSheet();
  145.                    
  146.                 } else if(Configuration.class.isAssignableFrom(paramTypes[i])) {
  147.                     paramValues[i] = config;
  148.                    
  149.                 } else {
  150.                     paramValues[i] = null;
  151.                 }
  152.             }
  153.            
  154.             try {
  155.                 return (String) method.get().invoke(targetBean, paramValues);
  156.                
  157.             } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
  158.                 final Class<?> targetClass = targetBean.getClass();
  159.                 final Throwable t = e.getCause() == null ? e : e.getCause();
  160.                 throw new XlsMapperException(
  161.                         String.format("Fail execute method '%s#%s'.", targetClass.getName(), method.get().getName()),
  162.                         t);
  163.             }
  164.            
  165.         } else {
  166.             // 数式や対応するメソッドがない場合
  167.             throw new IllegalStateException("not found for formula or method.");
  168.         }
  169.        
  170.     }
  171.    
  172.     /**
  173.      * セルの値が設定済みの時に、数式の設定を優先するかどうか。
  174.      */
  175.     public boolean isPrimaryFormula() {
  176.         return primaryFormula;
  177.     }
  178.    
  179.     /**
  180.      * セルの値が設定済みの時に、数式の設定を優先するかどうか。
  181.      * @param primaryFormula 数式の設定を優先するかどうか
  182.      */
  183.     public void setPrimaryFormula(boolean primaryFormula) {
  184.         this.primaryFormula = primaryFormula;
  185.     }
  186.    
  187. }