2008-07-01

mscgen

Trac に Graphviz の調べものをしていた時に見つけた mscgen というものがあるみたいです。


A Message Sequence Chart GENerator ということでシーケンス図を dot 記法に似た文字情報から生成できるようです。未導入。あとでやってみるときの備忘録。

考えを整理するときや人に説明するときにシーケンス図は、とても有用。これなら、元図が行方不明になったりして散らかることもないし、修正もその場で可能。是非、導入したいな。

2008-05-22

Trac に Graphviz

ダイアグラムをチケットに貼りたいときに、OpenOffice Draw を使ってましたがノード数が少ない簡単なものの時は面倒でした。整形済みテキストマークアップ {{{...}}} で ASCII アートすると、今度は修正が大変。

TracGraphviz が使える GraphvizPlugin があることは知っていたけど、一度挑戦してうまく行ってなかったが今回、再チャレンジで成功。なんのことは無い、「エラーメッセージをよく読みましょう」と、いう類のことでした。

こんな環境です。

まず、Graphviz を単体で動くようにしておきます。RHEL 5 用のパッケージを使いました。

  • graphviz-2.16.1-1.el5.i386.rpm
  • graphviz-gd-2.16.1-1.el5.i386.rpm

GraphvizPlugin では、PNG 出力を使うため graphviz-gd が必要だったことが最初の躓き。

echo "digraph G {Hello->World}" | dot -Tpng >hello.png


これが、動作すれば OK だと思う。



次に GraphvizPlugin 。ここからダウンロードした、graphvizplugin.zip を展開して



# cd graphvizplugin/0.10
# python setup.py bdist_egg
running bdist_egg
~~~
~~~
creating 'dist/graphviz-0.6.11-py2.4.egg' and adding 'build/bdist.linux-i686/egg' to it
removing 'build/bdist.linux-i686/egg' (and everything under it)

# python setup.py install
running install
~~~
~~~
Installed /usr/lib/python2.4/site-packages/graphviz-0.6.11-py2.4.egg
Processing dependencies for graphviz==0.6.11
Finished processing dependencies for graphviz==0.6.11


これで、サイトワイドにインストールできたので httpd 再起動。



Trac の WebAdmin で Plugins に graphviz 0.6.11 が現れるので enabled して、Apply changes 。



trac.ini に 以下追加



[graphviz]
cmd_path = /usr/bin
cache_dir = /キャッシュ/への/フルパス
cache_manager = yes
cache_max_size = 10000000
cache_min_size = 5000000
cache_max_count = 2000
cache_min_count = 1500


cache_dir は、web サーバーが書き込めるディレクトリにする。これで、以下のマークアップで日本語を含むダイアグラムが表示されました。



{{{
#!graphviz
digraph sample {
node [fontname="/usr/share/fonts/japanese/TrueType/sazanami-gothic.ttf"];
こんちわ -> みなさん;
}
}}}

2008-04-24

dojo.io.bind ではない?

現時点での最新版 Dojo Toolkit 1.1.0 を使ってます。
情報は少なくないのですが実際にやってみると、どうもかみ合わないと思ったら、

Dojo Porting Guide: 0.4.x to 0.9

0.9 以降と 0.4 以前のリリース間では、後方非互換とのこと。 dojo.io.bind については、 IO Transports [AJAX] にあるように dojo.xhrXXX に変更になったみたいです。

では、HTML フォームの submit を抑止し、XmlHttpRequest する実験の続き

以下のようなコードになりました。

    function receiveData(response, ioArg) {
}

function onSubmitTestForm() {
var testFormNode = dojo.byId('TestForm');
var deffered = dojo.xhrPost({
url: testFormNode.action + '.json',
handleAs: 'json',
load: receiveData,
form: testFormNode});
}

function init() {
var formWidget = dijit.byId('TestForm');
dojo.connect(formWidget , 'onSubmit', 'onSubmitTestForm');
}

dojo.addOnLoad(init);
...
...
<form id="TestForm" onsubmit="return false;" action="..." method="post" dojotype="dijit.form.Form">



  1. form タグの onsubmit で return false し、送信抑止。


  2. ページの onLoad 時に init 関数内でフォームの onSubmit イベントに onSubmitTestForm を接続


  3. フォームの送信時に onSubmitTestForm が実行され、フォームの内容を伴った Post リクエストが行われる。


  4. レスポンスを受け取ると、receiveData が実行される。



と、いう動作を firebug と デバッガ で確認できました。



嵌ったところが一箇所あって、




  • dojo.connect() では、第1引数に dijit.byId() で取得したオブジェクト


  • dojo.xhrPost() の form 引数には、dojo.byId() で取得した DOM node



でした。dojo.xhrPost() に、dijit.byId() を使うと、form.getAttributeNode is not a function なんてのが firebug のエラーに捕捉されてました。

2008-04-22

dijit.form.Form submit 抑止はどうするの?

Dojo Toolkit 1.1.0 でのお話。

dojo の connect 機構を使ってやってみた。

    function onSubmitTestForm() {
return false;
}

function init() {
var formTest = dijit.byId('TestForm');
dojo.connect(formTest, 'onSubmit', 'onSubmitTestForm');
}

dojo.addOnLoad(init);


これでは、フォームは送信されてしまう。



オフィシャルの trac に Broken Backwards compatibility for dijit.form.Form と、いうチケットを見つけた。まさにこのことについてのやり取り。 接続からのリターン値は、無視される。オーバーライドする必要がある。とのこと。オーバーライド?


他にも、いくつか同様のチケット(#6280 とか)上がってて、斜め読みした感じでは、



<form dojoType="dijit.form.Form" onSubmit="return false;" id="TestForm" method="post" action="...">


こうすると、リクエストは送信されず、onSubmitTestForm が実行されました。onSubmit="dojo.stopEvent(arguments[0]);" でも、同じ。何が違うんだろう?



とりあえず onSubmitTestForm から dojo.io.bind してみます。

2008-04-16

PHP で Excel 2003 の SummaryInformation を取得したい。

SummaryInformation は、エクスプローラで xls ファイルを右クリックしてコンテキストメニューの「プロパティ」を開き、「概要」タブなどに表示されている情報です。ワークシートの内容にアクセスするものは、いくつかヒットしました。

  • 茶漬けブログさんの Excel_Peruser
    ワークシートが読めます。ちょっと使ってみた範囲では完璧でした。素晴らしい。 Excel_Reviser と組合わせて既存の Excel ファイルをテンプレートとして使えます。
  • PHPExcel
    Excel 2007 (.xslx)の読み込みは、できるらしい。PHPExcel_DocumentProperties->getTitle など、それらしい実装があります。Excel 2007 持ってないから、チェックできない。
  • PHP-ExcelReader
    ワークシートが読める。
  • PEAR::Spreadsheet_Excel_Writer
    ワークシートの書き込みができるらしい。

ワークシートがやっぱりメインだよな。その他の資料としては、

1日やって、だめだったらあきらめるつもりで PHP-ExcelReader を使ってダンプを出して眺めたりしてました。それらしい、箇所はあるのですがたどり着く方法がなかなかわかりませんでした。そろそろ、見切りをつけようと思っていたところで、jakarta になんかあったことを思い出して、探してみると The Apache POI Project になってました。いつの間にかトッププロジェクトに昇格してる。

で、PropertySet 辺りの実装を参考に改めてダンプを見てみると、

「読めるっ!!読めるぞぉ」(ラピュタのムスカ風に)

甚だ、中途半端ですが必要な部分のみ parse できるだけの PHP 実装をしてみました。

    function parseProperty($data){
$offset = 0;
$result = array();
$result['byteOrder'] = GetInt2d($data, $offset);
$offset += 2;
// 以下は、Intel little endian きめうち
$result['format'] = GetInt2d($data, $offset);
$offset += 2;
$result['osVersion'] = GetInt4d($data, $offset);
$offset += 4;
$result['classID'] = unpack('C*', substr($data, $offset, 16));
$offset += 16;
$result['numSections'] = GetInt4d($data, $offset);
$offset += 4;
// section を取得
for ($secidx = 0 ; $secidx < $result['numSections'] ; $secidx++) {
$section = array();
$section['formatID'] = unpack('C*', substr($data, $offset, 16));
$offset += 16;
$section['offset'] = GetInt4d($data, $offset);
$offset += 4;
$propTop = $propTblOffset = $section['offset'];
$section['size'] = GetInt4d($data, $propTblOffset);
$propTblOffset += 4;
$section['numProperties'] = GetInt4d($data, $propTblOffset);
$propTblOffset += 4;
// UTF-16 の可能性もあるようだが今のところ SHIFT-JIS きめうち
// 以下の propertyID = 1 がコードページなので対応は可能じゃないかな。
$srcenc = 'SJIS-win';
for($propidx = 0 ; $propidx < $section['numProperties'] ; $propidx++) {
$property = array();
$property['propertyID'] = GetInt4d($data, $propTblOffset);
$propTblOffset += 4;
$property['propertyOffset'] = GetInt4d($data, $propTblOffset);
$propTblOffset += 4;
$propOffset = $propTop + $property['propertyOffset'];
$property['propertyType'] = GetInt4d($data, $propOffset);
$propOffset += 4;
// ここで、propertyType (Variant型)ごとの値取得
switch($property['propertyType']) {
case 2: // VT_I2 = 2 byte signed int.
$property['propertyLength'] = 2;
$property['propertyValue'] = GetInt2d($data, $propOffset);
$propOffset += $property['propertyLength'];
break;
case 30: // VT_LPSTR = null terminated string.
$property['propertyLength'] = GetInt4d($data, $propOffset);
$propOffset += 4;
$strval = mb_convert_encoding(
substr($data, $propOffset, $property['propertyLength']),
"UTF-8", $srcenc);
$property['propertyValue'] = str_replace("\x00", "", $strval);
$propOffset += $property['propertyLength'];
break;
case 64: // VT_LPSTR = null terminated string.
$property['propertyLength'] = 8;
$lowWord = GetInt4d($data, $propOffset);
$propOffset += 4;
$highWord = GetInt4d($data, $propOffset);
$propOffset += 4;
// EPOC の違いを変換。
// 1601-01-01 00:00:00 (Windows EPOC/100nsec)
// -> 1970-01-01 00:00:00 (unix EPOC/1sec) に変換
$unixTime = $highWord * 4294967296 + $lowWord;
$unixTime = floor($unixTime / (1000 * 1000 * 10));
$unixTime -= 11644473600;
$property['propertyValue'] = date('Y/m/d H:i:s', $unixTime);
break;
}
$section['properties'][$propidx] = $property;
}
$result['sections'][$secidx] = $section;
}
return $result;
}

これで、propertyID = 2 の propertyValue にタイトルの文字列が取得できます。日付関係も問題ないよう。手持ちのファイルでは、概ね OK でした。

もっと、効率的に書けるでしょうし、エンコーディングのきめうちや未対応の Variant 型など突込みどころも多いですが、後で必要になったらがんばるかもしれません。