[libxml++] Improved handling of entity references and processing instructions.



commit 09a10ea98255449c71ad07e389057cdbb80d2bb7
Author: Kjell Ahlstedt <kjell ahlstedt bredband net>
Date:   Wed Feb 15 15:09:59 2012 +0100

    Improved handling of entity references and processing instructions.
    
    * libxml++/nodes/entitydeclaration.[h|cc]: New files.
    * Makefile.am:
    * libxml++/Makefile.am: Add the new files.
    * libxml++/libxml++.h: Add the new .h file.
    * docs/manual/libxml++_without_code.xml: Add EntityDeclaration in the list
    of node classes.
    * libxml++/document.[h|cc]: Add add_processing_instruction().
    * libxml++/nodes/element.[h|cc]: Add add_child_entity_reference() and
    add_child_processing_instruction().
    * libxml++/nodes/entityreference.h: Improve the description of
    get_resolved_text() and get_original_text().
    * libxml++/nodes/node.cc: get_namespace_prefix() and get_namespace_uri():
    XML_ENTITY_DECL has no namespace. Don't try to find it.
    create_wrapper(): Create an EntityDeclaration when type == XML_ENTITY_DECL.
    free_wrappers(): Don't walk the child list when type == XML_ENTITY_REF_NODE.
    * examples/dom_build/main.cc: Add entity declarations and references, and
    processing instructions to the built xml file.
    * examples/dom_parse_entities/example.dtd: Make it compatible with example.xml.
    * examples/dom_parse_entities/example.xml: Add an entity definition that
    contains entity references.
    * examples/dom_parse_entities/main.cc: Print the parsed file both with and
    without entity substitution.
    * examples/dom_parser/example.dtd: Make it compatible with example.xml.
    * examples/dom_parser/main.cc: Add command flag -E (Don't substitute entities).
    Bug #669481

 ChangeLog                               |   30 +++++++++++
 Makefile.am                             |    2 +
 docs/manual/libxml++_without_code.xml   |    3 +-
 examples/dom_build/main.cc              |   16 ++++--
 examples/dom_parse_entities/example.dtd |    2 +-
 examples/dom_parse_entities/example.xml |    7 +++
 examples/dom_parse_entities/main.cc     |   84 +++++++++++++++++++------------
 examples/dom_parser/example.dtd         |    5 +-
 examples/dom_parser/main.cc             |   56 +++++++++-----------
 libxml++/Makefile.am                    |    2 +
 libxml++/document.cc                    |   17 ++++++
 libxml++/document.h                     |   18 ++++++-
 libxml++/libxml++.h                     |    1 +
 libxml++/nodes/element.cc               |   30 ++++++++++-
 libxml++/nodes/element.h                |   31 +++++++++++-
 libxml++/nodes/entitydeclaration.cc     |   44 ++++++++++++++++
 libxml++/nodes/entitydeclaration.h      |   54 ++++++++++++++++++++
 libxml++/nodes/entityreference.h        |   12 +++--
 libxml++/nodes/node.cc                  |   39 ++++++++++----
 19 files changed, 361 insertions(+), 92 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 32b6352..e84c148 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,35 @@
 2012-02-15  Kjell Ahlstedt  <kjell ahlstedt bredband net>
 
+	Improved handling of entity references and processing instructions.
+
+	* libxml++/nodes/entitydeclaration.[h|cc]: New files.
+	* Makefile.am:
+	* libxml++/Makefile.am: Add the new files.
+	* libxml++/libxml++.h: Add the new .h file.
+	* docs/manual/libxml++_without_code.xml: Add EntityDeclaration in the list
+	of node classes.
+	* libxml++/document.[h|cc]: Add add_processing_instruction().
+	* libxml++/nodes/element.[h|cc]: Add add_child_entity_reference() and
+	add_child_processing_instruction().
+	* libxml++/nodes/entityreference.h: Improve the description of
+	get_resolved_text() and get_original_text().
+	* libxml++/nodes/node.cc: get_namespace_prefix() and get_namespace_uri():
+	XML_ENTITY_DECL has no namespace. Don't try to find it.
+	create_wrapper(): Create an EntityDeclaration when type == XML_ENTITY_DECL.
+	free_wrappers(): Don't walk the child list when type == XML_ENTITY_REF_NODE.
+	* examples/dom_build/main.cc: Add entity declarations and references, and
+	processing instructions to the built xml file.
+	* examples/dom_parse_entities/example.dtd: Make it compatible with example.xml.
+	* examples/dom_parse_entities/example.xml: Add an entity definition that
+	contains entity references.
+	* examples/dom_parse_entities/main.cc: Print the parsed file both with and
+	without entity substitution.
+	* examples/dom_parser/example.dtd: Make it compatible with example.xml.
+	* examples/dom_parser/main.cc: Add command flag -E (Don't substitute entities).
+	Bug #669481
+
+2012-02-15  Kjell Ahlstedt  <kjell ahlstedt bredband net>
+
 	Add some files to .gitignore.
 
 	* .gitignore: Add docs files that are copied from mm-common.
diff --git a/Makefile.am b/Makefile.am
index 4458317..5b76b34 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -53,6 +53,7 @@ h_nodes_sources_public = libxml++/nodes/cdatanode.h \
 	libxml++/nodes/commentnode.h \
 	libxml++/nodes/contentnode.h \
 	libxml++/nodes/element.h \
+	libxml++/nodes/entitydeclaration.h \
 	libxml++/nodes/entityreference.h \
 	libxml++/nodes/node.h \
 	libxml++/nodes/processinginstructionnode.h \
@@ -88,6 +89,7 @@ cc_sources = libxml++/attribute.cc \
 	libxml++/nodes/cdatanode.cc \
 	libxml++/nodes/commentnode.cc \
 	libxml++/nodes/contentnode.cc \
+	libxml++/nodes/entitydeclaration.cc \
 	libxml++/nodes/entityreference.cc \
 	libxml++/nodes/element.cc \
 	libxml++/nodes/node.cc \
diff --git a/docs/manual/libxml++_without_code.xml b/docs/manual/libxml++_without_code.xml
index 62b988e..c30a885 100644
--- a/docs/manual/libxml++_without_code.xml
+++ b/docs/manual/libxml++_without_code.xml
@@ -91,6 +91,7 @@ url="http://libxmlplusplus.sourceforge.net";>libxmlplusplus.sourceforge.net</ulin
           <itemizedlist>
             <listitem><para>xmlpp::CdataNode</para></listitem>
             <listitem><para>xmlpp::CommentNode</para></listitem>
+            <listitem><para>xmlpp::EntityDeclaration</para></listitem>
             <listitem><para>xmlpp::ProcessingInstructionNode</para></listitem>
             <listitem><para>xmlpp::TextNode</para></listitem>
           </itemizedlist>
@@ -103,7 +104,7 @@ url="http://libxmlplusplus.sourceforge.net";>libxmlplusplus.sourceforge.net</ulin
       </itemizedlist>
     </para>
 
-      <para>Although you may obtain pointers to the <literal>Node</literal>s, these <literal>Node</literal>s are always owned by their parent Nodes. In most cases that means that the Node will exist, and your pointer will be valid, as long as the <literal>Document</literal> instance exists.</para>
+      <para>Although you may obtain pointers to the <literal>Node</literal>s, these <literal>Node</literal>s are always owned by their parent <literal>Node</literal>. In most cases that means that the <literal>Node</literal> will exist, and your pointer will be valid, as long as the <literal>Document</literal> instance exists.</para>
       <para>There are also several methods which can create new child <literal>Node</literal>s. By using these, and one of the <literal>Document::write_*()</literal> methods, you can use libxml++ to build a new XML document.</para>
 
 
diff --git a/examples/dom_build/main.cc b/examples/dom_build/main.cc
index 82cdc0b..1ed3090 100644
--- a/examples/dom_build/main.cc
+++ b/examples/dom_build/main.cc
@@ -24,10 +24,8 @@
 #endif
 
 #include <libxml++/libxml++.h>
-
 #include <iostream>
 
-
 int
 main(int /* argc */, char** /* argv */)
 {
@@ -41,11 +39,16 @@ main(int /* argc */, char** /* argv */)
   #endif //LIBXMLCPP_EXCEPTIONS_ENABLED
     xmlpp::Document document;
     document.set_internal_subset("example_xml_doc", "", "example_xml_doc.dtd");
+    document.set_entity_declaration("example1", xmlpp::XML_INTERNAL_GENERAL_ENTITY,
+      "", "example_xml_doc.dtd", "Entity content");
+    document.add_processing_instruction("application1", "This is an example document");
+    document.add_comment("First comment");
 
     //foo is the default namespace prefix.
     xmlpp::Element* nodeRoot = document.create_root_node("exampleroot", "http://foo";, "foo"); //Declares the namespace and uses its prefix for this node
     nodeRoot->set_namespace_declaration("http://foobar";, "foobar"); //Also associate this prefix with this namespace: 
 
+    nodeRoot->set_child_text("\n");
     xmlpp::Element* nodeChild = nodeRoot->add_child("examplechild");
 
     //Associate prefix with namespace:
@@ -53,8 +56,13 @@ main(int /* argc */, char** /* argv */)
      
     nodeChild->set_namespace("bar"); //So it will be bar::examplechild.
     nodeChild->set_attribute("id", "1", "foo"); //foo is the namespace prefix. You could also just use a name of foo:id".
-    nodeChild->set_child_text("Some content");
+    nodeChild->set_child_text("\nSome content\n");
     nodeChild->add_child_comment("Some comments");
+    nodeChild->add_child_entity_reference("example1");
+    nodeChild->add_child_entity_reference("#x20ac"); // â
+    nodeChild->add_child_text("\n");
+    nodeChild->add_child_processing_instruction("application1", "This is an example node");
+    nodeChild->add_child_text("\n");
     nodeChild->add_child("child_of_child", "bar");
 
     nodeChild = nodeRoot->add_child("examplechild", "foobar"); //foobar is the namespace prefix
@@ -62,7 +70,7 @@ main(int /* argc */, char** /* argv */)
 
     Glib::ustring whole = document.write_to_string();
     std::cout << "XML built at runtime: " << std::endl << whole << std::endl;
-    std::cout << "default namespace: " << nodeRoot->get_namespace_uri() << std::endl;
+    std::cout << "namespace of root node: " << nodeRoot->get_namespace_uri() << std::endl;
   #ifdef LIBXMLCPP_EXCEPTIONS_ENABLED
   }
   catch(const std::exception& ex)
diff --git a/examples/dom_parse_entities/example.dtd b/examples/dom_parse_entities/example.dtd
index 925f074..3d5c32f 100644
--- a/examples/dom_parse_entities/example.dtd
+++ b/examples/dom_parse_entities/example.dtd
@@ -5,7 +5,7 @@ DTD for libxml++ example.
 
 <!ELEMENT example (examplechild)+ >
 
-<!ELEMENT examplechild (child_of_child)+ >
+<!ELEMENT examplechild (#PCDATA | child_of_child)* >
 <!ATTLIST examplechild
   id	CDATA	#REQUIRED
 >
diff --git a/examples/dom_parse_entities/example.xml b/examples/dom_parse_entities/example.xml
index b8c97ad..93d4a4c 100644
--- a/examples/dom_parse_entities/example.xml
+++ b/examples/dom_parse_entities/example.xml
@@ -2,6 +2,11 @@
 <!DOCTYPE example PUBLIC "" "example.dtd" [
 <!ENTITY wwwmurrayc "http://www.murrayc.com";>
 <!ENTITY wwwlibxmlplusplus "http://libxmlplusplus.sourceforge.net";>
+<!ENTITY mercury "&#x263f;">
+<!ENTITY venus "&#x2640;">
+<!ENTITY earth "&#x2641;">
+<!ENTITY mars "&#x2642;">
+<!ENTITY planets "&mercury;(&#x263f;), &venus;(&#x2640;), &earth;(&#x2641;), &mars;(&#x2642;)">
 ]>
 
 <example>
@@ -9,6 +14,8 @@
     Some content. &wwwmurrayc;
     Some other content &wwwlibxmlplusplus;
     <child_of_child/>
+    Mercury &mercury;
+    Some planets &planets;
   </examplechild>
 </example>
 
diff --git a/examples/dom_parse_entities/main.cc b/examples/dom_parse_entities/main.cc
index 9a326a4..b0c47ff 100644
--- a/examples/dom_parse_entities/main.cc
+++ b/examples/dom_parse_entities/main.cc
@@ -27,25 +27,31 @@
 
 #include <iostream>
 
-void print_indentation(unsigned int indentation)
-{
-  for(unsigned int i = 0; i < indentation; ++i)
-    std::cout << " ";
-}
-
-void print_node(const xmlpp::Node* node, unsigned int indentation = 0)
+void print_node(const xmlpp::Node* node, bool substitute_entities, unsigned int indentation = 0)
 {  
+  const Glib::ustring indent(indentation, ' ');
   std::cout << std::endl; //Separate nodes by an empty line.
-  
-  const xmlpp::EntityReference* nodeEntityReference = dynamic_cast<const xmlpp::EntityReference*>(node);
 
-  if(nodeEntityReference)
+  if (substitute_entities)
   {
-    print_indentation(indentation);
-    std::cout << "entity reference name = " << nodeEntityReference->get_name() << std::endl;
-    std::cout <<  "  resolved text = " << nodeEntityReference->get_resolved_text() << std::endl;
-    std::cout <<  "  original text = " << nodeEntityReference->get_original_text() << std::endl;
+    // Entities have been substituted. Print the text nodes.
+    const xmlpp::TextNode* nodeText = dynamic_cast<const xmlpp::TextNode*>(node);
+    if (nodeText && !nodeText->is_white_space())
+    {
+      std::cout << indent << "text = " << nodeText->get_content() << std::endl;
+    }
   }
+  else
+  {
+    // Entities have not been substituted. Print the entity reference nodes.
+    const xmlpp::EntityReference* nodeEntityReference = dynamic_cast<const xmlpp::EntityReference*>(node);
+    if (nodeEntityReference)
+    {
+      std::cout << indent << "entity reference name = " << nodeEntityReference->get_name() << std::endl;
+      std::cout << indent <<  "  resolved text = " << nodeEntityReference->get_resolved_text() << std::endl;
+      std::cout << indent <<  "  original text = " << nodeEntityReference->get_original_text() << std::endl;
+    }
+  } // end if (substitute_entities)
 
   const xmlpp::ContentNode* nodeContent = dynamic_cast<const xmlpp::ContentNode*>(node);
   if(!nodeContent)
@@ -54,7 +60,7 @@ void print_node(const xmlpp::Node* node, unsigned int indentation = 0)
     xmlpp::Node::NodeList list = node->get_children();
     for(xmlpp::Node::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
     {   
-      print_node(*iter, indentation + 2); //recursive
+      print_node(*iter, substitute_entities, indentation + 2); //recursive
     }
   }
 }
@@ -71,27 +77,41 @@ int main(int argc, char* argv[])
   else
     filepath = "example.xml";
   
-  #ifdef LIBXMLCPP_EXCEPTIONS_ENABLED
-  try
+  // Parse first without, then with, entity substitution.
+  bool substitute_entities = false;
+  while (true)
   {
-  #endif //LIBXMLCPP_EXCEPTIONS_ENABLED 
-    xmlpp::DomParser parser;
-    //parser.set_validate();
-    parser.set_substitute_entities(false);
-    parser.parse_file(filepath);
-    if(parser)
+    if (substitute_entities)
+      std::cout << std::endl << "<<< With entity substitution >>>" << std::endl;
+    else
+      std::cout << std::endl << "<<< Without entity substitution >>>" << std::endl;
+
+    #ifdef LIBXMLCPP_EXCEPTIONS_ENABLED
+    try
     {
-      //Walk the tree:
-      const xmlpp::Node* pNode = parser.get_document()->get_root_node(); //deleted by DomParser.
-      print_node(pNode);
+    #endif //LIBXMLCPP_EXCEPTIONS_ENABLED
+      xmlpp::DomParser parser;
+      parser.set_validate();
+      parser.set_substitute_entities(substitute_entities);
+      parser.parse_file(filepath);
+      if(parser)
+      {
+        //Walk the tree:
+        const xmlpp::Node* pNode = parser.get_document()->get_root_node(); //deleted by DomParser.
+        print_node(pNode, substitute_entities);
+      }
+    #ifdef LIBXMLCPP_EXCEPTIONS_ENABLED
     }
-  #ifdef LIBXMLCPP_EXCEPTIONS_ENABLED
-  }
-  catch(const std::exception& ex)
-  {
-    std::cout << "Exception caught: " << ex.what() << std::endl;
+    catch(const std::exception& ex)
+    {
+      std::cout << "Exception caught: " << ex.what() << std::endl;
+    }
+    #endif //LIBXMLCPP_EXCEPTIONS_ENABLED
+
+    if (substitute_entities) break;
+
+    substitute_entities = true;
   }
-  #endif //LIBXMLCPP_EXCEPTIONS_ENABLED 
 
   return 0;
 }
diff --git a/examples/dom_parser/example.dtd b/examples/dom_parser/example.dtd
index 925f074..b21ed41 100644
--- a/examples/dom_parser/example.dtd
+++ b/examples/dom_parser/example.dtd
@@ -3,12 +3,13 @@
 DTD for libxml++ example.
 -->
 
-<!ELEMENT example (examplechild)+ >
+<!ELEMENT example (examplechild | examplechildtext)+ >
 
 <!ELEMENT examplechild (child_of_child)+ >
 <!ATTLIST examplechild
-  id	CDATA	#REQUIRED
+  id    CDATA	#REQUIRED
 >
 
 <!ELEMENT child_of_child EMPTY >
 
+<!ELEMENT examplechildtext (#PCDATA) >
diff --git a/examples/dom_parser/main.cc b/examples/dom_parser/main.cc
index 3508216..5929ce3 100644
--- a/examples/dom_parser/main.cc
+++ b/examples/dom_parser/main.cc
@@ -27,14 +27,9 @@
 
 #include <iostream>
 
-void print_indentation(unsigned int indentation)
-{
-  for(unsigned int i = 0; i < indentation; ++i)
-    std::cout << " ";
-}
-
 void print_node(const xmlpp::Node* node, unsigned int indentation = 0)
 {
+  const Glib::ustring indent(indentation, ' ');
   std::cout << std::endl; //Separate nodes by an empty line.
   
   const xmlpp::ContentNode* nodeContent = dynamic_cast<const xmlpp::ContentNode*>(node);
@@ -48,62 +43,55 @@ void print_node(const xmlpp::Node* node, unsigned int indentation = 0)
 
   if(!nodeText && !nodeComment && !nodename.empty()) //Let's not say "name: text".
   {
-    print_indentation(indentation);
-
     const Glib::ustring namespace_prefix = node->get_namespace_prefix();
-    if(namespace_prefix.empty())
-      std::cout << "Node name = " << nodename << std::endl;
-    else
-      std::cout << "Node name = " << namespace_prefix << ":" << nodename << std::endl;
+
+    std::cout << indent << "Node name = ";
+    if(!namespace_prefix.empty())
+      std::cout << namespace_prefix << ":";
+    std::cout << nodename << std::endl;
   }
   else if(nodeText) //Let's say when it's text. - e.g. let's say what that white space is.
   {
-    print_indentation(indentation);
-    std::cout << "Text Node" << std::endl;
+    std::cout << indent << "Text Node" << std::endl;
   }
 
   //Treat the various node types differently: 
   if(nodeText)
   {
-    print_indentation(indentation);
-    std::cout << "text = \"" << nodeText->get_content() << "\"" << std::endl;
+    std::cout << indent << "text = \"" << nodeText->get_content() << "\"" << std::endl;
   }
   else if(nodeComment)
   {
-    print_indentation(indentation);
-    std::cout << "comment = " << nodeComment->get_content() << std::endl;
+    std::cout << indent << "comment = " << nodeComment->get_content() << std::endl;
   }
   else if(nodeContent)
   {
-    print_indentation(indentation);
-    std::cout << "content = " << nodeContent->get_content() << std::endl;
+    std::cout << indent << "content = " << nodeContent->get_content() << std::endl;
   }
   else if(const xmlpp::Element* nodeElement = dynamic_cast<const xmlpp::Element*>(node))
   {
     //A normal Element node:
 
     //line() works only for ElementNodes.
-    print_indentation(indentation);
-    std::cout << "     line = " << node->get_line() << std::endl;
+    std::cout << indent << "     line = " << node->get_line() << std::endl;
 
     //Print attributes:
     const xmlpp::Element::AttributeList& attributes = nodeElement->get_attributes();
     for(xmlpp::Element::AttributeList::const_iterator iter = attributes.begin(); iter != attributes.end(); ++iter)
     {
       const xmlpp::Attribute* attribute = *iter;
-      print_indentation(indentation);
-
       const Glib::ustring namespace_prefix = attribute->get_namespace_prefix();
-      if(namespace_prefix.empty())
-        std::cout << "  Attribute " << attribute->get_name() << " = " << attribute->get_value() << std::endl; 
-      else
-        std::cout << "  Attribute " << namespace_prefix  << ":" << attribute->get_name() << " = " << attribute->get_value() << std::endl;
+
+      std::cout << indent << "  Attribute ";
+      if(!namespace_prefix.empty())
+        std::cout << namespace_prefix  << ":";
+      std::cout << attribute->get_name() << " = " << attribute->get_value() << std::endl;
     }
 
     const xmlpp::Attribute* attribute = nodeElement->get_attribute("title");
     if(attribute)
     {
-      std::cout << "title found: =" << attribute->get_value() << std::endl;
+      std::cout << indent << "title = " << attribute->get_value() << std::endl;
     }
   }
   
@@ -127,6 +115,7 @@ int main(int argc, char* argv[])
   bool validate = false;
   bool set_throw_messages = false;
   bool throw_messages = false;
+  bool substitute_entities = true;
 
   int argi = 1;
   while (argc > argi && *argv[argi] == '-') // option
@@ -144,11 +133,15 @@ int main(int argc, char* argv[])
        set_throw_messages = true;
        throw_messages = false;
        break;
+      case 'E':
+        substitute_entities = false;
+        break;
      default:
        std::cout << "Usage: " << argv[0] << " [-v] [-t] [-e] [filename]" << std::endl
                  << "       -v  Validate" << std::endl
                  << "       -t  Throw messages in an exception" << std::endl
-                 << "       -e  Write messages to stderr" << std::endl;
+                 << "       -e  Write messages to stderr" << std::endl
+                 << "       -E  Do not substitute entities" << std::endl;
        return 1;
      }
      argi++;
@@ -168,7 +161,8 @@ int main(int argc, char* argv[])
       parser.set_validate();
     if (set_throw_messages)
       parser.set_throw_messages(throw_messages);
-    parser.set_substitute_entities(); //We just want the text to be resolved/unescaped automatically.
+    //We can have the text resolved/unescaped automatically.
+    parser.set_substitute_entities(substitute_entities);
     parser.parse_file(filepath);
     if(parser)
     {
diff --git a/libxml++/Makefile.am b/libxml++/Makefile.am
index e511103..091ce26 100644
--- a/libxml++/Makefile.am
+++ b/libxml++/Makefile.am
@@ -20,6 +20,7 @@ h_sources_public = libxml++.h \
 	nodes/commentnode.h \
 	nodes/contentnode.h \
 	nodes/element.h \
+	nodes/entitydeclaration.h \
 	nodes/entityreference.h \
 	nodes/node.h \
 	nodes/processinginstructionnode.h \
@@ -48,6 +49,7 @@ cc_sources = attribute.cc \
 	nodes/cdatanode.cc \
 	nodes/commentnode.cc \
 	nodes/contentnode.cc \
+	nodes/entitydeclaration.cc \
 	nodes/entityreference.cc \
 	nodes/element.cc \
 	nodes/node.cc \
diff --git a/libxml++/document.cc b/libxml++/document.cc
index 0be6a67..03e6996 100644
--- a/libxml++/document.cc
+++ b/libxml++/document.cc
@@ -175,6 +175,23 @@ CommentNode* Document::add_comment(const Glib::ustring& content)
   return static_cast<CommentNode*>(node->_private);
 }
 
+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)
+  {
+    #ifdef LIBXMLCPP_EXCEPTIONS_ENABLED
+    throw internal_error("Cannot create processing instruction node");
+    #else
+    return 0;
+    #endif //LIBXMLCPP_EXCEPTIONS_ENABLED
+  }
+  node = xmlAddChild((xmlNode*)impl_, node);
+  Node::create_wrapper(node);
+  return node ? static_cast<ProcessingInstructionNode*>(node->_private) : 0;
+}
+
 void Document::write_to_file(const Glib::ustring& filename, const Glib::ustring& encoding)
 {
   do_write_to_file(filename, encoding, false);
diff --git a/libxml++/document.h b/libxml++/document.h
index d8a66fb..5c2e50e 100644
--- a/libxml++/document.h
+++ b/libxml++/document.h
@@ -42,7 +42,7 @@ class Document;
 //TODO: Make Document inherit from Node, when we can break ABI one day?
 //
 //libxml might intend xmlDoc to derive (theoretically) from xmlNode.
-//This is suggested because the XmlNodeSet returned by xmlXPathEval (see the Node::find() implementation) can contain either xmlNode or xmlDocument elements.
+//This is suggested because the xmlNodeSet returned by xmlXPathEval (see the Node::find() implementation) can contain either xmlNode or xmlDocument elements.
 /**
  * Represents an XML document in the DOM model.
  */
@@ -88,7 +88,7 @@ public:
 
   /** Creates the root node.
    * @param name The node's name.
-   * @param ns_uri The namespace URI. A namspace declaration will be added to this node, because it could not have
+   * @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.
@@ -112,6 +112,18 @@ public:
    */
   CommentNode* add_comment(const Glib::ustring& content);
 
+  /** Append a new processing instruction node.
+   *
+   * @newin{2,36}
+   *
+   * @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
+   */
+  ProcessingInstructionNode* add_processing_instruction(
+    const Glib::ustring& name, const Glib::ustring& content);
+
   //TODO: Use std::string for filenames.
   /** Write the document to a file.
    * @param filename
@@ -178,7 +190,7 @@ public:
 protected:
   /** Retrieve an Entity.
    * The entity can be from an external subset or internally declared.
-   * @param name Then name of the entity to get.
+   * @param name The name of the entity to get.
    * @returns A pointer to the libxml2 entity structure.
    */
   _xmlEntity* get_entity(const Glib::ustring& name);
diff --git a/libxml++/libxml++.h b/libxml++/libxml++.h
index 95184ed..6f630a7 100644
--- a/libxml++/libxml++.h
+++ b/libxml++/libxml++.h
@@ -55,6 +55,7 @@
 #include <libxml++/nodes/node.h>
 #include <libxml++/nodes/commentnode.h>
 #include <libxml++/nodes/element.h>
+#include <libxml++/nodes/entitydeclaration.h>
 #include <libxml++/nodes/entityreference.h>
 #include <libxml++/nodes/textnode.h>
 #include <libxml++/attribute.h>
diff --git a/libxml++/nodes/element.cc b/libxml++/nodes/element.cc
index a798814..809eaa3 100644
--- a/libxml++/nodes/element.cc
+++ b/libxml++/nodes/element.cc
@@ -5,7 +5,6 @@
  */
 
 #include <libxml++/nodes/element.h>
-#include <libxml++/nodes/textnode.h>
 #include <libxml++/exceptions/internal_error.h>
 #include <libxml++/document.h>
 
@@ -247,7 +246,6 @@ CommentNode* Element::add_child_comment(const Glib::ustring& content)
 }
 
 
-
 CdataNode* Element::add_child_cdata(const Glib::ustring& content)
 {
   xmlNode* node = xmlNewCDataBlock(cobj()->doc, (const xmlChar*)content.c_str(), content.bytes());
@@ -256,5 +254,33 @@ CdataNode* Element::add_child_cdata(const Glib::ustring& content)
   return static_cast<CdataNode*>(node->_private);
 }
 
+EntityReference* Element::add_child_entity_reference(const Glib::ustring& name)
+{
+  const Glib::ustring extended_name = name + "  "; // This is at least two chars long.
+  int ichar = 0;
+  if (extended_name[ichar] == '&')
+    ++ichar;
+
+  // Is it an entity reference or a character reference?
+  // libxml uses xmlNode::type == XML_ENTITY_REF_NODE for both.
+  xmlNode* node = 0;
+  if (extended_name[ichar] == '#')
+    node = xmlNewCharRef(cobj()->doc, (const xmlChar*)name.c_str());
+  else
+    node = xmlNewReference(cobj()->doc, (const xmlChar*)name.c_str());
+  node = xmlAddChild(cobj(), node);
+  Node::create_wrapper(node);
+  return node ? static_cast<EntityReference*>(node->_private) : 0;
+}
+
+ProcessingInstructionNode* Element::add_child_processing_instruction(
+  const Glib::ustring& name, const Glib::ustring& content)
+{
+  xmlNode* node = xmlNewDocPI(cobj()->doc, (const xmlChar*)name.c_str(), (const xmlChar*)content.c_str());
+  node = xmlAddChild(cobj(), node);
+  Node::create_wrapper(node);
+  return node ? static_cast<ProcessingInstructionNode*>(node->_private) : 0;
+}
+
 
 } //namespace xmlpp
diff --git a/libxml++/nodes/element.h b/libxml++/nodes/element.h
index e72b235..da46d4c 100644
--- a/libxml++/nodes/element.h
+++ b/libxml++/nodes/element.h
@@ -11,6 +11,9 @@
 #include <libxml++/attribute.h>
 #include <libxml++/nodes/commentnode.h>
 #include <libxml++/nodes/cdatanode.h>
+#include <libxml++/nodes/textnode.h>
+#include <libxml++/nodes/processinginstructionnode.h>
+#include <libxml++/nodes/entityreference.h>
 
 namespace xmlpp
 {
@@ -45,7 +48,7 @@ public:
   //See the patch at https://bugzilla.gnome.org/show_bug.cgi?id=632524
   // FIXME: the following only returns explicitely provided
   // attributes, not default ones declared in the dtd.
-  // TOOD: Is this still true? murrayc
+  // TODO: Is this still true? murrayc
   Attribute* get_attribute(const Glib::ustring& name,
                            const Glib::ustring& ns_prefix = Glib::ustring()) const;
 
@@ -141,6 +144,32 @@ public:
    */
   CdataNode* add_child_cdata(const Glib::ustring& content);
 
+  /** Append a new entity reference node.
+   * The reference can be either an entity reference ("name" or "&name;") or
+   * a character reference ("#dec", "#xhex", "&#dec;", or "&#xhex;").
+   *
+   * '&' and ';' are optional. If they exist, they are stripped from the stored
+   * copy of the name. Node::get_name() returns the name without '&' and ';'.
+   * If the Document is written to an XML file, '&' and ';' are written.
+   *
+   * @newin{2,36}
+   *
+   * @param name The name of the entity.
+   * @returns The new entity reference node.
+   */
+  EntityReference* add_child_entity_reference(const Glib::ustring& name);
+
+  /** Append a new processing instruction node.
+   *
+   * @newin{2,36}
+   *
+   * @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.
+   */
+  ProcessingInstructionNode* add_child_processing_instruction(
+    const Glib::ustring& name, const Glib::ustring& content);
+
 protected:
   Glib::ustring get_namespace_uri_for_prefix(const Glib::ustring& ns_prefix) const;
 };
diff --git a/libxml++/nodes/entitydeclaration.cc b/libxml++/nodes/entitydeclaration.cc
new file mode 100644
index 0000000..9486cb4
--- /dev/null
+++ b/libxml++/nodes/entitydeclaration.cc
@@ -0,0 +1,44 @@
+/* entitydeclaration.cc
+ * libxml++ and this file are copyright (C) 2000 by Ari Johnson, and
+ * are covered by the GNU Lesser General Public License, which should be
+ * included with libxml++ as the file COPYING.
+ */
+
+#include <libxml++/nodes/entitydeclaration.h>
+#include <libxml/tree.h>
+
+namespace xmlpp
+{
+
+EntityDeclaration::EntityDeclaration(xmlNode* node)
+: ContentNode(node)
+{}
+
+EntityDeclaration::~EntityDeclaration()
+{}
+
+Glib::ustring EntityDeclaration::get_resolved_text() const
+{
+  return cobj()->content ? (const char*)cobj()->content : "";
+}
+
+Glib::ustring EntityDeclaration::get_original_text() const
+{
+  return cobj()->orig ? (const char*)cobj()->orig : "";
+}
+
+xmlEntity* EntityDeclaration::cobj()
+{
+  // An XML_ENTITY_DECL is represented by an xmlEntity struct. Reinterpret
+  // the xmlNode pointer stored in the base class as an xmlEntity pointer.
+  return reinterpret_cast<xmlEntity*>(Node::cobj());
+}
+
+const xmlEntity* EntityDeclaration::cobj() const
+{
+  // An XML_ENTITY_DECL is represented by an xmlEntity struct. Reinterpret
+  // the xmlNode pointer stored in the base class as an xmlEntity pointer.
+  return reinterpret_cast<const xmlEntity*>(Node::cobj());
+}
+
+} //namespace xmlpp
diff --git a/libxml++/nodes/entitydeclaration.h b/libxml++/nodes/entitydeclaration.h
new file mode 100644
index 0000000..f5f4a35
--- /dev/null
+++ b/libxml++/nodes/entitydeclaration.h
@@ -0,0 +1,54 @@
+/* entitydeclaration.h
+ * libxml++ and this file are copyright (C) 2000 by Ari Johnson, and
+ * are covered by the GNU Lesser General Public License, which should be
+ * included with libxml++ as the file COPYING.
+ */
+
+#ifndef __LIBXMLPP_NODES_ENTITYDECLARATION_H
+#define __LIBXMLPP_NODES_ENTITYDECLARATION_H
+
+#include <libxml++/nodes/contentnode.h>
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+extern "C" {
+  struct _xmlEntity;
+}
+#endif //#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+namespace xmlpp
+{
+
+/** Entity declaration. This will be instantiated by the parser.
+ *
+ * @newin{2,36}
+ *
+ */
+class EntityDeclaration : public ContentNode
+{
+public:
+  explicit EntityDeclaration(_xmlNode* node);
+  virtual ~EntityDeclaration();
+
+  /** Get the text with character references (like "&#xdf;") resolved.
+   * If the entity declaration does not contain any reference to another entity,
+   * this is the text that an entity reference would have resolved to, if the XML
+   * document had been parsed with Parser::set_substitute_entities(true).
+   * @returns The text with character references unescaped.
+   */
+  Glib::ustring get_resolved_text() const;
+
+  /** Get the text as read from the XML or DTD file.
+   * @returns The escaped text.
+   */
+  Glib::ustring get_original_text() const;
+
+  ///Access the underlying libxml implementation.
+  _xmlEntity* cobj();
+
+  ///Access the underlying libxml implementation.
+  const _xmlEntity* cobj() const;
+};
+
+} // namespace xmlpp
+
+#endif //__LIBXMLPP_NODES_ENTITYDECLARATION_H
diff --git a/libxml++/nodes/entityreference.h b/libxml++/nodes/entityreference.h
index d9d935f..f0d0495 100644
--- a/libxml++/nodes/entityreference.h
+++ b/libxml++/nodes/entityreference.h
@@ -20,13 +20,17 @@ public:
   explicit EntityReference(_xmlNode* node);
   virtual ~EntityReference();
 
-  /** Get the text to which this entity reference would have resolved if the XML document had been parsed with Parser::set_substitute_entities(true).
-   * @returns The unescaped text.
+  /** Get the text with character references (like "&#xdf;") resolved.
+   * If the corresponding entity declaration does not contain any reference to
+   * another entity, this is the text that the reference would have resolved to
+   * if the XML document had been parsed with Parser::set_substitute_entities(true).
+   * @returns The text with character references unescaped.
    */
   Glib::ustring get_resolved_text() const;
 
-  //TODO: I'm not sure what this is. So far it seems to be the same as get_resolved_text().
-  //      Maybe it's for nested entity declarations, though I don't know if that is even possile. murrayc.
+  /** Get the text as read from the XML or DTD file.
+   * @returns The escaped text.
+   */
   Glib::ustring get_original_text() const;
 
 };
diff --git a/libxml++/nodes/node.cc b/libxml++/nodes/node.cc
index bc56266..36494a4 100644
--- a/libxml++/nodes/node.cc
+++ b/libxml++/nodes/node.cc
@@ -6,6 +6,7 @@
 
 #include <libxml++/nodes/element.h>
 #include <libxml++/nodes/node.h>
+#include <libxml++/nodes/entitydeclaration.h>
 #include <libxml++/nodes/entityreference.h>
 #include <libxml++/nodes/textnode.h>
 #include <libxml++/nodes/commentnode.h>
@@ -350,13 +351,14 @@ NodeSet Node::find(const Glib::ustring& xpath,
 
 Glib::ustring Node::get_namespace_prefix() const
 {
-  if(impl_->type == XML_DOCUMENT_NODE)
+  if(impl_->type == XML_DOCUMENT_NODE || impl_->type == XML_ENTITY_DECL)
   {
-    //impl_ is actually of type xmlDoc, instead of just xmlNode.
-    //libxml does not always use GObject-style inheritance, so xmlDoc does not have all the same struct fields as xmlNode.
+    //impl_ is actually of type xmlDoc or xmlEntity, instead of just xmlNode.
+    //libxml does not always use GObject-style inheritance, so xmlDoc and
+    //xmlEntity do not have all the same struct fields as xmlNode.
     //Therefore, a call to impl_->ns would be invalid.
     //This can be an issue when calling this method on a Node returned by Node::find().
-    //See the TODO comment on Document, suggesting that Document should derived from Node.
+    //See the TODO comment on Document, suggesting that Document should derive from Node.
 
     return Glib::ustring();
   }
@@ -369,10 +371,11 @@ Glib::ustring Node::get_namespace_prefix() const
 
 Glib::ustring Node::get_namespace_uri() const
 {
-  if(impl_->type == XML_DOCUMENT_NODE)
+  if(impl_->type == XML_DOCUMENT_NODE || impl_->type == XML_ENTITY_DECL)
   {
-    //impl_ is actually of type xmlDoc, instead of just xmlNode.
-    //libxml does not always use GObject-style inheritance, so xmlDoc does not have all the same struct fields as xmlNode.
+    //impl_ is actually of type xmlDoc or xmlEntity, instead of just xmlNode.
+    //libxml does not always use GObject-style inheritance, so xmlDoc and
+    //xmlEntity do not have all the same struct fields as xmlNode.
     //Therefore, a call to impl_->ns would be invalid.
     //This can be an issue when calling this method on a Node returned by Node::find().
     //See the TODO comment on Document, suggesting that Document should derived from Node.
@@ -454,6 +457,11 @@ void Node::create_wrapper(xmlNode* node)
     //  //node->_private = new xmlpp::ProcessingInstructionNode(node);
     //  break;
     //}
+    case XML_ENTITY_DECL:
+    {
+      node->_private = new xmlpp::EntityDeclaration(node);
+      break;
+    }
     case XML_ENTITY_REF_NODE:
     {
       node->_private = new xmlpp::EntityReference(node);
@@ -468,7 +476,8 @@ void Node::create_wrapper(xmlNode* node)
     {
       // good default for release versions
       node->_private = new xmlpp::Node(node);
-      std::cerr << G_STRFUNC << "Warning: new node of unknown type created: " << node->type << std::endl;
+      std::cerr << G_STRFUNC << " Warning: new node of unknown type created: "
+                << node->type << std::endl;
       break;
     }
   }
@@ -479,9 +488,17 @@ void Node::free_wrappers(xmlNode* node)
   if(!node)
     return;
     
-  //Walk the children list
-  for(xmlNode* child=node->children; child; child=child->next)
-    free_wrappers(child);
+  //If an entity declaration contains an entity reference, there can be cyclic
+  //references between entity declarations and entity references. (It's not
+  //a tree.) We must avoid an infinite recursion.
+  //Compare xmlFreeNode(), which frees the children of all node types except
+  //XML_ENTITY_REF_NODE.
+  if (node->type != XML_ENTITY_REF_NODE)
+  {
+    //Walk the children list.
+    for (xmlNode* child = node->children; child; child = child->next)
+      free_wrappers(child);
+  }
 
   //Delete the local one
   switch(node->type)



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