UIEvolutionのラボで公開されているRecordsetについて考察 ソースコードにコメントで解説(2006/07/07)
今回はUIEvolution社から公開されたデータバインディングサンプルのソースコードを解説する。当ブログでも何度か部分的な解説をしてきたが、これから何回かで自分用のものを作る際の修正点を含めて解説していく予定だ。予定は未定だが。
※このエントリーは初めて本ブログに来た方が読むには敷居が高い。まずは、初心者向けまとめページに目を通すことをおすすめする。
ちなみに以前、当ブログで解説したエントリーはこちら
「Zipcode converter app」で学ぶ その1 _link()の使い方
ソースコードはもちろんUIEvolution開発者のブログ
UIEvolution Developer Blog.
http://www.uievolution.com/blogs/developer/
にて以前公開された、
UIEvolution Developer Blog.: Databinding
http://www.uievolution.com/blogs/developer/2006/04/databinding.html
を活用する。
まず重要なファイルは llmodel.ujms というものだ。このファイルでサーバーからのデータをパースする。バインドするデータのモデルを変更したらこのファイルを変更する必要がある。ここを正しく書き換えることさえできれば、UIEから公開されたものを使いこなせるようになったと言えるだろう。
では、いきなりだが llmodel.ujms のソースコードを貼り付け、コメントにてその解説を入れる。
※コメントがうまく緑色になっていない部分があるが、ご容赦頂きたい。
<?xml version="1.0"?>
<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.5//EN" ""[
<!ENTITY MAX_LIST_COUNT "50">
<!ENTITY % modelconsts SYSTEM "llmodelconsts.ent">
%modelconsts;
<!ENTITY % recordset SYSTEM "recordset.ent">
%recordset;
<!ENTITY HPOST "0">
<!ENTITY RECORDSET_HANDLE_COUNT "2">
<!-- uiengineda.blogs.com お、jsp,perl,ruby,php版があるのね。 -->
<!--
<!ENTITY LOCALHOST_SERVLET_CONTEXT "http://localhost:8080/rsdemo/">
<!ENTITY POST_URL "ziptocity.jsp">
<!ENTITY LOCALHOST_SERVLET_LANGUAGE "Java recordset compiler">
<!ENTITY LOCALHOST_SERVLET_CONTEXT "http://lab.uievolution.com/cgi-bin/">
<!ENTITY POST_URL "ziptocity.pl">
<!ENTITY LOCALHOST_SERVLET_LANGUAGE "Perl recordset compiler">
<!ENTITY LOCALHOST_SERVLET_CONTEXT "http://lab.uievolution.com/cgi-bin/">
<!ENTITY POST_URL "ziptocity.rb">
<!ENTITY LOCALHOST_SERVLET_LANGUAGE "Ruby recordset compiler">
-->
<!ENTITY LOCALHOST_SERVLET_LANGUAGE "PHP recordset compiler">
<!-- <!ENTITY LOCALHOST_SERVLET_CONTEXT "http://lab.uievolution.com/rs/"> -->
<!ENTITY LOCALHOST_SERVLET_CONTEXT "../../../rs/">
<!ENTITY POST_URL "ziptocity.php">
<!-- ここの定数もモデル独自の実装となるので適宜書き換える -->
<!-- zipcode to city/state recordset data format -->
<!ENTITY POST_HEADER_RECORD "0">
<!ENTITY POST_HEADER_SIGNATURE_VALUE "post">
<!ENTITY POST_LIST_SIZE_RECORD "1">
<!ENTITY POST_STATUS_RECORD "2">
<!ENTITY POST_CITYSTATE_RECORD "3">
]>
<!--
uiengineda.blogs.com
サーバー側のデータとバインドするためのモデル
モデルを変更する場合にはこのソースコードを変更して対応する
重要なのはたぶんこの辺↓
<state var="RecordSet.sEvents" index="&HPOST;">
インデントがばらばらだが、しばらく気にしないで頂戴な。TODO 修正
強制的に整形してくれるEmeditorのプラグインなかったかな? TODO 探す
-->
<!--
llmodel.ujms
Copyright (c) 2006, UIEvolution, Inc.
Convert zipcode to city/state, client side.
-->
<ujml>
<partition>
<state-machines>
<state-machine name="LLModel">
<state-variables>
<!-- load state notification/completion -->
<!-- postScoreResponse -->
<state-var name="sPostResponseLoaded" type="int" visibility="public"/>
</state-variables>
<variables>
<var name="mstServletContext" type="string"/>
<var name="mUserCity" type="string"/>
<var name="mUserState" type="string"/>
<var name="mbPosted" type="boolean"/>
<!-- high score data -->
<var name="mcHighScores" type="int"/>
<var name="mstHighScoreDisplayString" type="string" size="&MAX_LIST_COUNT;"/>
<var name="mLoadHandle" type="int" size="&RECORDSET_HANDLE_COUNT;"/>
</variables>
<functions>
<!-- ここから共通関数 特に変更の必要無し -->
<!-- set different servlet context for testing -->
<function name="setServletContext" type="void" visibility="public">
<parameters>
<var name="st" type="string"/>
</parameters>
<script>
mstServletContext = st;
</script>
</function>
<function name="getServletContext" type="string" visibility="public">
<return><eval>mstServletContext</eval></return>
</function>
<function name="getServerLanguage" type="string" visibility="public">
<return><eval>"&LOCALHOST_SERVLET_LANGUAGE;"</eval></return>
</function>
<!-- revert to built in default servlet context -->
<function name="resetServletContext" type="void" visibility="public">
<script>
setServletContext("");
</script>
</function>
<!-- ここまで共通関数 -->
<!-- ここからモデル毎に書き換えること! -->
<!-- score posting function -->
<function name="zipLookup" type="void" visibility="public">
<parameters>
<var name="stZip" type="string"/>
</parameters>
<variables>
<var name="stParams" type="string"/>
<var name="stServletContext" type="string"/>
</variables>
<script>
if (_strlen(stZip) == 0)
{
stZip = "98052";
}
else
{
mUserCity = stZip;
}
stParams = _strcat("&uid=", stZip);
stServletContext = "&LOCALHOST_SERVLET_CONTEXT;";
if (_not(_streq(mstServletContext, "")))
{
stServletContext = mstServletContext;
}
mLoadHandle[&HPOST;] = RecordSet.openEx("&HPOST;",
_strcat(stServletContext, "&POST_URL;"),
stParams, true);
_clear_state(sPostResponseLoaded);
sPostResponseLoaded = &LLMODEL_LOAD_PENDING;;
</script>
</function>
<function name="getUserCity" type="string" visibility="public">
<return><eval>mUserCity</eval></return>
</function>
<function name="getUserState" type="string" visibility="public">
<return><eval>mUserState</eval></return>
</function>
<function name="getPostStatus" type="boolean" visibility="public">
<return><eval>mbPosted</eval></return>
</function>
<!-- ここまで -->
</functions>
<states>
<!-- ああ、こういうコードになっちゃうのねやっぱり 汎用的なものだからしょうがないか -->
<!--
uiengineda.blogs.com
ここ以降はモデル独自の実装になるので毎回書き換えないといけない
ポインタの移動を適切にやる必要があるので、サーバー側で生成されるデータの仕様を理解している必要有り
その辺を隠蔽できるとスマート
-->
<!-- record set event catching -->
<state var="RecordSet.sEvents" index="&HPOST;">
<transition value="0">
<variables>
<var name="iLoadStatus" type="int"/>
<var name="iPostStatus" type="int"/>
<var name="cPostListItems" type="int"/>
</variables>
<script>
<!-- たぶん_link()が成功するとここに来る by uiengineda-->
iLoadStatus = &LLMODEL_LOAD_FAILED;; // うまくデータロードできたかどうかのフラグを「失敗」で初期化
mbPosted = false;
if (RecordSet.moveTo(mLoadHandle[&HPOST;], &POST_HEADER_RECORD;) == mLoadHandle[&HPOST;])
{
// 先頭のフィールドの値が所定の値"post"と同じかどうかチェック(今のところ何のためにやっているか不明)
if (_streq(RecordSet.getField(mLoadHandle[&HPOST;], 0), "&POST_HEADER_SIGNATURE_VALUE;"))
{
// レコードのサイズが入っているところまでポインタを移動?
RecordSet.moveTo(mLoadHandle[&HPOST;], &POST_LIST_SIZE_RECORD;);
// レコードのサイズを取得
cPostListItems = _string_to_int(RecordSet.getField(mLoadHandle[&HPOST;], 0));
if (cPostListItems >= 1)// サイズが1以上
{
// ステータスが入っているところにポインタを移動
RecordSet.moveTo(mLoadHandle[&HPOST;], &POST_STATUS_RECORD;);
// データを取得してtrueかどうか判定、trueなら上手く読めたということか?
if (_streq(RecordSet.getField(mLoadHandle[&HPOST;], 0), "true"))
{
mbPosted = true;
}
}
// サイズが2の場合
if (cPostListItems == 2)
{
// 州と町のデータが入っているところにポインタを移動
RecordSet.moveTo(mLoadHandle[&HPOST;], &POST_CITYSTATE_RECORD;);
// 町の値
mUserCity = RecordSet.getField(mLoadHandle[&HPOST;], 0);
// 州の値
mUserState = RecordSet.getField(mLoadHandle[&HPOST;], 1);
}
// 成功しましたとさ
iLoadStatus = &LLMODEL_LOAD_SUCCEEDED;;
}
else
{
iLoadStatus = &LLMODEL_LOAD_FAILED;;
} <!-- end if signature checking -->
}
RecordSet.close(mLoadHandle[&HPOST;]);
_clear_state(sPostResponseLoaded);
sPostResponseLoaded = iLoadStatus;;
</script>
</transition>
<transition value="1">
<!-- underlying error preventing a recordset retrieval -->
<script>
RecordSet.close(mLoadHandle[&HPOST;]);
_clear_state(sPostResponseLoaded);
sPostResponseLoaded = &LLMODEL_LOAD_FAILED;;
</script>
</transition>
</state>
</states>
</state-machine>
</state-machines>
</partition>
</ujml>
コメント