[libxml++] Document, Schema: Improve the error handling.



commit f2767328abcb86fc0b0a4998676d9e4db72b2f1f
Author: Kjell Ahlstedt <kjell ahlstedt bredband net>
Date:   Sun Aug 5 15:53:34 2012 +0200

    Document, Schema: Improve the error handling.
    
    * libxml++/document.[h|cc]:
    * libxml++/schema.[h|cc]: Check more return codes from libxml2 functions.
    Improve the description of errors in the reference documentation. Bug #635846.

 ChangeLog            |    8 ++++++
 libxml++/document.cc |   65 ++++++++++++++++++++++++++++++++++++--------------
 libxml++/document.h  |   62 ++++++++++++++++++++++++++++++++++++----------
 libxml++/schema.cc   |   29 +++++++++++----------
 libxml++/schema.h    |   20 ++++++++++----
 5 files changed, 132 insertions(+), 52 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 04e7ab7..5f3a5f0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2012-08-05  Kjell Ahlstedt  <kjell ahlstedt bredband net>
+ 
+	Document, Schema: Improve the error handling.
+
+	* libxml++/document.[h|cc]:
+	* libxml++/schema.[h|cc]: Check more return codes from libxml2 functions.
+	Improve the description of errors in the reference documentation. Bug #635846.
+
 2012-08-02  Kjell Ahlstedt  <kjell ahlstedt bredband net>
  
 	Element, Node: Improve the error handling.
diff --git a/libxml++/document.cc b/libxml++/document.cc
index 06cf137..d07f07d 100644
--- a/libxml++/document.cc
+++ b/libxml++/document.cc
@@ -59,6 +59,8 @@ Document::Init Document::init_;
 Document::Document(const Glib::ustring& version)
   : impl_(xmlNewDoc((const xmlChar*)version.c_str()))
 {
+  if (!impl_)
+    throw internal_error("Could not create Document.");
   impl_->_private = this;
 }
 
@@ -125,7 +127,16 @@ Element* Document::create_root_node(const Glib::ustring& name,
                                     const Glib::ustring& ns_prefix)
 {
   xmlNode* node = xmlNewDocNode(impl_, 0, (const xmlChar*)name.c_str(), 0);
-  xmlDocSetRootElement(impl_, node);
+  if (!node)
+    throw internal_error("Could not create root element node " + name);
+
+  node = xmlDocSetRootElement(impl_, node);
+  if (node)
+  {
+    // An old root element node has been replaced.
+    Node::free_wrappers(node);
+    xmlFreeNode(node);
+  }
 
   Element* element = get_root_node();
 
@@ -141,28 +152,39 @@ Element* Document::create_root_node(const Glib::ustring& name,
 Element* Document::create_root_node_by_import(const Node* node,
 					      bool recursive)
 {
+  if (!node)
+    return 0;
+
   //Create the node, by copying:
   xmlNode* imported_node = xmlDocCopyNode(const_cast<xmlNode*>(node->cobj()), impl_, recursive);
   if (!imported_node)
   {
-    throw exception("Unable to import node");
+    throw exception("Unable to copy the node that shall be imported");
   }
 
-  xmlDocSetRootElement(impl_, imported_node);
+  xmlNode* old_node = xmlDocSetRootElement(impl_, imported_node);
+  if (old_node)
+  {
+    // An old root element node has been replaced.
+    Node::free_wrappers(old_node);
+    xmlFreeNode(old_node);
+  }
 
   return get_root_node();
 }
 
 CommentNode* Document::add_comment(const Glib::ustring& content)
 {
-  xmlNode* node = xmlNewComment((const xmlChar*)content.c_str());
-  if(!node)
+  xmlNode* child = xmlNewComment((const xmlChar*)content.c_str());
+ 
+  // Use the result, because child can be freed when merging text nodes:
+  xmlNode* node = xmlAddChild((xmlNode*)impl_, child);
+  if (!node)
   {
-    throw internal_error("Cannot create comment node");
+    if (child)
+      xmlFreeNode(child);
+    throw internal_error("Could not add comment node \"" + content + "\"");
   }
-
-  // Use the result, because node can be freed when merging text nodes:
-  node = xmlAddChild( (xmlNode*)impl_, node);
   Node::create_wrapper(node);
   return static_cast<CommentNode*>(node->_private);
 }
@@ -170,14 +192,16 @@ CommentNode* Document::add_comment(const Glib::ustring& content)
 ProcessingInstructionNode* Document::add_processing_instruction(
   const Glib::ustring& name, const Glib::ustring& content)
 {
-  xmlNode* node = xmlNewDocPI(impl_, (const xmlChar*)name.c_str(), (const xmlChar*)content.c_str());
-  if(!node)
+  xmlNode* child = xmlNewDocPI(impl_, (const xmlChar*)name.c_str(), (const xmlChar*)content.c_str());
+  xmlNode* node = xmlAddChild((xmlNode*)impl_, child);
+  if (!node)
   {
-    throw internal_error("Cannot create processing instruction node");
+    if (child)
+      xmlFreeNode(child);
+    throw internal_error("Could not add processing instruction node " + name);
   }
-  node = xmlAddChild((xmlNode*)impl_, node);
   Node::create_wrapper(node);
-  return node ? static_cast<ProcessingInstructionNode*>(node->_private) : 0;
+  return static_cast<ProcessingInstructionNode*>(node->_private);
 }
 
 void Document::write_to_file(const Glib::ustring& filename, const Glib::ustring& encoding)
@@ -217,12 +241,13 @@ void Document::do_write_to_file(
 {
   KeepBlanks k(KeepBlanks::Default);
   xmlIndentTreeOutput = format?1:0;
+  xmlResetLastError();
   const int result = xmlSaveFormatFileEnc(filename.c_str(), impl_,
     get_encoding_or_utf8(encoding), format?1:0);
 
   if(result == -1)
   {
-    throw exception("do_write_to_file() failed.");
+    throw exception("do_write_to_file() failed.\n" + format_xml_error());
   }
 }
 
@@ -235,12 +260,13 @@ Glib::ustring Document::do_write_to_string(
   xmlChar* buffer = 0;
   int length = 0;
 
+  xmlResetLastError();
   xmlDocDumpFormatMemoryEnc(impl_, &buffer, &length,
     get_encoding_or_utf8(encoding), format?1:0);
 
   if(!buffer)
   {
-    throw exception("do_write_to_string() failed.");
+    throw exception("do_write_to_string() failed.\n" + format_xml_error());
   }
 
   // Create a Glib::ustring copy of the buffer
@@ -260,12 +286,13 @@ void Document::do_write_to_stream(std::ostream& output, const Glib::ustring& enc
 {
   // TODO assert document encoding is UTF-8 if encoding is different than UTF-8
   OStreamOutputBuffer buffer(output, encoding);
+  xmlResetLastError();
   const int result = xmlSaveFormatFileTo(buffer.cobj(), impl_,
     get_encoding_or_utf8(encoding), format ? 1 : 0);
   
   if(result == -1)
   {
-    throw exception("do_write_to_stream() failed.");
+    throw exception("do_write_to_stream() failed.\n" + format_xml_error());
   }
 }
 
@@ -273,10 +300,12 @@ void Document::set_entity_declaration(const Glib::ustring& name, XmlEntityType t
                               const Glib::ustring& publicId, const Glib::ustring& systemId,
                               const Glib::ustring& content)
 {
-  xmlAddDocEntity( impl_, (const xmlChar*) name.c_str(), type,
+  xmlEntity* entity = xmlAddDocEntity( impl_, (const xmlChar*) name.c_str(), type,
     publicId.empty() ? (const xmlChar*)0 : (const xmlChar*)publicId.c_str(),
     systemId.empty() ? (const xmlChar*)0 : (const xmlChar*)systemId.c_str(),
     (const xmlChar*) content.c_str() );
+  if (!entity)
+    throw internal_error("Could not add entity declaration " + name);
 }
 
 _xmlEntity* Document::get_entity(const Glib::ustring& name)
diff --git a/libxml++/document.h b/libxml++/document.h
index 5c2e50e..d68675c 100644
--- a/libxml++/document.h
+++ b/libxml++/document.h
@@ -58,8 +58,13 @@ class Document : NonCopyable
 
   friend class DomParser;
   friend class SaxParser;
+  friend class Schema;
 
 public:
+  /** Create a new document.
+   * @param version XML version.
+   * @throws xmlpp::internal_error If memory allocation fails.
+   */
   explicit Document(const Glib::ustring& version = "1.0");
   
 protected:
@@ -69,11 +74,20 @@ public:
   virtual ~Document();
 
   /** @return The encoding used in the source from which the document has been loaded.
-    */
+   */
   Glib::ustring get_encoding() const;
 
+  /** Get the internal subset of this document.
+   * @returns A pointer to the DTD, or <tt>0</tt> if not found.
+   */
   Dtd* get_internal_subset() const;
 
+  /** Create the internal subset of this document.
+   * If the document already has an internal subset, a new one is not created.
+   * @param name The DTD name.
+   * @param external_id The external (PUBLIC) ID, or an empty string.
+   * @param system_id The system ID, or an empty string.
+   */
   void set_internal_subset(const Glib::ustring& name,
                            const Glib::ustring& external_id,
                            const Glib::ustring& system_id);
@@ -81,27 +95,36 @@ public:
   //TODO: There should be a const and non-const version.
   //See the patch here: https://bugzilla.gnome.org/show_bug.cgi?id=632522
   /** Return the root node.
-   * This function does _not_ create a default root node if it doesn't exist.
-   * @return A pointer to the root node if it exists, 0 otherwise.
+   * This function does @b not create a default root node if it doesn't exist.
+   * @return A pointer to the root node if it exists, <tt>0</tt> otherwise.
    */
   Element* get_root_node() const;
 
-  /** Creates the root node.
+  /** Create the root element node.
+   * If the document already contains a root element node, it is replaced, and
+   * the old root element node and all its descendants are deleted.
    * @param name The node's name.
-   * @param ns_uri The namespace URI. A namespace declaration will be added to this node, because it could not have
-     been declared before.
-   * @param ns_prefix The namespace prefix to associate with the namespace. If no namespace prefix is specified then
-     the namespace URI will be the default namespace.
-   * @return A pointer to the new root node
+   * @param ns_uri The namespace URI. A namespace declaration will be added to
+   *        this node, because it could not have been declared before.
+   * @param ns_prefix The namespace prefix to associate with the namespace.
+   *        If no namespace prefix is specified then the namespace URI will be the default namespace.
+   * @return A pointer to the new root node.
+   * @throws xmlpp::internal_error If memory allocation fails.
+   * @throws xmlpp::exception If a new namespace node cannot be created.
    */
   Element* create_root_node(const Glib::ustring& name,
                             const Glib::ustring& ns_uri = Glib::ustring(),
                             const Glib::ustring& ns_prefix = Glib::ustring() );
 
-  /** Creates a root node by importing the node from another document, without affecting the source node.
-   * @param node The node to copy and insert as the root node of the document
+  /** Create a root element node by importing the node from another document,
+   * without affecting the source node.
+   * If the document already contains a root element node, it is replaced, and
+   * the old root element node and all its descendants are deleted.
+   * @param node The node to copy and insert as the root node of the document.
+   *             It must be an element node.
    * @param recursive Whether to import the child nodes also. Defaults to true.
    * @return A pointer to the new root node
+   * @throws xmlpp::exception If the node can't be copied.
    */
   Element* create_root_node_by_import(const Node* node,
 				      bool recursive = true);
@@ -109,6 +132,7 @@ public:
   /** Append a new comment node.
    * @param content The text. This should be unescaped - see ContentNode::set_content().
    * @returns The new comment node.
+   * @throws xmlpp::internal_error
    */
   CommentNode* add_comment(const Glib::ustring& content);
 
@@ -119,7 +143,7 @@ public:
    * @param name The name of the application to which the instruction is directed.
    * @param content The content of the instruction. This should be unescaped - see ContentNode::set_content().
    * @returns The new processing instruction node.
-   * @throws internal_error
+   * @throws xmlpp::internal_error
    */
   ProcessingInstructionNode* add_processing_instruction(
     const Glib::ustring& name, const Glib::ustring& content);
@@ -128,6 +152,7 @@ public:
   /** Write the document to a file.
    * @param filename
    * @param encoding If not provided, UTF-8 is used
+   * @throws xmlpp::exception
    */
   void write_to_file(const Glib::ustring& filename, const Glib::ustring& encoding = Glib::ustring());
 
@@ -136,11 +161,14 @@ public:
    * but may insert unwanted significant whitespaces. Use with care !
    * @param filename
    * @param encoding If not provided, UTF-8 is used
+   * @throws xmlpp::exception
    */
   void write_to_file_formatted(const Glib::ustring& filename, const Glib::ustring& encoding = Glib::ustring());
 
   /** Write the document to the memory.
    * @param encoding If not provided, UTF-8 is used
+   * @returns The written document.
+   * @throws xmlpp::exception
    */
   Glib::ustring write_to_string(const Glib::ustring& encoding = Glib::ustring());
 
@@ -148,13 +176,16 @@ public:
    * The output is formatted by inserting whitespaces, which is easier to read for a human,
    * but may insert unwanted significant whitespaces. Use with care !
    * @param encoding If not provided, UTF-8 is used
-   * @return The written document.
+   * @returns The written document.
+   * @throws xmlpp::exception
    */
   Glib::ustring write_to_string_formatted(const Glib::ustring& encoding = Glib::ustring());
 
   /** Write the document to a std::ostream.
    * @param output A reference to the stream in which the document will be written
    * @param encoding If not provided, UTF-8 is used
+   * @throws xmlpp::exception
+   * @throws xmlpp::internal_error
    * @warning This method is much less efficient than write_to_string if you want to dump the
    * document to a buffer or the standard output. Writing to a fstream is almost as fast as write_to_file
    */
@@ -165,6 +196,8 @@ public:
    * but may insert unwanted significant whitespaces. Use with care !
    * @param output A reference to the stream in which the document will be written
    * @param encoding If not provided, UTF-8 is used
+   * @throws xmlpp::exception
+   * @throws xmlpp::internal_error
    * @warning See write_to_stream
    */
   void write_to_stream_formatted(std::ostream & output, const Glib::ustring& encoding = Glib::ustring());
@@ -176,6 +209,7 @@ public:
    * @param systemId The system ID of the subset.
    * @param content The value of the Entity. In entity reference substitutions, this
    * is the replacement value.
+   * @throws xmlpp::internal_error
    */
   virtual void set_entity_declaration(const Glib::ustring& name, XmlEntityType type,
                                       const Glib::ustring& publicId, const Glib::ustring& systemId,
@@ -191,7 +225,7 @@ protected:
   /** Retrieve an Entity.
    * The entity can be from an external subset or internally declared.
    * @param name The name of the entity to get.
-   * @returns A pointer to the libxml2 entity structure.
+   * @returns A pointer to the libxml2 entity structure, or <tt>0</tt> if not found.
    */
   _xmlEntity* get_entity(const Glib::ustring& name);
 
diff --git a/libxml++/schema.cc b/libxml++/schema.cc
index 581bb55..17c7f78 100644
--- a/libxml++/schema.cc
+++ b/libxml++/schema.cc
@@ -36,18 +36,19 @@ void Schema::set_document(Document* document, bool embed)
 {
   release_underlying();
 
+  xmlResetLastError();
   xmlSchemaParserCtxtPtr context = xmlSchemaNewDocParserCtxt( document->cobj() );
 
   if(!context)
   {
-   throw parse_error("Schema could not be parsed");
+    throw parse_error("Schema could not be parsed.\n" + format_xml_error());
   }
   
   impl_ = xmlSchemaParse(context);
   if(!impl_)
   {
     xmlSchemaFreeParserCtxt(context);
-    throw parse_error("Schema could not be parsed");
+    throw parse_error("Schema could not be parsed.\n" + format_xml_error());
   }
 
   impl_->_private = this;
@@ -57,22 +58,22 @@ void Schema::set_document(Document* document, bool embed)
 
 Glib::ustring Schema::get_name() const
 {
-  return (char*)impl_->name;
+  return impl_ ? (char*)impl_->name : "";
 }
 
 Glib::ustring Schema::get_target_namespace() const
 {
-  return (char*)impl_->targetNamespace;
+  return impl_ ? (char*)impl_->targetNamespace : "";
 }
 
 Glib::ustring Schema::get_version() const
 {
-  return (char*)impl_->version;
+  return impl_ ? (char*)impl_->version : "";
 }
 
 void Schema::release_underlying()
 {
-  if(embedded_doc_ && impl_ && impl_->doc->_private)
+  if(embedded_doc_ && impl_ && impl_->doc && impl_->doc->_private)
   {
     delete (Document*) impl_->doc->_private;
     embedded_doc_ = false;
@@ -87,18 +88,18 @@ void Schema::release_underlying()
 
 Document* Schema::get_document()
 {
-  if(impl_)
-    return (Document*) impl_->doc->_private;
-  else
+  if (!(impl_ && impl_->doc))
     return 0;
+
+  if (!impl_->doc->_private) // Possible if *this was created with Schema(xmlSchema* schema).
+    new Document(impl_->doc); // Sets impl_->doc->_private
+
+  return (Document*) impl_->doc->_private;
 }
 
-const Document* Schema::get_document()const
+const Document* Schema::get_document() const
 {
-  if(impl_)
-    return (Document*) impl_->doc->_private;
-  else
-    return 0;
+  return const_cast<Schema*>(this)->get_document();
 }
 
 _xmlSchema* Schema::cobj()
diff --git a/libxml++/schema.h b/libxml++/schema.h
index 3f56059..52806e4 100644
--- a/libxml++/schema.h
+++ b/libxml++/schema.h
@@ -32,17 +32,21 @@ public:
    */
   explicit Schema(_xmlSchema* schema);
 
-  /** Create a schema from a XML document.
+  /** Create a schema from an XML document.
    * @param document XMLSchema document, 0 to create an empty schema document.
    * @param embed If true, the document will be deleted when
    *   the schema is deleted or another document is set.
+   * @throws xmlpp::parse_error
    */
   explicit Schema(Document* document = 0, bool embed = false);
   ~Schema();
 
   /** Set a new document to the schema.
+   * If the old schema document is owned by the schema (embed == true), the old
+   * schema document and all its nodes are deleted.
    * @param document XMLSchema document, 0 to create an empty schema document.
    * @param embed If true, the document will be deleted when the schema is deleted or another document is set.
+   * @throws xmlpp::parse_error
    */
   virtual void set_document(Document* document = 0, bool embed = false);
 
@@ -50,8 +54,15 @@ public:
   Glib::ustring get_target_namespace() const;
   Glib::ustring get_version() const;
 
+  /** Get the schema document.
+   * @returns A pointer to the schema document, or <tt>0</tt> if none exists.
+   */
   Document* get_document();
-  const Document* get_document()const;
+
+  /** Get the schema document.
+   * @returns A pointer to the schema document, or <tt>0</tt> if none exists.
+   */
+  const Document* get_document() const;
   
   /** Access the underlying libxml implementation. */
   _xmlSchema* cobj();
@@ -65,13 +76,10 @@ protected:
 private:
   _xmlSchema* impl_;
 
-  /** Is the base document is created with the schema. */
+  /** If the base document is created with the schema. */
   bool embedded_doc_;
 };
 
 } // namespace xmlpp
 
 #endif //__LIBXMLPP_SCHEMA_H
-
-
-



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