Trabalhando com coleções usando JSP e JSTL
Por Guilherme de Almeida Moreira em 22/08/06Sou novo na Caelum e estou em um projeto usando JSP 2 e JSTL como view. Tenho um problema extremamente simples, e que deve ser comum a todos: como descobrir o size() de uma Collection. E como posso chamar o contains() dela, ou outro método qualquer, sem ser um getter?
O Paulo Silveira disse que é aí que ele sente saudades do velocity, que lá você poderia escrever algo como $colecao.contains(x) e $colecao.size que tudo funcionava. Usando a expression language em um JSP isso não funciona, pois você só pode invocar getters. $colecao.size iria chamar colecao.getSize(), que obviamente não funciona. No Velocity e outros engines de template (como o freemarker), ele vai tentar arrancar o size de várias maneiras, inclusive fazendo .size() por reflection.
Ele ainda disse que muita gente que fez a especificação da expression language eram desenvolvedores do velocity, mas que esse tipo de recurso eles não colocaram no JSP pois você estaria colocando regras de negócios dentro da sua camada View, quebrando o MVC.
O Paulo costuma criar uma taglib com funçõezinhas estáticas para chamar do jsp, criando um .tld dentro do seu WEB-INF, por exemplo:
<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.0">
<tlib-version>1.1</tlib-version>
<uri>http://www.caelum.com.br/taglib</uri>
<function>
<name>contains</name>
<function-class>br.com.caelum.util.TagLibrary</function-class>
<function-signature>
boolean contains(java.util.Collection , br.com.caelum.nomedoprojeto.Option)
</function-signature>
</function>
</taglib>
Aí, você cria uma classe cheia de métodos estáticos (realmente não é elegante!):
public class TagLibrary {
public static boolean contains(Collection options, Option option) {
return options == null ? false : options.contains(option);
}
}
E acaba usando na página:
<%@ taglib uri="http://www.caelum.com.br/taglib" prefix="m" %>
<c:if test="${m:contains(listOfSelected, option)}">checked</c:if>
Já o Sérgio Lopes disse que você poderia colocar esse linha dentro de um scriptlet, desde que ela esteja dentro de um .tag. Depois o Paulo e o Sérgio começaram a brigardiscutir sobre as duas opções, e no fundo me parece que nenhum deles gosta muito de nenhuma das duas.
Uma outra saída seria ter alguns métodos que fazem esse trabalho nos seus value objects, como por exemplo ter getTamanhoDaColecao() . Mas isso nem sempre é possível e começa a gerar uma quantidade de métodos enorme que são totalmente redundantes.
Então eu pergunto, o que fazer para resolver esse tipo de problema com JSP e JSTL?
Use o fn:length do JSTL 1.1
http://java.sun.com/products/jsp/jstl/1.1/docs/tlddocs/fn/length.fn.html
http://www.onjava.com/pub/a/onjava/2003/11/05/jsp.html?page=2
Comment by Fernando Boaglio — August 22, 2006 @ 6:16 pm
É, o Urubatan veio comentar comigo que o
lengthfunciona não só paraStringe arrays. Mas de qualquer forma o problema docontainse outros métodos continua.Comment by Paulo Silveira — August 22, 2006 @ 6:19 pm
Como estavamos conversando pelo MSN, o contains realmente não tem pronto, e pelo menos na minha opinião, ele não deveria ser feito na JSP, e sim pela lógica de negócio antes de chegar na página
Mas se realmente for necessário fazer isto na página, acho que tanto a TagFile quanto a função são boas opções …
Passei por um problema semelhante a umas duas semanas mais ou menos, tinha situações semelhantes em 4 páginas diferentes, com 4 tipos de objetos diferentes dentro do mesmo sistema …
o que fiz foi criar uma classe:
public class Selected{
private boolean checked;
private Object value;
… get e set
}
e processava as duas coleções diferentes na camada de negócio antes de chegar na página, e entregava para a JSP uma coleção de objetos do tipo Selected, na hora achei que era a solução mais elegante …
mas concordo que é trabalho demais para uma coisa que deveria ser simples …
Se desse para fazer isto usando XPath seria o ouro, muito barbada, mas achei que era trabalho demais configurar o JXPath para aquela app só por causa de 4 telinhas
bom, acho que era isto, em qualquer uma das opções, parece ser trabalho demais onde deveria ser simples, ou então parece lógica no lugar errado, pelo menos é o que parece pra mim
Comment by Urubatan — August 22, 2006 @ 7:14 pm
Acho que muitos já passaram por isso. Tb sinto saudades do Velocity quando me deparo com essas situações. Apesar de tb jah ter adotado soluções bem parecidadas (criando taglibs), isso deixa com uma bela cara de programação estruturada! Os objetos viram structs praticamente!
Comment by Flavio Coutinho — August 22, 2006 @ 10:35 pm
Como o Coutinho disse, é nesse momento que Velocity salva.
O fn:length resolve alguns, mas não todos os problemas.
Por fim, uma solução boa é criar um wrapper para coleções que possui os métodos que você deseja e utilizá-lo de alguma maneira…
Qualquer solução é gambiarra…
A lógica está no lugar errado, mas que é chato mover, ah, isso é…
Comment by Guilherme Silveira — August 25, 2006 @ 7:39 pm
Pessoal, por que assim não funciona?
${tMsg.descricao}
Comment by Eduardo — September 3, 2007 @ 8:44 pm