似非PDAをfluent interfaceで
みんな変態にしかスターをくれないので。
型変数使うとプッシュダウンオートマトン(のような動作)を実現できたりもする。ただし、スタックに積めるのが1種類の値だけなのでかなりイマイチではある。
System.out.println( new Xml() .tag("hello") .attribute("value", "world") .text("Hello, world!") .end() .tag("tag1") .tag("tag2") .tag("tag3") .end() .end() .end() .get() );
こうなる。
<hello value="world">Hello, world!</hello><tag1><tag2><tag3></tag3></tag2></tag1>
こんな感じ。リファクタリングをまだやってない。
public class XmlBuilder { abstract static class Element { abstract void append(String text); } static class Xml extends Element { StringBuilder buf = new StringBuilder(); Tag<Xml> tag(String tagName) { return new Tag<Xml>(this, tagName); } @Override void append(String text) { buf.append(text); } String get() { return buf.toString(); } } static class Tag<T extends Element> extends Element{ T parent; String tagName; Tag(T parent, String name) { this.tagName = name; this.parent = parent; parent.append("<" + name); } Tag<T> attribute(String name, String value) { append(" " + name + "=\"" + value + "\""); return this; } TagBody<T> text(String text) { append(">"); append(text); return new TagBody<T>(parent, tagName); } Tag<TagBody<T>> tag(String name) { append(">"); return new Tag<TagBody<T>>( new TagBody<T>(parent, this.tagName), name); } T end() { append(">"); append("</" + tagName + ">"); return parent; } @Override void append(String text) { parent.append(text); } } static class TagBody<T extends Element> extends Element { T parent; String tagName; TagBody(T parent, String name) { this.tagName = name; this.parent = parent; } TagBody<T> text(String text) { append(text); return this; } Tag<TagBody<T>> tag(String name) { return new Tag<TagBody<T>>(this, name); } T end() { append("</" + tagName + ">"); return parent; } @Override void append(String text) { parent.append(text); } } }
ポイントはTagとTagBodyをパラメトリックにして、親の型を覚え続けている点。tag()を実行すると戻り値の型引数に現在の型を積んで、end()メソッドでその型をとりだしている点。さらに、TagとTagBodyに分割して、attributeを使えたり使えなかったりしている点(Tag.textやTag.tagを呼ぶとそのあとはそのタグにattributeをつけられなくなる)。
ただし、すべての操作は破壊的に行われるため、S2JDBCのように途中結果をビューとして利用したり、ということは上記ではできません。なお、タグ名を型として一通り定義すると、もっといろいろとやることもできます。