Skip to content

Instantly share code, notes, and snippets.

@p--q

p--q/unoinsp.py Secret

Last active December 20, 2015 02:03
Show Gist options
  • Save p--q/87dd3783f3e4ebbfdd7d to your computer and use it in GitHub Desktop.
Save p--q/87dd3783f3e4ebbfdd7d to your computer and use it in GitHub Desktop.
LibreOffice5(13)unoinsp.py:IDL名から継承図を出力する
#!/opt/libreoffice5.0/program/python
# -*- coding: utf-8 -*-
import re #正規表現モジュール。
import platform # OS名の取得に使用。
from com.sun.star.uno.TypeClass import SERVICE, INTERFACE, PROPERTY, INTERFACE_METHOD, INTERFACE_ATTRIBUTE
from com.sun.star.beans import PropertyValue
class ObjInsp: # XSCRIPTCONTEXTを引数にしてインスタンス化する。第二引数をFalseにするとローカルのAPIリファレンスへのリンクになる、第三引数をTrueにすると出力抑制をXInterfaceのみにする。
def __init__(self, XSCRIPTCONTEXT, online=True, all_op=False):
self.ctx = XSCRIPTCONTEXT.getComponentContext() # コンポーネントコンテクストを取得。
self.st_omi = {'.uno.XInterface'} if all_op else {'.uno.XInterface', '.uno.XWeak', '.lang.XTypeProvider'} # 結果を出力しないインターフェイス名の集合を初期化。'com.sun.star.uno.XInterface'は必ず指定が必要。
self.stack = list() # スタック。
self.lst_output = list() # 出力行を収納するリスト。
self.lst_key = ["SERVICE", "INTERFACE", "PROPERTY", "INTERFACE_METHOD", "INTERFACE_ATTRIBUTE"] # dic_fnのキーのリスト。
self.dic_fn = dict() # 出力方法を決める関数を入れる辞書。
self.prefix = "http://api.libreoffice.org/docs/idl/ref/" if online else "file://" + self._get_path() + "/sdk/docs/idl/ref/" # ローカルのSDKのリファレンスを参照するかどうか。
self.reg_idl = r'(?<!\w)\.[\w\.]+' # IDL名を抽出する正規表現パターン。
self.reg_i = r'(?<!\w)\.[\w\.]+\.X[\w]+' # インターフェイス名を抽出する正規表現パターン。
self.reg_e = r'(?<!\w)\.[\w\.]+\.[\w]+Exception' # 例外名を抽出する正規表現パターン。
self.css = "com.sun.star" # IDL名の先頭から省略する部分。
self.root = "pyuno object" # treeの根に表示させるもの。
self.flag = False # オブジェクトが直接インターフェイスをもっているときにTrueになるフラグ。
self.tdm = self.ctx.getByName('/singletons/com.sun.star.reflection.theTypeDescriptionManager') # TypeDescriptionManagerをシングルトンでインスタンス化。
def _get_path(self): # LibreOfficeのインストールパスを得る。
cp = self.ctx.getServiceManager().createInstanceWithContext('com.sun.star.configuration.ConfigurationProvider', self.ctx)
node = PropertyValue()
node.Name = 'nodepath'
node.Value = 'org.openoffice.Setup/Product' # share/registry/main.xcd内のノードパス。
ca = cp.createInstanceWithArguments('com.sun.star.configuration.ConfigurationAccess', (node,))
o_name = ca.getPropertyValue('ooName') # ooNameプロパティからLibreOfficeの名前を得る。
o_setupversion = ca.getPropertyValue('ooSetupVersion') # ooSetupVersionからLibreOfficeのメジャーバージョンとマイナーバージョンの番号を得る。
os = platform.system() # OS名を得る。
path = "/opt/" if os == "Linux" else "" # LinuxのときのLibreOfficeのインストール先フォルダ。
return path + o_name.lower() + o_setupversion
def tree(self, obj): # 修飾無しで出力。PyCharmでの使用を想定。
self.dic_fn = dict(zip(self.lst_key, [print for i in range(len(self.lst_key))])) # すべてprintする。
if isinstance(obj, str): # objがIDL名のとき
self.root = obj
print(self.root) # treeの根にIDL名を表示。
self._ext_desc_idl()
else:
print(self.root) # treeの根を表示。
self._ext_desc(obj)
def itree(self, obj): # アンカータグをつけて出力。IPython Notebookでの使用を想定
self._output_setting()
self.lst_output = ["<tt>"] # 出力行を収納するリストを初期化。等幅フォントのタグを指定。
if isinstance(obj, str): # objがIDL名のとき
self.root = obj # treeの根にIDL名を表示
self.lst_output.append(self.root)
self._ext_desc_idl()
else:
self.lst_output.append(self.root)
self._ext_desc(obj)
self.lst_output.append("</tt>") # 等速フォントのタグを閉じる。
from IPython.display import display, HTML # IPython Notebook用
display(HTML("<br/>".join(self.lst_output))) # IPython Notebookに出力。
def wtree(self, obj): # ウェブブラウザの新たなタブに出力。マクロやPyCharmでの使用を想定。
self._output_setting()
self.lst_output = ['<!DOCTYPE html><html><head><meta http-equiv="content-language" content="ja"><meta charset="UTF-8"></head><body><tt>'] # 出力行を収納するリストを初期化。等幅フォントのタグを指定。
if isinstance(obj, str): # objがIDL名のとき
self.root = obj # treeの根にIDL名を表示。
self.lst_output.append(self.root)
self._ext_desc_idl()
else:
self.lst_output.append(self.root)
self._ext_desc(obj)
self.lst_output = [i + "<br>" for i in self.lst_output] # リストの各要素の最後に改行タグを追加する。
self.lst_output.append("</tt></body></html>") # 等速フォントのタグを閉じる。
with open('workfile.html', 'w', encoding='UTF-8') as f: # htmlファイルをUTF-8で作成。すでにあるときは上書き。
f.writelines(self.lst_output) # シークエンスデータをファイルに書き出し。
import webbrowser
webbrowser.open_new_tab(f.name) # デフォルトのブラウザの新しいタブでhtmlファイルを開く。
def _output_setting(self):
self.dic_fn = dict(zip(self.lst_key, [self._fn for i in range(len(self.lst_key))])) # 一旦すべての値をself._fnにする。
self.dic_fn["SERVICE"] = self._fn_s # サービス名を出力するときの関数を指定。
self.dic_fn["INTERFACE"] = self._fn_i # インターフェイス名を出力するときの関数を指定。
def _fn_s(self, item_with_branch): # サービス名にアンカータグをつける。
idl = re.findall(self.reg_idl, item_with_branch) # 正規表現でIDL名を抽出する。
idl = idl[0] if idl else "" # 正規表現パターンにマッチするものがないときは""を入れる。
lnk = "<a href='" + self.prefix + "servicecom_1_1sun_1_1star" + idl.replace(".", "_1_1") + ".html'>" + idl + "</a>" # サービス名のアンカータグを作成。
self.lst_output.append(item_with_branch.replace(" ", "&nbsp;").replace(idl, lnk)) # 半角スペースを置換後にサービス名をアンカータグに置換。
def _fn_i(self, item_with_branch): # インターフェイス名にアンカータグをつける。
idl = re.findall(self.reg_i, item_with_branch) # 正規表現でインターフェイス名を抽出する。
idl = idl[0] if idl else "" # 正規表現パターンにマッチするものがないときは""を入れる。
lnk = "<a href='" + self.prefix + "interfacecom_1_1sun_1_1star" + idl.replace(".", "_1_1") + ".html'>" + idl + "</a>" # インターフェイス名のアンカータグを作成。
self.lst_output.append(item_with_branch.replace(" ", "&nbsp;").replace(idl, lnk)) # 半角スペースを置換後にインターフェイス名をアンカータグに置換。
def _fn(self, item_with_branch): # サービス名とインターフェイスを出力するときの関数。
idl = set(re.findall(self.reg_idl, item_with_branch)) # 正規表現でIDL名を抽出する。
inf = re.findall(self.reg_i, item_with_branch) # 正規表現でインターフェイス名を抽出する。
exc = re.findall(self.reg_e, item_with_branch) # 正規表現で例外名を抽出する。
idl.difference_update(inf, exc) # IDL名のうちインターフェイス名と例外名を除く。
idl = list(idl) # 残ったIDL名はすべてStructと考えて処理する。
item_with_branch = item_with_branch.replace(" ", "&nbsp;") # まず半角スペースをHTMLに置換する。
for i in inf: # インターフェイス名があるとき。
lnk = "<a href='" + self.prefix + "interfacecom_1_1sun_1_1star" + i.replace(".", "_1_1") + ".html' style='text-decoration:none;'>" + i + "</a>" # 下線はつけない。
item_with_branch = item_with_branch.replace(i, lnk)
for i in exc: # 例外名があるとき。
lnk = "<a href='" + self.prefix + "exceptioncom_1_1sun_1_1star" + i.replace(".", "_1_1") + ".html' style='text-decoration:none;'>" + i + "</a>" # 下線はつけない。
item_with_branch = item_with_branch.replace(i, lnk)
for i in idl: # インターフェイス名と例外名以外について。
lnk = "<a href='" + self.prefix + "structcom_1_1sun_1_1star" + i.replace(".", "_1_1") + ".html' style='text-decoration:none;'>" + i + "</a>" # 下線はつけない。
item_with_branch = item_with_branch.replace(i, lnk)
self.lst_output.append(item_with_branch)
def _ext_desc(self, obj): # 末裔を抽出する
self.stack = list() # スタックを初期化。
if hasattr(obj, "getSupportedServiceNames"): # オブジェクトがサービスを持っているとき。
self.flag = True if hasattr(obj, "getTypes") else False # サービスを介さないインターフェイスがあるときフラグを立てる。
st_ss = set(obj.getSupportedServiceNames()) # オブジェクトのサポートサービス名一覧の集合を得る。
st_sups = set() # 親サービスを入れる集合。
if len(st_ss) > 1: # サポートしているサービス名が複数ある場合。
self.stack = [self.tdm.getByHierarchicalName(i) for i in st_ss] # サポートサービスのTypeDescriptionオブジェクトをスタックに取得。
while self.stack: # スタックがある間実行。
j = self.stack.pop() # サービスのTypeDescriptionオブジェクトを取得。
t_std = j.getMandatoryServices() + j.getOptionalServices() # 親サービスのタプルを取得。
lst_std = [i for i in t_std if not i.Name in st_sups] # 親サービスのTypeDescriptionオブジェクトのうち既に取得した親サービスにないものだけを取得。
self.stack.extend(lst_std) # スタックに新たなサービスのTypeDescriptionオブジェクトのみ追加。
st_sups.update([i.Name for i in lst_std]) # 既に取得した親サービス名の集合型に新たに取得したサービス名を追加。
st_ss.difference_update(st_sups) # オブジェクトのサポートサービスのうち親サービスにないものだけにする=これがサービスの末裔。
self.stack = [self.tdm.getByHierarchicalName(i) for i in st_ss] # TypeDescriptionオブジェクトに変換。
if self.stack: self.stack.sort(key=lambda x: x.Name, reverse=True) # Name属性で降順に並べる。
self._make_tree()
if hasattr(obj, "getTypes"): # サポートしているインターフェイスがある場合。
self.flag = False #
st_si = set([i.typeName.replace(self.css, "") for i in obj.getTypes()]) # サポートインターフェイス名を集合型で取得。
lst_si = sorted(list(st_si.difference(self.st_omi)), reverse=True) # 除外するインターフェイス名を除いて降順のリストにする。
self.stack = [self.tdm.getByHierarchicalName(i if not i[0] == "." else self.css + i) for i in lst_si] # TypeDescriptionオブジェクトに変換。self.cssが必要。
self._make_tree()
if not (hasattr(obj, "getSupportedServiceNames") or hasattr(obj, "getTypes")): # サポートするサービスやインターフェイスがないとき。
self.dic_fn["SERVICE"]("サポートするサービスやインターフェイスがありません。")
def _ext_desc_idl(self): # objがIDL名のとき。
j = None
try:
j = self.tdm.getByHierarchicalName(self.root) # IDL名からTypeDescriptionオブジェクトを取得。
except:
pass
if not j: # TypeDescriptionオブジェクトを取得できなかったとき。
self.root = self.root + "はIDL名ではありません。"
print(self.root)
self.lst_output.append(self.root)
else:
typcls = j.getTypeClass() # jのタイプクラスを取得。
self.stack = list() # スタックを初期化。
if typcls == INTERFACE or typcls == SERVICE: # jがサービスかインターフェイスのとき。
self.stack = [j] # TypeDescriptionオブジェクトをスタックに取得
self._make_tree()
else: # サービスかインターフェイス以外のときは未対応。
pass
def _make_tree(self): # 末裔から祖先を得て木を出力する
if self.stack: # 起点となるサービスかインターフェイスがあるとき。
lst_level = [1 for i in self.stack] # self.stackの要素すべてについて階層を取得。
indent = " " # インデントを設定。
m = 0 # 最大文字数を初期化。
inout_dic = {(True, False): "[in]", (False, True): "[out]", (True, True): "[inout]"} # メソッドの引数のinout変換辞書。
while self.stack: # スタックがある間実行。
j = self.stack.pop() # スタックからTypeDescriptionオブジェクトをpop。
level = lst_level.pop() # jの階層を取得。
typcls = j.getTypeClass() # jのタイプクラスを取得。
branch = ["", ""] # 枝をリセット。jがサービスまたはインターフェイスのときjに直接つながる枝は1番の要素に入れる。それより左の枝は0番の要素に加える。
t_itd = tuple() # インターフェイスのTypeDescriptionオブジェクトの入れ物を初期化。
t_spd = tuple() # サービス属性のTypeDescriptionオブジェクトの入れ物を初期化。
t_md = tuple() # メソッドのTypeDescriptionオブジェクトの入れ物を初期化。
if level > 1: # 階層が2以上のとき。
p = 1 # 処理開始する階層を設定。
if self.flag: # サービスを介さないインターフェイスがあるとき
branch[0] = "│ " # 階層1に立枝をつける。
p = 2 # 階層2から処理する。
for i in range(p, level): # 階層iから出た枝が次行の階層i-1の枝になる。
branch[0] += "│ " if i in lst_level else indent # iは枝の階層ではなく、枝のより上の行にあるその枝がでた階層になる。
if typcls == INTERFACE or typcls == SERVICE: # jがサービスかインターフェイスのとき。
if level == 1 and self.flag: # 階層1かつサービスを介さないインターフェイスがあるとき
branch[1] = "├─" # 階層1のときは下につづく分岐をつける。
else:
branch[1] = "├─" if level in lst_level else "└─" # スタックに同じ階層があるときは"├─" 。
else: # jがインターフェイスかサービス以外のとき。
branch[1] = indent # 横枝は出さない。
if level in lst_level: # スタックに同じ階層があるとき。
typcls2 = self.stack[lst_level.index(level)].getTypeClass() # スタックにある同じ階層のものの先頭の要素のTypeClassを取得。
if typcls2 == INTERFACE or typcls2 == SERVICE: branch[1] = "│ " # サービスかインターフェイスのとき。横枝だったのを縦枝に書き換える。
if typcls == INTERFACE_METHOD: # jがメソッドのとき。
typ = j.ReturnType.Name.replace(self.css, "") # 戻り値の型を取得。
if typ[1] == "]": typ = typ.replace("]", "") + "]" # 属性がシークエンスのとき[]の表記を修正。
stack2 = list(j.Parameters)[::-1] # メソッドの引数について逆順(降順ではない)にスタック2に取得。
if not stack2: # 引数がないとき。
branch.append(typ.rjust(m) + " " + j.MemberName.replace(self.css, "") + "()") # 「戻り値の型(固定幅mで右寄せ) メソッド名()」をbranchの3番の要素に取得。
self.dic_fn["INTERFACE_METHOD"]("".join(branch)) # 枝をつけてメソッドを出力。
else: # 引数があるとき。
m3 = max([len(i.Type.Name.replace(self.css, "")) for i in stack2]) # 引数の型の最大文字数を取得。
k = stack2.pop() # 先頭の引数を取得。
inout = inout_dic[(k.isIn(), k.isOut())] # 引数の[in]の判定、[out]の判定
typ2 = k.Type.Name.replace(self.css, "") # 引数の型を取得。
if typ2[1] == "]": typ2 = typ2.replace("]", "") + "]" # 引数の型がシークエンスのとき[]の表記を修正。
branch.append(typ.rjust(m) + " " + j.MemberName.replace(self.css, "") + "( " + inout + " " + typ2.rjust(m3) + " " + k.Name.replace(self.css, "")) # 「戻り値の型(固定幅で右寄せ) メソッド名(inout判定 引数の型(固定幅m3で左寄せ) 引数名」をbranchの3番の要素に取得。
m2 = len(typ.rjust(m) + " " + j.MemberName.replace(self.css, "") + "( ") # メソッドの引数の部分をインデントする文字数を取得。
if stack2: # 引数が複数あるとき。
branch.append(",") # branchの4番の要素に「,」を取得。
self.dic_fn["INTERFACE_METHOD"]("".join(branch)) # 枝をつけてメソッド名とその0番の引数を出力。
del branch[2:] # branchの2番以上の要素は破棄する。
while stack2: # 1番以降の引数があるとき。
k = stack2.pop()
inout = inout_dic[(k.isIn(), k.isOut())] # 引数の[in]の判定、[out]の判定
typ2 = k.Type.Name.replace(self.css, "") # 引数の型を取得。
if typ2[1] == "]": typ2 = typ2.replace("]", "") + "]" # 引数の型がシークエンスのとき[]の表記を修正。
branch.append(" ".rjust(m2) + inout + " " + typ2.rjust(m3) + " " + k.Name.replace(self.css, "")) # 「戻り値の型とメソッド名の固定幅m2 引数の型(固定幅m3で左寄せ) 引数名」をbranchの2番の要素に取得。
if stack2: # 最後の引数でないとき。
branch.append(",") # branchの3番の要素に「,」を取得。
self.dic_fn["INTERFACE_METHOD"]("".join(branch)) # 枝をつけて引数を出力。
del branch[2:] # branchの2番以上の要素は破棄する。
t_ex = j.Exceptions # 例外を取得。
if t_ex: # 例外があるとき。
self.dic_fn["INTERFACE_METHOD"]("".join(branch)) # 最後の引数を出力。
del branch[2:] # branchの2番以降の要素を削除。
n = ") raises ( " # 例外があるときに表示する文字列。
m4 = len(n) # nの文字数。
stack2 = list(t_ex) # 例外のタプルをリストに変換。
branch.append(" ".rjust(m2 - m4)) # 戻り値の型とメソッド名の固定幅m2からnの文字数を引いて2番の要素に取得。
branch.append(n) # nを3番要素に取得。他の要素と別にしておかないとn in branchがTrueにならない。
while stack2: # stack2があるとき
k = stack2.pop() # 例外を取得。
branch.append(k.Name.replace(self.css, "")) # branchの3番(初回以外のループでは4番)の要素に例外名を取得。
if stack2: # まだ次の例外があるとき。
self.dic_fn["INTERFACE_METHOD"]("".join(branch) + ",") # 「,」をつけて出力。
else: # 最後の要素のとき。
self.dic_fn["INTERFACE_METHOD"]("".join(branch) + ")") # 閉じ括弧をつけて出力。
if n in branch: # nが枝にあるときはnのある3番以上のbranchの要素を削除。
del branch[3:]
branch.append(" ".rjust(m4)) # nを削った分の固定幅をbranchの3番の要素に追加。
else:
del branch[4:] # branchの4番以上の要素を削除。
else: # 例外がないとき。
self.dic_fn["INTERFACE_METHOD"]("".join(branch) + ")") # 閉じ括弧をつけて最後の引数を出力。
else: # jがメソッド以外のとき。
if typcls == INTERFACE: # インターフェイスのとき。XInterfaceTypeDescription2インターフェイスをもつTypeDescriptionオブジェクト。
branch.append(j.Name.replace(self.css, "")) # インターフェイス名をbranchの2番要素に追加。
t_itd = j.getBaseTypes() + j.getOptionalBaseTypes() # 親インターフェイスを取得。
t_md = j.getMembers() # インターフェイス属性とメソッドのTypeDescriptionオブジェクトを取得。
self.dic_fn["INTERFACE"]("".join(branch)) # 枝をつけて出力。
elif typcls == PROPERTY: # サービス属性のとき。
typ = j.getPropertyTypeDescription().Name.replace(self.css, "") # 属性の型
if typ[1] == "]": typ = typ.replace("]", "") + "]" # 属性がシークエンスのとき[]の表記を修正。
branch.append(typ.rjust(m) + " " + j.Name.replace(self.css, "")) # 型は最大文字数で右寄せにする。
self.dic_fn["PROPERTY"]("".join(branch)) # 枝をつけて出力。
elif typcls == INTERFACE_ATTRIBUTE: # インターフェイス属性のとき。
typ = j.Type.Name.replace(self.css, "") # 戻り値の型
if typ[1] == "]": typ = typ.replace("]", "") + "]" # 属性がシークエンスのとき[]の表記を修正。
branch.append(typ.rjust(m) + " " + j.MemberName.replace(self.css, "")) # 型は最大文字数で右寄せにする。
self.dic_fn["INTERFACE_METHOD"]("".join(branch)) # 枝をつけて出力。
elif typcls == SERVICE: # jがサービスのときtdはXServiceTypeDescriptionインターフェイスをもつ。
branch.append(j.Name.replace(self.css, "")) # サービス名をbranchの2番要素に追加。
t_std = j.getMandatoryServices() + j.getOptionalServices() # 親サービスを取得。
self.stack.extend(sorted(list(t_std), key=lambda x: x.Name, reverse=True)) # 親サービス名で降順に並べてサービスのTypeDescriptionオブジェクトをスタックに追加。
lst_level.extend([level + 1 for i in t_std]) # 階層を取得。
itd = j.getInterface() # new-styleサービスのインターフェイスを取得。TypeDescriptionオブジェクト。
if itd: # new-styleサービスのインターフェイスがあるとき。
t_itd = itd, # XInterfaceTypeDescription2インターフェイスをもつTypeDescriptionオブジェクト。
else: # new-styleサービスのインターフェイスがないときはold-styleサービスのインターフェイスを取得。
t_itd = j.getMandatoryInterfaces() + j.getOptionalInterfaces() # XInterfaceTypeDescriptionインターフェイスをもつTypeDescriptionオブジェクト。
t_spd = j.Properties # サービスからXPropertyTypeDescriptionインターフェイスをもつオブジェクトのタプルを取得。
self.dic_fn["SERVICE"]("".join(branch)) # 枝をつけて出力。
if t_itd: # 親インターフェイスがあるとき。(TypeDescriptionオブジェクト)
lst_itd = [i for i in t_itd if not i.Name.replace(self.css, "") in self.st_omi] # st_omiを除く。
self.stack.extend(sorted(lst_itd, key=lambda x: x.Name, reverse=True)) # 降順にしてスタックに追加。
lst_level.extend([level + 1 for i in lst_itd]) # 階層を取得。
self.st_omi.update([i.Name.replace(self.css, "") for i in lst_itd]) # すでにでてきたインターフェイス名をst_omiに追加して次は使わないようにする。
if t_md: # インターフェイス属性とメソッドがあるとき。
self.stack.extend(sorted(t_md, key=lambda x: x.Name, reverse=True)) # 降順にしてスタックに追加。
lst_level.extend([level + 1 for i in t_md]) # 階層を取得。
m = max([len(i.ReturnType.Name.replace(self.css, "")) for i in t_md if i.getTypeClass() == INTERFACE_METHOD] + [len(i.Type.Name.replace(self.css, "")) for i in t_md if i.getTypeClass() == INTERFACE_ATTRIBUTE]) # インターフェイス属性とメソッドの型のうち最大文字数を取得。
if t_spd: # サービス属性があるとき。
self.stack.extend(sorted(list(t_spd), key=lambda x: x.Name, reverse=True)) # 降順にしてスタックに追加。
lst_level.extend([level + 1 for i in t_spd]) # 階層を取得。
m = max([len(i.getPropertyTypeDescription().Name.replace(self.css, "")) for i in t_spd]) # サービス属性の型のうち最大文字数を取得。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment