001 
002 /*
003  *  JScripter Standard 1.0 - To Script In Java
004  *  Copyright (C) 2008-2011  J.J.Liu<jianjunliu@126.com> <http://www.jscripter.org>
005  *  
006  *  This program is free software: you can redistribute it and/or modify
007  *  it under the terms of the GNU Affero General Public License as published by
008  *  the Free Software Foundation, either version 3 of the License, or
009  *  (at your option) any later version.
010  *  
011  *  This program is distributed in the hope that it will be useful,
012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014  *  GNU Affero General Public License for more details.
015  *  
016  *  You should have received a copy of the GNU Affero General Public License
017  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
018  */
019 
020 package jsx.dom.query;
021 
022 import js.ArrayLike;
023 import js.Disposable;
024 import js.Js;
025 import js.user.JsElement;
026 import js.user.JsNode;
027 import jsx.client.Win;
028 
029 /**
030  * <p>Defines paths that perform efficient queries over documents and elements.</p>
031  * <p>A path defined by this class can extends optionally with a {@link Selector} and 
032  * selects over a DOM structured node.</p>
033  * 
034  * @author <a href="mailto:jianjunliu@126.com">J.J.Liu (Jianjun Liu)</a> at <a href="http://www.jscripter.org" target="_blank">http://www.jscripter.org</a>
035  */
036 public final class Path extends Disposable
037 {
038     private final String name;
039     private final Selector.Tag tagSelector;
040     private final Selector selector;
041     private final Path parent;
042 
043     private Path(String name, Selector selector, Path parent) {
044         this.name = name;
045         this.tagSelector = new Selector.Tag(name);
046         this.selector = selector;
047         this.parent = parent;
048     }
049     /**
050      * <p>Constructs a path with a tag name and a selector.</p>
051      * <p>The path will start query from the named child nodes of a document or element.</p>
052      * @param name A tag name of the nodes where this path starts query.
053      * @param selector Selects the nodes where this path starts query.
054      * @since 1.0
055      */
056     public Path(String name, Selector selector) {
057         this(name, selector, null);
058     }
059     /**
060      * <p>Constructs a path with a tag name.</p>
061      * <p>The path will start query from the named child nodes of a document or element.</p>
062      * @param name A tag name of the nodes where this path starts query.
063      * @since 1.0
064      */
065     public Path(String name) {
066         this(name, Selector.ANY);
067     }
068     /**
069      * <p>Constructs a path with a selector.</p>
070      * @param selector Selects the nodes where this path starts query.
071      * @since 1.0
072      */
073     public Path(Selector selector) {
074         this("", selector);
075     }
076     /**
077      * <p>Constructs a path without a tag name or a selector.</p>
078      * <p>The path will start query from all the child nodes of a document or element.</p>
079      * @since 1.0
080      */
081     public Path() {
082         this("");
083     }
084 
085     /**
086      * <p>Returns a new path with a tag name and a selector appended to the current one.</p>
087      * <p>The new path will continue its query from the named child nodes of the nodes it has selected.</p>
088      * @param name A tag name of the nodes where the new path continues its query.
089      * @param selector Selects the nodes where the new path continues its query.
090      * @return The appended new path.
091      * @since 1.0
092      */
093     public final Path add(String name, Selector selector) {
094         return new Path(name, selector, this);
095     }
096     /**
097      * <p>Returns a new path with a selector appended to the current one.</p>
098      * <p>The new path will continue its query from all the child nodes of the nodes it has selected.</p>
099      * @param selector Selects the nodes where the new path continues its query.
100      * @return The appended new path.
101      * @since 1.0
102      */
103     public final Path add(Selector selector) {
104         return new Path("", selector, this);
105     }
106     /**
107      * <p>Returns a new path with a tag name appended to the current one.</p>
108      * <p>The new path will continue its query from the named child nodes of the nodes it has selected.</p>
109      * @param name A tag name of the nodes where the new path continues its query.
110      * @return The appended new path.
111      * @since 1.0
112      */
113     public final Path add(String name) {
114         return new Path(name, Selector.ANY, this);
115     }
116     /**
117      * <p>Returns a new path without a tag name or a selector appended to the current one.</p>
118      * <p>The new path will continue its query from all the child nodes of the nodes it has selected.</p>
119      * @return The appended new path.
120      * @since 1.0
121      */
122     public final Path add() {
123         return add("");
124     }
125 
126     /**
127      * <p>Performs a query over the current document.</p>
128      * @return A list of nodes as the result.
129      * @since 1.0
130      */
131     public final ArrayLike<JsNode> select() {
132         return select(Win.document.var());
133     }
134 
135     /**
136      * <p>Performs queries over a list of nodes.</p>
137      * @param nodes The nodes where this path starts its queries.
138      * @return A list of nodes as the result.
139      * @since 1.0
140      */
141     public final ArrayLike<JsNode> select(ArrayLike<JsNode> nodes) {
142         if (Js.be(parent)) {
143             nodes = parent.select(nodes);
144         }
145         if (Js.not(name)) {
146             return selector.reset().select(nodes);
147         }
148         ArrayLike<JsNode> ret = Js.array();
149         if (Js.be(parent) && Js.not(parent.name)) {
150             for (int i = 0, len = nodes.length(); i < len; i++) {
151                 ret = ret.concat(
152                         Query.getElementsByTagName(
153                                 new JsElement(nodes.get(i)),
154                                 name,
155                                 selector
156                         )
157                 );
158             }
159         } else {
160             for (int i = 0, len = nodes.length(); i < len; i++) {
161                 ret = ret.concat(
162                         Query.getChildren(
163                                 nodes.get(i),
164                                 tagSelector.and(selector)
165                         )
166                 );
167             }
168         }
169         return ret; 
170     }
171 
172     /**
173      * <p>Performs a query over a document or element.</p>
174      * @param ctx The node where this path starts its query.
175      * @return A list of nodes as the result.
176      * @since 1.0
177      */
178     public final ArrayLike<JsNode> select(JsNode ctx) {
179         ArrayLike<JsNode> nodes = Js.array();
180         nodes.push(ctx);
181         return select(nodes);
182     }
183 }