View Javadoc
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 }