XML: Access to attributes
authorArjen Baart <arjen@andromeda.nl>
Wed, 5 Aug 2020 06:03:15 +0000 (08:03 +0200)
committerArjen Baart <arjen@andromeda.nl>
Wed, 5 Aug 2020 06:03:15 +0000 (08:03 +0200)
14 files changed:
TODO
doc/configuration.xml [new file with mode: 0644]
doc/manual.xml
doc/utc.xml
doc/xml.xml
src/configuration.cpp
src/configuration.h
src/superstring.cpp
src/xml.cpp
src/xml.h
test/Makefile.am
test/xml_elem_attr.cpp [new file with mode: 0644]
test/xml_elem_attr.exp [new file with mode: 0644]
test/xml_test01.xml

diff --git a/TODO b/TODO
index 0909e93..8d34e6e 100644 (file)
--- a/TODO
+++ b/TODO
@@ -14,5 +14,6 @@ Things to do:
 
 - UTC: Convert to and from  struct tm
 
-- xml: Add access to attributes
 - xml: Xinclude processing
+
+- configuraion: Handle environment variables to override parameters
diff --git a/doc/configuration.xml b/doc/configuration.xml
new file mode 100644 (file)
index 0000000..f229ddd
--- /dev/null
@@ -0,0 +1,56 @@
+<chapter>
+<heading>configuration - Class for configuration parameters</heading>
+
+<pre>
+#include &lt;configuration.h&gt;
+
+configuration config;
+</pre>
+
+<para>
+Handle configurational parameters for the application.
+Many applications need some permanently stored configurational
+data. The information is usually stored in two places: A system-
+wide configuration file and a configuration file per user.
+The content of the configuration file is in XML format.
+The configuration base class takes care of finding the configuration
+files, e.g. in /etc/app.conf or in /usr/local/etc/app.conf, along
+with the configuration file in the user's home directory.
+The config files are parsed with the gnome XML parser and a
+framework is provided to find configurational items.
+
+</para>
+
+<section>
+<heading>Class configuration</heading>
+<para>
+Hold the configuration data for an application.
+Both the system-wide configuration and a user-specific configuration are stores in a configuration object.
+
+<description>
+<item tag="configuration()">
+The default constructor creates an empty configuration.
+</item>
+<item tag="read(String name)">
+Find the config files and parse the XML.
+Both the filename and the root element of the XML files must match the name.
+Two configuarions are read. The system and the user config.
+First system config in /etc and if it does not exist, in /usr/local/etc.
+The user config is read from the current directory.
+If that does not exist, try the home directory.
+</item>
+</description>
+</para>
+
+<description>
+<item tag="find_parameter(String section, String parameter, int sec_nr = 0, int param_n = 0)">
+Return the value of a config parameter.
+The parameter is obtained from the system-wide config.
+If the parameter is also defined in the user config, this will override the value in the system config.
+</item>
+</description>
+
+</section>
+
+
+</chapter>
index 629da30..b124508 100644 (file)
@@ -19,5 +19,7 @@
          xmlns:xi="http://www.w3.org/2001/XInclude"/>
     <xi:include href="xml.xml"
          xmlns:xi="http://www.w3.org/2001/XInclude"/>
+    <xi:include href="configuration.xml"
+         xmlns:xi="http://www.w3.org/2001/XInclude"/>
   </book>
 </doc>
index 11b102d..bf242ea 100644 (file)
@@ -20,6 +20,9 @@ The default constructor creates a default of its parts.
 <item tag="UTC(String s)">
 Parse the date and time from String s.
 </item>
+<item tag="UTC(time_t clock)">
+Convert the UNIX time_t, defined as the number of seconds since the Epoch to a UTC object.
+</item>
 </description>
 </para>
 
@@ -37,6 +40,9 @@ Parse the date and time from String s.
 <para>
 String()
 </para>
+<para>
+time_t() - Convert to the UNIX time_t.
+</para>
 
 </section>
 
@@ -66,7 +72,9 @@ String()
 
 <description>
 <item tag="UTC operator+(UTC &amp;t1, UTC &amp;t2)"></item>
-<item tag="UTC operator-(UTC &amp;t1, UTC &amp;t2)"></item>
+<item tag="long operator-(UTC &amp;t1, UTC &amp;t2)">
+Calculate the number of seconds from t2 to t1;
+</item>
 <item tag="UTC &amp;operator+=(UTC &amp;t)"></item>
 <item tag="UTC &amp;operator-=(UTC &amp;t)"></item>
 <item tag="UTC &amp;operator++()">
index 09b1da4..5cc18ed 100644 (file)
@@ -105,6 +105,13 @@ of the XML document.
 <item tag="std::vector&lt;xml_element&gt; operator[](String tagname);">
 Find the child elements with the specified tagname.
 </item>
+<item tag="String attribute(String name);">
+Find the value of an attribute.
+Return an empty string is the attribute does not exist.
+</item>
+<item tag="std::map&lt;String, String&gt; attributes(void);">
+Find all attributes of the element in a map.
+</item>
 </description>
 
 </section>
index cbd2745..59408a7 100644 (file)
@@ -5,7 +5,7 @@
 ** MODULE INFORMATION *
 ***********************
 **      FILE NAME      : configuration.cpp
-**      SYSTEM NAME    : AXE - Andromeda X-windows Encapsulation
+**      SYSTEM NAME    : ACL - Andromeda Class Library
 **      VERSION NUMBER : $Revision: 1.6 $
 **
 **  DESCRIPTION      :  Implementation of configuration class
 **      MODIFICATIONS   : 
 **************************************************************************/
 
-/*****************************
-   $Log: configuration.cpp,v $
-   Revision 1.6  2007-05-04 14:01:22  arjen
-   Added new arguments to configuration::find_parameter() to allow for a list of
-   sections and a list of parameters in sections.
-   The new arguments are section index and parameter index (default = 0).
-
-   Revision 1.5  2003/03/29 07:19:37  arjen
-   Bugfix: Do not read the config file in the home
-   directory if the HOME environment variable doesn't exist
-
-   Revision 1.4  2002/11/23 12:48:18  arjen
-   Check if a configuration file exists before feeding it to the XML parser.
-
-   Revision 1.3  2002/11/02 14:35:15  arjen
-   Uses the XML2 library
-   BugFix: segfault when finding a parameters that is not in the user-sepcific
-   config tree.
-
-   Revision 1.2  2002/09/28 06:48:46  arjen
-   Bugfix: In configuration::find_parameter(), check if the parameter node exists
-   before retrieving its contents.
-
-   Revision 1.1  2002/07/25 08:01:26  arjen
-   First checkin, AXE release 0.2
-
-*****************************/
-
 #include <fcntl.h>
 #include <unistd.h>
 #include "configuration.h"
index c894a43..4af5ec3 100644 (file)
@@ -4,14 +4,14 @@
 ** MODULE INFORMATION *
 ***********************
 **      FILE NAME      : configuration.h
-**      SYSTEM NAME    : AXE - Andromeda X-windows Encapsulation
+**      SYSTEM NAME    : ACL - Andromeda class Library
 **      VERSION NUMBER : $Revision: 1.4 $
 **
 **  DESCRIPTION      : Definition of configuration class 
 **
 **  EXPORTED OBJECTS : class configuration
 **  LOCAL    OBJECTS : 
-**  MODULES  USED    : String
+**  MODULES  USED    : String, XML
 ***************************************************************************
 **  ADMINISTRATIVE INFORMATION *
 ********************************
 **      MODIFICATIONS   : 
 **************************************************************************/
 
-/*****************************
-   $Log: configuration.h,v $
-   Revision 1.4  2007-05-04 14:01:22  arjen
-   Added new arguments to configuration::find_parameter() to allow for a list of
-   sections and a list of parameters in sections.
-   The new arguments are section index and parameter index (default = 0).
-
-   Revision 1.3  2002/11/02 14:35:15  arjen
-   Uses the XML2 library
-   BugFix: segfault when finding a parameters that is not in the user-sepcific
-   config tree.
-
-   Revision 1.2  2002/09/02 06:18:20  arjen
-   Fixed some date and time conversion functions
-
-   Revision 1.1  2002/07/25 08:01:26  arjen
-   First checkin, AXE release 0.2
-
-*****************************/
-
-/* static const char *RCSID = "$Id: configuration.h,v 1.4 2007-05-04 14:01:22 arjen Exp $"; */
 
 #ifndef CONFIGURATION_H
 #define CONFIGURATION_H
index c2d6696..694cb93 100644 (file)
@@ -70,7 +70,7 @@ String SuperString::join(const String &separator)
    if (_ss.size() != 0)
    {
       joined = _ss[0];
-      for (int i = 1; i < _ss.size(); i++)
+      for (unsigned i = 1; i < _ss.size(); i++)
       {
          joined += separator + _ss[i];
       }
index 3e9267a..30b1109 100644 (file)
@@ -1,10 +1,13 @@
 
 #include "xml.h"
 
+/*
+ *   class xml
+ */
+
 void xml::ParseFile(const char * filename)
 {
    ctxt     = xmlNewParserCtxt();
-   //document = xmlParseFile(filename);
    document = xmlCtxtReadFile(ctxt, filename, NULL, XML_PARSE_NOWARNING|XML_PARSE_NOERROR);
 }
 
@@ -22,6 +25,10 @@ String xml::RootElementName(void)
    return root_name;
 }
 
+/*
+ *  class xml_node
+ */
+
 xml_node xml::RootNode(void)
 {
    xmlNodePtr   root;
@@ -31,11 +38,6 @@ xml_node xml::RootNode(void)
    return xml_node(root);
 }
 
-//xml_node::xml_node(xml doc)
-//{
-//   *this = doc.RootNode();
-//}
-
 //  Find the nth child element by name (non-recursive)
 xml_node xml_node::operator[](int i)
 {
@@ -72,6 +74,10 @@ xml_node xml_node::FindChildElement(String name, int nth)
 }
 
 
+/*
+ *   class xml_element
+ */
+
 std::vector<xml_element> xml_element::operator[](String tagname)
 {
    std::vector<xml_element> elements_found;
@@ -89,3 +95,44 @@ std::vector<xml_element> xml_element::operator[](String tagname)
 
    return elements_found;
 }
+
+String xml_element::attribute(String name)
+{
+   String value;
+
+   struct _xmlAttr          *prop_list;
+   prop_list = n->properties;
+
+   while (prop_list != NULL)
+   {
+      if (name == (const char *)prop_list->name)
+      {
+         value = (const char *)prop_list->children->content;
+      }
+      prop_list = prop_list->next;
+   }
+
+   return value;
+}
+
+std::map<String, String> xml_element::attributes(void)
+{
+   std::map<String, String> attrs;
+   struct _xmlAttr          *prop_list;
+
+   prop_list = n->properties;
+
+   while (prop_list != NULL)
+   {
+      String name, value;
+
+      name = String((const char *)prop_list->name);
+      value = String((const char *)prop_list->children->content);
+
+      attrs[name] = value;
+
+      prop_list = prop_list->next;
+   }
+
+   return attrs;
+}
index 4579a17..3b98aeb 100644 (file)
--- a/src/xml.h
+++ b/src/xml.h
@@ -1,5 +1,6 @@
 #include <libxml/parser.h>   // usually in /usr/include/libxml2, see xml2-config
 #include <vector>
+#include <map>
 
 #include "String.h"
 
@@ -131,5 +132,8 @@ public:
 
    std::vector<xml_element> operator[](String tagname);
 
+   String attribute(String name);
+   std::map<String, String> attributes(void);
+
 };
 
index c5ce407..1576eae 100644 (file)
@@ -9,7 +9,7 @@ check_PROGRAMS = string_assign string_basics string_compare string_cat string_co
                  hour_assign hour_parse hour_compare hour_arithmetic hour_check_now \
                  utc_assign utc_parse utc_compare utc_arithmetic utc_convert \
                  integer_assign integer_factorial integer_fibonacci integer_pow64 integer_modulo \
-                 xml_file xml_find xml_node xml_elem \
+                 xml_file xml_find xml_node xml_elem xml_elem_attr \
                  configuration_read configuration_find
 
 string_assign_SOURCES      = string_assign.cpp
@@ -61,6 +61,7 @@ xml_file_SOURCES           = xml_file.cpp
 xml_find_SOURCES           = xml_find.cpp
 xml_node_SOURCES           = xml_node.cpp
 xml_elem_SOURCES           = xml_elem.cpp
+xml_elem_attr_SOURCES      = xml_elem_attr.cpp
 
 configuration_read_SOURCES = configuration_read.cpp
 configuration_find_SOURCES = configuration_find.cpp
diff --git a/test/xml_elem_attr.cpp b/test/xml_elem_attr.cpp
new file mode 100644 (file)
index 0000000..0837c35
--- /dev/null
@@ -0,0 +1,44 @@
+/*******************************************************
+ *  Unit test for the xml_element class
+ *
+ * test xml attributes access
+ ******************************************************
+ *
+ */
+
+#include "xml.h"
+#include <assert.h>
+
+int main()
+{
+   xml    doc;
+
+   const char xml_file[] = "xml_test01.xml";
+
+   std::cout << "Reading XML file " << xml_file << "\n";
+
+   doc.ParseFile("xml_test01.xml");
+
+   xml_element root_node(doc);
+
+   std::cout << "Root element is " << root_node.name() << "\n";
+   std::cout << "Root node type is " << root_node.type() << "\n";
+   assert(root_node.name() == "doc");
+
+   //std::vector<xml_element> book;
+   xml_element book;
+   std::map<String, String> book_attrs;
+
+   book = root_node["book"][0];
+   book_attrs = book.attributes();
+
+   std::cout << book_attrs.size() << " attributes in element book.\n";
+   std::cout << "Attribute id = " << book_attrs["id"] << "\n";
+   assert(book_attrs["id"] == "toplevel");
+
+   std::cout << "Attribute layout = " << book.attribute("layout") << "\n";
+   assert(book.attribute("layout") == "A4");
+
+   return 0;
+}
+
diff --git a/test/xml_elem_attr.exp b/test/xml_elem_attr.exp
new file mode 100644 (file)
index 0000000..f0642b1
--- /dev/null
@@ -0,0 +1,7 @@
+Reading XML file xml_test01.xml
+Root element is doc
+Root node type is 1
+3 attributes in element book.
+Attribute id = toplevel
+Attribute layout = A4
+PASS xml_elem_attr (exit status: 0)
index dcfd8d3..e7dd907 100644 (file)
@@ -1,12 +1,12 @@
 <?xml version="1.0"?>
 <doc style="main.css">
-  <book attr="value">
+  <book id="toplevel" layout="A4" attr="value">
     <titlepage>
       <title>Andromeda Class Library</title>
     </titlepage>
     <toc/>
 
-    <chapter>
+    <chapter id="ch1">
       <heading>Chapter 1</heading>
     </chapter>
     <chapter>