LibreOffice5(17)イベント駆動する拡張機能のJavaの例:AsyncJob.oxt その2

2015-12-24

旧ブログ

t f B! P L

前の関連記事:LibreOffice5(16)イベント駆動する拡張機能のJavaの例:AsyncJob.oxt その1


AsyncJob.oxtのAsyncJob.javaとJobs.xcuについて詳しくみていきます。

AsyncJob.javaで実装しているメソッド一覧


AsyncJob.javaにはAsyncJobというクラスが一つあるだけで、それがWeakBaseを(Javaでいう)継承してXServiceInfo, XAsyncJobを(Javaでいう)実装しています。

サービス名はcom.sun.star.task.AsyncJob、実装名はcom.sun.star.comp.framework.java.services.AsyncJobになっています。

com.sun.star.task.AsyncJobのメソッドをとそれをインスタンス化した実装のメソッド一覧を比較してみます。
In [1]:
import unopy
XSCRIPTCONTEXT = unopy.connect()
if not XSCRIPTCONTEXT:
    print("Failed to connect.")
    import sys
    sys.exit(0)
In [2]:
import unoinsp
ins = unoinsp.ObjInsp(XSCRIPTCONTEXT, True, True)
In [3]:
ins.itree("com.sun.star.task.AsyncJob")

com.sun.star.task.AsyncJob
└─.task.AsyncJob
      ├─.task.XAsyncJob
      │         void  executeAsync( [in] [.beans.NamedValue] Arguments,
      │                             [in]  .task.XJobListener Listener
      │                  ) raises ( .lang.IllegalArgumentException)
      └─.util.XCloseable
            │   void  close( [in] boolean DeliverOwnership
            │     ) raises ( .util.CloseVetoException)
            └─.util.XCloseBroadcaster
                        void  addCloseListener( [in] .util.XCloseListener Listener)
                        void  removeCloseListener( [in] .util.XCloseListener Listener)
In [4]:
ctx = XSCRIPTCONTEXT.getComponentContext()
smgr = ctx.getServiceManager()
In [5]:
obj = smgr.createInstanceWithContext("com.sun.star.task.AsyncJob", ctx)
In [6]:
ins.itree(obj)

pyuno object
├─.task.AsyncJob
│   ├─.task.XAsyncJob
│   │         void  executeAsync( [in] [.beans.NamedValue] Arguments,
│   │                             [in]  .task.XJobListener Listener
│   │                  ) raises ( .lang.IllegalArgumentException)
│   └─.util.XCloseable
│         │   void  close( [in] boolean DeliverOwnership
│         │     ) raises ( .util.CloseVetoException)
│         └─.util.XCloseBroadcaster
│                     void  addCloseListener( [in] .util.XCloseListener Listener)
│                     void  removeCloseListener( [in] .util.XCloseListener Listener)
├─.lang.XServiceInfo
│           string  getImplementationName()
│         [string]  getSupportedServiceNames()
│          boolean  supportsService( [in] string ServiceName)
├─.lang.XTypeProvider
│         [byte]  getImplementationId()
│         [type]  getTypes()
└─.uno.XWeak
            .uno.XAdapter  queryAdapter()
AsyncJob.javaをみると実装しているメソッドはexecuteAsync()、getImplementationName()、getSupportedServiceNames()、supportsService()しかありません。

SDK付属のJavadocを読むとXTypeProviderインターフェイスとXWeakインターフェイスのメソッドはcom.sun.star.lib.uno.helper.WeakBaseサービスが実装しているとわかります。

なので実装していないのはAsyncJobサービスのXCloseableインターフェイスとXCloseBroadcasterインターフェイスのメソッドになります。

私にはどのメソッドは実装を省いていいのかの判断基準がまだ見えてきませんね。

AsyncJob.javaのexecuteAsync()メソッドの第一引数で受けとっている情報


AsyncJob.javaの91行目のXAsyncJobインターフェイスのexecuteAsync()メソッドの引数でLibreOfficeから情報を受け取っています。

第一引数は型.beans.NamedValueを要素とするIDLデータタイプのシーケンスです。

UNO Type mappingにUNOタイプとPythonの型との対応表があります。

シーケンスはPythonのタプルになります。

.beans.NamedValueはIDLデータタイプのsuructです。

.beans.NamedValueをインスタンス化するとNameとValueが対になったインスタンスができます。

IDLデータタイプのstructとPythonでの扱いの違いはIDLデータタイプでは継承ができるのにPythonでは継承はできないことです。

継承しているstructの例としてPropertyChangeEvent Structが挙げられています。

AsyncJob.javaがやっていることのひとつはexecuteAsync()メソッドの引数lArgsで受け取った.beans.NamedValueのシーケンスの情報を加工してメッセージボックスに表示していることです。

まずメニューのツール→アドオン→AsyncJob(ALIAS)を実行した場合に引数lArgsで受け取っているものをNetBeansからデバッガをアタッチしてみてみます。


lArgsは型NamedValueの要素が3つはいった配列です。


0番目の要素のNamedValueのNameはConfigという文字列、Valueは型NamedValueの要素が3つの配列が入っています。

さらにその配列をみてみます。


結局NamedValueのNameをプロパティと解釈して書くと次のようになります。

lArgs[0].Config[0].Alias = "AsyncJob"

lArgs[0].Config[1].Service = "com.sun.star.comp.framework.java.services.AsyncJob"

lArgs[0].Config[2].Context = ""

他の要素についても書き出してみます。

lArgs[1].Environment[0].EnvType = "DISPATCH"

lArgs[1].Environment[1].Frame = com.sun.star.frame.XFrame型のオブジェクト

lArgs[2].DynamicData[0].Referer = "private user"

同様にしてAsyncJob(EVENT)を実行した場合についても書き出します。

lArgs[0].Environment[0].EnvType = "DISPATCH"

lArgs[0].Environment[1].Frame = com.sun.star.frame.XFrame型のオブジェクト

lArgs[0].Environment[2].EventName = "onMyOwnJobEvent"

lArgs[1].DynamicData[0].Referer = "private user"

AsyncJob(SERVICE)を実行した場合。

lArgs[0].Environment[0].EnvType = "DISPATCH"

lArgs[0].Environment[1].Frame = com.sun.star.frame.XFrame型のオブジェクト

lArgs[1].DynamicData[0].Referer = "private user"

AsyncJob.oxtを拡張機能マネージャーに登録してLibreOfficeの最初の起動時だけに表示されるときに実行された時はデバッガをアタッチする方法がわからないので表示されるメッセージボックスの情報から推測します。

lArgs[0].Environment[0].EnvType = "EXECUTOR"

lArgs[0].Environment[1].EventName = "onFirstVisibleTask"

これらで設定している値の意味はInitialization - Apache OpenOffice Wiki初期化で解説されています。

Jobs.xcuでジョブの定義と、ジョブを駆動するイベントとの関連を定義している


.task.XAsyncJobインターフェイスのexecuteAsync()メソッドの引数の配列lArgsで受けとっている情報は拡張機能ファイルAsyncJob.oxtのJobs.xcuで定義しています。
<?xml version='1.0' encoding='UTF-8'?>
<!-- ジョブを定義していることを示す。 -->
<oor:component-data oor:name="Jobs" oor:package="org.openoffice.Office" xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <node oor:name="Jobs"><!-- ジョブを定義するノード -->
        <node oor:name="AsyncJob" oor:op="replace"><!-- ジョブの名前を定義するノード。Config-Aliasで渡される。 -->
            <prop oor:name="Service" oor:type="xs:string"><!-- ジョブAsyncJobの実装名を定義するノード。Config-Serviceで渡される。 -->
                <value>com.sun.star.comp.framework.java.services.AsyncJob</value><!-- Config-Serviceの値。 -->
            </prop>
            <node oor:name="Arguments"><!-- ジョブAsyncJobの引数を定義するノード。JobConfigで渡される。 -->
                <prop oor:name="arg_1" oor:type="xs:string" oor:op="replace"><!-- 第一引数を定義するノード。 -->
                    <value>val_1</value><!-- JobConfigの要素の1つの値。 -->
                </prop>
            </node>
        </node>
    </node>
    <node oor:name="Events"><!-- ジョブを駆動するイベントとの関連を定義するノード。 -->
        <node oor:name="onFirstVisibleTask" oor:op="fuse"><!-- ジョブを駆動するイベントを定義するノード。 -->
            <node oor:name="JobList"><!-- イベントで駆動するジョブのリストを定義するノード。 -->
                <node oor:name="AsyncJob" oor:op="replace"/><!-- 実行するジョブ名。 -->
            </node>
        </node>
        <node oor:name="onMyOwnJobEvent" oor:op="fuse"><!-- イベントで駆動するジョブのリストを定義するノード。自作イベント名の例。 -->
            <node oor:name="JobList"><!-- イベントで駆動するジョブのリストを定義するノード。 -->
                <node oor:name="AsyncJob" oor:op="replace"/><!-- 実行するジョブ名。 -->
            </node>
        </node>
    </node>
</oor:component-data>
ハイライトしている行はオリジナルから変更したところです。

とくに10行目はオリジナルにはoor:op="replace"が抜けているのですがこれがないためにJobContigの値が渡されていませんでした。


LibreOffice5(16)イベント駆動する拡張機能のJavaの例:AsyncJob.oxt その1の最初にやった実行結果と違ってJobContigにちゃんと引数が渡されていることがわかります。

17行目と22行目もoor:op=の値を変更しています。

オリジナルでは17行目はoor:op="modify"となっていましたが、この値の解説はデベロッパーガイドを読んでも見つけられませんでした。
(2015.4.2追記。資料をみつけてLibreOffice5(31)xcsファイルとxcuファイルとxcdファイル:その2で理解できました。)

代わりにConfiguration - Apache OpenOffice Wikiの最後にoor:op="fuse"の解説を見つけました。

複数のJobs.xcuの要素が欠落しないようにoor:op="fuse"を使うことが重要と書いてあります。

別にオリジナルのmodifyでも動作に支障はないようですが、fuseに変更しておきました。

22行目はoor:op="replace"となっていたものをoor:op="fuse"に変更しました。

Configuration - Apache OpenOffice Wikiにはoor:op="fuse"はOpenOffice.org 2.0.3,以降にサポートされたものでそれ以前はoor:op="replace"を使っていて複数のJobs.xcuの要素が結合した時にイベントとの連動が失われることがあった、と書いてありましたのでfuseに変更しました。
(2015.4.2追記LibreOffice5(32)イベント駆動する拡張機能のJavaの例:AsyncJob.oxt その4でようやくその意味が理解できました。)

しかしそのせいなのか、AsyncJob.oxtを拡張機能マネージャーに登録して最初の再起動時にエラーがでてくるときがあります。


もう一度再起動するとこのエラーは出ずにAsyncJob.oxtのメッセージボックスは問題なく表示されました。

どうしてJREの実行とJobs.xcuの設定が関係するのか推測がつきません。

ジョブを起動できるイベント一覧はList of Supported Eventsサポートされるイベントのリストにあります。

英語版のデベロッパーガイドの方がOnCreateとOnLoadFinishedというイベントが追加されています。

17行目のonMyOwnJobEventは自作イベント名です。

自作イベント名を呼び出し方は次のAddons.xcuでやります。

参考にしたサイト


Writing UNO components examples
今回このJobs Addon ExampleのAsyncJobという例をやりました。

LibreOffice: Main Page
LibreOfficeのAPIリファレンス。

UNO コンポーネントを書く
日本語デベロッパーガイドのjobの解説。

Python-UNO bridge
UNO IDLデータタイプとPythonのデータタイプの対応表。

次の関連記事:LibreOffice5(18)拡張機能python-tokencounter-calc-addin.oxtをリモートデバッグする

ブログ検索 by Blogger

Translate

最近のコメント

Created by Calendar Gadget

QooQ