Tips

Spotlightメタ情報を取得する方法(BLOB配列、ドラッグ&ドロップの応用例)

日付2008/08/14
ID08-022
バージョン11.2
プラットフォームMac

Mac OS X 10.4以降には、Spotlightという強力なファイル検索システムが存在します。この記事では、Spotlightメタ情報を取得するプログラムを例に、4D v11 SQLの新しい機能である疑似BLOB配列やドラッグ&ドロップの実装方法を紹介します。

リストボックスのデザイン

このサンプルでは、リストボックスのデザインが工夫されています。外観はファイルパスの配列Column1だけの構造ですが、ほかに非表示の列であるピクチャ配列Column2とピクチャ配列Column3が存在します。これらのピクチャ配列はデータを格納するためのもの(疑似BLOB配列)です。

ファイルパスが複数、ドラッグ&ドロップされたとき、リストボックスのOn Dropイベントで素早くパスを取得し、Column1に代入します。その後、ループの中でそれぞれのファイルシステムエイリアスのSpotlightメタ情報を取得します(後述するmdlsコマンド)。その結果、ファイルごとにSpotlightキーの配列とSpotlightデータの配列が返されます。これらをVARIABLE TO BLOBでBLOBに変換し、続いてBLOB TO PICTUREにカスタムコーデック("mdls_keys", "mdls_values"名前は任意)を渡してデータカプセルピクチャを作っています。これは絵にならないピクチャですが、リストボックスの非表示ピクチャ配列に格納するので問題ありません。結局、リストボックスの構造は、テキスト配列のファイルパス:配列の配列のキー名:配列の配列のキー値ということになります。

mdlsコマンドは、ドラッグ&ドロップの時に一度だけ実行しているのがポイントです。次からは(リストボックスOn Selection Changeイベント)、リストボックスの非表示列のピクチャをBLOBに変換し、さらにBLOBをテキストの配列に戻します。毎回、コマンドを実行するよりもずっと効率的です。

mdls

Spotlightのメタ情報を取得するDarwinコマンドは、mdlsです。これを4Dのメソッドでくるんだものを使用します。$1にファイルパス、$2と$3にはそれぞれメタ情報のキー名とキー値を受け取るテキスト配列を渡します。

C_TEXT($1)
C_POINTER($2;$3)

If (Count parameters=3)
	
	If (Not(Nil($2))) & (Not(Nil($3)))
		
		If (Type($2->)=Text array ) & (Type($3->)=Text array )
			
			PLATFORM PROPERTIES($Platform_l)
			
			If ($Platform_l#Windows )
				
				$TargetPath_t:=$1
				
				POSIX (->$TargetPath_t)
				
				C_TEXT($stdIn_t;$stdOut_t)
				C_BLOB($stdOut_x;$StdErr_x)
				
				LAUNCH EXTERNAL PROCESS("mdls "+$TargetPath_t;$stdIn_t;$stdOut_x;$StdErr_x)
				
				  `この変数をデバッガに登録しておくと良いかも
				ERROR_t:=Convert to text($StdErr_x;"UTF-8")
				
				$stdOut_t:=Convert to text($stdOut_x;"UTF-8")
				
				regex_mdls ($stdOut_t;$2;$3)
				
			End if 
			
		End if 
		
	End if 
	
End if

UNIXに渡すとき、パスはコロン区切りのHFSからスラッシュ区切りでエスケープされたPOSIXスタイルに変換する必要があります。

POSIX

C_POINTER($1)

PLATFORM PROPERTIES($Platform_l)

If ($Platform_l#Windows )
	
	If (Count parameters=1)
		
		If (Not(Nil($1)))
			
			If (Type($1->)=Is Text )
				
				$AppleScriptPath_t:=Replace string($1->;":";"/")
				$systemFolder_t:=System folder(System )
				$CurrentVolume_t:=Substring($systemFolder_t;1;Position(":";$systemFolder_t)-1)
				$TargetVolume_t:=Substring($AppleScriptPath_t;1;Position("/";$AppleScriptPath_t)-1)
				
				If ($CurrentVolume_t=$TargetVolume_t)  `Is Macintosh HD
					
					$AppleScriptPath_t:=Substring($AppleScriptPath_t;Position("/";$AppleScriptPath_t))
					
				Else 
					
					$AppleScriptPath_t:="/Volumes/"+$AppleScriptPath_t
					
				End if 
				
				$1->:=Replace string($AppleScriptPath_t;" ";"\\ ")
				
			End if 
			
		End if 
		
	End if 
	
End if 

regex_mdls

システムから標準出力で返されるデータはUTF-8エンコーディングされています。これを4Dの内部エンコーディングであるUTF-16に変換し、その上で行ごとの情報を切り出す必要があります。このようなときに便利なのがパターンマッチ(正規表現)コマンドのMatch regexです。

C_TEXT($1)
C_POINTER($2;$3)

If (Count parameters=3)
	
	If (Not(Nil($2))) & (Not(Nil($3)))
		
		If (Type($2->)=Text array ) & (Type($3->)=Text array )
			  `%W-518.5
			ARRAY TEXT($2->;0)
			ARRAY TEXT($3->;0)
			
			$regex_t:="(.+?)(\\s+)=(\\s+)(.+?)\\n"
			$SourceText_t:=$1
			
			ARRAY LONGINT($Positions_al;0)
			ARRAY LONGINT($Lengths_al;0)
			
			While (Match regex($regex_t;$SourceText_t;1;$Positions_al;$Lengths_al))
				
				APPEND TO ARRAY($2->;Substring($SourceText_t;$Positions_al{1};$Lengths_al{1}))
				APPEND TO ARRAY($3->;Substring($SourceText_t;$Positions_al{4};$Lengths_al{4}))
				
				If ($3->{Size of array($3->)}="(")
					
					$regex_sub_t:="(.+?)(\\s+)=(\\s+)\\(\\n(?s)(.+?)\\)\\n"
					
					If (Match regex($regex_sub_t;$SourceText_t;1;$Positions_al;$Lengths_al))
						
						$3->{Size of array($3->)}:=Substring($SourceText_t;$Positions_al{4};$Lengths_al{4})
						
					End if 
					
				End if 
				
				$SourceText_t:=Substring($SourceText_t;$Positions_al{4}+$Lengths_al{4}+1;Length($SourceText_t))
				
			End while 
			
		End if 
		
	End if 
	
End if 

regex_mdls_sub

メタ情報の中にはカンマで区切られた複数の情報を持つものもあります。これについては、さらにパターンマッチを適用して個別の値を取り出すようにします。

C_TEXT($1)
C_POINTER($2)

If (Count parameters=2)
	
	If (Not(Nil($2)))
		
		If (Type($2->)=Text array )
			  `%W-518.5
			ARRAY TEXT($2->;0)
			
			$regex_t:="(\\s*)(.+?)(,|$)"
			$SourceText_t:=$1
			
			ARRAY LONGINT($Positions_al;0)
			ARRAY LONGINT($Lengths_al;0)
			
			While (Match regex($regex_t;$SourceText_t;1;$Positions_al;$Lengths_al))
				
				APPEND TO ARRAY($2->;Substring($SourceText_t;$Positions_al{2};$Lengths_al{2}))
				$SourceText_t:=Substring($SourceText_t;$Positions_al{3}+$Lengths_al{3};Length($SourceText_t))
				
			End while 
			
		End if 
		
	End if 
	
End if 

まとめ

リストボックスは、リストデータに適したオブジェクトであるばかりでなく、ドラッグ&ドロップの実装も容易です。特にピクチャ配列を非表示のカラムとして含めておけば、BLOB変換により「配列の配列」もカラムデータとして扱うことができ、さらに複雑なデータも効果的に処理することができます。

ストラクチャファイル