[libxml++] Validators: Improve the error handling.



commit 09d2a9048191455dbd1849359949f212b906f0a3
Author: Kjell Ahlstedt <kjell ahlstedt bredband net>
Date:   Thu Aug 9 10:41:12 2012 +0200

    Validators: Improve the error handling.
    
    * libxml++/validators/validator.[h|cc]:
    * libxml++/validators/dtdvalidator.[h|cc]:
    * libxml++/validators/schemavalidator.[h|cc]: Check more return codes from
    libxml2 functions. Improve the description of member functions in the
    reference documentation. Bug #635846.

 ChangeLog                              |   10 ++++
 libxml++/validators/dtdvalidator.cc    |   44 +++++++++--------
 libxml++/validators/dtdvalidator.h     |   68 ++++++++++++++++++++++----
 libxml++/validators/schemavalidator.cc |   34 ++++++++++---
 libxml++/validators/schemavalidator.h  |   83 +++++++++++++++++++++++++++++---
 libxml++/validators/validator.cc       |   40 ++++++++++-----
 libxml++/validators/validator.h        |    3 +-
 7 files changed, 222 insertions(+), 60 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 6d1c1a5..ed58e7c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2012-08-09  Kjell Ahlstedt  <kjell ahlstedt bredband net>
+
+	Validators: Improve the error handling.
+
+	* libxml++/validators/validator.[h|cc]:
+	* libxml++/validators/dtdvalidator.[h|cc]:
+	* libxml++/validators/schemavalidator.[h|cc]: Check more return codes from
+	libxml2 functions. Improve the description of member functions in the
+	reference documentation. Bug #635846.
+
 2012-08-07  Kjell Ahlstedt  <kjell ahlstedt bredband net>
 
 	Add incremental parsing to the SaxParser example program.
diff --git a/libxml++/validators/dtdvalidator.cc b/libxml++/validators/dtdvalidator.cc
index f000214..133c75e 100644
--- a/libxml++/validators/dtdvalidator.cc
+++ b/libxml++/validators/dtdvalidator.cc
@@ -7,15 +7,13 @@
 
 #include "libxml++/validators/dtdvalidator.h"
 #include "libxml++/dtd.h"
-#include "libxml++/nodes/element.h"
-#include "libxml++/nodes/textnode.h"
-#include "libxml++/nodes/commentnode.h"
-#include "libxml++/keepblanks.h"
+#include "libxml++/nodes/node.h"
 #include "libxml++/exceptions/internal_error.h"
+#include "libxml++/exceptions/validity_error.h"
 #include "libxml++/io/istreamparserinputbuffer.h"
 #include "libxml++/document.h"
 
-#include <libxml/parserInternals.h>//For xmlCreateFileParserCtxt().
+#include <libxml/parser.h>
 
 #include <sstream>
 #include <iostream>
@@ -54,6 +52,7 @@ void DtdValidator::parse_file(const Glib::ustring& filename)
 void DtdValidator::parse_subset(const Glib::ustring& external,const Glib::ustring& system)
 {
   release_underlying(); // Free any existing dtd.
+  xmlResetLastError();
 
   xmlDtd* dtd = xmlParseDTD(
     external.empty() ? 0 : (const xmlChar *)external.c_str(),
@@ -61,7 +60,7 @@ void DtdValidator::parse_subset(const Glib::ustring& external,const Glib::ustrin
 
   if (!dtd)
   {
-    throw parse_error("Dtd could not be parsed");
+    throw parse_error("Dtd could not be parsed.\n" + format_xml_error());
   }
 
   Node::create_wrapper(reinterpret_cast<xmlNode*>(dtd));
@@ -78,7 +77,8 @@ void DtdValidator::parse_memory(const Glib::ustring& contents)
 
 void DtdValidator::parse_stream(std::istream& in)
 {
-  release_underlying(); //Free any existing document.
+  release_underlying(); // Free any existing dtd.
+  xmlResetLastError();
 
   IStreamParserInputBuffer ibuff( in );
 
@@ -86,7 +86,7 @@ void DtdValidator::parse_stream(std::istream& in)
 
   if (!dtd)
   {
-    throw parse_error("Dtd could not be parsed");
+    throw parse_error("Dtd could not be parsed.\n" + format_xml_error());
   }
 
   Node::create_wrapper(reinterpret_cast<xmlNode*>(dtd));
@@ -97,9 +97,9 @@ void DtdValidator::release_underlying()
 {
   if(dtd_)
   {
-    //Make a local copy as the wrapper is destroyed first
+    //Make a local pointer to the underlying xmlDtd object as the wrapper is destroyed first.
     //After free_wrappers is called dtd_ will be invalid (e.g. delete dtd_)
-    xmlDtd* dtd=dtd_->cobj();
+    xmlDtd* dtd = dtd_->cobj();
     Node::free_wrappers(reinterpret_cast<xmlNode*>(dtd));
     xmlFreeDtd(dtd);
     dtd_ = 0;
@@ -123,33 +123,37 @@ const Dtd* DtdValidator::get_dtd() const
 
 bool DtdValidator::validate(const Document* doc)
 {
+  if (!doc)
+  {
+    throw internal_error("Document pointer cannot be 0.");
+  }
+
+  if (!dtd_)
+  {
+    throw internal_error("No DTD to use for validation.");
+  }
+
   // A context is required at this stage only
   if (!valid_)
     valid_ = xmlNewValidCtxt();
 
   if(!valid_)
   {
-    throw internal_error("Couldn't create parsing context");
-  }
-
-  if (!doc)
-  {
-    throw internal_error("Document pointer cannot be 0");
+    throw internal_error("Couldn't create validation context");
   }
 
+  xmlResetLastError();
   initialize_valid();
 
   const bool res = (bool)xmlValidateDtd( valid_, (xmlDoc*)doc->cobj(), dtd_->cobj() );
 
-  if(res == 0)
+  if (!res)
   {
     check_for_exception();
-
-    throw validity_error("Document failed Dtd validation");
+    throw validity_error("Document failed DTD validation\n" + format_xml_error());
   }
 
   return res;
 }
 
 } // namespace xmlpp
-
diff --git a/libxml++/validators/dtdvalidator.h b/libxml++/validators/dtdvalidator.h
index 674e7af..863faa0 100644
--- a/libxml++/validators/dtdvalidator.h
+++ b/libxml++/validators/dtdvalidator.h
@@ -5,8 +5,8 @@
  * included with libxml++ as the file COPYING.
  */
 
-#ifndef __LIBXMLPP_PARSERS_DTDVALIDATOR_H
-#define __LIBXMLPP_PARSERS_DTDVALIDATOR_H
+#ifndef __LIBXMLPP_VALIDATORS_DTDVALIDATOR_H
+#define __LIBXMLPP_VALIDATORS_DTDVALIDATOR_H
 
 #include <libxml++/validators/validator.h>
 #include <libxml++/dtd.h>
@@ -14,28 +14,79 @@
 
 namespace xmlpp {
 
-/** XML DOM parser.
- *
+/** XML DTD validator.
  */
 class DtdValidator : public Validator
 {
 public:
   DtdValidator();
+
+  /** Create a validator and parse an external subset (DTD file) immediately.
+   * @param file The URL of the DTD.
+   * @throws xmlpp::parse_error
+   */
   explicit DtdValidator(const Glib::ustring& file);
+
+  /** Create a validator and parse an external subset (DTD file) immediately.
+   * @param external The external ID of the DTD.
+   * @param system The URL of the DTD.
+   * @throws xmlpp::parse_error
+   */
   explicit DtdValidator(const Glib::ustring& external,const Glib::ustring& system);
+
   virtual ~DtdValidator();
 
+  /** Parse an external subset (DTD file).
+   * If the validator already contains a DTD, that DTD is deleted.
+   * @param external The external ID of the DTD.
+   * @param system The URL of the DTD.
+   * @throws xmlpp::parse_error
+   */
   virtual void parse_subset(const Glib::ustring& external,const Glib::ustring& system);
+
+  /** Parse an external subset (DTD file).
+   * If the validator already contains a DTD, that DTD is deleted.
+   * @param filename The URL of the DTD.
+   * @throws xmlpp::parse_error
+   */
   virtual void parse_file(const Glib::ustring& filename);
+
+  /** Parse a DTD from a string.
+   * If the validator already contains a DTD, that DTD is deleted.
+   * @param contents The DTD as a string.
+   * @throws xmlpp::parse_error
+   */
   virtual void parse_memory(const Glib::ustring& contents);
+
+  /** Parse a DTD from a stream.
+   * If the validator already contains a DTD, that DTD is deleted.
+   * @param in The stream.
+   * @throws xmlpp::parse_error
+   */
   virtual void parse_stream(std::istream& in);
 
-  /** Test whether a document has been parsed.
-  */
+  /** Test whether a DTD has been parsed.
+   */
   operator bool() const;
+
+  /** Get the parsed DTD.
+   * @returns A pointer to the parsed DTD, or <tt>0</tt>.
+   */
   Dtd* get_dtd();
+
+  /** Get the parsed DTD.
+   * @returns A pointer to the parsed DTD, or <tt>0</tt>.
+   */
   const Dtd* get_dtd() const;
 
+  /** Validate a document, using a previously parsed DTD.
+   * The internal subset (if present) is de-coupled (i.e. not used),
+   * which could give problems if ID or IDREF is present.
+   * @param doc Pointer to the document.
+   * @returns Whether the document is valid.
+   * @throws xmlpp::internal_error
+   * @throws xmlpp::validity_error
+   */
   bool validate(const Document* doc);
 
 protected:
@@ -44,10 +95,7 @@ protected:
   Dtd* dtd_;
 };
 
-
-
-
 } // namespace xmlpp
 
-#endif //__LIBXMLPP_PARSERS_DTDVALIDATOR_H
+#endif //__LIBXMLPP_VALIDATORS_DTDVALIDATOR_H
 
diff --git a/libxml++/validators/schemavalidator.cc b/libxml++/validators/schemavalidator.cc
index 2a73640..c51b62c 100644
--- a/libxml++/validators/schemavalidator.cc
+++ b/libxml++/validators/schemavalidator.cc
@@ -71,11 +71,14 @@ SchemaValidator::~SchemaValidator()
 
 void SchemaValidator::parse_context(_xmlSchemaParserCtxt* context)
 {
-  release_underlying(); // Free any existing dtd.
+  if (!context)
+    throw parse_error("Could not create schema parser context\n" + format_xml_error());
+
+  release_underlying(); // Free any existing schema.
 
   xmlSchema* schema = xmlSchemaParse( context );
   if ( ! schema )
-   throw parse_error("Schema could not be parsed");
+    throw parse_error("Schema could not be parsed\n" + format_xml_error());
 
   schema->_private = new Schema(schema);
 
@@ -85,6 +88,7 @@ void SchemaValidator::parse_context(_xmlSchemaParserCtxt* context)
 
 void SchemaValidator::parse_file(const Glib::ustring& filename)
 {
+  xmlResetLastError();
   xmlSchemaParserCtxtPtr ctx = xmlSchemaNewParserCtxt( filename.c_str() );
   XmlSchemaParserContextHolder holder(ctx);
   parse_context( ctx );
@@ -92,6 +96,7 @@ void SchemaValidator::parse_file(const Glib::ustring& filename)
 
 void SchemaValidator::parse_memory(const Glib::ustring& contents)
 {
+  xmlResetLastError();
   xmlSchemaParserCtxtPtr ctx = xmlSchemaNewMemParserCtxt( contents.c_str(), contents.bytes() );
   XmlSchemaParserContextHolder holder(ctx);
   parse_context( ctx );
@@ -99,6 +104,7 @@ void SchemaValidator::parse_memory(const Glib::ustring& contents)
 
 void SchemaValidator::parse_document(Document& document)
 {
+  xmlResetLastError();
   xmlSchemaParserCtxtPtr ctx = xmlSchemaNewDocParserCtxt( document.cobj() );
   XmlSchemaParserContextHolder holder(ctx);
   parse_context( ctx );
@@ -168,17 +174,22 @@ bool SchemaValidator::validate(const Document* doc)
     throw internal_error("Couldn't create validating context");
   }
 
+  xmlResetLastError();
   initialize_valid();
 
-  int res = xmlSchemaValidateDoc( ctxt_, (xmlDoc*)doc->cobj() );
+  const int res = xmlSchemaValidateDoc( ctxt_, (xmlDoc*)doc->cobj() );
 
   if(res != 0)
   {
     check_for_exception();
-    throw validity_error("Document failed schema validation");
+
+    Glib::ustring error_str = format_xml_error();
+    if (error_str.empty())
+      error_str = "Error code from xmlSchemaValidateDoc(): " + Glib::ustring::format(res);
+    throw validity_error("Document failed schema validation\n" + error_str);
   }
 
-  return res;
+  return res == 0;
 }
 
 bool SchemaValidator::validate(const Glib::ustring& file)
@@ -188,6 +199,7 @@ bool SchemaValidator::validate(const Glib::ustring& file)
 
   if (!schema_)
     throw internal_error("Must have a schema to validate document");
+
   // A context is required at this stage only
   if (!ctxt_)
     ctxt_ = xmlSchemaNewValidCtxt( schema_->cobj() );
@@ -196,17 +208,23 @@ bool SchemaValidator::validate(const Glib::ustring& file)
   {
     throw internal_error("Couldn't create validating context");
   }
+
+  xmlResetLastError();
   initialize_valid();
 
-  int res = xmlSchemaValidateFile( ctxt_, file.c_str(), 0 );
+  const int res = xmlSchemaValidateFile( ctxt_, file.c_str(), 0 );
 
   if(res != 0)
   {
     check_for_exception();
-    throw validity_error("Document failed schema validation");
+
+    Glib::ustring error_str = format_xml_error();
+    if (error_str.empty())
+      error_str = "Error code from xmlSchemaValidateFile(): " + Glib::ustring::format(res);
+    throw validity_error("Document failed schema validation\n" + error_str);
   }
 
-  return res;
+  return res == 0;
 }
 
 } // namespace xmlpp
diff --git a/libxml++/validators/schemavalidator.h b/libxml++/validators/schemavalidator.h
index c3aa7c9..f1ee910 100644
--- a/libxml++/validators/schemavalidator.h
+++ b/libxml++/validators/schemavalidator.h
@@ -21,7 +21,7 @@ extern "C" {
 
 namespace xmlpp {
 
-/** Schema Validator
+/** XML Schema Validator.
  *
  * @newin{2,24}
  */
@@ -29,23 +29,95 @@ class SchemaValidator : public Validator
 {
 public:
   SchemaValidator();
+
+  /** Create a validator and parse a schema definition file immediately.
+   * @param file The URL of the schema.
+   * @throws xmlpp::parse_error
+   */
   explicit SchemaValidator(const Glib::ustring& file);
+
+  /** Create a validator and parse a schema definition document immediately.
+   * @param document A preparsed document tree, containing the schema definition.
+   * @note The document may be modified during the parsing process.
+   * @throws xmlpp::parse_error
+   */
   explicit SchemaValidator(Document& document);
+
+  /** Create a schema validator.
+   * @param schema A pointer to the XML schema to use when validating XML documents.
+   *        The validator does not take ownership of the schema. The caller must
+   *        guarantee that the schema exists as long as the validator keeps a
+   *        pointer to it. The caller is responsible for deleting the schema
+   *        when it's no longer needed.
+   */
   explicit SchemaValidator(Schema* schema);
+
   virtual ~SchemaValidator();
 
+  /** Parse a schema definition file.
+   * If the validator already contains a schema, that schema is released
+   * (deleted if the validator owns the schema).
+   * @param file The URL of the schema.
+   * @throws xmlpp::parse_error
+   */
   virtual void parse_file(const Glib::ustring& filename);
+
+  /** Parse a schema definition from a string.
+   * If the validator already contains a schema, that schema is released
+   * (deleted if the validator owns the schema).
+   * @param contents The schema definition as a string.
+   * @throws xmlpp::parse_error
+   */
   virtual void parse_memory(const Glib::ustring& contents);
+
+  /** Parse a schema definition from a document.
+   * If the validator already contains a schema, that schema is released
+   * (deleted if the validator owns the schema).
+   * @param document A preparsed document tree, containing the schema definition.
+   * @note The document may be modified during the parsing process.
+   * @throws xmlpp::parse_error
+   */
   virtual void parse_document(Document& document);
+
+  /** Set a schema.
+   * If the validator already contains a schema, that schema is released
+   * (deleted if the validator owns the schema).
+   * @param schema A pointer to the XML schema to use when validating XML documents.
+   *        The validator does not take ownership of the schema. The caller must
+   *        guarantee that the schema exists as long as the validator keeps a
+   *        pointer to it. The caller is responsible for deleting the schema
+   *        when it's no longer needed.
+   */
   virtual void set_schema(Schema* schema);
 
-  /** Test whether a document has been parsed.
-  */
+  /** Test whether a schema has been parsed.
+   */
   operator bool() const;
+
+  /** Get the parsed schema.
+   * @returns A pointer to the parsed schema, or <tt>0</tt>.
+   */
   Schema* get_schema();
+
+  /** Get the parsed schema.
+   * @returns A pointer to the parsed schema, or <tt>0</tt>.
+   */
   const Schema* get_schema() const;
 
+  /** Validate a document, using a previously parsed schema.
+   * @param doc Pointer to the document.
+   * @returns Whether the document is valid.
+   * @throws xmlpp::internal_error
+   * @throws xmlpp::validity_error
+   */
   bool validate(const Document* doc);
+
+  /** Validate an XML file, using a previously parsed schema.
+   * @param file The URI of the XML file.
+   * @returns Whether the document is valid.
+   * @throws xmlpp::internal_error
+   * @throws xmlpp::validity_error
+   */
   bool validate(const Glib::ustring& file);
 
 protected:
@@ -54,13 +126,10 @@ protected:
   virtual void release_underlying();
 
   Schema* schema_;
-  bool embbeded_shema_;
+  bool embbeded_shema_; //TODO Correct mis-spelling at the next API/ABI break.
   _xmlSchemaValidCtxt* ctxt_;
 };
 
-
-
-
 } // namespace xmlpp
 
 #endif //__LIBXMLPP_VALIDATOR_SCHEMAVALIDATOR_H
diff --git a/libxml++/validators/validator.cc b/libxml++/validators/validator.cc
index 119ba61..d073e16 100644
--- a/libxml++/validators/validator.cc
+++ b/libxml++/validators/validator.cc
@@ -26,8 +26,7 @@ Validator::~Validator()
 
 void Validator::initialize_valid()
 {
-  //Tell the validity valid about the callbacks:
-  //(These are only called if validation is on - see above)
+  //Tell the validation context about the callbacks:
   valid_->error = &callback_validity_error;
   valid_->warning = &callback_validity_warning;
 
@@ -64,21 +63,28 @@ void Validator::on_validity_warning(const Glib::ustring& message)
 
 void Validator::check_for_validity_messages()
 {
-  if(!validate_error_.empty())
-  {
-    if(!exception_)
-      exception_ = new validity_error("Validity error:\n" + validate_error_);
+  Glib::ustring msg(exception_ ? exception_->what() : "");
+  bool validity_msg = false;
 
+  if (!validate_error_.empty())
+  {
+    validity_msg = true;
+    msg += "\nValidity error:\n" + validate_error_;
     validate_error_.erase();
   }
 
-  if(!validate_warning_.empty())
+  if (!validate_warning_.empty())
   {
-    if(!exception_)
-      exception_ = new validity_error("Validity warning:\n" + validate_warning_);
-
+    validity_msg = true;
+    msg += "\nValidity warning:\n" + validate_warning_;
     validate_warning_.erase();
   }
+
+  if (validity_msg)
+  {
+    delete exception_;
+    exception_ = new validity_error(msg);
+  }
 }
 
 void Validator::callback_validity_error(void* valid_, const char* msg, ...)
@@ -133,9 +139,19 @@ void Validator::callback_validity_warning(void* valid_, const char* msg, ...)
 
 void Validator::handleException(const exception& e)
 {
+  delete exception_;
   exception_ = e.Clone();
 
-  release_underlying();
+  // Don't delete the DTD validation context or schema validation context
+  // while validating. It would cause accesses to deallocated memory in libxml2
+  // functions after the return from Validator::callback_validity_...().
+  // Parser::handleException() calls xmlStopParser(), but there is no
+  // xmlStopValidator() or similar function to call here.
+  // We don't throw the exception here, since it would have to pass through
+  // C functions. That's not guaranteed to work. It might work, but it depends
+  // on the C compiler and the options used when building libxml2.
+
+  //release_underlying();
 }
 
 void Validator::check_for_exception()
@@ -151,5 +167,3 @@ void Validator::check_for_exception()
 }
 
 } // namespace xmlpp
-
-
diff --git a/libxml++/validators/validator.h b/libxml++/validators/validator.h
index a7ca2b3..7dc1b90 100644
--- a/libxml++/validators/validator.h
+++ b/libxml++/validators/validator.h
@@ -22,8 +22,7 @@ extern "C" {
 
 namespace xmlpp {
 
-/** XML parser.
- *
+/** Base class for XML validators.
  */
 class Validator : NonCopyable
 {



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]