Package br.com.flexait.girinoextenso

Source Code of br.com.flexait.girinoextenso.NumeroPorExtenso

package br.com.flexait.girinoextenso;
import java.math.BigDecimal;
import java.util.*;

/**
* Esta classe traduz números de sua forma numeral para sua forma por extenso.
*
* @author girino
*
* Regras:
* 1. numeros abaixo de 20 tem nome próprio.
* 2. de 21 a 99 os numeros são formados por DEZENA 'e' UNIDADE (exemplo: "trinta e cinco")
* 3. dezenas redondas não tem nada depois (20 -> "vinte", e não "vinte e zero").
* 4. 100 tem nome próprio: cem
* 5. números maiores que cem são compostos por CENTENA 'e' extenso(numero % 100), onde extenso() é a função que converte números menores que 100.
*    (exemplos: cento e vinte e um, quinhentos e quarenta).
* 6. acima de 1000 agrupa-se em blocos de 3 dígitos (potências de 1000) onde a representação
*    descrita em 5 é usada internametne, posfixados pelo descritor do grupo (mil, milhão, etc). Os grupos são concatenados por virgula (opcional)
* 6a. A ultima concatenação é feita por "e".
* 6a1. A ultima concatenação é omitida (Ou substituida por vírgula) caso o ultimo grupo seja maior que 100 e não seja múltiplo e 100.
* 6b. o "um" em frente ao descritor de grupo "mil" é opcional e deve ser parametrizável.
*     Exemplos:
*               mil e um.
*               duzentos milhões, vinte mil e dez.
*               um milhão, dois mil duzentos e cinquenta.
*               um milhão, dois mil e quarenta e cinco.
* 7. Ao acrescentar a unidade (por exemplo, "reais") usa-se o prefixo "de" antes da undiade caso o ultimo seja de milhão ou maior.
*    Exemplos:
*              mil reais.
*              um milhão de reais.
*              dois bilhões de reais.
*              um milhão, cento e vinte mil duzentos e cinquenta reais.
*/

public class NumeroPorExtenso {

  /* parâmetros */
  private boolean milSemUm = false;
  private TipoSeparador separadorGrupo = TipoSeparador.VIRGULA;
  /* este parÂmetro não deveria existir, já que segundo as gramáticas omite-se apenas depois de mil */
  private TipoSeparador separadorUltimoGrupo = TipoSeparador.VIRGULA;
  private TipoSeparador separadorDepoisDeMil = TipoSeparador.VIRGULA;
  private boolean moeda;


  /**
   * O construtor permite que se crie novas instÂncias paramterizadas de forma distinta do padrão.
   * Os parametros opcionais definem:
   * 1. Se o descritor de milhares será prefixado por 1 quando for o caso ("um mil" ao invés de apenas "mil");
   * 2. Se a vírgula depois dos descritores de potencias de dez (mil, milhão, bilhão, etc)
   *    será omitda;
   * 3. Se haverá vírgula do descritor "mil" ("dez mil, duzentos e cinquenta" contra "dez mil duzentos e cinquenta").
   * 
   * @param milSemUm Caso seja "true", o "um" será omitido antes dos milhares.
   * @param virgulaEntreGrupos Caso seja true usa-se a vírgula, caso false, será omitida.
   * @param virgulaDepoisDeMil Caso seja true usa-se o que for determinado pelo
   *                 parametro virgulaEntreGrupos, caso seja false,
   *                 omite-se a virgula em todos os casos.
   */
  public NumeroPorExtenso(boolean milSemUm, boolean virgulaEntreGrupos, boolean virgulaDepoisDeMil) {
    this.milSemUm = milSemUm;
    separadorGrupo = virgulaEntreGrupos?TipoSeparador.VIRGULA:TipoSeparador.NENHUM;
    separadorUltimoGrupo = separadorGrupo;
    // virgula depois de mil só faz sentido se o separador for virgula
    separadorDepoisDeMil = virgulaDepoisDeMil?separadorGrupo:TipoSeparador.NENHUM;
  }
 
  private static NumeroPorExtenso defaultInstance = new NumeroPorExtenso(false, true, true);
  /**
   * singleton
   * @return a instância default.
   */
  public static NumeroPorExtenso getDefaultInstance() {
    return defaultInstance;
  }
 

  private enum TipoSeparador {
    NENHUM(" "),
    E(" e "),
    VIRGULA(", "),
    DE(" de ");

    String separadorStr;

    TipoSeparador(String separadorStr) {
      this.separadorStr = separadorStr;
    }

    public String getSeparador() {
      return separadorStr;
    }
  }

  public static final String ZERO = "zero";
  public static final String CEM = "cem";
  public static final BigDecimal C_1000 = new BigDecimal(1000);
  public static final BigDecimal C_100 = new BigDecimal(100);
  public static final BigDecimal C_0 = new BigDecimal(0);

  public static String[] UNIDADES = {
    "", "um", "dois", "tr\u00EAs", "quatro", "cinco", "seis", "sete", "oito", "nove",
      "dez", "onze", "doze", "treze", "quatorze","quinze","dezesseis","dezessete","dezoito","dezenove"
  };

  public static String[] DEZENAS = {
    "","", "vinte", "trinta", "quarenta", "cinquenta", "sessenta", "setenta", "oitenta", "noventa"
  };

  public static String[] CENTENAS = {
    "", "cento", "duzentos", "trezentos", "quatrocentos", "quinhentos", "seiscentos", "setecentos", "oitocentos", "novecentos"
  };

  public static String[] ordensSingular= {
    "", "mil", "milh\u00E3o", "bilh\u00E3o", "trilh\u00E3o", "quatrilh\u00E3o", "quintilh\u00E3o", "sextilh\u00E3o", "setilh\u00E3o", "octilh\u00E3o", "nonilh\u00E3o",
     "decilh\u00E3o", "undecilh\u00E3o", "dodecilh\u00E3o", "tredecilh\u00E3o", "quatordecilh\u00E3o", "quindecilh\u00E3o", "sedecilh\u00E3o", "septendecilh\u00E3o"
  };

  public static String[] ordensPlural = {
    "", "mil", "milh\u00F5es", "bilh\u00F5es", "trilh\u00F5es", "quatrilh\u00F5es", "quintilh\u00F5es", "sextilh\u00F5es", "setilh\u00F5es", "octilh\u00F5es",
    "decilh\u00F5es", "undecilh\u00F5es", "dodecilh\u00F5es", "tredecilh\u00F5es", "quatordecilh\u00F5es", "quindecilh\u00F5es", "sedecilh\u00F5es", "septendecilh\u00F5es"
  };

        /**
         * converte valores menores que 20.
         */
  String unidades(int n) {
    if (n == 0) return ZERO;
    if (n < UNIDADES.length) return UNIDADES[n];
    throw new RuntimeException("valor: " + n);
  }

  /**
   * Converte valores menores que 100. Utiliza o método unidades() auxiliarmente.
   */
  String dezenas(int n) {
    if (n < UNIDADES.length) return unidades(n);
    int unidade = n % 10;
    n = n / 10;
    // ignora o 0 em dezenas redondas (20, 30, 40, etc)
    String unidadeStr = "";
    if (unidade != 0) {
      unidadeStr = TipoSeparador.E.getSeparador() + unidades(unidade);
    }
    if (n < DEZENAS.length) return DEZENAS[n] + unidadeStr;
    throw new RuntimeException();
  }

  /**
   * Converte valores menores que 1000 usando dezenas() auxiliarmente.
   */
  String centenas(int n) {
    // caso especial "100", tratado a parte.
    if (n == 100) return CEM;
    if (n < 100) return dezenas(n);
    // separa centenas e dezenas
    int rem = n % 100;
    n = n/100;
    String dezenaStr = "";
    if (rem != 0) {
      dezenaStr = TipoSeparador.E.getSeparador() + dezenas(rem);
    }
    if (n < CENTENAS.length) return CENTENAS[n] + dezenaStr;
    throw new RuntimeException();
  }

  /**
   * monta grupos de 3 dígitos junto com seu sufixo, identificando o
   * uso de plural quando necessário.
   */
  String montaGrupo(int n, int grupo) {
    if (n == 0) return "";
    String nomeGrupo = "";
    if (n == 1) {
      // mil é exceção, regra 6b.
      if (grupo == 1 && milSemUm) {
        return ordensSingular[grupo];
      }
      nomeGrupo = ordensSingular[grupo];
    } else {
      nomeGrupo = ordensPlural[grupo];
    }
    if (grupo == 0) return centenas(n); // pra evitar "trailling space"
    return centenas(n) + TipoSeparador.NENHUM.getSeparador() + nomeGrupo;
  }

  private class TuplaGrupo {
    Integer valor;
    Integer posicao;
    String extenso;
    public TuplaGrupo(Integer valor, Integer posicao, String extenso) {
      this.valor = valor;
      this.posicao = posicao;
      this.extenso = extenso;
    }
    public Integer getValor() {
      return this.valor;
    }
    public Integer getPosicao() {
      return this.posicao;
    }
    public String getExtenso() {
      return this.extenso;
    }
  }

  protected List<TuplaGrupo> montaGrupos(BigDecimal n) {
    List<TuplaGrupo> grupos = new ArrayList<TuplaGrupo>();
    int i = 0;
    while (n.compareTo(C_0) > 0) {
      BigDecimal[] tmp = n.divideAndRemainder(C_1000);
      if (tmp[1].intValue() > 0) {
        TuplaGrupo grupo = new TuplaGrupo(tmp[1].intValue(), i, montaGrupo(tmp[1].intValue(), i));
        grupos.add(grupo);
      }
      n = tmp[0];
      i++;
    }
    return grupos;
  }

  protected String reagrupa(List<TuplaGrupo> grupos) {
    // avalia regra 6a1: ultimo grupo é > 100 e não é multiplo de 100
    boolean omiteUltimoSeparador = false;
    if (grupos.get(0).getValor() > 100 && grupos.get(0).getValor() % 100 != 0) {
      omiteUltimoSeparador = true;
    }
    Iterator<TuplaGrupo> it = grupos.iterator();
    String ret = it.next().getExtenso();
    if (!omiteUltimoSeparador && it.hasNext()) {
      ret = it.next().getExtenso() + TipoSeparador.E.getSeparador() + ret;
    } else if (it.hasNext()) {
      TuplaGrupo grupo = it.next();
      TipoSeparador separador = grupo.getPosicao().equals(1)?separadorDepoisDeMil:separadorUltimoGrupo;
      ret = grupo.getExtenso() + separador.getSeparador() + ret;
    }
    while (it.hasNext()) {
      ret = it.next().getExtenso() + separadorGrupo.getSeparador() + ret;
    }
    return ret;
  }

  /**
   * Implementa a regra 6:
   * Separa em grupos de 3 dígitos (potencias de 1000), processa os grupos separadamente
     * e depois reagrupa, separando por virgula exceto no ultimo separador, que pode ser
   * "e" ou entao ser omitido (regra 6a e 6a1).
   **/
  public  <T extends Number> String converteInteiro(T number) {
    BigDecimal n = new BigDecimal(number.toString());
    // na verdade a penas o 0 precisa ser exceção, mas isso poupa esforço.
    if (n.compareTo(C_1000) < 0) return centenas(n.intValue());

    List<TuplaGrupo> grupos = montaGrupos(n);
    return reagrupa(grupos);
  }

  String principal(BigDecimal n, String[] nomesMoeda) {
    String nomeMoeda = nomesMoeda[1];
    if (n.compareTo(BigDecimal.ONE) == 0) {
      nomeMoeda = nomesMoeda[0];
    }
    if (n.compareTo(C_1000) < 0) return centenas(n.intValue()) + TipoSeparador.NENHUM.getSeparador() + nomeMoeda;
    List<TuplaGrupo> grupos = montaGrupos(n);
    TipoSeparador separador = TipoSeparador.NENHUM;
    // se for maior que milhão, usa "de" (regra 7).
    if (grupos.get(0).getPosicao() >= 2) {
      separador = TipoSeparador.DE;
    }
    return reagrupa(grupos) + separador.getSeparador() + nomeMoeda;
  }

  String subdivisao(BigDecimal n, String[] nomesSubdivisao, int escalaSubdivisao) {
    // basicamente é executar o principal da subdivisao * escala.
    return principal(n.multiply(new BigDecimal(escalaSubdivisao)).divideToIntegralValue(BigDecimal.ONE), nomesSubdivisao);
  }

  /**
   * Similar ao converteInteiro(), mas usa o sufixo da moeda fornecido (e.g. real, dollar, etc)
   * e computa as casas decimais (centavos) de acordo com a escala fornecida e
   * usando os nomes fornecidos para a subdivisão da moeda.
   *
   * É necessário os nomes no singular e no plural para a moeda, bem como para a subdivisão.
   * A escala deve ser preferencialmente um múltiplo de 10, já que a notação original é decimal.
   * O funcionamento para escalas diferentes desse formato não é garantido.
   *
   * @param n Número a ser convertido.
   * @param nomesMoeda vetor contendo o nome no singular e no plural da moeda.
   * @param nomesSubdivisao vetor contendo o nome no singular e no plural da subdivisão da moeda.
   * @param escalaSubdivisao escala usada na subdivisão (100 para centavos).
   * @return O valor escrito por extenso.
   */
  public  <T extends Number> String converteMoeda(T number, String[] nomesMoeda, String[] nomesSubdivisao, int escalaSubdivisao) {
    BigDecimal n = new BigDecimal(number.toString());
   
    if(moeda) {
      n = n.setScale(2, BigDecimal.ROUND_HALF_UP);
    }
   
    BigDecimal[] split = n.divideAndRemainder(BigDecimal.ONE);

    // se não houver centavos, só retorna o principal
    if (split[1].compareTo(BigDecimal.ZERO) == 0) {
      return principal(split[0], nomesMoeda);
    }
    // se não houver principal, só retorna os centavos
    if (split[0].compareTo(BigDecimal.ZERO) == 0) {
      return subdivisao(split[1], nomesSubdivisao, escalaSubdivisao);
    }
    return principal(split[0], nomesMoeda) + TipoSeparador.E.getSeparador() + subdivisao(split[1], nomesSubdivisao, escalaSubdivisao);
  }
  /**
   * Este método é usado quando a moeda é o real (BRL), subdividido em centavos.
   *
   * @param n Número a ser convertido.
   * @return O valor escrito por extenso.
   */
  public <T extends Number> String converteMoeda(T numero) {
    return arredondado().converteMoeda(numero, new String[] {"real", "reais"}, new String[] {"centavo", "centavos"}, 100);
  }
 
  /**
   * Este método configura a api para arredondar em duas casas decimais
   * @return A instância da classe
   */
  public NumeroPorExtenso arredondado() {
    moeda = true;
    return this;
  }
}
TOP

Related Classes of br.com.flexait.girinoextenso.NumeroPorExtenso

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.