Share |

sabato 9 gennaio 2010

Java: Creare Files Excel

Torno a scrivere di excel per terminare la panoramica sull'argomento. Ho già pubblicato un articolo su come estrarre dati da file .xls. Salto a piè pari gli accenni alle librerie POI, perché già presenti nel suddetto articolo, e passo alla descrizione logica dell'esempio che vi esporrò.



Siccome un comune file excel è composto generalmente non solo da uno o più fogli di lavoro ma prima di tutto da un workBook che li contiene, ho deciso di implementare una factory class per la creazione di workbooks e successivamete un'altra per la gestione dei fogli di lavoro. Poi preso da convulsioni ne ho create altre 2 ancora per gestire la creazione delle celle ed il loro style. Tutte queste factory class le ho racchiuse in un package di nome core. Il tutto è poi utilizzato in una banale classe composta dal solo metodo main() che genera e salva sul File System locale il documento excel creato.

Dunque passiamo all'esempio: andando in ordine logico, come ho esposto sopra, partiamo con la classe WorkBookFactory.


import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;

public class WorkBookFactory {

  public static HSSFWorkbook createDefaultWorkBook(){
    return new HSSFWorkbook();
  }

  public static HSSFWorkbook createComplexWorkBook(int nSheet
      ,String[] titleSheet,String header,String footer){

    //Creo un WorkBook
    HSSFWorkbook wb = new HSSFWorkbook();

    //Creo n fogli da aggiungere al WorkBook
    HSSFSheet[] sheets = new HSSFSheet[nSheet];

    //Aggiungo n Sheet al WorkBook
    for(int i=0; i<nSheet; i++){
      sheets[i] = wb.createSheet(titleSheet[i]);

      //Aggiungo Header e Footer ad ogni Sheet
      sheets[i] = SheetFactory.addDefaultHeader(sheets[i],header);
      sheets[i] = SheetFactory.addDefaultFooter(sheets[i],footer);
    }

    return wb;
  }
}

Come si è potuto notare, all'interno della classe WorkBookFactory ho fatto riferimento a dei metodi statici della classe SheetFactory: questi due metodi creano header e footer del documento. Si può verificare la loro presenza provando a fare un'anteprima di stampa del documento excel.

Passiamo adesso alla seconda classe in ordine logico, appunto la suddetta classe SheetFactory:


import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFFooter;
import org.apache.poi.hssf.usermodel.HSSFHeader;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.util.Region;

public class SheetFactory {

  //Crea un header per la stampa
  public static HSSFSheet addDefaultHeader(HSSFSheet s,String header){ 
    HSSFHeader hd = s.getHeader();
    hd.setCenter(header);
    return s;
  }

  //Crea un footer per la stampa
  public static HSSFSheet addDefaultFooter(HSSFSheet s,String footer){
    HSSFFooter f = s.getFooter();
    f.setCenter(footer);
    return s;
  }

  //Genera una tabella semplice con dei dati
  public static HSSFSheet addDefaultTable(HSSFSheet s,List rows
      ,int colTop,int rowTop){
    Iterator it = rows.iterator();
    int y = rowTop;

    while(it.hasNext()){
      short x = (short)colTop;
      HSSFRow row = s.createRow(y);
      Object[] obj = (Object[])it.next();

      for(int j=0; j<obj.length; j++){
        HSSFCell c = row.createCell(x);
        c = CellFactory.createDefaultCell(c,obj[j]);
        x++;
      }

      y++;
    }
    return s;
  }

  //Genera una tabella formattata con dello stile
  public static HSSFSheet addTableWithStyle(HSSFSheet s
      ,String tableTitle,List rows,int colTop,int rowTop
      ,HSSFCellStyle st,HSSFFont font){

    boolean hasTitle = false;
    Iterator it = rows.iterator();
    int y = rowTop;

    //Titolo Tabella
    if(tableTitle != null && tableTitle.length() > 0){
      HSSFRow rtit = s.createRow(y);
      HSSFCell ctit = rtit.createCell((short)colTop);
      ctit = CellFactory.createCellWithStyle(ctit,tableTitle,st,font);

      Region r = new Region(y,(short)colTop,colTop+1
          ,(short)( ( (Object[])rows.get(0) ).length+1) );

      s.addMergedRegion(r);
      hasTitle = true;
    }

    while(it.hasNext()){
      short x = (short)colTop;
      HSSFRow row = s.createRow((hasTitle) ? y+1:y);
      Object[] obj = (Object[])it.next();

      for(int j=0; j<obj.length; j++){
        HSSFCell c = row.createCell(x);
  
        c = (y == rowTop) 
          ? CellFactory.createCellWithStyle(c,obj[j],st,font)
          : CellFactory.createDefaultCell(c,obj[j]);

        x++;
      }

      y++;
    }
    return s;
  }

  //Simula la base dati con cui caricare i dati nel documento .xls
  //Crea 6 celle per ogni riga e restituisce una lista di 4 righe.
  public static List caricamentoManuale(){
    List rows = new ArrayList();
    String[] colTitle = {"Col 1","Col 2","Col 3","Col 4","Col 5","Col 6"};
    Object[] cellValue1 = {"Genova",new Double("1.1"),new Double("1.2")
        ,new Double("1.3"),new Double("1.4"),new Double("1.5")};
    Object[] cellValue2 = {"Roma",new Double("2.1"),new Double("2.2")
        ,new Double("2.3"),new Double("2.4"),new Double("2.5")};
    Object[] cellValue3 = {"Milano",new Double("3.1"),new Double("3.2")
        ,new Double("3.3"),new Double("3.4"),new Double("3.5")};

    rows.add(colTitle);
    rows.add(cellValue1);
    rows.add(cellValue2);
    rows.add(cellValue3);

    return rows;
  }
}

All'interno della classe SheetFactory ho fatto riferimento a metodi statici della classe CellFactory:
il metodo createDefaultCell(HSSFCell c,Object value) che restituisce celle 'normali' ed il metodo
createCellWithStyle(HSSFCell c,Object value,HSSFCellStyle st,HSSFFont font) che restituisce celle formattate con un certo style.

Vediamo come è composta ora la classe CellFactory:

import java.util.Date;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;

public class CellFactory { 
  //Crea una cella senza alcuno style.
  //Gestisce il cast degli oggetti + utilizzati
  public static HSSFCell createDefaultCell(HSSFCell c,Object value){ 
    if(value instanceof Date) {
      c.setCellValue((Date)value); } 
    else if(value instanceof Double) {
      c.setCellValue(((Double)value).doubleValue());}
    else c.setCellValue((String)value);   

    return c;
  }

  //Crea una cella con uno style predefinito.
  //Gestisce il cast degli oggetti + utilizzati
  public static HSSFCell createCellWithStyle(HSSFCell c,Object value
      ,HSSFCellStyle st,HSSFFont font){
    c.setCellStyle(CellStyleFactory.getDefaultIntestazione(st,font)); 
    createDefaultCell(c,value); 
    return c;
  }
}

E analiziamo anche la classe CellStyleFactory:

import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.util.HSSFColor;

public class CellStyleFactory {
  public static HSSFCellStyle getDefaultIntestazione(HSSFCellStyle s
      , HSSFFont font){   
    HSSFFont f = font; 
    f.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); 
    f.setUnderline(HSSFFont.U_SINGLE);   

    //Prima di settare il colore di sfondo 
    //devo dichiarare il Pattern usato per riempire la cella 
    s.setFillPattern(HSSFCellStyle.FINE_DOTS); 
    s.setFillBackgroundColor(HSSFColor.ORANGE.index);   
    s.setFont(f); 
    s.setAlignment(HSSFCellStyle.ALIGN_CENTER); 
    s.setBorderBottom(HSSFCellStyle.BORDER_DOUBLE);   
    return s;
  }
}

Non rimane che assemblare il tutto e dare origine alla creazione del file excel.

Di ciò se ne occupa una banale classe, la CreateNewXLS_Example : questa, è composta semplicemente di un costruttore e del metodo main() nel quale creerò fisicamente il file excel contenente 3 fogli di dati. Buona visione.

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import core.SheetFactory;
import core.WorkBookFactory;

public class CreateNewXLS_Example {
  public static void main(String[] args) { 
    HSSFWorkbook wb = WorkBookFactory.createComplexWorkBook(
        3,new String[]{"Foglio 1","Foglio 2","Foglio 3"}
        ,"Default Header - solo per la Stampa"
        ,"Default Footer - solo per la Stampa");    

    //Lo style ed il font sono ricavati direttamente dal WorkBook 
    HSSFCellStyle style = wb.createCellStyle();
    HSSFFont font = wb.createFont();   

    try{
      //Creo il file fisico nel filesystem locale 
      FileOutputStream fileOut =
          new FileOutputStream("c://CreateNewXLS_Example.xls");   
      
      //Simulo la ricezione dei dati da caricare 
      List rows = SheetFactory.caricamentoManuale();     
      
      //Riempio i fogli con ogni riga che ho a disposizione
      //al 1o foglio gli aggiungo degli style 
      for(int f=0; f<wb.getNumberOfSheets(); f++){
        HSSFSheet s = wb.getSheetAt(f);

        if(f==0)
          s = SheetFactory.addTableWithStyle(
              s,"These Dicks!! che Titolo",rows,2,2,style,font);
        else
          s = SheetFactory.addDefaultTable(s,rows,2,2);
      }

      //Scrittura del xls e chiusura dell'outputStream
      wb.write(fileOut);
      fileOut.close();
      System.out.println("Fine creazione file.");
    }
    catch(IOException io){
      System.out.println("Eccezione durante la creazione del file:\n\t"
          + io.getMessage());
      io.printStackTrace(System.out);
    }
  }
}

Et voilat. Direi che non è stato assolutamente complicato creare il tutto e, ovviamente, si possono creare file meno banali di quello qui presentato. Il tutto è gestito dal programmatore in modo trasparente ed efficiente.
Alla prossima, MA

0 commenti:

Posta un commento

Non ti è chiaro qualcosa?
No problem, posta il tuo dubbio ;)

..... e ricordati di firmarlo!