Olá pessoal.
Estou fazendo os exercícios opcionais do capítulo 4.5 da apostila FJ-22 da Caelum. A leitura de dados de um XML, para popular a classe Negociacao do projeto do mercado de valores (“Argentum”).
Acontece que apesar de validar no construtor os valores obrigatórios, ao passar um XML incompleto, é criada uma negociação com valores null, gerando NPE, quando deveria na verdade pular o objeto ou acusar durante a leitura do XML.
Seguem as implementações da Negociação e do LeitorXML, bem como o teste JUnit da Negociação incompleta e um teste com log para mostrar a situação.
Suspeito que o XStream está usando Reflection para converter e atualizar os valores das variáveis da Negociação como último recurso, e isso está causando o problema supracitado.
Negociacao.java
package br.com.caelum.argentum.modelo;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Calendar;
public final class Negociacao {
private final BigDecimal preco;
private final int quantidade;
private final Calendar data;
@Deprecated
public Negociacao(double preco, int quantidade, Calendar data) {
this(BigDecimal.valueOf(preco),quantidade,data);
}
public Negociacao(BigDecimal preco, int quantidade, Calendar data) {
if (data == null)
throw new IllegalArgumentException("data nao pode ser nula");
if (preco == null)
throw new IllegalArgumentException("preco nao pode ser nulo");
else if (preco.compareTo(BigDecimal.ZERO) != 1)
throw new IllegalArgumentException("valor deve ser maior que 0");
if (quantidade <= 0)
throw new IllegalArgumentException("quantidade deve ser maior que 0");
this.preco = preco;
this.quantidade = quantidade;
this.data = (Calendar) data.clone();
}
public BigDecimal getPreco() {
return preco;
}
public int getQuantidade() {
return quantidade;
}
public Calendar getData() {
return (Calendar) data.clone();
}
public BigDecimal getVolume() {
return preco.multiply(BigDecimal.valueOf(quantidade));
}
@Override
public String toString() {
return "Data: " + new SimpleDateFormat("dd/MM/yyyy").format(data.getTime()) + "\n"
+ "Quantidade: " + quantidade + "\n"
+ "Preço: " + preco + "\n"
+ "Volume: " + getVolume() + "\n";
}
}
LeitorXML.java
package br.com.caelum.argentum.reader;
import java.io.InputStream;
import java.util.List;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import br.com.caelum.argentum.modelo.Negociacao;
public class LeitorXML {
public List<Negociacao> carrega(InputStream inputStream) {
XStream stream = new XStream(new DomDriver());
stream.autodetectAnnotations(true);
stream.alias("negociacao", Negociacao.class);
@SuppressWarnings("unchecked")
List<Negociacao> negocios = (List<Negociacao>) stream.fromXML(inputStream);
return negocios;
}
}
LeitorXMLTest.java
package br.com.caelum.argentum.reader;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.List;
import org.junit.Test;
import br.com.caelum.argentum.modelo.Negociacao;
public class LeitorXMLTest {
/**
* Stub para exibir a Negociacao criada com null
* @param args
*/
public static void main (String[] args) {
String xmlDeTeste =
"<list>" +
"<negociacao>" +
//"<preco>15.35</preco>" +
"<quantidade>1000</quantidade>" +
"<data>" +
"<time>1322233344455</time>" +
"</data>" +
"</negociacao>" +
"</list>";
InputStream xml = new ByteArrayInputStream(xmlDeTeste.getBytes());
List<Negociacao> list = new LeitorXML().carrega(xml);
if (!list.isEmpty()) {
System.out.println("Lista não vazia");
Negociacao n = list.get(0);
System.out.println("Preço: " + n.getPreco());
System.out.println("Quantidade: " + n.getQuantidade());
System.out.println("Tempo (ms): " + n.getData().getTimeInMillis());
}
}
@Test
public void carregaXmlComUmaNegociacaoEmListaUnitaria() {
String xmlDeTeste = "<list>"+
"<negociacao>" +
"<preco>43.5</preco>" +
"<quantidade>1000</quantidade>" +
"<data>" +
"<time>1322233344455</time>" +
"</data>" +
"</negociacao>" +
"</list>";
InputStream xml = new ByteArrayInputStream(xmlDeTeste.getBytes());
List<Negociacao> negocios = new LeitorXML().carrega(xml);
assertEquals(1,negocios.size());
assertEquals(43.5,negocios.get(0).getPreco().doubleValue(),0.00001);
assertEquals(1000, negocios.get(0).getQuantidade());
}
@Test
public void carregaXmlComNenhumaNegociacao() {
String xmlDeTeste = "<list></list>";
InputStream xml = new ByteArrayInputStream(xmlDeTeste.getBytes());
List<Negociacao> negocios = new LeitorXML().carrega(xml);
assertTrue("lista deve estar vazia",negocios.isEmpty());
}
@Test
public void carregaXmlComNegociacaoSemPreco() {
String xmlDeTeste = "<list>" +
"<negociacao>" +
"<quantidade>1000</quantidade>" +
"<data>" +
"<time>1322233344455</time>" +
"</data>" +
"</negociacao>" +
"</list>";
InputStream xml = new ByteArrayInputStream(xmlDeTeste.getBytes());
List<Negociacao> list = new LeitorXML().carrega(xml);
assertEquals(0, list.size());
}
@Test
public void carregaXmlComNegociacaoSemQuantidade() {
String xmlDeTeste = "<list>"+
"<negociacao>" +
"<preco>43.5</preco>" +
"<data>" +
"<time>1322233344455</time>" +
"</data>" +
"</negociacao>" +
"</list>";
InputStream xml = new ByteArrayInputStream(xmlDeTeste.getBytes());
List<Negociacao> list = new LeitorXML().carrega(xml);
assertEquals(0, list.size());
}
@Test
public void carregaXmlComVariasNegociacoesEmLista() {
String xmlDeTeste = "<list>"+
"<negociacao>" +
"<preco>43.5</preco>" +
"<quantidade>1000</quantidade>" +
"<data>" +
"<time>1322233344455</time>" +
"</data>" +
"</negociacao>" +
"<negociacao>" +
"<preco>43.5</preco>" +
"<quantidade>1000</quantidade>" +
"<data>" +
"<time>1322233344455</time>" +
"</data>" +
"</negociacao>" +
"<negociacao>" +
"<preco>43.5</preco>" +
"<quantidade>1000</quantidade>" +
"<data>" +
"<time>1322233344455</time>" +
"</data>" +
"</negociacao>" +
"</list>";
InputStream xml = new ByteArrayInputStream(xmlDeTeste.getBytes());
List<Negociacao> negocios = new LeitorXML().carrega(xml);
assertEquals(3,negocios.size());
assertEquals(43.5,negocios.get(0).getPreco().doubleValue(),0.00001);
assertEquals(1000, negocios.get(0).getQuantidade());
}
}