たぷつきません

おなかがでてきた。もうたぷついてるやん。

Tapestryマルチブラウザ対応

 TapestryはLocaleでテンプレートを変えられる(プロパティファイルのようにね)けど、同じような仕組みでUser-Agentごとにテンプレートが変えられるようにしてみた。

?xml version="1.0" encoding="UTF-8" ?>
<template-config>
    <user-agent-mapping>
        <default suffix=""/>
        <match pattern="UP.Browser" suffix="_cf"/>
        <match pattern="DoCoMo"     suffix="_cf"/>
        <match pattern="J-PHONE"    suffix="_cf"/>
        <match pattern="ASTEL"      suffix="_cf"/>
        <match pattern="DDIPOCKET"  suffix="_cf"/>
    </user-agent-mapping>
</template-config>

 ↑こんな感じの設定ファイルをtemplate-config.xmlという名前でWEB-INF直下に置いておくことで、pattern属性(といっても正規表現じゃなく只のindexOfだったりする)にヒットしたら、ページ(またはコンポーネント)テンプレートのファイル名は拡張子の前にsuffixが付くものを自動選択するというもの。例えば Hoge.pageがあったとしたら、上記設定ファイルに従えば、Hoge_cf.html というのが携帯用となる。
 template-source-delegate extentionでDefaultTemplateSourceを参考にしてUserAgentTemplateSourceなるITemplateDelegateの実装を作ることで、まあまずは実現できた。
 しかし一つ問題があった。patternにマッチしなかったUser-Agentの場合に選択する<default>のテンプレートの扱い。defaultの suffixが "_default" などであればdelegateでも問題なく動くが、「suffixがなし」の場合は、そもそもtemplate-source-delegateに制御が渡らない。これはTapestryの標準のEngineが作成するITemplateSourceクラス、DefaultTemplateSourceの動作がそうなっていたのだ。
 しかたないので DelegateFirstTemplateSourceというクラスを作り(これがDefaultTemplateSourceのほとんどコピーというのが悲しい)、先にdelegateを評価するようにして、別途用意したEngineのcreateTemplateSourceから生成するしかなかった。「suffixをなし」にしたい最大の理由は、Spindleの評価不全を解決したかったためだ。ちなみに Hoge_cf.html はHoge_cf.pageが無いことをSpingleが察知して赤×が付けられちゃいます。悲しい。
 さて、これを実際の仕事に使おうとして重要なことを忘れていたことに気づいた。携帯のHTMLとPCでのHTMLのエンコーディングの問題だ。PC用に開発していたのは全てUTF-8にしちゃっていたのだ。Win、Mac機種依存文字を気にする必要がないし、SJIS-MS932問題による文字化け対策も気にしなくていいんじゃないってことで。Postgres内のDBもUNICODEで定義したし相性が良いだろう(ってほとんど関係ないがJavaからの文字列が変換されずに格納されるのは良い)と判断したのだ。
 しかし携帯のブラウザはDOCOMOを初めとして皆SJIS(MS932)中心だ。うーんなんとか共存できないかな…と思いながら、encoding属性なるものを、<default>、<match>それぞれに付加して対応しようと考えてみました。

<?xml version="1.0" encoding="UTF-8" ?>
<template-config>
    <user-agent-mapping>
        <default encoding="UTF-8" suffix=""/>
        <match pattern="UP.Browser" encoding="MS932" suffix="_cf"/>
        <match pattern="DoCoMo"     encoding="MS932" suffix="_cf"/>
        <match pattern="J-PHONE"    encoding="MS932" suffix="_cf"/>
        <match pattern="ASTEL"      encoding="MS932" suffix="_cf"/>
        <match pattern="DDIPOCKET"  encoding="MS932" suffix="_cf"/>
    </user-agent-mapping>
</template-config>

 しかしよく調べると、テンプレート主導でcharsetを変えることはキツイのです。Tapestryのorg.apache.tapestry.template-encodingでは、コンポーネント/ページ/アプリケーションのspecification側で規定するのであって、テンプレート側から決定することができないんです。更に出力のエンコーディング(org.apache.tapestry.output-encoding)なんて、アプリケーションに対して1種類しか定義できない!!
うーーん、これでは、携帯はShift_JIS、PCはUTF-8と分けられない……
 入力エンコーディングに対しては、UserAgentTemplateSourceがInputStreamからencoding属性を使って読み出すようにした。問題は出力エンコーディングの方。
 これにはBasePageを継承したRawSupportPage(元は、まさたかさんが作ったもの)のgetOutputEncodingのoverrideで、自身のComponentTemplateを調べて、UserAgentTemplateSourceの作った、UserAgentComponentTemplateの場合だけ、encodingを渡してもらい決定するようにした。Pageに頼るのがいまいちなんですけど、このRawSupportPageは漢字コードもエスケープせずに出力します。(普通だと #32138 などコードにエンコードされちゃいます)なのでちょうど良いと言えばちょうど良いんですけどね。
 そんな感じでうまくいきました!やったー!