Nell'articolo dedicato a Jakarta POI ci siamo occupati di definire un elemento capace di effettuare l'export di dati in forma di documento excel. Attraverso l'introspezione siamo inoltre riusciti ad astrarre il concetto ad altissimo livello, facendo si che la definizione del tipo concreto fosse un'operazione eseguita a runtime, con l'evidente vantaggio di poter utilizzare la logica software prodotta con ogni classe.
L'idea di esportare i dati e di creare logiche software riutilizzabili, può diventare un potente strumento a disposizione degli sviluppatori, permettendo loro di utilizzare sempre lo stesso strato di logica senza preoccuparsi di ridefinire il comportamento di rappresentazione in base al tipo concreto che ogni volta si presenta. Il caso della rappresentazione in tabella excel, cioè di elenco in forma schematica ben si presta a questo tipo di idealizzazione. In ambito Web, prima ancora che l'export dei dati in formati standard, ci preoccuperemo della rappresentazione dell'elenco in HTML. Ma una tabella HTML non si presta a seguire lo stesso discorso fatto per una tabella excel? La risposta è affermativa ed è il contenuto di questo articolo.
Attraverso l'idea sviluppata nello scorso capitolo creeremo un componente custom tag che si occupa di definire la formattazione HTML di una tabella. Riutilizzeremo gli stessi concetti di introspezione, mostrando alla fine come sia possibile rappresentare una collezione di elementi indipendentemente dal loro tipo dinamico.
Implementazione Custom Tag
Come vedremo nel dettaglio del codice sorgente, utilizzeremo il custom tag per la formattazione degli elenchi, utilizzando quella classe ReflectionUtils e i suoi metodi, molto utili per gestire la logica di introspezione. Inoltre riprenderemo la classe prodotta per la creazione del documento excel, fornendo un link per effettuare l'operazione di export dei dati direttamente dalla Web application.
Alla fine avremo quindi una pagina Web con una tabella rappresentante i dati di una collezione di oggetti e un link che si occupa di effettuare l'export in excel degli stessi dati.
Per la creazione del custom tag, creeremo una classe che estende la classe TagSupport:
Listato 1. Definisce la logica per la produzione di un semplice custom tag
package it.html.tabletag;
...
//riproduce la formattazione automatica degli elementi passati in una collezione
public class TableExporter extends TagSupport {
//L'attributo viene utilizzato per definire uno stile
private String className;
//L'attributo viene utilizzato per definire il nome utente
private Collection list;
//Metodi getter and setter
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public void setList(Collection list){
this.list = list;
}
public Collection getList(){
return(list);
}
...
}
Le variabili di istanza sono la Collection, contenitore degli elementi da rappresentare, e il nome del foglio di stile, in modo da formattare la rappresentazione con un foglio CSS. Nello spezzone di codice vediamo anche i relativi metodi get e set.
La logica del componente, come sappiamo, risiede nella coppia di metodi doStartTag()
e doEndTag()
, che provvediamo ad estendere concretamente:
Listato 2. Viene invocato dal container quando viene incontrato il tag xml
public int doStartTag() {
try {
//Recupero lo stream di output e scrivo il contenuto personalizzato
JspWriter out = pageContext.getOut();
if (list != null && !list.isEmpty()){
creaIntestazione(out);
creaContenuto(out);
creaStatistiche(out);
}
else{
out.println("<p>Nessun elemento trovato.</p>");
}
} catch (Exception e) {
e.printStackTrace();
}
//Non c'è contenuto da elaborare all'interno del tag quindi restituiamo il codice SKIP_BODY
return TagSupport.SKIP_BODY;
}
//Il metodo viene invocato alla chiusura del tag
public int doEndTag(){
//NO operation
//Continua a valutare il resto della pagina
return TagSupport.EVAL_PAGE;
}
Dopo aver recuperato lo stream di output, divideremo il compito nei tre metodi creaXYZ()
, ognuno specializzato nella creazione dell'intestazione (quindi la prima riga), nel contenuto (quindi le righe contenenti i dati) e nelle statistiche (ultima riga). Il metodo doEndTag()
non ha nessun compito assegnato.
Listato 3. Scrive il tag di inizio tabella e riga
private void creaIntestazione(JspWriter out) throws IOException {
//Apriamo il tag di tabella
out.println("<table class="+this.className+">");
//Recuperiamo un'istanza dalla lista per leggere a runtime il nome delle variabili di istanza
String fields[] = ReflectionUtils.getStringFields(getFirstFromList());
//Valorizziamo l'intestazione con il nome della proprietà del bean, per ognuna delle proprietà presenti (sconosciute a tempo di compilazione)
//Apriamo il tag di riga
out.println("<tr>");
//Iteriamo sugli elementi dell'array
for(int i=0;i<fields.length;i++){
//Creiamo una colonna di intestazione
out.println("<th>");
out.println(fields[i]);
//Chiudiamo il tag
out.println("</th>");
}
//Chiudiamo la riga
out.println("</tr>");
}
Il compito di crea intestazione è quello di scrivere il tag di inizio di tabella e la riga contenente il nome delle proprietà. Come vediamo, si itera sulla lista di proprietà ottenute per introspezione del primo elemento della collezione, e si crea una colonna di intestazione (th) per ognuno di essi.
Listato 4. Recupera i dati e scrive in colonne
private void creaContenuto(JspWriter out) throws IOException{
String fields[] = ReflectionUtils.getStringFields(getFirstFromList());
//Iteriamo sugli elementi
Iterator it=list.iterator();
int cont=0;
while(it.hasNext()){
Object elem = it.next();
//Apriamo il tag di riga (settiamo due stili per colonne pari e dispari
out.println("<tr class="+(cont%2==0?"odd":"even")+">");
//Iteriamo sugli elementi dell'array (quindi, una colonna per ognuno)
for(int i=0;i<fields.length;i++){
//Recuperiamo il valore del campio iesimo via Reflection
String value = ReflectionUtils.getProperty(fields[i], elem);
//e lo settiamo
//Creiamo una colonna
out.println("<td>");
out.println(value);
//Chiudiamo il tag
out.println("</td>");
}
//Chiudiamo la riga
out.println("</tr>");
cont++;
}
}
CreaContenuto()
ha un comportamento analogo, con la differenza che c'è un'iterazione su tutti gli elementi della collezione e a ognuno di essi il recupero del valore della proprietà e la scrittura in colonna (td, questa volta). Per convenzione, daremo il nome del foglio di stile odd e even (pari e dispari) in maniera alternativa alle righe, sempre per poterne formattare la visualizzazione dall'esterno.
Il metodo creaStatistiche()
serve per aggiungere una riga di riepilogo e per chiudere la tabella (avremmo potuto utilizzare anche il metodo doEndTag()
a questo scopo).
Listato 5. Popola la tabella
private void creaStatistiche(JspWriter out) throws IOException {
String fields[] = ReflectionUtils.getStringFields(getFirstFromList());
int nElementi = list.size();
int nCols = fields.length;
//Apriamo il tag di riga
out.println("<tr class=footer>");
//Creiamo una colonna lunga quanto gli elementi
out.println("<td colspan="+nCols+">");
out.println("Numero elementi: "+nElementi);
//Chiudiamo il tag
out.println("</td>");
//Chiudiamo la riga
out.println("</tr>");
//Chiudiamo il tag di tabella
out.println("</table>");
}
Per poter utilizzare il custom tag all'interno di una pagina JSP, dobbiamo dichiararne il descrittore:
Listato 6. Pagina JSP per l'inclusione della tabella
<?xml version="1.0"
encodin="ISO-8859-1" ?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
"http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>tableTag</shortname>
<info>Table Exporter Custom Tag</info>
<tag>
<name>table</name>
<tagclass>it.html.tabletag.TableExporter</tagcl ass>
<bodycontent>empty</bodycontent>
<info>Un custom tag per l'export dei dati</info>
<attribute>
<name>list</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>className</name>
<required>true</required>
</attribute>
</tag>
</taglib>
Lo chiameremo tableTag.tld e lo inseriremo sotto WEB-INF.
Come vediamo, è necessario passare un attributo list (una Collection) e un className (una String). Per recuperare dinamicamente queste e altre informazioni, utilizzeremo una classe Controller, una servlet, che qui vediamo nel suo metodo principale:
Listato 7. Recupera l'attributo list e className
private void doExcel(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Collection aList = DataManager.getData();
//Creo la directory a partire dalla root del web server
String root = "/excel";
File dir = new File(this.getServletContext().getRealPath(root));
dir.mkdir();
//Operazione di logica POI
ExcelExporter ee=new ExcelExporter(dir.getPath(),"export.xls",aList);
ee.export("Test Foglio di Stile");
//Salviamo l'oggetto nella request, in modo da renderli visibili
RequestDispatcher rd=this.getServletContext().getRequestDispatcher("/sample.jsp");
req.setAttribute("list",aList);
req.setAttribute("filePath",root+"/export.xls");
//alla pagina JSP che verrà inoltrata
rd.forward(req,resp);
}
Dopo aver recuperato una Collection dalla classe DataManager (che simula la presenza di un database), creiamo il file excel, con la classe sviluppata nel precedente articolo. A questo punto passiamo alla pagina sample.jsp gli attributi aList (contenente gli elementi da passare al custom tag) e filePath, ossia il percorso per aprire il documento excel appena creato.
La pagina JSP, si attenderà dunque questi attributi e presenterà la seguente forma:
Listato 8. Pagina JSP di visualizzazione
<%@ taglib uri="/WEB-INF/tableTag.tld" prefix="tt" %>
<%
//Recupero la lista tra gli attributi di pagina
java.util.Collection lista = (java.util.Collection) request.getAttribute("list");
String excelFile = (String) request.getAttribute("filePath");
%>
<html>
<head>
<title>Custom Tag: Table Exporter</title>
</head>
<style>
...
</style>
<body>
<tt:table list="<%=lista%>" className="stile"/>
<hr/>
<%
if (excelFile!=null){
%>
<p><a href="./<%=excelFile%>">Apri file excel associato</a></p>
<%
}
else{
%>
<p><a href="./controller?op=excel">Crea file excel</a></p>
<%
}
%>
</body>
</html>
Si noti in particolare la semplicità di utilizzo del custom tag, che potrete ora utilizzare con tutte le entità presenti nella Web application, sia per operazioni di rappresentazione, sia per operazione di export dati in excel.
Esecuzione e conclusioni
In fondo alla tabella avremo il link diretto al file per effettuare l'operazione di export. L'esecuzione con i dati di esempio porta il seguente codice HTML:
Listato 9. Esempio della tabella
<table class=stile>
<tr>
<th>id</th>
<th>nome</th>
<th>cognome</th>
</tr>
<tr class=odd>
<td>1</td>
<td>Homer</td>
<td>Simpson</td>
</tr>
...
<tr class=footer>
<td colspan=3>
Numero elementi: 6
</td>
</tr>
</table>
Un'immediata miglioria che potrete apportare al componente è la gestione della paginazione, quindi, pensando al custom tag con nuovi parametri e una logica interna che gestisca la paginazione. L'esempio è servito per dare uno spunto allo sviluppo di più complesse e interessanti applicazioni che traggano vantaggio dall'astrazione permettendo il riutilizzo della logica creata.