/ja?>
チュートリアル1よりもう少し複雑な例を見てみます。/ja?> Let's see a little more complicated example./en?>
このチュートリアルで取り上げるスキーマは、あるディレクトリ以下のディレクトリとファイルの構造を表現したものです。以下のようになります。赤いところはRelaxNGCCのためのマークアップ部分です。青い数字は後述する説明に使います。/ja?> In this tutorial, we use the following grammar, which describes a structure of files and directories. The special mark-ups peculiar to RelaxNGCC are red./en?>
<?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が来ます。/ja?> This grammar allows files element as the root and arbitrary number of directory and file elements under the files element. Also it allows directory element under directory element./en?>
さて、このスキーマに則ったXML文書を読むとき、ピリオドで始まるファイル(Unixでは隠し属性のファイル)のみを集めてきたいと思ったとしましょう。ディレクトリ構造は無視して、ファイル名だけのコレクションを得ることが目的であるとします。/ja?> Now, let's suppose that we want to collect files that begin with a period (in Unix convention, it means "hidden" attribute). Additionaly, suppose that our interest is not directory tree but file names./en?>
以下、各部分についての説明です。/ja?> Then, explanations of each part are following./en?>
[1] java-importの中に書いた文は、Javaのクラス定義の本体の外側に出力されます。javaエレメントやjava-bodyで使用するコードがimport文を必要とするならこの位置に書いてください。即ちimport文とコメントが書けることになります。packageの指定はgrammarエレメントのアトリビュートで行うので、java-import内に書くことはできません。なお、java-importは、grammarの直下に置いた場合出力されるすべてのJavaファイルに適用されます。単一のファイルに適用するときには対応するstartまたはdefineエレメントの直下に置いてください。/ja?> The content of java-import element is copied besides the main class definition of Java. If your code within java element or java-body element[5] requires classes in external packages, you should write import declarations using java-import element. Note that java-import elements under the root grammar element affect all generated code and they under a start or define element affect only the corresponding class to the element./en?>
[2] java-bodyは、生成されるJavaコードに追加的なデータメンバやメソッドを定義したいときに使います。ここでは、ファイル名を収録するためにhiddenFilesという名前のメンバを宣言しています。/ja?> The content of java-body element is copied into the class definition of Java. By using this, you can add bodyary methods and data members to the generated class. In this sample, the java-body element declares a data member hiddenFiles for storing the file names./en?>
[3] javaエレメントは、XML文書を読んでスキーマの該当する位置に来たとき実行するコードを記述します。ここでは、ルートエレメントであるfilesが終わったら、hiddenFilesを設定しています。/ja?> The java element defines an action to be executed when a corresponding part of the input XML instance comes. In this example, initialize hiddenFiles when the files element ends./en?>
[4] RelaxNGCCは、このブロック単位で対応する1個のJavaファイルを生成するので、このサンプルではJavaファイルは2つ生成されることになります。このクラス名を指定するのが、start、defineそれぞれに追加されたアトリビュート c:class です。特に、define側ではその名前がfile-or-dirでハイフンを含んでいるためそのままではJavaのクラス名としては不正です。そのようなときはc:classが必須になります。/ja?> Since RelaxNGCC generates one Java class per a start block or a define block, RelaxNGCC generates 2 files from this grammar. The names of the classes are specified with c:class attribute for each start and define element. Especially the c:class attribute for the define element is mandatory because the name "file-or-dir" is not valid for a name of Java class./en?>
[5] ピリオドで始まるファイル名であればコレクションに格納しています。/ja?> At this location, the file name is added if it begins with a period to the hiddenFiles collection./en?>
[6] これもjavaエレメントですが、直前のrefエレメントにあるaliasを参照しているところに注意してください。refエレメントにaliasをつけると、それはrefエレメントが参照するdefineブロックに対応したRelaxNGCCのオブジェクトになります。つまりこの例では、file-or-dirブロックに対応するFileOrDirオブジェクトということになります。/ja?> This is also java element, but be careful that the code fragment of Java refers to the previous define block with an alias attribute. If you add an alias attribute to a ref element, the Java object accessed by the given alias is a object corrsponding to the define block. In this example, the instance content refers to FileOrDir object. /en?>
最終的にRelaxNGCCが出力したJavaファイルをコンパイル・実行すると、startエレメントに対応したオブジェクトのhiddenFilesメンバにすべての隠し属性ファイルが収録されます。起動手順は出力したファイルの中のmain()関数を参照してください。/ja?> After you compile and execute the generated code, the hiddenFiles member will contain all file names that begins with a period in the input XML instance. The main() method generated by RelaxNGCC may be helpful for understanding test procedure./en?>
文法からJavaのソースコードを出力する点では、RelaxNGCCとRelaxerは同じです。ですが、例えばこのサンプルで出したような目的でXML文書を読みたい場合、Relaxerを使ってXML文書を読み込ますと本来不要なdirectoryに対応したオブジェクトまでできてしまいます。もっと限定した情報がほしい場合、Relaxerの出力したオブジェクトモデルにアクセスしていかなければなりません。これに対しRelaxNGCCでは、すべてがSAXベースの1パスで処理が完了するため効率的です。/ja?> Relaxer and RelaxNGCC is same at the point of generating Java source code from a given grammar. But, if you use Relaxer for the case of this tutorial, you may have to traverse the object model includes a directory tree and collect file names that begins with a period. On the other hand, RelaxNGCC is more efficient for this purpose because it obtains the collection through one path via SAX interface./en?>
ただしJavaオブジェクトからXMLへの変換など、RelaxerにあってRelaxNGCCにない機能もあります。目的に応じて使い分ければよいでしょう。/ja?> However, there are some features supported by only Relaxer such as a conversion from Java object into XML instance. It is important to select more suitable tool according to your purpose./en?>