APTでテンプレートメソッドパターンを生成する その4

さてDTOのテンプレートから具象クラスを出力するアノテーションプロセッサだが、使っているうちにこんなXMLをマップする要件が出てきた。

<members>
    <member>
        <user-cd>001</user-cd>
        <name>はてなたろう</name>
        <address>東京都千代田区外神田1</address>
    </member>
    <member>
        <user-cd>002</user-cd>
        <name>はてなじろう</name>
        <address>東京都新宿区1</address>
    </member>
</members>

構造に他の構造を含むことができるのがXMLの特徴なのだから、このようなマッピングは当然だろう。
しかしそうするとアノテーションの設計を若干変えなければならない。

member要素のマップに使用するアノテーションは今までと何ら変りなく使える。

@XmlAutoBean(beandClass="net.kazzz.dto.api.MemberDto",
        elements={
            @Element(name="user-cd", fieldName="userCd"),
            @Element(name="name"),
            @Element(name="address")
        })
public class Member {}

しかし、MemberDtoのインスタンスを複数保持するMembersDtoのためのアノテーションは以下のようになるだろう。-- Members.java

@XmlAutoBean(beandClass="net.kazzz.dto.api.MembersDto",
        elements={
            @Element(name="member"
                , type= Array.class  //または List.class
                , genericType=MemberDto.class)
        })
public class FrameAssets {}

配列にせよリストにせよ、内部に保持するインスタンスの型を指定する必要があるので、上記のように@ElementアノテーションにはgenericType属性を追加する。
本当は

@XmlAutoBean(beandClass="net.kazzz.dto.api.MembersDto",
        elements={
            @Element(name="member"
                , type= List<MemberDto>.class)
        })
public class FrameAssets {}

と書くことができれば良いのだけれど、Javaジェネリクスはイレイジャなのでこの記述方法は無いものねだりとなる。(.NET C#などではきちんと型情報を記述できる)

最終的にアノテーションプロセッサに生成されるクラスMemberDtoとMembersDtoは、以下のようになると思われる。

@Generated(
       value={"net.kazzz.annotation.XmlAutoBeanAnnotationProcessor"}
       , date="2011-01-05T15:22:28.996+0900")
public class MemberDto extends AbstractXmlDto {

    protected String userCd;
    protected String name;
    protected String address;

    /* (non-Javadoc)
     * @see parse(net.kazzz.dto.xml.AbstractXmlDto)
     */
    @Override
    public void parse(XmlPullParser parser) throws XmlPullParserException, IOException {

        int type;
        String tagName = "";

        while((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
            switch(type) {
            case XmlPullParser.START_DOCUMENT:
                break;
            case XmlPullParser.START_TAG:
                tagName = parser.getName();
                break;
            case XmlPullParser.TEXT:
                if( tagName.equalsIgnoreCase("user-cd")) {
                    this.setUserCd(parser.getText());
                } else 
                if( tagName.equalsIgnoreCase("name")) {
                    this.setSeiKanji(parser.getText());
                } else 
                if( tagName.equalsIgnoreCase("address")) {
                    this.setMeiKanji(parser.getText());
                }
                break;
            case XmlPullParser.END_TAG:
                tagName = "";
                break;
            }
        }
    }

    public String getUserCd() {
        return this.userCd;
    }

    public void setUserCd(String userCd) {
        this.userCd = userCd;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return this.address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

}
    • MembersDto
@Generated(
       value={"net.kazzz.annotation.XmlAutoBeanAnnotationProcessor"}
       , date="2011-01-05T15:22:28.996+0900")
public class MembersDto extends AbstractXmlDto {

    protected List<MemberDto> membersDto;

    /* (non-Javadoc)
     * @see parse(net.kazzz.dto.xml.AbstractXmlDto)
     */
    @Override
    public void parse(XmlPullParser parser) throws XmlPullParserException, IOException {
        int type;
        String tagName = "";
        while((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
            switch(type) {
            case XmlPullParser.START_DOCUMENT:
                break;
            case XmlPullParser.START_TAG:
                tagName = parser.getName();
                if( tagName.equalsIgnoreCase("memberDto")) {
                    MemberDto f = new MemberDto();
                    XmlPullSubtreeParser subParser = new XmlPullSubtreeParser(parser); //※
                    while(subParser.hasNext()) {
                        f.parse(subParser);
                    };
                    membersDto.add(f);
                    tagName = "";
                }
                break;
            case XmlPullParser.TEXT:
                break;
            case XmlPullParser.END_TAG:
                tagName = "";
                break;
            }
        }
    }

    public List<MemberDto> getMembersDto() {
        return this.membersDto;
    }

    public void setMembersDto(List<MemberDto> membersDto) {
        this.membersDto= membersDto;
    }
}

うーん、コンポジションになったら一気に面倒になったなぁ。


※ここで昨日紹介したXmlPullSubtreeParserが登場する訳だ。(いきなり永久ループしたので書きなおし決定だが)