NumberTerm.java
package com.github.mygreen.cellformatter.term;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.Locale;
import com.github.mygreen.cellformatter.lang.MSLocale;
import com.github.mygreen.cellformatter.lang.Utils;
import com.github.mygreen.cellformatter.number.FormattedNumber;
import com.github.mygreen.cellformatter.number.NativeNumber;
import com.github.mygreen.cellformatter.number.NumberPartType;
import com.github.mygreen.cellformatter.tokenizer.Token;
/**
* 数値の書式の項
*
* @version 0.10
* @author T.TSUCHIE
*
*/
public abstract class NumberTerm implements Term<FormattedNumber> {
public static GeneralTerm general() {
return new GeneralTerm();
}
public static ZeroTerm zero() {
return new ZeroTerm();
}
public static SharpTerm sharp() {
return new SharpTerm();
}
public static QuestionTerm question() {
return new QuestionTerm();
}
public static ExponentTerm exponnet(final Token token) {
return new ExponentTerm(token);
}
public static SeparatorTerm separator(final Token.Symbol token) {
return new SeparatorTerm(token);
}
public static SymbolTerm symbol(final Token.Symbol token) {
return new SymbolTerm(token);
}
public static DigitsTerm digits(final Token.Digits token) {
return new DigitsTerm(token);
}
/**
* フォーマットの書式"General"を表現する項
*
*/
public static class GeneralTerm extends NumberTerm {
@Override
public String format(final FormattedNumber number, final MSLocale formatLocale, final Locale runtimeLocale) {
final double unsingedValue = Math.abs(number.getValue());
/*
* NativeNumberの場合、そのまま返す。
* ・DBNumXがあると、指数表現などしない。
*/
if(number instanceof NativeNumber) {
return new BigDecimal(unsingedValue).toPlainString();
}
// 指数表記の場合
if(isNumberAsExponent(unsingedValue)) {
final DecimalFormat format = new DecimalFormat("0.#####E0");
format.setRoundingMode(RoundingMode.HALF_UP);
String str = format.format(unsingedValue);
if(unsingedValue >= 1) {
// 指数に符号を付ける
str = str.replace("E", "E+");
}
return str;
}
final BigDecimal num = new BigDecimal(unsingedValue);
final String strNum = num.toPlainString();
/*
* 小数部がない場合
* ・小数点で判断
*/
if(!strNum.contains(".")) {
return strNum;
}
/*
* 小数部がある場合、整数部の桁数によって精度を変える
* ・整数部が10桁以上ある場合は、小数部は省略される。
* ・整数部が10桁未満の場合、有効桁数が10桁になるように少数の精度が増える。
*/
final String strIntPart = strNum.substring(0, strNum.indexOf("."));
final int intLength = strIntPart.length();
final String pattern;
if(intLength < 10) {
StringBuilder f = new StringBuilder();
f.append("0.");
for(int i=0; i < 10-intLength; i++) {
f.append("#");
}
pattern = f.toString();
} else {
pattern = "0";
}
final DecimalFormat format = new DecimalFormat(pattern);
format.setRoundingMode(RoundingMode.HALF_UP);
return format.format(unsingedValue);
}
/**
* 指数表示すべき数値を判定する
* @param unsingedValue 符号なしの数値
* @return true 指数表示する。
*/
private boolean isNumberAsExponent(final double unsingedValue) {
if(unsingedValue == 0.0d) {
return false;
} else if(unsingedValue >= 100000000000.0d) {
return true;
} else if(unsingedValue <= 0.0000000001d) {
return true;
} else {
return false;
}
}
}
/**
* 数値のフォーマット部分の項を表す抽象クラス。
*
*/
public static abstract class FormattedTerm extends NumberTerm {
/** 桁のインデックス */
protected int index;
/** 書式の部分 */
protected NumberPartType partType;
/** 書式の部分の最後かどうか */
protected boolean lastPart;
/** 桁の区切り文字を出力するかどうか */
protected boolean outSepearator;
public FormattedTerm index(final int index) {
this.index = index;
return this;
}
public FormattedTerm partType(final NumberPartType partType) {
this.partType = partType;
return this;
}
public FormattedTerm lastPart(final boolean lastPart) {
this.lastPart = lastPart;
return this;
}
/**
* 数値の部分に対する桁の値を取得する。
* @param number
* @return
*/
protected String getNumber(final FormattedNumber number) {
switch(partType) {
case Integer:
if(isLastPart()) {
return number.asDecimal().getIntegerPartAfter(getIndex());
} else {
return number.asDecimal().getIntegerPart(getIndex());
}
case Decimal:
return number.asDecimal().getDecimalPart(getIndex());
case Exponent:
if(isLastPart()) {
return number.asExponent().getExponentPartAfter(getIndex());
} else {
return number.asExponent().getExponentPart(getIndex());
}
case Denominator:
if(isLastPart()) {
return number.asFraction().getDenominatorPartAfter(getIndex());
} else {
return number.asFraction().getDenominatorPart(getIndex());
}
case Numerator:
if(isLastPart()) {
return number.asFraction().getNumeratorPartAfter(getIndex());
} else {
return number.asFraction().getNumeratorPart(getIndex());
}
case WholeNumber:
if(isLastPart()) {
return number.asFraction().getWholeNumberPartAfter(getIndex());
} else {
return number.asFraction().getWholeNumberPart(getIndex());
}
default:
return "";
}
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public NumberPartType getPartType() {
return partType;
}
public void setPart(NumberPartType partType) {
this.partType = partType;
}
public boolean isLastPart() {
return lastPart;
}
public void setLastPart(boolean lastPart) {
this.lastPart = lastPart;
}
public boolean isOutSepearator() {
return outSepearator;
}
public void setOutSepearator(boolean outSepearator) {
this.outSepearator = outSepearator;
}
}
/**
* フォーマットの書式"0"の記号。
* ・出力する項がない場合は、0を出力する。
*/
public static class ZeroTerm extends FormattedTerm {
private static final String ZERO = "0";
@Override
public String format(final FormattedNumber number, final MSLocale formatLocale, final Locale runtimeLocale) {
String num = getNumber(number);
if(num.isEmpty()) {
return ZERO;
}
return num;
}
}
/**
* フォーマットの書式"#"の記号。
* ・出力する項がない場合は、何も出力しない。
*
*/
public static class SharpTerm extends FormattedTerm {
@Override
public String format(final FormattedNumber number, final MSLocale formatLocale, final Locale runtimeLocale) {
String num = getNumber(number);
return num;
}
}
/**
* フォーマットの書式"?"の記号。
* ・出力する項がない場合は、半角スペースを出力する。
*
*/
public static class QuestionTerm extends FormattedTerm {
private static final String SPACE = " ";
@Override
public String format(final FormattedNumber number, final MSLocale formatLocale, final Locale runtimeLocale) {
String num = getNumber(number);
if(num.isEmpty()) {
return SPACE;
}
return num;
}
}
/**
* フォーマットの書式の指数"E"を表現する項。
* ・指数部の符号も出力する。
*
*/
public static class ExponentTerm extends NumberTerm {
/**
* 符号
* ・ただし、符号がない場合がある。
* ・出力するときには、設定された符号は無視する。
*/
private final Token token;
/**
* 指数の記号。
* ・パターンによって、大文字、小文字がある。
*/
private final String exponentSymbol;
public ExponentTerm(final Token token) {
this.token = token;
final String vale = token.getValue();
if(vale.startsWith("E")) {
this.exponentSymbol = "E";
} else {
this.exponentSymbol = "e";
}
}
@Override
public String format(final FormattedNumber number, final MSLocale formatLocale, final Locale runtimeLocale) {
if(number.asExponent().isExponentPositive()) {
if(Utils.startsWithIgnoreCase(getToken().getValue(), "E-")) {
// 指数がマイナスの場合は、正の時に符号は付与しない。
return exponentSymbol;
} else {
return exponentSymbol + "+";
}
} else {
return exponentSymbol + "-";
}
}
public Token getToken() {
return token;
}
public String getExponentSymbol() {
return exponentSymbol;
}
}
/**
* 桁区切り文字の処理
* ・区切り文字の挿入は、数値の出力時の行う。
*/
public static class SeparatorTerm extends NumberTerm {
private final Token.Symbol token;
public SeparatorTerm(final Token.Symbol token) {
this.token = token;
}
@Override
public String format(final FormattedNumber value, final MSLocale formatLocale, final Locale runtimeLocale) {
return "";
}
public Token.Symbol getToken() {
return token;
}
}
/**
* 記号の処理
*
* @author T.TSUCHIE
*
*/
public static class SymbolTerm extends NumberTerm {
private final Token.Symbol token;
public SymbolTerm(final Token.Symbol token) {
this.token = token;
}
@Override
public String format(final FormattedNumber value, final MSLocale formatLocale, final Locale runtimeLocale) {
return token.getValue();
}
public Token.Symbol getToken() {
return token;
}
}
public static class DigitsTerm extends NumberTerm {
private final Token.Digits token;
public DigitsTerm(Token.Digits token) {
this.token = token;
}
@Override
public String format(final FormattedNumber value, final MSLocale formatLocale, final Locale runtimeLocale) {
return token.getValue();
}
public Token.Digits getToken() {
return token;
}
}
}