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   * Redistribution and use in source and binary forms, with or without
12   * modification, are permitted provided that the following conditions are
13   * met:
14   * 
15   *   * Redistributions of source code must retain the above copyright
16   *     notice, this list of conditions and the following disclaimer.
17   * 
18   *   * Redistributions in binary form must reproduce the above copyright
19   *     notice, this list of conditions and the following disclaimer in the
20   *     documentation and/or other materials provided with the distribution.
21   * 
22   *   * Neither the name of the Jaxen Project nor the names of its
23   *     contributors may be used to endorse or promote products derived 
24   *     from this software without specific prior written permission.
25   * 
26   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
27   * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28   * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
29   * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
30   * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37   *
38   * ====================================================================
39   * This software consists of voluntary contributions made by many 
40   * individuals on behalf of the Jaxen Project and was originally 
41   * created by bob mcwhirter <bob@werken.com> and 
42   * James Strachan <jstrachan@apache.org>.  For more information on the 
43   * Jaxen Project, please see <http://www.jaxen.org/>.
44   * 
45   * $Id$
46   */
47  
48  package org.jaxen.pattern;
49  
50  import java.util.ArrayList;
51  import java.util.Iterator;
52  import java.util.List;
53  
54  import org.jaxen.Context;
55  import org.jaxen.JaxenException;
56  import org.jaxen.Navigator;
57  import org.jaxen.expr.FilterExpr;
58  import org.jaxen.util.SingletonList;
59  
60  /** <p><code>LocationPathPattern</code> matches any node using a
61    * location path such as A/B/C.
62    * The parentPattern and ancestorPattern properties are used to
63    * chain location path patterns together</p>
64    *
65    * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
66    * @version $Revision$
67    */
68  public class LocationPathPattern extends Pattern {
69  
70      /** The node test to perform on this step of the path */
71      private NodeTest nodeTest = AnyNodeTest.getInstance();
72      
73      /** Patterns matching my parent node */
74      private Pattern parentPattern;
75      
76      /** Patterns matching one of my ancestors */
77      private Pattern ancestorPattern;
78          
79      /** The filters to match against */
80      private List filters;
81  
82      /** Whether this lcoation path is absolute or not */
83      private boolean absolute;
84      
85      
86      public LocationPathPattern()   
87      {
88      }
89  
90      public LocationPathPattern(NodeTest nodeTest)   
91      {
92          this.nodeTest = nodeTest;
93      }
94  
95      public Pattern simplify()
96      {
97          if ( parentPattern != null )
98          {
99              parentPattern = parentPattern.simplify();
100         }
101         if ( ancestorPattern != null )
102         {
103             ancestorPattern = ancestorPattern.simplify();
104         }
105         if ( filters == null )
106         {
107             if ( parentPattern == null && ancestorPattern == null )
108             {
109                 return nodeTest;
110             }
111             if ( parentPattern != null && ancestorPattern == null )
112             {
113                 if ( nodeTest instanceof AnyNodeTest )
114                 {
115                     return parentPattern;
116                 }
117             }
118         }
119         return this;
120     }
121     
122     /** Adds a filter to this pattern
123      */
124     public void addFilter(FilterExpr filter) 
125     {
126         if ( filters == null )
127         {
128             filters = new ArrayList();
129         }
130         filters.add( filter );
131     }
132     
133     /** Adds a pattern for the parent of the current
134      * context node used in this pattern.
135      */
136     public void setParentPattern(Pattern parentPattern) 
137     {
138         this.parentPattern = parentPattern;
139     }
140     
141     /** Adds a pattern for an ancestor of the current
142      * context node used in this pattern.
143      */
144     public void setAncestorPattern(Pattern ancestorPattern) 
145     {
146         this.ancestorPattern = ancestorPattern;
147     }
148     
149     /** Allows the NodeTest to be set
150      */
151     public void setNodeTest(NodeTest nodeTest) throws JaxenException
152     {
153         if ( this.nodeTest instanceof AnyNodeTest )
154         {
155             this.nodeTest = nodeTest;
156         }   
157         else 
158         {
159             throw new JaxenException( "Attempt to overwrite nodeTest: " + this.nodeTest + " with: " + nodeTest );
160         }
161     }
162     
163     /** @return true if the pattern matches the given node
164       */
165     public boolean matches( Object node, Context context ) throws JaxenException
166     {
167         Navigator navigator = context.getNavigator();
168 
169 /*        
170         if ( isAbsolute() )
171         {
172             node = navigator.getDocumentNode( node );
173         }
174 */
175         if (! nodeTest.matches(node, context) )
176         {
177             return false;
178         }
179         
180         if (parentPattern != null) 
181         {
182             Object parent = navigator.getParentNode( node );
183             if ( parent == null ) 
184             {
185                 return false;
186             }
187             if ( ! parentPattern.matches( parent, context ) ) 
188             {
189                 return false;
190             }
191         }
192 
193         if (ancestorPattern != null) {
194             Object ancestor = navigator.getParentNode( node );
195             while (true)
196             {
197                 if ( ancestorPattern.matches( ancestor, context ) )
198                 {
199                     break;
200                 }
201                 if ( ancestor == null )
202                 {
203                     return false;
204                 }
205                 if ( navigator.isDocument( ancestor ) )
206                 {
207                     return false;
208                 }
209                 ancestor = navigator.getParentNode( ancestor );
210             }
211         }
212         
213         if (filters != null) 
214         {
215             List list = new SingletonList(node);
216 
217             context.setNodeSet( list );
218             
219             // XXXX: filters aren't positional, so should we clone context?
220 
221             boolean answer = true;
222 
223             for (Iterator iter = filters.iterator(); iter.hasNext(); ) 
224             {
225                 FilterExpr filter = (FilterExpr) iter.next();
226 
227                 if ( ! filter.asBoolean( context ) )
228                 {
229                     answer = false;
230                     break;
231                 }
232             }
233             // restore context
234 
235             context.setNodeSet( list );
236 
237             return answer;
238         }
239         return true;
240     }
241     
242     public double getPriority() 
243     {
244         if ( filters != null ) 
245         {
246             return 0.5;
247         }
248         return nodeTest.getPriority();
249     }
250 
251 
252     public short getMatchType() 
253     {
254         return nodeTest.getMatchType();
255     }
256     
257     public String getText() 
258     {
259         StringBuffer buffer = new StringBuffer();
260         if ( absolute )
261         {
262             buffer.append( "/" );
263         }
264         if (ancestorPattern != null) 
265         {
266             String text = ancestorPattern.getText();
267             if ( text.length() > 0 )
268             {
269                 buffer.append( text );
270                 buffer.append( "//" );
271             }
272         }
273         if (parentPattern != null) 
274         {
275             String text = parentPattern.getText();
276             if ( text.length() > 0 )
277             {
278                 buffer.append( text );
279                 buffer.append( "/" );
280             }
281         }
282         buffer.append( nodeTest.getText() );
283         
284         if ( filters != null ) 
285         {
286             buffer.append( "[" );
287             for (Iterator iter = filters.iterator(); iter.hasNext(); ) 
288             {
289                 FilterExpr filter = (FilterExpr) iter.next();
290                 buffer.append( filter.getText() );
291             }
292             buffer.append( "]" );
293         }        
294         return buffer.toString();
295     }
296     
297     @Override
298     public String toString()
299     {
300         return super.toString() + "[ absolute: " + absolute + " parent: " + parentPattern + " ancestor: " 
301             + ancestorPattern + " filters: " + filters + " nodeTest: " 
302             + nodeTest + " ]";
303     }
304     
305     public boolean isAbsolute()
306     {
307         return absolute;
308     }
309     
310     public void setAbsolute(boolean absolute)
311     {
312         this.absolute = absolute;
313     }
314     
315     public boolean hasAnyNodeTest()
316     {
317         return nodeTest instanceof AnyNodeTest;
318     }
319         
320 }