View Javadoc

1   /*
2    * $Header: /home/projects/jaxen/scm/jaxen/src/java/main/org/jaxen/expr/PredicateSet.java,v 1.23 2006/11/13 22:38:17 elharo Exp $
3    * $Revision: 1.23 $
4    * $Date: 2006/11/13 22:38:17 $
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: PredicateSet.java,v 1.23 2006/11/13 22:38:17 elharo Exp $
47   */
48  
49  
50  
51  package org.jaxen.expr;
52  
53  import java.io.Serializable;
54  import java.util.ArrayList;
55  import java.util.Collections;
56  import java.util.Iterator;
57  import java.util.List;
58  import org.jaxen.Context;
59  import org.jaxen.ContextSupport;
60  import org.jaxen.JaxenException;
61  import org.jaxen.function.BooleanFunction;
62  
63  /***
64   * <p>
65   * Represents the collection of predicates that follow the node-test in a
66   * location path. 
67   * </p>
68   * 
69   * <p>
70   * There is no rule that the same predicate may not 
71   * appear twice in an XPath expression, nor does this class enforce any such rule.
72   * This is implemented more as a list than a set. However, adding the swme predicate 
73   * twice should have no effect on the final result other than slowing it down.
74   * </p>
75   */
76  public class PredicateSet implements Serializable
77  {
78  
79      private static final long serialVersionUID = -7166491740228977853L;
80      
81      private List predicates;
82  
83      /***
84       * Create a new empty predicate set.
85       */
86      public PredicateSet()
87      {
88          this.predicates = Collections.EMPTY_LIST;
89      }
90  
91      /***
92       * Add a predicate to the set.
93       * 
94       * @param predicate the predicate to be inserted
95       */
96      public void addPredicate(Predicate predicate)
97      {
98          if ( this.predicates == Collections.EMPTY_LIST )
99          {
100             this.predicates = new ArrayList();
101         }
102 
103         this.predicates.add( predicate );
104     }
105 
106     /***
107      * Returns the list containing the predicates.
108      * This list is live, not a copy.
109      * 
110      * @return a live list of predicates
111      */
112     public List getPredicates()
113     {
114         return this.predicates;
115     }
116 
117     /***
118      * Simplify each of the predicates in the list.
119      */
120     public void simplify()
121     {
122         Iterator  predIter = this.predicates.iterator();
123         Predicate eachPred = null;
124 
125         while ( predIter.hasNext() )
126         {
127             eachPred = (Predicate) predIter.next();
128             eachPred.simplify();
129         }
130     }
131 
132     /***
133      * Returns the XPath string containing each of the predicates.
134      * 
135      * @return the XPath string containing each of the predicates
136      */
137     public String getText()
138     {
139         StringBuffer buf = new StringBuffer();
140 
141         Iterator  predIter = this.predicates.iterator();
142         Predicate eachPred = null;
143 
144         while ( predIter.hasNext() )
145         {
146             eachPred = (Predicate) predIter.next();
147             buf.append( eachPred.getText() );
148         }
149 
150         return buf.toString();
151     }
152 
153     /***
154      * <p>Returns true if any of the supplied nodes satisfy 
155      * all the predicates in the set. Returns false if none of the supplied
156      * nodes matches all the predicates in the set. Returns false if the 
157      * node-set is empty.</p>
158      * 
159      * @param contextNodeSet the nodes to test against these predicates
160      * @param support ????
161      * @return true if any node in the contextNodeSet matches all the predicates
162      * @throws JaxenException
163      */
164     protected boolean evaluateAsBoolean(List contextNodeSet,
165                                       ContextSupport support) throws JaxenException
166     {
167         return anyMatchingNode( contextNodeSet, support );
168     }
169 
170    private boolean anyMatchingNode(List contextNodeSet, ContextSupport support)
171      throws JaxenException {
172         // Easy way out (necessary)
173         if (predicates.size() == 0) {
174             return false;
175         }
176         Iterator predIter = predicates.iterator();
177 
178         // initial list to filter
179         List nodes2Filter = contextNodeSet;
180         // apply all predicates
181         while(predIter.hasNext()) {
182             final int nodes2FilterSize = nodes2Filter.size();
183             // Set up a dummy context with a list to hold each node
184             Context predContext = new Context(support);
185             List tempList = new ArrayList(1);
186             predContext.setNodeSet(tempList);
187             // loop through the current nodes to filter and add to the
188             // filtered nodes list if the predicate succeeds
189             for (int i = 0; i < nodes2FilterSize; ++i) {
190                 Object contextNode = nodes2Filter.get(i);
191                 tempList.clear();
192                 tempList.add(contextNode);
193                 predContext.setNodeSet(tempList);
194                 // ????
195                 predContext.setPosition(i + 1);
196                 predContext.setSize(nodes2FilterSize);
197                 Object predResult = ((Predicate)predIter.next()).evaluate(predContext);
198                 if (predResult instanceof Number) {
199                     // Here we assume nodes are in forward or reverse order
200                     // as appropriate for axis
201                     int proximity = ((Number) predResult).intValue();
202                     if (proximity == (i + 1)) {
203                         return true;
204                     }
205                 }
206                 else {
207                     Boolean includes =
208                         BooleanFunction.evaluate(predResult,
209                                                 predContext.getNavigator());
210                     if (includes.booleanValue()) {
211                         return true;
212                     }
213                 }
214             }
215         }
216         
217         return false;
218     }
219    
220     
221     
222     
223    /***
224     * <p>Returns all of the supplied nodes that satisfy 
225     * all the predicates in the set. </p>
226     * 
227     * @param contextNodeSet the nodes to test against these predicates
228     * @param support ????
229     * @return all the nodes that match each of the predicates
230     * @throws JaxenException
231     */
232    protected List evaluatePredicates(List contextNodeSet, ContextSupport support)
233             throws JaxenException {
234         // Easy way out (necessary)
235         if (predicates.size() == 0) {
236             return contextNodeSet;
237         }
238         Iterator predIter = predicates.iterator();
239 
240         // initial list to filter
241         List nodes2Filter = contextNodeSet;
242         // apply all predicates
243         while(predIter.hasNext()) {
244             nodes2Filter =
245                 applyPredicate((Predicate)predIter.next(), nodes2Filter, support);
246         }
247         
248         return nodes2Filter;
249     }
250    
251     public List applyPredicate(Predicate predicate, List nodes2Filter, ContextSupport support)
252             throws JaxenException {
253         final int nodes2FilterSize = nodes2Filter.size();
254         List filteredNodes = new ArrayList(nodes2FilterSize);
255         // Set up a dummy context with a list to hold each node
256         Context predContext = new Context(support);
257         List tempList = new ArrayList(1);
258         predContext.setNodeSet(tempList);
259         // loop through the current nodes to filter and add to the
260         // filtered nodes list if the predicate succeeds
261         for (int i = 0; i < nodes2FilterSize; ++i) {
262             Object contextNode = nodes2Filter.get(i);
263             tempList.clear();
264             tempList.add(contextNode);
265             predContext.setNodeSet(tempList);
266             // ????
267             predContext.setPosition(i + 1);
268             predContext.setSize(nodes2FilterSize);
269             Object predResult = predicate.evaluate(predContext);
270             if (predResult instanceof Number) {
271                 // Here we assume nodes are in forward or reverse order
272                 // as appropriate for axis
273                 int proximity = ((Number) predResult).intValue();
274                 if (proximity == (i + 1)) {
275                     filteredNodes.add(contextNode);
276                 }
277             }
278             else {
279                 Boolean includes =
280                     BooleanFunction.evaluate(predResult,
281                                             predContext.getNavigator());
282                 if (includes.booleanValue()) {
283                     filteredNodes.add(contextNode);
284                 }
285             }
286         }
287         return filteredNodes;
288     }
289     
290 }