pátek 18. ledna 2008

Jak na Excel 5 - knihovna JExcelApi

Konečně se v našem seriálu dostáváme k čistě javovým implemtacím Excelového formátu. Začneme knihovnou Java Excel API (JExcelApi), kterou vytvořil Andy Khan a uvolnil pod licencí GNU LGPL.

Co tato knihovna umí zajímavého?

  • vytváření a čtení XLS souborů
  • formátování buněk
  • práci se vzorci
  • vkládání PNG obrázků
  • kopírování grafů (vytváření nových podporováno není)
Knihovna není nijak rozsáhlá, takže se dá v API pohodlně orientovat.

Zajímavější pro vás může být informace, co mi na této knihovně vadí. Tady jsou mé připomínky:

  • odlišný přístup k dokumentům při čtení a při zápisu (třídy Workbook a WritableWorkbook nemají ani společný interface)
  • práce s čisly podporuje pouze typ double, osobně bych uvítal např. i BigDecimal
  • chybí jakákoliv podpora pro makra (ani při kopírování nezůstanou zachována)
  • nepodporuje automatickou šířku sloupce (Auto Fit Selection)
  • nelze nastavit typ buňky (viz příklad - vložím vzorec jehož výsledkem je text, ale JExcelApi si při čtení myslí, že to je číslo)
  • tvar vzorců neodpovídá na 100% tvaru z Excelu (chybí uvozovací rovnítko, jako oddělovač parametrů funkcí je použita čárka namísto středníku)
Další věc, která se mi moc nelíbí (a to ani v originálním Excelu) jsou překlady jmen funkcí používaných ve vzorcích. Sice jde jednoduše nastavit, že se použijí původní "americká" jména (WorkbookSettings.setLocale(...)), ale osobně bych to preferoval úplně bez překladů.

A nakonec praktická ukázka:

/**
* Priklad cteni dokumentu pres JExcel
* @throws BiffException
* @throws IOException
* @throws FormulaException
*/
public static void readDemo() throws BiffException, IOException, FormulaException {
    System.out.println("Ctu soubor: " + FILE_NAME);

    // otevreni souboru
    final Workbook wb = Workbook.getWorkbook(new File(FILE_NAME));

    //vypisem vsechny listy
    for (int i=0,m=wb.getNumberOfSheets();i<m;i++) {
        final Sheet sheet = wb.getSheet(i);
        System.out.println("List: " + sheet.getName());

        //vypisem hodnoty v listu
        for (int j=0,n=sheet.getRows();j<n;j++) {
            final Cell[] cells = sheet.getRow(j);
            final StringBuffer tmpStrRow = new StringBuffer();
            for (int k=0; k<cells.length; k++) {

                //spravne bychom tady meli bunku zpracovavat v zavislosti na jejim typu,
                //neb v JavaDocu je metoda getContents oznacena jako "dirty" :)
                tmpStrRow.append(cells[k].getContents());
                //zracovani podle typu bunky muze vypadat nasledovne:
                if (cells[k].getType()==CellType.BOOLEAN_FORMULA
                        || cells[k].getType()==CellType.DATE_FORMULA
                        || cells[k].getType()==CellType.NUMBER_FORMULA
                        || cells[k].getType()==CellType.STRING_FORMULA) {
                    final FormulaCell tmpFormula = (FormulaCell) cells[k];
                    //do zavorky vypiseme vzorech pouzity v teto bunce
                    tmpStrRow.append(" (").append(tmpFormula.getFormula()).append(")");
                }
                tmpStrRow.append("\t");
            }
            System.out.println(tmpStrRow.toString());
        }
    }
    //nakonec zavrem workbook  a uvolnime pamet
    wb.close();
}

/**
* Priklad vytvoreni XLS dokumentu pres JExcel
* @throws IOException see {@link Workbook}
* @throws RowsExceededException see {@link WritableSheet#addCell(jxl.write.WritableCell)}
* @throws WriteException see {@link WritableSheet#addCell(jxl.write.WritableCell)}
*/
public static void writeDemo() throws IOException, RowsExceededException, WriteException {
    System.out.println("Vytvarim soubor: " + FILE_NAME);
    // konfigurace - nechci pouzivat jmena funkci v nemcine :)
    final WorkbookSettings wbs = new WorkbookSettings();
    wbs.setLocale(Locale.US);
    //vytvoreni noveho Workbooku (souboru)
    final WritableWorkbook wb = Workbook.createWorkbook(new File(FILE_NAME),wbs);
    //pridame list
    final WritableSheet sheet = wb.createSheet("Muj super list", 0);
    //nastaveni fontu
    final WritableFont font = new WritableFont(WritableFont.TIMES, 16, WritableFont.BOLD, true);
    final WritableCellFormat cellFormat = new WritableCellFormat(font);

    //pridani stringu
    sheet.addCell(new Label(0,0,"Popisek hodnoty", cellFormat));
    //pridani cisla (bez specialniho formatovani)
    sheet.addCell(new Number(1,0, 1.23456d));
    //pridani vzorce
    sheet.addCell(new Formula(1,1, "CONCATENATE(\"Vysledek vzorce: \",B1+1.90703)"));

    //zapsani do souboru
    wb.write();
    //uvolneni z pameti
    wb.close();
    System.out.println("Soubor ulozen.");
}
výstupem programu bylo na mém německém počítači toto:
Vytvarim soubor: jne5.xls
Soubor ulozen.
Ctu soubor: jne5.xls
List: Muj super list
Popisek hodnoty 1,235 
 4 (VERKETTEN("Vysledek vzorce: ",B1+1.90703))
Celý příklad si můžete stáhnout na adrese http://www.cacek.cz/javlog-attachments/JakNaExcel5.zip

2 komentáře:

Anonymní řekl(a)...

Když spustím stáhnutelný příklad, při vytváření WorkbookSettings mi to vyhodí vyjímku:



Vytvarim soubor: jne5.xls
Exception in thread "main" java.lang.ExceptionInInitializerError
at JExcelApiTest.writeDemo(JExcelApiTest.java:86)
at JExcelApiTest.main(JExcelApiTest.java:116)
Caused by: java.lang.NullPointerException
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Unknown Source)
at common.Logger.initializeLogger(Logger.java:71)
at common.Logger.getLogger(Logger.java:42)
at jxl.WorkbookSettings.[clinit]*(WorkbookSettings.java:42)
... 2 more



(*[clinit] je v ostrych zavorkach, formular mi vsak nedovoli psat tagy)


cestu k souboru mám, si myslím, nastavenou dobře. Každopádně to spadne v době, kdy jí to snad ani nepotřebuje.

Nevíte kde je chyba?

Anonymní řekl(a)...

--> tak mi zrejme chybu udelal nekde Eclipse nebo ja. Kdyz jsem vytvoril projekt na jinem pocitaci ve starsim Eclipsu, fungovalo mi to dobre, po presunuti funkcniho projektu mi funguje uz i tady...