EnumeratorをEnumerableに2007年05月08日 02時15分18秒

Enumeratorオブジェクト

ActiveXObjectと同様なJScript固有オブジェクトにEnumeratorがある。これはCOMのコレクションインターフェイスをJScriptから操作するためにある(と思われる)

Enumeratorオブジェクトは、

// folderはfsoのFolderオブジェクトとする
// folder#Filesはfolderが示すディレクトリ内のファイルコレクション
var list = new Enumerator( folder.Files );
for(; ! list.atEnd(); list.moveNext()) {
	// list#item()でコレクション要素(=fsoのFileオブジェクト)を取得
	var file = list.item();
	// あとはfileを操作する
}
のように、atEnd()メソッドがtrueを返すまでmoveNext()でループ処理を行い、コレクション要素を取得するのにitem()メソッドを使用する、という流れで使用するが、ちょっと独特のインターフェイスなためちょっと面倒に感じる。

Enumerableに拡張

以前のコマンドライン引数の展開ではラッパーオブジェクトを返す$E()関数を定義したが、今回は直接Enumerableにしてみる。

といっても別に面倒なことがあるわけではなく、_each()を実装後にEnumerableインターフェイスを追加するだけ。

// EnumeratorにEnumerableの機能を追加する
Enumerator.prototype._each = function(iterator) {
	this.moveFirst();
	for(; ! this.atEnd(); this.moveNext()) {
		iterator( this.item() );
	}
}
Object.extend( Enumerator.prototype, Enumerable );
こうして拡張されたEnumeratorを使用して、C:\直下のファイルを列挙するサンプルは以下のようになる。JScript.consoleは前回のエントリで試したオブジェクトとする。
new Enumerator( JScript.fso.GetFolder( "C:\\" ).Files ).each( function(file, index) {
	JScript.console.println( index + " - " + file.Name );
} );
この方法なら、コレクションを配列にキャッシュしないので、たとえば1,000単位や10,000単位のファイルがあるディレクトリの操作でもたいしてメモリを消費せずにすむ(って前も書いたな)。

サンプルコード

まとめとして、前回のエントリの定義+ライブラリロードを加えたサンプルを以下に示す。dummy.jsとprototype.jsは同じディレクトリにあるものとしている。

var JScript = {
	// WshShellのインスタンス
	shell : new ActiveXObject("WScript.Shell"),
	// fsoのインスタンス
	fso : new ActiveXObject("Scripting.FileSystemObject"),
	// 起動ディレクトリ
	startupPath : WScript.ScriptFullName.substr( 0, WScript.ScriptFullName.lastIndexOf("\\") ),
	// スクリプトファイル名
	name : WScript.ScriptName,
	console : {
		// 文字列出力
		print : function(str) {
			WScript.StdOut.Write( str || "" );
		},
		// 文字列の行出力
		println : function(str) {
			WScript.Echo( str || "" );
		},
		// 文字列入力
		readln : function() {
			return WScript.StdIn.ReadLine();
		},
		// ポップアップアラート(consoleじゃないけど)
		popup : function(str, title) {
			JScript.shell.Popup( str || "", 0, title || WScript.ScriptName );
		}
	},
	// 2つのパスを結合するヘルパメソッド
	buildPath : function(path1, path2) {
		return [ path1, path2 ].join( /[\\\/]$/.test( path1 ) ? "" : "/" );
	}
}
// cscriptで起動しなおし
if( /wscript\.exe$/i.test( WScript.FullName ) ) {
	JScript.shell.Run( "cscript \"" + WScript.ScriptFullName + "\"" );
	WScript.Quit();
}

// ライブラリロード
with( {
	libs : [
		"dummy.js",
		"prototype.js"
	],
	index : 0,
	getSource : function(lib) {
		var xhr = new ActiveXObject("Microsoft.XMLHTTP");
		var stream = new ActiveXObject("ADODB.Stream");
		try {
			xhr.open( "GET", "file://" + JScript.buildPath( JScript.startupPath, lib ), false );
			xhr.send();
			
			stream.Open();
			stream.Type = 1; // open as binary mode
			stream.Write( xhr.responseBody );
			
			stream.Position = 0; // rewind
			stream.Type = 2; // change to text mode
			stream.Charset = "Shift_JIS";
			
			return stream.ReadText();
		} finally {
			if( stream ) {
				stream.Close();
			}
		}
	},
	path : ""
} ) {
	while( index < libs.length ) {
		path = libs[ index++ ];
		try {
			eval( getSource( path ) );
		} catch(e) {
			JScript.console.println( "ERROR on " + path );
			JScript.console.println( e.description );
		}
	}
}

// EnumeratorにEnumerableの機能を追加する
Enumerator.prototype._each = function(iterator) {
	this.moveFirst();
	for(; ! this.atEnd(); this.moveNext()) {
		iterator( this.item() );
	}
}
Object.extend( Enumerator.prototype, Enumerable );

// C:直下のファイル名をeachで列挙
new Enumerator( JScript.fso.GetFolder( "C:\\" ).Files ).each( function(file, index) {
	JScript.console.println( index + " - " + file.Name );
} );

JScript.console.print( "Enterキーで終了 > " );
JScript.console.readln();
arguments#calleeを使用した再起処理なんかもできるので、個人的には結構お勧め。 prototype.jsをインクルードできればよいので、当然HTAでも使用できる。

コメント

コメントをどうぞ

※メールアドレスとURLの入力は必須ではありません。 入力されたメールアドレスは記事に反映されず、ブログの管理者のみが参照できます。

※なお、送られたコメントはブログの管理者が確認するまで公開されません。

名前:
メールアドレス:
URL:
コメント:

トラックバック

このエントリのトラックバックURL: http://dara-j.asablo.jp/blog/2007/05/08/1492485/tb

※なお、送られたトラックバックはブログの管理者が確認するまで公開されません。