odsファイルを開きシートにアクセス

OpenOffice Calcのodsファイルは設定情報、スタイル情報、ドキュメント本体などのxmlファイルやリソースをzipで固めたものです。 odsファイルの拡張子をzipに変換して解凍すると、それぞれのファイルにアクセスできます。 ドキュメント本体のファイルはcontent.xmlです。 ドキュメントを更新するたびに手作業でcontent.xmlを取り出すのは面倒なので、javaの標準ライブラリでメモリに展開します。

下のコードはサンプルのOdsReaderクラスから抜粋したものです。 まず、ZipInputStreamクラスでodsファイルを開きます。 そしてエントリー名がcontent.xmlのファイルを探し、byte配列に読み込みます。

private byte[] readContent(String fileName)
{
    byte[] res = null;
    ZipInputStream zipInStream = null;
    
    try
    {
        zipInStream = new ZipInputStream(new BufferedInputStream(new FileInputStream(fileName) ) );
        ZipEntry entry = null;
        while( (entry = zipInStream.getNextEntry() ) != null)
        {
            // エントリー名が「content.xml」の場合、中身を読む
            String entryName = entry.getName();
            if(entryName.equals("content.xml") )
            {
                res = readFromZipInputStream(zipInStream);
            }
            zipInStream.closeEntry();
        }
    }
    catch(Exception exc)
    {
        exc.printStackTrace();
    }
    try
    {
        zipInStream.close();
    }
    catch(Exception exc)
    {
        exc.printStackTrace();
    }
    
    return res;
}

// ZipInputStreamのエントリーからbyte配列に読み込む
private byte[] readFromZipInputStream(ZipInputStream in) throws Exception
{
    byte[] res = null;
    byte[] buff = new byte[BUFF_SIZE];
    int readSize;
    
    while(true)
    {
        readSize = in.read(buff);
        if(0 < readSize)
        {
            // byte配列の連結
            res = concat(res, buff, readSize);
        }
        else
        {
            break;
        }
    }
    
    return res;
}

byte配列を解釈してDOMのDocumentを作成します。 パーサに直接byte配列を渡すことはできないので、ByteArrayInputStreamでラッピングしてから渡します。

try
{
    // odsファイルから「content.xml」を抜き出してbyte型配列に記憶
    byte[] xmlBytes = readContent(fileName);
    
    // byte配列をストリームクラスでラッピング
    ByteArrayInputStream bis = new ByteArrayInputStream(xmlBytes);

    DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance();
    DocumentBuilder builder = dbfactory.newDocumentBuilder();
    _xmldoc = builder.parse(bis);
}
catch(Exception exc)
{
    exc.printStackTrace();
}

DOMのDocumentにある目的のシートにアクセスします。 複雑なドキュメントは見ていませんが、content.xmlは大体以下のような形式になっているようです。

<?xml version="1.0" encoding="UTF-8"?>
<office:document-content xmlns:office=...>
    <office:scripts/>
    <office:font-face-decls>
        ... フォント情報
    </office:font-face-decls>
    <office:automatic-styles>
        ... スタイル情報、スタイル名毎に設定を記録
    </office:automatic-styles>
    <office:body>
        <office:spreadsheet>
            <table:table table:name="シート名" table:style-name="スタイル名" ...>
                <table:table-column table:style-name="スタイル名" .../>
                table:table-column要素(列ヘッダ情報)の繰り返し...
                
                <table:table-row table:style-name="スタイル名">
                    <table:table-cell ...>...</table:table-cell>
                    セル要素の繰り返し...
                </table:table-row>
                table:table-row要素(行情報)の繰り返し...
                
            </table:table>
            table:table要素(シート情報)の繰り返し...
            
        </office:spreadsheet>
    </office:body>
</office:document-content>

目的のシートにアクセスするには、table:tableタグをリストアップしてtable:name属性がシート名と一致するものを探します。

Element sheetElement = null;

// シートの情報は「table:table」タグに収められている
NodeList nodeList = _xmldoc.getElementsByTagName("table:table");

for(int i = 0; i < nodeList.getLength(); i++)
{
    Element elem = (Element)nodeList.item(i);
    
    // シート名は「table:table」タグの「table:name」属性に収められている
    String nameAttr = elem.getAttribute("table:name");
    if(nameAttr.equals(sheetName) )
    {
        sheetElement = elem;
        break;
    }
}

... sheetElementを使って処理