● Carbon視点でCocoa探求(2008/10/29)

  このニュースは、MOSAの会員にのみ配布されているデベロッパー向けの
  デジタルマガジンMOSADeNのに掲載された記事です。ほぼ一ヶ月遅れで
  ここに掲載されて行きます

 〜 ディレクトリなのか?画像ファイルなのか? 〜


今回は、 addImagesThread:のループ内から呼ばれている addImagesPath:メソッドのソースコードを解説することからImageBrowserへの画像登録処理の話を続けたいと思います。以下が addImagesPath:メソッドです。

- (void)addImagesPath:(NSString*)path
{
  NSArray  *content;
  NSInteger  i,ct;
  BOOL    dir;

  [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&dir];
  if( dir ) // パス名はディレクトリを指す
  {
    if( [[NSWorkspace sharedWorkspace]
                    isFilePackageAtPath:path]==NO )
    { // パス名がパッケージではない場合はその内容を得る
      content=[[NSFileManager defaultManager]
                        directoryContentsAtPath:path];
      ct=[content count]; // 配列個数を得る
      for( i=0;i<ct;i++ )  // 再帰(リカーシブル)呼び出しを行う
        [self addImagesPath:[path stringByAppendingPathComponent:
                          [content objectAtIndex:i]]];
    }
  }
  else
    [self addImageOne:path]; // パス名は単独ファイルを指す
}

このメソッドの役割は、ユーザがファイル選択ダイアログで選んだパス名(複数選ぶことも可能)が、 ディレクトリ(フォルダ)なのか?単独の画像ファイルなのか?を判断して処理を切り分けることです。CocoaのFundationフレームワークでファイル処理を実行する場合には、NSFileManagerクラスを用います。実際には、NSFileManagerオブジェクトをその都度確保するのではなく、defaultManagerクラスメソッドを呼び出して、その結果得られたオブジェクトに対してファイル処理用のメッセージを送ります。

先頭のfileExistsAtPath:isDirectory:メソッドにより、渡されたパス名(NSString)がディレクトリを指すのか?単独ファイルを指すのか?を判断しています。パス名が単独ファイルであれば、処理をそのままaddImageOne:メソッドへ引き継ぎます。そうではなくパス名がディレクトリを指している場合には、isFilePackageAtPath:メソッドにより、そのディレクトリがパッケージ構造(Mac OS Xのアプリケーションなど)なのかどうかを確認しています。この判断を怠ると、パッケージ内の画像ファイルもすべて抽出して登録してしまうことになりますので注意してください。

純粋なディレクトリの場合にだけ、directoryContentsAtPath:メソッドを利用して、その内容をNSArray配列に格納します。この時に得られる内容とはディレクトリか単独ファイルのパス名ですので、配列個数分を再びaddImagesPath:メソッドに渡して再帰処理(リカーシブル処理)を行います。これにより、選択されたディレクトリより下の階層に含まれてる画像ファイルがすべてサーチされ、そのままImageBrowserへと登録されることになります。例えばルートディレクトリを選べば、その下階層の全画像ファイルがサーチされることになりますので、登録できる画像数には何らかの制限(最大数)を付けておいた方が良いかもしれません。

最終的には、すべての単独ファイルのパス名が抽出されて、以下のaddImageOne:メソッドへと処理が渡ります。

- (BOOL)addImageOne:(NSString*)path
{
  NSString      *path1,*comf;
  BOOL        ret=NO;
  ImageFile      *imagef;
  NSMutableArray  *arry;
  NSFileHandle    *fhd;

  if( path1=resolveAlias( (CFStringRef)path ) ) // エイリアスファイルかどうか?
    path=[path1 autorelease];        // エイリアスからのパスを再代入

  if( comf=[MyDocument isImageFile:path] )  // 画像ファイルかどうか?
  {
    if( imagef=[[ImageFile alloc] init] ) // 画像ファイルの場合ImageFileを作成
    {
      imagef.path=path;      // パス名を登録
      imagef.type=comf;      // ファイル情報を登録
      [temp addObject:imagef];   // ImageFileをtemp配列に追加
      [imagef release];
      ret=YES;
    }
  }
  else // それ以外のファイルでは「しんぶんし 3」のドキュメントかどうか判断
  {
    if( fhd=[NSFileHandle fileHandleForReadingAtPath:path] ) // そうである!
    {
      if( arry=[NSUnarchiver unarchiveObjectWithData:
                           [fhd availableData]] )
      {  // ドキュメント読み込む
        [temp addObjectsFromArray:arry]; // 内容をtemp配列に追加
        ret=YES;
      }
    }
  }
  return ret;
}

addImageOne:メソッドが最初に実行することは、resolveAlias()ルーチンで対象がエイリアスファイルかどうかを調べ、もしそうであれば、それを解析してエイリアスが指している真のパス名を得ることです。残念ながら(何故か今でも)エイリアス処理に関してはCocoaのNSFileManagerクラスでは対応できません。エイリアス関連の処理を行う場合には、懐かしのCarbon Alias Managerを利用します(笑)。ちなみに、今回はフォルダのエイリアスについては無視しています(永久ループに陥る可能性があるため)。

続いて、そのファイルがImageBrowserに登録できる画像ファイルかどうかを判断します。それに用いているのがisImageFile:メソッドです。もし画像ファイルだと判断されれば、本アプリのモデルオブジェクトであるImageFileを作成して、そこにパス名やファイル情報を登録し、一時的にデータを確保しているtemp配列(NSMutableArray)へ追加します。そして最後に、対象画像ファイルではないと判断されたファイルの中から「しんぶんし3」のドキュメントファイルを見つけ出し、そこに登録されている画像ファイル情報(ImageFileオブジェクト)を抽出してすべてをtemp配列へ追加します。

次回は、 エイリアスファイルに関わる処理をするresolveAlias()ルーチンや、パス名で示されたファイルがImageBrowserで扱える画像かどうかを判断しているisImageFile:メソッドを中心に解説したいと思います。それにしてもゴールは遠いですね(笑)

copyright 2008 Ottimo, Inc. All rights reserved
無断転載・引用禁止