package br.com.caelum.stella.inwords;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
import java.util.MissingResourceException;
/**
* Responsável por transformar um número em sua representação por extenso.
*
* @author Victor dos Santos Pereira
* @author Leonardo Bessa
*
*/
public class NumericToWordsConverter {
private final FormatoDeExtenso formato;
private final Locale locale;
/**
* @param formato
* formato desejado para a transformação por extenso
*/
public NumericToWordsConverter(FormatoDeExtenso formato) {
this.formato = formato;
this.locale = Messages.LOCALE_PT_BR;
}
/**
* @param formato
* formato desejado para a transformação por extenso
* @param locale
* idioma desejado para a transformação por extenso
*/
public NumericToWordsConverter(FormatoDeExtenso formato, Locale locale) {
this.formato = formato;
this.locale = locale;
}
/**
* @param number
* número a ser transformado
* @return Representação do número por extenso.
*/
public String toWords(long number) {
try {
if (number < 0) {
throw new IllegalArgumentException("Não é possível transformar números negativos.");
}
StringBuffer result = new StringBuffer();
if (number == 0) {
result.append(getNumber(0));
} else {
DecimalFormatSymbols symbols = new DecimalFormatSymbols();
symbols.setGroupingSeparator(',');
symbols.setDecimalSeparator('.');
DecimalFormat formatter = new DecimalFormat("###,###", symbols);
String formattedInt = formatter.format(number);
String[] ints = formattedInt.split("[,]");
ThousandBlock[] blocks = new ThousandBlock[ints.length];
for (int i = 0; i < blocks.length; i++) {
String block = ints[i];
blocks[i] = new ThousandBlock(block);
}
appendIntegers(result, blocks);
appendIntegersUnits(number, result, blocks);
}
return result.toString();
} catch (MissingResourceException e) {
throw new IllegalArgumentException("Número muito grande para ser transformado em extenso.");
}
}
/**
* @param number
* número a ser transformado
* @return Representação do número por extenso.
*/
public String toWords(double number) {
try {
if (number < 0) {
throw new IllegalArgumentException("Não é possível transformar números negativos.");
}
StringBuffer result = new StringBuffer();
if (number == 0) {
result.append(getNumber(0));
} else {
String[] parts = split(number);
String formattedInt = parts[0];
String[] ints = formattedInt.split("[,]");
ThousandBlock[] blocks = new ThousandBlock[ints.length];
for (int i = 0; i < blocks.length; i++) {
String block = ints[i];
blocks[i] = new ThousandBlock(block);
}
String formattedMod = parts[1];
ThousandBlock modBlock = new ThousandBlock(formattedMod);
boolean hasMod = !modBlock.isZero();
boolean hasInteger = (blocks.length > 1) || (!blocks[blocks.length - 1].isZero());
if (hasInteger) {
appendIntegers(result, blocks);
appendIntegersUnits(number, result, blocks);
}
if (hasInteger && hasMod) {
result.append(getAndSeparator());
}
if (hasMod) {
appendIntegers(result, modBlock);
appendDecimalUnits(result, modBlock);
}
}
return result.toString();
} catch (MissingResourceException e) {
throw new IllegalArgumentException("Número muito grande para ser transformado em extenso.");
}
}
private void appendDecimalUnits(StringBuffer result, ThousandBlock modBlock) {
result.append(" ");
if (modBlock.isUnitary()) {
result.append(formato.getUnidadeDecimalNoSingular());
} else {
result.append(formato.getUnidadeDecimalNoPlural());
}
}
private String[] split(double number) {
final StringBuffer pattern = new StringBuffer();
pattern.append("###,000.");
for (int i = 1; i <= formato.getCasasDecimais(); i++) {
pattern.append("0");
}
DecimalFormatSymbols symbols = new DecimalFormatSymbols();
symbols.setGroupingSeparator(',');
symbols.setDecimalSeparator('.');
DecimalFormat decimalFormat = new DecimalFormat(pattern.toString(), symbols);
String formatted = decimalFormat.format(number);
String[] parts = formatted.split("[.]");
return parts;
}
private void appendIntegersUnits(double number, StringBuffer result, ThousandBlock[] blocks) {
if (blocks.length != 1 || !blocks[0].isZero()) {
if (number >= 2) {
String unit = formato.getUnidadeInteiraNoPlural();
if( !unit.isEmpty() ) {
result.append(" ");
int length = blocks.length;
if (length > 2 && blocks[length - 1].isZero() && blocks[length - 2].isZero()) {
result.append(getFormatSeparator());
}
result.append(unit);
}
} else {
String unit = formato.getUnidadeInteiraNoSingular();
if( !unit.isEmpty() ) {
result.append(" ").append(unit);
}
}
}
}
private void appendIntegers(StringBuffer result, ThousandBlock... blocks) {
boolean hasStarted = false;
for (int i = 0; i < blocks.length; i++) {
ThousandBlock thousandBlock = blocks[i];
if (!(hasStarted && thousandBlock.isZero())) {
int thousandPower = (blocks.length - i - 1);
if (hasStarted) {
if (thousandBlock.isUnitary()) {
result.append(getAndSeparator());
} else {
if (thousandPower < 1) {
result.append(getAndSeparator());
} else {
result.append(getThousandSeparator());
}
}
}
result.append(thousandBlock.toWords());
if (thousandPower > 0) {
result.append(" ");
result.append(this.getString("1e" + 3 * thousandPower + "."
+ (thousandBlock.isUnitary() ? "singular" : "plural")));
}
hasStarted = true;
}
}
}
private final class ThousandBlock {
private int numberValue;
public ThousandBlock(String number) {
super();
if (number.length() > 3) {
throw new IllegalArgumentException("ThousandBlock deve conter numeros" + " de no maximo 3 digitos.");
}
this.numberValue = Integer.parseInt(number);
}
public boolean isZero() {
return numberValue == 0;
}
public boolean isUnitary() {
return numberValue == 1;
}
public String toWords() {
String result;
if (numberValue <= 20) {
result = NumericToWordsConverter.this.getNumber(numberValue);
} else if (numberValue <= 99) {
result = getNumberUnder100(numberValue);
} else if (numberValue == 100) {
result = NumericToWordsConverter.this.getNumber(100);
} else {
int c = (numberValue / 100) * 100;
String centena;
if (c == 100) {
centena = NumericToWordsConverter.this.getString("100+?");
} else {
centena = NumericToWordsConverter.this.getNumber(c);
}
int resto = numberValue % 100;
if (resto == 0) {
result = centena;
} else {
result = centena + getAndSeparator() + getNumberUnder100(resto);
}
}
return result;
}
private String getNumberUnder100(int number) {
String result = null;
if (number <= 20) {
result = getNumber(number);
} else if (number <= 99) {
int d = number / 10;
int u = number % 10;
String dezena = getNumber(d * 10);
if (u == 0) {
result = dezena;
} else {
String unidade = getNumber(u);
result = dezena + getTensSeparator() + unidade;
}
}
return result;
}
}
private String getThousandSeparator() {
return getString("sep.mil");
}
private String getTensSeparator() {
return getString("sep.dezena");
}
private String getFormatSeparator() {
return getString("sep.formato");
}
private String getAndSeparator() {
return getString("sep");
}
private String getString(String paramMessage) {
return Messages.getString("Extenso." + paramMessage, locale);
}
private String getNumber(int number) {
DecimalFormat formater = new DecimalFormat("000");
String formatted = formater.format(number);
return Messages.getString("Extenso." + formatted, locale);
}
}