Relaxerオプション

Common

すべてのジェネレーターで使用できる共通のオプションです。

verbose

以下のような実行要約を表示します。

C:\fo\>relaxer -verbose xslfo.rng
Copyright(c) 2000-2004 ASAMI,Tomoharu. All rights reserved.
Relaxer Version 1.1b (20040405) by asami@relaxer.org
Source file     : file:/C:/fo/xslfo.rng
       artifact = IXslStylesheetChoice.java
       artifact = IXslTemplateMixed.java
       artifact = IXslTemplateMixedChoice.java
       artifact = IFoBlockContainerMixed.java
......

properties

任意のファイルをプロパティファイルとして指定します。以下の例では、カレントディレクトリにあるhogehoge.propertiesをプロパティファイルとして指定しています。

relaxer -properties:hogehoge.properties relax/hoge.rng

指定しない場合は、カレントディレクトリにあるRelaxer.propertiesファイルが使用されます。

dir

ソースコードを生成するディレクトリを指定します。

  • コマンドラインでの指定例
    -dir:./target/src/java
    -dir:c:\temp\target\src
    -dir:file://localhost/c:/temp/target/src
  • Relaxer.propertiesファイルでの指定例
    dir=./target/src/java
    dir=c:\temp\target\src
    dir=file://localhost/c:/temp/target/src
  • 指定が無い場合のデフォルトはカレントディレクトリです。
  • 指定したディレクトリが存在しない場合は、再帰的に自動生成してくれます。
  • 両方指定した場合は、コマンドラインが優先されます。
  • ディレクトリ内に同名ファイルが存在しても上書きされます。
  • リードオンリー属性の同名ファイルがある場合はエラーとなります。

dir.package

Relaxerの生成するライブラリの配置ディレクトリをパッケージに合わせるかどうかを指定します。

dir=src
dir.package=true
java.package.library=com.example.relaxer.lib

上記のように java.package.libraryオプションなどと併せて指定すると、URelaxer.java などのライブラリが、

src\com\example\relaxer\lib

以下に生成されます。

Java

java.package

生成されるクラスのパッケージを指定します。

以下のように指定すると、

java.package=com.example.relaxer

生成されるクラスにパッケージの設定が追加されます。

package com.example.relaxer;

java.package.library

URelaxer.javaUJAXP.javaなどのライブラリのパッケージを別途指定する場合に使用します。指定しない場合は、java.packageで指定したものが使われます。

java.package.library=com.example.relaxer.lib

java.name.class.prefix

生成するクラス名のプレフィクスを指定することができます。

例えば、"Hoge"をプレフィクスにした場合、要素名"book"からHogeBook.javaファイルが生成されます。

  • コマンドラインで指定する場合
    -java.name.class.prefix:Hoge
  • Relaxer.propertiesファイルに記入する場合
    java.name.class.prefix=Hoge

java.name.class.prefixes

名前空間を利用すると、複数のプレフィクスをスキーマの要素で使い分けることが出来ます。

以下、"xsl"と"fo"で異なるプレフィクスを使用したサンプルです。

  1. 名前空間を設定し、<element>要素の"name"属性にタグを付加
    <?xml version="1.0" encoding="Shift_JIS"?>
    <grammar datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
     xmlns="http://relaxng.org/ns/structure/1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:fo="http://www.w3.org/XSL/Format"
    >
    ......
    <element name="xsl:attribute-set"> ...... </element>
    <element name="fo:layout-master-set"> ...... </element>
    ......
    </grammar>
    
  2. "java.xml.namespace"オプションを指定し、"java.name.class.prefixes"オプションで名前空間URIとプレフィクスの対応を記述
    #Relaxer.properties
    java.xml.namespace=true
    java.name.class.prefixes=http://www.w3.org/1999/XSL/Transform:Xsl, (一行で)
                             http://www.w3.org/XSL/Format:Fo
  3. Relaxerでソースコード生成
    prepare-src:
       [mkdir] Created dir: C:\fo\target\src
        [java] Copyright(c) 2000-2004 ASAMI,Tomoharu. All rights reserved.
        [java] Relaxer Version 1.1b (20040203) by asami@relaxer.org
        [java] Property file       : C:\fo/Relaxer.properties
        [java] Source file : file:/C:/fo/src/relaxng/xslfo.rng
        ......
        [java]     artifact = XslAttributeSet.java
        [java]     artifact = FoLayoutMasterSet.java
        ......

Pattern

java.pattern.factory

factoryパターン*1のソースコードが生成されます。

このパターンを使うときは、

FoRoot root = new FoRoot();

とする代わりに

//設定しなければDefaultFondocFactoryが使われる。
FondocFactory.setFactory(new XslfonFactory());

と、設定しておいて、

IFondocFactory factory = FondocFactory.getFactory();
FoRoot root = factory.createFoRoot();

のように、オブジェクトを生成します。

面倒なようですが、自動生成したクラスを編集せずに、機能拡張したりする場合に便利なことがあります。 例えば、スキーマで検証できない部分を、すこしだけ機能拡張しようとします。 このとき、もし自動生成されたソースを編集して機能を追加したりすると、スキーマを修正してソースを再生成するたびに、大量にコピペする羽目になります。 そこで新しいFactoryを作って、これが機能拡張したクラスを返すようにしてやります。こうするとスキーマを再コンパイルしても、少しの修正でそのFactoryが使えますし、別のFactoryを使って機能を切り替えることもできます。

以下の例では、Factory(Concrete Creator)として以下のようなクラスを作り、バリデータに新しい検証機能(例えば、ある属性のどちらかが必要など)を追加しています。

public class XslfonFactory extends DefaultFondocFactory{
 /**
  * Creates a default <code>FoRoot</code>.
  * This method is a hook method of the AbstractXslfoFactory.
  *
  * @return FoRoot
  */
  // ...
  /**
   * Creates a default <code>XslTemplate</code>.
   * This method is a hook method of the AbstractFondocFactory.
   *
   * @return XslTemplate
   */
 public XslTemplate createXslTemplate() {
    XslTemplate node = new XslTemplate() {
      public void verify(RVerifyReport report, int index,
                         RVerifyContext context) {
        super.verify(report, index, context);
        report = report.newContext("template");
        String strMatch = getMatch();
        String strName  = getName();
        if(strMatch==null && strName==null) {
          report.addError(
            "@match|@name",index,RVerifyReport.INVALID_VALUE,"","string",
             "match、name属性のどちらかを設定する必要があります。"
          );
          return;
        }
      }
    };
    node.rGetRNSContext().declareNamespace(
      "xsl", "http://www.w3.org/1999/XSL/Transform"
    );
    return node;
    //return (new XslTemplate());
  }
}

java.pattern.visitor

Vistorパターンが手軽に使用できます。

文書指向のXMLが扱いやすくなります。例えばXMLの何所にでも置くことのできる要素を検索して取り出したりする場合などに使うと便利です。またスキーマが修正されてXMLのデータ構造が変わっても影響を受けないので、 データ構造に依存せずに、任意の処理を記述することができます。

java.pattern.visitorオプションをつけてスキーマをコンパイルすると、各要素から生成されたクラスがAcceptorであるIRVisitableをimplementsし、Concrete Acceptorになります。あとはVisitorであるRVisitorBaseをextendsするConcrete Visitorを作って利用します。

下のサンプルコードでは、MasterNameVisitorというConcrete Visitorで、root要素以下にあるsimple-page-master要素(Concrete Acceptorになる)を全て訪問し、その名前を収集しています。

XslRoot root = new XslRoot("test.xml"); // 検索のトップ要素
MasterNameVisitor mnv = new MasterNameVisitor();
URVisitor.traverse(root, mnv);
//Vector simplePageMasterList = mnv.getSimplePageMasterList();
Vector masterNameList = mnv.getMasterNameList();
JComboBox referenceComboBox = new JComboBox(masterNameList);
public class MasterNameVisitor extends RVisitorBase{
  private final Vector namelist = new Vector();
  private final Vector list = new Vector();
  public Vector getMasterNameList() {
    return namelist;
  }
  public Vector getSimplePageMasterList() {
    return list;
  }
  public boolean enter(FoSimplePageMaster visitable) {
    list.add(visitable);
    namelist.add(visitable.getMasterName());
    System.out.println("addMasterNameList "+visitable.getMasterName());
    return(true);
  }
}

または

XslRoot root = new XslRoot("test.xml");
final Vector namelist = new Vector();
URVisitor.traverse(root, new RVisitorBase() {
  public boolean enter(FoSimplePageMaster visitable) {
    //System.out.println("addMasterName "+visitable.getMasterName());
    namelist.add(visitable.getMasterName());
    return(true);
  }
});
JComboBox referenceComboBox = new JComboBox(namelist);

通常、Concrete Acceptorの追加は面倒らしいのですが、Relaxerはそれを自動生成してくれるので非常に楽です。

java.pattern.idmap

Swing

Swingの各種モデルで生成してくれます。

java.swing.comboBox(java.swing.list)

<define name="account">
  <element name="account">
    <attribute name="accountNo">
      <choice>
        <value>A12345</value>
        <value>B23456</value>
        <value>C34567</value>
      </choice>
    </attribute>
  </element>
</define>

上記のような選択肢のあるスキーマを-java.swing.comboBoxオプションをつけてコンパイルすると以下のように簡単にComboBoxModelを生成することができます。java.swing.listも同じ要領で使用することができます。

JComboBox accountNoComboBox = new JComboBox(account.swingComboBoxAccountNo());
relaxer-combomodel.png

Java/Extension Grammar

Relaxerで使える拡張文法について。

java:role

クラス間で共通のメソッドを定義したインタフェースが自動生成されます。

  • role
  • relax-users-j 02825より引用。

    java:roleの場合は、以下のようにjava:roleで指定したロール名(インタフェース)を同じロールとなるクラス間で共通のメソッドを定義したインタフェースを自動生成し、クラスが自動的にimplementsします。

<?xml version="1.0" encoding="UTF-8" ?>
<!--java:roleの例-->
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
         datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
         xmlns:java="http://www.relaxer.org/xmlns/relaxer/java">
  <start>
    <choice>
      <ref name="person"/>
      <ref name="company"/>
    </choice>
  </start>
  <define name="person">
    <element name="person" java:role="ICommon">
      <ref name="common"/>
    </element>
  </define>
  <define name="company">
    <element name="company" java:role="ICommon">
      <ref name="common"/>
    </element>
  </define>
  <define name="common">
    <attribute name="zip">
      <data type="token"/>
    </attribute>
    <element name="address">
      <data type="token"/>
    </element>
  </define>
</grammar>

上記の例では、Person、CompanyクラスにICommonインタフェースがimplementsされます。このICommonインタフェースはPerson、Companyクラスに共通するメソッドから自動的に定義され、ICommon.javaファイルが生成されます。

スペースで区切って複数のインタフェースを生成するよう指定することもできるようです。

<element name="company" java:role="ICommon IFontProperties IBorderProperies">
TODOメモ
XSLTスタイルシートを作れば、java:roleを自動的に付加することも出来そう?時間が取れたら実験すること。

java:default

属性などの初期値を設定することができます。

以下、整数、文字列などの初期値を指定しています。

<optional>
  <attribute name="column-count" java:default="1">
    <data type="int"/>
  </attribute>
</optional>
<optional>
  <attribute name="column-gap" java:default="12.0pt">
    <data type="string"/>
  </attribute>
</optional>
<optional>
  <attribute name="padding-start.conditionality" java:default="discard">
    <choice>
      <value type="NMTOKEN">discard</value>
      <value type="NMTOKEN">retain</value>
    </choice>
  </attribute>
</optional>

getColumnGap()メソッドなどが以下のように生成されます。

public final String getColumnGap() {
  if (columnGap_ == null) {
    return ("12.0pt");
  }
  return (columnGap_);
}
public final String getPaddingStartConditionality() {
  if (paddingStartConditionality_ == null) {
    return ("discard");
  }
  return (paddingStartConditionality_);
}

java:classNameとjava:propertyName

生成されるクラス名と属性名を指定することができます。

スキーマでelement要素にjava:classNameを以下のように指定すると、String.javaではなく、Hoge.javaが生成されます。

<element name="string" java:className="Hoge">
  <empty/>
</element>

Hogeは、string要素を操作するクラスになります。

//Hoge.javaの一部
public void makeTextElement(StringBuffer buffer) {
   int size;
  buffer.append("<string");
  buffer.append(">");
  buffer.append("</string>");
}

java:dataTypeとjava:dataClass

データを自作のクラスに格納できます。

java:extendsとjava:implements

それぞれ指定したクラス、インタフェースを継承させることができます。

<define name="aaa">
  <element name="aaa" java:extends="com.example.relaxer.Company"
                      java:implements="com.example.relaxer.ICompany">
    <ref name="common"/>
  </element>
</define>

この例では、以下のようなAaa.javaが生成されます。

public class Aaa extends com.example.relaxer.Company
  implements com.example.relaxer.ICompany, java.io.Serializable, Cloneable, IRNode {
// ...

java:abstract

生成されるクラスを、abstractクラスにすることができます。

<define name="company">
  <element name="company" java:abstract="true"
                          java:extends="com.example.relaxer.AbstractCompany">
    <ref name="common"/>
  </element>
</define>

この例では、以下のようなCompany.javaが生成されます。

public abstract class Company extends com.example.relaxer.AbstractCompany
  implements java.io.Serializable, Cloneable, IRNode {
// ...

com.example.relaxer.AbstractCompanyクラスは、別途用意することになります。

java:mapKey

java:mapKey="true"を使用すると、unique なキーを属性として持つXML要素群をMapオブジェクトに格納するようなJavaコードを生成してくれます。

その他

java:code

コンパイル時に使用するオプションではなく、スキーマに記述するオプション要素?です。element要素の子要素として<java:code>要素を置くことができ、この<java:code>要素の中にはJavaコードを自由に書くことができます。

<define name="account">
  <element name="account">
    <ref name="address"/>
    <zeroOrMore>
      <ref name="phone"/>
    </zeroOrMore>
<java:code xmlns:java="http://www.relaxer.org/xmlns/relaxer/java">
public boolean isOk() {
   return (true);
}
</java:code>
  </element>
</define>

コメント