View Javadoc

1   /*$Id: AstrogridAssert.java,v 1.9 2006/10/16 14:38:37 clq2 Exp $
2    * Created on 27-Aug-2004
3    *
4    * Copyright (C) AstroGrid. All rights reserved.
5    *
6    * This software is published under the terms of the AstroGrid 
7    * Software License version 1.2, a copy of which has been included 
8    * with this distribution in the LICENSE.txt file.  
9    *
10  **/
11  package org.astrogrid.test;
12  
13  import org.astrogrid.io.Piper;
14  import org.astrogrid.util.DomHelper;
15  
16  import org.apache.commons.logging.Log;
17  import org.apache.commons.logging.LogFactory;
18  import org.custommonkey.xmlunit.Validator;
19  import org.custommonkey.xmlunit.XMLAssert;
20  import org.w3c.dom.Document;
21  import org.w3c.dom.Element;
22  import org.xml.sax.InputSource;
23  import org.xml.sax.SAXException;
24  import org.xml.sax.SAXNotRecognizedException;
25  import org.xml.sax.SAXNotSupportedException;
26  import org.xml.sax.SAXParseException;
27  import org.xml.sax.XMLReader;
28  import org.xml.sax.helpers.DefaultHandler;
29  import org.xml.sax.helpers.XMLReaderFactory;
30  
31  import java.io.IOException;
32  import java.io.InputStream;
33  import java.io.InputStreamReader;
34  import java.io.StringReader;
35  import java.io.StringWriter;
36  import java.net.URL;
37  import java.util.Iterator;
38  import java.util.Map;
39  
40  import javax.xml.parsers.ParserConfigurationException;
41  
42  import junit.framework.Assert;
43  import junit.framework.AssertionFailedError;
44  
45  /*** class of static JUnit assertion methods, for asserting things relevant to astrogrid.
46   * extends XMLAssert - which makes assertions about xml documents - 
47   * this class builds upon this to check properties of votables, and assertions to validate against DTD and schema.
48   * @author Noel Winstanley nw@jb.man.ac.uk 27-Aug-2004
49   *
50   */
51  public class AstrogridAssert extends XMLAssert{
52      /***
53       * Commons Logger for this class
54       */
55      private static final Log logger = LogFactory.getLog(AstrogridAssert.class);
56      /*** Construct a new XMLAssertions
57       * 
58       */
59      private AstrogridAssert() {
60          super();
61      }
62      
63      /*** static initializer  - finds path to votable dtd.*/
64      static {
65          VOTABLE_SYSTEM_ID = AstrogridAssert.class.getResource("VOTable.dtd");
66      }
67      /*** system id for votable dtd - i.e. absolute location on classpath of votable dtd */
68      public static URL VOTABLE_SYSTEM_ID;
69      /*** doctype for votable */
70      public static final String VOTABLE_DOCTYPE = "VOTABLE";
71      
72      // assertions for votable.
73      /*** assert is a votable document 
74       * @param s string containing xml.
75       * 
76       */
77      public static void assertVotable(String s)  {
78          assertDTDValid(s,VOTABLE_DOCTYPE,VOTABLE_SYSTEM_ID);
79      }
80      /*** assert is a votable document 
81       * @param is input stream containing xml. */
82      public static void assertVotable(InputStream is) {
83          assertDTDValid(is,VOTABLE_DOCTYPE,VOTABLE_SYSTEM_ID);
84      }
85  
86      /*** assert is a votable document 
87       * @param d suspected votable.
88       * @asserts doctype, and dtd validates */
89      public static void assertVotable(Document d) {
90          assertDTDValid(d,VOTABLE_DOCTYPE,VOTABLE_SYSTEM_ID);
91      }
92      /*** assert is a votable document 
93       * @param e suspected votable.
94       * @asserts doctype, and dtd validates */    
95      public static void assertVotable(Element e) {
96          assertDTDValid(e,VOTABLE_DOCTYPE,VOTABLE_SYSTEM_ID);
97      }
98      
99      // the xml unit validation methods are a bit loosly typed - and poorly named if there's schema validation available too. 
100     // these are some wrapper methods with better typing.
101     /*** assert xml is DTD-valid.
102      * @param xml string containing xml
103      * @param doctype the DOCTYPE the xml should have (i.e. root element).
104      * @param dtdResource url of dtd file.
105      * 
106      */
107     public static void assertDTDValid(String xml,String doctype, URL dtdResource)  {
108         try {
109             assertXMLValid(xml,dtdResource.toString(),doctype);
110         } catch (SAXException e) {
111             fail("assertDTDValid: parse failure " + e.getMessage());
112         } catch (ParserConfigurationException e) {
113             fail("assertDTDValid: parser configuration failure " + e.getMessage());
114         }                
115     }
116     
117     /*** assert xml is DTD-valid
118      * @param d document to validate
119      * @param doctype the DOCTYPE that the xml should have (i.e. the root element).
120      * @param dtdResource url of dtd file.
121      */
122     public static void assertDTDValid(Document d,String doctype, URL dtdResource) {
123         try {
124             assertXMLValid(new Validator(d,dtdResource.toString(),doctype));
125         } catch (SAXException e) {
126             fail("assertDTDValid: parse failure " + e.getMessage());
127         } catch (ParserConfigurationException e) {
128             fail("assertDTDValid: parser configuration failure " + e.getMessage());
129         }         
130     }
131     
132     /*** assert xml is DTD valid.
133      * @param xmlStream stream of xml to validate
134      * @param doctype the DOCTYPE that the xml should have (ie. the expected root element)
135      * @param dtdResource url to dtd file.
136      */
137     public static void assertDTDValid(InputStream xmlStream, String doctype, URL dtdResource)  {
138         try {
139             assertXMLValid(new Validator(DomHelper.newDocument(xmlStream),dtdResource.toString(),doctype));
140         } catch (SAXException e) {
141             fail("assertDTDValid: parse failure " + e.getMessage());
142         } catch (ParserConfigurationException e) {
143             fail("assertDTDValid: parser configuration failure " + e.getMessage());       
144         } catch (IOException e) {
145             fail("assertDTDValid: failed to read from stream " + e.getMessage());
146         }
147     }
148     
149     /*** assert xml is DTD valid
150      * @param e xml to validate
151      * @param doctype the DOCTYPE that the xml should have (ie. the expected root element)
152      * @param dtdResource url to dtd file.
153      */
154     public static void assertDTDValid(Element e,String doctype,URL dtdResource)  {
155         try {
156             assertXMLValid(DomHelper.ElementToString(e),dtdResource.toString(),doctype);
157         } catch (SAXException e1) {
158             fail("assertDTDValid: parse failure " + e1.getMessage());
159         } catch (ParserConfigurationException e1) {
160             fail("assertDTDValid: parser configuration failure " + e1.getMessage());
161         }         
162     }
163     
164     /*** assert xml is schema valid.
165      * @param xml string containing xml to validate.
166      * @param rootElementName expected localname of the root element of the document 
167      * @param schemaLocations map of namespace - schema location associations.<br>
168      *  keys of map expected to be namespace (will be converted to java.lang.String via <tt>toString()</tt>), <br>
169      * values of map expected to be url locations of schema document (will be converted to java.lang.String via <tt>toString()</tt>)
170      * 
171      * @see org.astrogrid.test.schema.SchemaMap  in workflow-objects project
172      */
173     public static void assertSchemaValid(String xml,String rootElementName,Map schemaLocations) {
174         try {
175             assertXpathEvaluatesTo(rootElementName,"local-name(/*)",xml);
176         } catch (Exception e) {
177             fail("Failed to extract root element name " + e.getMessage());
178         }
179         AstrogridAssertDefaultHandler handler = new AstrogridAssertDefaultHandler();
180         XMLReader  parser = createParser(handler,schemaLocations);
181         InputSource source = new InputSource(new StringReader(xml));
182         try {
183             parser.parse(source);
184         } catch (Exception e) {
185             fail("failed to validate against schema: " + e.getMessage() + handler.getMessages());
186         }
187 
188     }
189     
190     /***assert xml is schema valid.
191      * @param d document to validate
192      * @param rootElementName expected localname of the root element of the document
193      * @param schemaLocations map of namespace - schema location associations.<br>
194      *  keys of map expected to be namespace (will be converted to java.lang.String via <tt>toString()</tt>), <br>
195      * values of map expected to be url locations of schema document (will be converted to java.lang.String via <tt>toString()</tt>)
196      * @see org.astrogrid.test.schema.SchemaMap  in workflow-objects project     * 
197      */
198     public static void assertSchemaValid(Document d,String rootElementName,Map schemaLocations) {
199         assertSchemaValid(DomHelper.DocumentToString(d),rootElementName,schemaLocations);       
200     }
201     
202     /***assert xml is schema valid.
203      * @param is stream of xml to validate
204      * @param rootElementName expected localname of the root element of the document
205      * @param schemaLocations map of namespace - schema location associations.<br>
206      *  keys of map expected to be namespace (will be converted to java.lang.String via <tt>toString()</tt>), <br>
207      * values of map expected to be url locations of schema document (will be converted to java.lang.String via <tt>toString()</tt>)
208      *     * @see org.astrogrid.test.schema.SchemaMap  in workflow-objects project 
209      */
210     public static void assertSchemaValid(InputStream is,String rootElementName,Map schemaLocations){ 
211        
212         try {
213             assertSchemaValid(getStreamContents(is),rootElementName,schemaLocations);
214         } catch (IOException e) {
215             fail("Failed to read from stream " + e.getMessage());
216         }
217     }
218     
219 
220     /***assert xml is schema valid.
221      * @param e element to validate
222      * @param rootElementName expected localname of the root element of the document
223      * @param schemaLocations map of namespace - schema location associations.<br>
224      *  keys of map expected to be namespace (will be converted to java.lang.String via <tt>toString()</tt>), <br>
225      * values of map expected to be url locations of schema document (will be converted to java.lang.String via <tt>toString()</tt>)
226      * @see org.astrogrid.test.schema.SchemaMap  in workflow-objects project     * 
227      */
228     public static void assertSchemaValid(Element e,String rootElementName,Map schemaLocations) {
229         assertSchemaValid(DomHelper.ElementToString(e),rootElementName,schemaLocations);
230     }
231 
232 
233     
234     
235     /*** create an xml parser, setup to validate using schema, register 
236      * appropriate schema locations, and an error handler 
237      * @param handler - handler to use
238      * @param schemaLocations
239      * @return
240      */
241     private static XMLReader createParser(DefaultHandler handler,Map schemaLocations)  {
242         
243         String locationString = mkSchemaLocationString(schemaLocations);
244         try {
245 
246             XMLReader reader = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
247             reader.setFeature("http://xml.org/sax/features/validation",true);
248             reader.setFeature("http://apache.org/xml/features/validation/schema",true);
249             reader.setProperty("http://apache.org/xml/properties/schema/external-schemaLocation",locationString);
250             reader.setErrorHandler(handler);
251             reader.setContentHandler(handler);
252             return reader;
253         } catch (SAXNotRecognizedException e) {
254             fail("required features not rcognized by this xml parser " + e.getMessage());
255         } catch (SAXNotSupportedException e) {
256             fail("required features not supported by this xml parser " + e.getMessage());
257         } catch (SAXException e) {
258             fail("cannot create xml reader " + e.getMessage());
259         }
260         throw new IllegalStateException("Cannot be reached");
261 
262         
263     }
264     /*** collapse the map into a space-separated string, suitable for passing to the parser */
265     private static String mkSchemaLocationString(Map schemaLocations) {
266         StringBuffer result = new StringBuffer();
267         for (Iterator i = schemaLocations.entrySet().iterator(); i.hasNext(); ) {
268             Map.Entry e = (Map.Entry)i.next();
269             result.append(e.getKey().toString());
270             result.append(' ');
271             result.append(e.getValue().toString());
272             result.append(' ');
273         }
274         return result.toString();
275     }
276     
277     // the basics -assert something is xml.
278     /*** assert that input is well formed xml
279      * @param xmlDocument document to check for wellformedness.
280      * 
281      */
282     public static void assertWellFormedXML(String xmlDocument) {
283         try {
284             assertNotNull("null document returned",DomHelper.newDocument(xmlDocument));
285         } catch (ParserConfigurationException e) {
286             Assert.fail("Problem with parser:" + e.getMessage());
287         } catch (SAXException e) {
288             Assert.fail("xmlDocument is not well formed:" + e.getMessage());            
289         } catch (IOException e) {
290             Assert.fail("Problem reading document:" + e.getMessage());
291         }
292     }
293     /*** assert that input is well formed xml
294      * @param xmlStream stream of content to check.
295      */
296     public static void assertWellFormedXML(InputStream xmlStream) {
297         try {
298             assertNotNull("null document returned",DomHelper.newDocument(xmlStream));
299         } catch (ParserConfigurationException e) {
300             Assert.fail("Problem with parser:" + e.getMessage());
301         } catch (SAXException e) {
302             Assert.fail("xmlDocument is not well formed:" + e.getMessage());            
303         } catch (IOException e) {
304             Assert.fail("Problem reading document:" + e.getMessage());
305         }        
306     }
307     
308     
309     // helper methods.
310     /***
311      * load a resource into a string.
312      * @asserts resource can be found, and is not null
313      * @param c class that resorce path is relative to
314      * @param resource path to resurce
315      * @return comtents of this resource.
316      * @throws IOException
317      */
318     public static String getResourceAsString(Class c, String resource) throws IOException {
319        InputStream is = c.getResourceAsStream(resource);
320        String script = AstrogridAssert.getStreamContents(is);
321        assertNotNull(script);
322        return script;
323     }
324 
325     /*** read stream contents into a string */
326     public static String getStreamContents(InputStream is) throws IOException {
327        assertNotNull(is);
328        StringWriter sw = new StringWriter();
329        Piper.bufferedPipe(new InputStreamReader(is), sw);
330        return sw.toString();
331     }    
332     /*** handler passed to schema-validation parses - logs all errors, throws assertion failed if errors seen by end */
333     static class AstrogridAssertDefaultHandler extends DefaultHandler {
334       private StringBuffer buff = new StringBuffer();
335       
336       public String getMessages() {
337         return buff.toString();
338       }
339             
340       boolean sawError = false;
341       
342       public void endDocument() throws SAXException {
343         if (sawError) {
344           throw new AssertionFailedError("The document is invalid with respect to its schema" 
345                                          + this.getMessages());
346         }
347       }
348 
349       public void error(SAXParseException e) throws SAXException {
350         sawError = true;
351         System.err.println("Error:" + this.getMessage(e));
352         this.buff.append("\n").append(this.getMessage(e));
353       }
354       
355       public void fatalError(SAXParseException e) throws SAXException {
356         sawError = true;
357         System.err.println("Fatal: " + this.getMessage(e));
358         this.buff.append("\n").append(this.getMessage(e));            
359       }
360       
361       public void warning(SAXParseException e) throws SAXException {
362         System.err.println("Warn: " + this.getMessage(e));
363         this.buff.append("\n").append(this.getMessage(e));           
364       }
365       
366       private String getMessage(SAXParseException e) {
367         return e.getMessage() +
368                " Found at line " +
369                e.getLineNumber() +
370                ", column " +
371                e.getColumnNumber() +
372                " in " +
373                e.getSystemId();
374       }
375     }
376     
377 }
378 
379 
380 /* 
381 $Log: AstrogridAssert.java,v $
382 Revision 1.9  2006/10/16 14:38:37  clq2
383 common-gtr-1933
384 
385 Revision 1.8.116.1  2006/10/13 17:48:29  gtr
386 Error reports in schema validation now report the line and column numbers where the problems are found.
387 
388 Revision 1.8  2004/11/19 09:36:27  clq2
389 common_nww_712
390 
391 Revision 1.7.42.1  2004/11/18 15:25:12  nw
392 schema assertions that fail to validate report parse errors.
393 
394 Revision 1.7  2004/09/03 09:18:03  nw
395 got schema validation asserts working.
396 
397 Revision 1.6  2004/09/02 11:25:25  nw
398 got schema validation working.
399 
400 Revision 1.5  2004/09/02 10:14:00  nw
401 improved documentation
402 
403 Revision 1.4  2004/09/02 09:55:53  nw
404 fixed bug.
405 
406 Revision 1.3  2004/09/02 01:32:17  nw
407 added in assertDTDValid() and assertSchemaValid() methods.
408 
409 Revision 1.2  2004/08/27 12:48:08  nw
410 started class of static assertions relevant to astrogird - assertVotable() for starters.
411 Also provides methods to check format of xml documents.
412 
413 Revision 1.1  2004/08/27 12:42:58  nw
414 started class of static assertions relevant to astrogird - assertVotable() for starters.
415 Also provides methods to check format of xml documents.
416  
417 */