OSDN Git Service

Implement an XML validation test suite.
authorKeith Marshall <keithmarshall@users.sourceforge.net>
Tue, 8 Oct 2013 12:42:49 +0000 (13:42 +0100)
committerKeith Marshall <keithmarshall@users.sourceforge.net>
Tue, 8 Oct 2013 12:42:49 +0000 (13:42 +0100)
ChangeLog
tests/Makefile.in [new file with mode: 0644]
tests/configure.ac [new file with mode: 0644]
tests/pkgspec.xsd [new file with mode: 0644]
tests/xmlcheck.cpp [new file with mode: 0644]

index 9617462..a5fe51c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
 2013-10-08  Keith Marshall  <keithmarshall@users.sourceforge.net>
 
+       Implement an XML validation test suite.
+
+       * tests: New directory.
+       * tests/configure.ac tests/Makefile.in: New files.
+       * tests/xmlcheck.cpp tests/pkgspec.xsd: Likewise.
+
+2013-10-08  Keith Marshall  <keithmarshall@users.sourceforge.net>
+
        Delegate version assignment to VERSION.m4 auxiliary file.
 
        * VERSION.m4: New file.
diff --git a/tests/Makefile.in b/tests/Makefile.in
new file mode 100644 (file)
index 0000000..2f16b38
--- /dev/null
@@ -0,0 +1,78 @@
+# @configure_input@
+#
+# $Id$
+#
+# Written by Keith Marshall <keithmarshall@users.sourceforge.net>
+# Copyright (C) 2013, MinGW.org Project
+#
+#
+# Makefile template for generating mingw-get distribution manifests.
+#
+#   Project: @PACKAGE_TARNAME@
+#   Version: @PACKAGE_VERSION@
+#
+#
+# This is free software.  Permission is granted to copy, modify and
+# redistribute this software, under the provisions of the GNU General
+# Public License, Version 3, (or, at your option, any later version),
+# as published by the Free Software Foundation; see the file COPYING
+# for licensing details.
+#
+# Note, in particular, that this software is provided "as is", in the
+# hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
+# even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
+# PARTICULAR PURPOSE.  Under no circumstances will the author, or the
+# MinGW Project, accept liability for any damages, however caused,
+# arising from the use of this software.
+#
+VPATH = @srcdir@
+vpath VERSION.m4 @srcdir@/..
+
+CXX = @CXX@
+CXXFLAGS = @CXXFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+
+EXEEXT = @EXEEXT@
+OBJEXT = @OBJEXT@
+
+srcdir = @srcdir@
+xml_datarootdir = ${srcdir}/..
+
+XMLCHECK =
+
+check: @XERCES_C_TOOLS@
+       ./xmlcheck$(EXEEXT) ${srcdir}/pkgspec.xsd ${xml_datarootdir}/*/*.xml \
+         2> xmlcheck.log && rm -f xmlcheck.log \
+            || { cat xmlcheck.log; exit 1; }
+
+no-xerces-c-tools:
+       @for msg in $(NO_XERCES_C_TOOLS_MSG); do echo $$msg; done; false
+
+NO_XERCES_C_TOOLS_MSG = "" \
+  "This test suite requires the Xerces-C++ SDK, but it appears that" \
+  "this has not been installed on your system." "" \
+  "Please install a copy of this SDK, which is compatible with your" \
+  "system C++ compiler, if you wish to run this test suite." ""
+
+xerces-c-tools: xmlcheck$(EXEEXT)
+xmlcheck$(EXEEXT): xmlcheck.$(OBJEXT)
+       $(CXX) $(CXXFLAGS) -o $@ $(LDFLAGS) $^ $(LIBS)
+
+sinclude *.d
+%.$(OBJEXT): %.cpp
+       $(CXX) -MMD -MP -c $(CPPFLAGS) $(CXXFLAGS) -o $@ $<
+
+configure: configure.ac VERSION.m4
+       cd ${srcdir}; autoconf
+
+config.status: configure
+       ./config.status --recheck
+
+Makefile: config.status Makefile.in
+       ./config.status
+
+clean:
+       rm -f *.$(OBJEXT) xmlcheck$(EXEEXT)
+
+# $RCSfile$: end of file
diff --git a/tests/configure.ac b/tests/configure.ac
new file mode 100644 (file)
index 0000000..ece2f94
--- /dev/null
@@ -0,0 +1,46 @@
+# configure.ac -*- autoconf -*- vim: filetype=config
+#
+# $Id$
+#
+# Written by Keith Marshall  <keithmarshall@users.sourceforge.net>
+# Copyright (C) 2013, MinGW.org Project
+#
+#
+# Configuration script for mingw-dist validation suite
+#
+#
+# This is free software.  Permission is granted to copy, modify and
+# redistribute this software, under the provisions of the GNU General
+# Public License, Version 3, (or, at your option, any later version),
+# as published by the Free Software Foundation; see the file COPYING
+# for licensing details.
+#
+# Note, in particular, that this software is provided "as is", in the
+# hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
+# even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
+# PARTICULAR PURPOSE.  Under no circumstances will the author, or the
+# MinGW Project, accept liability for any damages, however caused,
+# arising from the use of this software.
+#
+  m4_include([../VERSION.m4])
+  AC_INIT([mingw-dist-tests],__VERSION__,[http://mingw.org/reporting_bugs])
+#
+# Building the test suite driver program requires a C++ compiler,
+# (which must emit binary code to run on the build platform).
+#
+  AC_PROG_CXX
+#
+# The test suite is heavily dependent on the Xerces-C++ SDK; check, to
+# ensure that this is installed.
+#
+  AC_CHECK_LIB([xerces-c],[main])
+  AS_IF([test x"$ac_cv_lib_xerces_c_main" = xyes],dnl
+  [XERCES_C_TOOLS=xerces-c-tools],[XERCES_C_TOOLS=no-xerces-c-tools])dnl
+  AC_SUBST([XERCES_C_TOOLS])
+#
+# Configure output comprises a simple makefile.
+#
+  AC_CONFIG_FILES([Makefile])
+  AC_OUTPUT
+#
+# $RCSfile$: end of file
diff --git a/tests/pkgspec.xsd b/tests/pkgspec.xsd
new file mode 100644 (file)
index 0000000..747ab20
--- /dev/null
@@ -0,0 +1,413 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
+  <!--
+    $Id$
+
+    XSDL schema for validation of mingw-get package specifications.
+
+    Written by Keith Marshall  <keithmarshall@users.sourceforge.net>
+    Copyright (C) 2013, MinGW.org Project
+
+
+    This file is part of the mingw-dist catalogue validation suite.
+
+    This is free software.  Permission is granted to copy, modify and
+    redistribute this software, under the provisions of the GNU General
+    Public License, Version 3, (or, at your option, any later version),
+    as published by the Free Software Foundation; see the file COPYING
+    for licensing details.
+
+    Note, in particular, that this software is provided "as is", in the
+    hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
+    even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
+    PARTICULAR PURPOSE.  Under no circumstances will the author, or the
+    MinGW Project, accept liability for any damages, however caused,
+    arising from the use of this software.
+
+
+    Note to maintainers, (because this may seem counter intuitive, and
+    thus may be eminently forgettable): whilst the structure of any XML
+    document demands that attributes be specified in the opening tag of
+    their associated element, and thus they PRECEDE the element content,
+    XSDL requires that they be declared AFTER the corresponding content
+    model; please ensure that their declarations are correctly placed
+    within this XSDL schema.
+  -->
+
+  <xs:element name="software-distribution" type="specification-document" />
+  <!--
+    The package specification document root is always an XML element
+    named "software-distribution", which must conform to the following
+    type definition
+  -->
+  <xs:complexType name="specification-document">
+    <xs:sequence>
+      <!--
+       The entire specification comprises a sequence of at most three
+       optionally repeating section types; unless it is omitted...
+      -->
+      <xs:element name="package-group-hierarchy" minOccurs="0">
+       <!--
+         ...this must appear as the first section; only one instance
+         is permitted.
+       -->
+       <xs:complexType>
+         <xs:sequence maxOccurs="unbounded">
+            <!--
+              Within the package group hierarchy, at least one package
+              group must be specified.  Additional group specifications
+              may be added, without limit; all must conform to the type
+              definition, as specified below.
+            -->
+           <xs:element name="package-group" type="package-group-definition" />
+         </xs:sequence>
+       </xs:complexType>
+      </xs:element>
+      <!--
+        When present, a package group hierarchy specification is typically
+        followed by one or more instances of...
+      -->
+      <xs:element name="package-list" minOccurs="0" maxOccurs="unbounded">
+        <!--
+          ...this; it is optional, but when present, all instances must
+          follow the package group hierarchy specification, (if any), and
+          must precede any "package collection" specification.
+        -->
+       <xs:complexType>
+          <!--
+            This is an "attribute only" element.  The "catalogue" attribute
+            must be specified; it represents the name of a further XML file
+            to be included within the specification document, omitting both
+            any directory path, and the ".xml" file name suffix.
+          -->
+         <xs:attribute name="catalogue" type="xs:string" use="required" />
+          <!--
+            The specification author typically does NOT specify the "issue"
+            attribute; it is inserted automatically by the "mingw-dist" build
+            system, as an indicator to mingw-get when the catalogue named by
+            the "catalogue" attribute should be updated.
+          -->
+         <xs:attribute name="issue" type="serial-number" />
+       </xs:complexType>
+      </xs:element>
+      <!--
+        Typically specified in isolation, (i.e. entirely separated from any
+        package group hierarchy or package list specification), the final
+        section which may appear in the specification document is...
+      -->
+      <xs:element name="package-collection" minOccurs="0" maxOccurs="unbounded">
+        <!--
+          ...this.  Although any number of instances may appear in a single
+          specification document, most commonly, each document will specify
+          only one package collection, which itself comprises...
+        -->
+       <xs:complexType>
+          <!--
+            ...a group of child elements, as defined below, specifying the
+            set of packages which are included in this collection.
+          -->
+         <xs:group ref="package-collection-specification" />
+          <!--
+            All of the individual packages in any one collection MUST
+            be assigned to a common installation "subsystem", which is
+            identified by this required attribute.
+          -->
+         <xs:attribute name="subsystem" type="xs:string" use="required" />
+       </xs:complexType>
+      </xs:element>
+    </xs:sequence>
+    <!--
+      The specification document also supports three attributes: the "project"
+      and "host" attributes are optional; they are provided primarily as an aid
+      to documenting the origin of the specified package collection, but they
+      are not actually used by mingw-get.
+    -->
+    <xs:attribute name="project" type="xs:string" default="MinGW" />
+    <xs:attribute name="home" type="xs:anyURI" default="http://www.mingw.org" />
+    <!--
+      On the other hand, the "issue" attribute is required; it takes a string
+      value, conforming to the type definition below; in the source context of
+      a mingw-dist catalogue build, it should be specified using the template
+      form for the value; other contexts should use the YYYYMMDDNN form.
+    -->
+    <xs:attribute name="issue" type="serial-number" use="required" />
+  </xs:complexType>
+
+  <xs:simpleType name="serial-number">
+    <!--
+      This specialization of the XSDL string type is to be used to specify the
+      value for the "issue" attribute, which is required in every specification
+      document.  It may be either be the explicit "@YYYYMMDDNN@" substitution
+      template, as used in mingw-dist source documents, or any ten character
+      alpha-numeric pattern derived from this template.
+    -->
+    <xs:restriction base="xs:string">
+      <xs:pattern value="(@YYYYMMDDNN@)|([0-9A-Z]{10})" />
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:complexType name="package-group-definition">
+    <!--
+      Defines the structure of the elements used to specify package groups,
+      within the package group hierarchy section of the specification document.
+      Elements of this type may repeat any number of times, both within this
+      context, and recursively nested within other package groups.
+    -->
+    <xs:sequence minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="package-group" type="package-group-definition" />
+    </xs:sequence>
+    <!--
+      Every package group must be named; mingw-get uses the value of the "name"
+      attribute as a label, to create a tree-view display representation of the
+      package group hierarchy, in the left hand pane of the GUI client window.
+    -->
+    <xs:attribute name="name" type="xs:string" use="required" />
+    <!--
+      The optional "expand" attribute provides a mechanism for specifying those
+      levels of a nested package group hierarchy which are to be shown expanded,
+      in the initial rendition of the tree-view display.
+    -->
+    <xs:attribute name="expand" type="xs:boolean" />
+  </xs:complexType>
+
+  <xs:group name="package-group-association-and-description">
+    <!--
+      Provides the mechanism for defining package to package group associations,
+      allowing multiple associations to be specified, and intermingled with any
+      number of package description elements, at the beginning of any individual
+      package specification.
+    -->
+    <xs:sequence>
+      <xs:choice minOccurs="0" maxOccurs="unbounded">
+        <xs:element name="affiliate">
+          <!--
+            Defines a single package to package group association, in terms of
+            a single attribute only specification element...
+          -->
+         <xs:complexType>
+           <xs:attribute name="group" type="xs:string" />
+         </xs:complexType>
+        </xs:element>
+        <!--
+          ...whilst this allows "description" blocks to be interspersed among
+          repeating "affiliate" specifications.
+        -->
+       <xs:element name="description" type="package-description" />
+      </xs:choice>
+    </xs:sequence>
+  </xs:group>
+
+  <xs:complexType name="package-description">
+    <!--
+      Defines the structure of a "description" block as a container for a
+      sequence of one or more "paragraph" blocks...
+    -->
+    <xs:sequence>
+      <xs:element name="paragraph" maxOccurs="unbounded" />
+    </xs:sequence>
+    <!--
+      ...optionally qualified by "lang" and/or "title" attributes.
+    -->
+    <xs:attribute name="lang" type="xs:string" default="en" />
+    <xs:attribute name="title" type="xs:string" />
+  </xs:complexType>
+
+  <xs:group name="package-collection-specification">
+    <xs:sequence>
+      <!--
+        Defines the sequence of XML elements which are required to properly
+        specify any package group collection.
+      -->
+      <xs:element name="download-host">
+        <xs:complexType>
+          <!--
+            The first is always a specification for the URL whence all packages
+            in the collection may be downloaded; it is declared as a string type
+            because it is specified in a template format, which may violate the
+            constraints of the XSDL anyURI type, e.g.:
+
+              <download-host uri="http://host.domain.com/packages/%F" />
+
+            where "%F" is substituted, at download time, by the actual name of
+            the archive file wich is to be downloaded.
+            
+            Note: only one template may be specified for any package collection;
+            thus all packages in any collection MUST be served from one common
+            hosting domain.
+          -->
+          <xs:attribute name="uri" type="xs:string" use="required" />
+        </xs:complexType>
+      </xs:element>
+      <!--
+        The download host specification may be followed by an optional sequence
+        of package group association specifications.  These must conform to the
+        format defined above, where it may be seen that they comprise any number
+        of "affiliate" and "description" elements, in arbitrary order.  If any
+        of these are specified here, they apply to every package subsequently
+        included within the collection.
+      -->
+      <xs:group ref="package-group-association-and-description" />
+      <xs:choice maxOccurs="unbounded">
+        <!--
+          The principal content of any package collection comprises an arbitrary
+          number of "package" specification elements.
+        -->
+       <xs:element name="package">
+         <xs:complexType>
+           <xs:sequence>
+              <!--
+                Each package specification begins with an optional description,
+                and an arbitrary set of package group association declarations.
+              -->
+             <xs:group ref="package-group-association-and-description" />
+              <!--
+                This must be followed by an arbitray sequence of one or more
+                "source", "licence", "component", or "action" specifications.
+              -->
+             <xs:choice maxOccurs="unbounded">
+               <xs:group ref="source-or-licence-archive-reference" />
+               <xs:element name="component" type="component-specification" />
+               <xs:element name="action" type="action-script" />
+             </xs:choice>
+           </xs:sequence>
+            <!--
+              Each package MUST be named; it may also be assigned to the class
+              of "virtual" packages, so identifying it as a meta-package, and it
+              may also be identified by a list of aliases.
+            -->
+           <xs:attribute name="name" type="xs:string" use="required" />
+           <xs:attribute name="class" type="package-class" />
+           <xs:attribute name="alias" type="xs:string" />
+         </xs:complexType>
+       </xs:element>
+        <!--
+          Actions may also be specified within the package-collection scope, in
+          which case they apply to all packages, and component packages, which
+          are declared within the collection.
+        -->
+       <xs:element name="action" type="action-script" />
+      </xs:choice>
+    </xs:sequence>
+  </xs:group>
+
+  <xs:simpleType name="package-class">
+    <!--
+      Any package may be specified as a meta-package, by assigning it a "class"
+      attribute value of "virtual"; specification of this attribute is optional,
+      but no other value is permitted.
+    -->
+    <xs:restriction base="xs:string">
+      <xs:pattern value="virtual" />
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:complexType name="component-specification">
+    <!--
+      Each "package" should be subdivided into one or more "component" packages,
+      each of which may bear its own individual description and/or package group
+      associations.  Each "component" package should also define a sequence of
+      "release" specifications, optionally qualified by specifications of any
+      dependencies which are common to all releases of the "component" package,
+      and any actions which should be performed when installing or removing any
+      release of the "component" package.
+    -->
+    <xs:choice maxOccurs="unbounded">
+      <xs:group ref="package-group-association-and-description" />
+      <xs:element name="release" type="release-specification" />
+      <xs:element name="requires" type="dependency-specification" />
+      <xs:element name="action" type="action-script" />
+    </xs:choice>
+    <!--
+      Every "component" package MUST be classified; however, the choice of
+      "class" name is unrestricted.
+    -->
+    <xs:attribute name="class" type="xs:string" use="required" />
+  </xs:complexType>
+
+  <xs:complexType name="release-specification">
+    <!--
+      Each individual release of any component package must be specified by its
+      own "release" specification element; this may be qualified by a "download"
+      specification, (for cases where the actual archive file name differs from
+      the canonical "tarname", or is "none" in the case of a meta-release, for
+      which the container "package" element cannot be declared as "virtual");
+      each "release" may also specify its own set of dependencies, and/or its
+      own specific "source" and/or "licence" archive associations.
+    -->
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="download" type="tarname-reference" />
+      <xs:element name="requires" type="dependency-specification" />
+      <xs:group ref="source-or-licence-archive-reference" />
+    </xs:choice>
+    <!--
+      Every "release" MUST be identified by a canonical "tarname" attribute.
+    -->
+    <xs:attribute ref="tarname" use="required" />
+  </xs:complexType>
+
+  <xs:complexType name="action-script" mixed="true">
+    <!--
+      Any action is specified as Lua script, appearing as textual content within
+      the body of the "action" element; it also requires a specification for its
+      "class" attribute, to identify the context in which it is to be executed.
+    -->
+    <xs:attribute name="class" type="action-class" use="required" />
+  </xs:complexType>
+
+  <xs:simpleType name="action-class">
+    <!--
+      The context in which any specified action is to be executed must be one of
+      "pre-install",  "post-install", "pre-remove", or "post-remove".
+    -->
+    <xs:restriction base="xs:string">
+      <xs:pattern value="(pre|post)-(install|remove)" />
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:group name="source-or-licence-archive-reference">
+    <!--
+      "source" and "licence" elements are simple "attribute only" elements, each
+      of which requires only a "tarname" attribute; note that the spelling which
+      is required for the "licence" element uses the noun form, as prescribed by
+      the Oxford Dictionary of World English; use of the US English form, where
+      noun and verb are both spelled as "license" is NOT supported.
+    -->
+    <xs:choice>
+      <xs:element name="source" type="tarname-reference" />
+      <xs:element name="licence" type="tarname-reference" />
+    </xs:choice>
+  </xs:group>
+
+  <xs:complexType name="tarname-reference">
+    <!--
+      When any element accepts a "tarname" attribute, then specification of that
+      attribute is mandatory.
+    -->
+    <xs:attribute ref="tarname" use="required" />
+  </xs:complexType>
+  <!--
+    The "tarname" attribute is a simple string type; it should match a specific
+    pattern, but for the time being, we do not attempt to verify it.
+  -->
+  <xs:attribute name="tarname" type="xs:string" />
+
+  <xs:complexType name="dependency-specification">
+    <!--
+      Any "requires" specification MUST include one or more of the following
+      attributes, to declare an equality or a range dependency.  It may be noted
+      that, while a range specification requires two such attributes, there will
+      be some combinations which will be mutually inconsistent; unfortunately,
+      XSDL provides no mechanism to filter out such combinations.
+    -->
+    <xs:attribute name="lt" type="xs:string" />
+    <xs:attribute name="le" type="xs:string" />
+    <xs:attribute name="eq" type="xs:string" />
+    <xs:attribute name="ge" type="xs:string" />
+    <xs:attribute name="gt" type="xs:string" />
+  </xs:complexType>
+
+</xs:schema>
+
+<!-- vim: set nocompatible fileformat=unix: -->
+<!-- vim: set smartindent shiftwidth=2 expandtab textwidth=80 wrap: -->
+<!-- $RCSfile$: end of file -->
diff --git a/tests/xmlcheck.cpp b/tests/xmlcheck.cpp
new file mode 100644 (file)
index 0000000..01f1928
--- /dev/null
@@ -0,0 +1,409 @@
+/*
+ * xmlcheck.cpp
+ *
+ * $Id$
+ *
+ * Adapted from load-grammar-dom.cxx
+ * Written by Boris Kolpackov <boris@codesynthesis.com>
+ * Assigned, by the author, to the public domain
+ *
+ * This program uses Xerces-C++ DOM parser to load a set of schema files
+ * and then to validate a set of XML documents against these schemas. To
+ * build this program you will need Xerces-C++ 3.0.0 or later. For more
+ * information, see:
+ *
+ * http: *www.codesynthesis.com/~boris/blog/2010/03/15/validating-external-schemas-xerces-cxx/
+ *
+ *
+ * Adaptation by Keith Marshall <keithmarshall@users.sourceforge.net>
+ * Copyright (C) 2013, MinGW.org Project
+ *
+ * This is free software.  Permission is granted to copy, modify and
+ * redistribute this software, under the provisions of the GNU General
+ * Public License, Version 3, (or, at your option, any later version),
+ * as published by the Free Software Foundation; see the file COPYING
+ * for licensing details.
+ *
+ * Note, in particular, that this software is provided "as is", in the
+ * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
+ * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
+ * PARTICULAR PURPOSE.  Under no circumstances will the author, or the
+ * MinGW Project, accept liability for any damages, however caused,
+ * arising from the use of this software.
+ *
+ */
+#include <cstdio>
+#include <string>
+#include <memory>       /* for std::auto_ptr */
+#include <cstddef>      /* for std::size_t   */
+
+#include <libgen.h>     /* for basename()    */
+
+#include <xercesc/util/XMLUni.hpp>
+#include <xercesc/util/XMLString.hpp>
+#include <xercesc/util/PlatformUtils.hpp>
+
+#include <xercesc/dom/DOM.hpp>
+
+#include <xercesc/validators/common/Grammar.hpp>
+#include <xercesc/framework/XMLGrammarPoolImpl.hpp>
+
+using namespace std;
+using namespace xercesc;
+
+#if _XERCES_VERSION < 30000
+/* We need at least Xerces-C++ version 3.0.0
+ */
+# error Xerces-C++ version >= 3.0.0 is required!
+
+#elif _XERCES_VERSION >= 30100
+/* We may wish to exploit some features which were not introduced
+ * until Xerces-C++ version 3.1.0
+ */
+# define IF_XERCES_30100_PLUS( STATEMENT )  STATEMENT
+
+#else
+/* We cannot use Xerces-C++ version 3.1.0 features; make them no-op.
+ */
+# define IF_XERCES_30100_PLUS( STATEMENT )
+#endif
+
+class error_handler: public DOMErrorHandler
+{
+  /* A locally defined class for capture of fault conditions, as
+   * reported by our DOM parsers.
+   */
+  public:
+    /* Constructor.
+     */
+    error_handler( const char *rel): source(rel),
+      first_report(true), new_document(true), failed(false){}
+
+    /* Method to access recorded error condition status.
+     */
+    bool has_failed() const { return failed; }
+
+    /* Method to reset recorded status, in preparation for
+     * parsing a new document.
+     */
+    void reset(){ new_document = true; failed = false; }
+
+    /* Method to handle error conditions, on behalf of our
+     * DOM parsers.
+     */
+    virtual bool handleError( const xercesc::DOMError& );
+
+  private:
+    /* The type of XML input being parsed, recorded when we
+     * consturct the error handler for binding to a particular
+     * DOM parser.
+     */
+    const char *source;
+
+    /* Recording for error condition status.
+     */
+    bool first_report, new_document, failed;
+};
+
+bool
+error_handler::handleError( const xercesc::DOMError& condition )
+{
+  /* Implementation of the error handler, which we will use to capture
+   * status, and report abnormal conditions detected by our DOM parsers.
+   */
+  bool warn = condition.getSeverity() == DOMError::DOM_SEVERITY_WARNING;
+
+  /* Record detection of any condition which is more severe than
+   * a simple warning.
+   */
+  if( ! warn ) failed = true;
+
+  /* Identify the location, within the current XML schema or document
+   * file, where the abnormality has been detected.
+   */
+  DOMLocator* loc( condition.getLocation() );
+
+  /* When this is the first abnormality detected within the current
+   * XML schema or document file...
+   */
+  if( new_document )
+  {
+    /* ...but we've previously reported abnormalities within another
+     * input file, then separate the current report from diagnostics
+     * relating to that other file...
+     */
+    if( ! first_report ) fputc( '\n', stderr );
+
+    /* ...then, regardless of whatever may have gone before, format
+     * and emit a report header to identify the current file.
+     */
+    char *uri = XMLString::transcode( loc->getURI() );
+    fprintf( stderr, "Problem Report:\n%s: %s\n", source, uri );
+    XMLString::release( &uri );
+
+    /* Record that we've now emitted a report header and diagnostic
+     * for the current XML input file.
+     */
+    first_report = new_document = false;
+  }
+
+  /* Whether we added a new report header, or not, we still have a
+   * diagnostic message to emit.
+   */
+  char* msg = XMLString::transcode( condition.getMessage() );
+  fprintf( stderr, "%d:%d: %s: %s\n", loc->getLineNumber(),
+      loc->getColumnNumber(), warn ? "WARNING" : "ERROR", msg
+    );
+  XMLString::release( &msg );
+
+  /* Finally, we return "true" to tell the DOM parser that we've
+   * handled the error, and that it should continue parsing.
+   */
+  return true;
+}
+
+static bool
+insufficient_arguments( bool status, const char *program_pathname )
+{
+  /* Diagnostic routine to report a lack of any command arguments
+   * to specify the XML documents which are to be validated.
+   */
+  if( status )
+  {
+    /* The "status" flag indicates an abnormal condition...
+     *
+     * We want to call "basename()" on the passed "program_pathname";
+     * while this is likely safe, it MAY try to modify the input string,
+     * so create a temporary working copy...
+     */
+    char progname[1 + strlen( program_pathname )];
+
+    /* ...then format and emit an appropriate diagnostic message.
+     */
+    strcpy( progname, program_pathname );
+    fprintf( stderr, "%s: no XML documents specified for validation\n"
+       "usage: %s [schema.xsd ...] document.xml ...\n", basename( progname ),
+       program_pathname
+      );
+  }
+  /* Irrespective of condition, we echo back the input state.
+   */
+  return status;
+}
+
+DOMLSParser*
+create_parser( XMLGrammarPool* pool )
+{
+  /* Helper function, to instantiate a DOM parser with "LS", (load and
+   * save), capability, (although we intend to use only "load").
+   */
+  const XMLCh ls_id[] = { chLatin_L, chLatin_S, chNull };
+
+  /* Locate a DOM implementation, providing the requisite "LS" feature.
+   */
+  DOMImplementation* impl(
+    DOMImplementationRegistry::getDOMImplementation( ls_id ) );
+
+  /* Instantiate a parser, based on this DOM implementation.
+   */
+  DOMLSParser* parser(
+    impl->createLSParser(
+      DOMImplementationLS::MODE_SYNCHRONOUS,
+      0,
+      XMLPlatformUtils::fgMemoryManager,
+      pool ) );
+
+  /* Retrieve a pointer to its configuration data...
+   */
+  DOMConfiguration* conf( parser->getDomConfig() );
+
+  /* ...so we may apply this commonly useful configuration.
+   */
+  conf->setParameter( XMLUni::fgDOMComments, false );
+  conf->setParameter( XMLUni::fgDOMDatatypeNormalization, true );
+  conf->setParameter( XMLUni::fgDOMElementContentWhitespace, false );
+  conf->setParameter( XMLUni::fgDOMNamespaces, true );
+  conf->setParameter( XMLUni::fgDOMEntities, false );
+
+  /* Enable validation.
+   */
+  conf->setParameter( XMLUni::fgDOMValidate, true );
+  conf->setParameter( XMLUni::fgXercesSchema, true );
+  conf->setParameter( XMLUni::fgXercesSchemaFullChecking, false );
+
+  /* Use the loaded grammar during parsing.
+   */
+  conf->setParameter( XMLUni::fgXercesUseCachedGrammarInParse, true );
+
+  /* Don't load schemas from any other source (e.g., from XML document's
+   * xsi:schemaLocation attributes).
+   */
+  conf->setParameter( XMLUni::fgXercesLoadSchema, false );
+
+  /* Xerces-C++ 3.1.0 is the first version with working support for
+   * multiple import.
+   */
+  IF_XERCES_30100_PLUS(
+      conf->setParameter( XMLUni::fgXercesHandleMultipleImports, true )
+    );
+
+  /* We will release the DOM document ourselves.
+   */
+  conf->setParameter( XMLUni::fgXercesUserAdoptsDOMDocument, true );
+
+  /* Return a pointer to the instantiated parser.
+   */
+  return parser;
+}
+
+static inline int
+validation_status( int argc, char **argv )
+{
+  int retcode = 0;
+
+  /* Initialize a grammer pool, for use by our parser instances.
+   */
+  MemoryManager* mm( XMLPlatformUtils::fgMemoryManager );
+  auto_ptr<XMLGrammarPool> gp( new XMLGrammarPoolImpl( mm ) );
+
+  /* Load the schema definitions into the grammar pool.
+   */
+  int argind = 1;
+  {
+    /* Instantiate a parser for the schema definition file(s).
+     */
+    DOMLSParser* parser( create_parser( gp.get() ) );
+
+    /* Initialize an error handler for the schema context,
+     * and bind it to the schema file parser.
+     */
+    error_handler eh( "XML Schema" );
+    parser->getDomConfig()->setParameter( XMLUni::fgDOMErrorHandler, &eh );
+
+    /* Scan command arguments, left to right, to identify any XML schema
+     * files which we are expected to interpret.
+     */
+    do { const char *source = argv[argind]; size_t extent = strlen( source );
+        if( (extent > 4) && (strcasecmp( source + extent - 4, ".xsd" ) == 0) )
+        {
+          /* We have a "*.xsd" file to parse; do so, loading the grammar...
+           */
+          if( !parser->loadGrammar( source, Grammar::SchemaGrammarType, true ) )
+          {
+            /* ...but complain, and bail out, if loading fails...
+             */
+            fprintf( stderr, "%s: error: unable to load\n", source );
+            retcode = 1;
+          }
+          if( eh.has_failed() )
+            /*
+             * ...or if any schema parsing error was encountered.
+             */
+            retcode = 1;
+        }
+        else
+          /* We've exhausted the "*.xsd" file references; break out of
+           * the scanning loop, without further ceremony.
+           */
+          break;
+
+        /* Continue for the next "*.xsd" file, if any, provided there
+         * have been no schema abormalities detected thus far.
+         */
+       } while( (retcode == 0) && (++argind < argc) );
+
+    /* We're finished with our schema parser; release its resource pool.
+     */
+    parser->release();
+  }
+
+  /* Before proceeding to parse any XML documents, check that any
+   * specified XML schemas have been loaded successfully.
+   */
+  if( retcode == 0 )
+  {
+    /* It's okay to proceed, but it would be pointless to do so...
+     */
+    if( insufficient_arguments( argind >= argc, *argv ) )
+      /*
+       * ...when there are no remaining arguments to specify any
+       * XML documents for checking; in this case, bail out.
+       */
+      return 1;
+
+    /* Lock the grammar pool. This is necessary if we plan to use the
+     * same grammar pool in multiple threads (this way we can reuse the
+     * same grammar in multiple parsers). Locking the pool disallows any
+     * modifications to the pool, such as an attempt by one of the threads
+     * to cache additional schemas.
+     */
+    gp->lockPool();
+
+    /* Instantiate a new parser, to process the XML documents.
+     */
+    DOMLSParser* parser( create_parser( gp.get() ) );
+
+    /* Initialize an error handler for the XML document context,
+     * and bind it to the new parser.
+     */
+    error_handler eh( "XML Document" );
+    parser->getDomConfig()->setParameter( XMLUni::fgDOMErrorHandler, &eh );
+
+    /* Process all remaining arguments, as references to XML documents.
+     */
+    while( argind < argc )
+    {
+      /* Reset the error handler state, prior to loading each document.
+       */
+      eh.reset();
+      DOMDocument* doc( parser->parseURI( argv[argind++] ) );
+
+      /* In this application, all we care about is that the document
+       * can be successfully read by our validating parser; if we did
+       * read it successfully, we have no further use for it, se we
+       * may simply set it aside.
+       */
+      if( doc ) doc->release();
+
+      /* If any error occurred, while parsing the current document,
+       * the error handler will have recorded it; we need to capture
+       * that state here, for our eventual return code.
+       */
+      if( eh.has_failed() ) retcode = 1;
+    }
+    /* When all specified documents have been validated, we are done
+     * with our parser, so we may release its resource pool.
+     */
+    parser->release();
+  }
+  /* Report back, with the cumulative status from XML document parsing.
+   */
+  return retcode;
+}
+
+int
+main( int argc, char **argv )
+{
+  /* Fewer than one argument, after the command verb itself,
+   * is not useful; complain, and bail out.
+   */
+  if( insufficient_arguments( argc < 2, *argv ) )
+    return 1;
+
+  /* We must initialize Xerces-C++, before we can use it.
+   */
+  XMLPlatformUtils::Initialize();
+
+  /* Determine the validation status for all specified XML documents,
+   * with respect to any specified XML schema definitions.
+   */
+  int retcode = validation_status( argc, argv );
+
+  /* Shut down the Xerces-C++ subsystem, before returning the resultant
+   * validation status code to the operating system.
+   */
+  XMLPlatformUtils::Terminate();
+  return retcode;
+}
+
+/* $RCSfile$: end of file */