NumberFormatWrapper.java
package com.github.mygreen.supercsv.cellprocessor.format;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
/**
* 書式を指定した数値のフォーマッタ。
*
* @since 1.2
* @author T.TSUCHIE
*
*/
public class NumberFormatWrapper<T extends Number> extends AbstractTextFormatter<T> {
private final NumberFormat formatter;
private final Class<T> type;
private final boolean lenient;
public NumberFormatWrapper(final NumberFormat formatter, final Class<T> type) {
this(formatter, type, false);
}
public NumberFormatWrapper(final NumberFormat formatter, final Class<T> type, final boolean lenient) {
Objects.requireNonNull(formatter);
Objects.requireNonNull(type);
this.formatter = (NumberFormat) formatter.clone();
this.type = type;
this.lenient = lenient;
}
@Override
public synchronized String print(final Number number) {
return formatter.format(number);
}
@Override
public T parse(final String text) {
return parse(type, text);
}
/**
* 指定した数値のクラスに文字列をパースする。
* <p>Java標準のクラスタイプをサポートします。</p>
*
* @param type 変換する数値のクラス。
* @param text パース対象の文字列。
* @return パースした数値のオブジェクト。
* @throws IllegalArgumentException サポートしていないクラスタイプが指定された場合。
* @throws TextParseException fail convert Number or BigDecimal.
*/
@SuppressWarnings("unchecked")
synchronized <N extends Number> N parse(final Class<N> type, final String text) {
final Number result;
if(lenient) {
try {
result = formatter.parse(text);
} catch(ParseException e) {
throw new TextParseException(text, type, e);
}
} else {
ParsePosition position = new ParsePosition(0);
result = formatter.parse(text, position);
if(position.getIndex() != text.length()) {
throw new TextParseException(text, type, String.format("Cannot parse '%s' using fromat %s", text, getPattern()));
}
}
try {
if(result instanceof BigDecimal) {
// if set DecimalFormat#setParseBigDecimal(true)
return (N) convertWithBigDecimal(type, (BigDecimal) result, text);
} else {
return (N) convertWithNumber(type, result, text);
}
} catch(NumberFormatException | ArithmeticException e) {
throw new TextParseException(text, type, e);
}
}
private Number convertWithNumber(final Class<? extends Number> type, final Number number, final String str) {
if(Byte.class.isAssignableFrom(type) || byte.class.isAssignableFrom(type)) {
return number.byteValue();
} else if(Short.class.isAssignableFrom(type) || short.class.isAssignableFrom(type)) {
return number.shortValue() ;
} else if(Integer.class.isAssignableFrom(type) || int.class.isAssignableFrom(type)) {
return number.intValue();
} else if(Long.class.isAssignableFrom(type) || long.class.isAssignableFrom(type)) {
return number.longValue();
} 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 new BigInteger(str);
} else if(type.isAssignableFrom(BigDecimal.class)) {
return new BigDecimal(str);
}
throw new IllegalArgumentException(String.format("not support class type : %s", type.getCanonicalName()));
}
private Number convertWithBigDecimal(final Class<? extends Number> type, final BigDecimal number, final String str) {
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 Optional<String> getPattern() {
if(formatter instanceof DecimalFormat) {
DecimalFormat df = (DecimalFormat) formatter;
return Optional.of(df.toPattern());
}
return Optional.empty();
}
/**
* パースする際に、数値に変換可能な部分のみ変換するかどうか。
* <p>例えば、trueのときは、{@literal 123abc} をパースする際に{@literal 123}を数値としてパースします。
* <br>falseの場合は、例外{@link TextParseException}をスローします。
* </p>
* @return trueの場合、曖昧にパースします。
*/
public boolean isLenient() {
return lenient;
}
@Override
public Map<String, Object> getMessageVariables() {
final Map<String, Object> vars = new HashMap<>();
getPattern().ifPresent(p -> vars.put("pattern", p));
return vars;
}
}