Add class xml V0.3
authorArjen Baart <arjen@andromeda.nl>
Sun, 9 Feb 2020 15:27:20 +0000 (16:27 +0100)
committerArjen Baart <arjen@andromeda.nl>
Sun, 9 Feb 2020 15:27:20 +0000 (16:27 +0100)
12 files changed:
ChangeLog
configure.ac
doc/Makefile.am
src/Makefile.am
src/utc.cpp
src/xml.cpp [new file with mode: 0644]
src/xml.h [new file with mode: 0644]
test/Makefile.am
test/xml_file.cpp [new file with mode: 0644]
test/xml_find.cpp [new file with mode: 0644]
test/xml_find.exp [new file with mode: 0644]
test/xml_test01.xml [new file with mode: 0644]

index fb8153d..b959431 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,9 +1,13 @@
+Feb 07, 2020 - Release 0.3
+============================
+
   - hour: Add and subtract a number of seconds.
   - Add parameter bool localtime to now() and today()
   - String: Boundary checking on index and subtring operators
   - Ported class Integer from the Gnu libg++ library.
   - Added date::Weekday()
   - Added class configration
+  - Added class xml
 
 Oct 31, 2019 - Release 0.2
 ============================
index 19c4c15..302db0b 100644 (file)
@@ -2,7 +2,7 @@
 # Process this file with autoconf to produce a configure script.
 
 AC_PREREQ([2.69])
-AC_INIT([libACL], [0.2], [arjen@androemda.nl])
+AC_INIT([libACL], [0.3], [arjen@androemda.nl])
 AC_CONFIG_SRCDIR([src/date.cpp])
 AM_INIT_AUTOMAKE
 AC_CONFIG_MACRO_DIRS([m4])
index 76537f1..e863ea0 100644 (file)
@@ -38,7 +38,7 @@ manual.txt : $(XMLS)
        xml2text manual.xml > manual.txt
 
 check:
-       xmllint --noout --xinclude --loaddtd --noent --schema ../doc.xsd manual.xml
+#      xmllint --noout --xinclude --loaddtd --noent --schema /usr/local/xslt/doc.xsd manual.xml
 #      xmllint --noout --valid $(XMLS)
 
 clean-local:
index a52770b..19b99dd 100644 (file)
@@ -3,8 +3,9 @@ lib_LTLIBRARIES = libACL.la
 libACL_la_SOURCES = string.cpp regex.cpp date.cpp parsedate.c dateyacc.y datelex.c \
                     hour.cpp utc.cpp \
                     Integer.cpp \
+                    xml.cpp \
                     configuration.cpp
 
-libACL_la_LDFLAGS = -version-info 2:2:0
+libACL_la_LDFLAGS = -version-info 0:3:0
 
-include_HEADERS = String.h date.h Integer.h configuration.h
+include_HEADERS = String.h date.h Integer.h xml.h configuration.h
index 3a5548f..934ba38 100644 (file)
@@ -36,8 +36,6 @@
 
 *****************************/
 
-static const char *RCSID = "$Id: utc.cpp,v 1.1 2002-09-28 06:58:45 arjen Exp $";
-
 #include <time.h>
 
 #include "date.h"
diff --git a/src/xml.cpp b/src/xml.cpp
new file mode 100644 (file)
index 0000000..2ec74c7
--- /dev/null
@@ -0,0 +1,44 @@
+
+#include "xml.h"
+
+String xml::RootElementName(void)
+{
+   String       root_name;
+   xmlNodePtr   root;
+
+   root = xmlDocGetRootElement(document);
+   root_name = String((const char *)root->name);
+
+   return root_name;
+}
+
+xml_node xml::RootNode(void)
+{
+   xmlNodePtr   root;
+
+   root = xmlDocGetRootElement(document);
+
+   return xml_node(root);
+}
+
+//  Find the nth child element by name (non-recursive)
+xml_node xml_node::FindChildElement(String name, int nth)
+{
+   xml_node element;
+   xmlNodePtr node = n->children;
+
+   while (element.n == NULL && node != NULL)
+   {
+      if (node->type == XML_ELEMENT_NODE && name == (const char *)node->name)
+      {
+         if (nth == 0)
+         {
+            element.n = node;
+         }
+         nth--;
+      }
+      node = node->next;
+   }
+
+   return element;
+}
diff --git a/src/xml.h b/src/xml.h
new file mode 100644 (file)
index 0000000..63f1e5a
--- /dev/null
+++ b/src/xml.h
@@ -0,0 +1,90 @@
+#include <libxml/parser.h>   // usually in /usr/include/libxml2, see xml2-config
+
+#include "String.h"
+
+class xml_node
+{
+   xmlNodePtr     n;
+
+public:
+
+   xml_node()
+   {
+      n = NULL;
+   }
+
+   xml_node(xmlNodePtr np)
+   {
+      n = np;
+   }
+
+   bool is_a_node(void)
+   {
+      return n != NULL;
+   }
+
+   String name(void)
+   {
+      String node_name;
+
+      if (n != NULL)
+      {
+         node_name = String((const char *)n->name);
+      }
+
+      return node_name;
+   }
+
+   // Assumes one childnode of type TEXT
+   // TODO collect content from multiple TEXT nodes (recursively ?)
+   String content(void)
+   {
+      String node_content;
+
+      if (n != NULL)
+      {
+         node_content = String((const char *)n->children->content);
+      }
+
+      return node_content;
+   }
+
+   //  Find the nth child element by name (non-recursive)
+   xml_node FindChildElement(String name, int nth = 0);
+};
+
+class xml
+{
+   xmlDocPtr      document;
+
+public:
+
+   xml()
+   {
+      document = NULL;
+   }
+
+   ~xml()
+   {
+      if (document != NULL)
+      {
+         xmlFreeDoc(document);
+         document = NULL;
+      }
+   }
+
+   void ParseFile(const char * filename)
+   {
+      document = xmlParseFile(filename);
+   }
+
+   void SaveFile(const char * filename)
+   {
+      xmlSaveFile(filename, document);
+   }
+
+   String RootElementName(void);
+   xml_node RootNode(void);
+  
+};
+
index cb977a1..740bac0 100644 (file)
@@ -7,6 +7,7 @@ check_PROGRAMS = string_assign string_basics string_compare string_cat string_su
                  hour_assign hour_parse hour_compare hour_arithmetic hour_check_now \
                  utc_assign utc_compare utc_arithmetic \
                  integer_assign integer_factorial integer_fibonacci integer_pow64 integer_modulo \
+                 xml_file xml_find \
                  configuration_read configuration_find
 
 string_assign_SOURCES      = string_assign.cpp
@@ -43,5 +44,8 @@ integer_fibonacci_SOURCES  = integer_fibonacci.cpp tInteger.cpp
 integer_pow64_SOURCES      = integer_pow64.cpp tInteger.cpp
 integer_modulo_SOURCES     = integer_modulo.cpp tInteger.cpp
 
+xml_file_SOURCES           = xml_file.cpp
+xml_find_SOURCES           = xml_find.cpp
+
 configuration_read_SOURCES = configuration_read.cpp
 configuration_find_SOURCES = configuration_find.cpp
diff --git a/test/xml_file.cpp b/test/xml_file.cpp
new file mode 100644 (file)
index 0000000..ea4722e
--- /dev/null
@@ -0,0 +1,29 @@
+/*******************************************************
+ *  Unit test for the xml class
+ *
+ * test reading and writing of xml files.
+ ******************************************************
+ *
+ */
+
+#include "xml.h"
+#include <assert.h>
+
+int main()
+{
+   xml    doc;
+   String root;
+   xml_node root_node;
+
+   doc.ParseFile("xml_test01.xml");
+   doc.SaveFile("xml_test01.out.xml");
+
+   root = doc.RootElementName();
+   assert (root == "doc");
+
+   root_node = doc.RootNode();
+   assert(root_node.name() == "doc");
+
+   return 0;
+}
+
diff --git a/test/xml_find.cpp b/test/xml_find.cpp
new file mode 100644 (file)
index 0000000..b5c3cd6
--- /dev/null
@@ -0,0 +1,85 @@
+/*******************************************************
+ *  Unit test for the xml class
+ *
+ * test finding content in xml nodes
+ ******************************************************
+ *
+ */
+
+#include "xml.h"
+#include <assert.h>
+
+int main()
+{
+   xml    doc;
+   xml_node root_node;
+   xml_node found_node;
+   String   child_name;
+   String   title_content;
+
+   const char xml_file[] = "xml_test01.xml";
+
+   std::cout << "Reading XML file " << xml_file << "\n";
+
+   doc.ParseFile("xml_test01.xml");
+
+   root_node = doc.RootNode();
+
+   std::cout << "Root element is " << root_node.name() << "\n";
+   assert(root_node.name() == "doc");
+
+   child_name = "book";
+   found_node = root_node.FindChildElement(child_name);
+   std::cout << "Child element " << child_name;
+   if (!found_node.is_a_node())
+   {
+      std::cout << " not";
+   }
+   std::cout << " found.\n";
+   assert(found_node.is_a_node() == true);
+
+   child_name = "report";
+   found_node = root_node.FindChildElement(child_name);
+   std::cout << "Child element " << child_name;
+   if (!found_node.is_a_node())
+   {
+      std::cout << " not";
+   }
+   std::cout << " found.\n";
+   std::cout.flush();
+   assert(found_node.is_a_node() == false);
+
+   // Find a child node three levels deep.
+   child_name = "book";
+   found_node = root_node.FindChildElement(child_name);
+   assert(found_node.is_a_node() == true);
+   found_node = found_node.FindChildElement("titlepage");
+   assert(found_node.is_a_node() == true);
+   found_node = found_node.FindChildElement("title");     // Two levels deeper
+   std::cout << "Name of title node = " << found_node.name() << "\n";
+   std::cout << "Content of title node = " << found_node.content() << "\n";
+
+   // Find multple child nodes
+
+   xml_node chapter_node;
+
+   child_name = "book";
+   found_node = root_node.FindChildElement(child_name);
+
+   for (int i = 0; i < 6; i++)
+   {
+      chapter_node = found_node.FindChildElement("chapter", i);
+      if (chapter_node.is_a_node())
+      {
+         std::cout << "Chapter node " << i << " found.\n";
+      }
+      else
+      {
+         std::cout << "Chapter node " << i << " is not found.\n";
+      }
+
+   }
+
+   return 0;
+}
+
diff --git a/test/xml_find.exp b/test/xml_find.exp
new file mode 100644 (file)
index 0000000..f62e36b
--- /dev/null
@@ -0,0 +1,13 @@
+Reading XML file xml_test01.xml
+Root element is doc
+Child element book found.
+Child element report not found.
+Name of title node = title
+Content of title node = Andromeda Class Library
+Chapter node 0 found.
+Chapter node 1 found.
+Chapter node 2 found.
+Chapter node 3 is not found.
+Chapter node 4 is not found.
+Chapter node 5 is not found.
+PASS xml_find (exit status: 0)
diff --git a/test/xml_test01.xml b/test/xml_test01.xml
new file mode 100644 (file)
index 0000000..a6dd12d
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<doc style="main.css">
+  <book>
+    <titlepage>
+      <title>Andromeda Class Library</title>
+    </titlepage>
+    <toc/>
+
+    <chapter>
+      <heading>Chapter 1</heading>
+    </chapter>
+    <chapter>
+      <heading>Chapter 2</heading>
+    </chapter>
+    <chapter>
+      <heading>Chapter 3</heading>
+    </chapter>
+  </book>
+</doc>