それでは、実際にカウンタを使用したスタイルシートの例を見てみることにしましょう。ここでは、リスト 7.2 の section タグおよび listitem タグに対して番号付けを行うことにします。
リスト 7.1 [ report.dtd ]
<!ENTITY % ALPHA "loweralpha|upperalpha"> <!ENTITY % ROMAN "lowerroman|upperroman"> <!ELEMENT report (title?,section+)> <!ELEMENT section (title?,(section|orderedlist|para)*)> <!ELEMENT title (#PCDATA)> <!ELEMENT orderedlist (listitem+)> <!ATTLIST orderedlist numeration (arabic|%ALPHA;|%ROMAN;) "arabic"> <!ELEMENT listitem (orderedlist|para)*> <!ELEMENT para (#PCDATA|orderedlist|para)*>
リスト 7.2 [ report.xml ]
<?xml version="1.0" encoding="euc-jp"?> <!DOCTYPE report SYSTEM "report.dtd"> <report> <title>Web Kit / Web Foundation IPv6 化対応調査報告書</title> <section> <title>本報告書について</title> <para>本報告書は、F 社の要請により Apple Computer, Inc. の提供する Web ブラウジングフレームワーク Web Kit について、IPv6 化対応の可否および対応方法に関する調査を行った結果を記したものである。</para> </section> <section> <title>フレームワークの構成</title> <para>フレームワークは Web Kit および Web Foundation の2つのカテゴリから構成されている。</para> <section> <title>Web Kit</title> <para>Web Kit は取得済みデータの解析、レンダリング結果の表示を含むユーザインタフェースに関するクラス、およびプロトコルを含む。</para> </section> <section> <title>Web Foundation</title> <para>Web Foundation は内部処理に用いる API を含む。これにはリクエストの発行、データローディングに関する API が含まれる。したがって、IPv6 対応にあたり調査の必要があるのは Web Foundation に含まれるクラスおよびプロトコルとなる。</para> </section> </section> <section> <title>データローディングの概略</title> <para>データローディングはおおよそ以下の手順に沿って行われる。</para> <orderedlist numeration="upperalpha"> <listitem> <para>リクエストの生成 (NSURLRequest)</para> </listitem> <listitem> <para>接続要求 (NSURLConnection)</para> </listitem> <listitem> <para>プロトコル処理 (NSURLProtocol)</para> <orderedlist numeration="loweralpha"> <listitem> <para>接続</para> </listitem> <listitem> <para>レスポンス解析・通知 (NSURLResponse)</para> </listitem> <listitem> <para>メッセージボディ (データ) 取得</para> </listitem> </orderedlist> </listitem> </orderedlist> <para>これからわかるように、サーバへの実接続、データローディングはNSURLProtocol によって行われる。</para> </section> </report>
リスト7.3 [ report.dsl ]
<!DOCTYPE style-sheet PUBLIC "-//James Clark//DTD DSSSL Style Sheet//EN"> (define (format-string numeration) (case numeration (("loweralpha") "a") (("lowerroman") "i") (("upperalpha") "A") (("upperroman") "I") (else "1"))) (define *device-rgb* (color-space "ISO/IEC 10179:1996//Color-Space Family::Device RGB")) (define *gray80* (color *device-rgb* 0.8 0.8 0.8)) (element report (make simple-page-sequence page-width: 210mm page-height: 297mm top-margin: 25mm left-margin: 25mm right-margin: 25mm bottom-margin: 30mm line-spacing: 15pt)) (element (report title) (make paragraph font-family-name: "Times Roman" font-size: 18pt line-spacing: 18pt space-after: 12pt)) (element section (make display-group space-before: 10pt space-after: 15pt)) (element (section section) (make display-group space-before: 8pt)) (element (section title) (make paragraph font-family-name: "Helvetica" font-size: 12pt space-after: 6pt (let loop ((nl (hierarchical-number-recursive "section" (current-node))) (section-number (empty-sosofo))) (if (null? nl) (sosofo-append section-number (literal " ")) (loop (cdr nl) (sosofo-append section-number (literal (format-number (car nl) "1") "."))))) (process-children))) (element orderedlist (make display-group start-indent: (+ 12pt (inherited-start-indent)))) (element listitem (make display-group)) (element (listitem para) (make paragraph (if (first-sibling? (current-node)) (literal (format-number (child-number (parent (current-node))) (format-string (attribute-string "numeration" (parent (parent (current-node)))))) ". ") (empty-sosofo)) (process-children))) (element para (make paragraph font-family-name: "Times-Roman" font-size: 10pt))
スタイルシートを見ると、section タグのカウンタを実際に参照しているのは (section title) であり、listitem タグのカウンタを参照しているのは (listitem para) であることがわかります。カウンタをいつどこで参照するかについては決まった形式があるわけではありませんが、例題のようにカウンタ値の表示を行う指定で参照するのが一般的ではないかと思います。
ところで、この (listitem para) の指定には (first-sibling? …) という手続きが出てきます。これは、ノードを引数にとる SDQL のひとつで、引数に指定したノードが兄弟関係にある最初のノードであれば論理定数 #t を返します。ここでは、listitem タグの子である para タグの内、最初に現われるものにのみ番号を付加するために用いています。
さて、(section title) では section タグのカウンタを参照するために (hierarchical-number-recursive …) を利用していますが、先に説明したようにこの手続きが返すオブジェクトは番号(整数)を要素に持つリストですので、そのままでは (literal …) の引数にすることができません。そこで、何らかの方法で文字列に変換する必要があります。
変換処理はリスト 7.3に提示した以外に、先にすべてを文字列に変換してしまう方法などいくつか考えられると思いますが、いずれにせよ、リスト中の数値を一度に文字列に変換する方法はありませんので、リストの要素を一つずつ取り出して変換し、結果を結合することになります。
このような繰り返し処理には、一般に名前付き let を用います。 名前付き let は letの一変種であり、その見かけは通常の let(p.95 参照)にほぼ同じですが、<束縛部>の前に<変数>が追加されています。
(let <変数> <束縛部> <本体>)
この<変数>は、<束縛部>で定義した束縛変数を仮引数、<本体>を本体とする手続きをあらわします。次の式を例に、もう少し具体的に見てみることにしましょう。
(let loop ((arg1 expr1) ...... A (arg2 expr2)) ...... B (body-expression ... (loop value1 value2)) ...... C
この式では、変数 loop が A および B を仮引数とする手続きをあらわします。つまり、C が評価されると (let loop ((arg1 value1) (arg2 value2)) …) のように展開され、再度本体(ここでは body-expression 以降)が評価されます。2 度目以降の本体の評価においても、終了条件を満たさない限りは loop が呼び出され、繰り返し(ループ)処理[2]が行われます。
(section title) ではこれを利用して (hierarchical-number-recursive …) が返すリストから要素をひとつずつ取り出し、文字列変換と (literal …) によって sosofo を生成し、ループの度にこの sosofo を連結しています。
これと比較すると (listitem para) の指定は比較的単純ですが、(child-number …) によってカウンタを取得する際の対象ノードに注意する必要があります。ここでは (current-node) は para ですが、カウンタ値を取得したい対象はその親の listitem ですので、リスト 7.3では (parent (current-node)) としています。また、数値のフォーマット形式を listitem の親である orderedlist の numeration 属性から取得している点に着目してください。属性を取得する対象は para の親(listitem)の親(orderedlist)ですから、attribute-string に指定する対象ノードは (parent (parent (current-node))) となります。
このように指定してフォーマットを行ったサンプルが図7.3です。ここでは例を示しませんが、リスト 7.3の child-number を element-number に変更するなどして, どのように変化するかを試してみると良いでしょう。