MOSA Multi-OS Software Artists

MOSAはソフトウェア開発者を支援します

  • iPhone/iPod touch アプリ紹介
  • MOSA掲示板
  • 活動履歴
  • About MOSA(English)

MOSA Developer News[MOSADeN=モサ伝]第202号

2006-04-25
 

目次

  • 「WebObjects Dev Report」    最終回  田畑 英和
  • 小池邦人の「Carbon API 徒然草」
  • SqueakではじめるSmalltalk入門  第60回  鷲見 正人
  • ニュース・解説               木下 誠

「WebObjects Dev Report」  最終回  田畑 英和

 さて、今回で「WebObjects Dev Report」は最終回となります。今回はアプリケーションをデータの登録用と閲覧用に分割し、2つのアプリケーションの共通分をフレームワーク化した結果を解説します。
 まずは、これまで開発したアプリケーションを実際にInternet上でアクセスできるようにしてありますので、こちらのURLをご紹介します。

・単体アプリケーション版
http://210.175.246.197/cgi-bin/WebObjects/MOSAJobMatchTest.woa
・分割フレームワーク版
http://210.175.246.197/MOSAJobMatchTest.html

 どちらのバージョンも同じコードに基づいていますが、それぞれプロジェクトの構造が異なります。また分割フレームワーク版では2つのアプリケーションを使用するためトップページは通常のHTMLファイルとして作成し、それぞれのアプリケーションへのリンクを作成してあります。

◇プロジェクトの構造
 それではプロジェクトの構造を解説しましょう。分割フレームワーク版では次のように3つのプロジェクトからシステム全体が構成されています。

・データ登録用アプリケーション
MOSABusinessRegister
・データ閲覧用アプリケーション
MOSABusinessFinder
・フレームワーク
MOSABusiness

◇フレームワーク(MOSABusiness)
 フレームワーク用プロジェクトでは2つのアプリケーションで共通する各種ファイルを管理しています。フレームワークでは次のようなファイルを管理しています。

・アプリケーション、セッションクラス
 Application.java、Session.javaで共通する処理をまとめたクラスをフレームワークにMOSABusinessApplication.javaとMOSABusinessSession.javaとして実装しました(クラス名は前回紹介したものから変更しています)。
・カスタムEOクラス
 ジェネレーションギャップパターンを利用して作成したカスタムEOのクラスファイルです。分割前のプロジェクトで使用していたファイルを、そのままフレームワークに移動しました。
・Webコンポーネント
 2つのアプリケーションで共通して利用するReusable Componentです。ページのレイアウトを実装したPageTemplateと各ページのヘッダーおよびフッター(Header、Footer)をフレームワークに移動しました。またコンポーネント用の親クラスも移動してあります。
・モデルファイル
 両方のアプリケーションから共通のデータベースへアクセスするため、フレームワークにモデルを移動しました。
・Webサーバリソース
 画像ファイルおよびCSSファイルをフレームワークに移動しました。画像ファイルは数が多いため、すべてフレームワークに移動してあります。

◇データ登録用アプリケーション(MOSABusinessRegister)
◇データ閲覧用アプリケーション(MOSABusinessFinder)
 フレームワークに共通部分を移動したために、アプリケーション本体には、アプリケーションに依存したファイルのみを登録しました。

・アプリケーション、セッションクラス
 アプリケーションのApplication.javaとSession.javaは、フレームワークのMOSABusinessApplication.javaとMOSABusinessSession.javaを継承して実装しました。
・Webコンポーネント
 アプリケーションではフレームワークに実装したReusable Componentを使用しています。また画像ファイルもフレームワークのものを使用していますが、WOImageのframeworkプロパティを”MOSABusiness”と指定して、フレームワークを明示的に設定しています。
・モデルファイル、Webサーバリソース
 アプリケーション側にモデルファイルやWebサーバリソースは存在しません。
・フレームワーク
 MOSABusiness.frameworkを参照しています。

 このように共通部分をフレームワークに集約したために、各プロジェクトでのファイル数は分割前の1/3程度に減らすことができました。アプリケーションは2つに分割しましたが、フレームワーク部分を改良すれば、両方のアプリケーションに反映されることになります。
 なお今回解説しましたプロジェクトは下記のURLでダウンロードすることができます。

・プロジェクトダウンロード
http://210.175.246.197/WebObjects/mosa/MOSABusiness_Framework.zip

◇課題
 これにて連載は終わりになりますが、最後に今後アプリケーションを改良する場合の課題を上げておきましょう。
 まず、アプリケーション間でのデータの同期に関する問題があります。アプリケーションを2つに分割したため、それぞれのアプリケーションでデータをキャッシュすることになります。よって登録用アプリケーションで登録したデータが、閲覧用アプリケーションですぐに確認できないことも考えられます。
 また機能的な強化も考えられます。現在のアプリケーションではユーザ登録の機能やデータを登録するさいの質問項目を管理する機能がありません。

さて、約1年間にわたってWebObjectsでアプリケーションを開発する過程をレポートしてきましたが、いかがでしたでしょうか。皆さんの開発に少しでもお役にたてたなら幸いです。次回からはテーマを変えてMac OS X Serverに関する連載を新たにスタートさせます。ご期待ください。

小池邦人のCarbon API 徒然草(2006/04/21)

〜 アプリケーションのUniversal Binary化(その7) 〜

今回は、前回に引き続いてもう少しリソースデータについての例題を調べてみます。また、効率の良いエンディアンの反転方法についても考察してみます。

以下のaddIconResources()は、FSpOpenResFile()などでオープンされたカレントのリソースファイルに、48×48ピクセルのフルカラー画像アイコンリソースを付加するルーチンです。何故だか、現在のCarbon APIやQuickTime APIにはファイル(JPEGやTIFF)に画像アイコンを手軽に付加するAPIが存在しないために、こうした自作ルーチンが必要となります。アイコン用の画像は、引数で渡されたCGrafPtr(GWorldオフスクリーン)に先んじて描画されていることが条件です。

ここでは、リソースタイプが’icns’でリソースIDが-16455のリソースをAddResource()を用いて付加しています。’icns’リソースは、マスク画像、白黒画像、フルカラー画像など複数のアイコン情報を含むデータ構造を取っています。そのため、データの先頭に各データのタイプ(’icns’と’ich#’と’ih32′)とそのサイズ(容量)を保存します。これらの情報(数値)は、すべてビッグエンディアンフォーマットでリソースフォークへ保存されていますので、Intel CPU版のアプリケーションでは保存前にエンディアンを反転する必要があります。

short addIconResources( CGrafPtr off )
{
    long            size1=9216L+576L+24L;  // リソース全体のサイズ(容量)
    long            size2=576L+8L;         // "ich#"データのサイズ
    long            size3=9216L+8L;        // 'ih32'データのサイズ
    OSType          type1,type2,type3;
    Ptr             addr,optr,dptr;
    Rect            srt,drt,frt;
    GWorldPtr       off1,gptr;
    short           row,i;
    GDHandle        ghd;
    PixMapHandle    phd;
    Handle          hd;

    type1='icns';
    type2='ich#';
    type3='ih32';
    hd=NewHandleClear( size1 );  //  アイコン保存用メモリ領域をHandleとして確保
    if( ! hd )
        return( 1 );
    HLock( hd );
    dptr=*hd;

    #if __LITTLE_ENDIAN__      // Intel CPU版のみ#endifまでコンパイルされる

    type1=Endian32_Swap( type1 );  // 利用するOSTypeを反転する
    type2=Endian32_Swap( type2 );
    type3=Endian32_Swap( type3 );
    size1=Endian32_Swap( size1 );  // 利用するデータサイズ(long値)を反転する
    size2=Endian32_Swap( size2 );
    size3=Endian32_Swap( size3 );

    #endif

    BlockMoveData( &type1,dptr,4L );  // OSType 'icns'
    dptr+=4;
    BlockMoveData( &size1,dptr,4L );  // リソースデータ全体のサイズ(容量)
    dptr+=4;
    BlockMoveData( &type2,dptr,4L );  // OSType 'ich#'
    dptr+=4;
    BlockMoveData( &size2,dptr,4L );  // 'ich#'データのサイズ
    dptr+=4;

    GetGWorld( &gptr,&ghd );
    SetRect( &srt,0,0,48,48 );      // アイコンの矩形サイズは48x48ピクセル
    GetPortBounds( off,&frt );      // ソース画像の矩形枠
    OffsetRect( &frt,-frt.left,-frt.top );
    fitRect( 1,&srt,&frt,&drt );   // ソース画像の矩形枠を合わせる自作ルーチン
    if( ! NewGWorld( &off1,1,&srt,0L,0L,0 ) ) // 白黒画像とマスク用1bit GWorld
    {
        phd=GetGWorldPixMap( off1 );
        LockPixels( phd );
        addr=GetPixBaseAddr( phd ); // GWorldメモリ領域の先頭アドレスを得る
        row=GetPixRowBytes( phd );  // GWorldメモリ領域のRow Bytesを得る
        SetGWorld( off1,ghd );
        EraseRect( &srt );
        CopyBits(GetPortBitMapForCopyBits(off),GetPortBitMapForCopyBits(off1),
                                                            &frt,&drt,0,NULL );
        FrameRect( &drt );
        optr=addr;
        for( i=0;i<48;i++ )  // 白黒アイコン画像データを転送
        {
            BlockMoveData( optr,dptr,6L );
            optr+=row;
            dptr+=6;
        }
        PaintRect( &drt );
        optr=addr;
        for( i=0;i<48;i++ )  // 白黒アイコンマスクデータを転送
        {
            BlockMoveData( optr,dptr,6L );
            optr+=row;
            dptr+=6;
        }
        DisposeGWorld( off1 ); // GWorldを削除

        BlockMoveData( &type3,dptr,4L );  // OSType 'ih32'
        dptr+=4;
        BlockMoveData( &size3,dptr,4L );  // 'ih32'データのサイズ
        dptr+=4;

        SetGWorld( gptr,ghd );
        if( ! NewGWorld( &off1,32,&srt,NULL,NULL,0 ) )
        {                           // フルカラーアイコン画像用321bit GWorld
                                    // 画像フォーマットはビッグエンディアン
            phd=GetGWorldPixMap( off1 );
            LockPixels( phd );
            addr=GetPixBaseAddr( phd ); // GWorldメモリ領域の先頭アドレスを得る
            row=GetPixRowBytes( phd );  // GWorldメモリ領域のRow Bytesを得る
            SetGWorld( off1,ghd );
            EraseRect( &srt );
            CopyBits(GetPortBitMapForCopyBits(off),
                              GetPortBitMapForCopyBits(off1),&frt,&drt,0,NULL );
            FrameRect( &drt );
            optr=addr;
            for( i=0;i<48;i++ )  // フルカラーアイコン画像データを転送
            {
                BlockMoveData( optr,dptr,192L );
                optr+=row;
                dptr+=192;
            }
            DisposeGWorld( off1 ); // GWorldを削除
            SetGWorld( gptr,ghd );
            AddResource( hd,'icns',-16455,"\p" ); // 'icns'リソースを付加
            return( 0 );
        }
    }
    DisposeHandle( hd ); // メモリ領域を開放
    return( 1 );
}


こうして見てくると、エンディアンの反転は、データをファイルに保存する直前かファイルから読み込んだ直後に行うことが多いようです。しかし、こうした処理をファイルI/Oが発生する度にいちいち追加していては面倒ですし、必要な処理を見落としてしまう危険性もあります。そこで、以下の様にFSWriteFork()やFSReadFork()と自作フリッパーを組み合わせた上位ファイルI/Oルーチンを用意しておくと便利です。ファイルへ読み書きするフォーマット(構造体)の種類をOSTypeで指示してやることで、エンディアンの反転処理を一元管理することが可能です。ちょうどApple社が用意したカスタムリソースデータのフリッパーと同じ仕組みです。

OSErr writeFile( OSType type,short fref,ByteCount size,Ptr buf )
{
    OSErr        err;
    ByteCount    ss;

    flipFile( type,size,buf );    //  構造体のエンディアンをひっくり返す
    err=FSWriteFork( fref,fsAtMark,0,size,buf,&ss ); // ファイルへの書き込み
    flipFile( type,size,buf );    //  構造体のエンディアンを元に戻す
    return( err );
}

OSErr readFile( OSType type,short fref,ByteCount size,Ptr buf )
{
    OSErr        err;
    ByteCount    ss;

    err=FSReadFork( fref,fsAtMark,0,size,buf,&ss ); // ファイルからの読み込み
    if( err==noErr )
        flipFile( type,size,buf );   //  構造体のエンディアンをひっくり返す
    return( err );
}

void flipFile( OSType type,long size,Ptr buf )
{
    #if __LITTLE_ENDIAN__  // Intel CPU版のみ#endifまでコンパイルされる
    switch( type )
    {
        case 'pref':

            flipPref( (PrefPtr)buf );  // Pref構造体反転の自作ルーチン
            break;

        case 'list':

            flipLst( (LstPtr)buf );  // List構造体反転の自作ルーチン
            break;

        case 'head':

            flipHeader( (HeaderPtr)buf ); // Header構造体反転の自作ルーチン
            break;

        case 'indx':

            flipIndex( (IndexPtr)buf ); // Index構造体反転の自作ルーチン
            break;
    }
    #endif
}


こうした処理が必要なのは、ファイル保存されているデータフォーマットがエンディアンの違いに影響を受ける場合に限られます。つまり、shortやlongの整数値、floatやdoubleの浮動小数点値が、そのままファイルへ保存されている場合です。保存フォーマットがリトルエンディアンであれば、PowerPC用アプリケーションのファイルI/Oで反転する必要がありますし、逆にビッグエンディアンであればIntel CPU用アプリケーションで反転する必要があります。ちなみに、保存形式がテキストデータやXMLデータなどエンディアンの違いに影響されないフォーマットであれば、その必要はありません。

次回は、画像(ビットフィールド)を処理するアプリケーションにおけるエンディアンの違いの影響について調べてみます。例題として、OpenGLのテクスチャ画像を作成する場合などについて解説する予定です。

つづく

SqueakではじめるSmalltalk入門   第60回  鷲見 正人

 本連載では、名前は知っていてもなかなか触れる機会のないSmalltalkについて、最近話題のSqueakシステムを使って紹介しています。前回は少々寄り道をして、ジョブズやアトキンソンたちが1979年のPARC見学の際にかいま見たであろう、ALTO/Smalltalkシステム、つまり暫定ダイナブック環境の持つ動的性を、メニューのポップアップの挙動を変更する作業を通じて、プチ体験していただきました。今回は、再びポップアップメニューの“解剖”に戻りたいと思います。

念のため、観察用のメニューをポップアップさせるスクリプトを再掲します。

| menu |
menu := MenuMorph new.
menu defaultTarget: 1.
menu add: 'default (one)' action: #inspect.
menu addLine.
menu add: 'two'   target: 2 selector: #inspect.
menu add: 'three' target: 3 selector: #inspect.
menu add: 'four'  target: 4 selector: #inspect.
menu addLine.
menu
   add: 'open workspace'
   target: Workspace
   selector: #openLabel:
   argument: 'My New Workspace'.
menu invokeModal

 ワークスペースなど適当な場所にこのスクリプトを貼り付けて、全体を選択してからdo it(cmd + D)するとメニューを呼び出せます。前々回はこのメニューをモーフとして選択しインスペクト(検査)しました。今回は、メニューを構成するメニュー項目の中味をインスペクトしてみることにします。

スクリプトの記述からも伺えるように、そして、#add:action:、#add:target:selector:などのメソッドをシステムブラウザを使って“上流”に遡ることで#addMorphBack:メソッドの起動を確認できることからも、メニュー項目モーフ(a MenuItemMorph)がメニューモーフ(a MenuMorph)のサブモーフとして登録されていることは、すでにご説明したとおりです。

あるモーフに登録されたサブモーフをインスペクトするためには、コマンドクリックを繰り返す方法をすでに紹介済みですが、今回は、インスペクタの新たな操作法のレクチャーを兼ねて、サブモーフ群がオーナーのsubmorphsというインスタンス変数に束縛されていることを利用することにします。

まず、オーナーモーフであるメニューをコマンドクリックでモーフとして選択。現れたハローのうち右手上から二番目のグレイのハロー(デバッグハロー)をshiftキーを押しながらクリックすると、そのメニューのインスペクタを開くことができます。この操作はすでにお馴染みですね。

では次に、開いたインスペクタで、左手のリストペインのsubmorphsをダブルクリックしてみてください。するとsubmorphsに束縛されている配列(an Array)のインスペクタが現れます。Smalltalkシステムでは、このようにインスペクタを介して、オブジェクトのネットワークの海の深みに向かって次々に“ダイブ”してゆくことが可能となっています。

[fig.A]submorphsのダブルクリックで現れた新しいインスペクタ

 さて。我々が興味を持つサブモーフとしてのメニュー項目は、今、インスペクト中の配列の要素として束縛されています。メニュー項目は五つなのに、この配列には七つの要素があることを不思議に思ったかたは、メニュー項目だけではなく、区切り線もモーフ(a MenuLineMorph)であり、サブモーフとして登録されていることを思い出してください。

区切り線の二番目と六番目以外はメニュー項目なので、どれを選んでもかまわないのですが、ここでは最後の要素、つまり、選択するとワークスペースを開くメニュー項目を観察することにしましょう。

インデックス「7」の要素をダブルクリックして改めてインスペクタを開いてみてください。このとき、すでに項目が選択されているとダブルクリックしてもうまくインスペクタが開かないことがあります。いったんクリックして選択を解除してから改めてダブルクリックするか、ダブルクリック操作ではなくinspect it(cmd + I)をタイプしてください。インスペクタが開いたら、内容を見やすくするために、右下付近にポインタを合わせ、すこしウインドウサイズを大きめにしましょう。

[fig.B]配列のインスペクタから呼び出したメニュー項目のインスペクタ

 左手のリストペインに、メニュー項目モーフに用意されたインスタンス変数が列挙されています。最後のほうにあるtarget、selector、argumentsに注目してください。これらには、前述のスクリプトでこのメニュー項目を追加するときに指定した、ターゲットであるWorkspace、セレクタである#openLabel:、メッセージ送信に際して必要になるパラメータ(引数)として、'My New Workspace'を要素に持つ配列がそれぞれ束縛されているはずです。

メニュー項目は自らが選択されると、指定されたターゲットに、セレクタとパラメータを組み合わせたメッセージ(このメニュー項目では、「openLabel:'My New Workspace'」というメッセージ)を送信することで、その役割を果たします。試しに、このインスペクタで別のターゲットにすげ替えて、メニュー項目を選んだときにどんな挙動を示すかを観察してみましょう。

インスペクタの左手リストペインからtargetを選択します。右手のペインには今は束縛されているWorkspaceが表示されます。これを「Transcript」と書き換えてaccept(cmd + S)します。すると、ペインの内容は「a TranscriptStream ''」と変わります。これで、メッセージの送り先をクラスWorkspaceからトランスクリプト(TranscriptStreamのインスタンス。グローバル変数のTranscriptに束縛されている…)への、すげ替えが完了です。トランスクリプトも「openLabel: aLabelString」というメッセージを受けることができるので、問題はありません。

では、おおもとのメニューに戻って「open workspace」を選択してみてください。もし、何らかの理由でメニューがすでに消えてしまっていたら、最初に開いたメニューのインスペクタで「self openInWorld」をdo it(cmd + D)することで再び呼び出すことができます。

さて。メニュー項目を選択後に開いたウインドウは、一見、ワークスペースのように見えますが、さにあらず。これはれっきとしたトランスクリプト(標準出力もどき)ウインドウです。その証拠に、どこか適当な場所、たとえば、インスペクタの下部にあるペインで、

Transcript show: 'Hello World'

とタイプしてからdo it(cmd + D)すると、「Hello World」ときちんと表示されるはずです。

バックナンバー:

ニュース・解説

 今週の解説担当:木下 誠

----------------------------------------------------------------------
WWDCにてLeopardをプレビューすることが告知
----------------------------------------------------------------------

8月の7日から11日にかけて行われる、今年のWWDC (Worldwide Developers Conference)のページが更新されました。正式に、LeopardことMac OS X 10.5のプレビューを行う事を告知しています。

また、セッションの紹介も公開されています。セッションは5つのトラック(Application Technologies、Development Tools、Graphics and Media、Information Technologies、OS Foundations)に分かれており、合計で120以上のセッションが予定されています。

現在のところ、それぞれのトラックの紹介には、Leopardに関する新しい情報はないようです。もともと、ほとんどのセッションがNDAになるため、Webページで公開される情報は少ないのですが。

WWDC 2006
http://developer.apple.com/wwdc/

----------------------------------------------------------------------
J2SE 5.0 Release 4がリリース
----------------------------------------------------------------------

J2SE (Java 2 Standard Edition) 5.0がバージョンアップした、J2SE 5.0 Release 4がリリースされています。

このリリースから、JavaはデフォルトでJ2SE 5.0、つまりjava 1.5、を使用するようになりました。ただし、以前のバージョンであるjava 1.4はそのまま残されているので、切り替えて利用することが可能です。

同時に、J2SE 5.0 Release 4のリリースノートも公開されています。修正されたバグ、および認識されているバグが記述されていますので、Javaを利用している開発者の方は、要チェックです。EOModeler、JBuilder、OpenBase JDBCとの不具合などがあります。

About Java 2 Standard Edition (J2SE) 5.0 Release 4
http://docs.info.apple.com/article.html?artnum=302983

J2SE 5.0 Release 4 Release Notes
http://developer.apple.com/releasenotes/Java/Java50Release4RN/index.html

----------------------------------------------------------------------
Javaに関するTNとQA
----------------------------------------------------------------------

J2SE 5.0のバージョンアップに加えて、Javaに関するTechnical NoteとQ&Aが公開されています。

「TN2147: JNI Development on Mac OS X」は、Mac OS XでのJNI (Java Native Interface)の開発方法について述べられています。単なるJNIの使い方だけではなく、CocoaやCore Foundationの間での、オブジェクトの変換、グラフィック環境へのアクセス、スレッドセーフの実現、Java仮想マシンの呼び出しなどが議論されています。

「QA1474: UnsupportedClassVersionError With J2SE 5.0 Release 4」では、J2SE 5.0 Release 4をインストールしたときに、コンパイルで発生する問題について解説しています。XcodeのターゲットVMと、Javaコンパイラが作成するバイドコードのバージョンが異なる場合、問題が発生するようです。

TN2147: JNI Development on Mac OS X
http://developer.apple.com/technotes/tn2005/tn2147.html

QA1474: UnsupportedClassVersionError With J2SE 5.0 Release 4
http://developer.apple.com/qa/qa2006/qa1474.html

----------------------------------------------------------------------
ATSによるフォント一覧の取得
----------------------------------------------------------------------

ATS (Apple Type Services)を使って、インストールされているフォントの一覧を取得する方法を解説した、「QA1471: Enumerating fonts with ATS」が公開されています。

ATSFontIteratorNextか、ATSFontFamilyIteratorNextを使って、一覧を取得します。ただし、取得には長い時間がかかる事、取得中にユーザがフォントの構成を変更するとエラーが発生する事、が注意点として挙げられています。

QA1471: Enumerating fonts with ATS
http://developer.apple.com/qa/qa2006/qa1471.html

 

◇MOSAからのお知らせと編集後記は割愛します◇

 

MOSA Developer News   略称[MOSADeN=モサ伝]
      記事投稿受付 http://www.mosa.gr.jp/topics/mdn-toukou.html
Apple、Mac OSは米国アップルコンピュータ社の登録商標です。またそのほかの各製品名等はそれぞれ各社の商標ならびに登録商標です。
このメールの再配信、および掲載された記事の無断転載を禁じます。
特定非営利活動法人MOSA  http://www.mosa.gr.jp/
Copyright (C)2006 MOSA. All rights reserved.