XPath式をOOPにつくるXPathBuilder(よく考えないとだめ)

まいかいXPathをがんばって作るコードを書いてていやになったのでSQLみたいに

var xpath = new XPathBuilder();
xpath.div.className("next").position(4).a.rel("no-follow").img.toString();

こう書いたら

div[contains(" " + @class + " ", " next ") and position() = 4]/a[@rel = "no-follow" ]/img

こうなるやつができないかなーとおもって作ってみた(下のやつ。js1.7でないと動きません)けど、たしかに

xpath.div.className("next").position(4).a.rel("no-follow").img

みたいに単純だったらいいけど、じゃあ contains(@class, "no") とかどう書くの? とか、とりあえずは

xpath.div.contains( xpath.className, "no" )

とかにしたらごまかせるけど、きちんと設計できないとぜったい破綻するのが見えたので中止。

これ書くの楽しそうだけど自分はチョイ役ぐらいでしか使わないので、こんなの書いてていいのかという気持ちに。

誰か作ってください!

追記

とりあえず div.id って書いたときに attributeのidなのかXPath functionのidなのかがわかんないので、この書き方は厳しい。
div.@idは書けないのでその代わりにdiv.$idにするとかかなー。いまいち$が好きじゃない。

var XPathBuilder = function () {
        this.steps = [ ];
        //new XPathBuilder.Step(name) ];

        var htmlTags = [
                'a',
                'img',
                'div',
                'table',
                'tbody',
                'tr',
                'td',
                'p',
                'span',
                'em',
                'thead',
        ];

        var self = this;
        htmlTags.forEach( function (name) {
                self.__defineGetter__( name, let(n = name) function () {
                        var step = new XPathBuilder.Step(n);
                        self.steps.push(step);
                        return self;
                } );
        } );
}

XPathBuilder.Step = function (name) {
        this.elementName = name;
        this.predicates = [];
        console.log(this.predicates);
}

XPathBuilder.Step.prototype.toString = function () {
        console.log("XPathBuilder.Step", this.elementName, this.predicates);
        var exp = this.elementName;
        if ( this.predicates.length > 0 ) {
                var predicate = this.predicates.join(" and ");
                exp += '[' + predicate +']';
        }
        return exp;
}

XPathBuilder.prototype.__noSuchMethod__ = function (name, args) {
                if ( name == 'className' ) {
                        var classname = args.shift();
                        this.__push(
                                'contains(" " + @class + " ", " ' + classname + ' ")'
                        );
                } else {
                        var value = args.shift();
                        this.__push(
                                '@' + name + ' = "' + value + '" '
                        );
                }
                return this;
};

XPathBuilder.prototype.__defineGetter__("__tail", function () {
        return this.steps[this.steps.length - 1] ;
} );

XPathBuilder.prototype.__push = function (v) {
        this.__tail.predicates.push(v);
}
XPathBuilder.prototype.position = function (n) {
        this.__push( 'position() = ' + n);
        return this;
}

XPathBuilder.prototype.toString = function () {
        return this.steps.map( function (step) { return step.toString() } ).join("/");
}

追記

ていうかE4Xの構文でそのままいけるやつはそのまままねして、いけないところは工夫すればいいだけだ。アタマのいいひとたちが検討した結果だっていうことがE4Xの構文が破綻しないのを担保してくれてる。

E4Xおさらいして作る。
目的はE4Xで表現できるくらいの簡単なXPathE4Xとほぼ同じ構文で作れること。