Trabalhando com coleções usando JSP e JSTL

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?

É aí que muitos sentem saudades do Velocity, que permite escrever algo como $colecao.contains(x) e $colecao.size. 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?

Tags: , , ,

6 Comentários

  1. Paulo Silveira 22/08/2006 at 18:19 #

    É, o Urubatan veio comentar comigo que o length funciona não só para String e arrays. Mas de qualquer forma o problema do contains e outros métodos continua.

  2. Urubatan 22/08/2006 at 19:14 #

    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 🙁

  3. Flavio Coutinho 22/08/2006 at 22:35 #

    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! 😀

  4. Guilherme Silveira 25/08/2006 at 19:39 #

    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 é…

  5. Eduardo 03/09/2007 at 20:44 #

    Pessoal, por que assim não funciona?

    ${tMsg.descricao}

Deixe uma resposta