LibreOfficeをJava帳票開発に使う(lojの紹介)
を書きました。
この記事では、いかにUNOをJavaで書くのは煩雑かを示し、lojの使用をおすすめします。
lojはこちらです。
UNOは高機能だけどJavaから使うと大変
LibreOfficeにはUNO(Universal Network Objects)という非常に強力なAPIがあります。
Calcのセル操作、書式設定、シート操作、グラフ、印刷、PDF出力など、LibreOfficeのさまざまな機能をJavaから操作できます。
しかし、JavaからUNOを直接利用すると、ちょっとした操作でも複数のUNOインターフェースを扱う必要があります。
例えば、UNOでは取得したObjectをそのまま目的の型として扱うのではなく、
XSomething xSomething =
UnoRuntime.queryInterface(
XSomething.class,
object
);
のように、目的のUNOインターフェースを取得してから操作するコードが頻繁に登場します。
処理が複雑になってくると、
Object
↓
XSpreadsheetDocument
↓
XSpreadsheets
↓
XSpreadsheet
↓
XCellRange
↓
XPropertySet
のように、目的の操作をするために次々とインターフェースを取得することになります。
UNOの仕組みとしては合理的なのですが、帳票を作りたい側からすると、
A1セルに値を入れたいだけなんだけど……
となります。
プロパティ設定も長い
LibreOfficeでは書式などの多くがPropertyとして公開されています。
そのため、例えば背景色を設定する場合にも、対象オブジェクトからXPropertySetを取得して、
XPropertySet props =
UnoRuntime.queryInterface(
XPropertySet.class,
object
);
props.setPropertyValue(
"CellBackColor",
0xAAAAAA
);
のようなコードを書くことになります。
背景色を設定するという処理そのものは単純なのですが、
UNO Objectを取得
↓
XPropertySetを取得
↓
プロパティ名を指定
↓
値を設定
という手順が必要になります。
帳票ではセルの背景色、罫線、フォント、表示形式など、多数のプロパティを設定するため、このようなコードが大量に出てきます。
loj-proxyでUNOをラップする
そこでlojでは、UNOインターフェースをJava Reflectionで解析して、Javaのラッパークラスを自動生成します。
UNOインターフェース
↓
Reflection
↓
loj-proxy
↓
P_Doc
P_Sheet
P_Range
P_Cell
...
例えば、生成されたP_*クラス内部で必要なUNOインターフェースへの変換を行うため、アプリケーション側で毎回UnoRuntime.queryInterface()を書く必要を減らせます。
さらにXPropertySetを持つオブジェクトでは、lojはprop()を利用できます。
range
.prop("CellBackColor", 0xAAAAAA)
.prop("CellStyle", "My Style");
UNOを直接使う場合に繰り返し登場する処理を、生成されたラッパー側へ押し込んでいます。
さらに高水準APIを作る
ただし、UNO APIをラップしただけでは、まだ帳票を書くには低水準です。
そこでlojでは、自動生成されたP_*クラスの上に、
Doc
Sheet
Range
Cell
という手書きの高水準APIを用意しています。
構造としては、
アプリケーション
↓
Doc / Sheet / Range / Cell
↓
自動生成された P_* クラス
↓
LibreOffice UNO API
↓
LibreOffice
となります。
例えば、セルに値を設定しながら右へ移動する処理なら、
Cell c = sheet.getCell("A1");
c.set("JAN")
.right().set("FEB")
.right().set("MAR");
と書けます。
帳票を作っている側から見れば、
UNOのインターフェースを取得する
より、
セルに値を入れる
右へ移動する
次の値を入れる
と書けた方が、やりたいことがそのままコードに現れます。
全部を高水準APIにする必要はない
ここで重要なのは、lojがUNOを隠してしまうライブラリではないことです。
P_*クラスからgetProxy()を使えば、元のUNO Objectを取得できます。
つまり、
普段の帳票処理
↓
Doc / Sheet / Range / Cell
↓
P_*
特殊な処理
↓
getProxy()
↓
UNO APIを直接使用
という使い分けができます。
高水準APIに存在しないLibreOfficeの機能を使うために、すべてをloj側で実装する必要はありません。
必要なところだけUNOへ降りることができます。
なぜP_*を自動生成するのか
LibreOffice UNO APIは非常に巨大です。
これをすべて手書きのJavaラッパーとして実装して保守するのは大変です。
そこでlojでは、
機械的に作れる部分
↓
loj-proxyで自動生成
帳票として使いやすくする部分
↓
人間がDoc / Sheet / Range / Cellを書く
と役割を分けています。
LibreOfficeのバージョンを変更した場合も、UNOインターフェースからP_*クラスを再生成できます。
つまり、
機械に書けるコードは機械に書かせる
という方針です。
loj.myはサンプル
Doc、Sheet、Range、Cellなどはloj-sb-sampleのloj.myパッケージに入っています。
これは完成された高水準APIを固定的に提供するというより、利用者がコピーして、自分の帳票システムに合わせて育てていくことを想定しています。
例えば、
cell.set(...);
だけではなく、
cell.right();
cell.down();
のような、自分の帳票で頻繁に使う操作を追加していくことができます。
したがってlojは、
UNO
↓
自動生成された汎用ラッパー
↓
自分の用途に合った高水準API
↓
帳票プログラム
という構造になります。
サーバーで使うならloj-util
UNOをJavaから使う場合、API操作だけでなくLibreOfficeへの接続も考える必要があります。
loj-utilには、
- LibreOfficeプロセスの起動・接続
- Socket / Named Pipe接続
- 接続プール
- Java StreamとUNO Streamの変換
- 一時ユーザーディレクトリ
など、サーバーアプリケーションからLibreOfficeを利用するための仕組みを用意しています。
そのためSpring Bootなどから、
リクエスト
↓
Java / Spring Boot
↓
loj
↓
LibreOffice
↓
ODS / PDF / Printer
という帳票処理を構築できます。
まとめ
UNOは非常に強力です。
しかしJavaから直接利用すると、
UNO Object
↓
queryInterface()
↓
XSomething
↓
queryInterface()
↓
XPropertySet
↓
setPropertyValue()
といったコードが多くなり、帳票本来の処理がUNOの定型コードに埋もれやすくなります。
lojでは、
UNO API
↓
loj-proxyによる自動生成
↓
P_*クラス
↓
Doc / Sheet / Range / Cell
↓
帳票処理
という層を作ることで、UNOの煩雑な部分をできるだけ下の層へ追い出します。
それでも必要になったらgetProxy()からUNOへ戻れます。
UNOは強力なので使いたい。
でも、
UNOを毎回そのままJavaで書くのはつらい。
そんなときにlojを使ってみてください。