1 /*
2 * $Header$
3 * $Revision$
4 * $Date$
5 *
6 * ====================================================================
7 *
8 * Copyright 2000-2002 bob mcwhirter & James Strachan.
9 * All rights reserved.
10 *
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions are
14 * met:
15 *
16 * * Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 *
19 * * Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 *
23 * * Neither the name of the Jaxen Project nor the names of its
24 * contributors may be used to endorse or promote products derived
25 * from this software without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
28 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
30 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
31 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
32 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
33 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
34 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
35 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
36 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
37 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 *
39 * ====================================================================
40 * This software consists of voluntary contributions made by many
41 * individuals on behalf of the Jaxen Project and was originally
42 * created by bob mcwhirter <bob@werken.com> and
43 * James Strachan <jstrachan@apache.org>. For more information on the
44 * Jaxen Project, please see <http://www.jaxen.org/>.
45 *
46 * $Id$
47 */
48
49
50
51 package org.jaxen.expr;
52
53 import java.io.Serializable;
54 import java.util.ArrayList;
55 import java.util.Iterator;
56 import java.util.List;
57 import org.jaxen.Context;
58 import org.jaxen.ContextSupport;
59 import org.jaxen.JaxenException;
60 import org.jaxen.function.BooleanFunction;
61
62 /**
63 * <p>
64 * Represents the collection of predicates that follow the node-test in a
65 * location path.
66 * </p>
67 *
68 * <p>
69 * There is no rule that the same predicate may not
70 * appear twice in an XPath expression, nor does this class enforce any such rule.
71 * This is implemented more as a list than a set. However, adding the same predicate
72 * twice should have no effect on the final result other than slowing it down.
73 * </p>
74 */
75 public class PredicateSet implements Serializable
76 {
77
78 private static final long serialVersionUID = -7166491740228977853L;
79
80 private List<Predicate> predicates;
81
82 /**
83 * Create a new empty predicate set.
84 */
85 public PredicateSet()
86 {
87 this.predicates = new ArrayList<Predicate>();
88 }
89
90 /**
91 * Add a predicate to the set.
92 *
93 * @param predicate the predicate to be inserted
94 */
95 public void addPredicate(Predicate predicate)
96 {
97 this.predicates.add( predicate );
98 }
99
100 /**
101 * Returns the list containing the predicates.
102 * This list is live, not a copy.
103 *
104 * @return a live list of predicates
105 */
106 public List getPredicates()
107 {
108 return this.predicates;
109 }
110
111 /**
112 * Simplify each of the predicates in the list.
113 */
114 public void simplify()
115 {
116 Iterator<Predicate> predIter = this.predicates.iterator();
117 Predicate eachPred = null;
118
119 while ( predIter.hasNext() )
120 {
121 eachPred = predIter.next();
122 eachPred.simplify();
123 }
124 }
125
126 /**
127 * Returns the XPath string containing each of the predicates.
128 *
129 * @return the XPath string containing each of the predicates
130 */
131 public String getText()
132 {
133 StringBuffer buf = new StringBuffer();
134
135 Iterator<Predicate> predIter = this.predicates.iterator();
136 Predicate eachPred = null;
137
138 while ( predIter.hasNext() )
139 {
140 eachPred = (Predicate) predIter.next();
141 buf.append( eachPred.getText() );
142 }
143
144 return buf.toString();
145 }
146
147 /**
148 * <p>Returns true if any of the supplied nodes satisfy
149 * all the predicates in the set. Returns false if none of the supplied
150 * nodes matches all the predicates in the set. Returns false if the
151 * node-set is empty.</p>
152 *
153 * @param contextNodeSet the nodes to test against these predicates
154 * @param support ????
155 * @return true if any node in the contextNodeSet matches all the predicates
156 * @throws JaxenException
157 */
158 protected boolean evaluateAsBoolean(List contextNodeSet,
159 ContextSupport support) throws JaxenException
160 {
161 return anyMatchingNode( contextNodeSet, support );
162 }
163
164 private boolean anyMatchingNode(List contextNodeSet, ContextSupport support)
165 throws JaxenException {
166 // Easy way out (necessary)
167 if (predicates.isEmpty()) {
168 return false;
169 }
170 Iterator<Predicate> predIter = predicates.iterator();
171
172 // initial list to filter
173 List nodes2Filter = contextNodeSet;
174 // apply all predicates
175 while(predIter.hasNext()) {
176 final int nodes2FilterSize = nodes2Filter.size();
177 // Set up a dummy context with a list to hold each node
178 Context predContext = new Context(support);
179 List<Object> tempList = new ArrayList<Object>(1);
180 predContext.setNodeSet(tempList);
181 // loop through the current nodes to filter and add to the
182 // filtered nodes list if the predicate succeeds
183 for (int i = 0; i < nodes2FilterSize; ++i) {
184 Object contextNode = nodes2Filter.get(i);
185 tempList.clear();
186 tempList.add(contextNode);
187 predContext.setNodeSet(tempList);
188 // ????
189 predContext.setPosition(i + 1);
190 predContext.setSize(nodes2FilterSize);
191 Object predResult = ((Predicate)predIter.next()).evaluate(predContext);
192 if (predResult instanceof Number) {
193 // Here we assume nodes are in forward or reverse order
194 // as appropriate for axis
195 int proximity = ((Number) predResult).intValue();
196 if (proximity == (i + 1)) {
197 return true;
198 }
199 }
200 else {
201 Boolean includes =
202 BooleanFunction.evaluate(predResult,
203 predContext.getNavigator());
204 if (includes.booleanValue()) {
205 return true;
206 }
207 }
208 }
209 }
210
211 return false;
212 }
213
214
215
216
217 /**
218 * <p>Returns all of the supplied nodes that satisfy
219 * all the predicates in the set. </p>
220 *
221 * @param contextNodeSet the nodes to test against these predicates
222 * @param support ????
223 * @return all the nodes that match each of the predicates
224 * @throws JaxenException
225 */
226 protected List evaluatePredicates(List contextNodeSet, ContextSupport support)
227 throws JaxenException {
228 // Easy way out (necessary)
229 if (predicates.size() == 0) {
230 return contextNodeSet;
231 }
232 Iterator<Predicate> predIter = predicates.iterator();
233
234 // initial list to filter
235 List nodes2Filter = contextNodeSet;
236 // apply all predicates
237 while(predIter.hasNext()) {
238 nodes2Filter =
239 applyPredicate((Predicate)predIter.next(), nodes2Filter, support);
240 }
241
242 return nodes2Filter;
243 }
244
245 public List applyPredicate(Predicate predicate, List nodes2Filter, ContextSupport support)
246 throws JaxenException {
247 final int nodes2FilterSize = nodes2Filter.size();
248 List<Object> filteredNodes = new ArrayList<Object>(nodes2FilterSize);
249 // Set up a dummy context with a list to hold each node
250 Context predContext = new Context(support);
251 List<Object> tempList = new ArrayList<Object>(1);
252 predContext.setNodeSet(tempList);
253 // loop through the current nodes to filter and add to the
254 // filtered nodes list if the predicate succeeds
255 for (int i = 0; i < nodes2FilterSize; ++i) {
256 Object contextNode = nodes2Filter.get(i);
257 tempList.clear();
258 tempList.add(contextNode);
259 predContext.setNodeSet(tempList);
260 // ????
261 predContext.setPosition(i + 1);
262 predContext.setSize(nodes2FilterSize);
263 Object predResult = predicate.evaluate(predContext);
264 if (predResult instanceof Number) {
265 // Here we assume nodes are in forward or reverse order
266 // as appropriate for axis
267 int proximity = ((Number) predResult).intValue();
268 if (proximity == (i + 1)) {
269 filteredNodes.add(contextNode);
270 }
271 }
272 else {
273 Boolean includes =
274 BooleanFunction.evaluate(predResult,
275 predContext.getNavigator());
276 if (includes.booleanValue()) {
277 filteredNodes.add(contextNode);
278 }
279 }
280 }
281 return filteredNodes;
282 }
283
284 }