AbstractNumberCellConverterFactory.java
- package com.gh.mygreen.xlsmapper.cellconverter.impl;
- import java.math.BigDecimal;
- import java.math.MathContext;
- import java.math.RoundingMode;
- import java.text.DecimalFormat;
- import java.text.DecimalFormatSymbols;
- import java.text.NumberFormat;
- import java.text.ParsePosition;
- import java.util.Currency;
- import java.util.HashMap;
- import java.util.Locale;
- import java.util.Map;
- import java.util.Optional;
- import com.gh.mygreen.xlsmapper.Configuration;
- import com.gh.mygreen.xlsmapper.annotation.XlsNumberConverter;
- import com.gh.mygreen.xlsmapper.cellconverter.BaseCellConverter;
- import com.gh.mygreen.xlsmapper.cellconverter.CellConverterFactory;
- import com.gh.mygreen.xlsmapper.cellconverter.CellConverterFactorySupport;
- import com.gh.mygreen.xlsmapper.fieldaccessor.FieldAccessor;
- import com.gh.mygreen.xlsmapper.textformatter.TextFormatter;
- import com.gh.mygreen.xlsmapper.textformatter.TextParseException;
- import com.gh.mygreen.xlsmapper.util.ArgUtils;
- import com.gh.mygreen.xlsmapper.util.Utils;
- /**
- * 数値型のCellConverterを作成するためのベースクラス。
- *
- * @since 2.0
- * @author T.TSUCHIE
- *
- */
- public abstract class AbstractNumberCellConverterFactory<T extends Number> extends CellConverterFactorySupport<T>
- implements CellConverterFactory<T> {
-
- @Override
- protected void setupCustom(final BaseCellConverter<T> cellConverter, final FieldAccessor field, final Configuration config) {
-
- ArgUtils.instanceOf(cellConverter, AbstractNumberCellConverter.class, "cellConverter");
-
- if(cellConverter instanceof AbstractNumberCellConverter) {
-
- final AbstractNumberCellConverter<T> numberCellConverter = (AbstractNumberCellConverter<T>)cellConverter;
-
- // 書き込み時のセルの書式を設定する
- Optional<XlsNumberConverter> convertAnno = field.getAnnotation(XlsNumberConverter.class);
- Optional<String> excelPattern = getExcelPattern(convertAnno);
- excelPattern.ifPresent(pattern -> numberCellConverter.setExcelPattern(pattern));
-
- numberCellConverter.setMathContext(createMathContext(convertAnno));
-
- }
-
- }
-
- @Override
- protected TextFormatter<T> createTextFormatter(final FieldAccessor field, final Configuration config) {
-
- final Optional<XlsNumberConverter> convertAnno = field.getAnnotation(XlsNumberConverter.class);
- final Optional<NumberFormat> numberFormat = createFormatter(convertAnno);
- final MathContext mathContext = createMathContext(convertAnno);
-
- if(numberFormat.isPresent()) {
- final NumberFormat fromatter = numberFormat.get();
-
- // 書式が指定されている場合
- return new TextFormatter<T>() {
-
- @Override
- public T parse(final String text) {
- ParsePosition position = new ParsePosition(0);
- BigDecimal number = (BigDecimal) fromatter.parse(text, position);
-
- if(position.getIndex() != text.length()) {
- throw new TextParseException(text, field.getType());
- }
-
- try {
- number = number.setScale(mathContext.getPrecision(), mathContext.getRoundingMode());
- return convertTypeValue(number);
- } catch(NumberFormatException | ArithmeticException e) {
- final Map<String, Object> vars = new HashMap<>();
- vars.put("javaPattern", getJavaPattern(convertAnno).orElse(null));
- vars.put("excelPattern", getExcelPattern(convertAnno).orElse(null));
-
- throw new TextParseException(text, field.getType(), e, vars);
- }
-
- }
-
- @Override
- public String format(final T value) {
- return fromatter.format(value);
- }
-
- };
-
- } else {
- return new TextFormatter<T>() {
-
- @Override
- public T parse(final String text) {
- /*
- * 有効桁数15桁を超えている場合、Excelの場合は、切り捨てによる丸めが入るが、
- * Javaではそのまま、HALF_UPとなり、オーバーフローが起こる場合がある。
- */
-
- try {
- BigDecimal value = new BigDecimal(text, mathContext);
- return convertTypeValue(value);
- } catch(NumberFormatException | ArithmeticException e) {
- throw new TextParseException(text, field.getType(), e);
- }
- }
-
- @Override
- public String format(final T value) {
- return value.toString();
- }
-
- };
- }
-
- }
-
- /**
- * その型における型に変換する
- * @param value 変換対象の値
- * @return 変換後の値
- * @throws NumberFormatException
- * @throws ArithmeticException
- */
- protected abstract T convertTypeValue(BigDecimal value) throws NumberFormatException, ArithmeticException;
-
- /**
- * アノテーションから数値のフォーマッタを取得する。
- * @param convertAnno 引数がnull(アノテーションが設定されていない場合)は、nullを返す。
- * @return アノテーションに書式が設定されていない場合は空を返す。
- */
- private Optional<NumberFormat> createFormatter(final Optional<XlsNumberConverter> convertAnno) {
-
- if(!convertAnno.isPresent()) {
- return Optional.empty();
- }
-
- final Optional<String> javaPattern = getJavaPattern(convertAnno);
- final Locale locale = Utils.getLocale(convertAnno.get().locale());
- final DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
- final Optional<Currency> currency = convertAnno.get().currency().isEmpty() ? Optional.empty()
- : Optional.of(Currency.getInstance(convertAnno.get().currency()));
-
- if(!javaPattern.isPresent()) {
- if(!convertAnno.get().currency().isEmpty()) {
- // 通貨の場合
- DecimalFormat formatter = (DecimalFormat)NumberFormat.getCurrencyInstance(locale);
- formatter.setParseBigDecimal(true);
- formatter.setDecimalFormatSymbols(symbols);
- currency.ifPresent(c -> formatter.setCurrency(c));
-
- return Optional.of(formatter);
-
- } else {
- return Optional.empty();
- }
- }
-
- final DecimalFormat formatter = new DecimalFormat(javaPattern.get(), symbols);
-
- formatter.setRoundingMode(RoundingMode.HALF_UP);
- formatter.setParseBigDecimal(true);
- currency.ifPresent(c -> formatter.setCurrency(c));
-
- return Optional.of(formatter);
-
- }
-
- private Optional<String> getJavaPattern(final Optional<XlsNumberConverter> converterAnno) {
- if(!converterAnno.isPresent()) {
- return Optional.empty();
- }
-
- String pattern = converterAnno.get().javaPattern();
- if(pattern.isEmpty()) {
- return Optional.empty();
- }
-
- return Optional.of(pattern);
- }
-
- private Optional<String> getExcelPattern(final Optional<XlsNumberConverter> converterAnno) {
- if(!converterAnno.isPresent()) {
- return Optional.empty();
- }
-
- String pattern = converterAnno.get().excelPattern();
- if(pattern.isEmpty()) {
- return Optional.empty();
- }
-
- return Optional.of(pattern);
- }
-
- /**
- * アノテーションを元に、{@link MathContext}のインスタンスを取得する。
- * <p>有効桁数、丸め方法を設定したものを返す。
- * <p>有効桁数は、デフォルトでは無期限にする。
- * <p>丸め方法は、Excelに合わせて、{@link RoundingMode#HALF_UP}で固定。
- * @param convertAnno
- * @return
- */
- private MathContext createMathContext(final Optional<XlsNumberConverter> convertAnno) {
-
- if(convertAnno.isPresent() && convertAnno.get().precision() > 0) {
- return new MathContext(convertAnno.get().precision(), RoundingMode.HALF_UP);
-
- } else {
- //アノテーションがない場合は、制限なし。
- return MathContext.UNLIMITED;
- }
- }
-
- }