2009年12月5日土曜日

XSLT で直積

直積を求める XSLT を書いてみる。SQL の SELECT で FROM に複数テーブルを書いて JOIN せずに置くと出来てくる、こんな感じのあれ。
a
b
 × 
1
2
3
 → 
a1
a2
a3
b1
b2
b3

以下のような XML の変換になる。
<?xml version="1.0" encoding="UTF-8"?>
<two-tables>
<alpha-list>
<alpha>a</alpha>
<alpha>b</alpha>
</alpha-list>
<num-list>
<number>1</number>
<number>2</number>
<number>3</number>
</num-list>
</two-tables>
↓↓↓
<?xml version="1.0" encoding="UTF-8"?>
<cartesian-product>
<alpha-num>
<alpha>a</alpha><number>1</number>
</alpha-num>
<alpha-num>
<alpha>a</alpha><number>2</number>
・・・略
<alpha>b</alpha><number>3</number>
</alpha-num>
</cartesian-product>


こんな XSLT が 一個の解になる。(Eclipse で確認)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/two-tables">
<cartesian-product>
<xsl:apply-templates select="alpha-list"/>
</cartesian-product>
</xsl:template>
<xsl:template match="alpha-list">
<xsl:for-each select="alpha">
<xsl:apply-templates select="../../num-list">
<xsl:with-param name="alpha" select="."/>
</xsl:apply-templates>
</xsl:for-each>
</xsl:template>
<xsl:template match="num-list">
<xsl:param name="alpha"/>
<xsl:for-each select="number">
<alpha-num>
<xsl:copy-of select="$alpha"/>
<xsl:copy-of select="."/>
</alpha-num>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

xslt 行と列の変換

こんな感じで、表の形になったデータの行と列を入れ替えたいとする。
abc
def
⇒
ad
be
cf

XML ではこんな変換になる。
<?xml version="1.0" encoding="UTF-8" ?>
<matrix>
<r><c>a</c><c>b</c><c>c</c></r>
<r><c>d</c><c>e</c><c>f</c></r>
</matrix>
↓
<matrix>
<r><c>a</c><c>d</c></r>
<r><c>b</c><c>e</c></r>
<r><c>c</c><c>f</c></r>
</matrix>

こんな XSLT が一個の解になる。(Eclipse で確認)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/matrix/r[1]">
<matrix>
<xsl:for-each select="c">
<r>
<xsl:variable name="col-index" select="position()"/>
<xsl:for-each select="../../r/c[$col-index]">
<c><xsl:value-of select="."/></c>
</xsl:for-each>
</r>
</xsl:for-each>
</matrix>
</xsl:template>

<xsl:template match="r[position()!=1]"/>

</xsl:stylesheet>

2009年12月4日金曜日

XSLT で再帰でグラデーション

こんな XML を読み込んで、
<?xml version="1.0" encoding="UTF-8"?>
<table size="10"/>

HTML の<table>要素で作られた矩形領域の縦横を、size属性で指定された数で分割して、下のような緑と青のグラデーションのテーブルを作るような XSLT を書いてみたいとする。左上 の色が rgb(0, 0, 0) で、右下が rgb(0, 255, 255)である 。
<table size="5"><table size="10">

HTML ソースはこんな感じになる。
<table cellspacing="0" cellpadding="0">
<tr>
<td style="width:16px;height:16px;background-color:rgb(0,0,0)"></td>
<td style="width:16px;height:16px;background-color:rgb(0,0,28)"></td>
・・・略
<td style="width:16px;height:16px;background-color:rgb(0,0,255)"></td>
・・・略
<td style="width:16px;height:16px;background-color:rgb(0,255,0)"></td>
・・・略
<td style="width:16px;height:16px;background-color:rgb(0,255,255)"></td>
</tr>
</table>

これを以下のような再帰を使って、やってみた。
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="html" indent="no"/>
<xsl:strip-space elements="list"/>

<xsl:template match="/table">
<table cellspacing="0" cellpadding="0">
<xsl:call-template name="rows">
<xsl:with-param name="s" select="@size"/>
<xsl:with-param name="g" select="@size - 1"/>
</xsl:call-template>
</table>
</xsl:template>
<xsl:template name="rows">
<xsl:param name="s"/>
<xsl:param name="g"/>
<xsl:if test="0<$g">
<xsl:call-template name="rows">
<xsl:with-param name="s" select="@size"/>
<xsl:with-param name="g" select="$g - 1"/>
</xsl:call-template>
</xsl:if>
<tr>
<xsl:call-template name="cols">
<xsl:with-param name="s" select="$s"/>
<xsl:with-param name="g" select="$g"/>
<xsl:with-param name="b" select="$s - 1"/>
</xsl:call-template>
</tr>
</xsl:template>
<xsl:template name="cols">
<xsl:param name="s"/>
<xsl:param name="g"/>
<xsl:param name="b"/>
<xsl:if test="0<$b">
<xsl:call-template name="cols">
<xsl:with-param name="s" select="$s"/>
<xsl:with-param name="g" select="$g"/>
<xsl:with-param name="b" select="$b - 1"/>
</xsl:call-template>
</xsl:if>
<xsl:variable name="blue" select="floor(255 * $b div ($s - 1))"/>
<xsl:variable name="green" select="floor(255 * $g div ($s - 1))"/>
<xsl:variable name="size" select="floor(160 div $s)"/>
<td style="width:{$size}px;height:{$size}px;background-color:rgb(0,{$green},{$blue})"/>
</xsl:template>
</xsl:stylesheet>

これを Eclipse で実行して、生成された <table>をペーストしたものが、上のグラデーションのテーブル。

OpenLDAP/cygrunsrv/Cygwin

JNDI を使って、Java オブジェクトをLDAP に登録する実験をしたいが、その前にまず、LDAPサーバを準備する必要がある。そこで、以前 Cygwin を入れたときに一緒にインストールしたまま未使用だった OpenLDAP を活用してみる事にした。

■ バージョン等
  • OS: Windows XP SP3
  • Cygwin : 1.5.25
  • OpenLDAP: 2.3.43-1
■ 前提
OpenLDAP 込みで、Cygwin が入っている前提。
  • Cygwin がなかったら、ここから SetUp.exe を実行。
  • OpenLDAP がなかったら、SetUp.exe の「Select Package」で Net カテゴリの下にある openldap を選択して追加。

■ Windows サービスに登録
必須ではないがサービスとして登録した。
cygrunsrv --install OpenLDAP --path /usr/sbin/slapd --args "-d 0 -f /etc/openldap/slapd.conf" --desc OpenLDAP/Cygwin

登録内容の確認は
  • Cygwin コマンドプロンプトからcygrunsrv -L -V、または
  • 「コンパネ」/「管理ツール」/「サービス」(以下、単に「サービス」)での OpenLDAP の表示。

動いていることの確認は
  • 「サービス」で状態が”開始”になっている事
  • netstat -ano|grep 389 でポート 389のプロセス番号を調べて、タスク・マネージャでそのプロセス番号が slapd.exe のものである事を確認
※ 起動が遅くなるのがイヤなので、いったん登録した後「スタートアップの種類」を手動に設定。

■ 設定
こんなslapd.confにした
include  /etc/openldap/schema/core.schema
include /etc/openldap/schema/java.schema
pidfile /var/openldap/run/slapd.pid
argsfile /var/openldap/run/slapd.args
database bdb
suffix "dc=example,dc=com"
rootdn "cn=Manager,dc=example,dc=com"
rootpw secret
directory /var/openldap/openldap-data
index objectClass eq

2行目の java.schema のinclude を追加し、"dc=my-company"を、example に変更。

■ エントリを追加してみる
上記設定の動作を確認。
LDAP Admin というツールを見つけたので使う事にした。[→URL]
LDAP Admin を起動して、[New Connection] で 以下の接続設定を新規作成。
  • Host:127.0.0.1
  • Port:389
  • Version:3
  • Username:cn=Manager,dc=example,dc=com
  • Password:secret
接続すると dc=example,dc=com が表示されるが、今のところエントリ無し。

以下のように test.ldif を書いて、これを Cygwin から LDAP に読ませる。
dn: o=JNDITest,dc=example,dc=com
o: JNDITest
objectclass: top
objectclass: organization
$ ldapadd -x -D "cn=Manager,dc=example,dc=com" -w secret -f test.ldif

エントリが追加された事をLDAP Admin で確認

これで、JNDI + LDAP を試す準備ができた。

■ 参考URL
http://web.mit.edu/cygwin/cygwin_v1.3.2/usr/doc/Cygwin/cygrunsrv.README
http://rigsb.net/wiki/index.php?title=Run_OpenLDAP_as_a_Windows_service_via_Cygwin
http://linux.die.net/man/8/slapd

2009年12月3日木曜日

XSLT で文字列のfill

XLST を使って、ある文字列を加工して指定の文字列長に達するまでアスタリスクで埋めてみる。[参考URL]

たとえば以下のような変換になる。
<?xml version="1.0" encoding="UTF-8"?>
<list width="10">
<item>red</item>
<item>green</item>
<item>blue</item>
</list>
↓↓↓
<?xml version="1.0" encoding="UTF-8"?>
<lines>
<line>red*******</line>
<line>green*****</line>
<line>blue******</line>
</lines>

これを再帰を使ってやってみると、次のような XSLT になる。
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/list">
<lines>
<xsl:apply-templates>
<xsl:with-param name="width" select="@width" />
</xsl:apply-templates>
</lines>
</xsl:template>

<xsl:template match="item">
<xsl:param name="width" />
<line>
<xsl:value-of select="." />
<xsl:call-template name="dots">
<xsl:with-param name="count" select="$width - string-length(.)" />
</xsl:call-template>
</line>
</xsl:template>

<xsl:template name="dots">
<xsl:param name="count" select="1" />
<xsl:if test="$count &gt; 0">
<xsl:text>*</xsl:text>
<xsl:call-template name="dots">
<xsl:with-param name="count" select="$count - 1" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

2009年12月2日水曜日

XSLTで fibonacci

フィボナッチ数列を得る再帰関数を書くと、加算して Fn を計算するための Fn-1 と Fn-2 を求める際に、一度通った再帰の道筋を何度も通ってしまって、無駄な計算が物凄く多くなるのが、よく問題になる。

これを何とかして重複計算のないように改良するというのが、いろんな言語で初学者の練習問題にもなったりするようだけど、ここでは XSLT でそれをやってみる。

test2.xml
<?xml version="1.0" encoding="UTF-8"?>
<fib nth="10"/>
上のような XML を読んで、1番から nth番までのフィボナッチ数列を縦に並べたテキストが得られるようにしたい。一個の解として、こんな XSLT コードを考えてみた。

test2.xsl
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="no"/>
<xsl:template name="fib">
<xsl:param name="n" select="1"/>
<xsl:variable name="pair">
<xsl:choose>
<xsl:when test="$n=1">0,1</xsl:when>
<xsl:otherwise>
<xsl:call-template name="fib">
<xsl:with-param name="n" select="$n - 1"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="substring-after($pair, ',')"
/>,<xsl:value-of select="substring-before($pair, ',') + substring-after($pair, ',')"/>
</xsl:template>

<xsl:template name="fib-print">
<xsl:param name="p1"/>
<xsl:param name="p2"/>
<xsl:if test="0 &lt; $p1">
<xsl:call-template name="fib-print">
<xsl:with-param name="p1" select="$p2 - $p1"/>
<xsl:with-param name="p2" select="$p1"/>
</xsl:call-template>
</xsl:if>
<xsl:value-of select="$p2"/><xsl:text>&#010;</xsl:text>
</xsl:template>

<xsl:template match="/fib">
<xsl:variable name="pair">
<xsl:call-template name="fib">
<xsl:with-param name="n" select="@nth - 1"/>
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="fib-print">
<xsl:with-param name="p1" select="substring-before($pair, ',')"/>
<xsl:with-param name="p2" select="substring-after($pair, ',')"/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>

テンプレート fib は、二つの値を「Fn, Fn-1」のような形でカンマ区切りの文字列で返すようにした。例えば nth = 5 だと "5,8"、nth = 1 だと "1,1"となる。テンプレート fib-print は、この値の組をいったん逆にたどって、"0,1"に届いたところから折り返して出力するようにしてみた。

Eclipseで実行すると、1, 1, 2, 3, 5, 8, 13, 21, 34, 55と 縦に並んだテキストが生成される。文字列操作が多いのがちょっと面白くないけど、O(n)のロジックにはなった。

2009年12月1日火曜日

Eclipse XSL Developer Tools @ Galileo

XSLT を試そうとしたとき、ブラウザを使うと HTML以外の形式に変換した場合に確認しにくいし、コマンドラインから xalan 等 を叩くのも、ちょっと億劫なことがある。

調べてみると、今まで気付いていなかったけど、いつも使っている Eclipse に丁度いいプラグインが入っていた。試しに Hello Worldをやってみたので、以下レポート。

■ インストール
もし未インストールなら、Eclipse XSL Developer Tools を入れる。
・update サイト ⇒ http://download.eclipse.org/releases/galileo

■ Hello, World! 作成
・適当な名前で新規プロジェクトを作る
[New]->[Project...]->[General]->[Project]

・変換される XMLファイルを作る
[New]->[Other...]->[XML]->[XML]

ファイル名はデフォルトの NewFile.xml のままとした。これに一行追加。
<?xml version="1.0" encoding="UTF-8"?>
<person name="World"/>

・XSLファイルを作る
[New]->[Other...]->[XML]->[XSL]

ファイル名はデフォルトの NewStylesheet.xsl のままとした。これをちょっと編集。
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/person">
<greet>Hello, <xsl:value-of select="@name" />!</greet>
</xsl:template>
</xsl:stylesheet>

■ Hello, World! 実行
NewStylesheet.xsl のコンテキストメニュー から [Run As]->[XSL Transformation] を実行。
Input File を聞いてくるので、[Workspace] ボタンから NewFile.xml を指定。
で、[OK]で実行。NewFile.out.xml が生成されて、勝手に開く。こんな感じになってるはず。
<?xml version="1.0" encoding="UTF-8"?><greet>Hello, World!</greet>

Eclipse を常時使っているので、これは結構便利。