2019年4月28日日曜日

12: UNOディスパッチコマンドを実行し、全情報を取得する(Java編)

<このシリーズの前の記事 | このシリーズの目次 | このシリーズの次の記事>

あなたの怠惰な方法は、実行から入手可能な一部の有用な情報を逃しているかもしれません。

話題


About: UNO (Universal Network Objects)
About: LibreOffice
About: Apache OpenOffice
About: Javaプログラミング言語

この記事の目次


開始コンテキスト



ターゲットコンテキスト



  • 読者は、JavaでUNOディスパッチコマンドを実行してその結果情報および関連情報を取得する方法を知る。
ト書き
Hypothesizer 7、Objector 12A、Objector 12Bがコンピューターの前にいる。


オリエンテーション


Hypothesizer 7
この記事では、JavaでUNOディスパッチコマンドを実行してその結果情報および関連情報を取得する方法を知ることになります。

Objector 12A
「JavaでUNOディスパッチコマンドを実行」というのがどういう意味なのかが分からない。...LibreOfficeマクロにJavaコードが書けるという意味なのか?

Hypothesizer 7
サー、いいえ、そういう意味ではありません。外部コンソールJava UNOクライアント外部GUIJava UNOクライアントJavaコードを含むLibreOffice拡張機能を作ることができ、そうした生成物がUNOディスパッチコマンドを実行できます。

Objector 12A
よく分からない...

Hypothesizer 7
UNOが何で、LibreOfficeにどう関係しているかを理解していなければ、お分かりにならないかもしれません。

Objector 12A
私はただ、自分のマクロからUNOディスパッチコマンドを実行したいだけなんだが...

Hypothesizer 7
あなたのLibreOffice拡張機能内のUNOコンポーネントは、あなたのマクロ内で使用できるので、そのLibreOffice拡張機能内で任意のUNOディスパッチコマンドを実行できるということは、あなたのマクロ内で任意のUNOディスパッチコマンドを実行できるということを意味します。

Objector 12A
ああ、...だが、あなたの言う「LibreOffice拡張機能」とやら(それが何であろうが)を作るのは面倒なだけなんだが...

Hypothesizer 7
それは別に面倒ではありません、LibreOffice拡張機能を作成する環境を用意し(そうする方法は、Linux用はこちらWindows用はこちらにあります)、LibreOffice拡張機能を作成する方法を理解してしまえば(そうする方法は、ここにあります)。LibreOffice拡張機能を作成することがなぜ有利かというと、ドキュメントが整っていないマクロ言語であれこれする苦痛やマクロ言語の限界から解き放たれるからです。...もしあなたがJavaを十分に使いこなせるのであるのであれば、これは有益な取引だと私は思いますが。

Objector 12A
...そりゃ、Javaを十分使えはするが...

Hypothesizer 7
おめでとうございます!Javaでできることは何でもあなたのUNO拡張機能に組み込めます、開発環境を構築し、御自分のUNO拡張機能を作成する方法をを学びさえすれば。

Objector 12A
...そんな予備的行為をするのはうんざり(a pain in the ass)だ...

Hypothesizer 7
デリケートなお尻の穴(ass)をされているんですね...。ただ予備行為が求められるというだけの理由で何かをやりたくないという方々が一部にいらっしゃいますが、予備行為を行なうという少しの苦痛(もしも、それが本当に苦痛だとして)をまず受け入れれば、その後は、もっと大きな苦痛を避けられ、思い通りのことができるようになるんですけれど。

Objector 12A
私の尻の穴は私の問題だ。

Hypothesizer 7
ごもっともでございます。

Objector 12B
あなたの言ってる「関連情報」って、とてもあいまいなんだけど、それって、一体なんなの、具体的には?

Hypothesizer 7
マダム、それは「ステータス変更情報」と呼ばれているものでして、私も、かつてはそう呼んでいたのですが、今は、'関連情報'と私は呼んでいます、「ステータス変更情報」は、実態とかけ離れた名前ですから、私の意見では。

Objector 12B
「「ステータス変更情報」と呼ばれているもの」と言われても、そんな用語、聞いたことないし...

Hypothesizer 7
例えば、'.uno:GoToCell'というCalc UNOディスパッチコマンドがあり、前カレントセル位置および新カレントセル位置という関連情報をオファーします。

Objector 12B
それが?

Hypothesizer 7
そうした情報は、何らかのGUI要素のステータスを変更するために使用されるよう想定されているという理由で「ステータス変更情報」と呼ばれているようですが、その情報がどのように使われるかは全く、プログラマーであるあなた次第であって、その情報をあなたがどのように使うかを誰かが規定するというのは、全くの余計なお世話です。

Objector 12B
「ステータス変更情報」というのは、カレントセルのステータスが変わったという意味ではないの、あるGUI要素のステータスを変更するためにその情報を私が使用しなければならないという意味じゃなく?

Hypothesizer 7
いいえ、そういう意味ではないようです。その証拠に、例えば、'.uno:FontNameList'というCalc UNOディスパッチコマンドは、認識されているフォント名のリストという関連情報をオファーしますが、それは、仰っしゃた意味でのステータス変更では全然ありません。

Objector 12B
うーん、フォントが追加も削除もされていないなら、そのリストは何らのステータス変更も表してはいませんね。

Hypothesizer 7
各UNOディスパッチコマンドは、自身の裁量で、自身の関連情報をオファーしたりそのような情報を何もオファーしなかったりなので、'関連情報'という包括的用語が、私が思いついた最良のものなのです。

Objector 12A
そんな'関連情報'などには興味がないと言ったら?

Hypothesizer 7
その場合は、UNOディスパッチコマンド実行のより容易な方法を使用できます。しかしながら、一部のコマンドは何らかの有益な情報を関連情報としてオファーするので、関連情報を取得する方法の知識には価値があるかもしれません。

Objector 12A
「何らかの有益な情報」?

Hypothesizer 7
例えば、'.uno:GoToCell'というUNOディスパッチコマンドがオファーする前カレントセル位置は役に立つかもしれません、カレントセルの位置を取得する方法を私は他に知りませんから(選択されているセル(複数かもしれない)の位置は容易に取得できますが)。...また、'.uno:FontNameList'というUNOディスパッチコマンドを関連情報を取得することなく呼ぶのは無意味です。

Objector 12A
ははあ。


本体


1: UNOディスパッチコマンドを関連情報を取得することなく実行する容易な方法


Hypothesizer 7
LibreOfficeまたはApache OpenOfficeの任意のインスタンスへのUNOオブジェクト群コンテキストを取得する方法の知識を読者は持っていると事前想定されています(外部コンソールJavaプログラムでそうする方法の記事外部GUIJavaプログラムでそうする方法の記事Javaコードを含むLibreOfficeまたはApache OpenOfficeの拡張機能でそうする方法の記事(本シリーズで置き換えられつつある旧シリーズの旧記事)があります)。

Objector 12A
「事前想定されています」と言われても...

Hypothesizer 7
サー、同じ説明を何度も繰り返すわけにはいかないことをご理解ください。

Objector 12A
...

Hypothesizer 7
第1に、そのドキュメントUNOオブジェクトの'com.sun.star.frame.XModel' UNOプロキシを取得しなければなりません。

Objector 12A
「そのドキュメントUNOオブジェクト」?どの「ドキュメントUNOオブジェクト」だ?

Hypothesizer 7
その上でUNOディスパッチコマンドを実行したいというドキュメントのUNOオブジェクトです。

Objector 12A
うん?

Hypothesizer 7
UNOディスパッチコマンドはあるフレーム上で実行されますが、そのフレーム内に囲まれているドキュメントがそのドキュメントです。

Objector 12A
とにかく、あるCalcシートを操作している際は、そのCalcシートがそのドキュメントなんだろう?

Hypothesizer 7
えーと、私の用語体系では、Calcシートは、あるドキュメントに含まれる複数あるかもしれないシートの1つであって、Calcシート自体はドキュメントではなく、ドキュメント内に含まれる何物かです。

Objector 12A
...とにかく、どうすれば、そのドキュメントUNOオブジェクトが得られるんだ?

Hypothesizer 7
そのドキュメントをあなたのプログラム内で開いたのであれば、ドキュメントが開かれた際に、そのドキュメントUNOオブジェクトを取得できたはずです。

Objector 12A
そうでなければ?

Hypothesizer 7
その場合、特定のドキュメントのUNOオブジェクトを取得する方法がいくつかありますが、それらの全てにここで立ち入ることはやめましょう(将来の記事でそうします)。

その代わり、1つの方法を紹介しましょう: カレントドキュメントのUNOオブジェクトを取得するというものです。以下のコードを見てみましょう、ここで、'theBiasPlanet.unoUtilities.servicesHandling.UnoServiceHandler'は私の'unoUtilitiesToBeDisclosed'プロジェクト(私のUNOサンプル記事ののいずれでも取得できます)に含まれている私のユーティリティクラス、'l_objectsContext'はUNOオブジェクト群コンテキスト、'l_unoDocumentInXModel'は当該ドキュメントUNOオブジェクトの'com.sun.star.frame.XModel' UNOプロキシです。

@Java ソースコード
import com.sun.star.frame.XDesktop;
import com.sun.star.frame.XModel;
import com.sun.star.uno.UnoRuntime;
import theBiasPlanet.unoUtilities.servicesHandling.UnoServiceHandler;

			XDesktop l_desktopInXDesktop = (XDesktop) UnoServiceHandler.getServiceInstance (l_objectsContext, "com.sun.star.frame.Desktop", XDesktop.class);
			XModel l_unoDocumentInXModel = (XModel) UnoRuntime.queryInterface (XModel.class, l_desktopInXDesktop.getCurrentComponent ());

Objector 12A
ふーむ。

Hypothesizer 7
次に、当該フレームUNOオブジェクトの'com.sun.star.frame.XDispatchProvider' UNOプロキシを取得します、以下のように。

@Java ソースコード
import com.sun.star.frame.XController;
import com.sun.star.frame.XDispatchProvider;

			XController l_controllerInXController = (XController) UnoRuntime.queryInterface (XController.class, l_unoDocumentInXModel.getCurrentController ());
			XDispatchProvider l_frameInXDispatchProvider = (XDispatchProvider) UnoRuntime.queryInterface (XDispatchProvider.class, l_controllerInXController.getFrame ());

Objector 12A
うーん、...まあいいか。それから?

Hypothesizer 7
次に、UNOディスパッチヘルパーUNOオブジェクトの'com.sun.star.frame.XDispatchHelper' UNOプロキシを取得します、以下のように。

@Java ソースコード
import com.sun.star.frame.XDispatchHelper;

			XDispatchHelper l_dispatchHelperInXDispatchHelper = (XDispatchHelper) UnoServiceHandler.getServiceInstance (l_objectsContext, "com.sun.star.frame.DispatchHelper", XDispatchHelper.class);

Objector 12A
「UNOディスパッチヘルパー」というのは....ヘルパーなんだろ?

Hypothesizer 7
確かに、それは、'UNOディスパッチング'を容易にするヘルパーです。

Objector 12A
ヘルパーを取得する上記手順は、特に容易ではないが...

Hypothesizer 7
それは定型の手順であって、あなたはそれをユーティリティファンクションにただ一度書けばよいだけです。

Objector 12A
まあいいけど。

Hypothesizer 7
最後に、UNOディスパッチコマンドを実行します、コマンドURLおよびコマンド引数群を指定して、以下のように。

@Java ソースコード
import com.sun.star.beans.PropertyValue;

			PropertyValue [] l_commandArguments = new PropertyValue [1];
			l_commandArguments [0] = new PropertyValue ();
			l_commandArguments [0].Name = "ToPoint";
			l_commandArguments [0].Value = "$B$2";
			Object l_commandResult = l_dispatchHelperInXDispatchHelper.executeDispatch (l_frameInXDispatchProvider, ".uno:GoToCell", "_self", -1, l_commandArguments);

Objector 12A
そこの「_self」と「-1」は何だ?

Hypothesizer 7
それらは、コマンドのターゲットフレーム名とそのフレームを発見するための検索フラグです。

Objector 12A
「_self」は具体的に何を意味しているのか?

Hypothesizer 7
それは、問題にしているフレーム自体(本ケースでは'l_frameInXDispatchProvider')を意味する特殊フレーム名です。

Objector 12A
じゃあ、「-1」は具体的に何を意味しているのか?

Hypothesizer 7
実は、それは何も意味していません、その引数は、前引数で特殊フレーム名が指定された場合は無視されるので。

Objector 12A
'_self'以外のオプションがあるのか?

Hypothesizer 7
どのフレームも1つのフレーム群階層に属しますが、'_parent'は、問題にしているフレームの直接の親を意味し、'_top'は、問題にしているフレームが属する階層のトップフレームを意味し、'_blank'は、新たなトップフレームを意味します。

Objector 12A
...

Hypothesizer 7
ご注意いただきたいのですが、階層はどれもルートフレーム(デスクトップ)の下にありますが、階層のトップフレームは、ルートフレームではなく、ルートフレームの下のフレームです。

Objector 12A
ああ、それじゃあ、トップフレームはデスクトップフレームという親を持っていると...

Hypothesizer 7
はい。

Objector 12A
Calcドキュメントの階層が実際にどうなっているのかがわからない。

Hypothesizer 7
実は、私たちが取得したフレームは、トップフレームであり、子はありません。

Objector 12A
それでは、'_self'の代わりに'_top'を指定しても何も変わらないと?

Hypothesizer 7
変わりません。

Objector 12A
特殊でないフレーム名も使えるんだろう?

Hypothesizer 7
はい、使えます、ただし...

Objector 12A
ただし私は名前を全然知らない。

Hypothesizer 7
実は、あなたが名前を与えない限り、どのフレームの名前もブランクです。

Objector 12A
そういうことか...

コマンド実行の結果情報として何が得られるのか?

Hypothesizer 7
それはUNOディスパッチコマンド次第です、しかし多くの場合、結果情報はあまり有益ではありません。


2: UNOディスパッチコマンドを実行してその結果情報および関連情報を取得する方法


Hypothesizer 7
関連情報を取得するには、本セクションで論じられる方法を取らなければなりません。

結果情報および関連情報はそれぞれ'com.sun.star.frame.XDispatchResultListener'および'com.sun.star.frame.XStatusListener'に通知されるので、'com.sun.star.frame.XDispatchResultListener'を実装するクラス1つおよび'com.sun.star.frame.XStatusListener'を実装するクラス1つ以上を作成します。単一のクラスに両UNOインターフェースを実装させることもできます。

例えば、以下は、'com.sun.star.frame.XDispatchResultListener'の1実装と'com.sun.star.frame.XStatusListener'の1実装です。

@Java ソースコード
import com.sun.star.frame.XDispatchResultListener;
import com.sun.star.frame.XStatusListener;
import com.sun.star.lang.EventObject;
import com.sun.star.frame.DispatchResultEvent;
import com.sun.star.frame.FeatureStateEvent;

			class ResultInformationListener implements XDispatchResultListener {
				@Override
				public void dispatchFinished (DispatchResultEvent a_dispatchResultEvent) {
				}
				
				@Override
				public void disposing (EventObject a_source) {
				}
			}
			class RelatedInformationListener implements XStatusListener {
				@Override
				public void statusChanged (FeatureStateEvent a_featureStateEvent) {
				}
				
				@Override
				public void disposing (EventObject a_source) {
				}
			}
			ResultInformationListener l_resultInformationListener = new ResultInformationListener ();
			RelatedInformationListener l_relatedInformationListener = new RelatedInformationListener ();

Objector 12B
それらメソッドの引数タイプは何なの?

Hypothesizer 7
それらは、UNO APIドキュメント('com.sun.star.frame.DispatchResultEvent'はこちら、'com.sun.star.frame.FeatureStateEvent'はこちら)で調べてください。

Objector 12B
ふーん。

Hypothesizer 7
フレームUNOオブジェクトの'com.sun.star.frame.XDispatchProvider' UNOプロキシを取得しますが、方法は、前セクションで説明したのと同じです。

次に、'com.sun.star.util.URL'オブジェクト(UNOオブジェクトではない)を以下のようにセットアップします。

@Java ソースコード
import com.sun.star.util.XURLTransformer;

			com.sun.star.util.URL [] l_urls = new com.sun.star.util.URL [1];
			l_urls [0] = new com.sun.star.util.URL ();
			l_urls [0].Complete = ".uno:GoToCell";
			XURLTransformer l_urlTransformerInXURLTransformer = (XURLTransformer) UnoServiceHandler.getServiceInstance (l_objectsContext, "com.sun.star.util.URLTransformer", XURLTransformer.class, null);
			l_urlTransformerInXURLTransformer.parseStrict (l_urls);

Objector 12B
ただURLを準備するだけなのに面倒ね...

Hypothesizer 7
そう言えるかもしれません。とにかく、次に、ディスパッチャーUNOオブジェクトの'com.sun.star.frame.XNotifyingDispatch' UNOプロキシを以下のように取得します。

@Java ソースコード
import com.sun.star.frame.XNotifyingDispatch;

			XNotifyingDispatch l_dispatcherInXNotifyingDispatch = UnoRuntime.queryInterface (XNotifyingDispatch.class, l_frameInXDispatchProvider.queryDispatch (l_urls [0], "_self", -1));

Objector 12B
別のURLには別のディスパッチャーを取得するということのようね。

Hypothesizer 7
その可能性はあります。ディスパッチャープロバイダーが、指定されたURLに対するディスパッチャーが何かを決めます。

Objector 12B
そこの「_self」と「-1」は、前セクションと同じことを意味しているの?

Hypothesizer 7
はい、そうです。

次に、関連情報リスナーをディスパッチャーに登録し、最後に、私たちのコマンドをディスパッチャーに送ります、以下のように。

@Java ソースコード
			l_dispatcherInXNotifyingDispatch.addStatusListener (l_relatedInformationListener, l_urls [0]);
			PropertyValue [] l_commandArguments = new PropertyValue [2];
			l_commandArguments [0] = new PropertyValue ();
			l_commandArguments [0].Name = "SynchronMode";
			l_commandArguments [0].Value = Boolean.valueOf (true);
			l_commandArguments [1] = new PropertyValue ();
			l_commandArguments [1].Name = "ToPoint";
			l_commandArguments [1].Value = "$B$5";
			l_dispatcherInXNotifyingDispatch.dispatchWithNotification (l_urls [0], l_commandArguments, l_resultInformationListener);
			l_dispatcherInXNotifyingDispatch.removeStatusListener (l_relatedInformationListener, l_urls [0]);

Objector 12B
'dispatchWithNotification'は、コマンド実行が完了するまで待つの?

Hypothesizer 7
はい、そうします、'SynchronMode'引数によって同期モードが明示的にセットされていますから。

Objector 12B
ふーん...

Hypothesizer 7
'statusChanged'メソッドは複数回呼ばれる可能性があり(または全然呼ばれないかもしれない)、'dispatchFinished'メソッドは最後に一度だけ呼ばれます。

私の各種サンプル(コンソールJava UNOクライアントa GUI Java UNOクライアントなど)で使用されている私のUNOユーティリティプロジェクト、'unoUtilitiesToBeDisclosed'では、UNOドキュメントラッパークラスである'theBiasPlanet.unoUtilities.documentsHandling.UnoDocument'が両リスナーインターフェースを実装しており、'dispatch'というメソッドも持っています。このメソッドは、UNOディスパッチコマンドを実行し、結果情報および関連情報をリターン値にパックして戻します。

Objector 12B
その'dispatch'メソッドの'UnoDispatchSlotsConstantsGroup.BaseDispatchSlot a_dispatchSlot'という第1引数が何なのか分からないんだけど...

Hypothesizer 7
それは、実行しようとするUNOディスパッチコマンドのURLと引数名群を保持しているオブジェクトです。実は、'theBiasPlanet.unoUtilities.constantsGroups.UnoDispatchSlotsConstantsGroup'インターフェースがそうしたオブジェクトたちを保持しています。そうした情報は、各プログラムに文字列リテラルとして散らばらせるよりも1箇所においたほうが望ましいだろうと思いました。


3: 結びとその先


Hypothesizer 7
これで、JavaでUNOディスパッチコマンドを実行してその結果情報および関連情報を取得する方法を知りました。

もちろん、UNOディスパッチコマンドは、他のプログラミング言語(C++、Microsoft .NET Framework(C#およびVisual Basic.NET)、Python、LibreOffice BasicまたはApache OpenOffice Basic、BeanShell、JavaScript)で実行することもでき、それらでそうする方法を私たちは将来の記事群(C++用C#用Python用LibreOfficeまたはApache OpenOffice Basic用)で学びます。

LibreOffice基盤のためのUNOディスパッチコマンドの未完の(今のところ)リストとリストされたコマンドの仕様説明および LibreOffice CalcのためのUNOディスパッチコマンドの未完の(今のところ)リストとリストされたコマンドの仕様説明があります。

UNOディスパッチコマンドを実行するのは、LibreOfficeまたはApache OpenOfficeのインスタンスを操作する1つの方法ですが、LibreOfficeまたはApache OpenOfficeのインスタンスの操作で私たちができることの全てをカバーしているわけではありません: UNOオブジェクトを直接操作することでもっと多くのことを行なえます(本シリーズの目次を御参照ください)。


参考資料


<このシリーズの前の記事 | このシリーズの目次 | このシリーズの次の記事>