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を使って処理