カスタムプロバイダーを作ってみよう その3

前回の続きです。

今回はNew-Itemコマンドレットへの対応から始めます。

New-Itemコマンドレットへの対応

New-Itemコマンドレットに対応させるには、以下のメソッドをオーバーライドします。

  • void NewItem(string path, string itemTypeName, object newItemValue)

FileSystemProviderでNew-Itemコマンドレットを使う場合を考えてみましょう。

PS c:\> New-Item 100.txt File -itemType File -value Hello

ファイル名とitemTypeに「File」か「Directory」、valueにファイルの内容を指定します。

これらのパラメータがNewItemメソッドのそれぞれの引数に対応する事は明白ですね。それでは実装してみましょう。

protected override void NewItem(string path, string itemTypeName, object newItemValue) {
     using(var fs = File.Create(path)) {
         base.WriteItemObject(new HogeFile {
             FileName = Path.GetFileName(path),
             Size = fs.Length

         }, path, false);
     }
}

渡されたパスでファイルを作って、その情報をHogeFileオブジェクトとしてパイプラインに出力しています。

デバッグ実行して、動作をテストしてみましょう。

PS Hoge:\> New-Item 400.txt


ファイル名                                        ファイルサイズ
----------                                        --------------
400.txt                                           0

itemTypeNamenewItemValueへの対応はやめておきます。その代わり新しいパラメータを定義してみます。

例えば、以下のように-BufferSizeというパラメータで数値を指定すると、

PS Hoge:\>New-Item 400.txt -BufferSize 128


ファイル名                                        ファイルサイズ
----------                                        --------------
400.txt                                           128

そのサイズでファイルが作成されるというものです。

こういった新しいパラメータを定義するためのDynamicParametersという仕組みがプロバイダーには用意されています。

例えばNew-Itemコマンドレットに新しいパラメータを定義するには、NewItemDynamicParametersというメソッドをオーバーライドします。

このメソッドのシグネチャを見てみると、

  • object NewItemDynamicParameters(string path, string itemTypeName, object newItemValue)

となっていますが、引数はNewItemメソッドと同じで返り値がobject型になっています。このメソッドはNewItemメソッドが呼び出される前に必ず呼び出されるようになっています。

では、このメソッドの返り値として何を返せばいいのでしょうか?

答えは以下のようなパラメータをプロパティとして持つプレーンなクラスを定義して、そのインスタンスを返します。

NewItemParameters.cs
[Serializable]
[DebuggerStepThrough]
public class NewItemParameters {
     [Parameter]
     public int BufferSize {
         get;
         set;
     }
}

パラメータとして定義するプロパティにはParameter属性でマークをしておきます。

では、このクラスのインスタンスを返すようにNewItemDynamicParametersメソッドを実装しておきます。

protected override object NewItemDynamicParameters(string path, string itemTypeName, object newItemValue) {
     return new NewItemParameters();
}

パラメータを定義したので、NewItemメソッドもこれに対応できるように変更しておきます。

protected override void NewItem(string path, string itemTypeName, object newItemValue) {
     var niParams = base.DynamicParameters as NewItemParameters;
     var bufferSize = niParams != null ? niParams.BufferSize : 0;

     using(var fs = File.Create(path, bufferSize)) {
         fs.Write(new byte[bufferSize], 0, bufferSize);

         base.WriteItemObject(new HogeFile {
             FileName = Path.GetFileName(path),
             Size = fs.Length

         }, path, false);
     }
}

動的パラメータはDynamicParametersプロパティで取得できます。動的パラメータが定義されていれば、BufferSizeを取得して、そのサイズのファイルを作成しています。

これでデバッグ実行すると前述のように動作するはずです。

このようにして既存のコマンドレットに新しいパラメータを追加する仕組みがプロバイダーには用意されています。おもしろいですね!!

ファイル作成ができるようになったので、その逆のファイルの削除もできるようにしておきましょう。

Remove-Itemコマンドレットへの対応

Remove-Itemコマンドレットに対応させるには、以下のメソッドをオーバーライドします。

  • void RemoveItem(string path, bool recurse)
  • bool HasChildItems(string path)

RemoveItemメソッドはそのままですが、HasChildItemsメソッドはどういう役割でしょうか?
このメソッドはアイテムを削除する時にそのアイテムに子アイテムがあるかどうかを問い合わせます。このメソッドがfalseを返せばアイテムは削除されますが、trueを返せばRemove-Itemコマンドレットで-forceパラメータが指定された場合のみ削除されます。

これはすごく単純なのでさっさと実装します。

protected override bool HasChildItems(string path) {
     return false;
}

protected override void RemoveItem(string path, bool recurse) {
     File.Delete(path);
}

これだけです。

デバッグ実行して、以下のコマンドを実行するとファイルが削除されるはずです。

PS Hoge:\> Remove-Item 400.txt 

次回はタブ補完を実装します。