Philippe Gioseffi
Philippe Gioseffi

Reputation: 1658

First element inserted in a Map is always read as null

I have a Java 8 web application that reads a CSV file and maps its headers and values into a map. For some reason I don't know why the first element inserted into this map I never get to retrieve its value. I've already changed the CSV file colums order and the problem occurs the exactly same way.

When I debug my map I have it as follows:

enter image description here

As you can see I have the entry "Natureza" and its value is "RECEITA". That was the first element inserted into the map. But when I try to retrieve its value I always get null as an answer as you can see ahead:

enter image description here

Here's the method code that parses the CSV file into my entity:

@Transactional
public void processarArquivoCSV(final MultipartFile file) throws IOException {
    if (file != null) {
        try (InputStream arquivo = file.getInputStream()) {
            final String[] fileAsStrArray = Constantes.PATTERN_QUEBRA_LINHA
                    .split(IOUtils.toString(arquivo, StandardCharsets.UTF_8));
            final String[] header = Constantes.PATTERN_VIRGULA.split(fileAsStrArray[0]);
            final List<Titulo> titulos = new ArrayList<>();
            final Collection<String> todosErros = new ArrayList<>();
            final FormaPagamento formaPagamentoImportacao = this.formaPagamentoService
                    .obterFormaPagamentoDesbloqueadaPorDescricaoAsEntity("IMPORTADO");
            final ClassificacaoFinanceira cfContaAzulReceita = this.classificacaoFinanceiraService
                    .findClassificacaoFinanceiraContaAzulPorNatureza(NaturezaFinanceiraEnum.RECEITA);
            final ClassificacaoFinanceira cfContaAzulDespesa = this.classificacaoFinanceiraService
                    .findClassificacaoFinanceiraContaAzulPorNatureza(NaturezaFinanceiraEnum.DESPESA);

            for (int i = 1; i < fileAsStrArray.length; i++) {
                final String[] linhaCSV = Constantes.PATTERN_VIRGULA.split(fileAsStrArray[i]);
                if (header.length != linhaCSV.length) {
                    todosErros.add(String.format(
                            "Registro %d inválido. Número de colunas diferente do número de informações. Número de colunas totalizando %d e número de informações totalizando %d.",
                            Integer.valueOf(i), Integer.valueOf(header.length), Integer.valueOf(linhaCSV.length)));
                    continue;
                }

                final Map<String, String> tituloMap = new ConcurrentHashMap<>();
                for (int j = 0; j < header.length; j++) {
                    tituloMap.put(header[j], linhaCSV[j]);
                }

                final Titulo titulo = Titulo.builder().pisRetido(BigDecimal.ZERO).cofinsRetido(BigDecimal.ZERO)
                        .csllRetido(BigDecimal.ZERO).irrfRetido(BigDecimal.ZERO).inssRetido(BigDecimal.ZERO)
                        .issRetido(BigDecimal.ZERO).valorBase(BigDecimal.ZERO)
                        .formaPagamento(formaPagamentoImportacao).build();
                final Collection<String> erros = new ArrayList<>();
                final String situacao = tituloMap.get("Situacao");
                if (StringUtils.isNotBlank(situacao)) {
                    titulo.setSituacao(
                            (short) SituacaoFinanceiraEnum.valueOf(situacao).getValorSituacaoFinanceira());
                } else {
                    erros.add("Situação do título não pode ser vazia ou nula.");
                }

                final String natureza = tituloMap.get("Natureza").toUpperCase(Constantes.LOCALE_PT_BR);
                final NaturezaFinanceiraEnum naturezaEnum = NaturezaFinanceiraEnum.valueOf(natureza);
                final short naturezaAsShort = (short) naturezaEnum.getValorNaturezaFinanceira();
                if (StringUtils.isNotBlank(natureza)) {
                    titulo.setNatureza(naturezaAsShort);
                }

                final String nomeEstabelecimento = tituloMap.get("Estabelecimento");
                if (StringUtils.isNotBlank(nomeEstabelecimento)) {
                    final Optional<Estabelecimento> estabelecimentoOpt = this.estabelecimentoService
                            .findByDescricaoOuNomeFantasia(nomeEstabelecimento.trim());
                    if (estabelecimentoOpt.isPresent()) {
                        titulo.setEstabelecimento(estabelecimentoOpt.get());
                    } else {
                        erros.add("O estabelecimento informado é inexistente.");
                    }
                } else {
                    erros.add("O estabelecimento do título deve ser informado.");
                }

                final String vencimento = tituloMap.get("Data de vencimento");
                if (StringUtils.isNotBlank(vencimento)) {
                    try {
                        titulo.setVencimento(DateTimeUtil.DATE_FORMATTER_PT_BR.parse(vencimento, LocalDate::from));
                    } catch (DateTimeParseException | IndexOutOfBoundsException e) {
                        erros.add(String.format(
                                "A data de vencimento do título é inválida. Data de vencimento informada: %s. Erro encontrado: %s.",
                                vencimento, e.getMessage()));
                    }
                } else {
                    erros.add("A data de vencimento do título não pode ser nula.");
                }

                final String competenciaAsString = tituloMap.get("Data de Competência");
                if (StringUtils.isNotBlank(competenciaAsString)) {
                    try {
                        final LocalDate competencia = DateTimeUtil.DATE_FORMATTER_PT_BR.parse(competenciaAsString,
                                LocalDate::from);
                        titulo.setCompetencia(competencia);
                        titulo.setEmissao(competencia);
                    } catch (DateTimeParseException | IndexOutOfBoundsException e) {
                        erros.add(String.format(
                                "A data de competência do título é inválida. Data de competência informada: %s. Erro encontrado: %s.",
                                competenciaAsString, e.getMessage()));
                    }
                } else {
                    erros.add("A data de vencimento do título não pode ser nula.");
                }

                final String valor = tituloMap.get("Valor original");
                final boolean isReceita = NaturezaFinanceiraEnum.RECEITA == naturezaEnum;
                if (StringUtils.isNotBlank(valor)) {
                    final DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(Constantes.LOCALE_PT_BR);
                    df.setParseBigDecimal(true);
                    try {
                        final BigDecimal valorAsBigDecimal = (BigDecimal) df.parseObject(valor);
                        titulo.setValor(valorAsBigDecimal);

                        final RateioFinanceiro rateio = RateioFinanceiro.builder().valor(valorAsBigDecimal).build();

                        final String nomeClassificacaoFinanceira = tituloMap.get("Categoria");
                        if (StringUtils.isNotBlank(nomeClassificacaoFinanceira)) {
                            // TODO: Pesquisar os filhos do Conta Azul antes de criar.
                            rateio.setClassificacaoFinanceira(this.classificacaoFinanceiraService
                                    .findByDescricaoAndPaiContaAzul(nomeClassificacaoFinanceira.trim(),
                                            (isReceita ? cfContaAzulReceita : cfContaAzulDespesa).getGid())
                                    .map(cf -> cf)
                                    .orElseGet(() -> this.classificacaoFinanceiraService
                                            .save(ClassificacaoFinanceira.builder()
                                                    .paiId(isReceita ? cfContaAzulReceita : cfContaAzulDespesa)
                                                    .natureza(Short.valueOf(
                                                            (short) naturezaEnum.getValorNaturezaFinanceira()))
                                                    .codigo(nomeClassificacaoFinanceira)
                                                    .codigoAuxiliar(nomeClassificacaoFinanceira)
                                                    .descricao(nomeClassificacaoFinanceira)
                                                    .descricaoAuxiliar(nomeClassificacaoFinanceira).build())));
                        }

                        final String descricaoCentroCusto = tituloMap.get("Centro de custo");
                        if (StringUtils.isNoneBlank(descricaoCentroCusto)) {
                            rateio.setCentroCusto(
                                    this.centroCustoService.findByDescricao(descricaoCentroCusto).map(cc -> cc)
                                            .orElseGet(() -> this.centroCustoService
                                                    .save(CentroCusto.builder().codigo(descricaoCentroCusto)
                                                            .descricao(descricaoCentroCusto).build())));
                        }

                        titulo.setDocumentoRateado(DocumentoRateado.builder().valor(valorAsBigDecimal)
                                .rateiosFinanceiros(CollectionUtil.buildSingletonModifiableList(rateio)).build());
                    } catch (final ParseException e) {
                        erros.add(String.format(
                                "O valor do título é inválido. Valor informado: %s. Erro encontrado: %s.", valor,
                                e.getMessage()));
                    }
                } else {
                    erros.add("O valor do título não pode ser nulo ou vazio.");
                }

                final String numero = tituloMap.get("Descrição");
                if (StringUtils.isNotBlank(numero)) {
                    if (numero.length() > 30) {
                        erros.add("O número do título não pode conter mais do que trinta caracteres.");
                    } else {
                        titulo.setNumero(numero);
                        titulo.setDocumentoVinculado("ContaAzul-" + numero);
                    }
                } else {
                    erros.add("O número do título não pode ser nulo ou vazio.");
                }

                final String numeroDocumento = tituloMap.get("CNPJ / CPF");
                final String nomeCliente = tituloMap.get("Cliente");
                final String numeroDocumentoParseado = Constantes.PATTERN_ONLY_DIGITS.matcher(numeroDocumento)
                        .replaceAll(StringUtils.EMPTY);
                if (StringUtils.isBlank(numeroDocumento) && StringUtils.isBlank(nomeCliente)) {
                    erros.add("Deve ser informado pelo menos o nome ou o documento do cliente.");
                } else if (nomeCliente.length() > 150) {
                    erros.add("Nome do cliente não deve conter mais do que 150 caracteres.");
                } else if (numeroDocumentoParseado.length() > 14) {
                    erros.add("Número do documento do cliente não deve conter mais do que 14 caracteres.");
                } else if (StringUtils.isNotBlank(numeroDocumentoParseado)) {
                    if (!VitaiUtil.isCNPJValido(numeroDocumentoParseado)
                            && !VitaiUtil.isCPFValido(numeroDocumentoParseado)) {
                        erros.add("O número do documento do cliente não é válido como CNPJ ou como CPF.");
                    } else {
                        final Optional<Pessoa> pessoa = this.pessoaService
                                .findByNumeroDocumento(numeroDocumentoParseado);
                        if (pessoa.isPresent()) {
                            titulo.setPessoa(pessoa.get());
                        } else if (StringUtils.isNotBlank(nomeCliente)) {
                            final Optional<Pessoa> pessoaBackup = this.pessoaService
                                    .findByNomeOuNomeFantasia(nomeCliente);
                            if (pessoaBackup.isPresent()) {
                                titulo.setPessoa(pessoaBackup.get());
                            } else {
                                titulo.setPessoa(this.pessoaService.save(Pessoa.builder().nome(nomeCliente)
                                        .nomeFantasia(nomeCliente).numeroDocumento(numeroDocumento)
                                        .naturezaPessoa(Short.valueOf(naturezaAsShort)).bloqueado(false).build()));
                            }
                        } else {
                            erros.add(
                                    "Pessoa não encontrada e falta o nome do cliente para poder persistí-lo na base.");
                        }
                    }
                } else {
                    final Optional<Pessoa> pessoaBackup = this.pessoaService.findByNomeOuNomeFantasia(nomeCliente);
                    if (pessoaBackup.isPresent()) {
                        titulo.setPessoa(pessoaBackup.get());
                    } else {
                        erros.add(
                                "Pessoa não encontrada e falta o número do documento do cliente para poder persistí-lo na base.");
                    }
                }

                final String valorBaixado = tituloMap.get(isReceita ? "Valor recebido" : "Valor pago");
                if (StringUtils.isNotBlank(valorBaixado)) {
                    final DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(Constantes.LOCALE_PT_BR);
                    df.setParseBigDecimal(true);

                    try {
                        titulo.setValorBaixado((BigDecimal) df.parseObject(valorBaixado));
                    } catch (final ParseException e) {
                        erros.add(String.format(
                                "O valor baixado do título é inválido. Valor baixado informado: %s. Erro encontrado: %s.",
                                valor, e.getMessage()));
                    }
                } else {
                    erros.add("O valor baixado do título não pode ser nulo ou vazio.");
                }

                final String juros = tituloMap.get("Juros/Multa");
                if (StringUtils.isNotBlank(juros)) {
                    final DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(Constantes.LOCALE_PT_BR);
                    df.setParseBigDecimal(true);

                    try {
                        titulo.setJuros((BigDecimal) df.parseObject(juros));
                    } catch (final ParseException e) {
                        erros.add(String.format(
                                "O valor do juros/multa do título é inválido. Valor baixado informado: %s. Erro encontrado: %s.",
                                valor, e.getMessage()));
                    }
                } else {
                    erros.add("O valor do juros/multa do título não pode ser nulo ou vazio.");
                }

                final String desconto = tituloMap.get("Descontos/Taxas");
                if (StringUtils.isNotBlank(desconto)) {
                    final DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(Constantes.LOCALE_PT_BR);
                    df.setParseBigDecimal(true);

                    try {
                        titulo.setJuros((BigDecimal) df.parseObject(desconto));
                    } catch (final ParseException e) {
                        erros.add(String.format(
                                "O valor do desconto do título é inválido. Valor baixado informado: %s. Erro encontrado: %s.",
                                valor, e.getMessage()));
                    }
                } else {
                    erros.add("O valor do desconto do título não pode ser nulo ou vazio.");
                }

                final String conta = tituloMap.get("Conta");
                if (StringUtils.isNoneBlank(conta)) {
                    this.contaService.findByCodigoOuNome(conta).ifPresent(titulo::setConta);
                }

                final String observacao = tituloMap.get("Observações");
                if (StringUtils.isNoneBlank(observacao)) {
                    titulo.setObservacao(observacao);
                }

                titulo.setHistorico("Título criado pela importação de títulos Conta Azul.");

                if (!erros.isEmpty()) {
                    todosErros.add(String.format(
                            "Falha ao mapear registro %d do arquivo CSV para título. Registro não atende as especificações. ERRO(S):%n%s",
                            Integer.valueOf(i), String.join(Constantes.QUEBRA_LINHA_STR, erros)));
                } else {
                    titulos.add(titulo);
                }
            }

            if (!titulos.isEmpty()) {
                this.saveAll(titulos);
            }

            if (!todosErros.isEmpty()) {
                final String todosErrosAsStr = String.join(Constantes.QUEBRA_LINHA_STR, todosErros);
                TituloService.log.error(todosErrosAsStr);
                throw new RuntimeException(todosErrosAsStr);
            }
        }
    }
}

Does anyone have a clue what I might be doing wrong in this first insertion into the map?

EDIT:

After Holger comments I edited my solution and despite not solving my problem it led the application to be much faster. Here's the edited code:

try (BufferedReader reader = new BufferedReader(new InputStreamReader(file.getInputStream()))) {
    final List<Titulo> titulos = new ArrayList<>();
    final Collection<String> todosErros = new ArrayList<>();
    final String[] header = Constantes.PATTERN_VIRGULA.split(reader.readLine());
    String linhaCSV = StringUtils.EMPTY;
    final FormaPagamento formaPagamentoImportacao = this.formaPagamentoService
            .obterFormaPagamentoDesbloqueadaPorDescricaoAsEntity("IMPORTADO");
    final ClassificacaoFinanceira cfContaAzulReceita = this.classificacaoFinanceiraService
            .findClassificacaoFinanceiraContaAzulPorNatureza(NaturezaFinanceiraEnum.RECEITA);
    final ClassificacaoFinanceira cfContaAzulDespesa = this.classificacaoFinanceiraService
            .findClassificacaoFinanceiraContaAzulPorNatureza(NaturezaFinanceiraEnum.DESPESA);
    int i = 0;

    while ((linhaCSV = reader.readLine()) != null) {
        final String[] linhaCSVParseada = Constantes.PATTERN_VIRGULA.split(linhaCSV);
        if (header.length != linhaCSVParseada.length) {
            todosErros.add(String.format(
                    "Registro %d inválido. Número de colunas diferente do número de informações. Número de colunas totalizando %d e número de informações totalizando %d.",
                    Integer.valueOf(i), Integer.valueOf(header.length),
                    Integer.valueOf(linhaCSVParseada.length)));
            continue;
        }

        final Map<String, String> tituloMap = new HashMap<>(header.length);
        for (int j = 0; j < header.length; j++) {
            tituloMap.put(header[j], linhaCSVParseada[j]);
        }
    // The rest is the same as the first code provided...
    }
}

Upvotes: 0

Views: 122

Answers (1)

racraman
racraman

Reputation: 5034

The file starts with non-printable character(s), which becomes part of the key value for the first column only - which means both that the IDE isn’t displaying the complete key, and searching the map using only printable characters would not find an entry.

Upvotes: 1

Related Questions