AbstractNumberCellConverterFactory.java

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

  2. import java.math.BigDecimal;
  3. import java.math.MathContext;
  4. import java.math.RoundingMode;
  5. import java.text.DecimalFormat;
  6. import java.text.DecimalFormatSymbols;
  7. import java.text.NumberFormat;
  8. import java.text.ParsePosition;
  9. import java.util.Currency;
  10. import java.util.HashMap;
  11. import java.util.Locale;
  12. import java.util.Map;
  13. import java.util.Optional;

  14. import com.gh.mygreen.xlsmapper.Configuration;
  15. import com.gh.mygreen.xlsmapper.annotation.XlsNumberConverter;
  16. import com.gh.mygreen.xlsmapper.cellconverter.BaseCellConverter;
  17. import com.gh.mygreen.xlsmapper.cellconverter.CellConverterFactory;
  18. import com.gh.mygreen.xlsmapper.cellconverter.CellConverterFactorySupport;
  19. import com.gh.mygreen.xlsmapper.fieldaccessor.FieldAccessor;
  20. import com.gh.mygreen.xlsmapper.textformatter.TextFormatter;
  21. import com.gh.mygreen.xlsmapper.textformatter.TextParseException;
  22. import com.gh.mygreen.xlsmapper.util.ArgUtils;
  23. import com.gh.mygreen.xlsmapper.util.Utils;

  24. /**
  25.  * 数値型のCellConverterを作成するためのベースクラス。
  26.  *
  27.  * @since 2.0
  28.  * @author T.TSUCHIE
  29.  *
  30.  */
  31. public abstract class AbstractNumberCellConverterFactory<T extends Number> extends CellConverterFactorySupport<T>
  32.             implements CellConverterFactory<T> {
  33.    
  34.     @Override
  35.     protected void setupCustom(final BaseCellConverter<T> cellConverter, final FieldAccessor field, final Configuration config) {
  36.        
  37.         ArgUtils.instanceOf(cellConverter, AbstractNumberCellConverter.class, "cellConverter");
  38.        
  39.         if(cellConverter instanceof AbstractNumberCellConverter) {
  40.            
  41.             final AbstractNumberCellConverter<T> numberCellConverter = (AbstractNumberCellConverter<T>)cellConverter;
  42.            
  43.             // 書き込み時のセルの書式を設定する
  44.             Optional<XlsNumberConverter> convertAnno = field.getAnnotation(XlsNumberConverter.class);
  45.             Optional<String> excelPattern = getExcelPattern(convertAnno);
  46.             excelPattern.ifPresent(pattern -> numberCellConverter.setExcelPattern(pattern));
  47.            
  48.             numberCellConverter.setMathContext(createMathContext(convertAnno));
  49.            
  50.         }
  51.        
  52.     }
  53.    
  54.     @Override
  55.     protected TextFormatter<T> createTextFormatter(final FieldAccessor field, final Configuration config) {
  56.        
  57.         final Optional<XlsNumberConverter> convertAnno = field.getAnnotation(XlsNumberConverter.class);
  58.         final Optional<NumberFormat> numberFormat = createFormatter(convertAnno);
  59.         final MathContext mathContext = createMathContext(convertAnno);
  60.        
  61.         if(numberFormat.isPresent()) {
  62.             final NumberFormat fromatter = numberFormat.get();
  63.            
  64.             // 書式が指定されている場合
  65.             return new TextFormatter<T>() {
  66.                
  67.                 @Override
  68.                 public T parse(final String text) {
  69.                     ParsePosition position = new ParsePosition(0);
  70.                     BigDecimal number = (BigDecimal) fromatter.parse(text, position);
  71.                    
  72.                     if(position.getIndex() != text.length()) {
  73.                         throw new TextParseException(text, field.getType());
  74.                     }
  75.                    
  76.                     try {
  77.                         number = number.setScale(mathContext.getPrecision(), mathContext.getRoundingMode());
  78.                         return convertTypeValue(number);
  79.                     } catch(NumberFormatException | ArithmeticException e) {
  80.                         final Map<String, Object> vars = new HashMap<>();
  81.                         vars.put("javaPattern", getJavaPattern(convertAnno).orElse(null));
  82.                         vars.put("excelPattern", getExcelPattern(convertAnno).orElse(null));
  83.                        
  84.                         throw new TextParseException(text, field.getType(), e, vars);
  85.                     }
  86.                    
  87.                 }
  88.                
  89.                 @Override
  90.                 public String format(final T value) {
  91.                     return fromatter.format(value);
  92.                 }
  93.                
  94.             };
  95.            
  96.         } else {
  97.             return new TextFormatter<T>() {
  98.                
  99.                 @Override
  100.                 public T parse(final String text) {
  101.                     /*
  102.                      * 有効桁数15桁を超えている場合、Excelの場合は、切り捨てによる丸めが入るが、
  103.                      * Javaではそのまま、HALF_UPとなり、オーバーフローが起こる場合がある。
  104.                      */
  105.                    
  106.                     try {
  107.                         BigDecimal value = new BigDecimal(text, mathContext);
  108.                         return convertTypeValue(value);
  109.                     } catch(NumberFormatException | ArithmeticException e) {
  110.                         throw new TextParseException(text, field.getType(), e);
  111.                     }
  112.                 }
  113.                
  114.                 @Override
  115.                 public String format(final T value) {
  116.                     return value.toString();
  117.                 }
  118.                
  119.             };
  120.         }
  121.        
  122.     }
  123.    
  124.     /**
  125.      * その型における型に変換する
  126.      * @param value 変換対象の値
  127.      * @return 変換後の値
  128.      * @throws NumberFormatException
  129.      * @throws ArithmeticException
  130.      */
  131.     protected abstract T convertTypeValue(BigDecimal value) throws NumberFormatException, ArithmeticException;
  132.    
  133.     /**
  134.      * アノテーションから数値のフォーマッタを取得する。
  135.      * @param convertAnno 引数がnull(アノテーションが設定されていない場合)は、nullを返す。
  136.      * @return アノテーションに書式が設定されていない場合は空を返す。
  137.      */
  138.     private Optional<NumberFormat> createFormatter(final Optional<XlsNumberConverter> convertAnno) {
  139.        
  140.         if(!convertAnno.isPresent()) {
  141.             return Optional.empty();
  142.         }
  143.        
  144.         final Optional<String> javaPattern = getJavaPattern(convertAnno);
  145.         final Locale locale = Utils.getLocale(convertAnno.get().locale());
  146.         final DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
  147.         final Optional<Currency> currency = convertAnno.get().currency().isEmpty() ? Optional.empty()
  148.                 : Optional.of(Currency.getInstance(convertAnno.get().currency()));
  149.        
  150.         if(!javaPattern.isPresent()) {
  151.             if(!convertAnno.get().currency().isEmpty()) {
  152.                 // 通貨の場合
  153.                 DecimalFormat formatter = (DecimalFormat)NumberFormat.getCurrencyInstance(locale);
  154.                 formatter.setParseBigDecimal(true);
  155.                 formatter.setDecimalFormatSymbols(symbols);
  156.                 currency.ifPresent(c -> formatter.setCurrency(c));
  157.                
  158.                 return Optional.of(formatter);
  159.                
  160.             } else {
  161.                 return Optional.empty();
  162.             }
  163.         }
  164.        
  165.         final DecimalFormat formatter = new DecimalFormat(javaPattern.get(), symbols);
  166.        
  167.         formatter.setRoundingMode(RoundingMode.HALF_UP);
  168.         formatter.setParseBigDecimal(true);
  169.         currency.ifPresent(c -> formatter.setCurrency(c));
  170.        
  171.         return Optional.of(formatter);
  172.        
  173.     }
  174.    
  175.     private Optional<String> getJavaPattern(final Optional<XlsNumberConverter> converterAnno) {
  176.         if(!converterAnno.isPresent()) {
  177.             return Optional.empty();
  178.         }
  179.        
  180.         String pattern = converterAnno.get().javaPattern();
  181.         if(pattern.isEmpty()) {
  182.             return Optional.empty();
  183.         }
  184.        
  185.         return Optional.of(pattern);
  186.     }
  187.    
  188.     private Optional<String> getExcelPattern(final Optional<XlsNumberConverter> converterAnno) {
  189.         if(!converterAnno.isPresent()) {
  190.             return Optional.empty();
  191.         }
  192.        
  193.         String pattern = converterAnno.get().excelPattern();
  194.         if(pattern.isEmpty()) {
  195.             return Optional.empty();
  196.         }
  197.        
  198.         return Optional.of(pattern);
  199.     }
  200.    
  201.     /**
  202.      * アノテーションを元に、{@link MathContext}のインスタンスを取得する。
  203.      * <p>有効桁数、丸め方法を設定したものを返す。
  204.      * <p>有効桁数は、デフォルトでは無期限にする。
  205.      * <p>丸め方法は、Excelに合わせて、{@link RoundingMode#HALF_UP}で固定。
  206.      * @param convertAnno
  207.      * @return
  208.      */
  209.     private MathContext createMathContext(final Optional<XlsNumberConverter> convertAnno) {
  210.        
  211.         if(convertAnno.isPresent() && convertAnno.get().precision() > 0) {
  212.             return new MathContext(convertAnno.get().precision(), RoundingMode.HALF_UP);
  213.            
  214.         } else {
  215.             //アノテーションがない場合は、制限なし。
  216.             return MathContext.UNLIMITED;
  217.         }
  218.     }
  219.    
  220. }