1   /*
2    * $Header: /home/projects/jaxen/scm/jaxen/src/java/test/org/jaxen/XPathTestBase.java,v 1.31 2005/04/05 17:45:05 elharo Exp $
3    * $Revision: 1.31 $
4    * $Date: 2005/04/05 17:45:05 $
5    *
6    * ====================================================================
7    *
8    * Copyright (C) 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
13   * are met:
14   * 
15   * 1. Redistributions of source code must retain the above copyright
16   *    notice, this list of conditions, and the following disclaimer.
17   *
18   * 2. Redistributions in binary form must reproduce the above copyright
19   *    notice, this list of conditions, and the disclaimer that follows 
20   *    these conditions in the documentation and/or other materials 
21   *    provided with the distribution.
22   *
23   * 3. The name "Jaxen" must not be used to endorse or promote products
24   *    derived from this software without prior written permission.  For
25   *    written permission, please contact license@jaxen.org.
26   * 
27   * 4. Products derived from this software may not be called "Jaxen", nor
28   *    may "Jaxen" appear in their name, without prior written permission
29   *    from the Jaxen Project Management (pm@jaxen.org).
30   * 
31   * In addition, we request (but do not require) that you include in the 
32   * end-user documentation provided with the redistribution and/or in the 
33   * software itself an acknowledgement equivalent to the following:
34   *     "This product includes software developed by the
35   *      Jaxen Project (http://www.jaxen.org/)."
36   * Alternatively, the acknowledgment may be graphical using the logos 
37   * available at http://www.jaxen.org/
38   *
39   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
40   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
41   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
42   * DISCLAIMED.  IN NO EVENT SHALL THE Jaxen AUTHORS OR THE PROJECT
43   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
45   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
46   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
47   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
48   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
49   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50   * SUCH DAMAGE.
51   *
52   * ====================================================================
53   * This software consists of voluntary contributions made by many 
54   * individuals on behalf of the Jaxen Project and was originally 
55   * created by bob mcwhirter <bob@werken.com> and 
56   * James Strachan <jstrachan@apache.org>.  For more information on the 
57   * Jaxen Project, please see <http://www.jaxen.org/>.
58   * 
59   * $Id: XPathTestBase.java,v 1.31 2005/04/05 17:45:05 elharo Exp $
60   */
61  
62  
63  package org.jaxen;
64  
65  import junit.framework.TestCase;
66  
67  import java.io.File;
68  import java.util.ArrayList;
69  import java.util.Iterator;
70  import java.util.List;
71  import java.util.Stack;
72  
73  import javax.xml.parsers.DocumentBuilder;
74  import javax.xml.parsers.DocumentBuilderFactory;
75  import javax.xml.parsers.ParserConfigurationException;
76  
77  import org.jaxen.function.StringFunction;
78  import org.jaxen.saxpath.helpers.XPathReaderFactory;
79  import org.w3c.dom.Attr;
80  import org.w3c.dom.Document;
81  import org.w3c.dom.Element;
82  import org.w3c.dom.NamedNodeMap;
83  import org.w3c.dom.Node;
84  import org.w3c.dom.NodeList;
85  
86  public abstract class XPathTestBase extends TestCase
87  {
88      protected static String VAR_URI   = "http://jaxen.org/test-harness/var";
89      protected static String TESTS_XML = "xml/test/tests.xml";
90  
91      protected static boolean verbose = true;
92  
93      private DocumentBuilder builder;
94      private ContextSupport  contextSupport;
95  
96      private Stack executionContext = new Stack();
97  
98      public XPathTestBase(String name)
99      {
100         super( name );
101     }
102 
103     public void setUp() throws ParserConfigurationException
104     {
105         this.contextSupport = null;
106         System.setProperty( XPathReaderFactory.DRIVER_PROPERTY,
107                             "" );
108         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
109         factory.setNamespaceAware(true);
110         builder = factory.newDocumentBuilder();
111         
112     }
113 
114     public void log(String text)
115     {
116         log( verbose,
117              text );
118     }
119 
120     public void log(boolean actualVerbose,
121                     String text)
122     {
123         if ( ! actualVerbose )
124         {
125             return;
126         }
127             
128         System.out.println( text );
129     }
130 
131     public void testXPaths() throws Exception
132     {
133         log( "Loading test file: " + TESTS_XML );
134         
135         File f = new File( TESTS_XML);
136         Document doc = builder.parse( f );
137 
138         NodeList documents = doc.getElementsByTagName("document");
139         for (int i = 0; i < documents.getLength(); i++)
140         {
141             Element eachDocElem = (Element) documents.item(i);
142             testDocument( eachDocElem );
143         }
144     }
145 
146     protected void testDocument(Element docElem) throws Exception
147     {
148         String url = docElem.getAttribute( "url" );
149 
150         Object testDoc = getDocument( url );
151 
152         log( "-----------------------------" );
153         log( "Document [" + url + "]" );
154 
155         this.executionContext.push( url );
156 
157         NodeList contexts = docElem.getElementsByTagName( "context" );
158 
159         for (int i = 0; i < contexts.getLength(); i++)
160         {
161             Element context = (Element) contexts.item(i);
162 
163             testContext( testDoc,
164                          context );
165         }
166 
167         this.executionContext.pop();
168 
169         log( "-----------------------------" );
170     }
171 
172     protected void testContext(Object testDoc,
173                                Element contextElem) throws Exception
174     {
175         setupNamespaceContext( contextElem );
176         setupVariableContext( contextElem );
177 
178         String xpathStr = contextElem.getAttribute( "select" );
179 
180         log( "Initial Context :: " + xpathStr );
181 
182         this.executionContext.push( xpathStr );
183 
184         BaseXPath xpath = new BaseXPath( xpathStr );
185 
186         List list = xpath.selectNodes( getContext( testDoc ) );
187 
188         Iterator iter = list.iterator();
189         Object   contextNode = null;
190 
191         while ( iter.hasNext() )
192         {
193             contextNode = iter.next();
194             runTests( contextElem, contextNode );
195         }
196 
197         this.executionContext.pop();
198     }
199 
200     protected void runTests(Element contextElem,
201                             Object context) throws Exception
202     {
203         NodeList countTests = contextElem.getElementsByTagName( "test" );
204 
205         for (int i = 0; i < countTests.getLength(); i++)
206         {
207             Element test = (Element) countTests.item(i);
208             runTest( context, test );
209         }
210 
211         NodeList valueOfs = contextElem.getChildNodes();
212 
213         for (int i = 0; i < valueOfs.getLength(); i++)
214         { 
215             Node node = valueOfs.item(i);
216             if (node.getNodeType() == Node.ELEMENT_NODE) {
217                 Element valueOf = (Element) valueOfs.item(i);
218                 if (valueOf.getNodeName().equals("valueOf")) {
219                     testValueOf( context, valueOf );
220                 }
221             }
222         }
223     }
224 
225     protected void runTest(Object context,
226                            Element test) throws Exception
227     {
228         String xpathStr = test.getAttribute( "select" );
229         String debugStr  = test.getAttribute( "debug" );
230         boolean debug = verbose;
231 
232         if ( ! "".equals(debugStr) )
233         {
234             if ( "true".equals( debugStr ) || "on".equals( debugStr ) )
235             {
236                 debug = true;
237             }
238         }
239 
240         log( debug, "  Select :: " + xpathStr );
241 
242         this.executionContext.push( xpathStr );
243 
244         String count = test.getAttribute( "count" );
245         String exception = test.getAttribute( "exception" );
246 
247         try 
248         {
249             BaseXPath xpath = new BaseXPath( xpathStr );
250             List results = xpath.selectNodes( getContext( context ) );
251             if ( ! "".equals(count) )
252             {
253                 int expectedSize = Integer.parseInt( count );
254                 log ( debug,
255                       "    Expected Size :: " + expectedSize );
256                 log ( debug,
257                       "    Result Size   :: " + results.size() );
258 
259                 if ( expectedSize != results.size() )
260                 {
261                     log ( debug,
262                           "      ## FAILED" );
263                     log ( debug,
264                           "      ## xpath: " + xpath + " = " + xpath.debug() );
265 
266                     Iterator resultIter = results.iterator();
267 
268                     while ( resultIter.hasNext() )
269                     {
270                         log ( debug,
271                               "      --> " + resultIter.next() );
272                     }
273                 }
274                 assertEquals( this.executionContext.toString(),
275                               expectedSize,
276                               results.size() );
277 
278                 if (exception !=null && (exception.equals("on") || exception.equals("true"))) {
279                     fail("An exception was expected.");
280                 }
281 
282             }
283             
284             // XXX Now test any valueOf children of the test element
285             NodeList valueOfs = test.getElementsByTagName("valueOf");
286             for (int i = 0; i < valueOfs.getLength(); i++) {
287                 Element valueOf = (Element) valueOfs.item(i);
288                 testValueOf( results.get(0), valueOf);
289             }
290             
291         }
292         catch (UnsupportedAxisException e)
293         {
294             log ( debug,
295                   "      ## SKIPPED -- Unsupported Axis" );
296         }
297         catch (JaxenException e) {
298             // If an exception attribute was switched on, this is the desired behavior.
299             if (exception.equals("on") || exception.equals("true")) {
300                 log (debug, "    Caught expected exception "+e.getMessage());
301             } 
302             else throw e;
303         }
304 
305         this.executionContext.pop();
306     }
307 
308     protected void testValueOf(Object context,
309                                Element valueOf) throws Exception
310     {
311         String xpathStr = valueOf.getAttribute( "select" );
312         String debugStr = valueOf.getAttribute( "debug" );
313         boolean debug = verbose;
314 
315         if ( ! "".equals(debugStr) )
316         {
317             if ( "true".equals( debugStr )
318                  ||
319                  "on".equals( debugStr ) )
320             {
321                 debug = true;
322             }
323         }
324 
325         BaseXPath xpath = new BaseXPath( xpathStr );
326 
327         this.executionContext.push( xpathStr );
328 
329         try
330         {
331             Object node = xpath.evaluate( getContext( context ) );
332             
333             String expected = getFullText(valueOf);
334             String result = StringFunction.evaluate( node,
335                                                      getNavigator() );
336             
337             log ( debug,
338                   "  Select :: " + xpathStr );
339             log ( debug,
340                   "    Expected :: " + expected );
341             log ( debug,
342                   "    Result   :: " + result );
343             
344             if ( ! expected.equals( result ) )
345             {
346                 log ( debug,
347                       "      ## FAILED" );
348                 log ( debug,
349                       "      ## xpath: " + xpath + " = " + xpath.debug() );
350             }
351             
352             assertEquals( this.executionContext.toString(),
353                           expected,
354                           result );
355         }
356         catch (UnsupportedAxisException e)
357         {
358             log ( debug,
359                   "      ## SKIPPED -- Unsupported Axis " );
360 
361         }
362 
363         this.executionContext.pop();
364     }
365                                
366     protected Context getContext(Object contextNode)
367     {
368         Context context = new Context( getContextSupport() );
369 
370         List list = new ArrayList( 1 );
371         list.add( contextNode );
372         context.setNodeSet( list );
373 
374         return context;
375     }
376 
377     public ContextSupport getContextSupport()
378     {
379         if ( this.contextSupport == null )
380         {
381             this.contextSupport = new ContextSupport( new SimpleNamespaceContext(),
382                                                       XPathFunctionContext.getInstance(),
383                                                       new SimpleVariableContext(),
384                                                       getNavigator() );
385         }
386 
387         return this.contextSupport;
388     }
389 
390     public abstract Navigator getNavigator();
391 
392     public abstract Object getDocument(String url) throws Exception;
393 
394     private void setupNamespaceContext(Element contextElem)
395     {
396         SimpleNamespaceContext nsContext = new SimpleNamespaceContext();
397 
398         NamedNodeMap attributes = contextElem.getAttributes();
399 
400         for (int i = 0; i < attributes.getLength(); i++ )
401         {
402             Attr attr = (Attr) attributes.item(i);
403             if ("http://www.w3.org/2000/xmlns/".equals(attr.getNamespaceURI())) {
404 
405                 String prefix = attr.getLocalName();
406                 String uri    = attr.getNodeValue();
407     
408                 nsContext.addNamespace( prefix, uri );
409     
410                 System.out.println( " ---> " + prefix + " == " + uri );
411             }
412         }
413 
414         getContextSupport().setNamespaceContext( nsContext );
415     }
416 
417     private void setupVariableContext(Element contextElem)
418     {
419         SimpleVariableContext varContext = new SimpleVariableContext();
420 
421         NamedNodeMap varIter = contextElem.getAttributes();
422         
423         for (int i = 0; i < varIter.getLength(); i++ )
424         {
425             Attr eachVar = (Attr) varIter.item(i);
426 
427             if (  VAR_URI.equals(eachVar.getNamespaceURI()) )
428             {
429                 String varName  = eachVar.getLocalName();
430                 String varValue = eachVar.getNodeValue();
431 
432                 varContext.setVariableValue( null,
433                                              varName,
434                                              varValue );
435             }
436         }
437 
438         getContextSupport().setVariableContext( varContext );
439     }
440     
441     private static String getFullText(Node node) {
442         StringBuffer result = new StringBuffer(12);
443         NodeList children = node.getChildNodes();
444         for (int i = 0; i < children.getLength(); i++) {
445           Node child = children.item(i);
446           int type = child.getNodeType();
447           if (type == Node.TEXT_NODE) {
448             result.append(child.getNodeValue());
449           }
450           else if (type == Node.CDATA_SECTION_NODE) {
451             result.append(child.getNodeValue());
452           }
453           else if (type == Node.ELEMENT_NODE) {
454             result.append(getFullText(child));
455           }
456           else if (type == Node.ENTITY_REFERENCE_NODE) {
457             result.append(getFullText(child));
458           }
459         }
460         return result.toString();
461     }
462     
463 }