2008年12月30日火曜日

一人でほぼ丸一日過ごした

妻と子供が先に私の実家に行ったため、今日はほぼ丸一日独りで過ごしました。

・子供はおじいちゃんとおばあちゃんが大好き(怒らないから。何でも買ってくれるから。遊んでくれるから。そりゃあ好きになるよな。)なので、できるだけ長くいたい。

・妻と私は独りの時間が欲しい。

という理由で、前回から帰省シフト制を敷いてみたのです。行きが妻で、帰りは私が担当。

子供らを見送った後、明日持ってゆくお土産を買いに池袋へ。まずはお散歩。東急ハンズまで歩いて上から下までざっと眺めました。う~ん。あまり物欲が湧かない。疲れてるのかな。調理用具コーナーで無難に丸型の焼き網(127円)を購入。野菜を蒸すのに使うつもりです。こういうのが欲しかった。100円ショップにありそうでなかったから。土産はやっぱりとらやの羊羹。なぜか分からないけどとらやの羊羹って団塊世代には格別(ステータス?)らしい。他の有名なお菓子を買ってもぜんぜん喜ばない。それから家に帰ってレンタルDVD「ケロロ軍曹」を返却。用事は以上。

昼食はちょっと奢ってスモークサーモン。とは言っても370円くらいだけど。それからバゲットを買って残りもののKiriクリームチーズと食べるつもり。さて。家に到着。独りでモソモソとスモークサーモン食べるか。バゲットを切ってチーズを塗り、スモークサーモンを乗せて美味そう。ぱくっと。あれ?スモークサーモンとクリームチーズって合わないぞ?スモークサーモンの風味がクリームチーズを圧倒して、クリームチーズの油がサーモンに余計なこってり感を与えてる。パンの風味も隠れちゃったし。ばらばらに食べた方が美味いや。淡々と昼食を済ませてまた独り。

楽しみにしていたんですけど、なんとなく手持ち無沙汰というか寂しいですね。第一やることがない。最近料理を趣味にしていますが独りで食べきれる量なんてたかが知れてますし、食べてくれる人がいなければ作る甲斐もありません。残り物のスモークサーモンをたっぷりの玉ねぎとマリネにして以上。

家で軽くピアノを弾いたり、WEB眺めたりして時間を潰して、漠然と夕方を迎えました。

それからBGM(Back Ground Movie)としてNHK教育テレビをつけて(おかあさんといっしょ/忍たま乱太郎/おじゃる丸(順不同))夕食の準備。かぼちゃが残ってるんだよなー。まずはかぼちゃを始末しなきゃ。

ビタクラフトに硬い茶漉しを置いて、東急ハンズで買ってきた焼き網を乗せます。蒸し野菜なので水が張ってあります。

かぼちゃを並べて

ふたをします。

5分ほどで火を止め、3分ほどふたをしたまま蒸らします。

はい。完成。うん。あまり甘くないかぼちゃだ。いまいちだな(悲しい)。でもいまいちながら味が濃くて、そのまま食べられます。蒸し野菜はいい。

他はこんな感じ。
(つぶれてしまったけど右横は赤ワイン。パンと赤ワインはベストマッチ。スモークサーモンはやはり白ワインが美味かった。)

スモークサーモンのマリネはまあまあ。あー。そうね。って感じ。がっかり感がなかっただけでよしとすべきであろう。


なぜナイフがあるのかって?それはね。

昔上州屋で買った千円くらいの安物ナイフですが、砥ぐとそれなりに切れて楽しい。おじさんってこういうのが大好きなんだよ。
となんだかんだ言ってちゃんと独りメシを楽しみましたよ。
.

餅つき

妻の実家で餅つきを手伝いました。臼と杵でぺったんぺったんするやつです。

杵をずぽっと真っ白い餅から抜き、杵の重みに手を添える感じで臼にぼすっと落とす。(杵の重みだけで十分搗けます。テレビで杵をがんがん振り下ろしているのはパフォーマンスです。)しみじみ懐かしいこの感じ。いや、まいったホント懐かしいわ。思い出すのはもう10年以上前の、祖父の家で搗いた餅。

風呂場の釜にセイロを重ねて祖母がもち米をふかしてたなあ。ふけたもち米を父が小走りで運んで、臼に投入したっけ。祖父がそれを杵でねりねりと細かいリズムで搗く。いや、搗くというよりすり潰す。最初に潰しておかないと、搗く時に米が飛び散ってしまう。米が8割がた潰れてからようやく搗き始める。ぺったんぺったんと玄関で搗いたなあ。60後半になっても一臼を自分で搗きあげてた。孫にいいところを見せようとしたんだな。

最後の臼は必ず草もち。一キロほど離れた川辺で祖母が摘んだよもぎ(あらかじめ茹でてある)を、まずは餅なしで臼に投入。ぐりぐりと杵ですり潰す。繊維を断つわけですね。青臭い匂いがしたっけ。決して美味そうな匂いじゃなかったな。むしろ強烈な匂いだった。それからもち米を投入して、やはりねりねりと潰す。ヨモギの繊維が明らかに異質な感じで白いもち米に割り込んでいて、ちゃんと混ざるのかな。大変そうだな。と思うけど大丈夫。しばらく搗いていればちゃんときれいで美味そうなヨモギ餅になります。母親と妹と祖母は隣の部屋で餅を加工中。餡を詰めたり伸し餅にしたり鏡餅を作ったり。白い大福とヨモギ大福を交互に食べると飽きなくてヤバイんだよね。またこうやって作ったヨモギ餅は、本当に「草です。青臭いです。しかもヨモギです」って感じがする。春菊なんかよりよほど強力。市販のはヨモギ餅。こっちは噛み跡がちゃんと繊維っぽい。そんなよもぎ餅を食べておなかいっぱいになってもう食べられない、食べないぞ。と宣言すると、父が「餅は消化にいいからな。食べても太らない」とわけの分からないことを言って煽ってくる。しかも毎年。まったく信じてないんだけど、しっかり食べ過ぎてしまって何か悔しかったな。

そんなわが家の餅つきも、餅がいっぱいあっても食べきれないという理由でいつのまにかやらなくなった。おせち料理も作らなくなったし。スーパーは元旦から営業するようになったし。「静かで退屈なお正月三が日」がなくなったのも、その頃だった気がする。

で、現在に戻って熱いふけたばかりのもち米が臼に投入されて、それを待ち構える私。最初は体を使ってもち米を潰す。そうそう。この感じ。最初にもち米を潰すのが大変なんだよね。思わず「この最初に米を潰すのが大変なんですよね」と口をついて出る。軽く息を切らしながら、杵を小刻みに動かす。うん大変だ。餅つきって結構つらい作業なんだよね。楽しいことは楽しいけど。そうそう。一度父親の都合か何かで正月に帰省できなかったことがあって、そんなときでも祖父から伸し餅と大福が送られてきた。「ええ?じいちゃんだけで搗いたの?すごいね」。父が「ほら。この辺もち米が粒のままや。途中でダウンしたんやろ」。じいちゃん無理したなあ、と二人で笑ったこともあった。

そうか。じいちゃんが死んでもう10年か。孫の顔を見せたかったなあ。と再び杵を振り上げて。

向こうで子供が餅を丸めながら、どうすればいいの?と妻に聞いて、妻が大福の作り方を教えてる。餅を搗く動作に気合が入る。

そしてふと思う。あ。おれ「お父さん」になってるわ。いや、餅搗かなくっても父は父なんだけどさ。子供にとって特別な存在って意味で。そんな感慨がする年末の餅つき。
.

2008年12月29日月曜日

今日の料理(カマスの干物、ひよこ豆の蜜煮)

スーパーに行ってみたら、魚コーナーはすっかり正月モード。数の子とか酢ダコとか、おせち料理系のネタが幅を利かせていました。私好みの未処理の魚は隅に追いやられていて何だかつまらん。カワハギも最近見かけないし。旬のハズなのにな。君はいずこへ。食べたいと思う魚は入荷されない。そんな宇宙の法則でもあるのかしらん。

鯵とかサンマすらなかったので、カマスを買って帰りました。中型3匹で399円。まあいつもの青魚(鯵・サンマ・いわし・サバ)に比べれば高いけど、別に目が飛び出るほどでもないし。金目鯛のアラにもかなり惹かれたんですけどね。あの大きくて深い目。煮付けたらどんな味がするんだろう。美味そうというより興味本位ですか。でもアラの分際で480円もしたので諦めました。

というわけでこのカマスは干物にしてやろう。帰ってエイヤと背開き。あ、ウロコ取るの忘れた。まあいいや。もう一匹もウロコを取らずに背開き。なんだか腹が柔らかい。鮮度がちょっと疑問。でも干物だから全然問題なし。でもでもちょっと生臭い気がする。いや、新鮮な鯵やサンマだってそれなりににおうんですが、そっちは新鮮な血の生臭さ。カマスは元は上品な香りだったんじゃないかと往時をしのばせる生臭さ。このにおいは酒と塩に二時間ほど漬ける間も消えることはなく、大丈夫かね、と思いながら干物ネットに格納。寒空の夜、ベランダに吊るしました。

で、翌日。朝日を浴びている干物を引き上げてみると、これがいい香りがするんですね。生臭さは奥にやられて一夜干しの甘いいいにおい。身も金色がかった透明になっていて、見かけも美味そう。ほほう。これは楽しみ。

カマスの干物(ラップかけて冷蔵庫に仕舞ったところ)

それから昨夜から一晩熱湯に浸しておいたひよこ豆を甘く煮付けてみました。結果はまあまあかな。

砂糖はかなり大量。キビ砂糖ですが、煮詰めていくと茶色の蜜のようになりました。豆はやや固めに仕上がりました。水だけで茹でたときのぽくぽくした食感はなく、まあ失敗した黒豆の煮物みたいな感じですな。でもひよこ豆をたっぷりの砂糖で煮たわけで、不味いわけはなし。味は上等です。

ひよこ豆の蜜煮

最近は豆とか白身魚とか、体に負担がかからない系を志向してます。やはり体の求めるものに敏感になって意志をコントロールしなければ、体調は上がりませんな。

以上。

(12/30追記)
やはりカマスは美味かったです。旨味のある上品な塩味で、隠し香程度のほのかな海の匂いが口の奥に立ち込めます。上品なんだけど、小さな肉片にも味はしっかり付いている。いやあ青魚とは違うわ。子供も「これは好き」と言っていました。

他にはポテトサラダ(酢を多めに入れたら見事不評。自分的にはまあまあだったんだけど)、それから昆布と魚粉パックで出汁を取り、厚揚げ、みず菜、まいたけを煮て、薄口しょうゆとみりんで仕上げた煮物。かなり好評。ほとんど何も考えずに"冷蔵庫にあるもん"で作る手抜き料理なんだけどな。何か食材に手をかければかけるほど評判が悪くなっているような気がしてならん。悔しい。
.

2008年12月27日土曜日

今さらながら入力補完JavaScript(8)

【段取り】
(4)テキストボックスに入力された文字列にマッチする補完候補をリスト表示する。

【詳細】
今は変数に入った値全部を表示していますが、これを入力された文字列にマッチするものだけ表示するようにします。

今まで便宜的にfocusで候補リストを表示していましたが、ここを全て書き換えて(うわああぁぁぁ!)、キーボードから文字列が入力されたタイミングで候補リストを出力するように変更します。

【結論】
ダメですね。onkeypressイベントでテキストボックスの値を見ても、最後に入力された文字列が入らない。
かなり直感に反する動きになっています。文字を入力した後、右矢印キーなど無害なキーを押さないと、入力した文字を使ってくれません。

余裕があったらキーイベントに依存するのではなく、suggest.jsみたいにtimerで定期的に検索する形に変えてみたいと思います。

ソースを見ていただければバレバレですが、もう付け刃に付け刃してって感じで大変でした。やっぱりJavaScriptはツラい・・・

ディレクトリ構造は変更なしです。
WebContent
- js/prototype.js
- js/my.js
- a.html
- my.css

■my.js
// 外出しです。
var div1 = null;
var cursor_pos = -1;
var element_len = 0;

Event.observe(window, 'load', function(event) {
// イベントを登録します。
var keyevent = 'keydown';
if (window.opera || (navigator.userAgent.indexOf('Gecko') <= 0
&& navigator.userAgent.indexOf('KHTML') == -1)) {
keyevent = 'keypress';
}
Event.observe($('hoge'), keyevent, function(event) {
if (event.keyCode == Event.KEY_DOWN) {
if (element_len < cursor_pos) {
cursor_pos++;
}
var hogeTmp = $('div1'+cursor_pos);
initiateListColors();
hogeTmp.style.backgroundColor = '#00FF00';
} else /* elseをつけましたよ */
if (event.keyCode == Event.KEY_UP) {
if (cursor_pos > 0) {
cursor_pos--;
}
var hogeTmp = $('div1'+cursor_pos);
initiateListColors();
hogeTmp.style.backgroundColor = '#00FF00';
} else
if (event.keyCode == Event.KEY_TAB
|| event.keyCode == Event.KEY_RETURN) {
if (cursor_pos >= 0) {
var hogeTmp = $('div1'+cursor_pos);
$('hoge').value = hogeTmp.innerHTML;
}
$('candidate').style.visibility = "hidden";
} else {
// クリアします
$('candidate').innerHTML = "";
$('candidate').style.visibility = "visible";
cursor_pos = -1;

var i;
var size = -1; // <- 当たり前ですが重要です。
var rObj = new RegExp($('hoge').value);
for (i = 0; i < week.length; i++) {
// 入力された文字が一致していた候補を表示します。
var tmpSearch = week[i];
if (tmpSearch.match(rObj) != null) {
size++; // <- iではなくてsizeをincreしながら使います
// 文字列がマッチしていたらappendします。
var tmpId = 'div1'+size;
div1 = new Element('div', {'id' : tmpId });
div1.innerHTML = week[i];
div1.style.backgroundColor = "#FF0000";
div1.style.width = "100px";
$('candidate').appendChild(div1);
Event.observe(tmpId, 'mouseover', function(event) {
this.style.backgroundColor = "#00FF00";
});
Event.observe(tmpId, 'mouseout', function(event) {
this.style.backgroundColor = "#FF0000";
});
Event.observe(tmpId, 'mousedown', function(event) {
$('hoge').value = this.innerHTML;
$('candidate').style.visibility = "hidden";
});
}
}
element_len = size;
}
});
// onBlur イベントを登録します。
Event.observe('hoge', 'blur', function(event) {
if (div1) {
// div1がnullでない場合はhiddenにします。
$('candidate').style.visibility = "hidden";
// ポジションを初期化します。
cursor_pos = -1;
}
});
});

function initiateListColors() {
var tmpElems = $('candidate').childElements();
for (var j = 0; j < tmpElems.length; j++) {
tmpElems[j].style.backgroundColor = "#FF0000";
}
}

■a.html

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-31j">
<script type="text/css" src="my.css"></script>
<!-- 変数をmy.jsより先に読み込む必要があります。 -->
<script type="text/javascript">
var week = new Array("hokkaido","aomori","akita",
"yamagata","miyagi","niigata","fukushima",
"ikkei", "komori", "higata");
</script>
<script type="text/javascript" src="js/prototype.js"></script>
<script type="text/javascript" src="js/my.js"></script>
<title>Ajax tutorial - a</title>
</head>
<body>
<input type="text" id="hoge" name="hoge" />
<div id="candidate"></div>
</body>
</html>

■my.css

@CHARSET "UTF-8";
/* CSSは表示と関係ありません */
#div1 {
visibility:hidden;
}

.

今さらながら入力補完JavaScript(7)

【段取り】
(3)補完候補の選択確認

【詳細】
生成したDIV要素をキーボード/マウスで選択できるようにします。
次に選択された文字列をテキストボックスに入れ込みます。

TAB、ENTERが押された場合、その時に選択されていた値をテキストボックスに入力し、リストを消去します。すんなり行きました(なんだか動きに違和感があるような気もしますが気にしないことにします)。

ディレクトリ構造は変更なしです。
WebContent
- js/prototype.js
- js/my.js
- a.html
- my.css

■my.js
// 外出しです。
var div1 = null;
var cursor_pos = -1;
var element_len = 0;

Event.observe(window, 'load', function(event) {
// イベントを登録します。
var keyevent = 'keydown';
if (window.opera || (navigator.userAgent.indexOf('Gecko') >= 0
&& navigator.userAgent.indexOf('KHTML') == -1)) {
keyevent = 'keypress';
}
Event.observe($('hoge'), keyevent, function(event) {
if(div1) {
if (event.keyCode == Event.KEY_DOWN) {
if ((element_len - 1) > cursor_pos) {
cursor_pos++;
}
var hogeTmp = $('div1'+cursor_pos);
initiateListColors();
hogeTmp.style.backgroundColor = '#00FF00';
}
if (event.keyCode == Event.KEY_UP) {
if (cursor_pos > 0) {
cursor_pos--;
}
var hogeTmp = $('div1'+cursor_pos);
initiateListColors();
hogeTmp.style.backgroundColor = '#00FF00';
}
if (event.keyCode == Event.KEY_TAB
|| event.keyCode == Event.KEY_RETURN) {
var hogeTmp = $('div1'+cursor_pos);
$('hoge').value = hogeTmp.innerHTML;
$('candidate').style.visibility = "hidden";
}
}
});
// onFocus イベントを登録します。
Event.observe($('hoge'), 'focus', function(event) {
if (div1) {
// div1*がnullでなければcandidateをvisibleにします。
$('candidate').style.visibility = "visible";
} else {
// nullの場合はエレメントを作成して<div id="candidate"></div>に
// appendします。
var i;
for (i = 0; i < week.length; i++) {
var tmpId = 'div1'+i;
div1 = new Element('div', {'id' : tmpId });
div1.innerHTML = week[i];
div1.style.backgroundColor = "#FF0000";
div1.style.width = "100px";
$('candidate').appendChild(div1);
Event.observe(tmpId, 'mouseover', function(event) {
this.style.backgroundColor = "#00FF00";
});
Event.observe(tmpId, 'mouseout', function(event) {
this.style.backgroundColor = "#FF0000";
});
Event.observe(tmpId, 'mousedown', function(event) {
$('hoge').value = this.innerHTML;
$('candidate').style.visibility = "hidden";
});
}
element_len = i;
}
});
// onBlur イベントを登録します。
Event.observe('hoge', 'blur', function(event) {
if (div1) {
// div1がnullでない場合はhiddenにします。
$('candidate').style.visibility = "hidden";
// ポジションを初期化します。
cursor_pos = -1;
}
});
});

function initiateListColors() {
// .style.backgroundColor = "#FF0000";
for (k = 0; k < element_len; k++) {
$('div1'+k).style.backgroundColor = "#FF0000";
}
}

■a.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-31j">
<script type="text/css" src="my.css"></script>
<!-- 変数をmy.jsより先に読み込む必要があります。 -->
<script type="text/javascript">
var week = new Array("日","月","火","水","木","金","土");
</script>
<script type="text/javascript" src="js/prototype.js"></script>
<script type="text/javascript" src="js/my.js"></script>
<title>Ajax tutorial - a</title>
</head>
<body>
<input type="text" id="hoge" name="hoge" />
<div id="candidate" />
</body>
</html>

■my.css
<pre>@CHARSET "UTF-8";
/* CSSは表示と関係ありません */
#div1 {
visibility:hidden;
}

.

今さらながら入力補完JavaScript(6)

【段取り】
(3)補完候補の選択確認

【詳細】
生成したDIV要素をキーボード/マウスで選択できるようにします。
次に選択された文字列をテキストボックスに入れ込みます。

次はキーボードです。結構苦労しました。

ポイントは以下の通りです。
1)キーボードイベントは仕様として決まっていないようで、suggest.js を参考に以下のような条件分けをしています。
var keyevent = 'keydown';
if (window.opera || (navigator.userAgent.indexOf('Gecko') >= 0
&& navigator.userAgent.indexOf('KHTML') == -1)) {
keyevent = 'keypress';
}
Event.observe($('hoge'), keyevent, function(event) {

他は行き当たりばったりの力ずくな実装になっています。なりふり構わずなところを暖かく見守って頂きたい。

ディレクトリ構造は変更なしです。
WebContent
- js/prototype.js
- js/my.js
- a.html
- my.css

■my.js
// 外出しです。
var div1 = null;
var cursor_pos = -1;
var element_len = 0;

Event.observe(window, 'load', function(event) {
// イベントを登録します。
var keyevent = 'keydown';
if (window.opera || (navigator.userAgent.indexOf('Gecko') >= 0
&& navigator.userAgent.indexOf('KHTML') == -1)) {
keyevent = 'keypress';
}
Event.observe($('hoge'), keyevent, function(event) {
if(div1) {
if (event.keyCode == Event.KEY_DOWN) {
if ((element_len - 1) > cursor_pos) {
cursor_pos++;
}
var hogeTmp = $('div1'+cursor_pos);
initiateListColors();
hogeTmp.style.backgroundColor = '#00FF00';
}
if (event.keyCode == Event.KEY_UP) {
if (cursor_pos > 0) {
cursor_pos--;
}
var hogeTmp = $('div1'+cursor_pos);
initiateListColors();
hogeTmp.style.backgroundColor = '#00FF00';
}
}
});
// onFocus イベントを登録します。
Event.observe($('hoge'), 'focus', function(event) {
if (div1) {
// div1*がnullでなければcandidateをvisibleにします。
$('candidate').style.visibility = "visible";
} else {
// nullの場合はエレメントを作成して<div id="candidate"></div>に
// appendします。
var i;
for (i = 0; i < week.length; i++) {
var tmpId = 'div1'+i;
div1 = new Element('div', {'id' : tmpId });
div1.innerHTML = week[i];
div1.style.backgroundColor = "#FF0000";
div1.style.width = "100px";
$('candidate').appendChild(div1);
Event.observe(tmpId, 'mouseover', function(event) {
this.style.backgroundColor = "#00FF00";
});
Event.observe(tmpId, 'mouseout', function(event) {
this.style.backgroundColor = "#FF0000";
});
Event.observe(tmpId, 'mousedown', function(event) {
$('hoge').value = this.innerHTML;
$('candidate').style.visibility = "hidden";
});
}
element_len = i;
}
});
// onBlur イベントを登録します。
Event.observe('hoge', 'blur', function(event) {
if (div1) {
// div1がnullでない場合はhiddenにします。
$('candidate').style.visibility = "hidden";
// ポジションを初期化します。
cursor_pos = -1;
}
});
});

function initiateListColors() {
// .style.backgroundColor = "#FF0000";
for (k = 0; k < element_len; k++) {
$('div1'+k).style.backgroundColor = "#FF0000";
}
}

■a.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-31j">
<script type="text/css" src="my.css"></script>
<!-- 変数をmy.jsより先に読み込む必要があります。 -->
<script type="text/javascript">
var week = new Array("日","月","火","水","木","金","土");
</script>
<script type="text/javascript" src="js/prototype.js"></script>
<script type="text/javascript" src="js/my.js"></script>
<title>Ajax tutorial - a</title>
</head>
<body>
<input type="text" id="hoge" name="hoge" />
<div id="candidate" />
</body>
</html>

■my.css
@CHARSET "UTF-8";
/* CSSは表示と関係ありません */
#div1 {
visibility:hidden;
}

.

今さらながら入力補完JavaScript(5)

【段取り】
(3)補完候補の選択確認

【詳細】
生成したDIV要素をキーボード/マウスで選択できるようにします。
次に選択された文字列をテキストボックスに入れ込みます。
mousedownイベントを定義します(思ったより順調ですよ)。

※mousedown -> mouseup -> click という順にイベントが走ります。
mousedownの時点でonblurが走ってしまうため、DIV要素が消えてしまいます。
従ってclickイベントでは動きません。

ディレクトリ構造は変更なしです。
WebContent
- js/prototype.js
- js/my.js
- a.html
- my.css

■my.js
// 外出しです。
var div1 = null;

Event.observe(window, 'load', function(event) {
// onFocus イベントを登録します。
Event.observe('hoge', 'focus', function(event) {
if (div1) {
// div1*がnullでなければcandidateをvisibleにします。
$('candidate').style.visibility = "visible";
} else {
// nullの場合はエレメントを作成して<div id="candidate"></div>に
// appendします。
for (i = 0; i < week.length; i++) {
var tmpId = 'div1'+i;
div1 = new Element('div', {'id' : tmpId });
div1.innerHTML = week[i];
div1.style.backgroundColor = "#FF0000";
div1.style.width = "100px";
$('candidate').appendChild(div1);
Event.observe(tmpId, 'mouseover', function(event) {
this.style.backgroundColor = "#00FF00";
});
Event.observe(tmpId, 'mouseout', function(event) {
this.style.backgroundColor = "#FF0000";
});
Event.observe(tmpId, 'mousedown', function(event) {
$('hoge').value = this.innerHTML;
$('candidate').style.visibility = "hidden";
});
}
}
});
// onBlur イベントを登録します。
Event.observe('hoge', 'blur', function(event) {
if (div1) {
// div1がnullでない場合はhiddenにします。
$('candidate').style.visibility = "hidden";
}
});
});

■a.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-31j">
<script type="text/css" src="my.css"></script>
<!-- 変数をmy.jsより先に読み込む必要があります。 -->
<script type="text/javascript">
var week = new Array("日","月","火","水","木","金","土");
</script>
<script type="text/javascript" src="js/prototype.js"></script>
<script type="text/javascript" src="js/my.js"></script>
<title>Ajax tutorial - a</title>
</head>
<body>
<input type="text" id="hoge" name="hoge" />
<div id="candidate" />
</body>
</html>

■my.css
@CHARSET "UTF-8";
/* CSSは表示と関係ありません */
#div1 {
visibility:hidden;
}

.

今さらながら入力補完JavaScript(4)

【段取り】
(3)補完候補の選択確認

【詳細】
生成したDIV要素をキーボード/マウスで選択できるようにします。
まずはマウスでハイライトさせます。mouseoverとmouseoutイベントを定義します。

ディレクトリ構造は変更なしです。
WebContent
- js/prototype.js
- js/my.js
- a.html
- my.css

■my.js
// 外出しです。
var div1 = null;

Event.observe(window, 'load', function(event) {
// onFocus イベントを登録します。
Event.observe('hoge', 'focus', function(event) {
if (div1) {
// div1*がnullでなければcandidateをvisibleにします。
$('candidate').style.visibility = "visible";
} else {
// nullの場合はエレメントを作成して<div id="candidate"></div>に
// appendします。
for (i = 0; i < week.length; i++) {
var tmpId = 'div1'+i;
div1 = new Element('div', {'id' : tmpId });
div1.innerHTML = week[i];
div1.style.backgroundColor = "#FF0000";
div1.style.width = "100px";
$('candidate').appendChild(div1);
Event.observe(tmpId, 'mouseover', function(event) {
this.style.backgroundColor = "#00FF00";
});
Event.observe(tmpId, 'mouseout', function(event) {
this.style.backgroundColor = "#FF0000";
});
}
}
});
// onBlur イベントを登録します。
Event.observe('hoge', 'blur', function(event) {
if (div1) {
// div1がnullでない場合はhiddenにします。
$('candidate').style.visibility = "hidden";
}
});
});

■a.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-31j">
<script type="text/css" src="my.css"></script>
<!-- 変数をmy.jsより先に読み込む必要があります。 -->
<script type="text/javascript">
var week = new Array("日","月","火","水","木","金","土");
</script>
<script type="text/javascript" src="js/prototype.js"></script>
<script type="text/javascript" src="js/my.js"></script>
<title>Ajax tutorial - a</title>
</head>
<body>
<input type="text" id="hoge" name="hoge" />
<div id="candidate" />
</body>
</html>

■my.css
@CHARSET "UTF-8";
/* CSSは表示と関係ありません */
#div1 {
visibility:hidden;
}

.

今さらながら入力補完JavaScript(3)

【段取り】
(2)手作りリストボックスの確認

【詳細】
(2)手作りリストボックスの確認
DIV要素を配列から生成します。意外と簡単にできました。

ディレクトリ構造は変更なしです。
WebContent
- js/prototype.js
- js/my.js
- a.html
- my.css

■my.js
// 外出しです。
var div1 = null;

Event.observe(window, 'load', function(event) {
// onFocus イベントを登録します。
Event.observe('hoge', 'focus', function(event) {
if (div1) {
// div1がnullでなければvisibleにします。
$('candidate').style.visibility = "visible";
} else {
// nullの場合はエレメントを作成して<div id="candidate"></div>に
// appendします。
for (i = 0; i < week.length; i++) {
div1 = new Element('div', {'id' : 'div1'+i });
div1.innerHTML = week[i];
div1.style.backgroundColor = "#FF0000";
div1.style.width = "100px";
$('candidate').appendChild(div1);
}
}
});
// onBlur イベントを登録します。
Event.observe('hoge', 'blur', function(event) {
if (div1) {
// div1がnullでない場合はhiddenにします。
$('candidate').style.visibility = "hidden";
}
});
});

■a.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-31j">
<script type="text/css" src="my.css"></script>
<!-- 変数をmy.jsより先に読み込む必要があります。 -->
<script type="text/javascript">
var week = new Array("日","月","火","水","木","金","土");
</script>
<script type="text/javascript" src="js/prototype.js"></script>
<script type="text/javascript" src="js/my.js"></script>
<title>Ajax tutorial - a</title>
</head>
<body>
<input type="text" id="hoge" name="hoge" />
<div id="candidate" />
</body>
</html>

■my.css
@CHARSET "UTF-8";
/* CSSは表示と関係ありません */
#div1 {
visibility:hidden;
}

.

今さらながら入力補完JavaScript(2)

【段取り】
(1)イベントハンドリングの確認
まだ続きます。

【詳細】
(1)イベントハンドリングの確認
テキストボックスにフォーカスが当たったらDIV要素を表示、blurしたらDIV要素を消します。
DIV要素を動的に作ります。

【恥ずかしながらハマった点】
input要素にappendChildしても表示されない(分かってみれば当たり前)のでハマりました。

ディレクトリ構造は変更なしです。
WebContent
- js/prototype.js
- js/my.js
- a.html
- my.css

■my.js
// 外出しです。
var div1 = null;

Event.observe(window, 'load', function(event) {
// onFocus イベントを登録します。
Event.observe('hoge', 'focus', function(event) {
if (div1) {
// div1がnullでなければvisibleにします。
div1.style.visibility = "visible";
} else {
// nullの場合はエレメントを作成して<div id="candidate"></div>に
// appendします。
div1 = new Element('div', {'id' : 'div1' });
div1.innerHTML = "HOGE!";
div1.style.backgroundColor = "#FF0000";
div1.style.width = "100px";
$('candidate').appendChild(div1);
}
});
// onBlur イベントを登録します。
Event.observe('hoge', 'blur', function(event) {
if (div1) {
// div1がnullでない場合はhiddenにします。
div1.style.visibility = "hidden";
}
});
});

■my.css
@CHARSET "UTF-8";
/* CSSは表示と関係ありません */
#div1 {
visibility:hidden;
}

■a.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-31j">
<script type="text/javascript" src="js/prototype.js"></script>
<script type="text/javascript" src="js/my.js"></script>
<script type="text/css" src="my.css"></script>
<script type="text/javascript">
</script>
<title>Ajax tutorial - a</title>
</head>
<body>
<input type="text" id="hoge" name="hoge" />
<div id="candidate" />
</body>
</html>

.

2008年12月25日木曜日

今さらながら入力補完JavaScript(1)

使い勝手のいいライブラリも出ている(suggest.js)以上、今さら入力補完JavaScriptを作ってみてもしょうがないといえばしょうがないのですが、自前で実装できないというのはやはり悔しいので、勉強がてら作ってみます。

てゆーか本当に難しいですよ。JavaScriptってやつは。

【最終形】
prototype.js(v1.6)を使います。
配列の変数に、補完候補を持ちます。。
インプットボックスに入力された文字列とマッチする補完候補があったら、それをリストボックスとして表示します。
そのリストボックスはキーボードの上/下ボタンで選択、TABあるいはENTERキーで決定できます。マウスで選択もできます。

【段取り】
(1)イベントハンドリングの確認

(2)手作りリストボックスの確認
テキストエリアに文字が入力されると、配列の変数にある補完候補をリスト表示する。

(3)補完候補の選択確認
リストされた保管候補を、キーボード、マウスで選択できる。

(4)最終形
テキストエリアに入力された文字列にマッチする補完候補をリスト表示する。

さて、上手くいくかどうか。

【詳細】
(1)イベントハンドリングの確認
テキストボックスにフォーカスが当たったらDIV要素を表示、blurしたらDIV要素を消します。
ディレクトリ構造は以下の通りです。
WebContent
- js/prototype.js
- js/my.js
- a.html
- my.css

■my.js
Event.observe(window, 'load', function(event) {
Event.observe('hoge', 'focus', function(event) {
$('div1').style.visibility = "visible"
});
Event.observe('hoge', 'blur', function(event) {
$('div1').style.visibility = "hidden"
});
});

■my.css
@CHARSET "UTF-8";

#div1 {
visibility:hidden;
}

■a.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-31j">
<script type="text/javascript" src="js/prototype.js"></script>
<script type="text/javascript" src="js/my.js"></script>
<script type="text/javascript">
</script>
<title>Ajax tutorial - a</title>
</head>
<body>
<input type="text" id="hoge" name="hoge" />
<div id="div1">test</div>
</body>
</html>

どうなることやら不安がいっぱいです。
.

2008年12月23日火曜日

高度成長期のいわゆる中産階級に生まれた

クリスマス(今日はイブ・イブだけど)というのは、私にとっては高度成長期(団塊Jr)のいわゆる中産階級(中企業サラリーマン)に生まれたことを実感するイベントです。

どういうことかと言えば、今日はクリスマスだ。ワー。じゃあ、ハンバーグにしましょうか。やったー!夕方何気にテレビをつけるとディケンズのクリスマスキャロル(人形劇)をやってる。やったー!さあ、ハンバーグ食った。寝るぞ。プレゼント楽しみ~!という感じですんなり収まってしまう感覚。それが日本高度成長時代の中産階級に生まれた子の発想だと思うのです。

すなわち、クリスマスだからいつもとは違う感が欲しい。でもだからといってローストビーフとか、丸一匹のローストチキンというのもなんだか場違いな気がする。いや、別に親にねだればそのくらい作ってくれるのは分かってるし、ハンバーグが凄く特別なわけじゃない(てゆーか先週もハンバーグ食べた)。でも親もいそがしそうだし、ローストビーフが格別ハンバーグより美味いとも思えない。プレゼントももらえる。ハンバーグでかなり満足。別に苦労して背伸びする必要はないじゃん。やれば出来るけどさ(アンニュイ)。という感じ。

クリスマスというイベントを真剣に楽しもうとカロリーを消費し、熱く頑張る人と比較した時、若干「損?」という気もしないではありませんが、トータルで見れば妥当というか、バランスが取れている態度にも思えるわけで。家にはお手伝いさんがいました。クリスマスといえばローストチキンが当たり前。というお坊ちゃんお嬢ちゃん。あるいは、昔貧乏したことがあってなんとなくコンプレックスがあるんだよねー、だからクリスマスからローストビーフははずせない。という価値観よりも、まあ、それなりに楽しもうや、というところが自然体でよろしいのではないか、と。

そんなふうに自己肯定できるだけ、成長したなと自分を褒めてやろうじゃないか。自分を「よし」としたいと思う、今日この頃です。
.

吉行淳之介「飲む」

吉行淳之介の「飲む」というエッセーがあって、これがエッセーの白眉というか、何年かおきに読み返してしまうのです。

個人的には傑作と思ってます。一杯の酒がもたらす潤い。

琥珀色の美酒。ストイックでありつつも、酒を楽しむことのできる余裕。酒への憧れとストイックさとの共存。一杯の貴重な酒を飲むことで、生きている文脈に戻る。生きている実感と、酒を飲む幸せとが完全に合体する時。

いいですね。うん。

以上。
.

クリスマスの危機

まあ、今日はクリスマス・イブ・イブなわけですが。

だいたい子供というのは、こういうイベントがある日には朝からハイになり、日中は尋常じゃないテンションで騒ぎまくるわけです。

そして両親はせっかくだから、とケーキを作ってみたり、ご馳走を作ってみたりして神経を使いつつ、日中は労働しているわけで。

ということは、次第に子供はわけも分からず騒ぎ立て、父母は労働による疲れがかさみつつ、ハイテンションを引きずりつつ疲れも見えてきた子供たちに振り回されることで、次第に不機嫌になるわけで。

で、クライマックス。ケーキとかハレのデザートを準備し、ハレのご馳走を準備しつつあるそのとき、事故は起こるものなのです。

まあ、さすがになれましたけどね。皿やグラスが割れなければもう上出来というか。怪我しなけりゃあそれでいいか、みたいな。

今年は妻がクッキーで作るお菓子の家なるギミックを採用し、苦労して作ってました。及ばずながら私も皿洗いなど手伝いましたけど。それからローストチキン丸一羽分。これも妻が選択し、妻が頑張って作りました。美味かったです。一家四人おなか一杯食べた後、残った残骸から出来るだけ肉をこそげ取り、さらに残ったガラを鶏がらスープに。チェ・ゲバラ率いるゲリラのメンバーに食べさせてあげたいと思った。マジで。でも血はにごった感じ。昼食抜いたのにな。また断食しなければ・・・

という訳で、誰も怪我せず、皿が割れることもなく、楽しく過ごしました。うんうん。


.

2008年12月21日日曜日

Ajaxとか

Ajax入門。いいページですね。Ajaxがよく分かる。

これまでJavascriptでコーディングする人は神だと思っていましたが、この入門を一通りチェックして少し神が身近に感じられました。そうか。FireBugを使えばいいのか(それにしても大変だと思うけどなあ)。

それからSOAだとかweb serviceという(胡散臭い)ジャーゴンがここ数年生きながらえている理由も、少し理解できたような気がしました。上手く言えませんが、要するにショッピングサイトの構築には相性がいいんだなってとこでしょうか。まだまだEnterpriseの領域でSOAとかweb serviceが活用できるとは思えないんだけどな。だって必要性がありませんぜ。データベースを使えばいいんだからさ。

以上です。
.

今日の料理(タラコディップ・サンマのみりん干し)

カラスミを目指していたタラコの塩漬け。ハエにファーストキッスを奪われた悔しさのあまり、火あぶりにして食べてみたらちゃんと美味しいタラコでした。魚卵好きのうちの妻は喜んでました。当然アミノ酸やら蛋白加水分解物は入っていないので分かりやすい旨味はありません。でもタラコ好きならイケるんじゃないかな。無添加だし。ちゃんとしたタラコっぽい発酵臭としょっぱいツブツブ・プチプチのテクスチャ。それに遠くの方から漂ってくるほのかな旨味。うん。タラコだ。って感じです。戦前のタラコってこうじゃなかったのかな。食べたことないけど。

今日はそんなタラコを使ってマヨネーズディップを作り、蒸し野菜と食べました。美味かった。私が料理するとたんぱく質と脂が過剰になるので、野菜を意識的に摂取する必要があります。一部はマヨネーズと和えずに、妻がご飯と食べてました。

それから昨日作ったサンマのみりん干し。これが美味かった。鯵で作ったときはちょっと干し過ぎてしまい、焼いたら硬かったんですが、今回やや生干しっぽくしてみたらご飯に合う合う。今度鯵のみりん干しを作るときは干し加減に気をつけようと心に誓いました。

なんだか血が濁ってきた気がするのでしばらく野菜優先で行きたいと思ってます。(でも鳥ハム仕込んじゃってるんだよな・・・)

以上。
.

2008年12月20日土曜日

カラスミとか

生のタラコをカラスミにする計画ですが、本日のハエの挨拶ですっかりなえてしまいました。

一週間塩漬け → 丸一日水で塩抜き → 丸一日酒に漬ける → 冷凍庫で丸一日冷凍(一応アニサキス対応) → 冷蔵庫で乾燥 とかやってるうちに食い物に見えなくなったのよ。これ食うの?って。扱いが食品じゃなくなってて、何かの実験?みたいな。たとえるなら人体実験?

でも、途中でちょっとはみ出した卵を食べると美味しいんですけどね。マイルドで自然なタラコって感じで。でも、今日外に干してみたタラコに、いとおしそうにベタベタとキスしていたハエを観察してもう生食は断念。もう炙って食っちゃる。生食勘弁。パンとか乾き物なら多少ハエがとまっても気になりませんが、自作のカラスミにとまったハエは結構インパクトでした。大体個人でカラスミ作っていいのかと。ちゃんとお国の監査を経た、歴史ある企業がするべき仕事ではないのか、と。まあ、ようするにヘタレってことで。私は逃げますよ。明日か明後日に、しっかり炙っていただくことにします。



それからサンマのみりん干しと、鳥ハムを仕込んでます。



サンマはアジよりさばくのが楽ですね。頭を落としてしまえば背開きも結構簡単。ざくざくと出刃包丁で小骨を切る感触が楽しかったです。それからとうとう上州屋で干し網を買いました。ハエを寄せ付けないような干物を干す網です。物干し竿に引っ掛けます。700円台でした。元は取れると思うな。ふっふっふ。

以上。
.

再びチェ・ゲバラ

引き続きボリビア日誌を読んでいます。

人心掌握の難しさよ。チェほどの人間でも苦労するんだ。

希望を維持することができる。それだけで偉大だと思う。

ゲリラ戦すら楽しむ。ある人の生きる文脈が、他人を巻き込むことが出来る。

チェ・ゲバラの目標の気高さ。そして、それに恥じない彼の生き方。

結構心を打たれます。
.

寝不足とか

以前は朝の4時台に目が覚めてそのまま(よく)眠れない、ということがよくありました。

朝の4時、5時にふと目が覚めてしまうことを鬱病の兆候だとする意見もあったりするわけで、そんなこともあるかしら、とも思っていたのですが、節酒するとそういうことも少なくなって、たまに呑みすぎ・食べすぎるとやたらと早朝に目が覚めてしまって眠れない、そういうことが分かりました。

すなわち私の場合は鬱病というよりも呑みすぎ・食べすぎが原因で朝に目が覚めていたわけです。

まあ鬱病だろうが呑みすぎだろうが、やたら早くに目が覚めてそのまま悶々と朝を迎えると、体も疲れるし、眠気は残るし、ロクなことはないわけですけどね。

一応、そんなハメに陥ったときは、とにかく目をつぶって力を抜くことにしています。それから小さな音量で音楽を聴くのもいいですね。音楽は静かなクラシック(ショパンのノクターン、ドビュッシー、バロック音楽、ピアソラ)、民族音楽(インドのシタール(ラビ・シャンカール)とか、中世の聖歌とか)、瞑想にいざなう系の音楽です。

そうすっとですね、結構わけのわからない半覚醒の夢を見ることができて面白いんですね。安らかに眠ったとか疲れが取れた感じはないのですが、とにかく奇妙な夢が見られます。

最近見た半覚醒の夢。見て面白かったな、とその次の瞬間に忘れてしまうことが多いのですが今でも覚えているのはセーラー服を着たガンダム。夢に出てきたとたん面白い!と思った。ウケた。でも、その次の瞬間我にかえって、なぜに?と思った。別にガンダムにも女子高生にも執着しているつもりはないのだが。それから、怪しいセールスマンの夢。「せっかくですから有効な特許を取っておきませんか?例えば、こんな特許はいかがでしょう」と言うのでよく見るとカエルの飼育方法に関する特許。いや、せっかくですが私の仕事からしてもカエルの飼育方法に関する特許をとってもあまり使い道はありませんので・・・と一所懸命お断りしながら、ああ、これは夢だと。そのセールスマンには申し訳なく思いつつ、これは変な夢を俺は見ているな、とその感覚が奇妙でした。

そんな感じで朝を迎えるとなんとなく眠ったような眠らなかったような、そんな感じです。

以上。
.
(12/30後記)
ガンダム・女子高生・カエル、と来たらケロロ軍曹ですね。
でも夢ではケロロっぽくなかったんだよなー。
.

2008年12月17日水曜日

鷲田清一さんはいいことを言う

この人いいことを言うんですよ。哲学者とは思えない(失礼>ごく一部の人)。

「学力問題」って、なんだ?(1/2)

学力って何だ?「頭がいい」って何だ?「『テスト』なんて無礼だ」。ちゃんとまっとうに考えてる。ちゃんとまっとうに考える人の少なさよ。
.

うちにも来ましたよ

カンパニータンクの取材依頼が来ました。何か嬉しい。

ググると分かるカンパニータンク。カンパニータンクの営業の人は「Yahooで調べて下さい」って言ってました。GoogleとYahooの差別化ですね!

丁重にお断りしました。いや、詐欺とまでは思ってないですよ。

って何のことだか分からない人が多いでしょうから補足です。

「地元の元気な中小企業を取材したい」「現場の意見を反映したい」という誘い文句で雑誌の取材依頼が来ます。「kwsk(詳しくお願いします)」と聞くと次は「雑誌に載せるのでウン万円欲しい」と来るわけですな。ざっと見ましたが掲載料は10万~100万くらいの模様。

自分の名前が活字化されるなら10万くらい安い!っていうマーケットは十分あるでしょうから、わりと妥当なビジネスと言ってもいいんじゃないかしら。というわけで詐欺ってのは言いすぎかもね。

断りましたが別にあちらもしつこくなかったですし、ちゃんと話を聞いてました。押し売りっぽくきたら私も腹が立ったでしょうけど、普通の対応でした。

でも、最初のセールストークがちょっと怪しいかなあ。だって、こちらで広告料を払わなければならない、なんて一言も言わないんだもの。妻も「だまされた」って思ったみたいです。最初から「御社の広告を出しませんか?」という話にした方が後味が悪くないと思いましたね(まあ、それだと見込み客が減るか)。

まあ、貴重な体験でした。
.

2008年12月14日日曜日

今日の料理(肉と青魚)

昨日のアジ一夜干し。市販の干物よりしっとりしていて、ちょっと内臓あたりに醗酵臭がしました。腹の掃除が甘かった模様。自然というか野性的?で美味かったです。上の子には不人気だったけど・・・

子供は塩焼きが一番好きみたい。新鮮な魚は塩焼きが一番美味いよな。そりゃそうだよ。塩焼きには負けるよ。(悔しい)

今日も懲りずにサンマとアジを買って来ました。だって安いし。それから子供向けに特売(97円/g)豚肩ブロックを500gくらい購入。これは定番の煮豚にします。

今回は漬けダレにちょっと工夫してみました。生姜とにんにくとねぎを刻むのではなくすりおろすのです。焼く前に1時間程タレに漬けますが、タレから引き上げる時に、漬け汁で野菜のカスをゆすぎます。そうしないと次の工程(肉に焼き目をつける)で野菜部分が焦げてしまうので。焼くそばから焼肉屋さんのようないい香りがして、心の中で「成功」とボソッとつぶやく。

これが肉を漬けてる写真。


表面に焦げ目をつけた後、30分ほどタレを絡めながら煮て仕上げます。


豚の臭みが抜けて美味いです。この調理法、個人的にはヒレ肉で作るのが最高だと思います。案外硬くなりません。噛み応えはありつつもやわらかな感じになります。

サンマは昨日焦がしたリベンジで酢で煮るレシピに再チャレンジ。今度はビタクラフトのフライパンで作るつもり。塩加減に注意しないとな。それから骨もサクサクいけるように薄く切ってみようと思ってます(煮汁の酢に浸った方が骨が食べやすいんじゃないか、と予想。つまり薄く切って骨と煮汁がなるべく触れるようにしてみる)。

アジは塩焼きが美味いに違いないんだけど今回はみりん干しに挑戦する予定。許せ子供たちよ。

さ、これから魚をさばくか。(16:10)

(約4時間後)

はい。魚さばきましたよ。しっかり漬け汁にも漬けました。干物を作るのに4時間かかったわけではありません。夕食もとっくに食べ終わってます。(20:10)
メニューは以下のとおり。
・ご飯
鳥ハムのマカロニサラダ
・さんまの骨までやわらか煮(小骨はぜんぜん問題なし。美味い。でも背骨は硬かった・・・)
・ブロッコリー(妻が野菜がないことに気がついてあわててゆでた))


魚は寒空の下ベランダで干さってます。何か寒そう。つーかおせんべいのにおいですね。みりん干しって。うまそう。

サンマはまずまず成功です。
 携帯カメラの画像だと不味そうだな・・・(他はちょっと昔のNIKONデジタル一眼)
背骨は厳しいけど小骨は余裕で食べられる。ちょっとだけすっぱくて美味い。子供にはウケなかったけど・・・。そりゃ塩焼きには負けるよな。手をかけないほうが美味いなんてチョー悔しい。まあ美味いからいいや。これから頑張って消費しよう。

うん。まあマズマズの週末でしたよ。今週も。
.

2008年12月13日土曜日

今日の料理

一週間前からからすみの作成に挑戦しています。(*参考1)(*参考2)

ボラではなくタラの卵を(タラコとか辛子明太ではなく生のやつですね)を買ってきて、まずは冷蔵庫で塩漬け。つーか全体的に食い物なの?大丈夫なの?って感じでした。一部緑色してるし(まあ最初からだけど。)。漬けていた間、冷蔵庫はずっと醗酵した魚卵のかほりで充満。腐敗臭ではなく、なんとなくたんぱく質系のまったりとした香りに海の匂いがするというか。人によっては好きな匂いかも。でも結構濃厚で冷蔵庫を開けると毎度ふわあぁんと塩漬け魚卵の匂い。幸い、妻の鼻の調子が悪かったので咎められることはありませんでしたが、う~ん、という感じ。一週間経過した昨日、塩を払って水に漬けました。塩抜きのためです。丸一日ほど塩抜きして、今は日本酒に漬けてます。



週中あたりから外+冷蔵庫で干し始めます。どうなることやら。

もう一つはアジの干物。30cmくらいのむっちりしたアジが一匹97円だったので、もうこれは買わないわけには行かなかったですよ。背開きにしようとしたら思ったよりアジが大きかったので3枚におろしました(相変わらず骨に身が残ってぐやじい(下手だから))。水800ccに対して大匙4杯の塩水+しょうゆと酒を少々(塩加減は水1リットルに対して大匙4杯+-1杯の塩くらいでいいみたい)に1時間浸してから

ベランダで乾燥。よっしゃ。明日の朝はアジの一夜干しでご飯だ。・・・よだれが出る・・・

ベランダに魚がぶら下がってる。いや、シュールな感じがしたのは一瞬だけですよ。すぐに違和感なく認識。というかしみじみとした豊かな気分も。縄文時代のDNAが生きているのだろうか。

サンマも安かったので(一匹58円!)、二匹買って酢で煮たのですが何故か最終的に全面的に焦げ付いてノックアウト。味見で美味かったので悔しさもひとしおでしたよ。明日はリベンジだ。

料理ばっかやってるけど、これが何故かストレス解消になるんだな。

まあ幸せな話だ。
.

DIとかAOPについて考えてみた

フレームワークって何だろう。最近Spring frameworkとかSeasarを試して考えさせられました。

(現時点での感想です。今後評価は変わるかもしれません)

なぜエンジニアはフレームワークを求めるのか。

開発生産性の向上とか、スキルの蓄積とか、そういう一般論やセールストークは脇においておきましょう。

フレームワークとは何か。フレームワークを使うものはフレームワークから使われる。ハリウッドの法則とも呼ばれます。これがフレームワークの本質のひとつです。フレームワークがメインで稼動し、開発者はフレームワークが呼ぶ部品を作る。この部品はフレームワークが定めるお作法に則って作成されなければなりません。つまり開発者はフレームワークが規定する型にハマったコードを書く必要がある。すなわち、フレームワークとは「アプリケーションこうあるべし」と定めるものです。しかも実装レベルで定めます。フレームワークに従わなければ稼動することもできません。

Strutsの目指したものは何だったか。もちろん、Webアプリケーションをシンプルに手早く開発することです。今思えば0.9時代のStrutsの抽象度は低く、またフレームワークにしては強制力が弱く柔軟に過ぎていました。しかしHTMLのインプットフォームをFormビーンにマッピングする発想(抽象化)と、画面遷移とBeanをXMLで定義する設計(疎結合)は非常に先進的であったと思います。

それではSpringの目指すものは何なのか。
Spring is a layered Java/J2EE application platform, based on code published in Expert One-on-One J2EE Design and Development by Rod Johnson (Wrox, 2002).

すなわちロッド・ジョンソン氏が提唱した、アプリケーションのデザインパターンの実装です。細かいところは省きますが、要するにSpringはStrutsとは異なり直接的な何らかの成果物を目標としているのではなく、より理念的なもの(概念と言ってもいいですし、思想、デザインと言ってもいい)を目指しているのです。それがSpringや(恐らくはSeasarも)をとっつきにくくしている理由のひとつであると思われます。

私はこのことが、つまり「目的が抽象的であること」がSpringフレームワークの問題だと思います。なぜか。私はそこにわれわれの業界に付きものの「手段と目的の逆転」が起こっていると見るからです。

なぜSpringを使うのか。Springを使うモチベーションとは何か。Springの位置づけからすると、それは「よいデザインパターンを実装するためである」ということになります。しかしちょっと待ってください。よいデザインパターンを実装するために、われわれはコードを書いているのでしょうか?コードを書くのは、何かやりたいことがあると思います。よい設計のためにコードを書く?そんなことがありえるでしょうか。

確かにオブジェクトをXMLから挿入できるDIや、横断的に割り込み処理をするAOPはよい発想だと思います。しかし、われわれはDIをするためにコードを書いているのではありません。DIやAOPは、あれば便利かもしれないもの。気の利いたスキマ商品なのではないか、と私は思います。例えるなら高級システム手帳。あったらうれしい。使いこなす人はカッコいいと思う。でも・・・別にいらないや。

Spring frameworkを開発する方の熱意と才能はすばらしいと思います。あんなものとても私には作れない。「よいフレームワークを提供して使って欲しい」「いいデザインのアプリケーションを作って欲しい」という熱意はまさに尊敬に値します。

でも私にはSpringは違和感がある。Data Injectionやるためにアプリ作ってんじゃねーや。と思ってしまう。

DBとエレガントに連携し、読みやすいコードで書きたいなら、HibernateやJPA(EJB3.0)を使うでしょう。

Webアプリを効率的に作ろうと思ったら、今の私なら躊躇なくJSFを選びます。

しかし、DIとAOPを使いたいから、XMLでデータや横断的処理を挿入したいからSpringを使うか?使わないと思うな。というかSpringとEJB3.0を連携させる体力の余裕があったら他に回すよね。

ということで、以上が現段階での私のSpring観です。否定的ってことで。

以上
.
(2008/12/15追記)
DIって本当に必要? - ひがやすを blogを見ると、Seasarのコミッタの方も真剣に自問しているようですね。偉い!偉いぞ!!
同じページで、普通の業務アプリケーションにはインターフェースはいらないんじゃないか、と言ってますがこれも偉い!!!よくぞ言った。必要だから使う、ではなく、ただ「インターフェースを使って設計しなきゃいけない」という強迫観念に襲われがちじゃありませんか?こういう問題提起・洞察は非常に重要です。
.

Seasar2を使ってみます(4)

次はAOPです。
わりあいすんなり行きました。

まず自前のIntercepterを準備します。
package test;

import org.aopalliance.intercept.MethodInvocation;
import org.seasar.framework.aop.interceptors.AbstractInterceptor;

public class MyInterceptor extends AbstractInterceptor {

private static final long serialVersionUID = 1L;

@Override
public Object invoke(MethodInvocation method) throws Throwable {

System.out.println("Before");
Object ret = method.proceed();
System.out.println("After");
return ret;

}

}

次はexample2.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
"http://www.seasar.org/dtd/components24.dtd">
<components>
<component class="java.util.ArrayList" name="list">
<initMethod name="add"><arg>"Hoge"</arg></initMethod>
<initMethod name="add"><arg>"Piyo"</arg></initMethod>
</component>

<component class="java.lang.String" name="autoBinding"/>
<component class="java.lang.String" name="useAnnotation"/>

<component class="test.Hello" name="hello">
<arg>list</arg>
<property name="autoBinding">"自動バインディング"</property>
<!-- ダメだった
<property name="useAnnotation">"アノテーション利用"</property>
-->
<aspect pointcut="sayHello">
<component class="test.MyInterceptor"
name="intercepterTest"/>
</aspect>
</component>
</components>

MainとHelloは変更なしです(AOPだからね)。

以上です。(はて、しかしこれをどう使うのかな)
.

Seasar2を使ってみます(3)

次はフィールドインジェクションです。publicフィールドへの自動バインディングとアノテーションを使ったバインディングを試します。

【結論】
自動バインディングは上手くいきましたが、アノテーションはダメでした。なぜだろう??
以下がメモです。
■まず"Binding"アノテーションが存在しない
■Webを調べると"Binding"ではなく標準の"Resource"を使ったほうがよいとのことだったので"javax.annotation.Resource"を使ってみたがダメ。
■@Resource(name="useAnotation")  → ダメ
■@Resource → ダメ
■privateじゃなくprotectedにしてみた → ダメ
■付属のライブラリjarを全部ビルドパスに追加してみた → ダメ
■ダメな理由は結局分からず(なんかすごい恥ずかしいミスをしている予感も)

【作業】
eclipse上でUTF8を入力するために、example2.diconの拡張子をxmlとしています。
(dicon拡張子だとデフォルトエンコーディングのMS932が適用されるらしく、日本語を入力するとSAXExceptionが出るため)

Helloクラス
package test;

import java.util.List;

import javax.annotation.Resource;

public class Hello {

public String autoBinding;

// ↓結局ダメ
@Resource
protected String useAnnotation;

private List<String> listNames;

public List<String> getListNames() {
return listNames;
}

public Hello(List<String> listNames) {
this.listNames = listNames;
}

public void sayHello() {
for (String name: listNames) {
System.out.println("Hello "+name+"! ");
}
System.out.println("Auto binding -> "+autoBinding);
// ↓nullが出るだけです
System.out.println("Annotation binding -> "+useAnnotation);
}

}

Mainクラス
package test;

import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.SingletonS2ContainerFactory;

public class Main {
public static void main(String args[]) {
SingletonS2ContainerFactory.setConfigPath("example2.xml");
SingletonS2ContainerFactory.init();
S2Container container = SingletonS2ContainerFactory.getContainer();
Hello hello = (Hello)container.getComponent("hello");
hello.sayHello();
}
}

example2.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
"http://www.seasar.org/dtd/components24.dtd">
<components>
<component class="java.util.ArrayList" name="list">
<initMethod name="add"><arg>"Hoge"</arg></initMethod>
<initMethod name="add"><arg>"Piyo"</arg></initMethod>
</component>

<component class="java.lang.String" name="autoBinding"/>
<component class="java.lang.String" name="useAnnotation"/>

<component class="test.Hello" name="hello">
<arg>list</arg>
<property name="autoBinding">"自動バインディング"</property>
<!-- ダメだった
<property name="useAnnotation">"アノテーション利用"</property>
-->
</component>
</components>

以上
.

Seasar2を使ってみます(2)

今度はHelloクラスのコンストラクタからデータを入れてみます(コンストラクタ・インジェクション)。

Helloからsetterを削除してコンストラクタでlistを受けるようにします。
package test;

import java.util.List;

public class Hello {

private List<String> listNames;

public List<String> getListNames() {
return listNames;
}

public Hello(List<String> listNames) {
this.listNames = listNames;
}

public void sayHello() {
for (String name: listNames) {
System.out.println("Hello "+name+"! ");
}
}

}

example2.diconを作成します。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
"http://www.seasar.org/dtd/components24.dtd">
<components>
<component class="java.util.ArrayList" name="list">
<initMethod name="add"><arg>"Hoge"</arg></initMethod>
<initMethod name="add"><arg>"Piyo"</arg></initMethod>
</component>
<component class="test.Hello" name="hello">
<arg>list</arg>
</component>
</components>

Main.javaを一応載せておきます。
package test;

import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.SingletonS2ContainerFactory;

public class Main {
public static void main(String args[]) {
SingletonS2ContainerFactory.setConfigPath("example2.dicon");
SingletonS2ContainerFactory.init();
S2Container container = SingletonS2ContainerFactory.getContainer();
Hello hello = (Hello)container.getComponent("hello");
hello.sayHello();
}
}

以上
.

Seasar2を使ってみます(1)

適切なTutorialやサンプルが見当たらないため躊躇していましたが、Spring(DIとしての)をいじってみてなんとなくDIフレームワークとやらが分かってきたので、その流れでSeasar2を触ってみました。

元にしたのは以下のページ。これ以上ないほどシンプルなサンプルです。

Human System: The First Seasar2 Example

まあ取っ掛かりとしては十分なんだけど、何も知らない人がこれ見ても厳しいでしょうな。つーかやってみようとすら思うかな?

で、Eclipseにエイヤとサンプルをつっこんで動作確認完了。クラスパスとかパッケージのパスとか基本的なところに注意しましょう(example.diconも暮らすパス上に配置します)。それ以外の注意点は以下の通りです。

▽稼動に必要なライブラリ
s2-framework-2.4.33.jar
commons-logging-1.1.jar
ognl-2.6.9-patch-20070908.jar
javaassist-3.4.ga.jar

▽XMLについて
example.diconから以下の二行を削除しないとエラーコード"ESSR0054"のSAXExceptionが発生して動きません。
</components>
<components>
すなわち
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
"http://www.seasar.org/dtd/components24.dtd">
<components>
<component class="java.util.ArrayList" name="list">
<initMethod name="add"><arg>"Naoki Takezoe"</arg></initMethod>
</component>
</components> <!-- この行と -->
<components> <!-- この行が不要。 -->
<component class="jp.sf.amateras.seasar.example.Hello" name="hello">
<property name="target">list</property>
</component>
</components>

当たり前だけどこれだけではふーん、という感じ。確かにDIだね。みたいな。でもちょっとSpringに比べてプリミティブ?

国産フレームワークということなので、それだけでも応援したいですね。

引き続き調べてみたいと思います。(何やろうかな)

.

2008年12月8日月曜日

偶然とチェ・ゲバラ

図書館で何気なく手に取ったチェ・ゲバラの「ボリビア日記」。チェ・ゲバラって結局誰なんだっけ?という程度の認識でナニゲに借りてしまったのが二週間ほど前でした。すなわち「ゲバラって誰」という認識だった。ゆえに借りてみた。実に薄っぺらいきっかけです。帰ってから妻に「チェ・ゲバラって誰だっけ」と聞くと「えーと、何かキューバとかカストロとかそんな感じ。それ以上のことは知らない」との答え。私の認識はそれ以前でした。

で、読んでみるとなかなか面白い。そうか。チェ・ゲバラってこんな人だったのか。理想に駆り立てられ、世界を変えようとゲリラ活動に身を投じた男。永遠の青春。偶像化されるのも理解できるなあ。日本の学園闘争とか60年代後半もこんな空気が流れてたんだろうな。

ま、それはさておいて「ボリビア日記」を読み始めてしばらくしていたら何やらチェ・ゲバラが映画化される模様。うん。シンクロした。たまにこういうことがありますね。

さて、この偶然をどう解釈するか。人間はなんにつけても意味を見出したい存在なのでいろいろパターンを考えてみる。

1.私が図書館でチェ・ゲバラの本を借りたがゆえに、まさにこの私をターゲットとして映画が作られた。
→ うん。オカシイ人の発想ですな。大体時系列でつじつまが合わない。映画作るのに数年はかかるはずだから。

2.チェ・ゲバラ再評価の雰囲気が以前からじわじわと高まっていた。映画の作成・公開もその流れに沿ったもの。私がたまたまチェ・ゲバラの本を手に取ったのはまったくの偶然。意味はない。
→ まあ、妥当な解釈でしょう。

3.小林多喜二の蟹工船のヒットやアメリカ流資本主義に対する批判的風潮に乗り、次はチェ・ゲバラ関連を売り込もうとして広告代理店がいろいろ布石を打っていた。私も無意識のうちにWebやテレビから触発されており、図書館で「ボリビア日記」を手に取ったのは私の自由意志ではなく、実は広告代理店のキャンペーンの結果でしかなかった。映画化もその流れ。
→ ありそうですが、ややオーバーな解釈。

というわけで、2がメインで3はそういうことも少しはあるかな、という感じですね。偶然っていうのも楽しいもんです。

私が一週間ほど前にカワハギについて書いた後、asahi.comでカワハギが出てきたのもまた別のシンクロ。カワハギの旬が冬だって理由はあるでしょうが、やっぱり楽しい。

以上
.

2008年12月6日土曜日

改めてSpringを試す(DIコンテナとしての)

これまでSpring MVCのついでにSpringの機能を使って気ましたが、改めてDIコンテナとしてのSpringコアを触ってみたいと思います。

大体"DIコンテナ"なんていう名称が胡散臭かったんだよね。またしてもJargonか。けっ。位にしか思っていなかった。でも理解しないままケチをつけるのもカッコ悪いので。

試したのは以下のチュートリアルです。こなれていて(特徴の示し方がシンプルで明確。分かりやすい)悪くないです。
http://www.atmarkit.co.jp/fjava/rensai3/springdi01/springdi01_2.html

【まとめ】

いや、Springって結構いいアイデアですね。要するにXMLを介してオブジェクトを連携させることで、疎結合やAOP(処理の注入。intercept。そんな感じ)を実現するフレームワークです。例によってSpringの本質をぜんぜん理解してなかったみたい(なんでわざわざHibernateやらStrutsと連携させる必要があるんだ。わずらわしい。くらいの認識でした。お恥ずかしい)。

以前「SpringはアーミーナイフでStrutsは鋭い刃物」と喩えました。このときはSpring MVCのみを念頭においていたのですが、Spring全体に対しても当たらずとも遠からずかもしれません。Springは便利で軽快な汎用ツール群だと思います。それにSpringで開発するのは結構楽しい(これって重要)。エンジニア魂をくすぐる。人気があるのも理解できます。

使い場所にもよるでしょうが、プロジェクトによっては有効ではないでしょうか。ただちょっとマニアックな気がするなあ。Springで作って良かった。と実感するケースはあまりなかったりして・・・(「再利用可能性」を志向して一生懸命デザインしても、実は誰も再利用しない、とかそんな感じ)

【おまけ】

チュートリアルの流れで少し拡張してみました。(ぜんぜん大したものではないですが一応)

■ SpringTest.java
package test;

import java.util.List;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {

/**
* @param args
*/
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"applicationContext.xml");
Target target = (Target) context.getBean("target");
System.out.println(target.getMessage());
Parent parent = (Parent) context.getBean("parent");
System.out.println("親:" + parent.getName());
List<Child> children = parent.getChildren();
for (Child child : children) {
System.out.println(child.getName());
}

}

}

■ applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="target" class="test.Target">
<property name="message">
<value>Hello World!</value>
</property>
</bean>
<bean id="oldest-son" class="test.Child">
<property name="name">
<value>長男</value>
</property>
<property name="no">
<value>1</value>
</property>
</bean>
<bean id="oldest-daughter" class="test.Child">
<property name="name">
<value>長女</value>
</property>
<property name="no">
<value>2</value>
</property>
</bean>

<bean id="parent" class="test.Parent">
<property name="children">
<list>
<ref bean="oldest-son" />
<ref bean="oldest-daughter" />
</list>
</property>
<property name="name">
<value>親</value>
</property>
</bean>
</beans>

■Parent.java

package test;

import java.util.List;

public class Parent {

private List<Child> children;
private String name;

public List<Child> getChildren() {
return children;
}
public void setChildren(List<Child> children) {
this.children = children;
}

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}

}

■Child.java

package test;

public class Child {
private String name;
private Integer no;

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getNo() {
return no;
}
public void setNo(Integer no) {
this.no = no;
}
}

以上
.

2008年12月5日金曜日

RAD7.5でWeb Serviceを作ってみる

ここ数年使われているjargonであるところのWeb service(英語圏では大文字でなく、かつ複数形のweb servicesみたいな記載をよく見ます)。せっかくなのでRAD7.5で作ってみました。個人的にはこのテクノロジーに対しては出た当初から半信半疑です。セールストークやらわけの分からない専門用語が飛び交い過ぎている。まあ私が見限ったのはずいぶん昔のことだし、食わず嫌いもよくないので、最近のweb sevicesがどうなってるのか見てみたいと思います。

TutorialはIBM dW "Create stand-alone Web services applications with Eclipse and Java SE 6, Part 1"。例によってIBM IDの登録が必要です。

注意点)
build.xmlを実行する箇所について。RAD上で右クリック、実行 > Antビルド としてもエラーとなります。コマンド・プロンプトからantを実行すると(別途Apache antの導入が必要)、問題なく実行できました。ちなみにCygwinでもダメです。"Program Files"以下に導入しているからかもしれません。

RunServer を実行する箇所について。実行するためには commons-logging.jar と commons-discovery.jar をビルドパスに追加する必要がありました。

WebサービスエクスプローラーからUDDIメインを開いた後の画面について。Tutorialとは違うようです。RAD7.5では、
レジストリー名:SayHelloService
照会 URL :http://localhost:8080/wsServerExample?wsdl
と入力します。

Webサービスエクスプローラーの使い勝手。ぜんぜん違いますね。いろいろいじってみましたが、とうとうgetGreetingsを呼び出すことができませんでした。

結論。大山鳴動ねずみ一匹、という感じです。まだまだだな。(<偉そう)

以上。
.

2008年11月30日日曜日

カワハギ、カワハギ

だって魚関係のWebサイトを見れば「カワハギといえば肝。肝は旨い」ばっかりですよ。@nifty:デイリーポータルでも取り上げられてるし。で、スーパーに行く途中のこと。しっかりカワハギ=肝=旨いと刷り込まれた私の脳は、目ざとく通りすがりの居酒屋のメニュー「カワハギの肝和え 800円」を発見。カワハギの肝和えか。高いな。旨いらしいな。

で、スーパーに行ってみたら魚コーナーに未処理のカワハギが。何かカワハギにインスパイアされまくりって感じで。魚コーナーのお兄ちゃんに「これ刺身いけますか」と聞くと「大丈夫ですよー」と威勢のよい答え。もう買うしかないでしょ。今日の晩御飯はカワハギの肝和えに決定、ということで、カワハギを一匹買って帰りました。子供には無理だから、メインというよりは大人向けのサブアイテム。

そして夕食。メインのコロッケを作るのに一生懸命になってしまい、カワハギのことはすっかり忘れてコロッケ食べてごちそうさまー。あー美味しかった。ってカワハギどうしよう。とよくあるパターン。

しょうがない、お父さんの夜のおやつはカワハギだ。と重い腰を上げて(それでも少し楽しみにしながら)カワハギの解体に向かいました。出刃でツノを切り落とし、口を切り落とす。皮をメリメリと剥いで、後頭部?こめかみ?あたりにざくっと包丁を入れて骨を断ち、そこから頭をもぎ取る。頭にくっついて肝が出てきました。他人のWebサイトで見ると肝だけきれいに取れているんですが、実際にはグダグダといろいろ張り付いている。写真はイメージで実物とは多少異なる場合がってやつですな。慎重に取り外したつもりでしたが少し崩れてしまった。肝は皿に乗せてしょうゆを掛けておきます。子供たちが「気持ち悪ーい」「これ何?」「苦玉ってどれ?」と騒ぎながら覗き込んでます。頼むから出刃包丁のそばに顔を近づけるな。それから身を三枚に下ろして(下手なので骨にしっかり肉がついてしまう・・・クヤシイ)から、中骨を避けてサク取りに。刺身包丁で小さく切って、肝と和えました。(子供はもう飽きてどっかで遊んでる。)

実家からもらった酒「越中五箇山 三笑楽」をコップにたっぷり一合分注ぎます。普段呑む酒と違って薄い琥珀色してて、香りとうまみが濃い。ちびりとすすって、デワデワ、とカワハギに箸をつける。

う~ん。見た目結構グロいっすよ。肝にはところどころ薄い血管がまとわりついてるし(多分解体が下手だった。もしくは取るか裏ごしするべきだったみたい)。身には薄皮がついてて(これも取り忘れた)、全体になんだかぴろぴろしてる感じ。

箸に肝をつけてちょっとなめてみると。うん。確かに旨い。結構旨い。生臭さは許容範囲。

肝にまみれた身を食べてみると、こりこりして旨い。これからは普通に好物って言うかも。カワハギの肝和え行っとく?みたいな。高いから自分で作るか。

高級そうな店で、小ざかしい器にちょこんと盛られてきたら、その旨さに感動してしまうかもしれません。形のそろわない刺身にところどころ赤い血管が張り付いている、その時点で負けだったかも。でも逆に言えばその見た目にもかかわらず旨いことは確か。

演出がまずかったな。あとは技術的な失敗もいくつかあったように思われました。以下が次回の課題。

・生臭さを取るため肝には酒を振りかけておくこと
・しょうがの絞り汁を入れたい
・血管は取り除いておくこと
・身から薄皮を取ること
・刺身は形をそろえて薄く切ること(いっそ軽く叩くか)

また食べようと思ってます。

以上。
.

2008年11月28日金曜日

偽善とか自己満足という言葉が気になっていた

気になりますよね。え?気にしたことなんかないって?そうか・・・私だけか。時々妙に「自己満足?偽善?」って考えこんじゃうんだけどな。

確かに青臭い同士学生の議論じゃあるまいし、大人になってしまえば「それはお前の自己満足だ」とか「偽善だ」などと決め付けられる機会も滅多にありませんね。それにたとえ他人から決め付けられるても「それがどーした」「ほっとけ」で済みます。オレが満足しようがしまいがお前の知ったことか。2ちゃんかどこかの標語にありましたが「しない善よりする偽善」。客観的な成果がでればいいじゃないか。だいたいお前には善が分かっているのか。お前はブッシュ大統領か、と。

そもそも、「自己満足」と「偽善」という言葉を平然と並べているが、関係あるのか、と。一括りにして語る意味があるのか。そんな声も聞こえてきそうです。

う~ん。そのとおりかもしれません。でも引っかかる。昔から幾度か反芻させられてきた言葉なんです。非難と若干の責める側の優越感を漂わせたこの言葉。「自己満足で終わってないか?」「それは偽善じゃないのか?」

なぜそれほどまでにこの言葉に引っかかるんだろう。ちょっと考えてみました。

両者の共通点って何だろう。

それはこれらの言葉が二つとも、その程度で満足していいのか、それでいいと思っているのか、と人に根底的に問いかける意図を持って発せられる、まずはそこが共通する点であるように思えます(言葉の裏にひそむ悪意や揶揄は考えないことにしましょう。ストレートに問いかけられたとします)。

お前の人生それでいいのか?大きなお世話だといえばその通りですが、私はふと足を止めて考えてしまう。俺はこれまで正しいことをしてきたのだろうか。これでよかったのか?でも次の瞬間にその問いを忘れてしまう。今日中に完了しなければならない仕事がある。あるいは子供が呼んでいる。一時的に私はその問いを忘れてしまう。でも忘れたつもりでもその問いは心の奥深くに沈殿し、再び浮かび上がる機会をじっと待っている。

お前の人生それでいいのか?実に青臭い問いかけです。あるいは人生を知らない浅い問いなのかもしれません。昔は常に自問していたような気がします。これでいいんだろうか。俺は何をしたいんだろうか。何をすべきなんだろうか。でも年を取るにつれてそんな問いは忘れていった。仕事も収入もあり、養うべき家族もいる。いまさら根本的に反省する必要などない。それより日々働いて家族と暮らすので精一杯。

希望や挫折や将来の不安で押しつぶされそうになりつつ、何ごとかを成し遂げたいと考えていたかつての自分。むやみに焦り、イライラとしながら周りを軽蔑し、でも何が出来るわけでもなく独り悶々と過ごしていた昔の"俺"。そんな"俺"が今の私を見たら何と言うだろうか。今の人生でいいのか。それでお前は本当に良かったのか。そんなふうに厳しく問い詰めてくるのかもしれません。

昔の自分と今の自分が顔を見合わせたとき、恥ずかしそうに去って行くのは今の"私"の方なのでしょうか。それとも昔の"俺"?

「自己満足」とか「偽善」という言葉を聞くと、かつての青臭い自分を思い出すような気がする。それがこの言葉が引っかかる理由なのかも知れません。

生きて行くために失われるもの。それは失われる必然性があったのか、それとも失われるべきではなかったのか。

死ぬときに自分の人生を肯定できれば、それでいい。そんな気がします。
.

RAD v7.5 試用版 を使ってJSFとEJB3.0・・・(11)

値をサーバ側で取得します。

1. ListBeanに選択された値を扱う変数と処理を追加します。
2. list.jspを編集します(変数の追加・送信ボタンの追加)。
3. faces-config.xmlに画面遷移定義を追加します。

1. ListBeanに選択された値を扱う変数と処理を追加
package test.beans;

import java.util.List;

import javax.ejb.EJB;

import test.MySample;
import test.MySampleSessionLocal;

public class ListBean {
private static final long serialVersionUID = 1L;

List<MySample> mySamples = null;
// Stringにしないとエラーが出ます。理由はよく分かりません・・・
private String selectedIndex = null;

public String getSelectedIndex() {
return selectedIndex;
}

public void setSelectedIndex(String selectedIndex) {
this.selectedIndex = selectedIndex;
}

@EJB(name="MySample")
private MySampleSessionLocal mySampleSession;

public List<MySample> getMySamples() {
// まあ二回くらい呼ばれたっていいでしょう
if (mySamples == null) {
try {
List<MySample> tmpList = mySampleSession.listMySamples();
setMySamples(tmpList);
} catch (Exception e) {
//
}
}
return mySamples;
}

private void setMySamples(List<MySample> mySamples) {
// 読み取り専用
this.mySamples = mySamples;
}

public String executeTest() {
System.out.println("Selected index -> " + selectedIndex);
return "success";
}

}

2. list.jspを編集
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><%@page
language="java" contentType="text/html; charset=windows-31j"
pageEncoding="windows-31j"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="/WEB-INF/tld/mysample.tld" prefix="custom"%>
<html>
<head>
<title>list</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-31j">
</head>
<body>
<h2>リスト MySample</h2>
<f:view>
<h:form>
<h:dataTable value="#{listBean.mySamples}" var="item" border="1">
<h:column>
<f:facet name="header">
<h:outputText value="Select" />
</f:facet>
<custom:radioButton id="myRadioId1" name="myRadioCol"
overrideName="true" value="#{listBean.selectedIndex}"
itemValue="#{item.myid}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="ID" />
</f:facet>
<f:verbatim>id: </f:verbatim>
<h:outputText value="#{item.myid}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="VAL" />
</f:facet>
<f:verbatim>value: </f:verbatim>
<h:outputText value="#{item.myFlag}" />
</h:column>
</h:dataTable>
<h:commandButton value="実行"
action="#{listBean.executeTest}" />
</h:form>
</f:view>
</body>
</html>

3. faces-config.xmlに画面遷移定義を追加
 <navigation-rule>
<from-view-id>list.jsp</from-view-id>
<navigation-case>
<from-outcome>success</from-outcome>
<to-view-id>list.jsp</to-view-id>
</navigation-case>
</navigation-rule>

以上。
.

RAD v7.5 試用版 を使ってJSFとEJB3.0・・・(10)

一覧表示した行を、ラジオボタンで選択できるようにします。
残念ながらカスタムタグを開発する必要があります。
(JSTLでもこの手の実装に苦労した記憶が・・・どうして標準の機能に無いんだろう??)

以下のページを参考にしました。というか、一部型のエラーが出たりしたので、変数をStringからObjectに変更してtoString()してますが、ほとんどそのまんまです。(感謝)

※RAD環境ではweb.xmlへのtaglib要素の追加は不要でした。web-app_2_5.xsdだから?よく分かってません。

Group radio buttons inside a JSF dataTable component
Write a custom JSF tag for enhancing radio button behavior
By Srijeeb Roy, JavaWorld.com, 02/27/07
(JAVAWORLD)
http://www.javaworld.com/javaworld/jw-02-2007/jw-02-jsf.html

ずいぶんとdeprecatedになってしまっているようですが、Webをダラダラ調べたところ、気にせず使うのが一番いいみたい。まだこれといった代替方法は確立されていないようです。

■準備
MySampleJSFの中にそのまま作ってもよいのですが、jarに分けたほうがカッコよいのでユーティリティ・モジュールプロジェクトとして作成します。

まずファイル > 新規 > その他 > 新規Javaユーティリティ・モジュールプロジェクト > "MySampleCTG" を作成。

MySampleJSF のプロパティを開き、Java EE モジュール依存関係 > Webライブラリータブ(J2EEモジュールではなく)を開いてMySampleCTGにチェックを入れます。

準備完了。

■カスタムタグの作成
以下のステップが必要です。結構大変ですね。

1. 以下の三つのクラスを作成します。
1-1. コンポーネント クラス(MySampleCTGプロジェクトに作成)
1-2. レンダー クラス(同上)
1-3. カスタム・タグ クラス(同上)
2. TLDファイルを作成します。(MySampleJSFプロジェクトに作成。MySampleCTGのMETA-INFではうまく行きませんでした)
3. faces-config.xmlにコンポーネント要素を追加します。(同上)
4. jspにタグを書きます。(同上)

1-1. コンポーネントクラス(UICustomSelectOneRadio)
package test.ctg;

import javax.el.ValueExpression;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;

public class UICustomSelectOneRadio extends UIInput {

// 1. 属性をクラス変数として定義
private String name = null;
private String overrideName = null;
private String styleClass = null;
private String style = null;
private String disabled = null;
private String itemLabel = null;
private Object itemValue = null;
private String onClick = null;
private String onMouseOver = null;
private String onMouseOut = null;
private String onFocus = null;
private String onBlur = null;

// 2. 動的に属性を取得するためのメソッドを定義
public String returnValueBindingAsString(String attr) {
// サンプルコードは一部Deprecatedだったので適宜変更しています。
ValueExpression valueExpression = getValueExpression(attr);
if (valueExpression != null) {
return valueExpression.getValue(this.getFacesContext()
.getELContext()).toString();
} else {
return null;
}
}

// 3. setter, getterを定義. getterでreturnValueBindingAsStringを使っている
// ので注意
public String getName() {
if (name != null) {
return name;
}
return returnValueBindingAsString("name");

}

public void setName(String name) {
this.name = name;
}

public String getOverrideName() {
if (overrideName != null) {
return overrideName;
}
return returnValueBindingAsString("overrideName");
}

public void setOverrideName(String overrideName) {
this.overrideName = overrideName;
}

public String getStyleClass() {
if (styleClass != null) {
return styleClass;
}
return returnValueBindingAsString("styleClass");
}

public void setStyleClass(String styleClass) {
this.styleClass = styleClass;
}

public String getStyle() {
if (style != null) {
return style;
}
return returnValueBindingAsString("style");
}

public void setStyle(String style) {
this.style = style;
}

public String getDisabled() {
if (disabled != null) {
return disabled;
}
return returnValueBindingAsString("disabled");
}

public void setDisabled(String disabled) {
this.disabled = disabled;
}

public String getItemLabel() {
if (itemLabel != null) {
return itemLabel;
}
return returnValueBindingAsString("itemLabel");
}

public void setItemLabel(String itemLabel) {
this.itemLabel = itemLabel;
}

public Object getItemValue() {
if (itemValue != null) {
return itemValue;
}
return returnValueBindingAsString("itemValue");
}

public void setItemValue(Object itemValue) {
this.itemValue = itemValue;
}

public String getOnClick() {
if (onClick != null) {
return onClick;
}
return returnValueBindingAsString("onClick");
}

public void setOnClick(String onClick) {
this.onClick = onClick;
}

public String getOnMouseOver() {
if (onMouseOver != null) {
return onMouseOver;
}
return returnValueBindingAsString("onMouseOver");
}

public void setOnMouseOver(String onMouseOver) {
this.onMouseOver = onMouseOver;
}

public String getOnMouseOut() {
if (onMouseOut != null) {
return onMouseOut;
}
return returnValueBindingAsString("onMouseOut");
}

public void setOnMouseOut(String onMouseOut) {
this.onMouseOut = onMouseOut;
}

public String getOnFocus() {
if (onFocus != null) {
return onFocus;
}
return returnValueBindingAsString("onFocus");
}

public void setOnFocus(String onFocus) {
this.onFocus = onFocus;
}

public String getOnBlur() {
if (onBlur != null) {
return onBlur;
}
return returnValueBindingAsString("onBlur");
}

public void setOnBlur(String onBlur) {
this.onBlur = onBlur;
}

// 4. restoreStateとsaveStateメソッドを実装します。
@Override
public void restoreState(FacesContext context, Object state) {
Object[] values = (Object[]) state;
super.restoreState(context, values[0]);
styleClass = (String) values[1];
style = (String) values[2];
disabled = (String) values[3];
itemLabel = (String) values[4];
itemValue = (String) values[5];
onClick = (String) values[6];
onMouseOver = (String) values[7];
onMouseOut = (String) values[8];
onFocus = (String) values[9];
onBlur = (String) values[10];
name = (String) values[11];
overrideName = (String) values[12];
}
@Override
public Object saveState(FacesContext context) {
Object[] values = new Object[13];
values[0] = super.saveState(context);
values[1] = styleClass;
values[2] = style;
values[3] = disabled;
values[4] = itemLabel;
values[5] = itemValue;
values[6] = onClick;
values[7] = onMouseOver;
values[8] = onMouseOut;
values[9] = onFocus;
values[11] = name;
values[12] = overrideName;
return (values);
}

// 5. getFamilyを実装します。faces-config.xmlにこの名前を指定します。
@Override
public String getFamily() {
return ("CustomSelectOneRadio");
}
}

次はレンダークラス(HTMLCustomSelectOneRadioRenderer)です。
package test.ctg;

import java.io.IOException;
import java.util.Map;

import javax.faces.component.UIComponent;
import javax.faces.component.UIData;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.Renderer;

public class HTMLCustomSelectOneRadioRenderer extends Renderer {

@SuppressWarnings("unchecked")
@Override
public void decode(FacesContext context, UIComponent component) {
if ((context == null) || (component == null)) {
throw new NullPointerException();
}
UICustomSelectOneRadio aUICustomSelectOneRadio = null;
if (component instanceof UICustomSelectOneRadio) {
aUICustomSelectOneRadio = (UICustomSelectOneRadio) component;
} else {
return;
}
Map map = context.getExternalContext().getRequestParameterMap();
String name = getName(aUICustomSelectOneRadio, context);
if (map.containsKey(name)) {
String value = map.get(name).toString();
if (value != null) {
setSubmittedValue(component, value);
}
}
}

public void setSubmittedValue(UIComponent component, Object obj) {

if (component instanceof UIInput) {
((UIInput) component).setSubmittedValue(obj);
}
}

private String getName(UICustomSelectOneRadio aUICustomSelectOneRadio,
FacesContext context) {

UIComponent parentUIComponent = getParentDataTableFromHierarchy(aUICustomSelectOneRadio);
if (parentUIComponent == null) {
return aUICustomSelectOneRadio.getClientId(context);
} else {
if (aUICustomSelectOneRadio.getOverrideName() != null
&& aUICustomSelectOneRadio.getOverrideName().equals("true")) {
return aUICustomSelectOneRadio.getName();
} else {
String id = aUICustomSelectOneRadio.getClientId(context);
int lastIndexOfColon = id.lastIndexOf(":");
String partName = "";
if (lastIndexOfColon != -1) {
partName = id.substring(0, lastIndexOfColon + 1);
if (aUICustomSelectOneRadio.getName() == null) {
partName = partName + "generatedRad";
} else
partName = partName + aUICustomSelectOneRadio.getName();
}
return partName;
}
}
}

private UIComponent getParentDataTableFromHierarchy(UIComponent uiComponent) {
if (uiComponent == null) {
return null;
}
if (uiComponent instanceof UIData) {
return uiComponent;
} else {
// try to find recursively in the Component tree hierarchy
return getParentDataTableFromHierarchy(uiComponent.getParent());
}
}

@Override
public void encodeEnd(FacesContext context, UIComponent component)
throws IOException {
if ((context == null) || (component == null)) {
throw new NullPointerException();
}

UICustomSelectOneRadio aUICustomSelectOneRadio = (UICustomSelectOneRadio) component;

if (component.isRendered()) {
ResponseWriter writer = context.getResponseWriter();

writer.write("<input type=\"radio\"");

writer.write(" id=\"" + component.getClientId(context) + "\"");
writer.write(" name=\"" + getName(aUICustomSelectOneRadio, context)
+ "\"");
if (aUICustomSelectOneRadio.getStyleClass() != null
&& aUICustomSelectOneRadio.getStyleClass().trim().length() > 0) {
writer
.write(" class=\""
+ aUICustomSelectOneRadio.getStyleClass()
.trim() + "\"");
}
if (aUICustomSelectOneRadio.getStyle() != null
&& aUICustomSelectOneRadio.getStyle().trim().length() > 0) {
writer.write(" style=\""
+ aUICustomSelectOneRadio.getStyle().trim() + "\"");
}
if (aUICustomSelectOneRadio.getDisabled() != null
&& aUICustomSelectOneRadio.getDisabled().trim().length() > 0
&& aUICustomSelectOneRadio.getDisabled().trim().equals(
"true")) {
writer.write(" disabled=\"disabled\"");
}
if (aUICustomSelectOneRadio.getItemValue() != null) {
writer.write(" value=\""
+ aUICustomSelectOneRadio.getItemValue().toString().trim() + "\"");
}
if (aUICustomSelectOneRadio.getOnClick() != null
&& aUICustomSelectOneRadio.getOnClick().trim().length() > 0) {
writer.write(" onclick=\""
+ aUICustomSelectOneRadio.getOnClick().trim() + "\"");
}
if (aUICustomSelectOneRadio.getOnMouseOver() != null
&& aUICustomSelectOneRadio.getOnMouseOver().trim().length() > 0) {
writer.write(" onmouseover=\""
+ aUICustomSelectOneRadio.getOnMouseOver().trim()
+ "\"");
}
if (aUICustomSelectOneRadio.getOnMouseOut() != null
&& aUICustomSelectOneRadio.getOnMouseOut().trim().length() > 0) {
writer
.write(" onmouseout=\""
+ aUICustomSelectOneRadio.getOnMouseOut()
.trim() + "\"");
}
if (aUICustomSelectOneRadio.getOnFocus() != null
&& aUICustomSelectOneRadio.getOnFocus().trim().length() > 0) {
writer.write(" onfocus=\""
+ aUICustomSelectOneRadio.getOnFocus().trim() + "\"");
}
if (aUICustomSelectOneRadio.getOnBlur() != null
&& aUICustomSelectOneRadio.getOnBlur().trim().length() > 0) {
writer.write(" onblur=\""
+ aUICustomSelectOneRadio.getOnBlur().trim() + "\"");
}

if (aUICustomSelectOneRadio.getValue() != null
&& aUICustomSelectOneRadio.getValue().equals(
aUICustomSelectOneRadio.getItemValue())) {
writer.write(" checked=\"checked\"");
}
writer.write(">");
if (aUICustomSelectOneRadio.getItemLabel() != null) {
writer.write(aUICustomSelectOneRadio.getItemLabel());
}
writer.write("</input>");
}
}
}

■カスタム・タグクラス(HTMLCustomSelectOneRadioTag)
package test.ctg;

import javax.faces.FactoryFinder;
import javax.faces.application.Application;
import javax.faces.application.ApplicationFactory;
import javax.faces.component.UIComponent;
import javax.faces.el.ValueBinding;
import javax.faces.webapp.UIComponentELTag;
import javax.faces.webapp.UIComponentTag;

public class HTMLCustomSelectOneRadioTag extends UIComponentELTag {

public HTMLCustomSelectOneRadioTag() {
name = null;
value = null;
styleClass = null;
style = null;
disabled = null;
itemLabel = null;
itemValue = null;
onClick = null;
onMouseOver = null;
onMouseOut = null;
onFocus = null;
onBlur = null;
overrideName = null;
}

public String getDisabled() {
return disabled;
}

public String getItemLabel() {
return itemLabel;
}

public String getItemValue() {
return itemValue;
}

public String getName() {
return name;
}

public String getOnBlur() {
return onBlur;
}

public String getOnClick() {
return onClick;
}

public String getOnFocus() {
return onFocus;
}

public String getOnMouseOut() {
return onMouseOut;
}

public String getOnMouseOver() {
return onMouseOver;
}

public String getOverrideName() {
return overrideName;
}

public String getStyle() {
return style;
}

public String getStyleClass() {
return styleClass;
}

public String getValue() {
return value;
}

public void setDisabled(String string) {
disabled = string;
}

public void setItemLabel(String string) {
itemLabel = string;
}

public void setItemValue(String string) {
itemValue = string;
}

public void setName(String string) {
name = string;
}

public void setOnBlur(String string) {
onBlur = string;
}

public void setOnClick(String string) {
onClick = string;
}

public void setOnFocus(String string) {
onFocus = string;
}

public void setOnMouseOut(String string) {
onMouseOut = string;
}

public void setOnMouseOver(String string) {
onMouseOver = string;
}

public void setOverrideName(String string) {
overrideName = string;
}

public void setStyle(String string) {
style = string;
}

public void setStyleClass(String string) {
styleClass = string;
}

public void setValue(String string) {
value = string;
}

protected void setProperties(UIComponent component) {
super.setProperties(component);
UICustomSelectOneRadio aUICustomSelectOneRadio =
(UICustomSelectOneRadio) component;
if (name != null) {
if (UIComponentTag.isValueReference(name)) {
aUICustomSelectOneRadio.setValueBinding("name",
getValueBinding(name));
} else {
aUICustomSelectOneRadio.getAttributes().put("name", name);
}
}
if (value != null)
if (UIComponentTag.isValueReference(value))
aUICustomSelectOneRadio.setValueBinding("value",
getValueBinding(value));
else
aUICustomSelectOneRadio.getAttributes().put("value", value);
if (styleClass != null)
if (UIComponentTag.isValueReference(styleClass))
aUICustomSelectOneRadio.setValueBinding("styleClass",
getValueBinding(styleClass));
else
aUICustomSelectOneRadio.getAttributes().put("styleClass",
styleClass);
if (style != null)
if (UIComponentTag.isValueReference(style))
aUICustomSelectOneRadio.setValueBinding("style",
getValueBinding(style));
else
aUICustomSelectOneRadio.getAttributes().put("style", style);
if (disabled != null)
if (UIComponentTag.isValueReference(disabled))
aUICustomSelectOneRadio.setValueBinding("disabled",
getValueBinding(disabled));
else
aUICustomSelectOneRadio.getAttributes().put("disabled",
disabled);
if (itemLabel != null)
if (UIComponentTag.isValueReference(itemLabel)) {
aUICustomSelectOneRadio.setValueBinding("itemLabel",
getValueBinding(itemLabel));
} else {
System.out.println("itemLabel=" + itemLabel);
aUICustomSelectOneRadio.getAttributes().put("itemLabel",
itemLabel);
}
if (itemValue != null)
if (UIComponentTag.isValueReference(itemValue))
aUICustomSelectOneRadio.setValueBinding("itemValue",
getValueBinding(itemValue));
else
aUICustomSelectOneRadio.getAttributes().put("itemValue",
itemValue);
if (onClick != null)
if (UIComponentTag.isValueReference(onClick))
aUICustomSelectOneRadio.setValueBinding("onClick",
getValueBinding(onClick));
else
aUICustomSelectOneRadio.getAttributes().put("onClick", onClick);
if (onMouseOver != null)
if (UIComponentTag.isValueReference(onMouseOver))
aUICustomSelectOneRadio.setValueBinding("onMouseOver",
getValueBinding(onMouseOver));
else
aUICustomSelectOneRadio.getAttributes().put("onMouseOver",
onMouseOver);
if (onMouseOut != null)
if (UIComponentTag.isValueReference(onMouseOut))
aUICustomSelectOneRadio.setValueBinding("onMouseOut",
getValueBinding(onMouseOut));
else
aUICustomSelectOneRadio.getAttributes().put("onMouseOut",
onMouseOut);
if (onFocus != null)
if (UIComponentTag.isValueReference(onFocus))
aUICustomSelectOneRadio.setValueBinding("onFocus",
getValueBinding(onFocus));
else
aUICustomSelectOneRadio.getAttributes().put("onFocus", onFocus);
if (onBlur != null)
if (UIComponentTag.isValueReference(onBlur))
aUICustomSelectOneRadio.setValueBinding("onBlur",
getValueBinding(onBlur));
else
aUICustomSelectOneRadio.getAttributes().put("onBlur", onBlur);
if (overrideName != null)
if (UIComponentTag.isValueReference(overrideName))
aUICustomSelectOneRadio.setValueBinding("overrideName",
getValueBinding(overrideName));
else
aUICustomSelectOneRadio.getAttributes().put("overrideName",
overrideName);
}

public ValueBinding getValueBinding(String valueRef) {
ApplicationFactory af = (ApplicationFactory) FactoryFinder
.getFactory("javax.faces.application.ApplicationFactory");
Application a = af.getApplication();
return a.createValueBinding(valueRef);
}

private String name;
private String value;
private String styleClass;
private String style;
private String disabled;
private String itemLabel;
private String itemValue;
private String onClick;
private String onMouseOver;
private String onMouseOut;
private String onFocus;
private String onBlur;
private String overrideName;

@Override
public String getComponentType() {
return "component.CustomSelectOneRadio";
}

@Override
public String getRendererType() {
return "renderer.CustomSelectOneRadio";
}
}

2. mysample.tldファイルです。
/WEB-INF/tldに置いてください。
<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">

<taglib>
<!-- ============== Tag Library Description Elements ============= -->
<tlib-version>0.03</tlib-version>
<jsp-version>1.2</jsp-version>
<short-name>Custom JavaServerFaces Framework Tag Library</short-name>
<uri>http://www.srijeeb.com/jsf/tags-html</uri>
<tag>
<name>radioButton</name>
<tag-class>test.ctg.HTMLCustomSelectOneRadioTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>name</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
<attribute>
<name>overrideName</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
<attribute>
<name>id</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
<attribute>
<name>value</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
<attribute>
<name>styleClass</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
<attribute>
<name>style</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
<attribute>
<name>disabled</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
<attribute>
<name>rendered</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>Boolean</type>
</attribute>
<attribute>
<name>itemLabel</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
<attribute>
<name>itemValue</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
<attribute>
<name>onClick</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
<attribute>
<name>onMouseOver</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
<attribute>
<name>onMouseOut</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
<attribute>
<name>onFocus</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
<attribute>
<name>onBlur</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
</tag>
</taglib>

重要:TLDファイルをMySampleCTG/src/META-INF配下に置いたらうまく動きませんでした。web.xmlとかJSPをいろいろいじってみたのですがダメでした。

エラー例)
com.ibm.ws.jsp.inputsource.JspInputSourceImpl JspInputSourceImpl Failed to create inputsource contextURL =[file:/C:/Documents and Settings/user_name/IBM/rationalsdp/workspace/MySampleJSF/WebContent/ relativeURL =[http://www.srijeeb.com/jsf/tags-html]
java.net.MalformedURLException
at java.net.URL.<init>(URL.java:601)
...

おそらくそれと関連してると思われるのですが、WASテスト環境の調子も悪くなりました。
[08/11/27 13:14:21:000 JST] 0000001e annotations E ArchiveInputStreamData iterate unable to open file iterator
java.util.zip.ZipException: ZipFile closed
...のようなエラーが毎回出るようになった・WASの「公開」プロセスが0%のまま進まない、など。

ユーティリティプロジェクトは動作が怪しいですね。はっきり言って使わないほうがいいと思います。(昔のWSADでも動作が怪しかった記憶があります)
重要:ClassNotFoundエラーが出たら、MySampleCTGフォルダを右クリック Java EE > EARライブラリの更新 をしたら直ることがあります。MySampleCTG配下を編集したら「EARライブラリの更新」を実行するようにしましょう。

3. faces-config.xmlにコンポーネント要素を追加します。
以下の要素を追加してください。
 <component>
<component-type>component.CustomSelectOneRadio</component-type>
<component-class>test.ctg.UICustomSelectOneRadio
</component-class>
<component-extension>
<component-family>CustomSelectOneRadio</component-family>
<renderer-type>renderer.CustomSelectOneRadio</renderer-type>
</component-extension>
</component>
<render-kit>
<renderer>
<component-family>CustomSelectOneRadio</component-family>
<renderer-type>renderer.CustomSelectOneRadio</renderer-type>
<renderer-class>test.ctg.HTMLCustomSelectOneRadioRenderer
</renderer-class>
</renderer>
</render-kit>

4. jspにタグを書きます。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><%@page
language="java" contentType="text/html; charset=windows-31j"
pageEncoding="windows-31j"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="/WEB-INF/tld/mysample.tld" prefix="custom"%>
<html>
<head>
<title>list</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-31j">
</head>
<body>
<h2>リスト MySample</h2>
<f:view>
<h:dataTable value="#{listBean.mySamples}" var="item" border="1">
<h:column>
<f:facet name="header">
<h:outputText value="Select" />
</f:facet>
<custom:radioButton id="myRadioId1" name="myRadioCol"
overrideName="true" value="#{item.myid}"
itemValue="#{item.myid}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="ID" />
</f:facet>
<f:verbatim>id: </f:verbatim>
<h:outputText value="#{item.myid}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="VAL" />
</f:facet>
<f:verbatim>value: </f:verbatim>
<h:outputText value="#{item.myFlag}" />
</h:column>
</h:dataTable>
</f:view>
</body>
</html>s

以上です。表示だけはできるようになったはず。
(やれやれ。何で標準機能にしないんだろう・・・)

次回、radioボタンで送信されたデータを取得します。
.