OSDN Git Service

Pre-empt facility to support package visibility attributes.
[mingw/mingw-dist.git] / tests / xmlcheck.cpp
1 /*
2  * xmlcheck.cpp
3  *
4  * $Id$
5  *
6  * Adapted from load-grammar-dom.cxx
7  * Written by Boris Kolpackov <boris@codesynthesis.com>
8  * Assigned, by the author, to the public domain
9  *
10  * This program uses Xerces-C++ DOM parser to load a set of schema files
11  * and then to validate a set of XML documents against these schemas. To
12  * build this program you will need Xerces-C++ 3.0.0 or later. For more
13  * information, see:
14  *
15  * http: *www.codesynthesis.com/~boris/blog/2010/03/15/validating-external-schemas-xerces-cxx/
16  *
17  *
18  * Adaptation by Keith Marshall <keithmarshall@users.sourceforge.net>
19  * Copyright (C) 2013, MinGW.org Project
20  *
21  * This is free software.  Permission is granted to copy, modify and
22  * redistribute this software, under the provisions of the GNU General
23  * Public License, Version 3, (or, at your option, any later version),
24  * as published by the Free Software Foundation; see the file COPYING
25  * for licensing details.
26  *
27  * Note, in particular, that this software is provided "as is", in the
28  * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
29  * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
30  * PARTICULAR PURPOSE.  Under no circumstances will the author, or the
31  * MinGW Project, accept liability for any damages, however caused,
32  * arising from the use of this software.
33  *
34  */
35 #define __STDC_FORMAT_MACROS  1
36 #include <inttypes.h>   /* for PRIu64        */
37
38 #include <cstdio>       /* for fprintf()     */
39 #include <string>
40 #include <memory>       /* for std::auto_ptr */
41 #include <cstddef>      /* for std::size_t   */
42
43 #include <libgen.h>     /* for basename()    */
44
45 #include <xercesc/util/XMLUni.hpp>
46 #include <xercesc/util/XMLString.hpp>
47 #include <xercesc/util/PlatformUtils.hpp>
48
49 #include <xercesc/dom/DOM.hpp>
50
51 #include <xercesc/validators/common/Grammar.hpp>
52 #include <xercesc/framework/XMLGrammarPoolImpl.hpp>
53
54 using namespace std;
55 using namespace xercesc;
56
57 #if _XERCES_VERSION < 30000
58 /* We need at least Xerces-C++ version 3.0.0
59  */
60 # error Xerces-C++ version >= 3.0.0 is required!
61
62 #elif _XERCES_VERSION >= 30100
63 /* We may wish to exploit some features which were not introduced
64  * until Xerces-C++ version 3.1.0
65  */
66 # define IF_XERCES_30100_PLUS( STATEMENT )  STATEMENT
67
68 #else
69 /* We cannot use Xerces-C++ version 3.1.0 features; make them no-op.
70  */
71 # define IF_XERCES_30100_PLUS( STATEMENT )
72 #endif
73
74 class error_handler: public DOMErrorHandler
75 {
76   /* A locally defined class for capture of fault conditions, as
77    * reported by our DOM parsers.
78    */
79   public:
80     /* Constructor.
81      */
82     error_handler( const char *rel): source(rel),
83       first_report(true), new_document(true), failed(false){}
84
85     /* Method to access recorded error condition status.
86      */
87     bool has_failed() const { return failed; }
88
89     /* Method to reset recorded status, in preparation for
90      * parsing a new document.
91      */
92     void reset(){ new_document = true; failed = false; }
93
94     /* Method to handle error conditions, on behalf of our
95      * DOM parsers.
96      */
97     virtual bool handleError( const xercesc::DOMError& );
98
99   private:
100     /* The type of XML input being parsed, recorded when we
101      * consturct the error handler for binding to a particular
102      * DOM parser.
103      */
104     const char *source;
105
106     /* Recording for error condition status.
107      */
108     bool first_report, new_document, failed;
109 };
110
111 bool
112 error_handler::handleError( const xercesc::DOMError& condition )
113 {
114   /* Implementation of the error handler, which we will use to capture
115    * status, and report abnormal conditions detected by our DOM parsers.
116    */
117   bool warn = condition.getSeverity() == DOMError::DOM_SEVERITY_WARNING;
118
119   /* Record detection of any condition which is more severe than
120    * a simple warning.
121    */
122   if( ! warn ) failed = true;
123
124   /* Identify the location, within the current XML schema or document
125    * file, where the abnormality has been detected.
126    */
127   DOMLocator* loc( condition.getLocation() );
128
129   /* When this is the first abnormality detected within the current
130    * XML schema or document file...
131    */
132   if( new_document )
133   {
134     /* ...but we've previously reported abnormalities within another
135      * input file, then separate the current report from diagnostics
136      * relating to that other file...
137      */
138     if( ! first_report ) fputc( '\n', stderr );
139
140     /* ...then, regardless of whatever may have gone before, format
141      * and emit a report header to identify the current file.
142      */
143     char *uri = XMLString::transcode( loc->getURI() );
144     fprintf( stderr, "Problem Report:\n%s: %s\n", source, uri );
145     XMLString::release( &uri );
146
147     /* Record that we've now emitted a report header and diagnostic
148      * for the current XML input file.
149      */
150     first_report = new_document = false;
151   }
152
153   /* Whether we added a new report header, or not, we still have a
154    * diagnostic message to emit.
155    */
156   char* msg = XMLString::transcode( condition.getMessage() );
157   fprintf( stderr, "%" PRIu64 ":%" PRIu64 ": %s: %s\n", loc->getLineNumber(),
158       loc->getColumnNumber(), warn ? "WARNING" : "ERROR", msg
159     );
160   XMLString::release( &msg );
161
162   /* Finally, we return "true" to tell the DOM parser that we've
163    * handled the error, and that it should continue parsing.
164    */
165   return true;
166 }
167
168 static bool
169 insufficient_arguments( bool status, const char *program_pathname )
170 {
171   /* Diagnostic routine to report a lack of any command arguments
172    * to specify the XML documents which are to be validated.
173    */
174   if( status )
175   {
176     /* The "status" flag indicates an abnormal condition...
177      *
178      * We want to call "basename()" on the passed "program_pathname";
179      * while this is likely safe, it MAY try to modify the input string,
180      * so create a temporary working copy...
181      */
182     char progname[1 + strlen( program_pathname )];
183
184     /* ...then format and emit an appropriate diagnostic message.
185      */
186     strcpy( progname, program_pathname );
187     fprintf( stderr, "%s: no XML documents specified for validation\n"
188         "usage: %s [schema.xsd ...] document.xml ...\n", basename( progname ),
189         program_pathname
190       );
191   }
192   /* Irrespective of condition, we echo back the input state.
193    */
194   return status;
195 }
196
197 DOMLSParser*
198 create_parser( XMLGrammarPool* pool )
199 {
200   /* Helper function, to instantiate a DOM parser with "LS", (load and
201    * save), capability, (although we intend to use only "load").
202    */
203   const XMLCh ls_id[] = { chLatin_L, chLatin_S, chNull };
204
205   /* Locate a DOM implementation, providing the requisite "LS" feature.
206    */
207   DOMImplementation* impl(
208     DOMImplementationRegistry::getDOMImplementation( ls_id ) );
209
210   /* Instantiate a parser, based on this DOM implementation.
211    */
212   DOMLSParser* parser(
213     impl->createLSParser(
214       DOMImplementationLS::MODE_SYNCHRONOUS,
215       0,
216       XMLPlatformUtils::fgMemoryManager,
217       pool ) );
218
219   /* Retrieve a pointer to its configuration data...
220    */
221   DOMConfiguration* conf( parser->getDomConfig() );
222
223   /* ...so we may apply this commonly useful configuration.
224    */
225   conf->setParameter( XMLUni::fgDOMComments, false );
226   conf->setParameter( XMLUni::fgDOMDatatypeNormalization, true );
227   conf->setParameter( XMLUni::fgDOMElementContentWhitespace, false );
228   conf->setParameter( XMLUni::fgDOMNamespaces, true );
229   conf->setParameter( XMLUni::fgDOMEntities, false );
230
231   /* Enable validation.
232    */
233   conf->setParameter( XMLUni::fgDOMValidate, true );
234   conf->setParameter( XMLUni::fgXercesSchema, true );
235   conf->setParameter( XMLUni::fgXercesSchemaFullChecking, false );
236
237   /* Use the loaded grammar during parsing.
238    */
239   conf->setParameter( XMLUni::fgXercesUseCachedGrammarInParse, true );
240
241   /* Don't load schemas from any other source (e.g., from XML document's
242    * xsi:schemaLocation attributes).
243    */
244   conf->setParameter( XMLUni::fgXercesLoadSchema, false );
245
246   /* Xerces-C++ 3.1.0 is the first version with working support for
247    * multiple import.
248    */
249   IF_XERCES_30100_PLUS(
250       conf->setParameter( XMLUni::fgXercesHandleMultipleImports, true )
251     );
252
253   /* We will release the DOM document ourselves.
254    */
255   conf->setParameter( XMLUni::fgXercesUserAdoptsDOMDocument, true );
256
257   /* Return a pointer to the instantiated parser.
258    */
259   return parser;
260 }
261
262 static inline int
263 validation_status( int argc, char **argv )
264 {
265   int retcode = 0;
266
267   /* Initialize a grammer pool, for use by our parser instances.
268    */
269   MemoryManager* mm( XMLPlatformUtils::fgMemoryManager );
270   auto_ptr<XMLGrammarPool> gp( new XMLGrammarPoolImpl( mm ) );
271
272   /* Load the schema definitions into the grammar pool.
273    */
274   int argind = 1;
275   {
276     /* Instantiate a parser for the schema definition file(s).
277      */
278     DOMLSParser* parser( create_parser( gp.get() ) );
279
280     /* Initialize an error handler for the schema context,
281      * and bind it to the schema file parser.
282      */
283     error_handler eh( "XML Schema" );
284     parser->getDomConfig()->setParameter( XMLUni::fgDOMErrorHandler, &eh );
285
286     /* Scan command arguments, left to right, to identify any XML schema
287      * files which we are expected to interpret.
288      */
289     do { const char *source = argv[argind]; size_t extent = strlen( source );
290          if( (extent > 4) && (strcasecmp( source + extent - 4, ".xsd" ) == 0) )
291          {
292            /* We have a "*.xsd" file to parse; do so, loading the grammar...
293             */
294            if( !parser->loadGrammar( source, Grammar::SchemaGrammarType, true ) )
295            {
296              /* ...but complain, and bail out, if loading fails...
297               */
298              fprintf( stderr, "%s: error: unable to load\n", source );
299              retcode = 1;
300            }
301            if( eh.has_failed() )
302              /*
303               * ...or if any schema parsing error was encountered.
304               */
305              retcode = 1;
306          }
307          else
308            /* We've exhausted the "*.xsd" file references; break out of
309             * the scanning loop, without further ceremony.
310             */
311            break;
312
313          /* Continue for the next "*.xsd" file, if any, provided there
314           * have been no schema abormalities detected thus far.
315           */
316        } while( (retcode == 0) && (++argind < argc) );
317
318     /* We're finished with our schema parser; release its resource pool.
319      */
320     parser->release();
321   }
322
323   /* Before proceeding to parse any XML documents, check that any
324    * specified XML schemas have been loaded successfully.
325    */
326   if( retcode == 0 )
327   {
328     /* It's okay to proceed, but it would be pointless to do so...
329      */
330     if( insufficient_arguments( argind >= argc, *argv ) )
331       /*
332        * ...when there are no remaining arguments to specify any
333        * XML documents for checking; in this case, bail out.
334        */
335       return 1;
336
337     /* Lock the grammar pool. This is necessary if we plan to use the
338      * same grammar pool in multiple threads (this way we can reuse the
339      * same grammar in multiple parsers). Locking the pool disallows any
340      * modifications to the pool, such as an attempt by one of the threads
341      * to cache additional schemas.
342      */
343     gp->lockPool();
344
345     /* Instantiate a new parser, to process the XML documents.
346      */
347     DOMLSParser* parser( create_parser( gp.get() ) );
348
349     /* Initialize an error handler for the XML document context,
350      * and bind it to the new parser.
351      */
352     error_handler eh( "XML Document" );
353     parser->getDomConfig()->setParameter( XMLUni::fgDOMErrorHandler, &eh );
354
355     /* Process all remaining arguments, as references to XML documents.
356      */
357     while( argind < argc )
358     {
359       /* Reset the error handler state, prior to loading each document.
360        */
361       eh.reset();
362       DOMDocument* doc( parser->parseURI( argv[argind++] ) );
363
364       /* In this application, all we care about is that the document
365        * can be successfully read by our validating parser; if we did
366        * read it successfully, we have no further use for it, se we
367        * may simply set it aside.
368        */
369       if( doc ) doc->release();
370
371       /* If any error occurred, while parsing the current document,
372        * the error handler will have recorded it; we need to capture
373        * that state here, for our eventual return code.
374        */
375       if( eh.has_failed() ) retcode = 1;
376     }
377     /* When all specified documents have been validated, we are done
378      * with our parser, so we may release its resource pool.
379      */
380     parser->release();
381   }
382   /* Report back, with the cumulative status from XML document parsing.
383    */
384   return retcode;
385 }
386
387 int
388 main( int argc, char **argv )
389 {
390   /* Fewer than one argument, after the command verb itself,
391    * is not useful; complain, and bail out.
392    */
393   if( insufficient_arguments( argc < 2, *argv ) )
394     return 1;
395
396   /* We must initialize Xerces-C++, before we can use it.
397    */
398   XMLPlatformUtils::Initialize();
399
400   /* Determine the validation status for all specified XML documents,
401    * with respect to any specified XML schema definitions.
402    */
403   int retcode = validation_status( argc, argv );
404
405   /* Shut down the Xerces-C++ subsystem, before returning the resultant
406    * validation status code to the operating system.
407    */
408   XMLPlatformUtils::Terminate();
409   return retcode;
410 }
411
412 /* $RCSfile$: end of file */