LibreOffice(46)インターフェイス名からその属性とメソッド名一覧を取得

ラベル: ,

前の関連記事:LibreOffice(45)APIリファレンス、LibreOffice 4.2 SDK APIをたどる


LibreOffice(44)UNOオブジェクトの属性3:Writerドキュメントに出力でオブジェクトのインターフェイス名一覧を手に入れたのでで今度はメソッド一覧を得てみます。

オブジェクトから情報を得る方法と名前から情報を得る方法の2種類ある


LibreOffice(42)UNOオブジェクトの属性1:正規表現パターンを作成ではPyUNOでstr(UNOオブジェクト)でUNOオブジェクトがもっているサービス名やインターフェイス名を得ました。

そこまでわかっても実際にコードを書くときはメソッドがわからないといけません。

LibreOffice(45)APIリファレンス、LibreOffice 4.2 SDK APIをたどるでやったようにAPIリファレンスとデベロッパーガイドを参考に順番に使えるメソッドをたどっていけばよいのですが毎回やるのは面倒です。

そこでオブジェクトのもっているメソッド一覧を得る方法を探してみました。

OOoBasic/Generic/Reflection - ...?

ここにいろいろな方法の解説があります。

com.sun.star.reflection.CoreReflectionサービス、com.sun.star.beans.Introspectionサービス、com.sun.star.reflection.TypeDescriptionManagerサービスなどがあるようです。

CoreReflectionサービスはどう使うのか私には理解できませんでした。

IntrospectionサービスはCoreReflectionサービスと違って全ての型情報が得られます、とあるのでIntrospectionサービスを使えば目的が叶えられそうです。

TypeDescriptionManagerサービスはIntrospectionサービスと違ってオブジェクトではなく、「com.sun.star.lang.XComponent」といった名前を与えるとそのインターフェイスの情報が得られるとのことです。

デベロッパーガイドでは5.2.3 UNO リフレクション API UNO Reflection APIに解説があるのですが、読んでも難しくて素人にはよくわかりませんね。

でもこれらがLibreOffice(5)PythonでLibreOfficeが動く仕組み:UNOのPyUNOのような言語バインディングに利用されていることがわかりました。

早速Introspectionサービスでオブジェクトのメソッド一覧を得てみよう、と思ったのですがOOoBasic/Generic/Reflection - ...?のBasicの例ではmsgbox oInspected.getMethods( com.sun.star.beans.MethodConcept.ALL)でいけると思ったのですが、「オブジェクト変数は設定できていません。」といわれてうまくいきませんでした。

TypeDescriptionManagerサービスについてはOOoBasic/Generic/TypeDescription - ...?にPythonの例がありました。

これを参考にWriterに結果を出力します。

TypeDescriptionManagerでインターフェイス名から継承したものを除く属性とメソッド名を得る

def in_kara_m(name):  # インターフェイスの全階層名を引数にする。
    from com.sun.star.uno.TypeClass import INTERFACE, INTERFACE_METHOD, INTERFACE_ATTRIBUTE  # UNOの列挙型をインポート。
    tdm = XSCRIPTCONTEXT.getComponentContext().getByName('/singletons/com.sun.star.reflection.theTypeDescriptionManager')  # コンポーネントコンテクストからシングルトンを呼び出す。
    if tdm.hasByHierarchicalName(name):  # nameが存在するのなら
        td = tdm.getByHierarchicalName(name)  # nameを元に型情報を得る。
        list0 = list()  # 属性情報をいれるリストを生成。
        list1 = list()  # メソッド情報をいれるリストを生成。
        output = [name+"インターフェイスの属性とメソッド", ""]  # 出力データをいれるリストを生成。
        if td.getTypeClass() == INTERFACE:  # インターフェイスについて
            for m in td.getMembers():  # インターフェイスのメンバーについて
                type_class = m.getTypeClass() #  メンバーのタイプを取得
                if type_class == INTERFACE_ATTRIBUTE:  # 属性のとき
                    list0.append("\t{:>10}\t{}".format(m.getType().getName(), m.getMemberName()))  # 属性の型と属性の名前をlist0にフォーマットして取得。
                elif type_class == INTERFACE_METHOD:  # メソッドのとき
                    param = ", ".join([p.getType().getName().rpartition(".")[2] for p in m.getParameters()])  # メソッドの引数を型とともにを取得。型の階層名は長くなるので最下層のみ取得。
                    list1.append("\t{}({})".format(m.getMemberName(), param))  # メソッド名と引数をlist1に取得。
        if list0:  # 属性が存在するとき
            list0.append("")  # 属性一覧の最後に空白行を挿入するために空の要素を追加しておく。
            output.extend(["属性:"]+list0)  # 属性を出力リストに追加。
        if list1:
            output.extend(["メソッド:"]+list1)  # メソッドを出力に追加。
        doc1 = XSCRIPTCONTEXT.getDesktop().loadComponentFromURL("private:factory/swriter", "_blank", 0, ())  # 新規Writerドキュメントを開く。
        doc1.getText().setString("\n".join(output))  # 出力リストの要素を改行文字で連結してドキュメントに出力。
        doc1.getCurrentController().getViewCursor().jumpToFirstPage()  # 先頭ページを表示する。
オートメーションで実行するときは先頭にimport unoが必要です。
def libreoffice46():
    name = 'com.sun.star.frame.XController'  # メソッド一覧の例。
    # name = 'com.sun.star.io.XTempFile'  # 属性一覧の例。
    # name = "com.sun.star.awt.XMessageBoxFactory"  # 複数の引数をもつメソッドの例。
    in_kara_m(name)
引数にインターフェイスの全階層名を含んだものを指定してin_kara_m()を呼び出します。

メソッドのみをもつcom.sun.star.frame.XControllerインターフェイスの出力結果。


属性のみをもつcom.sun.star.io.XTempFileインターフェイスの出力結果。


フォントを等幅のCourier Newに変更すると属性の型が右端揃えになります。


複数の引数をもつメソッドがあるcom.sun.star.awt.XMessageBoxFactoryインターフェイスの出力結果。


これらはそのインターフェイス自身のメソッドを得ることはできますが、継承したメソッドは含まれていません。

APIリファレンスと同じような情報を引き出す



APIリファレンスのcreateMessageBoxの部分はこのようになっています。

同じような情報が取り出せないのか調べてみます。

10行目のgetMembers()までを1行にまとめてみます。
def libreoffice46():
    obj = XSCRIPTCONTEXT.getComponentContext().getByName('/singletons/com.sun.star.reflection.theTypeDescriptionManager').getByHierarchicalName("com.sun.star.awt.XMessageBoxFactory").getMembers()
    print(obj)
これを実行すると以下のようなタプルが返ってきます。
(pyuno object (com.sun.star.reflection.XInterfaceMemberTypeDescription)0x2ce27e4{, supportedInterfaces={com.sun.star.reflection.XInterfaceMethodTypeDescription,com.sun.star.lang.XTypeProvider,com.sun.star.uno.XWeak}},)
わかりにくいですが、「pyuno」から一番最後のカンマまでが一つの要素になります。

これは型com.sun.star.reflection.XInterfaceMemberTypeDescriptionのオブジェクトでインターフェイスがcom.sun.star.reflection.XInterfaceMethodTypeDescriptioncom.sun.star.lang.XTypeProvidercom.sun.star.uno.XWeakをもっているということになります。

複数のメソッドがある場合は「0x2ce27e4」が違うでかであとは全部同じ要素のタプルが返ってきます。

ということでこれらのインターフェイスのgetメソッドをみればいろいろ情報が取り出せることになります。

XInterfaceMemberTypeDescriptionXInterfaceMethodTypeDescriptionに継承されていて、XWeakにはgetメソッドがないのでXInterfaceMethodTypeDescriptionXTypeProviderのgetメソッドだけみればよいことになります。
def libreoffice46():
    obj = XSCRIPTCONTEXT.getComponentContext().getByName('/singletons/com.sun.star.reflection.theTypeDescriptionManager').getByHierarchicalName("com.sun.star.awt.XMessageBoxFactory").getMembers()
    print(obj[0].getReturnType())  #  以下XInterfaceMethodTypeDescriptionのメソッド。
    print(obj[0].getParameters())
    print(obj[0].getExceptions())
    print(obj[0].getMemberName())
    print(obj[0].getPosition())
    print(obj[0].getTypeClass())
    print(obj[0].getName())
    print(obj[0].getTypes())  # 以下XTypeProviderのメソッド。
    print(obj[0].getImplementationId())
ということでこれを実行するとXMessageBoxFactoryのメソッド一覧のタプルの0番目の要素についてそれぞれのgetメソッドの結果が返ってきます。

それぞれの行番号がprint()の出力結果に対応します。
pyuno object (com.sun.star.reflection.XTypeDescription)0x2cf0984{, supportedInterfaces={com.sun.star.reflection.XInterfaceTypeDescription2,com.sun.star.lang.XTypeProvider,com.sun.star.reflection.XPublished,com.sun.star.lang.XTypeProvider,com.sun.star.uno.XWeak}}
(pyuno object (com.sun.star.reflection.XMethodParameter)0x2cf0ac4{, supportedInterfaces={com.sun.star.reflection.XMethodParameter,com.sun.star.lang.XTypeProvider,com.sun.star.uno.XWeak}}, pyuno object (com.sun.star.reflection.XMethodParameter)0x2cf0a84{, supportedInterfaces={com.sun.star.reflection.XMethodParameter,com.sun.star.lang.XTypeProvider,com.sun.star.uno.XWeak}}, pyuno object (com.sun.star.reflection.XMethodParameter)0x2cf0a64{, supportedInterfaces={com.sun.star.reflection.XMethodParameter,com.sun.star.lang.XTypeProvider,com.sun.star.uno.XWeak}}, pyuno object (com.sun.star.reflection.XMethodParameter)0x2cf0984{, supportedInterfaces={com.sun.star.reflection.XMethodParameter,com.sun.star.lang.XTypeProvider,com.sun.star.uno.XWeak}}, pyuno object (com.sun.star.reflection.XMethodParameter)0x2cf0a44{, supportedInterfaces={com.sun.star.reflection.XMethodParameter,com.sun.star.lang.XTypeProvider,com.sun.star.uno.XWeak}})
()
createMessageBox
3
<uno.Enum com.sun.star.uno.TypeClass ('INTERFACE_METHOD')>
com.sun.star.awt.XMessageBoxFactory::createMessageBox
(<Type instance com.sun.star.reflection.XInterfaceMethodTypeDescription (<uno.Enum com.sun.star.uno.TypeClass ('INTERFACE')>)>, <Type instance com.sun.star.lang.XTypeProvider (<uno.Enum com.sun.star.uno.TypeClass ('INTERFACE')>)>, <Type instance com.sun.star.uno.XWeak (<uno.Enum com.sun.star.uno.TypeClass ('INTERFACE')>)>)
<ByteSequence instance 'b'\xf9\x0f\xe3g\xe2\x9aI7\xb8\xf7\xdf3uzm\x9b''>
3行目は戻り値の型が返ってくるはずなのですがcom.sun.star.reflection.XTypeDescriptionの型で返ってきていますので、そのgetName()メソッドで戻り値の型名を得ます。
    print(obj[0].getReturnType().Name)
LibreOffice(35)マクロの記録をPythonに翻訳1:リストとタプルにでてきた属性参照でNameを指定してもgetName()と同じ結果が得られますね。

ほうほう、やってみると上記のgetメソッドは全部属性参照で結果が返ってきますね。

4行目はcom.sun.star.reflection.XMethodParameter型のタプルでメソッドの引数が返ってきています。
    print([[i.isIn(), i.isOut(), i.Type.Name, i.Name, i.Position] for i in obj[0].Parameters])
リストに展開しておきます。

5行目もcom.sun.star.container.XNameContainerなどの場合は例外が3行目と同様に返ってきますので同様に展開できるようにしておきます。

7行目は継承しているメソッドを含めて0から数えたときのそのメソッドの番号になります。

10行目も[i.typeName for i in obj[0].Types]でインターフェイス名のリストが得られましたがこれは調べているメソッド自身の情報ではないので割愛します。

11行目も使い道がよくわからないので割愛します。
def libreoffice46():
    obj = XSCRIPTCONTEXT.getComponentContext().getByName('/singletons/com.sun.star.reflection.theTypeDescriptionManager').getByHierarchicalName('com.sun.star.awt.XMessageBoxFactory').getMembers()  # com.sun.star.awt.XMessageBoxFactoryインターフェイスについて情報を得る。
    print(obj[0].ReturnType.Name)  # 戻り値の型
    print([[i.isIn(), i.isOut(), i.Type.Name, i.Name, i.Position] for i in obj[0].Parameters]) #  引数について、[in]の判定、[out]の判定、型、名前、位置番号、のリスト。
    print([i.Name for i in obj[0].Exceptions])  # 例外のリスト
    print(obj[0].MemberName)  # メソッドの名前。
    print(obj[0].Position)  # 継承しているメソッドから先に数えた位置番号。
    print(obj[0].TypeClass)  # enum TypeClass
    print(obj[0].Name)  # メソッドのフルネーム
    # print([i.typeName for i in obj[0].Types])
    # print(obj[0].ImplementationId)
これの3行目から9行目に対して以下の結果が返ってきます。
com.sun.star.awt.XMessageBox
[[True, False, 'com.sun.star.awt.XWindowPeer', 'aParent', 0], [True, False, 'com.sun.star.awt.MessageBoxType', 'eType', 1], [True, False, 'long', 'nButtons', 2], [True, False, 'string', 'sTitle', 3], [True, False, 'string', 'sMessage', 4]]
[]
createMessageBox
3
<uno.Enum com.sun.star.uno.TypeClass ('INTERFACE_METHOD')>
com.sun.star.awt.XMessageBoxFactory::createMessageBox
これらを加工すればAPIリファレンスのメソッド表記を再現できますね。

TypeDescriptionManagerサービスはシングルトンでインスタンス化する


LibreOffice(15)デベロッパーガイド3 コンポーネントコンテクストで学習したシングルトンですが「シングルトンはサービスマネジャーだけ」、と嘘を書いていましたね、修正しなくてはいけません。

上のlibreoffice46()ではcom.sun.star.reflection.TypeDescriptionManagerサービスをシングルトンでインスタンス化しています。

シングルトンでインスタンス化するサービスはcom.sun.star.lang.XMultiComponentFactoryインターフェイスのメソッドではなく、com.sun.star.uno.XComponentContextインターフェイスのgetValueByName()メソッドの引数に/singletons/を先頭にしてサービス名にtheを付けた名前をいれてインスタンス化します。

com.sun.star.reflection.TypeDescriptionManagerサービスの場合は以下のようになります。

XSCRIPTCONTEXT.getComponentContext().getValueByName('/singletons/com.sun.star.reflection.theTypeDescriptionManager')

赤字がサービス名に追加した部分です。

LibreOffice 4.2 SDK APIを「the」で検索するとシングルトンが5個見つかりましたがtheTypeDescriptionManagerはなく、全部のシングルトンの項目があるわけではないようです。

theDefaultProvidertheMacroExpandertheMasterScriptProviderFactorytheOfficeInstallationDirectoriesthePackageManagerFactoryのシングルトンの項目がみつかりました。

ServiceManagerサービスもシングルトンでインスタンス化しますが、あまりにも頻繁に使うもののためにcom.sun.star.uno.XComponentContextインターフェイスに特別にgetServiceManager()メソッドが用意されているようです。

コンポーネントコンテクストのgetByName()メソッドはどこ由来?


libreoffice46()ではコンポーネントコンテクストからシングルトンを得るのにXComponentContextインターフェイスのメソッドのgetValueByName()ではなく、getByName()を使っています。

これがどこ由来なのかわからず悩みました。

デベロッパーズガイドにはコンポーテクストはXComponentContextインターフェイスだけしかサポートしないと書いてあります。(ComponentContext API ComponentContext API)

でもLibreOffice(44)UNOオブジェクトの属性3:Writerドキュメントに出力のobj_atr()で見てみると5個もインターフェイスがでてきます。

継承している型
com.sun.star.uno.XComponentContext

使えるインターフェイス(supportedInterfaces)
com.sun.star.container.XNameContainer
com.sun.star.lang.XComponent
com.sun.star.lang.XTypeProvider
com.sun.star.uno.XComponentContext
com.sun.star.uno.XWeak

getByName()はcom.sun.star.container.XNameContainerからXNameReplaceとたどってXNameAccessインターフェイスにみつけることができました。

でもコンポーネントコンテクストとXNameContainerの接点はAPIリファレンスやデベロッパーズガイドでも見つけられませんでした。

LibreOffice Module cppuhelper (master): cppu::ComponentContext Class Reference

Googleで検索するとこのようなcppu(C++UNO)での継承図をみつけました。

ここに接点がありますね。

PyUNOでも同じような仕組みになっているのかもしれません。

どっちも使えるのなら例外を発生させることができるgetByName()の方が好ましいのかもしれません。

例外はcom.sun.star.uno.RuntimeExceptionはすべてのメソッドで発生するようです。

参考にしたサイト


OOoBasic/Generic/Reflection - ...?
UNO リフレクション API に関するBasicの例での解説。

5.2.3 UNO リフレクション API UNO Reflection API
属性を得る方法のデベロッパーガイドの該当部分。

OOoBasic/Generic/TypeDescription - ...?
TypeDescriptionManagerサービスの使い方のPythonの例。

Python-UNO bridge
Pythonマクロ固有の書き方についての資料があります。

LibreOffice: Namespace List
「the」で検索するとシングルトンの一部がでてきます。

次の関連記事:LibreOffice(47)オブジェクトからメソッド名一覧を得る

PR

0 件のコメント:

コメントを投稿