2008年4月24日木曜日

これは強力!XDoclet

非常に便利そうに見えるのですが、EJBやStrutsと共に使われるため、この人単体で何ができるのかが見えづらいXDocletです。
実際に試してみましたが、かなりマニアックですが、相当使えるツールとみました。
(そもそも目的が違うかもしれませんが、JJTreeとかjavaccとかで苦労したことがサクっとできる感じです)

かなりシンプルに試してみました。

【概要】
XDocletはAntと連携して動きます。
今回、Ant1.7、XDoclet1.23を使いました。

(1)ant buildファイルにXDocletタスクを定義します。
タスクにはejbとかStrugsとかSpring用にすでにカスタマイズされたものがあるようですが、今回はシンプルに実行するため「xdoclet.DocletTask」を使います。

antを実行すると、ソースディレクトリに含まれるJavaソースを解析してくれます。クラス名や、JavaDocコメント形式のコメントそれぞれにアクセスできるようになります。

(2)次に.xdtファイルを準備します。
特にXMLフォーマットにこだわる必要はなさそうです。CSVなどにも出力できますし、当然JavaソースをジェネるのもOK(なんと素晴らしい)
<XDtClass:forAllClasses>タグなどを使って、クラス名やコメントにアクセスします。

(3)Javaを準備します。
JavaDocコメント形式
 /**
  * @mytest.tag3
  * name="foo"
  */
で書いておくと、XDocletが解析してくれます(ただのコメントでは駄目です。)

(4)Antタスクを実行すると、xdtに解析結果を埋め込んだ形でアウトプットが作成されます。

EJBやStruts、SpringのXML定義ファイルや、Setter, Getterしか持たない単純なBeanクラスなどをジェネるのに最適!素晴らしいツールです。

【詳細】
ディレクトリ構造はこんな感じ。


まずはAnt build fileです。
<?xml version="1.0" encoding="UTF-8"?>
<project basedir="./" default="build" name="xdoclet-test">
<!-- Properties -->
<property name="xdoclet.home" value="F:/libraries/xdoclet-1.23"/>
<property name="src.dir" value="src"/>
<property name="xdoclet.template.dir" value="xdt"/>
<property name="build.xml.dir" value="xml"/>
<property name="build.dir" value="bin"/>

<!-- Path definitions -->
<path id="xdoclet.classpath">
<fileset dir="${xdoclet.home}/lib">
<include name="*.jar"/>
</fileset>
</path>

<!-- Targets -->
<target name="compile">
<mkdir dir="${build.dir}"/>
<javac destdir="${build.dir}" source="1.5" target="1.5" debug="true"
deprecation="false" optimize="false" failonerror="true">
<src path="${src.dir}"/>
<classpath refid="xdoclet.classpath"/>
</javac>
</target>

<target name="build" depends="compile">
<taskdef classname="xdoclet.DocletTask" name="doclet"
classpathref="xdoclet.classpath" />
<doclet destdir="${src.dir}"
excludedtags="@version, @author, @todo" force="true"
verbose="false">
<fileset dir="${src.dir}">
<include name="**/*.java" />
</fileset>
<template destDir="${build.xml.dir}"
destinationFile="test.xml"
templateFile="${xdoclet.template.dir}/test_xml.xdt"
subTaskName="Generate XML file" />
<template destDir="${build.xml.dir}"
destinationFile="test.txt"
templateFile="${xdoclet.template.dir}/test_print.xdt"
subTaskName="Generate Class info" />

</doclet>
</target>
</project>

次に'*.xdt'です。二通り準備しました。
■test_print.xdt
<XDtClass:forAllClasses>==================================================
Class name :
<XDtClass:fullClassName/>
Class tag values :
<XDtClass:forAllClassTags tagName="mytest.tag1">
<XDtClass:classTagValue
tagName="mytest.tag1" paramName="value"/>
Field tag values :
<XDtField:forAllFields>
<XDtField:fieldTagValue
tagName="mytest.tag3" paramName="name"/>
</XDtField:forAllFields>
Method tag values :
<XDtMethod:forAllMethods>
<XDtMethod:ifHasMethodTag tagName="mytest.tag2">
<XDtMethod:methodTagValue
tagName="mytest.tag2" paramName="name"/>
</XDtMethod:ifHasMethodTag></XDtMethod:forAllMethods>
</XDtClass:forAllClassTags>
</XDtClass:forAllClasses>

■test_xml.xdt
<?XML version="1.0" encoding="UTF-8"?>
<XDtClass:forAllClasses>
<class name="<XDtClass:className/>">
<variables>
<XDtClass:forAllClassTags tagName="mytest.tag1">
<tag value="<XDtClass:classTagValue
tagName="mytest.tag1" paramName="value"/>"/>
</XDtClass:forAllClassTags>
</variables>
<fields>
<XDtField:forAllFields>
<tag value="<XDtField:fieldTagValue
tagName="mytest.tag3" paramName="name"/>"/>
</XDtField:forAllFields>
</fields>
<methods>
<XDtMethod:forAllMethods>
<XDtMethod:ifHasMethodTag tagName="mytest.tag2">
<tag value="<XDtMethod:methodTagValue
tagName="mytest.tag2" paramName="name"/>"/>
</XDtMethod:ifHasMethodTag>
</XDtMethod:forAllMethods>
</methods>
</class>
</XDtClass:forAllClasses>

次にJavaソース(1本/2本)
package testpack;

/**
* @mytest.tag1
* value="hoge1"
*/
public class XDocletTest0 {

/**
* @mytest.tag3
* name="foo"
*/
public static String STATIC_VAR = "hoge";
int i = 0;

/**
* @mytest.tag2
* name="piyo2"
*/
public static void method1() {
return;
}

private String method2(String arg) {
return arg;
}

}

(2本/2本)
package testpack;

/**
* @mytest.tag1
* value="hoge1-2"
*/
public class XDocletTest1 {

/**
* @mytest.tag3
* name="foo-2"
*/
public static String STATIC_VAR = "hoge";
int i = 0;

/**
* @mytest.tag2
* name="piyo2-2"
*/
public static void method1() {
return;
}

private String method2(String arg) {
return arg;
}

}


実行すると、以下の二つのファイルが作成されます。
う~ん。強力。(って不親切過ぎかな)
==================================================
Class name :
testpack.XDocletTest1
Class tag values :
hoge1-2
Field tag values :
foo-2
Method tag values :
piyo2-2

==================================================
Class name :
testpack.XDocletTest0
Class tag values :
hoge1
Field tag values :
foo
Method tag values :
piyo2

<?XML version="1.0" encoding="UTF-8"?>

<class name="XDocletTest1">
<variables>
<tag value="hoge1-2"/>
</variables>
<fields>
<tag value="foo-2"/>
<tag value=""/>
</fields>
<methods>
<tag value="piyo2-2"/>
</methods>
</class>

<class name="XDocletTest0">
<variables>
<tag value="hoge1"/>
</variables>
<fields>
<tag value="foo"/>
<tag value=""/>
</fields>
<methods>
<tag value="piyo2"/>
</methods>
</class>


余裕が出たら、もう少し細かく解説します。

参考)
春のXDoclet中心生活