指定のBundle IDのアプリケーションのInfo.plistの値からSDEF(AppleScript命令定義ファイル)のファイル情報を読み取って、実際にSDEFの実ファイルを読み取り、得られた文字列をXMLとして解釈する際にXincludeを展開して、再度文字列に戻すAppleScriptです。
もともと、SDEFを読み取って分析することは、アプリケーションのバージョン間の変更差分を検出する用途で試していました。これは、変化のみを検出することを目的としたもので、内容について仔細に認識することを目的としたものではありませんでした。
そこから1歩進んで、AppleScript側から実行環境情報を取得する試みをいろいろ続けてきました。
実行環境自体が何なのか、という判別はここ数年で一番大きな成果だったと感じます。地球にいながら「銀河系の中の、太陽系の中の第3惑星にいる」ということが観測できるようになったわけです。
さらに、それぞれのランタイム環境の識別が可能になったことにより、各実行環境の「差」を判定しやすくなった、という副産物もありました。
話を戻しましょう。
SDEFを読んで、当該アプリケーションが何を行えるかをAppleScript側からも認識できるはずで、そのための調査も開始していました。
System Eventsの「Scripting Definition Suite」はその名前と用語から、AppleScriptからSDEFの解析を行うための部品であることが推測され、いろいろ調べてきたものの…..Apple側がSystem Eventsそれ自体のチェックを行うために設けた「メンテナンス&デバッグのための機能(ただしSystem Events専用)」だという結論にいたっています。つまり、System Events以外にはまるっきり使えません。
これで、AppleScriptのプログラム側からアプリケーション側の仕様を自動判別&解析するためには、SDEFそのものを解析するしかないという話になりました。
ただ、SDEFの直接解析については1つの「ちょっと困った仕様」がありました。外部ファイルをincludeできる(Xinclude)ようになっており、そのファイルを元のSDEF上に展開するのは、まじめにXML上の情報を分析する必要があります。ファイル・システム上のどこの場所からどのファイルを読み込むのか、という話と……XMLのオブジェクトモデル上のどこに展開するのかという話の両方を処理する必要があります。まじめに組むと、それなりの時間がかかってしまいそうです。
少ない例から推測してScriptを書くことは不可能ではありませんが、「隠れ仕様」が存在していた場合には対処できません。OS側にその機能があれば、そのままそっくり利用できたほうがよいだろうと思われました。
このXincludeの展開を(他力本願で)行うには、どこのレイヤーのどの機能を用いるべきなのか?
matttのOno.frameworkなどもビルドして試してみたものの、Xincludeの展開はソースコードを全文検索しても見つけられず、もうちょっと物理層に近いレイヤーのlibxml2の機能にアクセスする必要があるようでした。
ほかにも、Unix Shell Commandに、「xsltproc」コマンドが存在しており、Xinclude展開を行ってくれるようでした(XSLTを書けば)。スクリプトエディタに任意のアプリケーションをオープンさせればAppleScript用語辞書を表示してくれるわけで、この表示中のSDEFについてはXincludeがすべて解決された状態になっているため、AppleScriptからスクリプトエディタを操作するという手段もないわけではないのですが、もっとスマートに(こっそり)処理したかったので、これは(機能することがわかっていながらも)使えません。
結局、NSXMLDocumentのNSXMLNodeOptionsでNSXMLDocumentXIncludeを指定すると、XMLテキストをオブジェクトに解釈する際に外部ファイルを展開してくれることが判明。
Mail.appのSDEFでいえば、Standard Suiteにinclude指定があり、
元ファイルを読み込んだだけでは完全体のSDEFを取得できません。これを、本AppleScriptを用いて読み込んで解釈すると、
のように、指定パスに存在している「CocoaStandard.sdef」を展開します。ほかにも、Microsoft系のアプリケーションでXincludeを使用しており、本AppleScriptのような処理を経た上でSDEFの分析を行う必要があることでしょう。
これが、今日明日の段階で何か「すごいこと」ができるような存在になることは決してありませんが、この部品が存在していることで異次元の処理ができるようになるような手応えはあります。
AppleScript名:xincludeを有効にしてXML(sdef)読み込み v2.scptd |
— – Created by: Takaaki Naganoya – Created on: 2022/07/26 — – Copyright © 2022 Piyomaru Software, All Rights Reserved — use AppleScript version "2.4" — Yosemite (10.10) or later use framework "Foundation" use framework "AppKit" use scripting additions set targAppBundleID to "com.apple.mail" –SDEFを取り出すターゲットのアプリケーションのBundle ID set thePath to POSIX path of (path to application id targAppBundleID) set aSDEFname to retAppSdefNameFromBundleIPath(thePath, "OSAScriptingDefinition") of me if aSDEFname = false then return if aSDEFname does not end with ".sdef" then set aSDEFname to aSDEFname & ".sdef" set sdefFullPath to thePath & "Contents/Resources/" & aSDEFname set sdefAlias to (POSIX file sdefFullPath) as alias –sdefのフルパスを求める –SDEF読み込み(Xincludeの展開が必要な状態) tell current application set theXML to read sdefAlias as «class utf8» end tell –NSXMLDocumentの生成、Xincludeを有効に set {theXMLDoc, theError} to current application’s NSXMLDocument’s alloc()’s initWithXMLString:theXML options:(current application’s NSXMLDocumentXInclude) |error|:(reference) –XMLを文字データ化 set aDocStr to (theXMLDoc’s XMLData) set aDocStr2 to (current application’s NSString’s alloc()’s initWithData:(aDocStr) encoding:(current application’s NSUTF8StringEncoding)) as string –指定パスからアプリケーションのScriptabilityをbooleanで返す on retAppSdefNameFromBundleIPath(appPath as string, aKey as string) set aDict to (current application’s NSBundle’s bundleWithPath:appPath)’s infoDictionary() set aRes to aDict’s valueForKey:(aKey) if aRes = missing value then return false set asRes to aRes as string return asRes as string end retAppSdefNameFromBundleIPath |