2005-07-05
目次
「WebObjects Dev Report」 第12回 田畑 英和
今回はEntity間の関係を定義するRelationについて解説したいと思います。通常1つのモデルファイルの中には通常複数のEntityが存在し、それらはお互いに関係をもつことにより、複雑なデータ構造を定義することができます。
Relationにはいくつかの種類がありますが、まずはどのような種類があるかをおさえておきたいと思います。具体的には以下の3種類があります。
(1)対1
(2)対多
(3)多対多
例えば住所Entityと都道府県Entityがあったとしましょう。住所Entityには名前のとおり住所データが格納されていて、都道府県Entityには計47の都道府県データが格納されているとします。このとき住所Entityと都道府県Entityの間にRelationを設定することにより、それぞれお互いのEntityを参照できるようになります。
Relationは双方向に設定する場合と、片方向のみ設定する場合がありますがまずは双方向に設定したとしましょう。住所Entityからみるとリレーション先の都道府県Entityのレコードのどれか1つを参照することになります。この場合(1)の対1のRelationとなります。逆に都道府県Entityからみてみますと、都道府県Entityのある1レコードからは、住所テーブルの複数のレコードを参照することになります。この場合(2)の対多のRelationとなります。
住所Entityから都道府県Entityの参照だけができればよい場合には、住所Entityからの片方向のRelationのみを設定することができます。また各都道府県の住所一覧も取得したいような場合には、都道府県EntityからのRelationも設定し双方向のRelationを定義することもできます。
(3)の多対多の場合は次回解説するとして、モデルファイル上でどのようにRelationを設定するかを説明しておきます。前回の連載では、MOSAUserというEntityを作成しましたが、ユーザ情報として都道府県の情報を追加したいと思います。
このときMOSAUserに都道府県用のAttributeを追加するという方法もありますが、この方法では拡張するにしたがってEntityが肥大化することになりますし、都道府県のようなデータの場合、各レコードに重複するデータが何度も入力されることになります。
そこでMOSAUserとは別に都道府県用のEntityを追加し、Relationを設定することによって効率的なデータ構造を定義することができます。まずは都道府県用のEntityですが、以下のように定義してみました。
・プライマリーキー
prefId, PREF_ID, Number, int
・都道府県名
prefName, PREF_NAME, String, char(32)
・表示順
order, ORDER, Number, int
prefId, PREF_ID, Number, int
※左からAttribute名、カラム名、Javaデータタイプ、OpenBaseデータタイプ
都道府県名を格納するAttribute(prefName)を設定し、都道府県の一覧を表示するときのために表示順のAttribute(order)も追加してみました。英語データの場合はアルファベット順での表示ができますが、日本語データの場合はなんらかの方法でソート順を設定する必要がありますので、こういった工夫が必要になります。
これで都道府県用のEntityが追加できましたが、Relationを設定するためにはMOSAUser側にRelation用のAttributeを追加する必要があります。各Entityには各レコードをユニークに識別するためのプライマリーキー(主キー)がありますが、Relationの設定をおこなうには外部キーの追加をおこなう必要があります。
外部キーですが、対1のRelationを設定するEntity側にRelation先Entityのプライマリーキーを追加します。つまりMOSAUserにPrefectureのプライマリーキーを外部キーとして追加します。EOModeler上ではAttributeをコピー&ペーストできますので、あるEntity上のAttributeを別のEntityに簡単に追加することができます。このとき外部キーもプライマリーキーと同様にEOFによって自動的に管理されますので、外部キーのClass Propertyの設定はOffにしておきます。
では最後に実際にRelationを設定する方法ですが、一番直感的な方法を紹介しておきます。まずはEOModelerでDiagram View(Toolsメニューから実行)に切り替えて、Ctrlキーを押しながらPrefectureのprefIdを、MOSAUserのprefIdまでドラッグアンドドロップします。そうするとEntity間に線が引かれて双方向のRelationが自動的に設定されます。このときRelation名は自動的に設定されますが、必要に応じて変更することも可能です。
このようにしてEntityおよびRelationを設定していくことができます。それでは次回はRelationの詳細設定と今回取り上げなかった多対多のRelationについて解説したいと思います。なおモデルファイルですが、WebObjectsのサンプルにモデルファイルが付属していますので、そちらも参考にしてみてください。
・モデルファイルのサンプル
/Developer/Examples/JavaWebObjects/Frameworks/JavaBusinessLogic/Movies.eomodeld
小池邦人のCarbon API 徒然草(2005/07/01)
今回からは、前回紹介したドラッグ&ドロップにおけるSend処理の手順を追って行きます。まずは、startMyDrag()内で実行されているcreateDataMyDrag()ルーチンの紹介です。ドラッグ&ドロップで受け渡すデータの種類が変わることで、そのデータを登録する処理がどのようになるかを調べてみます。
Drag Managerでは、Send処理で受け渡すデータを「ドラッグアイテム」と呼びます。ドラッグアイテムのうち幾つかの種類については、アプリケーション同士が暗黙のうちに認識できるように、種類やその内容がOS標準として定義されています。例えば、Mac OS 9時代に頻繁に利用していたスクラップブックに保存されていた画像やテキストデータなどがそうです。まず最初に、クリップボードに保存されているPICT画像を、ワープロやペイントアプリケーションへ渡す場合のドラッグアイテムの作り方を取り上げてみます。以下のgetClipbord()は、クリップボードから任意のデータを取り出す簡単なルーチンです。
short getClipbord( Handle *hd,ScrapFlavorType type )
{
Size size;
ScrapRef sref;
GetCurrentScrap( &sref ); // 現在のクリップボードを得る
GetScrapFlavorSize( sref,type,&size ); // 指定タイプのデータサイズを得る
if( size ) // 指定タイプのデータが存在する
{
if( *hd=NewHandle( size ) ) // サイズと同容量のハンドラを確保
{
HLock( *hd ); // ハンドラをロック
if( ! GetScrapFlavorData( sref,type,&size,**hd ) ) // データを得る
{
HUnlock( *hd ); // ハンドラのアンロック
return( noErr );
}
DisposeHandle( *hd ); // 確保に失敗した場合にはハンドラを解放
}
}
return( 1 );
}
Send処理で好みのデータを受け渡したい場合には、AddDragItemFlavor()を利用して現在のDragReferenceへ新規ドラッグアイテムを追加します。以下のcreateDataMyDrag()ルーチンでは、クリップボードから抽出したPICT画像(PicHandle)を、AddDragItemFlavor()でドラッグアイテムとして登録しています。このドラッグアイテムをデスクトップへドロップすると、Finderによりクリッピングファイル(Mac OS Xであればピクチャクリッピング)が作成されることになります。ここで注意することは、AddDragItemFlavor()で渡したデータはシステム内部で複製されますので、渡した後のオリジナルの方は削除(不必要であれば)している点です。そうしないと、ドラッグ&ドロップするたびに不必要なメモリ領域が消費されていくことになります。
short createDataMyDrag( WindowPtr window,DragReference dref )
{
short ret=1;
PicHandle pict;
long len;
if( ! getClipbord( (Handle *)&pict,'PICT' ) )
// クリップボードからPICT画像を得る
{
HLock( (Handle)pict ); // PicHandleをロックする
len=GetHandleSize( (Handle)pict ); // PicHandleのサイズを得る
ret=AddDragItemFlavor( dref,(ItemReference)1,'PICT',(Ptr)*pict,len,0 );
// データをドラッグアイテムに追加する
KillPicture( pict ); // PicHandleを削除する
}
return( ret );
}
getClipbord()に渡すデータタイプを’PICT’から’TEXT’へ変更すれば、クリップボードに保存されている文字列データを別アプリケーションへ渡すことが可能になります。
short createDataMyDrag( WindowPtr window,DragReference dref )
{
short ret=1;
Size len;
Handle hd;
if( ! getClipbord( &hd,'TEXT' ) )
// クリップボードからテキストデータを得る
{
HLock( hd ); // ハンドラをロックする
len=GetHandleSize( hd ); // ハンドラのサイズを得る
ret=AddDragItemFlavor( dref,(ItemReference)1,'TEXT',*hd,len,0 );
// データをドラッグアイテムに追加する
DisposeHandle( hd ); // ハンドラを削除する
}
return( ret );
}
次は、ImageWellコントロールに表示されているPICT画像をドラッグアイテムに加えてみましょう。ImageWellコントロールは、PICT画像だけでなくアイコンなども表示することが可能です。表示されているデータは、ControlButtonContentInfo構造体に適切な情報を代入してGetImageWellContentInfo()に渡すことで得ることができます。contentTypeメンバーに画像の種類をセットすれば、そのデータへの参照パラメータ(リファレンス)については、次に続くUnion構造体のメンバーに代入されて返ります。
struct ControlButtonContentInfo {
ControlContentType contentType;
union {
SInt16 resID;
CIconHandle cIconHandle;
Handle iconSuite;
IconRef iconRef;
PicHandle picture;
Handle ICONHandle;
CGImageRef imageRef;
} u;
};
今回欲しいのは表示されているPICT画像のPicHandleですので、info.contentTypeにはkControlContentPictHandleをセットします。もし、ImageWellコントロールにPICT画像が表示されていれば、info.u.pictureにその画像のPicHandleが返されます。クリップボードの時と同様に、これもオリジナルの複製となりますので注意してください。
short getMyControlWellPict( ControlRef chd,PicHandle *pict )
{
ControlButtonContentInfo info; // ImageWellの内容物を指示するための構造体
*pict=info.u.picture=NULL; // PicHandleの初期化
info.contentType=kControlContentPictHandle; // PicHandleを返すように指示
if( ! GetImageWellContentInfo( chd,&info ) ) // ImageWellの内容物を得る
{
if( info.contentType==kControlContentPictHandle ) // タイプはPICTか?
*pict=info.u.picture; // そうであれば得られたPicHandleを返す
}
if( *pict ) // PicHandleが返れば正常終了
return( noErr );
return( 1 );
}
以下のcreateDataMyDrag()ルーチンでは、ImageWellコントロールに表示されているPICT画像をドラッグアイテムに加えています。どのコントロールなのかは、引数で渡されてくるControlRefで判断するのですが、それ以外の処理内容は、クリップボード経由の場合とほとんど同じとなります。
short createDataMyDrag( WindowPtr window,ControlRef chd,DragReference dref )
{
short ret=1;
PicHandle pict;
long len;
if( ! getMyControlWellPict( chd,&pict ) ) // ImageWellの表示PICT画像を得る
{
HLock( (Handle)pict ); // PicHandleをロックする
len=GetHandleSize( (Handle)pict ); // PicHandleのサイズを得る
ret=AddDragItemFlavor( dref,(ItemReference)1,'PICT',(Ptr)*pict,len,0 );
// データをドラッグアイテムに追加
KillPicture( pict ); // PicHandleを削除する
}
return( ret );
}
次回は、ファイル情報をアプリケーションからFinderにドラッグ&ドロップしたい時のドラッグアイテムの追加方法を紹介します。具体的には、ドラッグアイテムとして渡すことになる、HFSFlavor構造体とPromiseHFSFlavor構造体の「使い分け」を解説します。
つづく
SqueakではじめるSmalltalk入門 第42回 鷲見正人
本連載では、名前は知っていてもなかなか触れる機会のないSmalltalkについて、最近話題のSqueakシステムを使って紹介しています。
ストリーム(a Stream)は「絶え間のないデータの流れ」を表現するオブジェクトで、磁気ヘッド(position)を使って磁気テープ(基になるコレクション)から情報を読み(next)書き(nextPut: anObject)できることを前回示しました。見方を変えると、ストリームは最後に読み出した場所を覚えていてくれる、ちょっと変わったコレクションの一種だと考えることもできます。実際、ストリームにはコレクションの特徴的なメソッドでもある#do:も定義されていて期待通りの動作をします。
| stream |
stream _ 'abcdefg' readStream.
World findATranscript: nil.
Transcript cr.
stream do: [: each | Transcript show: each asUppercase]
" => ABCDEFG (トランスクリプトへの出力) "
さて。今回は外部ストリーム、つまりファイル情報にアクセスするためのストリームについてです。と、申しましてもこのオブジェクトについては、
(FileStream fileNamed: ‘test.txt’) edit
のような式において、editメッセージを受けるレシーバとしてすでに登場済みです。ただ、このeditによって起動されるメソッドFileStream >> #editはファイルリスト(a FileList)というオブジェクトと連携して少々ややこしいことをしていますので、まずは、オーソドックスなファイルストリームの扱いを見てみましょう。
ファイルストリームも、前回紹介した内部ストリームとほとんど同じように扱えます。ちょっと違うのは、内部ストリームを作るときは基になるコレクションを指定したのに対し、ファイルストリームではファイル名を指定すること。また、ストリームを使い終わったらデバイスを解放するためにそれを“閉じる”必要があることです。たとえば、FileStreamExample.txtという名前のファイルを作って、そこに文字列を書き込む式は次のようになります。
| stream |
stream _ FileStream fileNamed: 'FileStreamExample.txt'.
stream nextPutAll: 'abcdefg'.
stream close
「読み」と「書き」それぞれ専用のものがあった内部ストリームと違って、ファイルストリームは読み書き共用です。nextを送れば、データの読み出しもできます。
| stream |
stream _ FileStream fileNamed: 'FileStreamExample.txt'.
World findATranscript: nil.
Transcript cr.
[stream atEnd] whileFalse: [Transcript show: stream next asUppercase].
stream close
もちろん必要ならば読み出し専用のストリームにもできますが、モードのようなものを切り換えているだけで、内部ストリームのように属するクラスを使い分けているわけではありません。
| stream |
stream _ FileStream readOnlyFileNamed: 'FileStreamExample.txt'.
[stream nextPut: $a] ensure: [stream ifNotNil: [stream close]].
" => Error: Cannot write a read-only file "
(FileStream fileNamed: 'FileStreamExample.txt') class
" => MultiByteFileStream "
(FileStream readOnlyFileNamed: 'FileStreamExample.txt') class
" => MultiByteFileStream "
ここで、FileStreamにメッセージを送っているのに、出てきたストリームがMultiByteFileStreamのインスタンスであるというのはオカシイ…と気付かれた方もおられることと思います。この疑問はFileStream >> #fileNamed:をブラウズするとすぐに解決できます。
FileStream >> fileNamed: fileName
^ self concreteStream fileNamed: (self fullName: fileName)
self、つまり、この文脈ではFileStreamというクラス自身にconcreteStreamというメッセージを送って、その返値に改めて「fileNamed: …」というメッセージを送りなおしています。concreteStreamの送信に対する返値がおそらくMultiByteFileStreamで、FileStream fileNamed: … は、実際にはMultiByteFileStream fileNamed: …と等価なのだろう…ということは容易に想像できると思います。このことを確認するには、FileStream >>#concreteStreamの定義を見ます。FileStream >> #fileNamed:を閲覧中のブラウザで、implementorsボタンのポップアップから「concreteStream」を選ぶなどして、新しいブラウザを開いてみてください。どうです? 予想どおりですね。
FileStream >> concreteStream
^ MultiByteFileStream
さて。日本語の書き出しもしてみましょう。
| stream |
stream _ FileStream fileNamed: 'NihongoExample.txt'.
stream nextPutAll: '日本語の文字列'.
stream close
内容を確認してみます。
| stream contents |
stream _ FileStream fileNamed: 'NihongoExample.txt'.
contents _ stream contents.
stream close.
^ contents " => '日本語の文字列' "
問題ないですね。だだ、SqueakNihongo7はデフォルトでは文字コードにUTF-8を使って日本語文字列を出力するので、シフトJISを期待する他のソフトでは文字化けしてしまいます。
a MultiByteFileStreamでは、読み書きにテキストコンバータ(a TextConverter)というオブジェクトが介在し、あらかじめ決められた文字コードで読み書きが行なわれています。
(FileStream fileNamed: 'test.txt') converter
" => an UTF8TextConverter "
Mac OSではシフトJISが多く使われるので、an UTF8TextConverterの代わりにシフトJISを理解するテキストコンバータに代えてやれば、UTF-8からの変換の手間を省くことができて便利です。
| stream |
stream _ FileStream fileNamed: 'ShiftJisExample.txt'.
stream converter: ShiftJISTextConverter new.
stream nextPutAll: 'シフトJISの日本語文字列'.
stream close
TextConverterのサブクラスを見れば、使用可能なコンバータを一覧できます。
TextConverter subclasses
" => #(CompoundTextConverter EUCTextConverter MacRomanTextConverter
ShiftJISTextConverter UTF8TextConverter Latin1TextConverter
CP1253TextConverter ISO88597TextConverter UTF16TextConverter
CP1250TextConverter ISO88592TextConverter) "
バックナンバー:
http://squab.no-ip.com:8080/mosaren/
ニュース・解説
今週の解説担当:木下 誠
———————————————————————-
Cocoaへのスイッチ記事
———————————————————————-
ADCで、アプリケーションのCocoaへのスイッチ紹介記事、「Chronos Switches to Cocoa」が公開されていました。StickyBrainという、ノートパッドタイプのアプリケーションを、Cocoaベースで書き直した、という記事です。
この記事では、デベロッパにCocoaへの移行のメリットを説いています。今後、Universal Binaryの導入もあることですし、CocoaやXcode環境への移行が強調されることでしょう。
Chronos Switchs to Cococa
http://developer.apple.com/business/macmarket/chronos.html
———————————————————————-
プリンタのインクの残量を調べる
———————————————————————-
Technical Noteで、プリンタのインクの残量を調べる方法が公開されています。
Mac OS X 10.4からは、IPプリンタに対して、コマンドラインツールsnmpInkを使うことで、インクの残量を調べることができるようになりました。snmpInkは調査結果をXMLで返すのですが、この記事でそのXMLのキーの解説をしています。
TN2144: Detecting low printing ink levels
http://developer.apple.com/technotes/tn2005/tn2144.html
———————————————————————-
NSSoundとCoreAudioを同時に使う
———————————————————————-
Technical Q&Aで、10.3.xで、NSSoundとCoreAudioを同時に使う際のバグと、その回避方法が説明されています。
10.3.xでは、NSSoundの実装とCoreAudioの実装が、ハードウェアの抽象レイヤー(HAL)で干渉してしまうバグがあるそうです。これを避けるには、NSSoundのインスタンスが作られる前に、CoreAudioで適切な処理をする必要があるそうです。
QA1394: Using NSSound with CoreAudio on Mac OS 10.3.x
http://developer.apple.com/qa/qa2005/qa1394.html
———————————————————————-
サンプルが4つ公開
———————————————————————-
TextTrack:
AppleScriptとPerlスクリプトを使って、Final Cut ProのXMLファイルを操作するサンプルです。
http://developer.apple.com/samplecode/TextTrack/TextTrack.html
AlbumToSlideshow:
CoreFoundationとNSXMLを使って、Final Cut ProのXMLファイルを操作します。iPhotoのアルバムデータをFinal Cut Proにインポートします。
http://developer.apple.com/samplecode/AlbumToSlideshow/AlbumToSlideshow.html
QTAudioExtractionPanel
QTKitのサンプルです。WWDCで使われたサンプルQTKitPlayerを拡張して、オーディオトラックにアクセスできるようになっています。
http://developer.apple.com/samplecode/QTAudioExtractionPanel/QTAudioExtractionPanel.html
ScriptingDefinitions
AppleScriptの定義ファイルである、.sdefファイルのサンプルです。
http://developer.apple.com/samplecode/ScriptingDefinitions/ScriptingDefinitions.html
MOSAからのお知らせと編集後記は割愛します