From cdb8fe7d827842c31f682008539c1e21464fce9e Mon Sep 17 00:00:00 2001 From: Jürgen Knödlseder Date: Fri, 31 Oct 2014 23:04:39 +0100 Subject: [PATCH] Handle pre-defined entities in XML module. --- ChangeLog | 1 + NEWS | 9 +++++++++ include/GTools.hpp | 3 +++ pyext/GTools.i | 32 +++++++++----------------------- src/support/GTools.cpp | 185 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/xml/GXmlAttribute.cpp | 20 +++++++++++++++----- src/xml/GXmlText.cpp | 12 +++++++----- test/test_GSupport.cpp | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++-------------- test/test_GSupport.hpp | 3 ++- 9 files changed, 284 insertions(+), 48 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6f692c2..293769d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -16,6 +16,7 @@ Add utc() methods to GTime class Add hierachical element access methods to GXml and GXmlNode Add table creation and saving methods to GCsv class + Add predefined entity support to XML module Read/write altitude and azimuth attributes of CTA data Refactor CTA response class, add GCTAResponseCube class Improve CTA response computation for extended models diff --git a/NEWS b/NEWS index 2630fc2..6827ef3 100644 --- a/NEWS +++ b/NEWS @@ -163,6 +163,9 @@ array values. Methods have been added to the GCsv class that allow the creation and the saving of CSV tables. +The str2xml() and xml2str() functions have been added to GTools to handle +predefined entities in XML files. + 14. XML module -------------- @@ -174,6 +177,12 @@ in case that multiple elements with the same name exist. If squared brackets are omitted, an index of 0 is assumed. If the indicated element does not exist, the method returns a pointer to NULL. +The XML module now handles predefined entities. Special characters, such as +" will now be transformed into their predefined entities, e.g. ", when +writing the XML file. Upon reading, predefined entities will be converted +into special chacaters, so that the user always deals with a human readable +string. + 15. VO module ------------- diff --git a/include/GTools.hpp b/include/GTools.hpp index d241a7d..19d4a73 100644 --- a/include/GTools.hpp +++ b/include/GTools.hpp @@ -105,8 +105,11 @@ namespace gammalib { const std::string& message); GXmlElement* parameter(GXmlElement& xml, const std::string& name); + std::string xml2str(const std::string& arg); + std::string str2xml(const std::string& arg); } + /***********************************************************************//** * @brief Signal if argument is infinite * diff --git a/pyext/GTools.i b/pyext/GTools.i index 7434935..76463f3 100644 --- a/pyext/GTools.i +++ b/pyext/GTools.i @@ -45,29 +45,6 @@ namespace gammalib { std::string expand_env(const std::string& arg); std::string filepath(const std::string& pathname, const std::string& filename); -/* - std::string str(const unsigned short int& value); - std::string str(const unsigned int& value); - std::string str(const unsigned long int& value); - std::string str(const unsigned long long int& value); - std::string str(const short int& value); - std::string str(const int& value); - std::string str(const long int& value); - std::string str(const long long int& value); - std::string str(const float& value); - std::string str(const double& value); - char* tochar(const std::string& arg); - short toshort(const std::string& arg); - unsigned short toushort(const std::string& arg); - int toint(const std::string& arg); - unsigned int touint(const std::string& arg); - long tolong(const std::string& arg); - unsigned long toulong(const std::string& arg); - long long tolonglong(const std::string& arg); - unsigned long long toulonglong(const std::string& arg); - float tofloat(const std::string& arg); - double todouble(const std::string& arg); -*/ std::string toupper(const std::string& s); std::string tolower(const std::string& s); std::vector split(const std::string& s, const std::string& sep); @@ -84,8 +61,17 @@ namespace gammalib { const double& emax, const double& epivot, const double& gamma); + GEnergy elogmean(const GEnergy& a, const GEnergy& b); bool file_exists(const std::string& filename); bool dir_exists(const std::string& dirname); bool is_infinite(const double& x); bool is_notanumber(const double& x); + bool contains(const std::string& str, + const std::string& substring); + void warning(const std::string& origin, + const std::string& message); + GXmlElement* parameter(GXmlElement& xml, + const std::string& name); + std::string xml2str(const std::string& arg); + std::string str2xml(const std::string& arg); } diff --git a/src/support/GTools.cpp b/src/support/GTools.cpp index cc6b21a..71fd1ac 100644 --- a/src/support/GTools.cpp +++ b/src/support/GTools.cpp @@ -39,9 +39,13 @@ #include #include #include "GTools.hpp" +#include "GException.hpp" /* __ Compile options ____________________________________________________ */ +/* __ Function name definitions __________________________________________ */ +#define G_XML2STRING "gammalib::xml2string(std::string&)" + /* __ Coding definitions _________________________________________________ */ #define G_PARFORMAT_LENGTH 29 @@ -1017,3 +1021,184 @@ GXmlElement* gammalib::parameter(GXmlElement& xml, const std::string& name) // Return return par; } + + +/***********************************************************************//** + * @brief Convert XML character references in string to characters + * + * @param[in] arg String containing XML character references. + * @return String with character reference replaced by respective characters. + * + * Converts all character references found in a string in their respective + * characters. For more information about XML character references read + * http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references + ***************************************************************************/ +std::string gammalib::xml2str(const std::string& arg) +{ + // Initialise string + std::string result; + + // Iitialise position, lenghts and flags + size_t length = arg.length(); + size_t pos = 0; + size_t start = 0; + size_t stop = 0; + size_t len = 0; // Length of string preceeding char. reference + bool found = false; + + // Loop over string + while (pos < length) { + + // If we have not yet found a character reference then search for + // the next one. If we do not find one we break here. + if (!found) { + start = arg.find("&", pos); + if (start != std::string::npos) { + len = start - pos; + pos = start; + found = true; + } + else { + break; + } + } + + // ... otherwise search for end of the actual character reference. + // Throw an exception if no end is found. + else { + stop = arg.find(";", pos); + if (stop != std::string::npos) { + + // First attach leading string to the result + if (len > 0) { + result += arg.substr(start-len, len); + } + + // Next extract character reference + std::string cref = arg.substr(start+1, stop-start-1); + len = cref.length(); + + // Check for a numerical character reference + //TODO: Check that there are only valid characters in + // numerical field + if (len >= 2 && cref[0] == '#') { + int number = -1; + if (cref[1] == 'x') { + number = (int)std::strtol(cref.substr(2,len-2).c_str(), NULL, 16); + } + else { + number = toint(cref.substr(1,len-1)); + } + if (number != -1) { + result.push_back((char)number); + } + else { + std::string msg = "Could not extract number from " + "numerical character reference &"+ + cref+";"; + throw GException::invalid_argument(G_XML2STRING, msg); + } + } + + // ... otherwise check for a character entity reference + // and push back the corresponding character to the result + // string + else { + if (cref == "quot") { + result.push_back((char)34); + } + else if (cref == "amp") { + result.push_back((char)38); + } + else if (cref == "apos") { + result.push_back((char)39); + } + else if (cref == "lt") { + result.push_back((char)60); + } + else if (cref == "gt") { + result.push_back((char)62); + } + else { + std::string msg = "Unknown character entity reference " + "&"+cref+"; encountered in XML string \""+ + arg+"\"."; + throw GException::invalid_argument(G_XML2STRING, msg); + } + } + + // Signal that we're done and that we search for the + // next character reference + found = false; + pos = stop + 1; + + } // endif: end of character reference found + + // ... otherwise throw an exception + else { + std::string msg = "Missing ; character at end of character " + "reference in XML string \""+arg+"\"."; + throw GException::invalid_argument(G_XML2STRING, msg); + } + + } // endelse: search for end of character reference + + } // endwhile + + // Append any pending string to the result + if (pos < length) { + len = length - pos; + result += arg.substr(pos, len); + } + + // Return result + return result; +} + + +/***********************************************************************//** + * @brief Convert special characters in string to XML character references + * + * @param[in] arg String. + * @return String with special characters replaced by character references. + * + * Converts all special characters found in a string into character + * references. For more information about XML character references read + * http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references + ***************************************************************************/ +std::string gammalib::str2xml(const std::string& arg) +{ + // Initialise string + std::string result; + + // Loop over string + for (int i = 0; i < arg.length(); ++i) { + + // Initialize string to add + std::string character = arg.substr(i, 1); + + // Replace special characters + if (character == "\"") { + character = """; + } + else if (character == "&") { + character = "&"; + } + else if (character == "'") { + character = "'"; + } + else if (character == "<") { + character = "<"; + } + else if (character == ">") { + character = ">"; + } + + // Append character + result += character; + + } + + // Return result + return result; +} diff --git a/src/xml/GXmlAttribute.cpp b/src/xml/GXmlAttribute.cpp index c1dd2d6..7a22e36 100644 --- a/src/xml/GXmlAttribute.cpp +++ b/src/xml/GXmlAttribute.cpp @@ -1,7 +1,7 @@ /*************************************************************************** * GXmlAttribute.cpp - XML attribute class implementation * * ----------------------------------------------------------------------- * - * copyright (C) 2010-2013 by Juergen Knoedlseder * + * copyright (C) 2010-2014 by Juergen Knoedlseder * * ----------------------------------------------------------------------- * * * * This program is free software: you can redistribute it and/or modify * @@ -29,6 +29,7 @@ #include #endif #include "GException.hpp" +#include "GTools.hpp" #include "GXmlAttribute.hpp" /* __ Method name definitions ____________________________________________ */ @@ -84,7 +85,9 @@ GXmlAttribute::GXmlAttribute(const GXmlAttribute& attr) * @param[in] name Attribute name. * @param[in] value Attribute value. * - * Construct attribute form a p@ name and a @p value. + * Construct attribute form a p@ name and a @p value. Predefined entities + * (e.g. ") in attribute values are automatically converted into normal + * characters. ***************************************************************************/ GXmlAttribute::GXmlAttribute(const std::string& name, const std::string& value) { @@ -93,7 +96,7 @@ GXmlAttribute::GXmlAttribute(const std::string& name, const std::string& value) // Set attribute this->name(name); - this->value(value); + this->value(gammalib::xml2str(value)); // Return return; @@ -187,12 +190,19 @@ GXmlAttribute* GXmlAttribute::clone(void) const * * @param[in] url Unified Resource Locator. * - * Writes the element attribute into the @p url. + * Writes the element attribute into the @p url. Special characters are + * automatically transformed into predefined entities (e.g. "). ***************************************************************************/ void GXmlAttribute::write(GUrl& url) const { + // Get value without quotation marks + std::string value = m_value.substr(1, m_value.length()-2); + + // Convert value to XML format + value = "\""+gammalib::str2xml(value)+"\""; + // Write attribute into URL - url.printf(" %s=%s", m_name.c_str(), m_value.c_str()); + url.printf(" %s=%s", m_name.c_str(), value.c_str()); // Return return; diff --git a/src/xml/GXmlText.cpp b/src/xml/GXmlText.cpp index 35ac4b1..395ea6e 100644 --- a/src/xml/GXmlText.cpp +++ b/src/xml/GXmlText.cpp @@ -1,7 +1,7 @@ /*************************************************************************** * GXmlText.cpp - XML text node class * * ----------------------------------------------------------------------- * - * copyright (C) 2010-2013 by Juergen Knoedlseder * + * copyright (C) 2010-2014 by Juergen Knoedlseder * * ----------------------------------------------------------------------- * * * * This program is free software: you can redistribute it and/or modify * @@ -82,7 +82,8 @@ GXmlText::GXmlText(const GXmlText& node) : GXmlNode(node) * * @param[in] text Text string. * - * Construct object by attributing a text string. + * Construct object by attributing a text string. Predefined entities + * (e.g. ") are automatically converted into normal characters. ***************************************************************************/ GXmlText::GXmlText(const std::string& text) : GXmlNode() { @@ -90,7 +91,7 @@ GXmlText::GXmlText(const std::string& text) : GXmlNode() init_members(); // Set text - m_text = text; + m_text = gammalib::xml2str(text); // Return return; @@ -190,12 +191,13 @@ GXmlText* GXmlText::clone(void) const * @param[in] url Unified Resource Locator. * @param[in] indent Text indentation (parameter ignored). * - * Writes the text into the URL. + * Writes the text into the URL. Special characters are automatically + * transformed into predefined entities (e.g. "). ***************************************************************************/ void GXmlText::write(GUrl& url, const int& indent) const { // Write text - url.printf("%s", m_text.c_str()); + url.printf("%s", gammalib::str2xml(m_text).c_str()); // Return return; diff --git a/test/test_GSupport.cpp b/test/test_GSupport.cpp index 80d3f37..099788e 100644 --- a/test/test_GSupport.cpp +++ b/test/test_GSupport.cpp @@ -49,6 +49,7 @@ void TestGSupport::set(void){ name("GSupport"); // Append tests + append(static_cast(&TestGSupport::test_tools), "Test GTools"); append(static_cast(&TestGSupport::test_expand_env), "Test Environment variable"); append(static_cast(&TestGSupport::test_node_array), "Test GNodeArray"); append(static_cast(&TestGSupport::test_url_file), "Test GUrlFile"); @@ -72,6 +73,30 @@ TestGSupport* TestGSupport::clone(void) const /***********************************************************************//** + * @brief Test GTools + * + * Test the GTools functions + ***************************************************************************/ +void TestGSupport::test_tools(void) +{ + // Test XML to string conversion + std::string s_in = "Hallo World, you \"are\" my 'nice' & place"; + std::string s_ref = "Hallo World, you "are" my 'nice'" + " <planet> & place"; + std::string s_out; + s_out = gammalib::str2xml(s_in); + test_assert(s_out == s_ref, "gammalib::str2xml()", + "Unexpected string \""+s_out+"\" (expected \""+s_ref+"\")"); + s_out = gammalib::xml2str(s_out); + test_assert(s_out == s_in, "gammalib::str2xml()", + "Unexpected string \""+s_out+"\" (expected \""+s_in+"\")"); + + // Return + return; +} + + +/***********************************************************************//** * @brief Test expand_env function * * Test the environment variable expansion function. @@ -88,7 +113,8 @@ void TestGSupport::test_expand_env(void) s_in = "This string has no environment variable."; s_ref = s_in; s_out = gammalib::expand_env(s_in); - test_assert(s_out == s_ref,"no environment variable","Unexpected string '"+s_out+"' (expected '"+s_ref+"')"); + test_assert(s_out == s_ref,"no environment variable", + "Unexpected string \""+s_out+"\" (expected \""+s_ref+"\")"); // Get home environment variable std::string home(std::getenv("HOME")); @@ -97,79 +123,92 @@ void TestGSupport::test_expand_env(void) s_in = "My $ENV{HOME} is my castle."; s_ref = "My "+home+" is my castle."; s_out = gammalib::expand_env(s_in); - test_assert(s_out == s_ref,"$ENV{HOME} environment variable within string","Unexpected string '"+s_out+"' (expected '"+s_ref+"')"); + test_assert(s_out == s_ref,"$ENV{HOME} environment variable within string", + "Unexpected string \""+s_out+"\" (expected \""+s_ref+"\")"); // ${HOME} environment variable within string s_in = "My ${HOME} is my castle."; s_ref = "My "+home+" is my castle."; s_out = gammalib::expand_env(s_in); - test_assert(s_out == s_ref,"${HOME} environment variable within string","Unexpected string '"+s_out+"' (expected '"+s_ref+"')"); + test_assert(s_out == s_ref,"${HOME} environment variable within string", + "Unexpected string \""+s_out+"\" (expected \""+s_ref+"\")"); // $HOME/path environment variable within string s_in = "My $HOME/path is my castle."; s_ref = "My "+home+"/path is my castle."; s_out = gammalib::expand_env(s_in); - test_assert(s_out == s_ref,"$HOME/path environment variable within string","Unexpected string '"+s_out+"' (expected '"+s_ref+"')"); + test_assert(s_out == s_ref,"$HOME/path environment variable within string", + "Unexpected string \""+s_out+"\" (expected \""+s_ref+"\")"); // $HOME environment variable at end of string s_in = "My $HOME"; s_ref = "My "+home; s_out = gammalib::expand_env(s_in); - test_assert(s_out == s_ref,"$HOME environment variable at end of string","Unexpected string '"+s_out+"' (expected '"+s_ref+"')"); + test_assert(s_out == s_ref,"$HOME environment variable at end of string", + "Unexpected string \""+s_out+"\" (expected \""+s_ref+"\")"); // Environment variable within single quotes s_in = "My '$(HOME)' is my castle."; s_ref = s_in; s_out = gammalib::expand_env(s_in); - test_assert(s_out == s_ref,"Environment variable within single quotes","Unexpected string '"+s_out+"' (expected '"+s_ref+"')"); + test_assert(s_out == s_ref,"Environment variable within single quotes", + "Unexpected string \""+s_out+"\" (expected \""+s_ref+"\")"); // Environment variable within double quotes s_in = "My \"$(HOME)\" is my castle."; s_ref = "My \""+home+"\" is my castle."; s_out = gammalib::expand_env(s_in); - test_assert(s_out == s_ref,"Environment variable within double quotes","Unexpected string '"+s_out+"' (expected '"+s_ref+"')"); + test_assert(s_out == s_ref,"Environment variable within double quotes", + "Unexpected string \""+s_out+"\" (expected \""+s_ref+"\")"); // Non existing environment variable within string s_in = "My $(HOMEIXYZ) is my castle."; s_ref = "My is my castle."; s_out = gammalib::expand_env(s_in); - test_assert(s_out == s_ref,"Non existing environment variable within string","Unexpected string '"+s_out+"' (expected '"+s_ref+"')"); + test_assert(s_out == s_ref,"Non existing environment variable within string", + "Unexpected string \""+s_out+"\" (expected \""+s_ref+"\")"); // Empty environment variable within string s_in = "My $() is my castle."; s_ref = "My is my castle."; s_out = gammalib::expand_env(s_in); - test_assert(s_out == s_ref,"Empty environment variable within string","Unexpected string '"+s_out+"' (expected '"+s_ref+"')"); + test_assert(s_out == s_ref,"Empty environment variable within string", + "Unexpected string \""+s_out+"\" (expected \""+s_ref+"\")"); // Two successive environment variables within string s_in = "My $(HOME)$ENV{HOME} is my castle."; s_ref = "My "+home+home+" is my castle."; s_out = gammalib::expand_env(s_in); - test_assert(s_out == s_ref,"Two successive environment variables within string","Unexpected string '"+s_out+"' (expected '"+s_ref+"')"); + test_assert(s_out == s_ref,"Two successive environment variables within string", + "Unexpected string \""+s_out+"\" (expected \""+s_ref+"\")"); // $(HOME) at beginning of string s_in = "$(HOME) is my castle."; s_ref = home+" is my castle."; s_out = gammalib::expand_env(s_in); - test_assert(s_out == s_ref,"$(HOME) at beginning of string","Unexpected string '"+s_out+"' (expected '"+s_ref+"')"); + test_assert(s_out == s_ref,"$(HOME) at beginning of string", + "Unexpected string \""+s_out+"\" (expected \""+s_ref+"\")"); // $ENV(HOME) at beginning of string s_in = "$ENV(HOME) is my castle."; s_ref = home+" is my castle."; s_out = gammalib::expand_env(s_in); - test_assert(s_out == s_ref,"$ENV(HOME) at beginning of string","Unexpected string '"+s_out+"' (expected '"+s_ref+"')"); + test_assert(s_out == s_ref,"$ENV(HOME) at beginning of string", + "Unexpected string \""+s_out+"\" (expected \""+s_ref+"\")"); // $(HOME) at end of string s_in = "My castle is $(HOME)"; s_ref = "My castle is "+home; s_out = gammalib::expand_env(s_in); - test_assert(s_out == s_ref,"$(HOME) at end of string","Unexpected string '"+s_out+"' (expected '"+s_ref+"')"); + test_assert(s_out == s_ref,"$(HOME) at end of string", + "Unexpected string \""+s_out+"\" (expected \""+s_ref+"\")"); // $ENV{HOME}${HOME}$ENV(HOME)$(HOME) only string s_in = "$ENV{HOME}${HOME}$ENV(HOME)$(HOME)"; s_ref = home+home+home+home; s_out = gammalib::expand_env(s_in); - test_assert(s_out == s_ref,"$ENV{HOME}${HOME}$ENV(HOME)$(HOME) only string","Unexpected string '"+s_out+"' (expected '"+s_ref+"')"); + test_assert(s_out == s_ref,"$ENV{HOME}${HOME}$ENV(HOME)$(HOME) only string", + "Unexpected string \""+s_out+"\" (expected \""+s_ref+"\")"); // Debugging //std::cout << std::endl; diff --git a/test/test_GSupport.hpp b/test/test_GSupport.hpp index e150f0c..3bd1d25 100644 --- a/test/test_GSupport.hpp +++ b/test/test_GSupport.hpp @@ -1,7 +1,7 @@ /*************************************************************************** * test_GSupport.hpp - test support module * * ----------------------------------------------------------------------- * - * copyright (C) 2012-2013 by Jean-Baptiste Cayrou * + * copyright (C) 2012-2014 by Jean-Baptiste Cayrou * * ----------------------------------------------------------------------- * * * * This program is free software: you can redistribute it and/or modify * @@ -51,6 +51,7 @@ public: void test_node_array(void); void test_url_file(void); void test_url_string(void); + void test_tools(void); private: // Private methods -- libgit2 0.21.4