# RelaxNGCC チュートリアル２

チュートリアル１よりもう少し複雑な例を見てみます。

## スキーマの説明

このチュートリアルで取り上げるスキーマは、あるディレクトリ以下のディレクトリとファイルの構造を表現したものです。以下のようになります。赤いところはRelaxNGCCのためのマークアップ部分です。青い数字は後述する説明に使います。

    <?xml version="1.0" encoding="utf-8"?>
    <grammar xmlns="http://relaxng.org/ns/structure/1.0"
      datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
      xmlns:c="http://www.xml.gr.jp/xmlns/relaxngcc">

    [1]
    <c:java-import>
    import java.util.Set;
    import java.util.HashSet;
    </c:java-import>

    <start c:class="sample2">
      [2]
      <c:java-body>
      public Set hiddenfiles;
      </c:java-body>
      <element name="files">
        <ref name="file-or-dir" c:alias="child"/>
        [3]
        <c:java>hiddenFiles = new HashSet(child.hiddenFiles);</c:java>
      </element>
    </start>

    [4]
    <define name="file-or-dir" c:class="FileOrDir">
      <c:java-body>
      public Set hiddenFiles;
      </c:java-body>
      <c:java>hiddenFiles = new HashSet();</c:java>
      <oneOrMore>
        <choice>
          <element name="file">
            <attribute name="name">
              <text c:alias="filename"/>
              [5]
              <c:java>if(filename.startsWith(".")) hiddenFiles.add(filename);</c:java>
            </attribute>
          </element>
          <element name="directory">
            <attribute name="name"><text/></attribute>
            <ref name="file-or-dir" c:alias="content"/>
            [6]
            <c:java>hiddenFiles.add(content.hiddenFiles);</c:java>
          </element>
        </choice>
      </oneOrMore>
    </define>

    </grammar>

このように、このスキーマではルートに`files`エレメントが来て、その下に`directory`または`file`が来ます。`directory`の下にも`directoryfile`が来ます。

## RelaxNGCC マークアップ

さて、このスキーマに則ったXML文書を読むとき、ピリオドで始まるファイル(Unixでは隠し属性のファイル)のみを集めてきたいと思ったとしましょう。ディレクトリ構造は無視して、ファイル名だけのコレクションを得ることが目的であるとします。

以下、各部分についての説明です。

[1] `java-import`の中に書いた文は、Javaのクラス定義の本体の外側に出力されます。javaエレメントや`java-body`で使用するコードが`import`文を必要とするならこの位置に書いてください。即ち`import`文とコメントが書けることになります。packageの指定はgrammarエレメントのアトリビュートで行うので、`java-import`内に書くことはできません。なお、`java-import`は、grammarの直下に置いた場合出力されるすべてのJavaファイルに適用されます。単一のファイルに適用するときには対応する`start`または`define`エレメントの直下に置いてください。

[2] `java-body`は、生成されるJavaコードに追加的なデータメンバやメソッドを定義したいときに使います。ここでは、ファイル名を収録するために`hiddenFiles`という名前のメンバを宣言しています。

[3] `java`エレメントは、XML文書を読んでスキーマの該当する位置に来たとき実行するコードを記述します。ここでは、ルートエレメントである`files`が終わったら、hiddenFilesを設定しています。

[4] RelaxNGCCは、このブロック単位で対応する１個のJavaファイルを生成するので、このサンプルではJavaファイルは２つ生成されることになります。このクラス名を指定するのが、start、defineそれぞれに追加されたアトリビュート c:class です。特に、define側ではその名前がfile-or-dirでハイフンを含んでいるためそのままではJavaのクラス名としては不正です。そのようなときは`c:class`が必須になります。

[5] ピリオドで始まるファイル名であればコレクションに格納しています。

[6] これも`java`エレメントですが、直前の`ref`エレメントにあるaliasを参照しているところに注意してください。`ref`エレメントに`alias`をつけると、それは`ref`エレメントが参照する`define`ブロックに対応したRelaxNGCCのオブジェクトになります。つまりこの例では、file-or-dirブロックに対応するFileOrDirオブジェクトということになります。

最終的にRelaxNGCCが出力したJavaファイルをコンパイル・実行すると、startエレメントに対応したオブジェクトの`hiddenFiles`メンバにすべての隠し属性ファイルが収録されます。起動手順は出力したファイルの中の`main()`関数を参照してください。

## Relaxerとの比較

文法からJavaのソースコードを出力する点では、RelaxNGCCとRelaxerは同じです。ですが、例えばこのサンプルで出したような目的でXML文書を読みたい場合、Relaxerを使ってXML文書を読み込ますと本来不要なdirectoryに対応したオブジェクトまでできてしまいます。もっと限定した情報がほしい場合、Relaxerの出力したオブジェクトモデルにアクセスしていかなければなりません。これに対しRelaxNGCCでは、すべてがSAXベースの１パスで処理が完了するため効率的です。

ただしJavaオブジェクトからXMLへの変換など、RelaxerにあってRelaxNGCCにない機能もあります。目的に応じて使い分ければよいでしょう。

RelaxNGCC home