Reputation: 1658
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:
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:
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
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