package com.github.mygreen.supercsv.cellprocessor.format; import java.math.BigDecimal; import java.math.BigInteger; import java.math.MathContext; import java.util.HashMap; import java.util.Map; import java.util.Objects; /** * 書式がない数値のフォーマッタ。 * * @since 2.0 * @author T.TSUCHIE * */ public class SimpleNumberFormatter<T extends Number> extends AbstractTextFormatter<T> { private final Class<T> type; private final boolean lenient; private final MathContext mathContext; /** * デフォルトコンストラクタ * @param type 数値のクラスタイプ * @param lenient 曖昧にパースするかどうか。 * @throws NullPointerException {@literal type is null.} * */ public SimpleNumberFormatter(final Class<T> type, final boolean lenient) { this(type, lenient, null); } /** * デフォルトコンストラクタ * @param type 数値のクラスタイプ * @param lenient 曖昧にパースするかどうか。 * @param mathContext 丸めの方法を指定します。nullを渡すと省略できます。 * @throws NullPointerException {@literal type is null.} * */ public SimpleNumberFormatter(final Class<T> type, final boolean lenient, final MathContext mathContext) { Objects.requireNonNull(type); this.type = type; this.lenient = lenient; this.mathContext = mathContext; } @SuppressWarnings("unchecked") @Override public T parse(final String text) { try { final BigDecimal number = mathContext == null ? new BigDecimal(text) : new BigDecimal(text, mathContext); return (T) parseFromBigDecimal(type, number); } catch(NumberFormatException | ArithmeticException e) { throw new TextParseException(text, type, e); } } private Number parseFromBigDecimal(final Class<? extends Number> type, final BigDecimal number) { if(Byte.class.isAssignableFrom(type) || byte.class.isAssignableFrom(type)) { return lenient ? number.byteValue() : number.byteValueExact(); } else if(Short.class.isAssignableFrom(type) || short.class.isAssignableFrom(type)) { return lenient ? number.shortValue() : number.shortValueExact(); } else if(Integer.class.isAssignableFrom(type) || int.class.isAssignableFrom(type)) { return lenient ? number.intValue() : number.intValueExact(); } else if(Long.class.isAssignableFrom(type) || long.class.isAssignableFrom(type)) { return lenient ? number.longValue() : number.longValueExact(); } else if(Float.class.isAssignableFrom(type) || float.class.isAssignableFrom(type)) { return number.floatValue(); } else if(Double.class.isAssignableFrom(type) || double.class.isAssignableFrom(type)) { return number.doubleValue(); } else if(type.isAssignableFrom(BigInteger.class)) { return lenient ? number.toBigInteger() : number.toBigIntegerExact(); } else if(type.isAssignableFrom(BigDecimal.class)) { return number; } throw new IllegalArgumentException(String.format("Not support class type : %s", type.getCanonicalName())); } @Override public String print(final T object) { if(mathContext != null) { return printNumber(object); } return object.toString(); } private String printNumber(final Object object) { if(Byte.class.isAssignableFrom(type) || byte.class.isAssignableFrom(type)) { return new BigDecimal((byte)object, mathContext).toPlainString(); } else if(Short.class.isAssignableFrom(type) || short.class.isAssignableFrom(type)) { return new BigDecimal((short)object, mathContext).toPlainString(); } else if(Integer.class.isAssignableFrom(type) || int.class.isAssignableFrom(type)) { return new BigDecimal((int)object, mathContext).toPlainString(); } else if(Long.class.isAssignableFrom(type) || long.class.isAssignableFrom(type)) { return new BigDecimal((long)object, mathContext).toPlainString(); } else if(Float.class.isAssignableFrom(type) || float.class.isAssignableFrom(type)) { return new BigDecimal((float)object, mathContext).toPlainString(); } else if(Double.class.isAssignableFrom(type) || double.class.isAssignableFrom(type)) { return new BigDecimal((double)object, mathContext).toPlainString(); } else if(BigInteger.class.isAssignableFrom(type)) { return new BigDecimal((BigInteger)object, mathContext).toPlainString(); } else { return object.toString(); } } /** * パースする際に、数値に変換可能な部分のみ変換するかどうか。 * <p>例えば、trueのときは、{@literal 123abc} をパースする際に{@literal 123}を数値としてパースします。 * <br>falseの場合は、例外{@link TextParseException}をスローします。 * </p> * @return trueの場合、曖昧にパースします。 */ public boolean isLenient() { return lenient; } /** * パースする際の数値の丸め方法の指定情報を取得します。 * @return 数値の丸め方法 */ public MathContext getMathContext() { return mathContext; } @Override public Map<String, Object> getMessageVariables() { final Map<String, Object> vars = new HashMap<>(); if(mathContext != null) { vars.put("precision", mathContext.getPrecision()); } return vars; } }